196 lines
9.8 KiB
TypeScript
196 lines
9.8 KiB
TypeScript
import LandlordLayout from "@/Layouts/LandlordLayout";
|
|
import { Head, useForm } from "@inertiajs/react";
|
|
import { User, Lock, Mail } from "lucide-react";
|
|
import { FormEvent } from "react";
|
|
import { toast } from "sonner";
|
|
|
|
interface User {
|
|
id: number;
|
|
name: string;
|
|
email: string;
|
|
username: string;
|
|
}
|
|
|
|
interface Props {
|
|
user: User;
|
|
}
|
|
|
|
export default function Edit({ user }: Props) {
|
|
// 個人資料表單
|
|
const { data: profileData, setData: setProfileData, patch: patchProfile, processing: profileProcessing, errors: profileErrors } = useForm({
|
|
name: user.name,
|
|
username: user.username || "",
|
|
email: user.email || "",
|
|
});
|
|
|
|
// 密碼表單
|
|
const { data: passwordData, setData: setPasswordData, put: putPassword, processing: passwordProcessing, errors: passwordErrors, reset: resetPassword } = useForm({
|
|
current_password: "",
|
|
password: "",
|
|
password_confirmation: "",
|
|
});
|
|
|
|
const handleProfileSubmit = (e: FormEvent) => {
|
|
e.preventDefault();
|
|
patchProfile(route('landlord.profile.update'), {
|
|
onSuccess: () => toast.success('個人資料已更新'),
|
|
onError: () => toast.error('更新失敗,請檢查輸入內容'),
|
|
});
|
|
};
|
|
|
|
const handlePasswordSubmit = (e: FormEvent) => {
|
|
e.preventDefault();
|
|
putPassword(route('landlord.profile.password'), {
|
|
onSuccess: () => {
|
|
toast.success('密碼已更新');
|
|
resetPassword();
|
|
},
|
|
onError: () => toast.error('密碼更新失敗'),
|
|
});
|
|
};
|
|
|
|
return (
|
|
<LandlordLayout
|
|
title="使用者設定"
|
|
>
|
|
<Head title="使用者設定" />
|
|
|
|
<div className="max-w-4xl mx-auto space-y-6">
|
|
{/* 頁面標題 */}
|
|
<div>
|
|
<h1 className="text-2xl font-bold text-slate-900 flex items-center gap-2">
|
|
<User className="h-6 w-6 text-primary-main" />
|
|
使用者設定
|
|
</h1>
|
|
<p className="text-slate-500 mt-1">管理您的個人資料與帳號安全</p>
|
|
</div>
|
|
|
|
{/* 個人資料區塊 */}
|
|
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
|
<div className="px-6 py-4 border-b border-slate-200 bg-slate-50">
|
|
<h2 className="text-lg font-semibold text-slate-900 flex items-center gap-2">
|
|
<User className="h-5 w-5 text-slate-600" />
|
|
個人資料
|
|
</h2>
|
|
</div>
|
|
<form onSubmit={handleProfileSubmit} className="p-6 space-y-6">
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-2">
|
|
帳號 (登入用) <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={profileData.username}
|
|
onChange={(e) => setProfileData("username", e.target.value)}
|
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-main focus:border-primary-main"
|
|
placeholder="請輸入登入帳號"
|
|
/>
|
|
{profileErrors.username && <p className="mt-1 text-sm text-red-500">{profileErrors.username}</p>}
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-2">
|
|
使用者名稱 <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="text"
|
|
value={profileData.name}
|
|
onChange={(e) => setProfileData("name", e.target.value)}
|
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-main focus:border-primary-main"
|
|
/>
|
|
{profileErrors.name && <p className="mt-1 text-sm text-red-500">{profileErrors.name}</p>}
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-2">
|
|
Email (選填)
|
|
</label>
|
|
<div className="relative">
|
|
<Mail className="absolute left-3 top-1/2 -translate-y-1/2 h-5 w-5 text-slate-400" />
|
|
<input
|
|
type="email"
|
|
value={profileData.email}
|
|
onChange={(e) => setProfileData("email", e.target.value)}
|
|
className="w-full pl-10 pr-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-main focus:border-primary-main"
|
|
placeholder="example@mail.com"
|
|
/>
|
|
</div>
|
|
{profileErrors.email && <p className="mt-1 text-sm text-red-500">{profileErrors.email}</p>}
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4 pt-4 border-t border-slate-200">
|
|
<button
|
|
type="submit"
|
|
disabled={profileProcessing}
|
|
className="bg-primary-main hover:bg-primary-dark text-white px-6 py-2 rounded-lg disabled:opacity-50 transition-colors"
|
|
>
|
|
{profileProcessing ? "儲存中..." : "儲存變更"}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
|
|
{/* 密碼變更區塊 */}
|
|
<div className="bg-white rounded-xl border border-slate-200 overflow-hidden">
|
|
<div className="px-6 py-4 border-b border-slate-200 bg-slate-50">
|
|
<h2 className="text-lg font-semibold text-slate-900 flex items-center gap-2">
|
|
<Lock className="h-5 w-5 text-slate-600" />
|
|
變更密碼
|
|
</h2>
|
|
</div>
|
|
<form onSubmit={handlePasswordSubmit} className="p-6 space-y-6">
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-2">
|
|
目前密碼 <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="password"
|
|
value={passwordData.current_password}
|
|
onChange={(e) => setPasswordData("current_password", e.target.value)}
|
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-main focus:border-primary-main"
|
|
/>
|
|
{passwordErrors.current_password && <p className="mt-1 text-sm text-red-500">{passwordErrors.current_password}</p>}
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-2">
|
|
新密碼 <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="password"
|
|
value={passwordData.password}
|
|
onChange={(e) => setPasswordData("password", e.target.value)}
|
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-main focus:border-primary-main"
|
|
/>
|
|
{passwordErrors.password && <p className="mt-1 text-sm text-red-500">{passwordErrors.password}</p>}
|
|
<p className="mt-1 text-sm text-slate-500">密碼至少需要 8 個字元</p>
|
|
</div>
|
|
|
|
<div>
|
|
<label className="block text-sm font-medium text-slate-700 mb-2">
|
|
確認新密碼 <span className="text-red-500">*</span>
|
|
</label>
|
|
<input
|
|
type="password"
|
|
value={passwordData.password_confirmation}
|
|
onChange={(e) => setPasswordData("password_confirmation", e.target.value)}
|
|
className="w-full px-4 py-2 border border-slate-300 rounded-lg focus:ring-2 focus:ring-primary-main focus:border-primary-main"
|
|
/>
|
|
</div>
|
|
|
|
<div className="flex items-center gap-4 pt-4 border-t border-slate-200">
|
|
<button
|
|
type="submit"
|
|
disabled={passwordProcessing}
|
|
className="bg-primary-main hover:bg-primary-dark text-white px-6 py-2 rounded-lg disabled:opacity-50 transition-colors"
|
|
>
|
|
{passwordProcessing ? "更新中..." : "更新密碼"}
|
|
</button>
|
|
</div>
|
|
</form>
|
|
</div>
|
|
</div>
|
|
</LandlordLayout>
|
|
);
|
|
}
|