Task #739: تحويل تبويب القاعات من بطاقات إلى جدول

- protocol.tsx (tab === "rooms"): replaced the 2-column card grid with a
  Table styled like the bookings table (bg-slate-100/80 header, centered
  headers/cells) with columns: # | القاعات | الحالة | الإجراء.
  - # = sequential index; name via nameOf with capacity line beneath.
  - Status badge keeps emerald-100 / slate-200 colors.
  - Actions column (edit/delete, same setRoomDialog/setDeleteTarget
    handlers) rendered only when canManageRooms.
  - EmptyState (rooms.empty), "قاعة جديدة" button, and dialogs unchanged.
- i18n: added protocol.rooms.status ("الحالة"/"Status") and
  protocol.rooms.actions ("الإجراء"/"Action") to ar.json and en.json.
- Verified: pnpm --filter @workspace/tx-os exec tsc --noEmit clean;
  architect review passed with no blocking issues.
This commit is contained in:
Replit Agent
2026-07-09 09:58:09 +00:00
parent e2afc0bb28
commit 409b2c6fd5
3 changed files with 84 additions and 53 deletions
+2
View File
@@ -1893,6 +1893,8 @@
"location": "الموقع", "location": "الموقع",
"active": "مفعّلة", "active": "مفعّلة",
"isActive": "مفعّلة", "isActive": "مفعّلة",
"status": "الحالة",
"actions": "الإجراء",
"empty": "لا توجد قاعات." "empty": "لا توجد قاعات."
}, },
"issues": { "issues": {
+2
View File
@@ -1738,6 +1738,8 @@
"location": "Location", "location": "Location",
"active": "Active", "active": "Active",
"isActive": "Active", "isActive": "Active",
"status": "Status",
"actions": "Action",
"empty": "No rooms." "empty": "No rooms."
}, },
"issues": { "issues": {
+80 -53
View File
@@ -1780,60 +1780,87 @@ export default function ProtocolPage() {
</Button> </Button>
)} )}
</div> </div>
<div className="grid gap-2 sm:grid-cols-2"> {rooms.data?.length === 0 ? (
{(rooms.data ?? []).map((r) => ( <EmptyState text={t("protocol.rooms.empty")} />
<div ) : (
key={r.id} <div className="rounded-xl border border-slate-200 bg-white overflow-hidden">
className="bg-white rounded-xl border border-slate-200 p-3" <Table>
> <TableHeader>
<div className="flex items-start justify-between gap-2"> <TableRow className="bg-slate-100/80 hover:bg-slate-100/80">
<div className="min-w-0"> <TableHead className="w-10 text-center">#</TableHead>
<div className="font-medium text-slate-800"> <TableHead className="text-center whitespace-nowrap">
{nameOf(r.nameAr, r.nameEn)} {t("protocol.rooms.title")}
</div> </TableHead>
<div className="text-xs text-slate-500"> <TableHead className="text-center whitespace-nowrap">
{r.capacity {t("protocol.rooms.status")}
? `${t("protocol.rooms.capacity")}: ${r.capacity}` </TableHead>
: ""} {c?.canManageRooms && (
</div> <TableHead className="text-center whitespace-nowrap">
</div> {t("protocol.rooms.actions")}
<span </TableHead>
className={cn(
"text-xs px-2 py-0.5 rounded-full shrink-0",
r.isActive
? "bg-emerald-100 text-emerald-700"
: "bg-slate-200 text-slate-500",
)} )}
> </TableRow>
{r.isActive </TableHeader>
? t("protocol.rooms.active") <TableBody>
: t("protocol.rooms.inactive")} {(rooms.data ?? []).map((r, i) => (
</span> <TableRow key={r.id}>
</div> <TableCell className="text-center text-slate-500">
{c?.canManageRooms && ( {i + 1}
<div className="flex gap-1.5 mt-2"> </TableCell>
<Button <TableCell className="text-center">
size="sm" <div className="font-medium text-slate-800">
variant="ghost" {nameOf(r.nameAr, r.nameEn)}
onClick={() => setRoomDialog({ open: true, edit: r })} </div>
> {r.capacity ? (
<Pencil size={14} /> <div className="text-xs text-slate-500">
</Button> {`${t("protocol.rooms.capacity")}: ${r.capacity}`}
<Button </div>
size="sm" ) : null}
variant="ghost" </TableCell>
onClick={() => setDeleteTarget({ kind: "room", id: r.id })} <TableCell className="text-center">
> <span
<Trash2 size={14} className="text-rose-500" /> className={cn(
</Button> "text-xs px-2 py-0.5 rounded-full",
</div> r.isActive
)} ? "bg-emerald-100 text-emerald-700"
</div> : "bg-slate-200 text-slate-500",
))} )}
{rooms.data?.length === 0 && ( >
<EmptyState text={t("protocol.rooms.empty")} /> {r.isActive
)} ? t("protocol.rooms.active")
</div> : t("protocol.rooms.inactive")}
</span>
</TableCell>
{c?.canManageRooms && (
<TableCell className="text-center">
<div className="flex justify-center gap-1.5">
<Button
size="sm"
variant="ghost"
onClick={() =>
setRoomDialog({ open: true, edit: r })
}
>
<Pencil size={14} />
</Button>
<Button
size="sm"
variant="ghost"
onClick={() =>
setDeleteTarget({ kind: "room", id: r.id })
}
>
<Trash2 size={14} className="text-rose-500" />
</Button>
</div>
</TableCell>
)}
</TableRow>
))}
</TableBody>
</Table>
</div>
)}
</section> </section>
)} )}