fix: instance config errors
This commit is contained in:
parent
895fbcd5a7
commit
31ca9e447d
35 changed files with 159 additions and 197 deletions
|
|
@ -1,16 +1,11 @@
|
|||
import { Metadata } from "next";
|
||||
// styles
|
||||
import "@/styles/globals.css";
|
||||
// components
|
||||
import { InstanceNotReady, InstanceFailureView } from "@/components/instance";
|
||||
// helpers
|
||||
import { ASSET_PREFIX } from "@/helpers/common.helper";
|
||||
// lib
|
||||
import { AppProvider } from "@/lib/app-providers";
|
||||
// services
|
||||
import { InstanceService } from "@/services/instance.service";
|
||||
|
||||
const instanceService = new InstanceService();
|
||||
// components
|
||||
import { InstanceProvider } from "@/lib/instance-provider";
|
||||
import { StoreProvider } from "@/lib/store-provider";
|
||||
// styles
|
||||
import "@/styles/globals.css";
|
||||
|
||||
export const metadata: Metadata = {
|
||||
title: "Plane Deploy | Make your Plane boards public with one-click",
|
||||
|
|
@ -27,9 +22,7 @@ export const metadata: Metadata = {
|
|||
},
|
||||
};
|
||||
|
||||
export default async function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
const instanceDetails = await instanceService.getInstanceInfo().catch(() => undefined);
|
||||
|
||||
export default function RootLayout({ children }: { children: React.ReactNode }) {
|
||||
return (
|
||||
<html lang="en">
|
||||
<head>
|
||||
|
|
@ -40,21 +33,9 @@ export default async function RootLayout({ children }: { children: React.ReactNo
|
|||
<link rel="shortcut icon" href={`${ASSET_PREFIX}/favicon/favicon.ico`} />
|
||||
</head>
|
||||
<body>
|
||||
<AppProvider initialState={{ instance: instanceDetails }}>
|
||||
{!instanceDetails ? (
|
||||
<InstanceFailureView />
|
||||
) : (
|
||||
<>
|
||||
{instanceDetails.instance.is_setup_done ? (
|
||||
<>{children}</>
|
||||
) : (
|
||||
<div className="h-screen w-screen">
|
||||
<InstanceNotReady />
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</AppProvider>
|
||||
<StoreProvider>
|
||||
<InstanceProvider>{children}</InstanceProvider>
|
||||
</StoreProvider>
|
||||
</body>
|
||||
</html>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ import Image from "next/image";
|
|||
// assets
|
||||
import UserLoggedInImage from "public/user-logged-in.svg";
|
||||
|
||||
export default function InstanceNotFound() {
|
||||
export default function NotFound() {
|
||||
return (
|
||||
<div className="flex h-screen w-screen flex-col">
|
||||
<div className="grid h-full w-full place-items-center p-6">
|
||||
|
|
|
|||
|
|
@ -1,7 +1,5 @@
|
|||
"use client";
|
||||
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR from "swr";
|
||||
// components
|
||||
import { UserLoggedIn } from "@/components/account";
|
||||
import { LogoSpinner } from "@/components/common";
|
||||
|
|
@ -9,15 +7,8 @@ import { AuthView } from "@/components/views";
|
|||
// hooks
|
||||
import { useUser } from "@/hooks/store";
|
||||
|
||||
function HomePage() {
|
||||
const { data: currentUser, fetchCurrentUser, isAuthenticated, isLoading } = useUser();
|
||||
|
||||
useSWR("CURRENT_USER", () => fetchCurrentUser(), {
|
||||
errorRetryCount: 0,
|
||||
revalidateIfStale: false,
|
||||
revalidateOnFocus: false,
|
||||
refreshWhenHidden: false,
|
||||
});
|
||||
export default function HomePage() {
|
||||
const { data: currentUser, isAuthenticated, isLoading } = useUser();
|
||||
|
||||
if (isLoading) return <LogoSpinner />;
|
||||
|
||||
|
|
@ -25,5 +16,3 @@ function HomePage() {
|
|||
|
||||
return <AuthView />;
|
||||
}
|
||||
|
||||
export default observer(HomePage);
|
||||
|
|
|
|||
|
|
@ -43,7 +43,7 @@ export const AuthRoot: FC = observer(() => {
|
|||
const [errorInfo, setErrorInfo] = useState<TAuthErrorInfo | undefined>(undefined);
|
||||
const [isPasswordAutoset, setIsPasswordAutoset] = useState(true);
|
||||
// hooks
|
||||
const { instance } = useInstance();
|
||||
const { config } = useInstance();
|
||||
|
||||
useEffect(() => {
|
||||
if (error_code) {
|
||||
|
|
@ -67,11 +67,10 @@ export const AuthRoot: FC = observer(() => {
|
|||
}
|
||||
}, [error_code]);
|
||||
|
||||
const isSMTPConfigured = instance?.config?.is_smtp_configured || false;
|
||||
const isMagicLoginEnabled = instance?.config?.is_magic_login_enabled || false;
|
||||
const isEmailPasswordEnabled = instance?.config?.is_email_password_enabled || false;
|
||||
const isOAuthEnabled =
|
||||
(instance?.config && (instance?.config?.is_google_enabled || instance?.config?.is_github_enabled)) || false;
|
||||
const isSMTPConfigured = config?.is_smtp_configured || false;
|
||||
const isMagicLoginEnabled = config?.is_magic_login_enabled || false;
|
||||
const isEmailPasswordEnabled = config?.is_email_password_enabled || false;
|
||||
const isOAuthEnabled = (config && (config?.is_google_enabled || config?.is_github_enabled)) || false;
|
||||
|
||||
// submit handler- email verification
|
||||
const handleEmailVerification = async (data: IEmailCheckData) => {
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import { useInstance } from "@/hooks/store";
|
|||
|
||||
export const OAuthOptions: React.FC = observer(() => {
|
||||
// hooks
|
||||
const { instance } = useInstance();
|
||||
const { config } = useInstance();
|
||||
|
||||
return (
|
||||
<>
|
||||
|
|
@ -16,12 +16,12 @@ export const OAuthOptions: React.FC = observer(() => {
|
|||
<hr className="w-full border-onboarding-border-100" />
|
||||
</div>
|
||||
<div className={`mt-7 grid gap-4 overflow-hidden`}>
|
||||
{instance?.config?.is_google_enabled && (
|
||||
{config?.is_google_enabled && (
|
||||
<div className="flex h-[42px] items-center !overflow-hidden">
|
||||
<GoogleOAuthButton text="Sign in with Google" />
|
||||
</div>
|
||||
)}
|
||||
{instance?.config?.is_github_enabled && <GithubOAuthButton text="Sign in with Github" />}
|
||||
{config?.is_github_enabled && <GithubOAuthButton text="Sign in with Github" />}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useContext } from "react";
|
||||
// lib
|
||||
import { StoreContext } from "@/lib/app-providers";
|
||||
import { StoreContext } from "@/lib/store-provider";
|
||||
// store
|
||||
import { IInstanceStore } from "@/store/instance.store";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useContext } from "react";
|
||||
// lib
|
||||
import { StoreContext } from "@/lib/app-providers";
|
||||
import { StoreContext } from "@/lib/store-provider";
|
||||
// store
|
||||
import { IIssueDetailStore } from "@/store/issue-detail.store";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useContext } from "react";
|
||||
// lib
|
||||
import { StoreContext } from "@/lib/app-providers";
|
||||
import { StoreContext } from "@/lib/store-provider";
|
||||
// store
|
||||
import { IIssueFilterStore } from "@/store/issue-filters.store";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useContext } from "react";
|
||||
// lib
|
||||
import { StoreContext } from "@/lib/app-providers";
|
||||
import { StoreContext } from "@/lib/store-provider";
|
||||
// store
|
||||
import { IIssueStore } from "@/store/issue.store";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useContext } from "react";
|
||||
// lib
|
||||
import { StoreContext } from "@/lib/app-providers";
|
||||
import { StoreContext } from "@/lib/store-provider";
|
||||
// store
|
||||
import { IProjectStore } from "@/store/project.store";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useContext } from "react";
|
||||
// lib
|
||||
import { StoreContext } from "@/lib/app-providers";
|
||||
import { StoreContext } from "@/lib/store-provider";
|
||||
// store
|
||||
import { IProfileStore } from "@/store/profile.store";
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
import { useContext } from "react";
|
||||
// lib
|
||||
import { StoreContext } from "@/lib/app-providers";
|
||||
import { StoreContext } from "@/lib/store-provider";
|
||||
// store
|
||||
import { IUserStore } from "@/store/user.store";
|
||||
|
||||
|
|
|
|||
63
space/lib/instance-provider.tsx
Normal file
63
space/lib/instance-provider.tsx
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
"use client";
|
||||
|
||||
import { ReactNode } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import Image from "next/image";
|
||||
import { useTheme } from "next-themes";
|
||||
import useSWR from "swr";
|
||||
// components
|
||||
import { LogoSpinner } from "@/components/common";
|
||||
import { InstanceFailureView } from "@/components/instance";
|
||||
// hooks
|
||||
import { useInstance, useUser } from "@/hooks/store";
|
||||
// assets
|
||||
import PlaneBackgroundPatternDark from "public/auth/background-pattern-dark.svg";
|
||||
import PlaneBackgroundPattern from "public/auth/background-pattern.svg";
|
||||
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
||||
|
||||
export const InstanceProvider = observer(({ children }: { children: ReactNode }) => {
|
||||
const { fetchInstanceInfo, instance, error } = useInstance();
|
||||
const { fetchCurrentUser } = useUser();
|
||||
const { resolvedTheme } = useTheme();
|
||||
|
||||
const patternBackground = resolvedTheme === "dark" ? PlaneBackgroundPatternDark : PlaneBackgroundPattern;
|
||||
|
||||
useSWR("INSTANCE_INFO", () => fetchInstanceInfo(), {
|
||||
revalidateOnFocus: false,
|
||||
revalidateIfStale: false,
|
||||
errorRetryCount: 0,
|
||||
});
|
||||
useSWR("CURRENT_USER", () => fetchCurrentUser());
|
||||
|
||||
if (!instance && !error)
|
||||
return (
|
||||
<div className="flex h-screen min-h-[500px] w-full justify-center items-center">
|
||||
<LogoSpinner />
|
||||
</div>
|
||||
);
|
||||
|
||||
if (error) {
|
||||
return (
|
||||
<div className="relative">
|
||||
<div className="h-screen w-full overflow-hidden overflow-y-auto flex flex-col">
|
||||
<div className="container h-[110px] flex-shrink-0 mx-auto px-5 lg:px-0 flex items-center justify-between gap-5 z-50">
|
||||
<div className="flex items-center gap-x-2 py-10">
|
||||
<Image src={BluePlaneLogoWithoutText} height={30} width={30} alt="Plane Logo" />
|
||||
<span className="text-2xl font-semibold sm:text-3xl">Plane</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="absolute inset-0 z-0">
|
||||
<Image src={patternBackground} className="w-screen h-full object-cover" alt="Plane background pattern" />
|
||||
</div>
|
||||
<div className="relative z-10 flex-grow">
|
||||
<div className="relative h-full w-full overflow-y-auto px-6 py-10 mx-auto flex justify-center items-center">
|
||||
<InstanceFailureView />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
});
|
||||
|
|
@ -23,12 +23,13 @@ function initializeStore(initialData = {}) {
|
|||
return singletonRootStore;
|
||||
}
|
||||
|
||||
export type AppProviderProps = {
|
||||
export type StoreProviderProps = {
|
||||
children: ReactNode;
|
||||
initialState: any;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
initialState?: any;
|
||||
};
|
||||
|
||||
export const AppProvider = ({ children, initialState = {} }: AppProviderProps) => {
|
||||
export const StoreProvider = ({ children, initialState = {} }: StoreProviderProps) => {
|
||||
const store = initializeStore(initialState);
|
||||
return (
|
||||
<ThemeProvider themes={["light", "dark"]} defaultTheme="system" enableSystem>
|
||||
12
space/lib/user-provider.tsx
Normal file
12
space/lib/user-provider.tsx
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
import { ReactNode } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import useSWR from "swr";
|
||||
import { useUser } from "@/hooks/store";
|
||||
|
||||
export const UserProvider = observer(({ children }: { children: ReactNode }) => {
|
||||
const { fetchCurrentUser } = useUser();
|
||||
|
||||
useSWR("CURRENT_USER", () => fetchCurrentUser());
|
||||
|
||||
return <>{children}</>;
|
||||
});
|
||||
|
|
@ -1,89 +0,0 @@
|
|||
import { FC, ReactNode } from "react";
|
||||
import { observer } from "mobx-react-lite";
|
||||
import { useRouter } from "next/navigation";
|
||||
import useSWR from "swr";
|
||||
import { Spinner } from "@plane/ui";
|
||||
// helpers
|
||||
import { EPageTypes } from "@/helpers/authentication.helper";
|
||||
// hooks
|
||||
import { useUser, useUserProfile } from "@/hooks/store";
|
||||
|
||||
type TAuthWrapper = {
|
||||
children: ReactNode;
|
||||
pageType?: EPageTypes;
|
||||
};
|
||||
|
||||
export const AuthWrapper: FC<TAuthWrapper> = observer((props) => {
|
||||
const router = useRouter();
|
||||
const { children, pageType = EPageTypes.AUTHENTICATED } = props;
|
||||
// hooks
|
||||
const { isLoading, data: currentUser, fetchCurrentUser } = useUser();
|
||||
const { data: currentUserProfile } = useUserProfile();
|
||||
|
||||
const { isLoading: isSWRLoading } = useSWR("INSTANCE_INFORMATION", () => fetchCurrentUser(), {
|
||||
revalidateOnFocus: false,
|
||||
});
|
||||
|
||||
if (isSWRLoading || isLoading)
|
||||
return (
|
||||
<div className="relative flex h-screen w-full items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
);
|
||||
|
||||
if (pageType === EPageTypes.PUBLIC) return <>{children}</>;
|
||||
|
||||
if (pageType === EPageTypes.INIT) {
|
||||
if (!currentUser?.id) return <>{children}</>;
|
||||
else {
|
||||
if (
|
||||
currentUserProfile &&
|
||||
currentUserProfile?.id &&
|
||||
Boolean(currentUserProfile?.onboarding_step?.profile_complete)
|
||||
)
|
||||
return <>{children}</>;
|
||||
else {
|
||||
router.push(`/onboarding`);
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pageType === EPageTypes.NON_AUTHENTICATED) {
|
||||
if (!currentUser?.id) return <>{children}</>;
|
||||
else {
|
||||
if (currentUserProfile?.id && currentUserProfile?.onboarding_step?.profile_complete) {
|
||||
router.push(`/`);
|
||||
return <></>;
|
||||
} else {
|
||||
router.push(`/onboarding`);
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (pageType === EPageTypes.ONBOARDING) {
|
||||
if (!currentUser?.id) {
|
||||
router.push(`/`);
|
||||
return <></>;
|
||||
} else {
|
||||
if (currentUserProfile?.id && currentUserProfile?.onboarding_step?.profile_complete) {
|
||||
router.push(`/`);
|
||||
return <></>;
|
||||
} else return <>{children}</>;
|
||||
}
|
||||
}
|
||||
|
||||
if (pageType === EPageTypes.AUTHENTICATED) {
|
||||
if (!currentUser?.id) return <>{children}</>;
|
||||
else {
|
||||
if (currentUserProfile?.id && currentUserProfile?.onboarding_step?.profile_complete) return <>{children}</>;
|
||||
else {
|
||||
router.push(`/onboarding`);
|
||||
return <></>;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return <>{children}</>;
|
||||
});
|
||||
|
|
@ -1 +0,0 @@
|
|||
export * from "./auth-wrapper";
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
// types
|
||||
import type { IInstance } from "@plane/types";
|
||||
import type { IInstanceInfo } from "@plane/types";
|
||||
// helpers
|
||||
import { API_BASE_URL } from "@/helpers/common.helper";
|
||||
// services
|
||||
|
|
@ -10,7 +10,7 @@ export class InstanceService extends APIService {
|
|||
super(API_BASE_URL);
|
||||
}
|
||||
|
||||
async getInstanceInfo(): Promise<IInstance> {
|
||||
async getInstanceInfo(): Promise<IInstanceInfo> {
|
||||
return this.get("/api/instances/")
|
||||
.then((response) => response.data)
|
||||
.catch((error) => {
|
||||
|
|
|
|||
|
|
@ -1,7 +1,7 @@
|
|||
import set from "lodash/set";
|
||||
import { observable, action, makeObservable, runInAction } from "mobx";
|
||||
// types
|
||||
import { IInstance } from "@plane/types";
|
||||
import { IInstance, IInstanceConfig } from "@plane/types";
|
||||
// services
|
||||
import { InstanceService } from "@/services/instance.service";
|
||||
// store types
|
||||
|
|
@ -20,6 +20,7 @@ export interface IInstanceStore {
|
|||
// observables
|
||||
isLoading: boolean;
|
||||
instance: IInstance | undefined;
|
||||
config: IInstanceConfig | undefined;
|
||||
error: TError | undefined;
|
||||
// action
|
||||
fetchInstanceInfo: () => Promise<void>;
|
||||
|
|
@ -29,6 +30,7 @@ export interface IInstanceStore {
|
|||
export class InstanceStore implements IInstanceStore {
|
||||
isLoading: boolean = true;
|
||||
instance: IInstance | undefined = undefined;
|
||||
config: IInstanceConfig | undefined = undefined;
|
||||
error: TError | undefined = undefined;
|
||||
// services
|
||||
instanceService;
|
||||
|
|
@ -38,6 +40,7 @@ export class InstanceStore implements IInstanceStore {
|
|||
// observable
|
||||
isLoading: observable.ref,
|
||||
instance: observable,
|
||||
config: observable,
|
||||
error: observable,
|
||||
// actions
|
||||
fetchInstanceInfo: action,
|
||||
|
|
@ -56,10 +59,11 @@ export class InstanceStore implements IInstanceStore {
|
|||
try {
|
||||
this.isLoading = true;
|
||||
this.error = undefined;
|
||||
const instance = await this.instanceService.getInstanceInfo();
|
||||
const instanceInfo = await this.instanceService.getInstanceInfo();
|
||||
runInAction(() => {
|
||||
this.isLoading = false;
|
||||
this.instance = instance;
|
||||
this.instance = instanceInfo.instance;
|
||||
this.config = instanceInfo.config;
|
||||
});
|
||||
} catch (error) {
|
||||
runInAction(() => {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue