修正:UI 優化與使用者角色分配改為單選
All checks were successful
Koori-ERP-Deploy-System / deploy-demo (push) Successful in 51s
Koori-ERP-Deploy-System / deploy-production (push) Has been skipped

This commit is contained in:
2026-01-16 12:05:45 +08:00
parent 43d7cada34
commit a2c99e3a36
6 changed files with 85 additions and 54 deletions

33
package-lock.json generated
View File

@@ -14,6 +14,7 @@
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.4",
@@ -1514,6 +1515,38 @@
}
}
},
"node_modules/@radix-ui/react-radio-group": {
"version": "1.3.8",
"resolved": "https://registry.npmjs.org/@radix-ui/react-radio-group/-/react-radio-group-1.3.8.tgz",
"integrity": "sha512-VBKYIYImA5zsxACdisNQ3BjCBfmbGH3kQlnFVqlWU4tXwjy7cGX8ta80BcrO+WJXIn5iBylEH3K6ZTlee//lgQ==",
"license": "MIT",
"dependencies": {
"@radix-ui/primitive": "1.1.3",
"@radix-ui/react-compose-refs": "1.1.2",
"@radix-ui/react-context": "1.1.2",
"@radix-ui/react-direction": "1.1.1",
"@radix-ui/react-presence": "1.1.5",
"@radix-ui/react-primitive": "2.1.3",
"@radix-ui/react-roving-focus": "1.1.11",
"@radix-ui/react-use-controllable-state": "1.2.2",
"@radix-ui/react-use-previous": "1.1.1",
"@radix-ui/react-use-size": "1.1.1"
},
"peerDependencies": {
"@types/react": "*",
"@types/react-dom": "*",
"react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc",
"react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"@types/react-dom": {
"optional": true
}
}
},
"node_modules/@radix-ui/react-roving-focus": {
"version": "1.1.11",
"resolved": "https://registry.npmjs.org/@radix-ui/react-roving-focus/-/react-roving-focus-1.1.11.tgz",

View File

@@ -28,6 +28,7 @@
"@radix-ui/react-dropdown-menu": "^2.1.16",
"@radix-ui/react-label": "^2.1.8",
"@radix-ui/react-popover": "^1.1.15",
"@radix-ui/react-radio-group": "^1.3.8",
"@radix-ui/react-scroll-area": "^1.2.10",
"@radix-ui/react-select": "^2.2.6",
"@radix-ui/react-slot": "^1.2.4",
@@ -46,4 +47,4 @@
"sonner": "^2.0.7",
"tailwind-merge": "^3.4.0"
}
}
}

View File

