Files
star-erp/resources/js/Pages/Admin/User/Edit.tsx
sky121113 55272d5d43
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Successful in 47s
Koori-ERP-Deploy-System / deploy-production (push) Has been skipped
feat: 新增租戶品牌客製化系統(Logo、主色系)、修正 hardcoded 顏色為 CSS 變數
2026-01-16 14:36:24 +08:00

217 lines
11 KiB
TypeScript
Raw 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.
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-primary-main" />
使
</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>
);
}