Files

364 lines
12 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"use client"
import { useState, useEffect } from "react"
import { useRouter } from "next/navigation"
import { ProtectedRoute } from "@/components/protected-route"
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Alert, AlertDescription } from "@/components/ui/alert"
import { Separator } from "@/components/ui/separator"
import { User, Lock, ArrowLeft, Save } from "lucide-react"
import Link from "next/link"
import { useAuth } from "@/lib/hooks/use-auth"
export default function SettingsPage() {
return (
<ProtectedRoute>
<SettingsContent />
</ProtectedRoute>
)
}
function SettingsContent() {
const { user, logout } = useAuth()
const router = useRouter()
const [isLoading, setIsLoading] = useState(false)
const [message, setMessage] = useState("")
const [error, setError] = useState("")
// Profile form state
const [profileData, setProfileData] = useState({
name: "",
email: "",
department: "",
})
// Password form state
const [passwordData, setPasswordData] = useState({
currentPassword: "",
newPassword: "",
confirmPassword: "",
})
const departments = ["人力資源部", "資訊技術部", "財務部", "行銷部", "業務部", "研發部", "客服部", "其他"]
useEffect(() => {
if (user) {
setProfileData({
name: user.name,
email: user.email,
department: user.department,
})
}
}, [user])
const handleProfileUpdate = async () => {
setError("")
setMessage("")
setIsLoading(true)
try {
if (!user) {
setError("用戶未登入")
return
}
// 更新個人資料到資料庫
const response = await fetch('/api/user/profile', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId: user.id,
name: profileData.name,
email: profileData.email,
department: profileData.department,
}),
})
const data = await response.json()
if (data.success) {
// 更新本地用戶資料
const updatedUser = {
...user,
name: profileData.name,
email: profileData.email,
department: profileData.department,
}
localStorage.setItem("hr_current_user", JSON.stringify(updatedUser))
setMessage("個人資料已成功更新")
// 刷新頁面以更新用戶上下文
setTimeout(() => {
window.location.reload()
}, 1500)
} else {
setError(data.error || "更新個人資料失敗")
}
} catch (err) {
console.error('更新個人資料錯誤:', err)
setError("更新個人資料時發生錯誤")
} finally {
setIsLoading(false)
}
}
const handlePasswordChange = async () => {
setError("")
setMessage("")
if (passwordData.newPassword !== passwordData.confirmPassword) {
setError("新密碼確認不一致")
return
}
if (passwordData.newPassword.length < 6) {
setError("新密碼長度至少需要6個字元")
return
}
setIsLoading(true)
try {
if (!user) {
setError("用戶未登入")
return
}
// 更新密碼到資料庫
const response = await fetch('/api/user/profile', {
method: 'PUT',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
userId: user.id,
currentPassword: passwordData.currentPassword,
newPassword: passwordData.newPassword,
}),
})
const data = await response.json()
if (data.success) {
setPasswordData({
currentPassword: "",
newPassword: "",
confirmPassword: "",
})
setMessage("密碼已成功更新")
} else {
setError(data.error || "密碼更新失敗")
}
} catch (err) {
console.error('密碼更新錯誤:', err)
setError("密碼更新時發生錯誤")
} finally {
setIsLoading(false)
}
}
if (!user) return null
return (
<div className="min-h-screen bg-background">
{/* Header */}
<header className="border-b bg-card/50 backdrop-blur-sm">
<div className="container mx-auto px-4 py-4">
<div className="flex items-center gap-4">
<Button variant="ghost" size="sm" asChild>
<Link href="/dashboard">
<ArrowLeft className="w-4 h-4 mr-2" />
<span className="hidden sm:inline"></span>
</Link>
</Button>
<div>
<h1 className="text-xl font-bold text-foreground"></h1>
<p className="text-sm text-muted-foreground"></p>
</div>
</div>
</div>
</header>
<div className="container mx-auto px-4 py-8">
<div className="max-w-2xl mx-auto space-y-8">
{/* Profile Settings */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<User className="w-5 h-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="name"></Label>
<Input
id="name"
value={profileData.name}
onChange={(e) => setProfileData({ ...profileData, name: e.target.value })}
placeholder="請輸入您的姓名"
/>
</div>
<div className="space-y-2">
<Label htmlFor="email"></Label>
<Input
id="email"
type="email"
value={profileData.email}
onChange={(e) => setProfileData({ ...profileData, email: e.target.value })}
placeholder="請輸入電子郵件"
/>
</div>
<div className="space-y-2">
<Label htmlFor="department"></Label>
<Select
value={profileData.department}
onValueChange={(value) => setProfileData({ ...profileData, department: value })}
>
<SelectTrigger>
<SelectValue placeholder="請選擇部門" />
</SelectTrigger>
<SelectContent>
{departments.map((dept) => (
<SelectItem key={dept} value={dept}>
{dept}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label></Label>
<div className="p-3 bg-muted rounded-md">
<span className="text-sm font-medium">{user.role === "admin" ? "管理員" : "一般用戶"}</span>
<p className="text-xs text-muted-foreground mt-1"></p>
</div>
</div>
<Button onClick={handleProfileUpdate} disabled={isLoading} className="w-full">
<Save className="w-4 h-4 mr-2" />
{isLoading ? "更新中..." : "更新個人資料"}
</Button>
</CardContent>
</Card>
<Separator />
{/* Password Settings */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Lock className="w-5 h-5" />
</CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label htmlFor="currentPassword"></Label>
<Input
id="currentPassword"
type="password"
value={passwordData.currentPassword}
onChange={(e) => setPasswordData({ ...passwordData, currentPassword: e.target.value })}
placeholder="請輸入目前密碼"
/>
</div>
<div className="space-y-2">
<Label htmlFor="newPassword"></Label>
<Input
id="newPassword"
type="password"
value={passwordData.newPassword}
onChange={(e) => setPasswordData({ ...passwordData, newPassword: e.target.value })}
placeholder="請輸入新密碼至少6個字元"
/>
</div>
<div className="space-y-2">
<Label htmlFor="confirmPassword"></Label>
<Input
id="confirmPassword"
type="password"
value={passwordData.confirmPassword}
onChange={(e) => setPasswordData({ ...passwordData, confirmPassword: e.target.value })}
placeholder="請再次輸入新密碼"
/>
</div>
<Button onClick={handlePasswordChange} disabled={isLoading} className="w-full">
<Lock className="w-4 h-4 mr-2" />
{isLoading ? "更新中..." : "更新密碼"}
</Button>
</CardContent>
</Card>
{/* Account Info */}
<Card>
<CardHeader>
<CardTitle></CardTitle>
<CardDescription></CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-4 text-sm">
<div>
<span className="text-muted-foreground">ID</span>
<span className="font-mono">{user.id}</span>
</div>
<div>
<span className="text-muted-foreground"></span>
<span>{new Date(user.createdAt).toLocaleDateString()}</span>
</div>
</div>
<Separator />
<div className="flex justify-between items-center">
<div>
<h4 className="font-medium"></h4>
<p className="text-sm text-muted-foreground"></p>
</div>
<Button
variant="outline"
onClick={() => {
logout()
router.push("/login")
}}
>
</Button>
</div>
</CardContent>
</Card>
{/* Messages */}
{message && (
<Alert>
<AlertDescription className="text-green-600">{message}</AlertDescription>
</Alert>
)}
{error && (
<Alert variant="destructive">
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
</div>
</div>
</div>
)
}