Files
star-erp/resources/js/Pages/Admin/User/Edit.tsx

217 lines
11 KiB
TypeScript
Raw Normal View History

import AuthenticatedLayout from '@/Layouts/AuthenticatedLayout';
import { Head, Link, useForm } from '@inertiajs/react';
import { Users, ArrowLeft, Check, Lock, Mail, User, AlertCircle } from 'lucide-react';
import { Button } from '@/Components/ui/button';
import { Input } from '@/Components/ui/input';
import { Label } from '@/Components/ui/label';
import { RadioGroup, RadioGroupItem } from '@/Components/ui/radio-group';
import { FormEvent } from 'react';
interface Role {
id: number;
name: string;
display_name: string;
}
interface UserData {
id: number;
name: string;
email: string;
username: string | null;
}
interface Props {
user: UserData;
roles: Role[];
currentRoles: string[];
}
export default function UserEdit({ user, roles, currentRoles }: Props) {
const { data, setData, put, processing, errors } = useForm({
name: user.name,
email: user.email || '',
username: user.username || '',
password: '',
password_confirmation: '',
roles: currentRoles,
});
const handleSubmit = (e: FormEvent) => {
e.preventDefault();
put(route('users.update', user.id));
};
return (
<AuthenticatedLayout
breadcrumbs={[
{ label: '系統管理', href: '#' },
{ label: '使用者管理', href: route('users.index') },
{ label: '編輯使用者', href: route('users.edit', user.id), isPage: true },
]}
>
<Head title={`編輯使用者 - ${user.name}`} />
<div className="container mx-auto p-6 max-w-7xl">
<form onSubmit={handleSubmit} className="space-y-6">
{/* Header Area */}
<div>
<Link href={route('users.index')}>
<Button
variant="outline"
type="button"
className="gap-2 button-outlined-primary mb-2"
>
<ArrowLeft className="h-4 w-4" />
使
</Button>
</Link>
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-grey-0 flex items-center gap-2">
<Users className="h-6 w-6 text-[#01ab83]" />
使
</h1>
<p className="text-gray-500 mt-1">
使
</p>
</div>
<Button
type="submit"
className="button-filled-primary"
disabled={processing}
>
<Check className="h-4 w-4 mr-2" />
</Button>
</div>
</div>
<div className="space-y-6">
{/* Basic Info */}
<div className="bg-white p-6 rounded-xl border border-gray-200 shadow-sm space-y-6">
<h3 className="font-bold text-gray-900 border-b pb-2 mb-4"></h3>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="username" className="flex items-center gap-2">
<User className="h-4 w-4" /> 使 ()
</Label>
<Input
id="username"
value={data.username}
onChange={e => setData('username', e.target.value)}
placeholder="請輸入登入帳號"
/>
{errors.username && <p className="text-sm text-red-500">{errors.username}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="name" className="flex items-center gap-2">
<User className="h-4 w-4" />
</Label>
<Input
id="name"
value={data.name}
onChange={e => setData('name', e.target.value)}
placeholder="例如:王小明"
/>
{errors.name && <p className="text-sm text-red-500">{errors.name}</p>}
</div>
</div>
<div className="space-y-2">
<Label htmlFor="email" className="flex items-center gap-2">
<Mail className="h-4 w-4" /> ()
</Label>
<Input
id="email"
type="email"
value={data.email}
onChange={e => setData('email', e.target.value)}
placeholder="user@example.com (可省略)"
/>
{errors.email && <p className="text-sm text-red-500">{errors.email}</p>}
</div>
</div>
{/* Roles */}
<div className="bg-white p-6 rounded-xl border border-gray-200 shadow-sm">
<h3 className="font-bold text-gray-900 border-b pb-2 mb-4"> ()</h3>
<RadioGroup
value={data.roles[0] || ''}
onValueChange={(value) => setData('roles', [value])}
className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4"
>
{roles.map((role) => (
<Label
key={role.id}
htmlFor={`role-${role.id}`}
className={`flex items-center space-x-3 p-3 border rounded-lg transition-colors cursor-pointer ${data.roles.includes(role.name)
? 'border-primary-main bg-primary-lightest'
: 'border-gray-100 bg-gray-50/50 hover:bg-gray-100'
}`}
>
<RadioGroupItem
value={role.name}
id={`role-${role.id}`}
className="text-primary-main border-gray-300"
/>
<div className="grid gap-1 leading-none">
<span className="text-sm font-medium leading-none">
{role.display_name}
</span>
<span className="text-xs text-gray-500 font-mono">
{role.name}
</span>
</div>
</Label>
))}
</RadioGroup>
{errors.roles && <p className="text-sm text-red-500 mt-2">{errors.roles}</p>}
</div>
{/* Password Reset */}
<div className="bg-white p-6 rounded-xl border border-gray-200 shadow-sm space-y-6">
<h3 className="font-bold text-gray-900 border-b pb-2 mb-4"></h3>
<div className="bg-amber-50 text-amber-800 p-3 rounded-lg text-sm flex items-start gap-2 mb-4">
<AlertCircle className="h-4 w-4 mt-0.5 shrink-0" />
</div>
<div className="grid grid-cols-1 md:grid-cols-2 gap-6">
<div className="space-y-2">
<Label htmlFor="password" className="flex items-center gap-2">
<Lock className="h-4 w-4" />
</Label>
<Input
id="password"
type="password"
value={data.password}
onChange={e => setData('password', e.target.value)}
placeholder="••••••••"
/>
{errors.password && <p className="text-sm text-red-500">{errors.password}</p>}
</div>
<div className="space-y-2">
<Label htmlFor="password_confirmation" className="flex items-center gap-2">
<Lock className="h-4 w-4" />
</Label>
<Input
id="password_confirmation"
type="password"
value={data.password_confirmation}
onChange={e => setData('password_confirmation', e.target.value)}
placeholder="••••••••"
/>
</div>
</div>
</div>
</div>
</form>
</div>
</AuthenticatedLayout>
);
}