feat: adopt kumo green ui system
This commit is contained in:
@@ -7,7 +7,9 @@ import { Badge } from "@/components/ui/badge";
|
||||
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 { Input, Textarea } from "@/components/ui/input";
|
||||
import { Select } from "@/components/ui/select";
|
||||
import { Table } from "@/components/ui/table";
|
||||
import type { ApiResult } from "@/modules/core/server/api";
|
||||
import type { Permission } from "@/modules/core/types";
|
||||
import type { CareLevel, Elder, ElderInput, ElderStatus } from "@/modules/elders/types";
|
||||
@@ -66,6 +68,20 @@ function getRiskLabel(elder: Elder): { label: string; variant: "danger" | "warni
|
||||
return { label: "绿色稳定", variant: "success" };
|
||||
}
|
||||
|
||||
const statusOptions = [
|
||||
{ value: ALL_STATUS, label: "全部状态" },
|
||||
...Object.entries(ELDER_STATUS_LABELS).map(([value, label]) => ({ value, label })),
|
||||
];
|
||||
|
||||
const careLevelOptions = [
|
||||
{ value: ALL_CARE_LEVELS, label: "全部照护" },
|
||||
...Object.entries(CARE_LEVEL_LABELS).map(([value, label]) => ({ value, label })),
|
||||
];
|
||||
|
||||
const genderOptions = Object.entries(GENDER_LABELS).map(([value, label]) => ({ value, label }));
|
||||
const formCareLevelOptions = Object.entries(CARE_LEVEL_LABELS).map(([value, label]) => ({ value, label }));
|
||||
const formStatusOptions = Object.entries(ELDER_STATUS_LABELS).map(([value, label]) => ({ value, label }));
|
||||
|
||||
function matchesFilters(
|
||||
elder: Elder,
|
||||
query: string,
|
||||
@@ -291,33 +307,23 @@ export function EldersClient({ initialElders, permissions }: EldersClientProps):
|
||||
value={query}
|
||||
/>
|
||||
</label>
|
||||
<label className="relative block">
|
||||
<div className="relative block">
|
||||
<Filter className="pointer-events-none absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" />
|
||||
<select
|
||||
className="h-11 w-full rounded-md border bg-background px-9 text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
|
||||
onChange={(event) => updateStatusFilter(event.target.value)}
|
||||
<Select
|
||||
aria-label="按老人状态筛选"
|
||||
className="w-full pl-9"
|
||||
onValueChange={updateStatusFilter}
|
||||
options={statusOptions}
|
||||
value={statusFilter}
|
||||
>
|
||||
<option value={ALL_STATUS}>全部状态</option>
|
||||
{Object.entries(ELDER_STATUS_LABELS).map(([value, label]) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<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"
|
||||
onChange={(event) => updateCareLevelFilter(event.target.value)}
|
||||
/>
|
||||
</div>
|
||||
<Select
|
||||
aria-label="按照护等级筛选"
|
||||
className="w-full"
|
||||
onValueChange={updateCareLevelFilter}
|
||||
options={careLevelOptions}
|
||||
value={careLevelFilter}
|
||||
>
|
||||
<option value={ALL_CARE_LEVELS}>全部照护</option>
|
||||
{Object.entries(CARE_LEVEL_LABELS).map(([value, label]) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
{message ? (
|
||||
@@ -328,46 +334,46 @@ export function EldersClient({ initialElders, permissions }: EldersClientProps):
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="overflow-x-auto rounded-md border">
|
||||
<table className="w-full min-w-[980px] text-sm">
|
||||
<thead className="bg-secondary text-left text-xs text-muted-foreground">
|
||||
<tr>
|
||||
<th className="px-4 py-3 font-medium">姓名</th>
|
||||
<th className="px-4 py-3 font-medium">照护</th>
|
||||
<th className="px-4 py-3 font-medium">床位</th>
|
||||
<th className="px-4 py-3 font-medium">联系人</th>
|
||||
<th className="px-4 py-3 font-medium">状态</th>
|
||||
<th className="px-4 py-3 font-medium">标记</th>
|
||||
<th className="px-4 py-3 text-right font-medium">操作</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y bg-card">
|
||||
<Table className="w-full min-w-[980px] text-sm">
|
||||
<Table.Header className="bg-secondary text-left text-xs text-muted-foreground">
|
||||
<Table.Row>
|
||||
<Table.Head className="px-4 py-3 font-medium">姓名</Table.Head>
|
||||
<Table.Head className="px-4 py-3 font-medium">照护</Table.Head>
|
||||
<Table.Head className="px-4 py-3 font-medium">床位</Table.Head>
|
||||
<Table.Head className="px-4 py-3 font-medium">联系人</Table.Head>
|
||||
<Table.Head className="px-4 py-3 font-medium">状态</Table.Head>
|
||||
<Table.Head className="px-4 py-3 font-medium">标记</Table.Head>
|
||||
<Table.Head className="px-4 py-3 text-right font-medium">操作</Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body className="divide-y bg-card">
|
||||
{visibleElders.map((elder) => {
|
||||
const risk = getRiskLabel(elder);
|
||||
|
||||
return (
|
||||
<tr key={elder.id}>
|
||||
<td className="px-4 py-3">
|
||||
<Table.Row key={elder.id}>
|
||||
<Table.Cell className="px-4 py-3">
|
||||
<p className="font-medium">{elder.name}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{GENDER_LABELS[elder.gender]} · {elder.age} 岁
|
||||
</p>
|
||||
</td>
|
||||
<td className="px-4 py-3">{CARE_LEVEL_LABELS[elder.careLevel]}</td>
|
||||
<td className="px-4 py-3 text-muted-foreground">
|
||||
</Table.Cell>
|
||||
<Table.Cell className="px-4 py-3">{CARE_LEVEL_LABELS[elder.careLevel]}</Table.Cell>
|
||||
<Table.Cell className="px-4 py-3 text-muted-foreground">
|
||||
{elder.room}-{elder.bed}
|
||||
</td>
|
||||
<td className="px-4 py-3 text-muted-foreground">
|
||||
</Table.Cell>
|
||||
<Table.Cell className="px-4 py-3 text-muted-foreground">
|
||||
{elder.primaryContact} / {elder.phone}
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
</Table.Cell>
|
||||
<Table.Cell className="px-4 py-3">
|
||||
<Badge variant={elder.status === "active" ? "success" : "secondary"}>
|
||||
{ELDER_STATUS_LABELS[elder.status]}
|
||||
</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
</Table.Cell>
|
||||
<Table.Cell className="px-4 py-3">
|
||||
<Badge variant={risk.variant}>{risk.label}</Badge>
|
||||
</td>
|
||||
<td className="px-4 py-3">
|
||||
</Table.Cell>
|
||||
<Table.Cell className="px-4 py-3">
|
||||
<div className="flex justify-end gap-2">
|
||||
<Button
|
||||
disabled={!canUpdate}
|
||||
@@ -390,19 +396,19 @@ export function EldersClient({ initialElders, permissions }: EldersClientProps):
|
||||
删除
|
||||
</Button>
|
||||
</div>
|
||||
</td>
|
||||
</tr>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
);
|
||||
})}
|
||||
{visibleElders.length === 0 ? (
|
||||
<tr>
|
||||
<td className="px-4 py-8 text-center text-muted-foreground" colSpan={7}>
|
||||
<Table.Row>
|
||||
<Table.Cell className="px-4 py-8 text-center text-muted-foreground" colSpan={7}>
|
||||
暂无匹配老人档案
|
||||
</td>
|
||||
</tr>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
) : null}
|
||||
</tbody>
|
||||
</table>
|
||||
</Table.Body>
|
||||
</Table>
|
||||
</div>
|
||||
<div className="mt-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
@@ -444,20 +450,15 @@ export function EldersClient({ initialElders, permissions }: EldersClientProps):
|
||||
<Input value={form.name} onChange={(event) => updateField("name", event.target.value)} />
|
||||
</label>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<label className="grid gap-1.5 text-sm font-medium">
|
||||
<div className="grid gap-1.5 text-sm font-medium">
|
||||
性别
|
||||
<select
|
||||
className="h-11 rounded-md border bg-background px-3 text-sm"
|
||||
onChange={(event) => updateField("gender", event.target.value as ElderInput["gender"])}
|
||||
<Select
|
||||
aria-label="性别"
|
||||
onValueChange={(value) => updateField("gender", value as ElderInput["gender"])}
|
||||
options={genderOptions}
|
||||
value={form.gender}
|
||||
>
|
||||
{Object.entries(GENDER_LABELS).map(([value, label]) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
/>
|
||||
</div>
|
||||
<label className="grid gap-1.5 text-sm font-medium">
|
||||
年龄
|
||||
<Input
|
||||
@@ -470,34 +471,24 @@ export function EldersClient({ initialElders, permissions }: EldersClientProps):
|
||||
</label>
|
||||
</div>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<label className="grid gap-1.5 text-sm font-medium">
|
||||
<div className="grid gap-1.5 text-sm font-medium">
|
||||
护理等级
|
||||
<select
|
||||
className="h-11 rounded-md border bg-background px-3 text-sm"
|
||||
onChange={(event) => updateField("careLevel", event.target.value as ElderInput["careLevel"])}
|
||||
<Select
|
||||
aria-label="护理等级"
|
||||
onValueChange={(value) => updateField("careLevel", value as ElderInput["careLevel"])}
|
||||
options={formCareLevelOptions}
|
||||
value={form.careLevel}
|
||||
>
|
||||
{Object.entries(CARE_LEVEL_LABELS).map(([value, label]) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
<label className="grid gap-1.5 text-sm font-medium">
|
||||
/>
|
||||
</div>
|
||||
<div className="grid gap-1.5 text-sm font-medium">
|
||||
状态
|
||||
<select
|
||||
className="h-11 rounded-md border bg-background px-3 text-sm"
|
||||
onChange={(event) => updateField("status", event.target.value as ElderInput["status"])}
|
||||
<Select
|
||||
aria-label="状态"
|
||||
onValueChange={(value) => updateField("status", value as ElderInput["status"])}
|
||||
options={formStatusOptions}
|
||||
value={form.status}
|
||||
>
|
||||
{Object.entries(ELDER_STATUS_LABELS).map(([value, label]) => (
|
||||
<option key={value} value={value}>
|
||||
{label}
|
||||
</option>
|
||||
))}
|
||||
</select>
|
||||
</label>
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className="grid gap-3 sm:grid-cols-2">
|
||||
<label className="grid gap-1.5 text-sm font-medium">
|
||||
@@ -519,8 +510,8 @@ export function EldersClient({ initialElders, permissions }: EldersClientProps):
|
||||
</label>
|
||||
<label className="grid gap-1.5 text-sm font-medium">
|
||||
健康备注
|
||||
<textarea
|
||||
className="min-h-24 rounded-md border bg-background px-3 py-2 text-sm outline-none transition-colors focus-visible:ring-2 focus-visible:ring-ring"
|
||||
<Textarea
|
||||
className="min-h-24"
|
||||
onChange={(event) => updateField("medicalNotes", event.target.value)}
|
||||
value={form.medicalNotes}
|
||||
/>
|
||||
|
||||
Reference in New Issue
Block a user