diff --git a/admin/app/ai/form.tsx b/admin/app/(all)/(dashboard)/ai/form.tsx similarity index 99% rename from admin/app/ai/form.tsx rename to admin/app/(all)/(dashboard)/ai/form.tsx index 47ab9480eaf..8b7d036ad2f 100644 --- a/admin/app/ai/form.tsx +++ b/admin/app/(all)/(dashboard)/ai/form.tsx @@ -5,7 +5,7 @@ import { Lightbulb } from "lucide-react"; import { IFormattedInstanceConfiguration, TInstanceAIConfigurationKeys } from "@plane/types"; import { Button, TOAST_TYPE, setToast } from "@plane/ui"; // components -import { ControllerInput, TControllerInputFormField } from "@/components/common"; +import { ControllerInput, TControllerInputFormField } from "@/components/common/controller-input"; // hooks import { useInstance } from "@/hooks/store"; diff --git a/admin/app/ai/layout.tsx b/admin/app/(all)/(dashboard)/ai/layout.tsx similarity index 53% rename from admin/app/ai/layout.tsx rename to admin/app/(all)/(dashboard)/ai/layout.tsx index d461a626aa2..42f3796496f 100644 --- a/admin/app/ai/layout.tsx +++ b/admin/app/(all)/(dashboard)/ai/layout.tsx @@ -1,11 +1,10 @@ import { ReactNode } from "react"; import { Metadata } from "next"; -import { AdminLayout } from "@/layouts/admin-layout"; export const metadata: Metadata = { - title: "Artificial Intelligence Settings - Plane Web", + title: "Artificial Intelligence Settings - God Mode", }; export default function AILayout({ children }: { children: ReactNode }) { - return {children}; + return <>{children}; } diff --git a/admin/app/ai/page.tsx b/admin/app/(all)/(dashboard)/ai/page.tsx similarity index 100% rename from admin/app/ai/page.tsx rename to admin/app/(all)/(dashboard)/ai/page.tsx diff --git a/admin/app/authentication/github/form.tsx b/admin/app/(all)/(dashboard)/authentication/github/form.tsx similarity index 95% rename from admin/app/authentication/github/form.tsx rename to admin/app/(all)/(dashboard)/authentication/github/form.tsx index 0c6d81ae6af..6e5f2a90387 100644 --- a/admin/app/authentication/github/form.tsx +++ b/admin/app/(all)/(dashboard)/authentication/github/form.tsx @@ -10,14 +10,10 @@ import { IFormattedInstanceConfiguration, TInstanceGithubAuthenticationConfigura import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui"; import { cn } from "@plane/utils"; // components -import { - CodeBlock, - ConfirmDiscardModal, - ControllerInput, - CopyField, - TControllerInputFormField, - TCopyField, -} from "@/components/common"; +import { CodeBlock } from "@/components/common/code-block"; +import { ConfirmDiscardModal } from "@/components/common/confirm-discard-modal"; +import { ControllerInput, TControllerInputFormField } from "@/components/common/controller-input"; +import { CopyField, TCopyField } from "@/components/common/copy-field"; // hooks import { useInstance } from "@/hooks/store"; diff --git a/admin/app/(all)/(dashboard)/authentication/github/layout.tsx b/admin/app/(all)/(dashboard)/authentication/github/layout.tsx new file mode 100644 index 00000000000..373f9340aff --- /dev/null +++ b/admin/app/(all)/(dashboard)/authentication/github/layout.tsx @@ -0,0 +1,10 @@ +import { ReactNode } from "react"; +import { Metadata } from "next"; + +export const metadata: Metadata = { + title: "GitHub Authentication - God Mode", +}; + +export default function GitHubAuthenticationLayout({ children }: { children: ReactNode }) { + return <>{children}; +} diff --git a/admin/app/authentication/github/page.tsx b/admin/app/(all)/(dashboard)/authentication/github/page.tsx similarity index 90% rename from admin/app/authentication/github/page.tsx rename to admin/app/(all)/(dashboard)/authentication/github/page.tsx index 986a5ebd24e..75cb84e4afd 100644 --- a/admin/app/authentication/github/page.tsx +++ b/admin/app/(all)/(dashboard)/authentication/github/page.tsx @@ -9,8 +9,7 @@ import useSWR from "swr"; import { Loader, ToggleSwitch, setPromiseToast } from "@plane/ui"; import { resolveGeneralTheme } from "@plane/utils"; // components -import { AuthenticationMethodCard } from "@/components/authentication"; -import { PageHeader } from "@/components/common"; +import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card"; // hooks import { useInstance } from "@/hooks/store"; // icons @@ -61,9 +60,11 @@ const InstanceGithubAuthenticationPage = observer(() => { setIsSubmitting(false); }); }; + + const isGithubEnabled = enableGithubConfig === "1"; + return ( <> -
{ } config={ { - Boolean(parseInt(enableGithubConfig)) === true - ? updateConfig("IS_GITHUB_ENABLED", "0") - : updateConfig("IS_GITHUB_ENABLED", "1"); + updateConfig("IS_GITHUB_ENABLED", isGithubEnabled ? "0" : "1"); }} size="sm" disabled={isSubmitting || !formattedConfig} diff --git a/admin/app/authentication/gitlab/form.tsx b/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx similarity index 95% rename from admin/app/authentication/gitlab/form.tsx rename to admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx index f64158744a4..888b2533c2c 100644 --- a/admin/app/authentication/gitlab/form.tsx +++ b/admin/app/(all)/(dashboard)/authentication/gitlab/form.tsx @@ -8,14 +8,10 @@ import { IFormattedInstanceConfiguration, TInstanceGitlabAuthenticationConfigura import { Button, TOAST_TYPE, getButtonStyling, setToast } from "@plane/ui"; import { cn } from "@plane/utils"; // components -import { - CodeBlock, - ConfirmDiscardModal, - ControllerInput, - CopyField, - TControllerInputFormField, - TCopyField, -} from "@/components/common"; +import { CodeBlock } from "@/components/common/code-block"; +import { ConfirmDiscardModal } from "@/components/common/confirm-discard-modal"; +import { ControllerInput, TControllerInputFormField } from "@/components/common/controller-input"; +import { CopyField, TCopyField } from "@/components/common/copy-field"; // hooks import { useInstance } from "@/hooks/store"; diff --git a/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx b/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx new file mode 100644 index 00000000000..fc89e9752fb --- /dev/null +++ b/admin/app/(all)/(dashboard)/authentication/gitlab/layout.tsx @@ -0,0 +1,10 @@ +import { ReactNode } from "react"; +import { Metadata } from "next"; + +export const metadata: Metadata = { + title: "GitLab Authentication - God Mode", +}; + +export default function GitlabAuthenticationLayout({ children }: { children: ReactNode }) { + return <>{children}; +} diff --git a/admin/app/authentication/gitlab/page.tsx b/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx similarity index 96% rename from admin/app/authentication/gitlab/page.tsx rename to admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx index 7a4d8248ef2..cdcfcd61bbc 100644 --- a/admin/app/authentication/gitlab/page.tsx +++ b/admin/app/(all)/(dashboard)/authentication/gitlab/page.tsx @@ -6,8 +6,7 @@ import Image from "next/image"; import useSWR from "swr"; import { Loader, ToggleSwitch, setPromiseToast } from "@plane/ui"; // components -import { AuthenticationMethodCard } from "@/components/authentication"; -import { PageHeader } from "@/components/common"; +import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card"; // hooks import { useInstance } from "@/hooks/store"; // icons @@ -57,7 +56,6 @@ const InstanceGitlabAuthenticationPage = observer(() => { }; return ( <> -
{children}; +} diff --git a/admin/app/authentication/google/page.tsx b/admin/app/(all)/(dashboard)/authentication/google/page.tsx similarity index 96% rename from admin/app/authentication/google/page.tsx rename to admin/app/(all)/(dashboard)/authentication/google/page.tsx index 992c7a8a7a1..6ac4ea09b9b 100644 --- a/admin/app/authentication/google/page.tsx +++ b/admin/app/(all)/(dashboard)/authentication/google/page.tsx @@ -6,8 +6,7 @@ import Image from "next/image"; import useSWR from "swr"; import { Loader, ToggleSwitch, setPromiseToast } from "@plane/ui"; // components -import { AuthenticationMethodCard } from "@/components/authentication"; -import { PageHeader } from "@/components/common"; +import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card"; // hooks import { useInstance } from "@/hooks/store"; // icons @@ -57,7 +56,6 @@ const InstanceGoogleAuthenticationPage = observer(() => { }; return ( <> -
{children}; + return <>{children}; } diff --git a/admin/app/authentication/page.tsx b/admin/app/(all)/(dashboard)/authentication/page.tsx similarity index 100% rename from admin/app/authentication/page.tsx rename to admin/app/(all)/(dashboard)/authentication/page.tsx diff --git a/admin/app/email/email-config-form.tsx b/admin/app/(all)/(dashboard)/email/email-config-form.tsx similarity index 99% rename from admin/app/email/email-config-form.tsx rename to admin/app/(all)/(dashboard)/email/email-config-form.tsx index 73a1af17442..de7f33f5d82 100644 --- a/admin/app/email/email-config-form.tsx +++ b/admin/app/(all)/(dashboard)/email/email-config-form.tsx @@ -7,7 +7,7 @@ import { IFormattedInstanceConfiguration, TInstanceEmailConfigurationKeys } from // ui import { Button, CustomSelect, TOAST_TYPE, setToast } from "@plane/ui"; // components -import { ControllerInput, TControllerInputFormField } from "@/components/common"; +import { ControllerInput, TControllerInputFormField } from "@/components/common/controller-input"; // hooks import { useInstance } from "@/hooks/store"; // local components diff --git a/admin/app/email/layout.tsx b/admin/app/(all)/(dashboard)/email/layout.tsx similarity index 62% rename from admin/app/email/layout.tsx rename to admin/app/(all)/(dashboard)/email/layout.tsx index 2084af1ea41..cb321295107 100644 --- a/admin/app/email/layout.tsx +++ b/admin/app/(all)/(dashboard)/email/layout.tsx @@ -1,15 +1,14 @@ import { ReactNode } from "react"; import { Metadata } from "next"; -import { AdminLayout } from "@/layouts/admin-layout"; interface EmailLayoutProps { children: ReactNode; } export const metadata: Metadata = { - title: "Email Settings - Plane Web", + title: "Email Settings - God Mode", }; export default function EmailLayout({ children }: EmailLayoutProps) { - return {children}; + return <>{children}; } diff --git a/admin/app/email/page.tsx b/admin/app/(all)/(dashboard)/email/page.tsx similarity index 100% rename from admin/app/email/page.tsx rename to admin/app/(all)/(dashboard)/email/page.tsx diff --git a/admin/app/email/test-email-modal.tsx b/admin/app/(all)/(dashboard)/email/test-email-modal.tsx similarity index 100% rename from admin/app/email/test-email-modal.tsx rename to admin/app/(all)/(dashboard)/email/test-email-modal.tsx diff --git a/admin/app/general/form.tsx b/admin/app/(all)/(dashboard)/general/form.tsx similarity index 98% rename from admin/app/general/form.tsx rename to admin/app/(all)/(dashboard)/general/form.tsx index 4fbd7053561..0700c4d0d5f 100644 --- a/admin/app/general/form.tsx +++ b/admin/app/(all)/(dashboard)/general/form.tsx @@ -8,7 +8,7 @@ import { IInstance, IInstanceAdmin } from "@plane/types"; // ui import { Button, Input, TOAST_TYPE, ToggleSwitch, setToast } from "@plane/ui"; // components -import { ControllerInput } from "@/components/common"; +import { ControllerInput } from "@/components/common/controller-input"; import { useInstance } from "@/hooks/store"; import { IntercomConfig } from "./intercom"; // hooks diff --git a/admin/app/general/intercom.tsx b/admin/app/(all)/(dashboard)/general/intercom.tsx similarity index 100% rename from admin/app/general/intercom.tsx rename to admin/app/(all)/(dashboard)/general/intercom.tsx diff --git a/admin/app/general/layout.tsx b/admin/app/(all)/(dashboard)/general/layout.tsx similarity index 57% rename from admin/app/general/layout.tsx rename to admin/app/(all)/(dashboard)/general/layout.tsx index 374257daade..af300051052 100644 --- a/admin/app/general/layout.tsx +++ b/admin/app/(all)/(dashboard)/general/layout.tsx @@ -1,11 +1,10 @@ import { ReactNode } from "react"; import { Metadata } from "next"; -import { AdminLayout } from "@/layouts/admin-layout"; export const metadata: Metadata = { - title: "General Settings - Plane Web", + title: "General Settings - God Mode", }; export default function GeneralLayout({ children }: { children: ReactNode }) { - return {children}; + return <>{children}; } diff --git a/admin/app/general/page.tsx b/admin/app/(all)/(dashboard)/general/page.tsx similarity index 100% rename from admin/app/general/page.tsx rename to admin/app/(all)/(dashboard)/general/page.tsx diff --git a/admin/core/components/auth-header.tsx b/admin/app/(all)/(dashboard)/header.tsx similarity index 77% rename from admin/core/components/auth-header.tsx rename to admin/app/(all)/(dashboard)/header.tsx index b97dd7c9eae..92bf1021034 100644 --- a/admin/core/components/auth-header.tsx +++ b/admin/app/(all)/(dashboard)/header.tsx @@ -3,16 +3,27 @@ import { FC } from "react"; import { observer } from "mobx-react"; import { usePathname } from "next/navigation"; -// mobx -// ui -import { Settings } from "lucide-react"; +import { Menu, Settings } from "lucide-react"; // icons import { Breadcrumbs } from "@plane/ui"; // components -import { SidebarHamburgerToggle } from "@/components/admin-sidebar"; -import { BreadcrumbLink } from "@/components/common"; +import { BreadcrumbLink } from "@/components/common/breadcrumb-link"; +// hooks +import { useTheme } from "@/hooks/store"; -export const InstanceHeader: FC = observer(() => { +export const HamburgerToggle: FC = observer(() => { + const { isSidebarCollapsed, toggleSidebar } = useTheme(); + return ( +
toggleSidebar(!isSidebarCollapsed)} + > + +
+ ); +}); + +export const AdminHeader: FC = observer(() => { const pathName = usePathname(); const getHeaderTitle = (pathName: string) => { @@ -63,7 +74,7 @@ export const InstanceHeader: FC = observer(() => { return (
- + {breadcrumbItems.length >= 0 && (
diff --git a/admin/app/image/form.tsx b/admin/app/(all)/(dashboard)/image/form.tsx similarity index 97% rename from admin/app/image/form.tsx rename to admin/app/(all)/(dashboard)/image/form.tsx index 61d2875edaa..be77983ec91 100644 --- a/admin/app/image/form.tsx +++ b/admin/app/(all)/(dashboard)/image/form.tsx @@ -4,7 +4,7 @@ import { useForm } from "react-hook-form"; import { IFormattedInstanceConfiguration, TInstanceImageConfigurationKeys } from "@plane/types"; import { Button, TOAST_TYPE, setToast } from "@plane/ui"; // components -import { ControllerInput } from "@/components/common"; +import { ControllerInput } from "@/components/common/controller-input"; // hooks import { useInstance } from "@/hooks/store"; diff --git a/admin/app/image/layout.tsx b/admin/app/(all)/(dashboard)/image/layout.tsx similarity index 62% rename from admin/app/image/layout.tsx rename to admin/app/(all)/(dashboard)/image/layout.tsx index 32233e07837..7ec0ff54b7c 100644 --- a/admin/app/image/layout.tsx +++ b/admin/app/(all)/(dashboard)/image/layout.tsx @@ -1,15 +1,14 @@ import { ReactNode } from "react"; import { Metadata } from "next"; -import { AdminLayout } from "@/layouts/admin-layout"; interface ImageLayoutProps { children: ReactNode; } export const metadata: Metadata = { - title: "Images Settings - Plane Web", + title: "Images Settings - God Mode", }; export default function ImageLayout({ children }: ImageLayoutProps) { - return {children}; + return <>{children}; } diff --git a/admin/app/image/page.tsx b/admin/app/(all)/(dashboard)/image/page.tsx similarity index 100% rename from admin/app/image/page.tsx rename to admin/app/(all)/(dashboard)/image/page.tsx diff --git a/admin/core/layouts/admin-layout.tsx b/admin/app/(all)/(dashboard)/layout.tsx similarity index 52% rename from admin/core/layouts/admin-layout.tsx rename to admin/app/(all)/(dashboard)/layout.tsx index 88f71aa3c4a..17962378375 100644 --- a/admin/core/layouts/admin-layout.tsx +++ b/admin/app/(all)/(dashboard)/layout.tsx @@ -1,20 +1,22 @@ "use client"; + import { FC, ReactNode, useEffect } from "react"; import { observer } from "mobx-react"; import { useRouter } from "next/navigation"; // components -import { InstanceSidebar } from "@/components/admin-sidebar"; -import { InstanceHeader } from "@/components/auth-header"; -import { LogoSpinner } from "@/components/common"; +import { LogoSpinner } from "@/components/common/logo-spinner"; import { NewUserPopup } from "@/components/new-user-popup"; // hooks import { useUser } from "@/hooks/store"; +// local components +import { AdminHeader } from "./header"; +import { AdminSidebar } from "./sidebar"; type TAdminLayout = { children: ReactNode; }; -export const AdminLayout: FC = observer((props) => { +const AdminLayout: FC = (props) => { const { children } = props; // router const router = useRouter(); @@ -35,14 +37,20 @@ export const AdminLayout: FC = observer((props) => { ); } - return ( -
- -
- -
{children}
-
- -
- ); -}); + if (isUserLoggedIn) { + return ( +
+ +
+ +
{children}
+
+ +
+ ); + } + + return <>; +}; + +export default observer(AdminLayout); diff --git a/admin/core/components/admin-sidebar/sidebar-dropdown.tsx b/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx similarity index 99% rename from admin/core/components/admin-sidebar/sidebar-dropdown.tsx rename to admin/app/(all)/(dashboard)/sidebar-dropdown.tsx index 0cde7f5519d..cdce94f0117 100644 --- a/admin/core/components/admin-sidebar/sidebar-dropdown.tsx +++ b/admin/app/(all)/(dashboard)/sidebar-dropdown.tsx @@ -16,7 +16,7 @@ import { useTheme, useUser } from "@/hooks/store"; // service initialization const authService = new AuthService(); -export const SidebarDropdown = observer(() => { +export const AdminSidebarDropdown = observer(() => { // store hooks const { isSidebarCollapsed } = useTheme(); const { currentUser, signOut } = useUser(); diff --git a/admin/core/components/admin-sidebar/help-section.tsx b/admin/app/(all)/(dashboard)/sidebar-help-section.tsx similarity index 98% rename from admin/core/components/admin-sidebar/help-section.tsx rename to admin/app/(all)/(dashboard)/sidebar-help-section.tsx index d776477497b..cedc735a91e 100644 --- a/admin/core/components/admin-sidebar/help-section.tsx +++ b/admin/app/(all)/(dashboard)/sidebar-help-section.tsx @@ -33,7 +33,7 @@ const helpOptions = [ }, ]; -export const HelpSection: FC = observer(() => { +export const AdminSidebarHelpSection: FC = observer(() => { // states const [isNeedHelpOpen, setIsNeedHelpOpen] = useState(false); // store diff --git a/admin/core/components/admin-sidebar/sidebar-menu.tsx b/admin/app/(all)/(dashboard)/sidebar-menu.tsx similarity index 98% rename from admin/core/components/admin-sidebar/sidebar-menu.tsx rename to admin/app/(all)/(dashboard)/sidebar-menu.tsx index 618551ae65c..e536a51454f 100644 --- a/admin/core/components/admin-sidebar/sidebar-menu.tsx +++ b/admin/app/(all)/(dashboard)/sidebar-menu.tsx @@ -49,7 +49,7 @@ const INSTANCE_ADMIN_LINKS = [ }, ]; -export const SidebarMenu = observer(() => { +export const AdminSidebarMenu = observer(() => { // store hooks const { isSidebarCollapsed, toggleSidebar } = useTheme(); // router diff --git a/admin/core/components/admin-sidebar/root.tsx b/admin/app/(all)/(dashboard)/sidebar.tsx similarity index 81% rename from admin/core/components/admin-sidebar/root.tsx rename to admin/app/(all)/(dashboard)/sidebar.tsx index 05dde0d8ab1..09dab86eef2 100644 --- a/admin/core/components/admin-sidebar/root.tsx +++ b/admin/app/(all)/(dashboard)/sidebar.tsx @@ -4,12 +4,14 @@ import { FC, useEffect, useRef } from "react"; import { observer } from "mobx-react"; // plane helpers import { useOutsideClickDetector } from "@plane/hooks"; -// components -import { HelpSection, SidebarMenu, SidebarDropdown } from "@/components/admin-sidebar"; // hooks import { useTheme } from "@/hooks/store"; +// components +import { AdminSidebarDropdown } from "./sidebar-dropdown"; +import { AdminSidebarHelpSection } from "./sidebar-help-section"; +import { AdminSidebarMenu } from "./sidebar-menu"; -export const InstanceSidebar: FC = observer(() => { +export const AdminSidebar: FC = observer(() => { // store const { isSidebarCollapsed, toggleSidebar } = useTheme(); @@ -47,9 +49,9 @@ export const InstanceSidebar: FC = observer(() => { `} >
- - - + + +
); diff --git a/admin/app/workspace/create/form.tsx b/admin/app/(all)/(dashboard)/workspace/create/form.tsx similarity index 100% rename from admin/app/workspace/create/form.tsx rename to admin/app/(all)/(dashboard)/workspace/create/form.tsx diff --git a/admin/app/workspace/create/page.tsx b/admin/app/(all)/(dashboard)/workspace/create/page.tsx similarity index 100% rename from admin/app/workspace/create/page.tsx rename to admin/app/(all)/(dashboard)/workspace/create/page.tsx diff --git a/admin/app/workspace/layout.tsx b/admin/app/(all)/(dashboard)/workspace/layout.tsx similarity index 56% rename from admin/app/workspace/layout.tsx rename to admin/app/(all)/(dashboard)/workspace/layout.tsx index 9f2a63c67d5..78b0f3c4036 100644 --- a/admin/app/workspace/layout.tsx +++ b/admin/app/(all)/(dashboard)/workspace/layout.tsx @@ -1,12 +1,10 @@ import { ReactNode } from "react"; import { Metadata } from "next"; -// layouts -import { AdminLayout } from "@/layouts/admin-layout"; export const metadata: Metadata = { - title: "Workspace Management - Plane Web", + title: "Workspace Management - God Mode", }; export default function WorkspaceManagementLayout({ children }: { children: ReactNode }) { - return {children}; + return <>{children}; } diff --git a/admin/app/workspace/page.tsx b/admin/app/(all)/(dashboard)/workspace/page.tsx similarity index 98% rename from admin/app/workspace/page.tsx rename to admin/app/(all)/(dashboard)/workspace/page.tsx index 3ca34b69e39..b8f79db04a6 100644 --- a/admin/app/workspace/page.tsx +++ b/admin/app/(all)/(dashboard)/workspace/page.tsx @@ -10,7 +10,7 @@ import { TInstanceConfigurationKeys } from "@plane/types"; import { Button, getButtonStyling, Loader, setPromiseToast, ToggleSwitch } from "@plane/ui"; import { cn } from "@plane/utils"; // components -import { WorkspaceListItem } from "@/components/workspace"; +import { WorkspaceListItem } from "@/components/workspace/list-item"; // hooks import { useInstance, useWorkspace } from "@/hooks/store"; diff --git a/admin/core/components/authentication/auth-banner.tsx b/admin/app/(all)/(home)/auth-banner.tsx similarity index 100% rename from admin/core/components/authentication/auth-banner.tsx rename to admin/app/(all)/(home)/auth-banner.tsx diff --git a/admin/core/lib/auth-helpers.tsx b/admin/app/(all)/(home)/auth-helpers.tsx similarity index 93% rename from admin/core/lib/auth-helpers.tsx rename to admin/app/(all)/(home)/auth-helpers.tsx index f9882b5e512..7613548b969 100644 --- a/admin/core/lib/auth-helpers.tsx +++ b/admin/app/(all)/(home)/auth-helpers.tsx @@ -7,13 +7,11 @@ import { SUPPORT_EMAIL, EAdminAuthErrorCodes, TAdminAuthErrorInfo } from "@plane import { TGetBaseAuthenticationModeProps, TInstanceAuthenticationModes } from "@plane/types"; import { resolveGeneralTheme } from "@plane/utils"; // components -import { - EmailCodesConfiguration, - GithubConfiguration, - GitlabConfiguration, - GoogleConfiguration, - PasswordLoginConfiguration, -} from "@/components/authentication"; +import { EmailCodesConfiguration } from "@/components/authentication/email-config-switch"; +import { GithubConfiguration } from "@/components/authentication/github-config"; +import { GitlabConfiguration } from "@/components/authentication/gitlab-config"; +import { GoogleConfiguration } from "@/components/authentication/google-config"; +import { PasswordLoginConfiguration } from "@/components/authentication/password-config-switch"; // images import githubLightModeImage from "@/public/logos/github-black.png"; import githubDarkModeImage from "@/public/logos/github-white.png"; diff --git a/admin/core/layouts/default-layout.tsx b/admin/app/(all)/(home)/layout.tsx similarity index 65% rename from admin/core/layouts/default-layout.tsx rename to admin/app/(all)/(home)/layout.tsx index 1be40ea1296..19cab04cb01 100644 --- a/admin/core/layouts/default-layout.tsx +++ b/admin/app/(all)/(home)/layout.tsx @@ -1,26 +1,18 @@ "use client"; -import { FC, ReactNode } from "react"; import Image from "next/image"; import Link from "next/link"; import { useTheme } from "next-themes"; -// logo/ images +// logo assets import PlaneBackgroundPatternDark from "public/auth/background-pattern-dark.svg"; import PlaneBackgroundPattern from "public/auth/background-pattern.svg"; import BlackHorizontalLogo from "public/plane-logos/black-horizontal-with-blue-logo.png"; import WhiteHorizontalLogo from "public/plane-logos/white-horizontal-with-blue-logo.png"; -type TDefaultLayout = { - children: ReactNode; - withoutBackground?: boolean; -}; - -export const DefaultLayout: FC = (props) => { - const { children, withoutBackground = false } = props; - // hooks +export default function RootLayout({ children }: { children: React.ReactNode }) { const { resolvedTheme } = useTheme(); - const patternBackground = resolvedTheme === "dark" ? PlaneBackgroundPatternDark : PlaneBackgroundPattern; + const patternBackground = resolvedTheme === "light" ? PlaneBackgroundPattern : PlaneBackgroundPatternDark; const logo = resolvedTheme === "light" ? BlackHorizontalLogo : WhiteHorizontalLogo; return ( @@ -33,13 +25,11 @@ export const DefaultLayout: FC = (props) => {
- {!withoutBackground && ( -
- Plane background pattern -
- )} +
+ Plane background pattern +
{children}
); -}; +} diff --git a/admin/app/(all)/(home)/page.tsx b/admin/app/(all)/(home)/page.tsx new file mode 100644 index 00000000000..80ea40ee61e --- /dev/null +++ b/admin/app/(all)/(home)/page.tsx @@ -0,0 +1,62 @@ +"use client"; + +import { observer } from "mobx-react"; +// components +import { InstanceFailureView } from "@/components/instance/failure"; +import { InstanceLoading } from "@/components/instance/loading"; +import { InstanceSetupForm } from "@/components/instance/setup-form"; +// hooks +import { useInstance } from "@/hooks/store"; +// components +import { InstanceSignInForm } from "./sign-in-form"; + +const HomePage = () => { + // store hooks + const { instance, error } = useInstance(); + + // if instance is not fetched, show loading + if (!instance && !error) { + return ( +
+ +
+ ); + } + + // if instance fetch fails, show failure view + if (error) { + return ( +
+ +
+ ); + } + + // if instance is fetched and setup is not done, show setup form + if (instance && !instance?.is_setup_done) { + return ( +
+ +
+ ); + } + + // if instance is fetched and setup is done, show sign in form + return ( +
+
+
+

