diff --git a/src/app/(dashboard)/profile/page.tsx b/src/app/(dashboard)/profile/page.tsx index 1edfd7b..97a47ae 100755 --- a/src/app/(dashboard)/profile/page.tsx +++ b/src/app/(dashboard)/profile/page.tsx @@ -1,10 +1,31 @@ "use client"; +import * as React from "react"; import { redirect } from "next/navigation"; -import { PasswordChangeForm } from "~/components/profile/password-change-form"; -import { ProfileEditForm } from "~/components/profile/profile-edit-form"; -import { Badge } from "~/components/ui/badge"; +import Link from "next/link"; +import { useSession } from "~/lib/auth-client"; +import { api } from "~/trpc/react"; +import { toast } from "sonner"; +import { format } from "date-fns"; +import { + User, + Mail, + Shield, + Lock, + Settings, + Building, + Calendar, + ChevronRight, + Loader2, + Save, + X, +} from "lucide-react"; + import { Button } from "~/components/ui/button"; +import { Input } from "~/components/ui/input"; +import { Label } from "~/components/ui/label"; +import { Badge } from "~/components/ui/badge"; +import { Separator } from "~/components/ui/separator"; import { Card, CardContent, @@ -12,192 +33,326 @@ import { CardHeader, CardTitle, } from "~/components/ui/card"; -import { Separator } from "~/components/ui/separator"; -import { PageHeader } from "~/components/ui/page-header"; -import { useBreadcrumbsEffect } from "~/components/ui/breadcrumb-provider"; -import { formatRole, getRoleDescription } from "~/lib/auth-client"; -import { User, Shield, Download, Trash2, Lock, UserCog } from "lucide-react"; -import { useSession } from "~/lib/auth-client"; -import { cn } from "~/lib/utils"; -import { api } from "~/trpc/react"; +import { + Dialog, + DialogContent, + DialogDescription, + DialogFooter, + DialogHeader, + DialogTitle, + DialogTrigger, +} from "~/components/ui/dialog"; -interface ProfileUser { - id: string; - name: string | null; - email: string; - image: string | null; - roles?: Array<{ - role: "administrator" | "researcher" | "wizard" | "observer"; - grantedAt: Date; - grantedBy: string | null; - }>; -} +function ProfilePageContent() { + const { data: session } = useSession(); + const utils = api.useUtils(); + const [isEditing, setIsEditing] = React.useState(false); + const [name, setName] = React.useState(session?.user?.name ?? ""); + const [email, setEmail] = React.useState(session?.user?.email ?? ""); + const [passwordOpen, setPasswordOpen] = React.useState(false); + const [currentPassword, setCurrentPassword] = React.useState(""); + const [newPassword, setNewPassword] = React.useState(""); + const [confirmPassword, setConfirmPassword] = React.useState(""); + + const { data: userData } = api.users.get.useQuery( + { id: session?.user?.id ?? "" }, + { enabled: !!session?.user?.id }, + ); + + const updateProfile = api.users.update.useMutation({ + onSuccess: () => { + toast.success("Profile updated successfully"); + void utils.users.get.invalidate(); + setIsEditing(false); + }, + onError: (error) => { + toast.error("Failed to update profile", { description: error.message }); + }, + }); + + const changePassword = api.users.changePassword.useMutation({ + onSuccess: () => { + toast.success("Password changed successfully"); + setPasswordOpen(false); + setCurrentPassword(""); + setNewPassword(""); + setConfirmPassword(""); + }, + onError: (error) => { + toast.error("Failed to change password", { description: error.message }); + }, + }); + + const handleSave = () => { + if (!name.trim()) { + toast.error("Name is required"); + return; + } + updateProfile.mutate({ id: session?.user?.id ?? "", name, email }); + }; + + const handlePasswordChange = (e: React.FormEvent) => { + e.preventDefault(); + if (newPassword !== confirmPassword) { + toast.error("Passwords don't match"); + return; + } + if (newPassword.length < 8) { + toast.error("Password must be at least 8 characters"); + return; + } + changePassword.mutate({ + currentPassword, + newPassword, + }); + }; + + const user = userData ?? session?.user; + const roles = (userData as any)?.systemRoles ?? []; + const initials = (user?.name ?? user?.email ?? "U").charAt(0).toUpperCase(); -function ProfileContent({ user }: { user: ProfileUser }) { return ( -
- ({ - label: formatRole(r.role), - variant: "secondary" as const, - })) ?? []), - ]} - /> +
+ {/* Header */} +
+
+
+ {initials} +
+
+

