Implement profile management (task 14)

- Add updateProfile and updatePassword server actions
- Add getUserProfile function with sensitive data protection
- Create ProfileForm component for name and email updates
- Create PasswordChangeForm component with current password verification
- Create profile page at /profile with all profile management features
- Add profile and invitations navigation links to main page
- Implement email uniqueness validation
- Implement password hashing on password change

Requirements: 7.1, 7.2, 7.3, 7.4, 7.5
This commit is contained in:
2025-12-02 01:06:21 +00:00
parent b102e86b33
commit 85c8a1650b
6 changed files with 745 additions and 3 deletions

119
app/profile/page.tsx Normal file
View File

@@ -0,0 +1,119 @@
import { redirect } from 'next/navigation'
import { auth } from '@/auth'
import { getUserProfile } from '@/lib/auth-actions'
import ProfileForm from '@/components/ProfileForm'
import PasswordChangeForm from '@/components/PasswordChangeForm'
import Link from 'next/link'
export const metadata = {
title: 'Profile - Ultimate Lists',
description: 'Manage your profile and account settings',
}
export default async function ProfilePage() {
const session = await auth()
if (!session?.user?.id) {
redirect('/auth/login')
}
const userProfile = await getUserProfile(session.user.id)
if (!userProfile) {
redirect('/auth/login')
}
return (
<div className="min-h-screen bg-gray-50 px-4 py-8">
<div className="max-w-3xl mx-auto">
{/* Header */}
<div className="mb-6">
<Link
href="/"
className="inline-flex items-center text-sm text-gray-600 hover:text-gray-900 mb-4"
>
<svg
className="w-4 h-4 mr-1"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 19l-7-7 7-7"
/>
</svg>
Back to Lists
</Link>
<h1 className="text-3xl sm:text-4xl font-bold text-gray-900">
Profile Settings
</h1>
<p className="text-sm sm:text-base text-gray-600 mt-2">
Manage your account information and security settings
</p>
</div>
{/* Profile Information Section */}
<div className="bg-white rounded-xl shadow-md p-6 sm:p-8 mb-6">
<h2 className="text-xl sm:text-2xl font-semibold text-gray-900 mb-4">
Profile Information
</h2>
<p className="text-sm text-gray-600 mb-6">
Update your name and email address
</p>
<ProfileForm
initialName={userProfile.name}
initialEmail={userProfile.email}
userId={userProfile.id}
/>
</div>
{/* Password Section */}
<div className="bg-white rounded-xl shadow-md p-6 sm:p-8 mb-6">
<h2 className="text-xl sm:text-2xl font-semibold text-gray-900 mb-4">
Change Password
</h2>
<p className="text-sm text-gray-600 mb-6">
Update your password to keep your account secure
</p>
<PasswordChangeForm userId={userProfile.id} />
</div>
{/* Account Information */}
<div className="bg-white rounded-xl shadow-md p-6 sm:p-8">
<h2 className="text-xl sm:text-2xl font-semibold text-gray-900 mb-4">
Account Information
</h2>
<div className="space-y-3 text-sm">
<div className="flex justify-between py-2 border-b border-gray-100">
<span className="text-gray-600">Account ID</span>
<span className="text-gray-900 font-mono text-xs">{userProfile.id}</span>
</div>
<div className="flex justify-between py-2 border-b border-gray-100">
<span className="text-gray-600">Member Since</span>
<span className="text-gray-900">
{new Date(userProfile.createdAt).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</span>
</div>
<div className="flex justify-between py-2">
<span className="text-gray-600">Last Updated</span>
<span className="text-gray-900">
{new Date(userProfile.updatedAt).toLocaleDateString('en-US', {
year: 'numeric',
month: 'long',
day: 'numeric',
})}
</span>
</div>
</div>
</div>
</div>
</div>
)
}