+ Manage your Plane instance +

+

+ Configure instance-wide settings to secure your instance +

+
+ +
+
+ ); +}; + +export default observer(HomePage); diff --git a/admin/app/(all)/(home)/sign-in-form.tsx b/admin/app/(all)/(home)/sign-in-form.tsx new file mode 100644 index 00000000000..12b250a93b9 --- /dev/null +++ b/admin/app/(all)/(home)/sign-in-form.tsx @@ -0,0 +1,178 @@ +"use client"; + +import { FC, useEffect, useMemo, useState } from "react"; +import { useSearchParams } from "next/navigation"; +import { Eye, EyeOff } from "lucide-react"; +// plane internal packages +import { API_BASE_URL, EAdminAuthErrorCodes, TAdminAuthErrorInfo } from "@plane/constants"; +import { AuthService } from "@plane/services"; +import { Button, Input, Spinner } from "@plane/ui"; +// components +import { Banner } from "@/components/common/banner"; +// local components +import { AuthBanner } from "./auth-banner"; +import { authErrorHandler } from "./auth-helpers"; + +// service initialization +const authService = new AuthService(); + +// error codes +enum EErrorCodes { + INSTANCE_NOT_CONFIGURED = "INSTANCE_NOT_CONFIGURED", + REQUIRED_EMAIL_PASSWORD = "REQUIRED_EMAIL_PASSWORD", + INVALID_EMAIL = "INVALID_EMAIL", + USER_DOES_NOT_EXIST = "USER_DOES_NOT_EXIST", + AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED", +} + +type TError = { + type: EErrorCodes | undefined; + message: string | undefined; +}; + +// form data +type TFormData = { + email: string; + password: string; +}; + +const defaultFromData: TFormData = { + email: "", + password: "", +}; + +export const InstanceSignInForm: FC = () => { + // search params + const searchParams = useSearchParams(); + const emailParam = searchParams.get("email") || undefined; + const errorCode = searchParams.get("error_code") || undefined; + const errorMessage = searchParams.get("error_message") || undefined; + // state + const [showPassword, setShowPassword] = useState(false); + const [csrfToken, setCsrfToken] = useState(undefined); + const [formData, setFormData] = useState(defaultFromData); + const [isSubmitting, setIsSubmitting] = useState(false); + const [errorInfo, setErrorInfo] = useState(undefined); + + const handleFormChange = (key: keyof TFormData, value: string | boolean) => + setFormData((prev) => ({ ...prev, [key]: value })); + + useEffect(() => { + if (csrfToken === undefined) + authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token)); + }, [csrfToken]); + + useEffect(() => { + if (emailParam) setFormData((prev) => ({ ...prev, email: emailParam })); + }, [emailParam]); + + // derived values + const errorData: TError = useMemo(() => { + if (errorCode && errorMessage) { + switch (errorCode) { + case EErrorCodes.INSTANCE_NOT_CONFIGURED: + return { type: EErrorCodes.INSTANCE_NOT_CONFIGURED, message: errorMessage }; + case EErrorCodes.REQUIRED_EMAIL_PASSWORD: + return { type: EErrorCodes.REQUIRED_EMAIL_PASSWORD, message: errorMessage }; + case EErrorCodes.INVALID_EMAIL: + return { type: EErrorCodes.INVALID_EMAIL, message: errorMessage }; + case EErrorCodes.USER_DOES_NOT_EXIST: + return { type: EErrorCodes.USER_DOES_NOT_EXIST, message: errorMessage }; + case EErrorCodes.AUTHENTICATION_FAILED: + return { type: EErrorCodes.AUTHENTICATION_FAILED, message: errorMessage }; + default: + return { type: undefined, message: undefined }; + } + } else return { type: undefined, message: undefined }; + }, [errorCode, errorMessage]); + + const isButtonDisabled = useMemo( + () => (!isSubmitting && formData.email && formData.password ? false : true), + [formData.email, formData.password, isSubmitting] + ); + + useEffect(() => { + if (errorCode) { + const errorDetail = authErrorHandler(errorCode?.toString() as EAdminAuthErrorCodes); + if (errorDetail) { + setErrorInfo(errorDetail); + } + } + }, [errorCode]); + + return ( +
setIsSubmitting(true)} + onError={() => setIsSubmitting(false)} + > + {errorData.type && errorData?.message ? ( + + ) : ( + <>{errorInfo && setErrorInfo(value)} />} + )} + + +
+ + handleFormChange("email", e.target.value)} + autoComplete="on" + autoFocus + /> +
+ +
+ +
+ handleFormChange("password", e.target.value)} + autoComplete="on" + /> + {showPassword ? ( + + ) : ( + + )} +
+
+
+ +
+ + ); +}; diff --git a/admin/app/(all)/instance.provider.tsx b/admin/app/(all)/instance.provider.tsx new file mode 100644 index 00000000000..ac8fa74e82c --- /dev/null +++ b/admin/app/(all)/instance.provider.tsx @@ -0,0 +1,23 @@ +import { FC, ReactNode } from "react"; +import { observer } from "mobx-react"; +import useSWR from "swr"; +// hooks +import { useInstance } from "@/hooks/store"; + +type InstanceProviderProps = { + children: ReactNode; +}; + +export const InstanceProvider: FC = observer((props) => { + const { children } = props; + // store hooks + const { fetchInstanceInfo } = useInstance(); + // fetching instance details + useSWR("INSTANCE_DETAILS", () => fetchInstanceInfo(), { + revalidateOnFocus: false, + revalidateIfStale: false, + errorRetryCount: 0, + }); + + return <>{children}; +}); diff --git a/admin/app/(all)/layout.tsx b/admin/app/(all)/layout.tsx new file mode 100644 index 00000000000..ddfba732a91 --- /dev/null +++ b/admin/app/(all)/layout.tsx @@ -0,0 +1,33 @@ +"use client"; + +import { ThemeProvider } from "next-themes"; +import { SWRConfig } from "swr"; +// providers +import { InstanceProvider } from "./instance.provider"; +import { StoreProvider } from "./store.provider"; +import { ToastWithTheme } from "./toast"; +import { UserProvider } from "./user.provider"; + +const DEFAULT_SWR_CONFIG = { + refreshWhenHidden: false, + revalidateIfStale: false, + revalidateOnFocus: false, + revalidateOnMount: true, + refreshInterval: 600000, + errorRetryCount: 3, +}; + +export default function InstanceLayout({ children }: { children: React.ReactNode }) { + return ( + + + + + + {children} + + + + + ); +} diff --git a/admin/core/lib/store-provider.tsx b/admin/app/(all)/store.provider.tsx similarity index 100% rename from admin/core/lib/store-provider.tsx rename to admin/app/(all)/store.provider.tsx diff --git a/admin/app/(all)/toast.tsx b/admin/app/(all)/toast.tsx new file mode 100644 index 00000000000..7d7938a9b16 --- /dev/null +++ b/admin/app/(all)/toast.tsx @@ -0,0 +1,10 @@ +"use client"; + +import { useTheme } from "next-themes"; +import { Toast } from "@plane/ui"; +import { resolveGeneralTheme } from "@plane/utils"; + +export const ToastWithTheme = () => { + const { resolvedTheme } = useTheme(); + return ; +}; diff --git a/admin/core/lib/user-provider.tsx b/admin/app/(all)/user.provider.tsx similarity index 99% rename from admin/core/lib/user-provider.tsx rename to admin/app/(all)/user.provider.tsx index 17d70262797..3a50823dcb7 100644 --- a/admin/core/lib/user-provider.tsx +++ b/admin/app/(all)/user.provider.tsx @@ -19,6 +19,7 @@ export const UserProvider: FC = observer(({ children }) => { useSWR("CURRENT_USER", () => fetchCurrentUser(), { shouldRetryOnError: false, }); + useSWR("INSTANCE_ADMINS", () => fetchInstanceAdmins()); useEffect(() => { diff --git a/admin/app/layout.tsx b/admin/app/layout.tsx index b10e9186c3c..e735723695e 100644 --- a/admin/app/layout.tsx +++ b/admin/app/layout.tsx @@ -1,22 +1,25 @@ -"use client"; - import { ReactNode } from "react"; -import { ThemeProvider, useTheme } from "next-themes"; -import { SWRConfig } from "swr"; +import { Metadata } from "next"; // plane imports -import { ADMIN_BASE_PATH, DEFAULT_SWR_CONFIG } from "@plane/constants"; -import { Toast } from "@plane/ui"; -import { resolveGeneralTheme } from "@plane/utils"; -// lib -import { InstanceProvider } from "@/lib/instance-provider"; -import { StoreProvider } from "@/lib/store-provider"; -import { UserProvider } from "@/lib/user-provider"; +import { ADMIN_BASE_PATH } from "@plane/constants"; // styles import "@/styles/globals.css"; -const ToastWithTheme = () => { - const { resolvedTheme } = useTheme(); - return ; +export const metadata: Metadata = { + title: "Plane | Simple, extensible, open-source project management tool.", + description: + "Open-source project management tool to manage work items, sprints, and product roadmaps with peace of mind.", + openGraph: { + title: "Plane | Simple, extensible, open-source project management tool.", + description: + "Open-source project management tool to manage work items, sprints, and product roadmaps with peace of mind.", + url: "https://plane.so/", + }, + keywords: + "software development, customer feedback, software, accelerate, code management, release management, project management, work items tracking, agile, scrum, kanban, collaboration", + twitter: { + site: "@planepowers", + }, }; export default function RootLayout({ children }: { children: ReactNode }) { @@ -30,18 +33,7 @@ export default function RootLayout({ children }: { children: ReactNode }) { - - - - - - - {children} - - - - - + {children} ); } diff --git a/admin/app/page.tsx b/admin/app/page.tsx deleted file mode 100644 index 1a274025a0a..00000000000 --- a/admin/app/page.tsx +++ /dev/null @@ -1,30 +0,0 @@ -import { Metadata } from "next"; -// components -import { InstanceSignInForm } from "@/components/login"; -// layouts -import { DefaultLayout } from "@/layouts/default-layout"; - -export const metadata: Metadata = { - title: "Plane | Simple, extensible, open-source project management tool.", - description: - "Open-source project management tool to manage work items, sprints, and product roadmaps with peace of mind.", - openGraph: { - title: "Plane | Simple, extensible, open-source project management tool.", - description: - "Open-source project management tool to manage work items, sprints, and product roadmaps with peace of mind.", - url: "https://plane.so/", - }, - keywords: - "software development, customer feedback, software, accelerate, code management, release management, project management, work items tracking, agile, scrum, kanban, collaboration", - twitter: { - site: "@planepowers", - }, -}; - -export default async function LoginPage() { - return ( - - - - ); -} diff --git a/admin/ce/components/authentication/authentication-modes.tsx b/admin/ce/components/authentication/authentication-modes.tsx index 3c7ec111a33..c9001670292 100644 --- a/admin/ce/components/authentication/authentication-modes.tsx +++ b/admin/ce/components/authentication/authentication-modes.tsx @@ -1,19 +1,28 @@ import { observer } from "mobx-react"; import Image from "next/image"; import { useTheme } from "next-themes"; +import { KeyRound, Mails } from "lucide-react"; // types import { TGetBaseAuthenticationModeProps, TInstanceAuthenticationMethodKeys, TInstanceAuthenticationModes, } from "@plane/types"; +import { resolveGeneralTheme } from "@plane/utils"; // components -import { AuthenticationMethodCard } from "@/components/authentication"; -// helpers -import { getBaseAuthenticationModes } from "@/lib/auth-helpers"; +import { AuthenticationMethodCard } from "@/components/authentication/authentication-method-card"; +import { EmailCodesConfiguration } from "@/components/authentication/email-config-switch"; +import { GithubConfiguration } from "@/components/authentication/github-config"; +import { GitlabConfiguration } from "@/components/authentication/gitlab-config"; +import { GoogleConfiguration } from "@/components/authentication/google-config"; +import { PasswordLoginConfiguration } from "@/components/authentication/password-config-switch"; // plane admin components import { UpgradeButton } from "@/plane-admin/components/common"; -// images +// assets +import githubLightModeImage from "@/public/logos/github-black.png"; +import githubDarkModeImage from "@/public/logos/github-white.png"; +import GitlabLogo from "@/public/logos/gitlab-logo.svg"; +import GoogleLogo from "@/public/logos/google-logo.svg"; import OIDCLogo from "@/public/logos/oidc-logo.svg"; import SAMLLogo from "@/public/logos/saml-logo.svg"; @@ -28,7 +37,49 @@ export const getAuthenticationModes: (props: TGetBaseAuthenticationModeProps) => updateConfig, resolvedTheme, }) => [ - ...getBaseAuthenticationModes({ disabled, updateConfig, resolvedTheme }), + { + key: "unique-codes", + name: "Unique codes", + description: + "Log in or sign up for Plane using codes sent via email. You need to have set up SMTP to use this method.", + icon: , + config: , + }, + { + key: "passwords-login", + name: "Passwords", + description: "Allow members to create accounts with passwords and use it with their email addresses to sign in.", + icon: , + config: , + }, + { + key: "google", + name: "Google", + description: "Allow members to log in or sign up for Plane with their Google accounts.", + icon: Google Logo, + config: , + }, + { + key: "github", + name: "GitHub", + description: "Allow members to log in or sign up for Plane with their GitHub accounts.", + icon: ( + GitHub Logo + ), + config: , + }, + { + key: "gitlab", + name: "GitLab", + description: "Allow members to log in or sign up to plane with their GitLab accounts.", + icon: GitLab Logo, + config: , + }, { key: "oidc", name: "OIDC", diff --git a/admin/core/components/admin-sidebar/index.ts b/admin/core/components/admin-sidebar/index.ts deleted file mode 100644 index e800fe3c5c0..00000000000 --- a/admin/core/components/admin-sidebar/index.ts +++ /dev/null @@ -1,5 +0,0 @@ -export * from "./root"; -export * from "./help-section"; -export * from "./sidebar-menu"; -export * from "./sidebar-dropdown"; -export * from "./sidebar-menu-hamburger-toogle"; diff --git a/admin/core/components/admin-sidebar/sidebar-menu-hamburger-toogle.tsx b/admin/core/components/admin-sidebar/sidebar-menu-hamburger-toogle.tsx deleted file mode 100644 index 337d9baaf34..00000000000 --- a/admin/core/components/admin-sidebar/sidebar-menu-hamburger-toogle.tsx +++ /dev/null @@ -1,20 +0,0 @@ -"use client"; - -import { FC } from "react"; -import { observer } from "mobx-react"; -// hooks -import { Menu } from "lucide-react"; -import { useTheme } from "@/hooks/store"; -// icons - -export const SidebarHamburgerToggle: FC = observer(() => { - const { isSidebarCollapsed, toggleSidebar } = useTheme(); - return ( -
toggleSidebar(!isSidebarCollapsed)} - > - -
- ); -}); diff --git a/admin/core/components/authentication/index.ts b/admin/core/components/authentication/index.ts deleted file mode 100644 index d189a727ba6..00000000000 --- a/admin/core/components/authentication/index.ts +++ /dev/null @@ -1,7 +0,0 @@ -export * from "./auth-banner"; -export * from "./email-config-switch"; -export * from "./password-config-switch"; -export * from "./authentication-method-card"; -export * from "./gitlab-config"; -export * from "./github-config"; -export * from "./google-config"; diff --git a/admin/core/components/common/index.ts b/admin/core/components/common/index.ts deleted file mode 100644 index 4d664b0a4aa..00000000000 --- a/admin/core/components/common/index.ts +++ /dev/null @@ -1,10 +0,0 @@ -export * from "./breadcrumb-link"; -export * from "./confirm-discard-modal"; -export * from "./controller-input"; -export * from "./copy-field"; -export * from "./password-strength-meter"; -export * from "./banner"; -export * from "./empty-state"; -export * from "./logo-spinner"; -export * from "./page-header"; -export * from "./code-block"; diff --git a/admin/core/components/instance/instance-failure-view.tsx b/admin/core/components/instance/failure.tsx similarity index 100% rename from admin/core/components/instance/instance-failure-view.tsx rename to admin/core/components/instance/failure.tsx diff --git a/admin/core/components/instance/index.ts b/admin/core/components/instance/index.ts deleted file mode 100644 index 56d1933f419..00000000000 --- a/admin/core/components/instance/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export * from "./instance-not-ready"; -export * from "./instance-failure-view"; -export * from "./setup-form"; diff --git a/admin/core/components/instance/loading.tsx b/admin/core/components/instance/loading.tsx new file mode 100644 index 00000000000..a21319d9ee2 --- /dev/null +++ b/admin/core/components/instance/loading.tsx @@ -0,0 +1,21 @@ +import Image from "next/image"; +import { useTheme } from "next-themes"; +// assets +import LogoSpinnerDark from "@/public/images/logo-spinner-dark.gif"; +import LogoSpinnerLight from "@/public/images/logo-spinner-light.gif"; + +export const InstanceLoading = () => { + const { resolvedTheme } = useTheme(); + const logoSrc = resolvedTheme === "dark" ? LogoSpinnerDark : LogoSpinnerLight; + + return ( +
+
+
+ logo +

