gohorsejobs/frontend/src/app/dashboard/profile/page.tsx
2026-01-03 20:21:29 -03:00

391 lines
16 KiB
TypeScript

"use client"
import { ProfilePictureUpload } from "@/components/profile-picture-upload-v2"
import { Button } from "@/components/ui/button"
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Textarea } from "@/components/ui/textarea"
import { RichTextEditor } from "@/components/rich-text-editor"
import { Save, Loader2, Building2 } from "lucide-react"
import { useEffect, useState } from "react"
import { profileApi, authApi, adminCompaniesApi, type AdminCompany } from "@/lib/api"
import { toast } from "sonner"
import { getCurrentUser, isAdminUser } from "@/lib/auth"
export default function ProfilePage() {
const [loading, setLoading] = useState(true)
const [saving, setSaving] = useState(false)
const [savingCompany, setSavingCompany] = useState(false)
const [user, setUser] = useState<any>(null)
const [company, setCompany] = useState<AdminCompany | null>(null)
const [isAdmin, setIsAdmin] = useState(false)
const [formData, setFormData] = useState({
fullName: "",
email: "",
phone: "",
bio: "",
})
const [passwordData, setPasswordData] = useState({
currentPassword: "",
newPassword: "",
confirmPassword: "",
})
const [companyData, setCompanyData] = useState({
name: "",
email: "",
phone: "",
website: "",
description: "",
})
const getInitials = (name: string) => {
const initials = name
.trim()
.split(/\s+/)
.filter(Boolean)
.map((word) => word[0])
.join("")
.toUpperCase()
return initials.slice(0, 2) || "U"
}
useEffect(() => {
const currentUser = getCurrentUser()
setIsAdmin(isAdminUser(currentUser))
loadProfile()
}, [])
const loadProfile = async () => {
console.log("[PROFILE_FLOW] Loading profile...")
try {
const userData = await authApi.getCurrentUser()
console.log("[PROFILE_FLOW] Profile loaded:", userData)
setUser(userData)
setFormData({
fullName: userData.fullName || userData.name || "",
email: userData.email || "",
phone: userData.phone || "",
bio: userData.bio || ""
})
// Load company if user has companyId
if (userData.companyId) {
try {
const companiesRes = await adminCompaniesApi.list()
const userCompany = companiesRes.data.find(c => c.id === userData.companyId)
if (userCompany) {
setCompany(userCompany)
setCompanyData({
name: userCompany.name || "",
email: userCompany.email || "",
phone: userCompany.phone || "",
website: userCompany.website || "",
description: userCompany.description || "",
})
}
} catch (err) {
console.warn("[PROFILE_FLOW] Could not load company:", err)
}
}
} catch (error) {
console.error("[PROFILE_FLOW] Error loading profile:", error)
toast.error("Failed to load profile")
} finally {
setLoading(false)
}
}
const handleInputChange = (field: string, value: string) => {
setFormData(prev => ({ ...prev, [field]: value }))
}
const handleCompanyChange = (field: string, value: string) => {
setCompanyData(prev => ({ ...prev, [field]: value }))
}
const handlePasswordChange = (field: string, value: string) => {
setPasswordData(prev => ({ ...prev, [field]: value }))
}
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setSaving(true)
console.log("[PROFILE_FLOW] Updating profile:", formData)
try {
await profileApi.update({
name: formData.fullName,
phone: formData.phone,
bio: formData.bio
})
console.log("[PROFILE_FLOW] Profile updated successfully")
toast.success("Profile updated")
} catch (error) {
console.error("[PROFILE_FLOW] Error updating profile:", error)
toast.error("Failed to update profile")
} finally {
setSaving(false)
}
}
const handleCompanySubmit = async (e: React.FormEvent) => {
e.preventDefault()
if (!company) return
setSavingCompany(true)
console.log("[PROFILE_FLOW] Updating company:", companyData)
try {
await adminCompaniesApi.update(company.id, {
name: companyData.name,
email: companyData.email,
phone: companyData.phone,
website: companyData.website,
description: companyData.description,
})
console.log("[PROFILE_FLOW] Company updated successfully")
toast.success("Company updated")
loadProfile()
} catch (error) {
console.error("[PROFILE_FLOW] Error updating company:", error)
toast.error("Failed to update company")
} finally {
setSavingCompany(false)
}
}
const handlePasswordSubmit = async (e: React.FormEvent) => {
e.preventDefault()
console.log("[PROFILE_FLOW] Requesting password reset/update")
if (passwordData.newPassword !== passwordData.confirmPassword) {
toast.error("Passwords do not match")
return
}
try {
await profileApi.updatePassword({
currentPassword: passwordData.currentPassword,
newPassword: passwordData.newPassword,
})
toast.success("Password updated")
setPasswordData({
currentPassword: "",
newPassword: "",
confirmPassword: "",
})
} catch (error) {
console.error("[PROFILE_FLOW] Error updating password:", error)
toast.error("Failed to update password")
}
}
if (loading) {
return (
<div className="flex items-center justify-center h-full">
<Loader2 className="w-8 h-8 animate-spin" />
</div>
)
}
return (
<div className="max-w-2xl mx-auto py-8 space-y-6">
{/* Profile Card */}
<Card>
<CardHeader>
<CardTitle className="text-2xl font-bold text-center">
Edit profile
</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
<div className="flex justify-center">
<ProfilePictureUpload
fallbackText={formData.fullName ? getInitials(formData.fullName) : "U"}
size="xl"
useDatabase={false}
onImageChange={async (file, url) => {
if (file) {
try {
console.log("[PROFILE_FLOW] Uploading avatar:", { name: file.name, size: file.size })
await profileApi.uploadAvatar(file)
loadProfile()
toast.success("Avatar updated")
} catch (err) {
console.error("[PROFILE_FLOW] Avatar upload failed:", err)
toast.error("Failed to upload avatar")
}
}
}}
initialImage={user?.avatarUrl}
/>
</div>
<form onSubmit={handleSubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<Label htmlFor="name">Full name</Label>
<Input
id="name"
value={formData.fullName}
onChange={(e) => handleInputChange("fullName", e.target.value)}
/>
</div>
<div>
<Label htmlFor="email">Email</Label>
<Input
id="email"
value={formData.email}
disabled
className="bg-muted"
/>
</div>
</div>
<div>
<Label htmlFor="phone">Phone</Label>
<Input
id="phone"
value={formData.phone}
onChange={(e) => handleInputChange("phone", e.target.value)}
/>
</div>
<div>
<Label htmlFor="bio">Bio</Label>
<RichTextEditor
value={formData.bio}
onChange={(value) => handleInputChange("bio", value)}
placeholder="Tell us about yourself..."
minHeight="140px"
/>
</div>
<Button type="submit" className="w-full" size="lg" disabled={saving}>
{saving ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Save className="mr-2 h-4 w-4" />}
Save profile
</Button>
</form>
</CardContent>
</Card>
<Card>
<CardHeader>
<CardTitle>Password Reset</CardTitle>
<CardDescription>Update your account password securely.</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handlePasswordSubmit} className="space-y-4">
<div>
<Label htmlFor="currentPassword">Current password</Label>
<Input
id="currentPassword"
type="password"
value={passwordData.currentPassword}
onChange={(e) => handlePasswordChange("currentPassword", e.target.value)}
/>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<Label htmlFor="newPassword">New password</Label>
<Input
id="newPassword"
type="password"
value={passwordData.newPassword}
onChange={(e) => handlePasswordChange("newPassword", e.target.value)}
/>
</div>
<div>
<Label htmlFor="confirmPassword">Confirm new password</Label>
<Input
id="confirmPassword"
type="password"
value={passwordData.confirmPassword}
onChange={(e) => handlePasswordChange("confirmPassword", e.target.value)}
/>
</div>
</div>
<Button type="submit" className="w-full" size="lg">
<Save className="mr-2 h-4 w-4" />
Reset password
</Button>
</form>
</CardContent>
</Card>
{/* Company Card - Only for Admin users who have a company */}
{isAdmin && company && (
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Building2 className="h-5 w-5 text-primary" />
<CardTitle>Company Information</CardTitle>
</div>
<CardDescription>Edit your company details</CardDescription>
</CardHeader>
<CardContent>
<form onSubmit={handleCompanySubmit} className="space-y-4">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<Label htmlFor="companyName">Company Name</Label>
<Input
id="companyName"
value={companyData.name}
onChange={(e) => handleCompanyChange("name", e.target.value)}
/>
</div>
<div>
<Label htmlFor="companyEmail">Company Email</Label>
<Input
id="companyEmail"
type="email"
value={companyData.email}
onChange={(e) => handleCompanyChange("email", e.target.value)}
/>
</div>
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div>
<Label htmlFor="companyPhone">Phone</Label>
<Input
id="companyPhone"
value={companyData.phone}
onChange={(e) => handleCompanyChange("phone", e.target.value)}
/>
</div>
<div>
<Label htmlFor="companyWebsite">Website</Label>
<Input
id="companyWebsite"
value={companyData.website}
onChange={(e) => handleCompanyChange("website", e.target.value)}
placeholder="https://example.com"
/>
</div>
</div>
<div>
<Label htmlFor="companyDescription">Description</Label>
<Textarea
id="companyDescription"
value={companyData.description}
onChange={(e) => handleCompanyChange("description", e.target.value)}
rows={4}
placeholder="Tell us about your company..."
/>
</div>
<Button type="submit" className="w-full" size="lg" disabled={savingCompany}>
{savingCompany ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Save className="mr-2 h-4 w-4" />}
Save company info
</Button>
</form>
</CardContent>
</Card>
)}
</div>
)
}