Files
edr-platform/apps/edr-freight-web/backoffice/src/components/fleet/FleetCardGrid.tsx

186 lines
6.2 KiB
TypeScript

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}>
{presentation.subtitleKey === "currentYard" ||
presentation.subtitleKey === "currentYardId"
? formatFleetCell(subtitle, "entityLabel", presentation.subtitleKey)
: 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;