Fetching instance details...

+
+
+
+ ); +}; diff --git a/admin/core/components/instance/setup-form.tsx b/admin/core/components/instance/setup-form.tsx index fcc5c6c83df..4e771e91be8 100644 --- a/admin/core/components/instance/setup-form.tsx +++ b/admin/core/components/instance/setup-form.tsx @@ -10,7 +10,8 @@ import { AuthService } from "@plane/services"; import { Button, Checkbox, Input, Spinner } from "@plane/ui"; import { getPasswordStrength } from "@plane/utils"; // components -import { Banner, PasswordStrengthMeter } from "@/components/common"; +import { Banner } from "@/components/common/banner"; +import { PasswordStrengthMeter } from "@/components/common/password-strength-meter"; // service initialization const authService = new AuthService(); diff --git a/admin/core/components/login/index.ts b/admin/core/components/login/index.ts deleted file mode 100644 index bdeb387f3fb..00000000000 --- a/admin/core/components/login/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./sign-in-form"; diff --git a/admin/core/components/login/sign-in-form.tsx b/admin/core/components/login/sign-in-form.tsx deleted file mode 100644 index 553ffe6c56f..00000000000 --- a/admin/core/components/login/sign-in-form.tsx +++ /dev/null @@ -1,194 +0,0 @@ -"use client"; - -import { FC, useEffect, useMemo, useState } from "react"; -import { useSearchParams } from "next/navigation"; -import { Eye, EyeOff } from "lucide-react"; -// plane internal packages -import { API_BASE_URL, EAdminAuthErrorCodes, TAdminAuthErrorInfo } from "@plane/constants"; -import { AuthService } from "@plane/services"; -import { Button, Input, Spinner } from "@plane/ui"; -// components -import { Banner } from "@/components/common"; -// helpers -import { authErrorHandler } from "@/lib/auth-helpers"; -// local components -import { AuthBanner } from "../authentication"; - -// service initialization -const authService = new AuthService(); - -// error codes -enum EErrorCodes { - INSTANCE_NOT_CONFIGURED = "INSTANCE_NOT_CONFIGURED", - REQUIRED_EMAIL_PASSWORD = "REQUIRED_EMAIL_PASSWORD", - INVALID_EMAIL = "INVALID_EMAIL", - USER_DOES_NOT_EXIST = "USER_DOES_NOT_EXIST", - AUTHENTICATION_FAILED = "AUTHENTICATION_FAILED", -} - -type TError = { - type: EErrorCodes | undefined; - message: string | undefined; -}; - -// form data -type TFormData = { - email: string; - password: string; -}; - -const defaultFromData: TFormData = { - email: "", - password: "", -}; - -export const InstanceSignInForm: FC = (props) => { - const {} = props; - // search params - const searchParams = useSearchParams(); - const emailParam = searchParams.get("email") || undefined; - const errorCode = searchParams.get("error_code") || undefined; - const errorMessage = searchParams.get("error_message") || undefined; - // state - const [showPassword, setShowPassword] = useState(false); - const [csrfToken, setCsrfToken] = useState(undefined); - const [formData, setFormData] = useState(defaultFromData); - const [isSubmitting, setIsSubmitting] = useState(false); - const [errorInfo, setErrorInfo] = useState(undefined); - - const handleFormChange = (key: keyof TFormData, value: string | boolean) => - setFormData((prev) => ({ ...prev, [key]: value })); - - useEffect(() => { - if (csrfToken === undefined) - authService.requestCSRFToken().then((data) => data?.csrf_token && setCsrfToken(data.csrf_token)); - }, [csrfToken]); - - useEffect(() => { - if (emailParam) setFormData((prev) => ({ ...prev, email: emailParam })); - }, [emailParam]); - - // derived values - const errorData: TError = useMemo(() => { - if (errorCode && errorMessage) { - switch (errorCode) { - case EErrorCodes.INSTANCE_NOT_CONFIGURED: - return { type: EErrorCodes.INVALID_EMAIL, message: errorMessage }; - case EErrorCodes.REQUIRED_EMAIL_PASSWORD: - return { type: EErrorCodes.REQUIRED_EMAIL_PASSWORD, message: errorMessage }; - case EErrorCodes.INVALID_EMAIL: - return { type: EErrorCodes.INVALID_EMAIL, message: errorMessage }; - case EErrorCodes.USER_DOES_NOT_EXIST: - return { type: EErrorCodes.USER_DOES_NOT_EXIST, message: errorMessage }; - case EErrorCodes.AUTHENTICATION_FAILED: - return { type: EErrorCodes.AUTHENTICATION_FAILED, message: errorMessage }; - default: - return { type: undefined, message: undefined }; - } - } else return { type: undefined, message: undefined }; - }, [errorCode, errorMessage]); - - const isButtonDisabled = useMemo( - () => (!isSubmitting && formData.email && formData.password ? false : true), - [formData.email, formData.password, isSubmitting] - ); - - useEffect(() => { - if (errorCode) { - const errorDetail = authErrorHandler(errorCode?.toString() as EAdminAuthErrorCodes); - if (errorDetail) { - setErrorInfo(errorDetail); - } - } - }, [errorCode]); - - return ( -
-
-
-

