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:
@@ -1893,6 +1893,8 @@
|
|||||||
"location": "الموقع",
|
"location": "الموقع",
|
||||||
"active": "مفعّلة",
|
"active": "مفعّلة",
|
||||||
"isActive": "مفعّلة",
|
"isActive": "مفعّلة",
|
||||||
|
"status": "الحالة",
|
||||||
|
"actions": "الإجراء",
|
||||||
"empty": "لا توجد قاعات."
|
"empty": "لا توجد قاعات."
|
||||||
},
|
},
|
||||||
"issues": {
|
"issues": {
|
||||||
|
|||||||
@@ -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": {
|
||||||
|
|||||||
@@ -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>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user