@@ -348,7 +348,7 @@ export default function AuthenticatedLayout({
<DropdownMenuTrigger className="flex items-center gap-2 outline-none group">
<div className="hidden md:flex flex-col items-end mr-1">
<span className="text-sm font-medium text-slate-700 group-hover:text-slate-900 transition-colors">
{user.name}
{user.name} ({user.username})
</span>
<span className="text-xs text-slate-500">
{user.role_labels?.[0] || user.roles?.[0] || '一般用戶'}

View File

@@ -117,7 +117,7 @@ export default function LandlordLayout({ children, title }: LandlordLayoutProps)
<DropdownMenuTrigger className="flex items-center gap-2 outline-none group">
<div className="flex flex-col items-end mr-1">
<span className="text-sm font-medium text-slate-700 group-hover:text-slate-900 transition-colors">
{user.name}
{user.name} ({user.username})
</span>
<span className="text-xs text-slate-500">
{user.role_labels?.[0] || user.roles?.[0] || '系統管理員'}

View File

@@ -4,7 +4,7 @@ import { Users, ArrowLeft, Check, Lock, Mail, User } from 'lucide-react';
import { Button } from '@/Components/ui/button';
import { Input } from '@/Components/ui/input';
import { Label } from '@/Components/ui/label';
import { Checkbox } from '@/Components/ui/checkbox';
import { RadioGroup, RadioGroupItem } from '@/Components/ui/radio-group';
import { FormEvent } from 'react';
interface Props {
@@ -26,14 +26,6 @@ export default function UserCreate({ roles }: Props) {
post(route('users.store'));
};
const toggleRole = (roleName: string) => {
if (data.roles.includes(roleName)) {
setData('roles', data.roles.filter(r => r !== roleName));
} else {
setData('roles', [...data.roles, roleName]);
}
};
return (
<AuthenticatedLayout
@@ -131,30 +123,37 @@ export default function UserCreate({ roles }: Props) {
{/* 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>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<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"
>
{Object.entries(roles).map(([roleName, displayName]) => (
<div key={roleName} className="flex items-center space-x-3 p-3 border border-gray-100 bg-gray-50/50 hover:bg-gray-100 rounded-lg transition-colors">
<Checkbox
<Label
key={roleName}
htmlFor={`role-${roleName}`}
className={`flex items-center space-x-3 p-3 border rounded-lg transition-colors cursor-pointer ${data.roles.includes(roleName)
? 'border-primary-main bg-primary-lightest'
: 'border-gray-100 bg-gray-50/50 hover:bg-gray-100'
}`}
>
<RadioGroupItem
value={roleName}
id={`role-${roleName}`}
checked={data.roles.includes(roleName)}
onCheckedChange={() => toggleRole(roleName)}
className="text-primary-main border-gray-300"
/>
<div className="grid gap-1 leading-none">
<label
htmlFor={`role-${roleName}`}
className="text-sm font-medium leading-none cursor-pointer"
onClick={() => toggleRole(roleName)}
>
<span className="text-sm font-medium leading-none">
{displayName}
</label>
<p className="text-xs text-gray-500 font-mono">
</span>
<span className="text-xs text-gray-500 font-mono">
{roleName}
</p>
</span>
</div>
</div>
</Label>
))}
</div>
</RadioGroup>
{errors.roles && <p className="text-sm text-red-500 mt-2">{errors.roles}</p>}
</div>

View File

@@ -4,7 +4,7 @@ import { Users, ArrowLeft, Check, Lock, Mail, User, AlertCircle } from 'lucide-r
import { Button } from '@/Components/ui/button';
import { Input } from '@/Components/ui/input';
import { Label } from '@/Components/ui/label';
import { Checkbox } from '@/Components/ui/checkbox';
import { RadioGroup, RadioGroupItem } from '@/Components/ui/radio-group';
import { FormEvent } from 'react';
interface Role {
@@ -41,15 +41,6 @@ export default function UserEdit({ user, roles, currentRoles }: Props) {
put(route('users.update', user.id));
};
const toggleRole = (roleName: string) => {
if (data.roles.includes(roleName)) {
setData('roles', data.roles.filter(r => r !== roleName));
} else {
setData('roles', [...data.roles, roleName]);
}
};
return (
<AuthenticatedLayout
breadcrumbs={[
@@ -146,30 +137,37 @@ export default function UserEdit({ user, roles, currentRoles }: Props) {
{/* 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>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
<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) => (
<div key={role.id} className="flex items-center space-x-3 p-3 border border-gray-100 bg-gray-50/50 hover:bg-gray-100 rounded-lg transition-colors">
<Checkbox
<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}`}
checked={data.roles.includes(role.name)}
onCheckedChange={() => toggleRole(role.name)}
className="text-primary-main border-gray-300"
/>
<div className="grid gap-1 leading-none">
<label
htmlFor={`role-${role.id}`}
className="text-sm font-medium leading-none cursor-pointer"
onClick={() => toggleRole(role.name)}
>
<span className="text-sm font-medium leading-none">
{role.display_name}
</label>
<p className="text-xs text-gray-500 font-mono">
</span>
<span className="text-xs text-gray-500 font-mono">
{role.name}
</p>
</span>
</div>
</div>
</Label>
))}
</div>
</RadioGroup>
{errors.roles && <p className="text-sm text-red-500 mt-2">{errors.roles}</p>}
</div>