Files
diplom-frontend/app/routes/app/settings.tsx
2025-05-21 08:46:12 +03:00

166 lines
6.3 KiB
TypeScript

import { zodResolver } from "@hookform/resolvers/zod";
import { LogOutIcon, UserIcon } from "lucide-react";
import { useForm } from "react-hook-form";
import { z } from "zod";
import { Button } from "~/components/ui/button";
import {
Form,
FormControl,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "~/components/ui/form";
import { IconUploadField } from "~/components/ui/icon-upload-field";
import { Input } from "~/components/ui/input";
import file from "~/lib/api/client/file";
import { patchUser } from "~/lib/api/client/user";
import { useTokenStore } from "~/stores/token-store";
import { useUsersStore } from "~/stores/users-store";
const schema = z.object({
displayName: z.string().min(1).max(32).optional().nullable(),
avatar: z.instanceof(File).optional().nullable(),
});
// Note: This is a mockup based on the provided store structure
export default function Settings() {
const setToken = useTokenStore((state) => state.setToken);
const user = useUsersStore((state) => state.getCurrentUser());
const form = useForm<z.infer<typeof schema>>({
resolver: zodResolver(schema),
defaultValues: {
displayName: user?.displayName,
avatar: undefined,
},
});
const onSubmit = async (values: z.infer<typeof schema>) => {
if (!values) return;
let avatarId: string | null | undefined =
values.avatar === null ? null : undefined;
if (values.avatar) {
avatarId = (await file.uploadFile(values.avatar))[0];
}
const response = await patchUser({
displayName:
values.displayName === user?.displayName
? undefined
: values.displayName === ""
? null
: values.displayName,
avatarId,
});
form.control._defaultValues = {
displayName: user?.displayName,
avatar: undefined,
};
form.reset();
};
const onLogout = () => {
setToken(undefined);
window.location.reload();
};
return (
<div className="flex min-h-screen">
{/* Sidebar */}
<div className="w-64 border-r p-6 flex flex-col">
<h1 className="text-2xl font-bold mb-6">Settings</h1>
<Button variant="outline" className="justify-start mb-2 w-full">
<UserIcon className="mr-2 h-4 w-4" />
Profile
</Button>
<div className="mt-auto">
<Button
variant="destructive"
className="w-full"
onClick={onLogout}
>
<LogOutIcon className="mr-2 h-4 w-4" />
Logout
</Button>
</div>
</div>
{/* Main content */}
<div className="flex-1 p-8">
<div className="max-w-2xl mx-auto">
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-4"
>
<FormField
control={form.control}
name="avatar"
render={({ field, fieldState }) => (
<FormItem>
<FormLabel
required={
!schema.shape.avatar.isOptional()
}
>
Avatar
</FormLabel>
<FormControl>
<div className="flex flex-col items-center justify-center">
<IconUploadField
defaultPreview={
user?.avatarUrl
}
formDefaultValue={
user?.avatarUrl
}
field={field}
error={fieldState.error}
/>
</div>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="displayName"
render={({ field }) => (
<FormItem>
<FormLabel
required={
!schema.shape.displayName.isOptional()
}
>
Display Name
</FormLabel>
<FormControl>
<Input {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<Button
type="submit"
disabled={form.formState.isSubmitting}
>
{form.formState.isSubmitting
? "Saving..."
: "Save"}
</Button>
</form>
</Form>
</div>
</div>
</div>
);
}