{user?.name ?? "User"}

+

{user?.email}

+ {roles.length > 0 && ( +
+ {roles.map((role: any) => ( + + {role.role} + + ))} +
+ )} +
+
+
+ {isEditing ? ( + <> + + + + ) : ( + + )} +
+
-
- {/* Main Content (Left Column) */} -
+ {/* Main Content */} +
+ {/* Left Column - Profile Info */} +
{/* Personal Information */} -
-
- -

Personal Information

-
- - - Contact Details - - Update your public profile information - - - - - - -
+ + + + + Personal Information + + + Your public profile information + + + +
+
+ + {isEditing ? ( + setName(e.target.value)} + placeholder="Your name" + /> + ) : ( +
+ + {name || "Not set"} +
+ )} +
+
+ + {isEditing ? ( + setEmail(e.target.value)} + placeholder="you@example.com" + /> + ) : ( +
+ + {email} +
+ )} +
+
+
+ +
+ {user?.id ?? session?.user?.id} +
+
+
+
- {/* Security */} -
-
- -

Security

-
- - - Password - - Ensure your account stays secure - - - - - - -
+ {/* Recent Activity */} + + + + + Recent Activity + + + Your recent actions across the platform + + + +
+ +

No recent activity

+

+ Your recent actions will appear here +

+
+
+
- {/* Sidebar (Right Column) */} -
- {/* Permissions */} -
-
- -

Permissions

-
- - - {user.roles && user.roles.length > 0 ? ( -
- {user.roles.map((roleInfo, index) => ( -
-
- - {formatRole(roleInfo.role)} - - - Since{" "} - {new Date(roleInfo.grantedAt).toLocaleDateString()} - -
-

- {getRoleDescription(roleInfo.role)} -

- {index < (user.roles?.length || 0) - 1 && ( - - )} -
- ))} -
-
- - Role Management -
- System roles are managed by administrators. Contact - support if you need access adjustments. -
+ {/* Right Column - Settings */} +
+ {/* Security */} + + + + + Security + + + +
+
+ +
+

Password

+

Last changed: Never

- ) : ( -
-

No Roles Assigned

-

- Contact an admin to request access. -

-
+ + + -
- )} - - -
+ + + + Change Password + + Enter your current password and choose a new one. + + +
+
+ + setCurrentPassword(e.target.value)} + required + /> +
+
+ + setNewPassword(e.target.value)} + required + minLength={8} + /> +
+
+ + setConfirmPassword(e.target.value)} + required + /> +
+ + + + +
+
+ +
- {/* Data & Privacy */} -
-
- -

Data & Privacy

-
+ - - -
-

Export Data

-

- Download a copy of your personal data. -

- -
- -
-

- Delete Account -

-

- This action is irreversible. -

- -
-
-
-
+
+

Danger Zone

+

+ Account deletion is not available. Contact an administrator for assistance. +

+
+ + + + {/* Studies Access */} + + + + + Studies Access + + + Studies you have access to + + + +
+ +

View your studies

+ +
+
+
@@ -206,22 +361,11 @@ function ProfileContent({ user }: { user: ProfileUser }) { export default function ProfilePage() { const { data: session, isPending } = useSession(); - const { data: userData, isPending: isUserPending } = api.auth.me.useQuery( - undefined, - { - enabled: !!session?.user, - }, - ); - useBreadcrumbsEffect([ - { label: "Dashboard", href: "/dashboard" }, - { label: "Profile" }, - ]); - - if (isPending || isUserPending) { + if (isPending) { return ( -
- Loading profile... +
+
); } @@ -230,13 +374,5 @@ export default function ProfilePage() { redirect("/auth/signin"); } - const user: ProfileUser = { - id: session.user.id, - name: userData?.name ?? session.user.name ?? null, - email: userData?.email ?? session.user.email, - image: userData?.image ?? session.user.image ?? null, - roles: userData?.systemRoles as ProfileUser["roles"], - }; - - return ; + return ; }