on admin dashboard header create a user icon where i can manage admin us

This commit is contained in:
Leon Serfaty G
2025-07-17 11:25:03 +00:00
parent 9a4ec2a118
commit 42bfc7ca05
3 changed files with 247 additions and 7 deletions
+61 -7
View File
@@ -1,5 +1,6 @@
import * as React from 'react';
import Link from 'next/link';
import {
Sidebar,
SidebarContent,
@@ -15,15 +16,34 @@ import {
SidebarTrigger,
SidebarFooter,
} from '@/components/ui/sidebar';
import { LayoutDashboard, LogOut, Settings, Mail } from 'lucide-react';
import { signOut } from '@/lib/auth';
import { LayoutDashboard, LogOut, Settings, Mail, User as UserIcon } from 'lucide-react';
import { signOut, getSession } from '@/lib/auth';
import { Button } from '@/components/ui/button';
import {
DropdownMenu,
DropdownMenuContent,
DropdownMenuItem,
DropdownMenuLabel,
DropdownMenuSeparator,
DropdownMenuTrigger,
} from '@/components/ui/dropdown-menu';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import type { User } from '@/lib/types';
export default function AdminLayout({
export default async function AdminLayout({
children,
}: {
children: React.ReactNode;
}) {
const session = (await getSession()) as User | null;
const userInitials = session?.name
? session.name
.split(' ')
.map((n) => n[0])
.join('')
: '';
return (
<SidebarProvider>
<Sidebar>
@@ -47,6 +67,12 @@ export default function AdminLayout({
Settings
</SidebarMenuButton>
<SidebarMenuSub>
<SidebarMenuSubItem>
<SidebarMenuSubButton href="/admin/settings/user" >
<UserIcon />
User
</SidebarMenuSubButton>
</SidebarMenuSubItem>
<SidebarMenuSubItem>
<SidebarMenuSubButton href="/admin/settings/email" >
<Mail />
@@ -73,11 +99,39 @@ export default function AdminLayout({
</SidebarFooter>
</Sidebar>
<SidebarInset>
<header className="flex h-12 items-center justify-between border-b bg-background p-2 px-4">
<SidebarTrigger />
<h1 className="text-lg font-semibold">Dashboard</h1>
<header className="flex h-14 items-center justify-between border-b bg-background px-4 lg:h-[60px] lg:px-6">
<div className="flex items-center gap-4">
<SidebarTrigger className="md:hidden"/>
<h1 className="text-lg font-semibold md:text-2xl">Dashboard</h1>
</div>
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="secondary" size="icon" className="rounded-full">
<Avatar>
<AvatarImage src={`https://placehold.co/100x100.png`} alt={session?.name ?? ''} />
<AvatarFallback>{userInitials}</AvatarFallback>
</Avatar>
<span className="sr-only">Toggle user menu</span>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent align="end">
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem asChild>
<Link href="/admin/settings/user">Settings</Link>
</DropdownMenuItem>
<DropdownMenuSeparator />
<form action={signOut} className="w-full">
<DropdownMenuItem asChild>
<button type="submit" className="w-full">
Sign Out
</button>
</DropdownMenuItem>
</form>
</DropdownMenuContent>
</DropdownMenu>
</header>
<main className="flex-1 p-4">{children}</main>
<main className="flex-1 p-4 lg:p-6">{children}</main>
</SidebarInset>
</SidebarProvider>
);
+122
View File
@@ -0,0 +1,122 @@
"use client";
import { useForm, type SubmitHandler } from "react-hook-form";
import { zodResolver } from "@hookform/resolvers/zod";
import { z } from "zod";
import { Button } from "@/components/ui/button";
import {
Card,
CardContent,
CardDescription,
CardFooter,
CardHeader,
CardTitle,
} from "@/components/ui/card";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label";
import { useToast } from "@/hooks/use-toast";
import { useEffect, useTransition } from "react";
import { getUser, updateUser } from "@/lib/actions/user";
import { User } from "@/lib/types";
const userProfileSchema = z.object({
name: z.string().min(1, "Name is required"),
email: z.string().email("Invalid email address"),
password: z.string().min(6, "Password must be at least 6 characters").optional().or(z.literal('')),
confirmPassword: z.string().optional(),
}).refine((data) => data.password === data.confirmPassword, {
message: "Passwords don't match",
path: ["confirmPassword"],
});
type UserProfileFormValues = z.infer<typeof userProfileSchema>;
export default function UserProfilePage() {
const { toast } = useToast();
const [isSaving, startSavingTransition] = useTransition();
const {
register,
handleSubmit,
reset,
formState: { errors },
} = useForm<UserProfileFormValues>({
resolver: zodResolver(userProfileSchema),
});
useEffect(() => {
async function fetchUser() {
const user = await getUser();
if (user) {
reset({ name: user.name, email: user.email });
}
}
fetchUser();
}, [reset]);
const onSubmit: SubmitHandler<UserProfileFormValues> = async (data) => {
startSavingTransition(async () => {
const result = await updateUser({
name: data.name,
email: data.email,
password: data.password || undefined,
});
if (result.success) {
toast({
title: "Profile Updated",
description: "Your profile has been updated successfully.",
});
// Clear password fields after successful submission
reset({ ...data, password: '', confirmPassword: '' });
} else {
toast({
title: "Update Failed",
description: result.error || "An unknown error occurred.",
variant: "destructive",
});
}
});
};
return (
<Card className="max-w-2xl">
<form onSubmit={handleSubmit(onSubmit)}>
<CardHeader>
<CardTitle>My Account</CardTitle>
<CardDescription>
Update your account details. Leave password fields blank to keep the current password.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-6">
<div className="grid gap-2">
<Label htmlFor="name">Name</Label>
<Input id="name" {...register("name")} />
{errors.name && <p className="text-sm text-destructive">{errors.name.message}</p>}
</div>
<div className="grid gap-2">
<Label htmlFor="email">Email</Label>
<Input id="email" type="email" {...register("email")} />
{errors.email && <p className="text-sm text-destructive">{errors.email.message}</p>}
</div>
<div className="grid gap-2">
<Label htmlFor="password">New Password</Label>
<Input id="password" type="password" {...register("password")} />
{errors.password && <p className="text-sm text-destructive">{errors.password.message}</p>}
</div>
<div className="grid gap-2">
<Label htmlFor="confirmPassword">Confirm New Password</Label>
<Input id="confirmPassword" type="password" {...register("confirmPassword")} />
{errors.confirmPassword && <p className="text-sm text-destructive">{errors.confirmPassword.message}</p>}
</div>
</CardContent>
<CardFooter className="flex justify-end">
<Button type="submit" disabled={isSaving}>
{isSaving ? 'Saving...' : 'Save Changes'}
</Button>
</CardFooter>
</form>
</Card>
);
}