refactor: settings
This commit is contained in:
parent
975ff23799
commit
1f1d584eea
4 changed files with 56 additions and 28 deletions
|
|
@ -1,7 +1,6 @@
|
|||
import { useMutation, useQuery } from "@tanstack/react-query";
|
||||
import { Download, KeyRound, User, X, Users, Settings as SettingsIcon } from "lucide-react";
|
||||
import { useState } from "react";
|
||||
import { useNavigate, useSearchParams } from "react-router";
|
||||
import { toast } from "sonner";
|
||||
import {
|
||||
downloadResticPasswordMutation,
|
||||
|
|
@ -24,31 +23,20 @@ import { Label } from "~/client/components/ui/label";
|
|||
import { Switch } from "~/client/components/ui/switch";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "~/client/components/ui/tabs";
|
||||
import { authClient } from "~/client/lib/auth-client";
|
||||
import { appContext } from "~/context";
|
||||
import { type AppContext } from "~/context";
|
||||
import { TwoFactorSection } from "../components/two-factor-section";
|
||||
import { UserManagement } from "../components/user-management";
|
||||
import type { Route } from "./+types/settings";
|
||||
import { useNavigate, useSearch } from "@tanstack/react-router";
|
||||
|
||||
export const handle = {
|
||||
breadcrumb: () => [{ label: "Settings" }],
|
||||
};
|
||||
|
||||
export function meta(_: Route.MetaArgs) {
|
||||
return [
|
||||
{ title: "Zerobyte - Settings" },
|
||||
{
|
||||
name: "description",
|
||||
content: "Manage your account settings and preferences.",
|
||||
},
|
||||
];
|
||||
}
|
||||
type Props = {
|
||||
appContext: AppContext;
|
||||
};
|
||||
|
||||
export async function clientLoader({ context }: Route.LoaderArgs) {
|
||||
const ctx = context.get(appContext);
|
||||
return ctx;
|
||||
}
|
||||
|
||||
export default function Settings({ loaderData }: Route.ComponentProps) {
|
||||
export function SettingsPage({ appContext }: Props) {
|
||||
const [currentPassword, setCurrentPassword] = useState("");
|
||||
const [newPassword, setNewPassword] = useState("");
|
||||
const [confirmPassword, setConfirmPassword] = useState("");
|
||||
|
|
@ -56,11 +44,11 @@ export default function Settings({ loaderData }: Route.ComponentProps) {
|
|||
const [downloadPassword, setDownloadPassword] = useState("");
|
||||
const [isChangingPassword, setIsChangingPassword] = useState(false);
|
||||
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const activeTab = searchParams.get("tab") || "account";
|
||||
const { tab } = useSearch({ from: "/(dashboard)/settings/" });
|
||||
const activeTab = tab || "account";
|
||||
|
||||
const navigate = useNavigate();
|
||||
const isAdmin = loaderData.user?.role === "admin";
|
||||
const isAdmin = appContext.user?.role === "admin";
|
||||
|
||||
const registrationStatusQuery = useQuery({
|
||||
...getRegistrationStatusOptions(),
|
||||
|
|
@ -84,7 +72,7 @@ export default function Settings({ loaderData }: Route.ComponentProps) {
|
|||
await authClient.signOut({
|
||||
fetchOptions: {
|
||||
onSuccess: () => {
|
||||
void navigate("/login", { replace: true });
|
||||
void navigate({ to: "/login", replace: true });
|
||||
},
|
||||
onError: ({ error }) => {
|
||||
console.error(error);
|
||||
|
|
@ -118,7 +106,7 @@ export default function Settings({ loaderData }: Route.ComponentProps) {
|
|||
},
|
||||
});
|
||||
|
||||
const handleChangePassword = async (e: React.FormEvent) => {
|
||||
const handleChangePassword = async (e: React.SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (newPassword !== confirmPassword) {
|
||||
|
|
@ -157,7 +145,7 @@ export default function Settings({ loaderData }: Route.ComponentProps) {
|
|||
});
|
||||
};
|
||||
|
||||
const handleDownloadResticPassword = (e: React.FormEvent) => {
|
||||
const handleDownloadResticPassword = (e: React.SubmitEvent) => {
|
||||
e.preventDefault();
|
||||
|
||||
if (!downloadPassword) {
|
||||
|
|
@ -173,7 +161,7 @@ export default function Settings({ loaderData }: Route.ComponentProps) {
|
|||
};
|
||||
|
||||
const onTabChange = (value: string) => {
|
||||
setSearchParams({ tab: value });
|
||||
navigate({ to: ".", search: () => ({ tab: value }) });
|
||||
};
|
||||
|
||||
return (
|
||||
|
|
@ -198,7 +186,7 @@ export default function Settings({ loaderData }: Route.ComponentProps) {
|
|||
<CardContent className="p-6 space-y-4">
|
||||
<div className="space-y-2">
|
||||
<Label>Username</Label>
|
||||
<Input value={loaderData.user?.username || ""} disabled className="max-w-md" />
|
||||
<Input value={appContext.user?.username || ""} disabled className="max-w-md" />
|
||||
</div>
|
||||
</CardContent>
|
||||
|
||||
|
|
@ -318,7 +306,7 @@ export default function Settings({ loaderData }: Route.ComponentProps) {
|
|||
</Dialog>
|
||||
</CardContent>
|
||||
|
||||
<TwoFactorSection twoFactorEnabled={loaderData.user?.twoFactorEnabled} />
|
||||
<TwoFactorSection twoFactorEnabled={appContext.user?.twoFactorEnabled} />
|
||||
</Card>
|
||||
</TabsContent>
|
||||
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import { Route as authOnboardingRouteImport } from './routes/(auth)/onboarding'
|
|||
import { Route as authLoginRouteImport } from './routes/(auth)/login'
|
||||
import { Route as authDownloadRecoveryKeyRouteImport } from './routes/(auth)/download-recovery-key'
|
||||
import { Route as dashboardVolumesIndexRouteImport } from './routes/(dashboard)/volumes/index'
|
||||
import { Route as dashboardSettingsIndexRouteImport } from './routes/(dashboard)/settings/index'
|
||||
import { Route as dashboardRepositoriesIndexRouteImport } from './routes/(dashboard)/repositories/index'
|
||||
import { Route as dashboardNotificationsIndexRouteImport } from './routes/(dashboard)/notifications/index'
|
||||
import { Route as dashboardBackupsIndexRouteImport } from './routes/(dashboard)/backups/index'
|
||||
|
|
@ -59,6 +60,11 @@ const dashboardVolumesIndexRoute = dashboardVolumesIndexRouteImport.update({
|
|||
path: '/volumes/',
|
||||
getParentRoute: () => dashboardRouteRoute,
|
||||
} as any)
|
||||
const dashboardSettingsIndexRoute = dashboardSettingsIndexRouteImport.update({
|
||||
id: '/settings/',
|
||||
path: '/settings/',
|
||||
getParentRoute: () => dashboardRouteRoute,
|
||||
} as any)
|
||||
const dashboardRepositoriesIndexRoute =
|
||||
dashboardRepositoriesIndexRouteImport.update({
|
||||
id: '/repositories/',
|
||||
|
|
@ -157,6 +163,7 @@ export interface FileRoutesByFullPath {
|
|||
'/backups/': typeof dashboardBackupsIndexRoute
|
||||
'/notifications/': typeof dashboardNotificationsIndexRoute
|
||||
'/repositories/': typeof dashboardRepositoriesIndexRoute
|
||||
'/settings/': typeof dashboardSettingsIndexRoute
|
||||
'/volumes/': typeof dashboardVolumesIndexRoute
|
||||
'/repositories/$repoId/$snapshotId': typeof dashboardRepositoriesRepoIdSnapshotIdRoute
|
||||
'/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute
|
||||
|
|
@ -178,6 +185,7 @@ export interface FileRoutesByTo {
|
|||
'/backups': typeof dashboardBackupsIndexRoute
|
||||
'/notifications': typeof dashboardNotificationsIndexRoute
|
||||
'/repositories': typeof dashboardRepositoriesIndexRoute
|
||||
'/settings': typeof dashboardSettingsIndexRoute
|
||||
'/volumes': typeof dashboardVolumesIndexRoute
|
||||
'/repositories/$repoId/$snapshotId': typeof dashboardRepositoriesRepoIdSnapshotIdRoute
|
||||
'/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute
|
||||
|
|
@ -201,6 +209,7 @@ export interface FileRoutesById {
|
|||
'/(dashboard)/backups/': typeof dashboardBackupsIndexRoute
|
||||
'/(dashboard)/notifications/': typeof dashboardNotificationsIndexRoute
|
||||
'/(dashboard)/repositories/': typeof dashboardRepositoriesIndexRoute
|
||||
'/(dashboard)/settings/': typeof dashboardSettingsIndexRoute
|
||||
'/(dashboard)/volumes/': typeof dashboardVolumesIndexRoute
|
||||
'/(dashboard)/repositories/$repoId/$snapshotId': typeof dashboardRepositoriesRepoIdSnapshotIdRoute
|
||||
'/(dashboard)/backups/$backupId/$snapshotId/restore': typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute
|
||||
|
|
@ -224,6 +233,7 @@ export interface FileRouteTypes {
|
|||
| '/backups/'
|
||||
| '/notifications/'
|
||||
| '/repositories/'
|
||||
| '/settings/'
|
||||
| '/volumes/'
|
||||
| '/repositories/$repoId/$snapshotId'
|
||||
| '/backups/$backupId/$snapshotId/restore'
|
||||
|
|
@ -245,6 +255,7 @@ export interface FileRouteTypes {
|
|||
| '/backups'
|
||||
| '/notifications'
|
||||
| '/repositories'
|
||||
| '/settings'
|
||||
| '/volumes'
|
||||
| '/repositories/$repoId/$snapshotId'
|
||||
| '/backups/$backupId/$snapshotId/restore'
|
||||
|
|
@ -267,6 +278,7 @@ export interface FileRouteTypes {
|
|||
| '/(dashboard)/backups/'
|
||||
| '/(dashboard)/notifications/'
|
||||
| '/(dashboard)/repositories/'
|
||||
| '/(dashboard)/settings/'
|
||||
| '/(dashboard)/volumes/'
|
||||
| '/(dashboard)/repositories/$repoId/$snapshotId'
|
||||
| '/(dashboard)/backups/$backupId/$snapshotId/restore'
|
||||
|
|
@ -325,6 +337,13 @@ declare module '@tanstack/react-router' {
|
|||
preLoaderRoute: typeof dashboardVolumesIndexRouteImport
|
||||
parentRoute: typeof dashboardRouteRoute
|
||||
}
|
||||
'/(dashboard)/settings/': {
|
||||
id: '/(dashboard)/settings/'
|
||||
path: '/settings'
|
||||
fullPath: '/settings/'
|
||||
preLoaderRoute: typeof dashboardSettingsIndexRouteImport
|
||||
parentRoute: typeof dashboardRouteRoute
|
||||
}
|
||||
'/(dashboard)/repositories/': {
|
||||
id: '/(dashboard)/repositories/'
|
||||
path: '/repositories'
|
||||
|
|
@ -438,6 +457,7 @@ interface dashboardRouteRouteChildren {
|
|||
dashboardBackupsIndexRoute: typeof dashboardBackupsIndexRoute
|
||||
dashboardNotificationsIndexRoute: typeof dashboardNotificationsIndexRoute
|
||||
dashboardRepositoriesIndexRoute: typeof dashboardRepositoriesIndexRoute
|
||||
dashboardSettingsIndexRoute: typeof dashboardSettingsIndexRoute
|
||||
dashboardVolumesIndexRoute: typeof dashboardVolumesIndexRoute
|
||||
dashboardRepositoriesRepoIdSnapshotIdRoute: typeof dashboardRepositoriesRepoIdSnapshotIdRoute
|
||||
dashboardBackupsBackupIdSnapshotIdRestoreRoute: typeof dashboardBackupsBackupIdSnapshotIdRestoreRoute
|
||||
|
|
@ -458,6 +478,7 @@ const dashboardRouteRouteChildren: dashboardRouteRouteChildren = {
|
|||
dashboardBackupsIndexRoute: dashboardBackupsIndexRoute,
|
||||
dashboardNotificationsIndexRoute: dashboardNotificationsIndexRoute,
|
||||
dashboardRepositoriesIndexRoute: dashboardRepositoriesIndexRoute,
|
||||
dashboardSettingsIndexRoute: dashboardSettingsIndexRoute,
|
||||
dashboardVolumesIndexRoute: dashboardVolumesIndexRoute,
|
||||
dashboardRepositoriesRepoIdSnapshotIdRoute:
|
||||
dashboardRepositoriesRepoIdSnapshotIdRoute,
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@ import type { AppContext } from "~/context";
|
|||
import { authMiddleware } from "~/middleware/auth";
|
||||
import { auth } from "~/server/lib/auth";
|
||||
|
||||
const fetchUser = createServerFn({ method: "GET" }).handler(async () => {
|
||||
export const fetchUser = createServerFn({ method: "GET" }).handler(async () => {
|
||||
const headers = getRequestHeaders();
|
||||
const session = await auth.api.getSession({ headers });
|
||||
|
||||
|
|
|
|||
19
app/routes/(dashboard)/settings/index.tsx
Normal file
19
app/routes/(dashboard)/settings/index.tsx
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
import { createFileRoute } from "@tanstack/react-router";
|
||||
import { fetchUser } from "../route";
|
||||
import type { AppContext } from "~/context";
|
||||
import { SettingsPage } from "~/client/modules/settings/routes/settings";
|
||||
import { type } from "arktype";
|
||||
|
||||
export const Route = createFileRoute("/(dashboard)/settings/")({
|
||||
component: RouteComponent,
|
||||
validateSearch: type({ tab: "string?" }),
|
||||
loader: async () => {
|
||||
const authContext = await fetchUser();
|
||||
return authContext as AppContext;
|
||||
},
|
||||
});
|
||||
|
||||
function RouteComponent() {
|
||||
const appContext = Route.useLoaderData();
|
||||
return <SettingsPage appContext={appContext} />;
|
||||
}
|
||||
Loading…
Reference in a new issue