mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 09:58:12 +00:00
booking operations and trains scheduling also allocations
This commit is contained in:
@@ -0,0 +1,182 @@
|
||||
import type { OnChangeFn, PaginationState } from "@edr/ui-common";
|
||||
import { Card, Group, SimpleGrid, Stack, Text } from "@mantine/core";
|
||||
import { Badge } from "@mantine/core";
|
||||
|
||||
import type { FleetResourceConfig } from "@/pages/fleet/config/resources";
|
||||
import type { FleetRecord } from "@/services/fleet/fleet.service";
|
||||
|
||||
import FleetRecordActions from "./FleetRecordActions";
|
||||
import { cardInitials, resolveFleetCardPresentation } from "./fleetCardMeta";
|
||||
import { formatFleetCell } from "./fleetFormat";
|
||||
import RuleEngineListFooter from "../ruleEngine/RuleEngineListFooter";
|
||||
|
||||
export interface FleetCardGridProps {
|
||||
config: FleetResourceConfig;
|
||||
rows: FleetRecord[];
|
||||
status: "loading" | "error" | "success";
|
||||
emptyMessage: string;
|
||||
pagination: PaginationState;
|
||||
pageCount: number;
|
||||
totalCount: number;
|
||||
onPaginationChange: OnChangeFn<PaginationState>;
|
||||
onEdit: (record: FleetRecord) => void;
|
||||
onRemove: (record: FleetRecord) => void;
|
||||
}
|
||||
|
||||
const FleetCardGrid = ({
|
||||
config,
|
||||
rows,
|
||||
status,
|
||||
emptyMessage,
|
||||
pagination,
|
||||
pageCount,
|
||||
totalCount,
|
||||
onPaginationChange,
|
||||
onEdit,
|
||||
onRemove,
|
||||
}: FleetCardGridProps) => {
|
||||
const presentation = resolveFleetCardPresentation(config);
|
||||
|
||||
if (status === "loading") {
|
||||
return (
|
||||
<Text size="sm" c="dimmed" py="xl" ta="center">
|
||||
Loading…
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (status === "error") {
|
||||
return (
|
||||
<Text size="sm" c="red" py="xl" ta="center">
|
||||
Failed to load data
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
if (!rows.length) {
|
||||
return (
|
||||
<Text size="sm" c="dimmed" py="xl" ta="center">
|
||||
{emptyMessage}
|
||||
</Text>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Stack gap={0}>
|
||||
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md" p="md">
|
||||
{rows.map((record) => {
|
||||
const title = String(
|
||||
(record as unknown as Record<string, unknown>)[presentation.titleKey] ?? config.entityLabel,
|
||||
);
|
||||
const code = presentation.codeKey
|
||||
? (record as unknown as Record<string, unknown>)[presentation.codeKey]
|
||||
: null;
|
||||
const subtitle = presentation.subtitleKey
|
||||
? (record as unknown as Record<string, unknown>)[presentation.subtitleKey]
|
||||
: null;
|
||||
const statusValue = presentation.statusKey
|
||||
? (record as unknown as Record<string, unknown>)[presentation.statusKey]
|
||||
: null;
|
||||
|
||||
return (
|
||||
<Card
|
||||
key={String((record as { id: string }).id)}
|
||||
radius="lg"
|
||||
padding="lg"
|
||||
withBorder
|
||||
style={{ borderColor: "var(--mantine-color-gray-2)" }}
|
||||
>
|
||||
<Stack gap="md">
|
||||
<Group justify="space-between" align="flex-start" wrap="nowrap">
|
||||
<Group gap="sm" wrap="nowrap">
|
||||
<div
|
||||
style={{
|
||||
width: 40,
|
||||
height: 40,
|
||||
borderRadius: 10,
|
||||
background: "var(--mantine-color-green-0)",
|
||||
color: "var(--mantine-color-green-7)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
fontWeight: 700,
|
||||
fontSize: 14,
|
||||
}}
|
||||
>
|
||||
{cardInitials(title)}
|
||||
</div>
|
||||
<Stack gap={2}>
|
||||
<Text fw={600} size="sm" lineClamp={1}>
|
||||
{title || "—"}
|
||||
</Text>
|
||||
{subtitle != null && subtitle !== "" ? (
|
||||
<Text size="xs" c="dimmed" lineClamp={1}>
|
||||
{String(subtitle)}
|
||||
</Text>
|
||||
) : null}
|
||||
</Stack>
|
||||
</Group>
|
||||
{code != null && code !== "" ? (
|
||||
<Badge variant="light" color="blue" size="sm" radius="md">
|
||||
{String(code)}
|
||||
</Badge>
|
||||
) : null}
|
||||
</Group>
|
||||
|
||||
<Stack gap={6}>
|
||||
{config.columns
|
||||
.filter(
|
||||
(col) =>
|
||||
col.accessorKey !== presentation.titleKey &&
|
||||
col.accessorKey !== presentation.codeKey &&
|
||||
col.accessorKey !== presentation.statusKey,
|
||||
)
|
||||
.slice(0, 4)
|
||||
.map((col) => (
|
||||
<Group key={col.id} justify="space-between" gap="xs">
|
||||
<Text size="xs" c="dimmed">
|
||||
{col.header}
|
||||
</Text>
|
||||
<Text size="xs" fw={500}>
|
||||
{formatFleetCell(
|
||||
(record as unknown as Record<string, unknown>)[col.accessorKey],
|
||||
col.format,
|
||||
col.accessorKey,
|
||||
)}
|
||||
</Text>
|
||||
</Group>
|
||||
))}
|
||||
{statusValue != null ? (
|
||||
<Group justify="space-between" gap="xs">
|
||||
<Text size="xs" c="dimmed">
|
||||
Status
|
||||
</Text>
|
||||
{formatFleetCell(statusValue, "statusBadge")}
|
||||
</Group>
|
||||
) : null}
|
||||
</Stack>
|
||||
|
||||
<FleetRecordActions
|
||||
record={record}
|
||||
config={config}
|
||||
layout="compact"
|
||||
onEdit={onEdit}
|
||||
onRemove={onRemove}
|
||||
/>
|
||||
</Stack>
|
||||
</Card>
|
||||
);
|
||||
})}
|
||||
</SimpleGrid>
|
||||
<RuleEngineListFooter
|
||||
pagination={pagination}
|
||||
pageCount={pageCount}
|
||||
totalCount={totalCount}
|
||||
itemLabel={config.entityLabel.toLowerCase() + "s"}
|
||||
onPaginationChange={onPaginationChange}
|
||||
/>
|
||||
</Stack>
|
||||
);
|
||||
};
|
||||
|
||||
export default FleetCardGrid;
|
||||
Reference in New Issue
Block a user