mirror of
https://github.com/makeplane/plane
synced 2024-06-14 14:31:34 +00:00
fix: custom theme persisting after signing out (#1780)
* fix: custom theme persistence * chore: remove console logs * fix: build error * fix: change theme from command k
This commit is contained in:
parent
5aad6c71da
commit
97c3fb40e7
@ -5,6 +5,8 @@ import { Command } from "cmdk";
|
|||||||
import { THEMES_OBJ } from "constants/themes";
|
import { THEMES_OBJ } from "constants/themes";
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
import { SettingIcon } from "components/icons";
|
import { SettingIcon } from "components/icons";
|
||||||
|
import userService from "services/user.service";
|
||||||
|
import useUser from "hooks/use-user";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
|
setIsPaletteOpen: Dispatch<SetStateAction<boolean>>;
|
||||||
@ -12,24 +14,50 @@ type Props = {
|
|||||||
|
|
||||||
export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
|
export const ChangeInterfaceTheme: React.FC<Props> = ({ setIsPaletteOpen }) => {
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
const { setTheme } = useTheme();
|
const { setTheme } = useTheme();
|
||||||
|
|
||||||
|
const { user, mutateUser } = useUser();
|
||||||
|
|
||||||
|
const updateUserTheme = (newTheme: string) => {
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
setTheme(newTheme);
|
||||||
|
|
||||||
|
mutateUser((prevData) => {
|
||||||
|
if (!prevData) return prevData;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prevData,
|
||||||
|
theme: {
|
||||||
|
...prevData.theme,
|
||||||
|
theme: newTheme,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
userService.updateUser({
|
||||||
|
theme: {
|
||||||
|
...user.theme,
|
||||||
|
theme: newTheme,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// useEffect only runs on the client, so now we can safely show the UI
|
// useEffect only runs on the client, so now we can safely show the UI
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!mounted) {
|
if (!mounted) return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{THEMES_OBJ.map((theme) => (
|
{THEMES_OBJ.filter((t) => t.value !== "custom").map((theme) => (
|
||||||
<Command.Item
|
<Command.Item
|
||||||
key={theme.value}
|
key={theme.value}
|
||||||
onSelect={() => {
|
onSelect={() => {
|
||||||
setTheme(theme.value);
|
updateUserTheme(theme.value);
|
||||||
setIsPaletteOpen(false);
|
setIsPaletteOpen(false);
|
||||||
}}
|
}}
|
||||||
className="focus:outline-none"
|
className="focus:outline-none"
|
||||||
|
@ -28,6 +28,7 @@ const defaultValues: ICustomTheme = {
|
|||||||
sidebarText: "#c5c5c5",
|
sidebarText: "#c5c5c5",
|
||||||
darkPalette: false,
|
darkPalette: false,
|
||||||
palette: "",
|
palette: "",
|
||||||
|
theme: "custom",
|
||||||
};
|
};
|
||||||
|
|
||||||
export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => {
|
export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => {
|
||||||
@ -56,6 +57,7 @@ export const CustomThemeSelector: React.FC<Props> = ({ preLoadedData }) => {
|
|||||||
sidebarText: formData.sidebarText,
|
sidebarText: formData.sidebarText,
|
||||||
darkPalette: darkPalette,
|
darkPalette: darkPalette,
|
||||||
palette: `${formData.background},${formData.text},${formData.primary},${formData.sidebarBackground},${formData.sidebarText}`,
|
palette: `${formData.background},${formData.text},${formData.primary},${formData.sidebarBackground},${formData.sidebarText}`,
|
||||||
|
theme: "custom",
|
||||||
};
|
};
|
||||||
|
|
||||||
await userService
|
await userService
|
||||||
|
@ -1,43 +1,71 @@
|
|||||||
import { useState, useEffect, Dispatch, SetStateAction } from "react";
|
import { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
// next-themes
|
||||||
import { useTheme } from "next-themes";
|
import { useTheme } from "next-themes";
|
||||||
|
// services
|
||||||
|
import userService from "services/user.service";
|
||||||
|
// hooks
|
||||||
|
import useUser from "hooks/use-user";
|
||||||
// constants
|
// constants
|
||||||
import { THEMES_OBJ } from "constants/themes";
|
import { THEMES_OBJ } from "constants/themes";
|
||||||
// ui
|
// ui
|
||||||
import { CustomSelect } from "components/ui";
|
import { CustomSelect } from "components/ui";
|
||||||
// types
|
// types
|
||||||
import { ICustomTheme, IUser } from "types";
|
import { ICustomTheme } from "types";
|
||||||
|
import { unsetCustomCssVariables } from "helpers/theme.helper";
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
user: IUser | undefined;
|
setPreLoadedData: React.Dispatch<React.SetStateAction<ICustomTheme | null>>;
|
||||||
setPreLoadedData: Dispatch<SetStateAction<ICustomTheme | null>>;
|
|
||||||
customThemeSelectorOptions: boolean;
|
customThemeSelectorOptions: boolean;
|
||||||
setCustomThemeSelectorOptions: Dispatch<SetStateAction<boolean>>;
|
setCustomThemeSelectorOptions: React.Dispatch<React.SetStateAction<boolean>>;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const ThemeSwitch: React.FC<Props> = ({
|
export const ThemeSwitch: React.FC<Props> = ({
|
||||||
user,
|
|
||||||
setPreLoadedData,
|
setPreLoadedData,
|
||||||
customThemeSelectorOptions,
|
customThemeSelectorOptions,
|
||||||
setCustomThemeSelectorOptions,
|
setCustomThemeSelectorOptions,
|
||||||
}) => {
|
}) => {
|
||||||
const [mounted, setMounted] = useState(false);
|
const [mounted, setMounted] = useState(false);
|
||||||
|
|
||||||
const { theme, setTheme } = useTheme();
|
const { theme, setTheme } = useTheme();
|
||||||
|
|
||||||
|
const { user, mutateUser } = useUser();
|
||||||
|
|
||||||
|
const updateUserTheme = (newTheme: string) => {
|
||||||
|
if (!user) return;
|
||||||
|
|
||||||
|
setTheme(newTheme);
|
||||||
|
|
||||||
|
mutateUser((prevData) => {
|
||||||
|
if (!prevData) return prevData;
|
||||||
|
|
||||||
|
return {
|
||||||
|
...prevData,
|
||||||
|
theme: {
|
||||||
|
...prevData.theme,
|
||||||
|
theme: newTheme,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}, false);
|
||||||
|
|
||||||
|
userService.updateUser({
|
||||||
|
theme: {
|
||||||
|
...user.theme,
|
||||||
|
theme: newTheme,
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
// useEffect only runs on the client, so now we can safely show the UI
|
// useEffect only runs on the client, so now we can safely show the UI
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
if (!mounted) {
|
if (!mounted) return null;
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentThemeObj = THEMES_OBJ.find((t) => t.value === theme);
|
const currentThemeObj = THEMES_OBJ.find((t) => t.value === theme);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
|
||||||
<CustomSelect
|
<CustomSelect
|
||||||
value={theme}
|
value={theme}
|
||||||
label={
|
label={
|
||||||
@ -84,26 +112,18 @@ export const ThemeSwitch: React.FC<Props> = ({
|
|||||||
user.theme.palette !== ",,,,"
|
user.theme.palette !== ",,,,"
|
||||||
? user.theme.palette
|
? user.theme.palette
|
||||||
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
||||||
|
theme: "custom",
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
|
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
|
||||||
} else {
|
} else {
|
||||||
if (customThemeSelectorOptions) setCustomThemeSelectorOptions(false);
|
if (customThemeSelectorOptions) setCustomThemeSelectorOptions(false);
|
||||||
|
unsetCustomCssVariables();
|
||||||
for (let i = 10; i <= 900; i >= 100 ? (i += 100) : (i += 10)) {
|
|
||||||
document.documentElement.style.removeProperty(`--color-background-${i}`);
|
|
||||||
document.documentElement.style.removeProperty(`--color-text-${i}`);
|
|
||||||
document.documentElement.style.removeProperty(`--color-border-${i}`);
|
|
||||||
document.documentElement.style.removeProperty(`--color-primary-${i}`);
|
|
||||||
document.documentElement.style.removeProperty(`--color-sidebar-background-${i}`);
|
|
||||||
document.documentElement.style.removeProperty(`--color-sidebar-text-${i}`);
|
|
||||||
document.documentElement.style.removeProperty(`--color-sidebar-border-${i}`);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
setTheme(value);
|
updateUserTheme(value);
|
||||||
document.documentElement.style.setProperty("color-scheme", type);
|
document.documentElement.style.setProperty("--color-scheme", type);
|
||||||
}}
|
}}
|
||||||
input
|
input
|
||||||
width="w-full"
|
width="w-full"
|
||||||
@ -137,6 +157,5 @@ export const ThemeSwitch: React.FC<Props> = ({
|
|||||||
</CustomSelect.Option>
|
</CustomSelect.Option>
|
||||||
))}
|
))}
|
||||||
</CustomSelect>
|
</CustomSelect>
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -3,10 +3,10 @@ import { Fragment } from "react";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
|
||||||
// next-themes
|
|
||||||
import { useTheme } from "next-themes";
|
|
||||||
// headless ui
|
// headless ui
|
||||||
import { Menu, Transition } from "@headlessui/react";
|
import { Menu, Transition } from "@headlessui/react";
|
||||||
|
// next-themes
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
// hooks
|
// hooks
|
||||||
import useUser from "hooks/use-user";
|
import useUser from "hooks/use-user";
|
||||||
import useThemeHook from "hooks/use-theme";
|
import useThemeHook from "hooks/use-theme";
|
||||||
@ -91,7 +91,7 @@ export const WorkspaceSidebarDropdown = () => {
|
|||||||
.then(() => {
|
.then(() => {
|
||||||
mutateUser(undefined);
|
mutateUser(undefined);
|
||||||
router.push("/");
|
router.push("/");
|
||||||
setTheme("dark");
|
setTheme("system");
|
||||||
})
|
})
|
||||||
.catch(() =>
|
.catch(() =>
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
|
@ -1,6 +1,16 @@
|
|||||||
export const THEMES = ["light", "dark", "light-contrast", "dark-contrast", "custom"];
|
export const THEMES = ["light", "dark", "light-contrast", "dark-contrast", "custom"];
|
||||||
|
|
||||||
export const THEMES_OBJ = [
|
export const THEMES_OBJ = [
|
||||||
|
{
|
||||||
|
value: "system",
|
||||||
|
label: "System Preference",
|
||||||
|
type: "light",
|
||||||
|
icon: {
|
||||||
|
border: "#DEE2E6",
|
||||||
|
color1: "#FAFAFA",
|
||||||
|
color2: "#3F76FF",
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
value: "light",
|
value: "light",
|
||||||
label: "Light",
|
label: "Light",
|
||||||
|
@ -11,7 +11,7 @@ import projectService from "services/project.service";
|
|||||||
// fetch-keys
|
// fetch-keys
|
||||||
import { USER_PROJECT_VIEW } from "constants/fetch-keys";
|
import { USER_PROJECT_VIEW } from "constants/fetch-keys";
|
||||||
// helper
|
// helper
|
||||||
import { applyTheme } from "helpers/theme.helper";
|
import { applyTheme, unsetCustomCssVariables } from "helpers/theme.helper";
|
||||||
// constants
|
// constants
|
||||||
|
|
||||||
export const themeContext = createContext<ContextType>({} as ContextType);
|
export const themeContext = createContext<ContextType>({} as ContextType);
|
||||||
@ -92,7 +92,9 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const theme = localStorage.getItem("theme");
|
const theme = localStorage.getItem("theme");
|
||||||
if (theme && theme === "custom") {
|
|
||||||
|
if (theme) {
|
||||||
|
if (theme === "custom") {
|
||||||
if (user && user.theme.palette) {
|
if (user && user.theme.palette) {
|
||||||
applyTheme(
|
applyTheme(
|
||||||
user.theme.palette !== ",,,,"
|
user.theme.palette !== ",,,,"
|
||||||
@ -101,6 +103,7 @@ export const ThemeContextProvider: React.FC<{ children: React.ReactNode }> = ({
|
|||||||
user.theme.darkPalette
|
user.theme.darkPalette
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else unsetCustomCssVariables();
|
||||||
}
|
}
|
||||||
}, [user]);
|
}, [user]);
|
||||||
|
|
||||||
|
@ -60,6 +60,7 @@ const calculateShades = (hexValue: string): TShades => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const applyTheme = (palette: string, isDarkPalette: boolean) => {
|
export const applyTheme = (palette: string, isDarkPalette: boolean) => {
|
||||||
|
const dom = document?.querySelector<HTMLElement>("[data-theme='custom']");
|
||||||
// palette: [bg, text, primary, sidebarBg, sidebarText]
|
// palette: [bg, text, primary, sidebarBg, sidebarText]
|
||||||
const values: string[] = palette.split(",");
|
const values: string[] = palette.split(",");
|
||||||
values.push(isDarkPalette ? "dark" : "light");
|
values.push(isDarkPalette ? "dark" : "light");
|
||||||
@ -79,41 +80,40 @@ export const applyTheme = (palette: string, isDarkPalette: boolean) => {
|
|||||||
const sidebarBackgroundRgbValues = `${sidebarBackgroundShades[shade].r}, ${sidebarBackgroundShades[shade].g}, ${sidebarBackgroundShades[shade].b}`;
|
const sidebarBackgroundRgbValues = `${sidebarBackgroundShades[shade].r}, ${sidebarBackgroundShades[shade].g}, ${sidebarBackgroundShades[shade].b}`;
|
||||||
const sidebarTextRgbValues = `${sidebarTextShades[shade].r}, ${sidebarTextShades[shade].g}, ${sidebarTextShades[shade].b}`;
|
const sidebarTextRgbValues = `${sidebarTextShades[shade].r}, ${sidebarTextShades[shade].g}, ${sidebarTextShades[shade].b}`;
|
||||||
|
|
||||||
document
|
dom?.style.setProperty(`--color-background-${shade}`, bgRgbValues);
|
||||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
dom?.style.setProperty(`--color-text-${shade}`, textRgbValues);
|
||||||
?.style.setProperty(`--color-background-${shade}`, bgRgbValues);
|
dom?.style.setProperty(`--color-primary-${shade}`, primaryRgbValues);
|
||||||
document
|
dom?.style.setProperty(`--color-sidebar-background-${shade}`, sidebarBackgroundRgbValues);
|
||||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
dom?.style.setProperty(`--color-sidebar-text-${shade}`, sidebarTextRgbValues);
|
||||||
?.style.setProperty(`--color-text-${shade}`, textRgbValues);
|
|
||||||
document
|
|
||||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
|
||||||
?.style.setProperty(`--color-primary-${shade}`, primaryRgbValues);
|
|
||||||
document
|
|
||||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
|
||||||
?.style.setProperty(`--color-sidebar-background-${shade}`, sidebarBackgroundRgbValues);
|
|
||||||
document
|
|
||||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
|
||||||
?.style.setProperty(`--color-sidebar-text-${shade}`, sidebarTextRgbValues);
|
|
||||||
|
|
||||||
if (i >= 100 && i <= 400) {
|
if (i >= 100 && i <= 400) {
|
||||||
const borderShade = i === 100 ? 70 : i === 200 ? 80 : i === 300 ? 90 : 100;
|
const borderShade = i === 100 ? 70 : i === 200 ? 80 : i === 300 ? 90 : 100;
|
||||||
|
|
||||||
document
|
dom?.style.setProperty(
|
||||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
|
||||||
?.style.setProperty(
|
|
||||||
`--color-border-${shade}`,
|
`--color-border-${shade}`,
|
||||||
`${bgShades[borderShade].r}, ${bgShades[borderShade].g}, ${bgShades[borderShade].b}`
|
`${bgShades[borderShade].r}, ${bgShades[borderShade].g}, ${bgShades[borderShade].b}`
|
||||||
);
|
);
|
||||||
document
|
dom?.style.setProperty(
|
||||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
|
||||||
?.style.setProperty(
|
|
||||||
`--color-sidebar-border-${shade}`,
|
`--color-sidebar-border-${shade}`,
|
||||||
`${sidebarBackgroundShades[borderShade].r}, ${sidebarBackgroundShades[borderShade].g}, ${sidebarBackgroundShades[borderShade].b}`
|
`${sidebarBackgroundShades[borderShade].r}, ${sidebarBackgroundShades[borderShade].g}, ${sidebarBackgroundShades[borderShade].b}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
document
|
dom?.style.setProperty("--color-scheme", values[5]);
|
||||||
.querySelector<HTMLElement>("[data-theme='custom']")
|
};
|
||||||
?.style.setProperty("--color-scheme", values[5]);
|
|
||||||
|
export const unsetCustomCssVariables = () => {
|
||||||
|
for (let i = 10; i <= 900; i >= 100 ? (i += 100) : (i += 10)) {
|
||||||
|
const dom = document.querySelector<HTMLElement>("[data-theme='custom']");
|
||||||
|
|
||||||
|
dom?.style.removeProperty(`--color-background-${i}`);
|
||||||
|
dom?.style.removeProperty(`--color-text-${i}`);
|
||||||
|
dom?.style.removeProperty(`--color-border-${i}`);
|
||||||
|
dom?.style.removeProperty(`--color-primary-${i}`);
|
||||||
|
dom?.style.removeProperty(`--color-sidebar-background-${i}`);
|
||||||
|
dom?.style.removeProperty(`--color-sidebar-text-${i}`);
|
||||||
|
dom?.style.removeProperty(`--color-sidebar-border-${i}`);
|
||||||
|
dom?.style.removeProperty("--color-scheme");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useTheme } from "next-themes";
|
|
||||||
|
|
||||||
|
// next-themes
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
// hooks
|
// hooks
|
||||||
import useUserAuth from "hooks/use-user-auth";
|
import useUserAuth from "hooks/use-user-auth";
|
||||||
// layouts
|
// layouts
|
||||||
@ -15,11 +16,13 @@ import { BreadcrumbItem, Breadcrumbs } from "components/breadcrumbs";
|
|||||||
import { ICustomTheme } from "types";
|
import { ICustomTheme } from "types";
|
||||||
|
|
||||||
const ProfilePreferences = () => {
|
const ProfilePreferences = () => {
|
||||||
const { user: myProfile } = useUserAuth();
|
|
||||||
const { theme } = useTheme();
|
|
||||||
const [customThemeSelectorOptions, setCustomThemeSelectorOptions] = useState(false);
|
const [customThemeSelectorOptions, setCustomThemeSelectorOptions] = useState(false);
|
||||||
const [preLoadedData, setPreLoadedData] = useState<ICustomTheme | null>(null);
|
const [preLoadedData, setPreLoadedData] = useState<ICustomTheme | null>(null);
|
||||||
|
|
||||||
|
const { theme } = useTheme();
|
||||||
|
|
||||||
|
const { user: myProfile } = useUserAuth();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (theme === "custom") {
|
if (theme === "custom") {
|
||||||
if (myProfile?.theme.palette)
|
if (myProfile?.theme.palette)
|
||||||
@ -37,6 +40,7 @@ const ProfilePreferences = () => {
|
|||||||
myProfile.theme.palette !== ",,,,"
|
myProfile.theme.palette !== ",,,,"
|
||||||
? myProfile.theme.palette
|
? myProfile.theme.palette
|
||||||
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
: "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5",
|
||||||
|
theme: "custom",
|
||||||
});
|
});
|
||||||
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
|
if (!customThemeSelectorOptions) setCustomThemeSelectorOptions(true);
|
||||||
}
|
}
|
||||||
@ -71,7 +75,6 @@ const ProfilePreferences = () => {
|
|||||||
</div>
|
</div>
|
||||||
<div className="col-span-12 sm:col-span-6">
|
<div className="col-span-12 sm:col-span-6">
|
||||||
<ThemeSwitch
|
<ThemeSwitch
|
||||||
user={myProfile}
|
|
||||||
setPreLoadedData={setPreLoadedData}
|
setPreLoadedData={setPreLoadedData}
|
||||||
customThemeSelectorOptions={customThemeSelectorOptions}
|
customThemeSelectorOptions={customThemeSelectorOptions}
|
||||||
setCustomThemeSelectorOptions={setCustomThemeSelectorOptions}
|
setCustomThemeSelectorOptions={setCustomThemeSelectorOptions}
|
||||||
|
@ -45,7 +45,7 @@ Router.events.on("routeChangeComplete", NProgress.done);
|
|||||||
function MyApp({ Component, pageProps }: AppProps) {
|
function MyApp({ Component, pageProps }: AppProps) {
|
||||||
return (
|
return (
|
||||||
// <UserProvider>
|
// <UserProvider>
|
||||||
<ThemeProvider themes={THEMES} defaultTheme="dark">
|
<ThemeProvider themes={THEMES} defaultTheme="system">
|
||||||
<ToastContextProvider>
|
<ToastContextProvider>
|
||||||
<ThemeContextProvider>
|
<ThemeContextProvider>
|
||||||
<CrispWithNoSSR />
|
<CrispWithNoSSR />
|
||||||
|
@ -1,143 +0,0 @@
|
|||||||
import React from "react";
|
|
||||||
|
|
||||||
// layouts
|
|
||||||
import DefaultLayout from "layouts/default-layout";
|
|
||||||
import { UserAuthorizationLayout } from "layouts/auth-layout/user-authorization-wrapper";
|
|
||||||
// types
|
|
||||||
import type { NextPage } from "next";
|
|
||||||
|
|
||||||
const Colors: NextPage = () => (
|
|
||||||
<UserAuthorizationLayout>
|
|
||||||
<DefaultLayout>
|
|
||||||
<div className="space-y-8 p-8">
|
|
||||||
<div>
|
|
||||||
Primary:
|
|
||||||
<div className="flex flex-wrap">
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-0" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-10" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-20" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-30" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-40" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-50" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-60" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-70" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-80" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-90" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-100" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-200" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-300" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-400" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-500" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-600" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-700" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-800" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-900" />
|
|
||||||
<div className="h-12 w-12 bg-custom-primary-1000" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Background:
|
|
||||||
<div className="flex flex-wrap">
|
|
||||||
<div className="h-12 w-12 bg-custom-background-0" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-10" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-20" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-30" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-40" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-50" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-60" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-70" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-80" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-90" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-100" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-200" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-300" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-400" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-500" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-600" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-700" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-800" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-900" />
|
|
||||||
<div className="h-12 w-12 bg-custom-background-1000" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Text:
|
|
||||||
<div className="flex flex-wrap">
|
|
||||||
<div className="h-12 w-12 bg-custom-text-0" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-10" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-20" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-30" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-40" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-50" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-60" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-70" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-80" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-90" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-100" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-200" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-300" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-400" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-500" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-600" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-700" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-800" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-900" />
|
|
||||||
<div className="h-12 w-12 bg-custom-text-1000" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Sidebar Background:
|
|
||||||
<div className="flex flex-wrap">
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-0" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-10" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-20" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-30" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-40" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-50" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-60" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-70" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-80" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-90" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-100" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-200" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-300" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-400" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-500" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-600" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-700" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-800" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-900" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-background-1000" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div>
|
|
||||||
Sidebar Text:
|
|
||||||
<div className="flex flex-wrap">
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-0" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-10" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-20" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-30" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-40" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-50" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-60" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-70" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-80" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-90" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-100" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-200" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-300" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-400" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-500" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-600" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-700" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-800" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-900" />
|
|
||||||
<div className="h-12 w-12 bg-custom-sidebar-text-1000" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</DefaultLayout>
|
|
||||||
</UserAuthorizationLayout>
|
|
||||||
);
|
|
||||||
|
|
||||||
export default Colors;
|
|
@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useEffect } from "react";
|
||||||
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
@ -22,6 +22,8 @@ import {
|
|||||||
import { Spinner } from "components/ui";
|
import { Spinner } from "components/ui";
|
||||||
// images
|
// images
|
||||||
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
import BluePlaneLogoWithoutText from "public/plane-logos/blue-without-text.png";
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
|
import { ICurrentUserResponse, IUser } from "types";
|
||||||
// types
|
// types
|
||||||
type EmailPasswordFormValues = {
|
type EmailPasswordFormValues = {
|
||||||
email: string;
|
email: string;
|
||||||
@ -34,6 +36,12 @@ const HomePage: NextPage = () => {
|
|||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
|
const { setTheme } = useTheme();
|
||||||
|
|
||||||
|
const changeTheme = (user: IUser) => {
|
||||||
|
setTheme(user.theme.theme ?? "system");
|
||||||
|
};
|
||||||
|
|
||||||
const handleGoogleSignIn = async ({ clientId, credential }: any) => {
|
const handleGoogleSignIn = async ({ clientId, credential }: any) => {
|
||||||
try {
|
try {
|
||||||
if (clientId && credential) {
|
if (clientId && credential) {
|
||||||
@ -43,7 +51,10 @@ const HomePage: NextPage = () => {
|
|||||||
clientId,
|
clientId,
|
||||||
};
|
};
|
||||||
const response = await authenticationService.socialAuth(socialAuthPayload);
|
const response = await authenticationService.socialAuth(socialAuthPayload);
|
||||||
if (response && response?.user) mutateUser();
|
if (response && response?.user) {
|
||||||
|
mutateUser();
|
||||||
|
changeTheme(response.user);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw Error("Cant find credentials");
|
throw Error("Cant find credentials");
|
||||||
}
|
}
|
||||||
@ -66,7 +77,10 @@ const HomePage: NextPage = () => {
|
|||||||
clientId: process.env.NEXT_PUBLIC_GITHUB_ID,
|
clientId: process.env.NEXT_PUBLIC_GITHUB_ID,
|
||||||
};
|
};
|
||||||
const response = await authenticationService.socialAuth(socialAuthPayload);
|
const response = await authenticationService.socialAuth(socialAuthPayload);
|
||||||
if (response && response?.user) mutateUser();
|
if (response && response?.user) {
|
||||||
|
mutateUser();
|
||||||
|
changeTheme(response.user);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
throw Error("Cant find credentials");
|
throw Error("Cant find credentials");
|
||||||
}
|
}
|
||||||
@ -85,7 +99,10 @@ const HomePage: NextPage = () => {
|
|||||||
.emailLogin(formData)
|
.emailLogin(formData)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
try {
|
try {
|
||||||
if (response) mutateUser();
|
if (response) {
|
||||||
|
mutateUser();
|
||||||
|
changeTheme(response.user);
|
||||||
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "error",
|
type: "error",
|
||||||
@ -109,7 +126,10 @@ const HomePage: NextPage = () => {
|
|||||||
|
|
||||||
const handleEmailCodeSignIn = async (response: any) => {
|
const handleEmailCodeSignIn = async (response: any) => {
|
||||||
try {
|
try {
|
||||||
if (response) mutateUser();
|
if (response) {
|
||||||
|
mutateUser();
|
||||||
|
changeTheme(response.user);
|
||||||
|
}
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
setToastAlert({
|
setToastAlert({
|
||||||
type: "error",
|
type: "error",
|
||||||
@ -120,6 +140,10 @@ const HomePage: NextPage = () => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTheme("system");
|
||||||
|
}, [setTheme]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DefaultLayout>
|
<DefaultLayout>
|
||||||
{isLoading ? (
|
{isLoading ? (
|
||||||
|
@ -1,6 +1,9 @@
|
|||||||
import React, { useState, useEffect } from "react";
|
import React, { useState, useEffect } from "react";
|
||||||
// next imports
|
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
// next-themes
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
// layouts
|
// layouts
|
||||||
import DefaultLayout from "layouts/default-layout";
|
import DefaultLayout from "layouts/default-layout";
|
||||||
// services
|
// services
|
||||||
@ -17,11 +20,17 @@ const MagicSignIn: NextPage = () => {
|
|||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
const { user, isLoading, mutateUser } = useUserAuth("sign-in");
|
const { setTheme } = useTheme();
|
||||||
|
|
||||||
|
const { mutateUser } = useUserAuth("sign-in");
|
||||||
|
|
||||||
const [isSigningIn, setIsSigningIn] = useState(false);
|
const [isSigningIn, setIsSigningIn] = useState(false);
|
||||||
const [errorSigningIn, setErrorSignIn] = useState<string | undefined>();
|
const [errorSigningIn, setErrorSignIn] = useState<string | undefined>();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTheme("system");
|
||||||
|
}, [setTheme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setIsSigningIn(() => false);
|
setIsSigningIn(() => false);
|
||||||
setErrorSignIn(() => undefined);
|
setErrorSignIn(() => undefined);
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
import Router from "next/router";
|
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
import useSWR, { mutate } from "swr";
|
import useSWR, { mutate } from "swr";
|
||||||
@ -32,7 +31,7 @@ import { CURRENT_USER, USER_WORKSPACE_INVITATIONS } from "constants/fetch-keys";
|
|||||||
const Onboarding: NextPage = () => {
|
const Onboarding: NextPage = () => {
|
||||||
const [step, setStep] = useState<number | null>(null);
|
const [step, setStep] = useState<number | null>(null);
|
||||||
|
|
||||||
const { theme } = useTheme();
|
const { theme, setTheme } = useTheme();
|
||||||
|
|
||||||
const { user, isLoading: userLoading } = useUserAuth("onboarding");
|
const { user, isLoading: userLoading } = useUserAuth("onboarding");
|
||||||
|
|
||||||
@ -117,6 +116,10 @@ const Onboarding: NextPage = () => {
|
|||||||
await userService.updateUserOnBoard({ userRole: user.role }, user);
|
await userService.updateUserOnBoard({ userRole: user.role }, user);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTheme("system");
|
||||||
|
}, [setTheme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const handleStepChange = async () => {
|
const handleStepChange = async () => {
|
||||||
if (!user || !invitations) return;
|
if (!user || !invitations) return;
|
||||||
|
@ -3,6 +3,8 @@ import React, { useEffect, useState } from "react";
|
|||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
|
|
||||||
|
// next-themes
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
// react-hook-form
|
// react-hook-form
|
||||||
import { useForm } from "react-hook-form";
|
import { useForm } from "react-hook-form";
|
||||||
// hooks
|
// hooks
|
||||||
@ -31,6 +33,8 @@ const ResetPasswordPage: NextPage = () => {
|
|||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
|
const { setTheme } = useTheme();
|
||||||
|
|
||||||
const {
|
const {
|
||||||
register,
|
register,
|
||||||
handleSubmit,
|
handleSubmit,
|
||||||
@ -76,6 +80,10 @@ const ResetPasswordPage: NextPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTheme("system");
|
||||||
|
}, [setTheme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/");
|
if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/");
|
||||||
else setIsLoading(false);
|
else setIsLoading(false);
|
||||||
|
@ -3,6 +3,8 @@ import React, { useEffect, useState } from "react";
|
|||||||
import Image from "next/image";
|
import Image from "next/image";
|
||||||
import { useRouter } from "next/router";
|
import { useRouter } from "next/router";
|
||||||
|
|
||||||
|
// next-themes
|
||||||
|
import { useTheme } from "next-themes";
|
||||||
// services
|
// services
|
||||||
import authenticationService from "services/authentication.service";
|
import authenticationService from "services/authentication.service";
|
||||||
// hooks
|
// hooks
|
||||||
@ -31,6 +33,8 @@ const SignUp: NextPage = () => {
|
|||||||
|
|
||||||
const { setToastAlert } = useToast();
|
const { setToastAlert } = useToast();
|
||||||
|
|
||||||
|
const { setTheme } = useTheme();
|
||||||
|
|
||||||
const { mutateUser } = useUserAuth("sign-in");
|
const { mutateUser } = useUserAuth("sign-in");
|
||||||
|
|
||||||
const handleSignUp = async (formData: EmailPasswordFormValues) => {
|
const handleSignUp = async (formData: EmailPasswordFormValues) => {
|
||||||
@ -62,6 +66,10 @@ const SignUp: NextPage = () => {
|
|||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setTheme("system");
|
||||||
|
}, [setTheme]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/");
|
if (parseInt(process.env.NEXT_PUBLIC_ENABLE_OAUTH || "0")) router.push("/");
|
||||||
else setIsLoading(false);
|
else setIsLoading(false);
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
// services
|
// services
|
||||||
import APIService from "services/api.service";
|
import APIService from "services/api.service";
|
||||||
|
import { ICurrentUserResponse } from "types";
|
||||||
|
|
||||||
const { NEXT_PUBLIC_API_BASE_URL } = process.env;
|
const { NEXT_PUBLIC_API_BASE_URL } = process.env;
|
||||||
|
|
||||||
@ -32,7 +33,11 @@ class AuthService extends APIService {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
async socialAuth(data: any) {
|
async socialAuth(data: any): Promise<{
|
||||||
|
access_token: string;
|
||||||
|
refresh_toke: string;
|
||||||
|
user: ICurrentUserResponse;
|
||||||
|
}> {
|
||||||
return this.post("/api/social-auth/", data, { headers: {} })
|
return this.post("/api/social-auth/", data, { headers: {} })
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.setAccessToken(response?.data?.access_token);
|
this.setAccessToken(response?.data?.access_token);
|
||||||
|
1
apps/app/types/users.d.ts
vendored
1
apps/app/types/users.d.ts
vendored
@ -46,6 +46,7 @@ export interface ICustomTheme {
|
|||||||
sidebarText: string;
|
sidebarText: string;
|
||||||
darkPalette: boolean;
|
darkPalette: boolean;
|
||||||
palette: string;
|
palette: string;
|
||||||
|
theme: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ICurrentUserResponse extends IUser {
|
export interface ICurrentUserResponse extends IUser {
|
||||||
|
Loading…
Reference in New Issue
Block a user