feat: adopt kumo green ui system

This commit is contained in:
2026-07-02 07:12:31 -07:00
parent 4bb4312b08
commit 0f0bd8813d
32 changed files with 1453 additions and 796 deletions

View File

@@ -9,6 +9,7 @@ import { Button } from "@/components/ui/button";
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card";
import { Dialog } from "@/components/ui/dialog";
import { Input } from "@/components/ui/input";
import { Select } from "@/components/ui/select";
import type { ApiResult } from "@/modules/core/server/api";
import type { JoinRequest, Organization, PublicAccount, RoleDefinition } from "@/modules/core/types";
@@ -36,6 +37,7 @@ export function UserManagementClient({
const router = useRouter();
const [isCreateOpen, setIsCreateOpen] = useState(false);
const [selectedOrganizationId, setSelectedOrganizationId] = useState(() => getDefaultOrganizationId(organizations));
const [reviewRoleByRequestId, setReviewRoleByRequestId] = useState<Record<string, string>>({});
const [message, setMessage] = useState("");
const [isPending, setIsPending] = useState(false);
@@ -169,6 +171,7 @@ export function UserManagementClient({
{pendingJoinRequests.map((request) => {
const requestRoles = getOrganizationRoles(roles, request.organizationId);
const defaultRoleId = requestRoles.find((role) => role.key === "caregiver")?.id ?? requestRoles[0]?.id ?? "";
const selectedRoleId = reviewRoleByRequestId[request.id] ?? defaultRoleId;
return (
<div key={request.id} className="rounded-md border p-3">
@@ -181,24 +184,18 @@ export function UserManagementClient({
<Badge variant="warning"></Badge>
</div>
<div className="mt-3 grid gap-2 sm:grid-cols-[1fr_auto_auto]">
<select
className="h-10 w-full rounded-md border bg-background px-3 text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
defaultValue={defaultRoleId}
id={`role-${request.id}`}
>
{requestRoles.map((role) => (
<option key={role.id} value={role.id}>
{role.label}
</option>
))}
</select>
<Select
aria-label={`${request.accountName} 审批角色`}
disabled={requestRoles.length === 0}
onValueChange={(roleId) =>
setReviewRoleByRequestId((current) => ({ ...current, [request.id]: roleId }))
}
options={requestRoles.map((role) => ({ value: role.id, label: role.label }))}
value={selectedRoleId}
/>
<Button
disabled={isPending || !defaultRoleId || !canManageAccounts}
onClick={() => {
const element = document.getElementById(`role-${request.id}`);
const roleId = element instanceof HTMLSelectElement ? element.value : defaultRoleId;
void reviewJoinRequest(request.id, "approved", roleId);
}}
disabled={isPending || !selectedRoleId || !canManageAccounts}
onClick={() => void reviewJoinRequest(request.id, "approved", selectedRoleId)}
size="sm"
type="button"
>
@@ -232,35 +229,26 @@ export function UserManagementClient({
<form className="grid gap-3" onSubmit={handleCreateAccount}>
<label className="grid gap-2 text-sm">
<span className="font-medium"></span>
<select
className="h-11 w-full rounded-md border bg-background px-3 text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
<Select
aria-label="机构"
name="organizationId"
onChange={(event) => setSelectedOrganizationId(event.target.value)}
onValueChange={setSelectedOrganizationId}
options={organizations.map((organization) => ({ value: organization.id, label: organization.name }))}
value={selectedOrganizationId}
>
{organizations.map((organization) => (
<option key={organization.id} value={organization.id}>
{organization.name}
</option>
))}
</select>
/>
</label>
<label className="grid gap-2 text-sm">
<span className="font-medium"></span>
<select
className="h-11 w-full rounded-md border bg-background px-3 text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
<Select
aria-label="角色"
defaultValue={organizationRoles[0]?.id ?? ""}
disabled={organizationRoles.length === 0}
key={selectedOrganizationId}
name="roleId"
options={organizationRoles.map((role) => ({ value: role.id, label: role.label }))}
required
>
{organizationRoles.map((role) => (
<option key={role.id} value={role.id}>
{role.label}
</option>
))}
</select>
/>
</label>
<label className="grid gap-2 text-sm">
@@ -424,47 +412,38 @@ export function UserAccountActions({
</label>
<label className="grid gap-2 text-sm">
<span className="font-medium"></span>
<select
className="h-11 w-full rounded-md border bg-background px-3 text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
<Select
aria-label="账号状态"
defaultValue={account.status}
disabled={isCurrentAccount}
name="status"
>
<option value="active"></option>
<option value="disabled"></option>
</select>
options={[
{ value: "active", label: "启用" },
{ value: "disabled", label: "停用" },
]}
/>
</label>
<div className="grid gap-3 md:grid-cols-2">
<label className="grid gap-2 text-sm">
<span className="font-medium"></span>
<select
className="h-11 w-full rounded-md border bg-background px-3 text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
<Select
aria-label="账号机构"
name="organizationId"
onChange={(event) => setSelectedOrganizationId(event.target.value)}
onValueChange={setSelectedOrganizationId}
options={organizations.map((organization) => ({ value: organization.id, label: organization.name }))}
value={selectedOrganizationId}
>
{organizations.map((organization) => (
<option key={organization.id} value={organization.id}>
{organization.name}
</option>
))}
</select>
/>
</label>
<label className="grid gap-2 text-sm">
<span className="font-medium"></span>
<select
className="h-11 w-full rounded-md border bg-background px-3 text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
<Select
aria-label="账号角色"
defaultValue={defaultRoleId}
disabled={organizationRoles.length === 0}
key={selectedOrganizationId}
name="roleId"
>
{organizationRoles.map((role) => (
<option key={role.id} value={role.id}>
{role.label}
</option>
))}
</select>
options={organizationRoles.map((role) => ({ value: role.id, label: role.label }))}
/>
</label>
</div>
{message ? (