MIF_E31221222/sigap-website/app/protected/(admin-pages)/_components/users/edit-user.tsx

483 lines
16 KiB
TypeScript

"use client";
import { useState, useEffect } from "react";
import { z } from "zod";
import { useForm } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { format } from "date-fns";
import { CalendarIcon, X } from "lucide-react";
import {
Sheet,
SheetContent,
SheetDescription,
SheetHeader,
SheetTitle,
SheetFooter,
SheetClose,
} from "@/app/_components/ui/sheet";
import {
Form,
FormControl,
FormDescription,
FormField,
FormItem,
FormLabel,
FormMessage,
} from "@/app/_components/ui/form";
import { Input } from "@/app/_components/ui/input";
import { Button } from "@/app/_components/ui/button";
import {
Select,
SelectContent,
SelectItem,
SelectTrigger,
SelectValue,
} from "@/app/_components/ui/select";
import { Textarea } from "@/app/_components/ui/textarea";
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/app/_components/ui/avatar";
import { Calendar } from "@/app/_components/ui/calendar";
import {
Popover,
PopoverContent,
PopoverTrigger,
} from "@/app/_components/ui/popover";
import { Separator } from "@/app/_components/ui/separator";
import {
Tabs,
TabsContent,
TabsList,
TabsTrigger,
} from "@/app/_components/ui/tabs";
import { User } from "./users-table";
// Create a schema for form validation
const userFormSchema = z.object({
// User fields
email: z.string().email({ message: "Please enter a valid email address" }),
firstName: z.string().min(1, { message: "First name is required" }),
lastName: z.string().min(1, { message: "Last name is required" }),
avatar: z.string().optional(),
role: z.enum(["admin", "staff", "user"]),
password: z
.string()
.min(8, { message: "Password must be at least 8 characters long" })
.optional(),
// Profile fields
bio: z.string().optional(),
phone: z.string().optional(),
address: z.string().optional(),
city: z.string().optional(),
country: z.string().optional(),
birthDate: z.date().optional(),
});
type UserFormValues = z.infer<typeof userFormSchema>;
// Extended user type to include profile information
type UserWithProfile = User & {
profile?: {
bio?: string;
phone?: string;
address?: string;
city?: string;
country?: string;
birthDate?: Date;
};
};
interface EditUserSheetProps {
open: boolean;
onOpenChange: (open: boolean) => void;
user?: UserWithProfile | null;
onSave: (user: UserWithProfile) => void;
isNew?: boolean;
}
export function EditUserSheet({
open,
onOpenChange,
user,
onSave,
isNew = false,
}: EditUserSheetProps) {
const form = useForm<UserFormValues>({
resolver: zodResolver(userFormSchema),
defaultValues: {
email: "",
firstName: "",
lastName: "",
avatar: "/placeholder.svg?height=40&width=40",
role: "user",
password: isNew ? "" : undefined,
bio: "",
phone: "",
address: "",
city: "",
country: "",
birthDate: undefined,
},
});
// Update form when user changes
useEffect(() => {
if (user) {
const birthDate = user.profile?.birthDate
? new Date(user.profile.birthDate)
: undefined;
form.reset({
email: user.email,
firstName: user.firstName,
lastName: user.lastName,
avatar: user.avatar,
role: user.role,
// Don't populate password for existing users
bio: user.profile?.bio || "",
phone: user.profile?.phone || "",
address: user.profile?.address || "",
city: user.profile?.city || "",
country: user.profile?.country || "",
birthDate: birthDate,
});
} else if (isNew) {
form.reset({
email: "",
firstName: "",
lastName: "",
avatar: "/placeholder.svg?height=40&width=40",
role: "user",
password: "",
bio: "",
phone: "",
address: "",
city: "",
country: "",
birthDate: undefined,
});
}
}, [user, isNew, form]);
const onSubmit = (data: UserFormValues) => {
// Prepare the user object with profile
const updatedUser: UserWithProfile = {
id: user?.id || "new-id", // In a real app, this would be handled by the backend
email: data.email,
firstName: data.firstName || "",
lastName: data.lastName || "",
avatar: data.avatar || "/placeholder.svg?height=40&width=40",
role: data.role,
status: user?.status || "active",
lastSignedIn:
user?.lastSignedIn || new Date().toISOString().split("T")[0],
profile: {
bio: data.bio,
phone: data.phone,
address: data.address,
city: data.city,
country: data.country,
birthDate: data.birthDate,
},
};
// Save the user
onSave(updatedUser);
// Close the sheet
onOpenChange(false);
};
return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent className="sm:max-w-xl overflow-y-auto">
<SheetHeader>
<SheetTitle>{isNew ? "Add New User" : "Edit User"}</SheetTitle>
<SheetDescription>
{isNew
? "Fill in the details to create a new user account."
: "Make changes to the user profile here."}
</SheetDescription>
</SheetHeader>
<div className="py-4">
<Tabs defaultValue="user" className="w-full">
<TabsList className="grid w-full grid-cols-2">
<TabsTrigger value="user">User Details</TabsTrigger>
<TabsTrigger value="profile">Profile Information</TabsTrigger>
</TabsList>
<Form {...form}>
<form
onSubmit={form.handleSubmit(onSubmit)}
className="space-y-6 pt-6"
>
<TabsContent value="user" className="space-y-4">
<div className="flex justify-center mb-6">
<Avatar className="h-24 w-24">
<AvatarImage
src={
form.watch("avatar") ||
"/placeholder.svg?height=96&width=96"
}
alt="User avatar"
/>
<AvatarFallback>
{form.watch("firstName")?.charAt(0) || ""}
{form.watch("lastName")?.charAt(0) || ""}
</AvatarFallback>
</Avatar>
</div>
<div className="grid grid-cols-2 gap-4">
<FormField
control={form.control}
name="firstName"
render={({ field }) => (
<FormItem>
<FormLabel>First Name</FormLabel>
<FormControl>
<Input placeholder="First name" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="lastName"
render={({ field }) => (
<FormItem>
<FormLabel>Last Name</FormLabel>
<FormControl>
<Input placeholder="Last name" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="email"
render={({ field }) => (
<FormItem>
<FormLabel>Email</FormLabel>
<FormControl>
<Input
placeholder="Email address"
type="email"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
{isNew && (
<FormField
control={form.control}
name="password"
render={({ field }) => (
<FormItem>
<FormLabel>Password</FormLabel>
<FormControl>
<Input
placeholder="Set password"
type="password"
{...field}
/>
</FormControl>
<FormDescription>
Minimum 8 characters
</FormDescription>
<FormMessage />
</FormItem>
)}
/>
)}
<FormField
control={form.control}
name="role"
render={({ field }) => (
<FormItem>
<FormLabel>Role</FormLabel>
<Select
onValueChange={field.onChange}
defaultValue={field.value}
value={field.value}
>
<FormControl>
<SelectTrigger>
<SelectValue placeholder="Select a role" />
</SelectTrigger>
</FormControl>
<SelectContent>
<SelectItem value="admin">Admin</SelectItem>
<SelectItem value="staff">Staff</SelectItem>
<SelectItem value="user">User</SelectItem>
</SelectContent>
</Select>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="avatar"
render={({ field }) => (
<FormItem>
<FormLabel>Avatar URL</FormLabel>
<FormControl>
<Input placeholder="Avatar URL" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</TabsContent>
<TabsContent value="profile" className="space-y-4">
<FormField
control={form.control}
name="bio"
render={({ field }) => (
<FormItem>
<FormLabel>Bio</FormLabel>
<FormControl>
<Textarea
placeholder="Tell us a little about this user"
className="resize-none"
{...field}
/>
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="phone"
render={({ field }) => (
<FormItem>
<FormLabel>Phone</FormLabel>
<FormControl>
<Input placeholder="Phone number" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="address"
render={({ field }) => (
<FormItem>
<FormLabel>Address</FormLabel>
<FormControl>
<Input placeholder="Street address" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<div className="grid grid-cols-2 gap-4">
<FormField
control={form.control}
name="city"
render={({ field }) => (
<FormItem>
<FormLabel>City</FormLabel>
<FormControl>
<Input placeholder="City" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
<FormField
control={form.control}
name="country"
render={({ field }) => (
<FormItem>
<FormLabel>Country</FormLabel>
<FormControl>
<Input placeholder="Country" {...field} />
</FormControl>
<FormMessage />
</FormItem>
)}
/>
</div>
<FormField
control={form.control}
name="birthDate"
render={({ field }) => (
<FormItem className="flex flex-col">
<FormLabel>Date of Birth</FormLabel>
<Popover>
<PopoverTrigger asChild>
<FormControl>
<Button
variant={"outline"}
className={`w-full pl-3 text-left font-normal ${
!field.value && "text-muted-foreground"
}`}
>
{field.value ? (
format(field.value, "PPP")
) : (
<span>Pick a date</span>
)}
<CalendarIcon className="ml-auto h-4 w-4 opacity-50" />
</Button>
</FormControl>
</PopoverTrigger>
<PopoverContent className="w-auto p-0" align="start">
<Calendar
mode="single"
selected={field.value}
onSelect={field.onChange}
disabled={(date: Date) =>
date > new Date() ||
date < new Date("1900-01-01")
}
initialFocus
/>
</PopoverContent>
</Popover>
<FormMessage />
</FormItem>
)}
/>
</TabsContent>
<SheetFooter className="pt-4">
<SheetClose asChild>
<Button variant="outline" type="button">
Cancel
</Button>
</SheetClose>
<Button type="submit">
{isNew ? "Create User" : "Save Changes"}
</Button>
</SheetFooter>
</form>
</Form>
</Tabs>
</div>
</SheetContent>
</Sheet>
);
}