- Manage your Plane instance -

-

- Configure instance-wide settings to secure your instance -

-
- - {errorData.type && errorData?.message ? ( - - ) : ( - <>{errorInfo && setErrorInfo(value)} />} - )} - -
setIsSubmitting(true)} - onError={() => setIsSubmitting(false)} - > - - -
- - handleFormChange("email", e.target.value)} - autoComplete="on" - autoFocus - /> -
- -
- -
- handleFormChange("password", e.target.value)} - autoComplete="on" - /> - {showPassword ? ( - - ) : ( - - )} -
-
-
- -
-
-
-
- ); -}; diff --git a/admin/core/components/workspace/index.ts b/admin/core/components/workspace/index.ts deleted file mode 100644 index 24950c4f20f..00000000000 --- a/admin/core/components/workspace/index.ts +++ /dev/null @@ -1 +0,0 @@ -export * from "./list-item"; diff --git a/admin/core/hooks/store/use-instance.tsx b/admin/core/hooks/store/use-instance.tsx index cf2edc39fa4..67ac3bca889 100644 --- a/admin/core/hooks/store/use-instance.tsx +++ b/admin/core/hooks/store/use-instance.tsx @@ -1,6 +1,6 @@ import { useContext } from "react"; // store -import { StoreContext } from "@/lib/store-provider"; +import { StoreContext } from "@/app/(all)/store.provider"; import { IInstanceStore } from "@/store/instance.store"; export const useInstance = (): IInstanceStore => { diff --git a/admin/core/hooks/store/use-theme.tsx b/admin/core/hooks/store/use-theme.tsx index bad89cfeeb5..0f07149b1db 100644 --- a/admin/core/hooks/store/use-theme.tsx +++ b/admin/core/hooks/store/use-theme.tsx @@ -1,6 +1,6 @@ import { useContext } from "react"; // store -import { StoreContext } from "@/lib/store-provider"; +import { StoreContext } from "@/app/(all)/store.provider"; import { IThemeStore } from "@/store/theme.store"; export const useTheme = (): IThemeStore => { diff --git a/admin/core/hooks/store/use-user.tsx b/admin/core/hooks/store/use-user.tsx index 8230031447a..eaf02862e26 100644 --- a/admin/core/hooks/store/use-user.tsx +++ b/admin/core/hooks/store/use-user.tsx @@ -1,6 +1,6 @@ import { useContext } from "react"; // store -import { StoreContext } from "@/lib/store-provider"; +import { StoreContext } from "@/app/(all)/store.provider"; import { IUserStore } from "@/store/user.store"; export const useUser = (): IUserStore => { diff --git a/admin/core/hooks/store/use-workspace.tsx b/admin/core/hooks/store/use-workspace.tsx index e3bde92d530..2203ec948ca 100644 --- a/admin/core/hooks/store/use-workspace.tsx +++ b/admin/core/hooks/store/use-workspace.tsx @@ -1,6 +1,6 @@ import { useContext } from "react"; // store -import { StoreContext } from "@/lib/store-provider"; +import { StoreContext } from "@/app/(all)/store.provider"; import { IWorkspaceStore } from "@/store/workspace.store"; export const useWorkspace = (): IWorkspaceStore => { diff --git a/admin/core/lib/instance-provider.tsx b/admin/core/lib/instance-provider.tsx deleted file mode 100644 index d021e3b83a2..00000000000 --- a/admin/core/lib/instance-provider.tsx +++ /dev/null @@ -1,55 +0,0 @@ -import { FC, ReactNode } from "react"; -import { observer } from "mobx-react"; -import useSWR from "swr"; -// components -import { LogoSpinner } from "@/components/common"; -import { InstanceSetupForm, InstanceFailureView } from "@/components/instance"; -// hooks -import { useInstance } from "@/hooks/store"; -// layout -import { DefaultLayout } from "@/layouts/default-layout"; - -type InstanceProviderProps = { - children: ReactNode; -}; - -export const InstanceProvider: FC = observer((props) => { - const { children } = props; - // store hooks - const { instance, error, fetchInstanceInfo } = useInstance(); - // fetching instance details - useSWR("INSTANCE_DETAILS", () => fetchInstanceInfo(), { - revalidateOnFocus: false, - revalidateIfStale: false, - errorRetryCount: 0, - }); - - if (!instance && !error) - return ( -
- -
- ); - - if (error) { - return ( - -
- -
-
- ); - } - - if (!instance?.is_setup_done) { - return ( - -
- -
-
- ); - } - - return <>{children}; -}); diff --git a/admin/core/store/instance.store.ts b/admin/core/store/instance.store.ts index 33954fe73a9..1f690f7080b 100644 --- a/admin/core/store/instance.store.ts +++ b/admin/core/store/instance.store.ts @@ -100,7 +100,7 @@ export class InstanceStore implements IInstanceStore { if (this.instance === undefined && !instanceInfo?.instance?.workspaces_exist) this.store.theme.toggleNewUserPopup(); runInAction(() => { - console.log("instanceInfo: ", instanceInfo); + // console.log("instanceInfo: ", instanceInfo); this.isLoading = false; this.instance = instanceInfo.instance; this.config = instanceInfo.config; diff --git a/admin/tsconfig.json b/admin/tsconfig.json index df72d07b406..d85abf2cc9a 100644 --- a/admin/tsconfig.json +++ b/admin/tsconfig.json @@ -8,6 +8,7 @@ ], "baseUrl": ".", "paths": { + "@/app/*": ["app/*"], "@/*": ["core/*"], "@/public/*": ["public/*"], "@/plane-admin/*": ["ce/*"], diff --git a/apps/space/next-env.d.ts b/apps/space/next-env.d.ts new file mode 100644 index 00000000000..40c3d68096c --- /dev/null +++ b/apps/space/next-env.d.ts @@ -0,0 +1,5 @@ +/// +/// + +// NOTE: This file should not be edited +// see https://nextjs.org/docs/app/building-your-application/configuring/typescript for more information. diff --git a/packages/eslint-config/next.js b/packages/eslint-config/next.js index 543cd131a42..a2da6601059 100644 --- a/packages/eslint-config/next.js +++ b/packages/eslint-config/next.js @@ -52,7 +52,7 @@ module.exports = { }, ], "import/order": [ - "error", + "warn", { groups: ["builtin", "external", "internal", "parent", "sibling"], pathGroups: [ @@ -80,6 +80,11 @@ module.exports = { pattern: "@/**", group: "internal", }, + { + pattern: "public/**", + group: "internal", + position: "after", + }, ], pathGroupsExcludedImportTypes: ["builtin", "internal", "react"], alphabetize: { diff --git a/packages/services/src/api.service.ts b/packages/services/src/api.service.ts index 619a0d4ec1f..e2b74993e27 100644 --- a/packages/services/src/api.service.ts +++ b/packages/services/src/api.service.ts @@ -1,6 +1,4 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ import axios, { AxiosInstance, AxiosRequestConfig } from "axios"; -import { IndexedDBService } from "./indexedDB.service"; /** * Abstract base class for making HTTP requests using axios @@ -20,38 +18,6 @@ export abstract class APIService { baseURL, withCredentials: true, }); - - this.setupInterceptors(); - } - - /** - * Sets up axios interceptors for handling responses - * Currently handles 401 unauthorized responses by redirecting to login - * @private - */ - private setupInterceptors() { - this.axiosInstance.interceptors.response.use( - (response) => response, - (error) => { - if (error.response && error.response.status === 401) { - const currentPath = window.location.pathname; - let prefix = "/"; - let updatedPath = currentPath; - - // Check for special path prefixes - if (currentPath.startsWith("/god-mode")) { - prefix = "/god-mode"; - updatedPath = currentPath.replace("/god-mode", ""); - } else if (currentPath.startsWith("/spaces")) { - prefix = "/spaces"; - updatedPath = currentPath.replace("/spaces", ""); - } - - window.location.replace(`${prefix}${updatedPath ? `?next_path=${updatedPath}` : ""}`); - } - return Promise.reject(error); - } - ); } /** diff --git a/packages/services/src/user/user.service.ts b/packages/services/src/user/user.service.ts index c302b1d4f13..33929766251 100644 --- a/packages/services/src/user/user.service.ts +++ b/packages/services/src/user/user.service.ts @@ -20,11 +20,10 @@ export class UserService extends APIService { /** * Retrieves the current user details - * @returns {Promise} Promise resolving to the current user details\ - * @remarks This method uses the validateStatus: null option to bypass interceptors for unauthorized errors. + * @returns {Promise} Promise resolving to the current user details */ async me(): Promise { - return this.get("/api/users/me/", { validateStatus: null }) + return this.get("/api/users/me/") .then((response) => response?.data) .catch((error) => { throw error?.response; @@ -76,10 +75,9 @@ export class UserService extends APIService { * Retrieves the current instance admin details * @returns {Promise} Promise resolving to the current instance admin details * @throws {Error} If the API request fails - * @remarks This method uses the validateStatus: null option to bypass interceptors for unauthorized errors. */ async adminDetails(): Promise { - return this.get("/api/instances/admins/me/", { validateStatus: null }) + return this.get("/api/instances/admins/me/") .then((response) => response?.data) .catch((error) => { throw error?.response?.data;