fix: refactored config screen for multiple options

This commit is contained in:
Palanikannan M
2025-01-13 18:18:57 +05:30
parent 557020cd47
commit f35083bc4f
7 changed files with 185 additions and 58 deletions

View File

@@ -1,78 +1,25 @@
"use client";
import { useEffect, useState } from "react";
import { observer } from "mobx-react";
import { useTheme } from "next-themes";
import { IUserTheme } from "@plane/types";
import { setPromiseToast } from "@plane/ui";
// components
import { LogoSpinner } from "@/components/common";
import { CustomThemeSelector, ThemeSwitch, PageHead } from "@/components/core";
import { PageHead } from "@/components/core";
import { PreferencesList } from "@/components/preferences/list";
import { ProfileSettingContentHeader, ProfileSettingContentWrapper } from "@/components/profile";
// constants
import { I_THEME_OPTION, THEME_OPTIONS } from "@/constants/themes";
// helpers
import { applyTheme, unsetCustomCssVariables } from "@/helpers/theme.helper";
// hooks
import { useUserProfile } from "@/hooks/store";
const ProfileAppearancePage = observer(() => {
const { setTheme } = useTheme();
// states
const [currentTheme, setCurrentTheme] = useState<I_THEME_OPTION | null>(null);
// hooks
const { data: userProfile, updateUserTheme } = useUserProfile();
useEffect(() => {
if (userProfile?.theme?.theme) {
const userThemeOption = THEME_OPTIONS.find((t) => t.value === userProfile?.theme?.theme);
if (userThemeOption) {
setCurrentTheme(userThemeOption);
}
}
}, [userProfile?.theme?.theme]);
const handleThemeChange = (themeOption: I_THEME_OPTION) => {
applyThemeChange({ theme: themeOption.value });
const updateCurrentUserThemePromise = updateUserTheme({ theme: themeOption.value });
setPromiseToast(updateCurrentUserThemePromise, {
loading: "Updating theme...",
success: {
title: "Success!",
message: () => "Theme updated successfully!",
},
error: {
title: "Error!",
message: () => "Failed to Update the theme",
},
});
};
const applyThemeChange = (theme: Partial<IUserTheme>) => {
setTheme(theme?.theme || "system");
if (theme?.theme === "custom" && theme?.palette) {
applyTheme(theme?.palette !== ",,,," ? theme?.palette : "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5", false);
} else unsetCustomCssVariables();
};
const { data: userProfile } = useUserProfile();
return (
<>
<PageHead title="Profile - Appearance" />
{userProfile ? (
<ProfileSettingContentWrapper>
<ProfileSettingContentHeader title="Appearance" />
<div className="grid grid-cols-12 gap-4 py-6 sm:gap-16">
<div className="col-span-12 sm:col-span-6">
<h4 className="text-lg font-semibold text-custom-text-100">Theme</h4>
<p className="text-sm text-custom-text-200">Select or customize your interface color scheme.</p>
</div>
<div className="col-span-12 sm:col-span-6">
<ThemeSwitch value={currentTheme} onChange={handleThemeChange} />
</div>
</div>
{userProfile?.theme?.theme === "custom" && <CustomThemeSelector applyThemeChange={applyThemeChange} />}
<ProfileSettingContentHeader title="Preferences" />
<PreferencesList />
</ProfileSettingContentWrapper>
) : (
<div className="grid h-full w-full place-items-center px-4 sm:px-0">

View File

@@ -0,0 +1,24 @@
import { SmoothCursorToggle } from "./smooth-cursor-toggle";
import { ThemeSwitcher } from "./theme-switcher";
export interface PreferenceOption {
id: string;
title: string;
description: string;
component: React.ComponentType<{ option: PreferenceOption }>;
}
export const PREFERENCE_OPTIONS: PreferenceOption[] = [
{
id: "theme",
title: "Theme",
description: "Select or customize your interface color scheme.",
component: ThemeSwitcher,
},
{
id: "smoothCursor",
title: "Smooth cursor movement",
description: "Select the cursor motion style that feels right for you",
component: SmoothCursorToggle,
},
];

View File

@@ -0,0 +1,2 @@
export * from "./theme-switcher";
export * from "./section";

View File

@@ -0,0 +1,23 @@
import { observer } from "mobx-react";
import { PREFERENCE_OPTIONS } from "./config";
import { PreferencesSection } from "./section";
export const PreferencesList = observer(() => {
console.log("list");
return (
<div className="space-y-6">
{PREFERENCE_OPTIONS.map((option) => {
const Component = option.component;
return <Component key={option.id} option={option} />;
return (
<PreferencesSection
key={option.id}
title={option.title}
description={option.description}
control={<Component />}
/>
);
})}
</div>
);
});

View File

@@ -0,0 +1,15 @@
interface SettingsSectionProps {
title: string;
description: string;
control: React.ReactNode;
}
export const PreferencesSection = ({ title, description, control }: SettingsSectionProps) => (
<div className="grid grid-cols-12 gap-4 py-6 sm:gap-16">
<div className="col-span-12 sm:col-span-6">
<h4 className="text-lg font-semibold text-custom-text-100">{title}</h4>
<p className="text-sm text-custom-text-200">{description}</p>
</div>
<div className="col-span-12 sm:col-span-6">{control}</div>
</div>
);

View File

@@ -0,0 +1,24 @@
import { ToggleSwitch } from "@plane/ui";
import { PreferenceOption } from "./config";
import { PreferencesSection } from ".";
export const SmoothCursorToggle = (props: { option: PreferenceOption }) => {
console.log("toggled");
return (
<PreferencesSection
title={props.option.title}
description={props.option.description}
control={
<ToggleSwitch
value={true}
onChange={() => {
console.log("toggled");
}}
label={"smooth-cursor-toggle"}
size={"sm"}
className={"some"}
/>
}
/>
);
};

View File

@@ -0,0 +1,92 @@
"use client";
import { useEffect, useState, useCallback } from "react";
import { observer } from "mobx-react";
import { useTheme } from "next-themes";
import { IUserTheme } from "@plane/types";
import { setPromiseToast } from "@plane/ui";
// components
import { CustomThemeSelector, ThemeSwitch } from "@/components/core";
// constants
import { I_THEME_OPTION, THEME_OPTIONS } from "@/constants/themes";
// helpers
import { applyTheme, unsetCustomCssVariables } from "@/helpers/theme.helper";
// hooks
import { useUserProfile } from "@/hooks/store";
import { PreferenceOption } from "./config";
import { PreferencesSection } from ".";
export const ThemeSwitcher = observer((props: { option: PreferenceOption }) => {
// hooks
const { setTheme } = useTheme();
const { data: userProfile, updateUserTheme } = useUserProfile();
// states
const [currentTheme, setCurrentTheme] = useState<I_THEME_OPTION | null>(null);
// initialize theme
useEffect(() => {
if (!userProfile?.theme?.theme) return;
const userThemeOption = THEME_OPTIONS.find((t) => t.value === userProfile.theme.theme);
if (userThemeOption) {
setCurrentTheme(userThemeOption);
}
}, [userProfile?.theme?.theme]);
// handlers
const applyThemeChange = useCallback(
(theme: Partial<IUserTheme>) => {
const themeValue = theme?.theme || "system";
setTheme(themeValue);
if (theme?.theme === "custom" && theme?.palette) {
const defaultPalette = "#0d101b,#c5c5c5,#3f76ff,#0d101b,#c5c5c5";
const palette = theme.palette !== ",,,," ? theme.palette : defaultPalette;
applyTheme(palette, false);
} else {
unsetCustomCssVariables();
}
},
[setTheme]
);
const handleThemeChange = useCallback(
async (themeOption: I_THEME_OPTION) => {
try {
applyThemeChange({ theme: themeOption.value });
const updatePromise = updateUserTheme({ theme: themeOption.value });
setPromiseToast(updatePromise, {
loading: "Updating theme...",
success: {
title: "Success!",
message: () => "Theme updated successfully!",
},
error: {
title: "Error!",
message: () => "Failed to update the theme",
},
});
} catch (error) {
console.error("Error updating theme:", error);
}
},
[applyThemeChange, updateUserTheme]
);
if (!userProfile) return null;
return (
<>
<PreferencesSection
title={props.option.title}
description={props.option.description}
control={<ThemeSwitch value={currentTheme} onChange={handleThemeChange} />}
/>
{userProfile.theme?.theme === "custom" && <CustomThemeSelector applyThemeChange={applyThemeChange} />}
</>
);
});