mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-28 23:00:57 +00:00
177 lines
5.7 KiB
TypeScript
177 lines
5.7 KiB
TypeScript
import { useMemo } from 'react';
|
|
import { ActionIcon, Box, Card, Divider, Group, Progress, SimpleGrid, Stack, Text, Tooltip } from '@mantine/core';
|
|
import { Building2, Eye, MapPin, Package, Pencil, Weight } from 'lucide-react';
|
|
|
|
import { useQuery } from '@tanstack/react-query';
|
|
|
|
import { api } from '@/services/api';
|
|
import type { Warehouse } from '@/types/warehouse';
|
|
import { WarehouseStatusBadge, WarehouseTypeBadge } from './badges';
|
|
import { formatCapacity } from './options';
|
|
|
|
interface WarehouseCardViewProps {
|
|
warehouses: Warehouse[];
|
|
onView: (warehouse: Warehouse) => void;
|
|
onEdit: (warehouse: Warehouse) => void;
|
|
}
|
|
|
|
export function WarehouseCardView({ warehouses, onView, onEdit }: WarehouseCardViewProps) {
|
|
const { data: stations } = useQuery(
|
|
api.stations.list.queryOptions({ staleTime: 5 * 60 * 1000 }),
|
|
);
|
|
const stationNameById = useMemo(
|
|
() => new Map((stations ?? []).map((s) => [s.id, s.name])),
|
|
[stations],
|
|
);
|
|
|
|
if (warehouses.length === 0) {
|
|
return (
|
|
<Text c="dimmed" ta="center" py="xl">
|
|
No warehouses found.
|
|
</Text>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<SimpleGrid cols={{ base: 1, sm: 2, lg: 3 }} spacing="md">
|
|
{warehouses.map((warehouse) => (
|
|
<Card
|
|
key={warehouse.id}
|
|
withBorder
|
|
radius="md"
|
|
padding={0}
|
|
style={{
|
|
overflow: 'hidden',
|
|
borderColor: 'var(--mantine-color-gray-2)',
|
|
background: 'white',
|
|
}}
|
|
>
|
|
<Box h={3} bg={warehouse.status === 'ACTIVE' ? 'green.5' : 'gray.4'} />
|
|
<Stack gap="md" p="lg">
|
|
<Group justify="space-between" wrap="nowrap" align="flex-start">
|
|
<Group gap="sm" wrap="nowrap" style={{ minWidth: 0 }}>
|
|
<Box
|
|
style={{
|
|
width: 38,
|
|
height: 38,
|
|
borderRadius: 8,
|
|
display: 'flex',
|
|
alignItems: 'center',
|
|
justifyContent: 'center',
|
|
flexShrink: 0,
|
|
background: 'var(--mantine-color-green-0)',
|
|
color: 'var(--mantine-color-green-7)',
|
|
border: '1px solid var(--mantine-color-green-2)',
|
|
}}
|
|
>
|
|
<Building2 size={18} />
|
|
</Box>
|
|
<Box style={{ minWidth: 0 }}>
|
|
<Text fw={800} size="md" truncate>
|
|
{warehouse.name}
|
|
</Text>
|
|
<Text size="xs" c="dimmed" fw={600} truncate>
|
|
{warehouse.code}
|
|
</Text>
|
|
</Box>
|
|
</Group>
|
|
|
|
<Stack gap={6} align="flex-end">
|
|
<WarehouseStatusBadge status={warehouse.status} />
|
|
<WarehouseTypeBadge type={warehouse.type} />
|
|
</Stack>
|
|
</Group>
|
|
|
|
<Stack gap={8}>
|
|
{warehouse.stationId && stationNameById.get(warehouse.stationId) && (
|
|
<Group gap={8} c="dimmed" wrap="nowrap">
|
|
<Building2 size={15} />
|
|
<Text size="sm" truncate>
|
|
{stationNameById.get(warehouse.stationId)}
|
|
</Text>
|
|
</Group>
|
|
)}
|
|
|
|
{warehouse.locationName && (
|
|
<Group gap={8} c="dimmed" wrap="nowrap">
|
|
<MapPin size={15} />
|
|
<Text size="sm" truncate>
|
|
{warehouse.locationName}
|
|
</Text>
|
|
</Group>
|
|
)}
|
|
</Stack>
|
|
|
|
<Divider />
|
|
|
|
<Stack gap="md">
|
|
<CapacityRow
|
|
icon={Weight}
|
|
label="Weight"
|
|
current={warehouse.currentWeight}
|
|
capacity={warehouse.capacityWeight}
|
|
/>
|
|
<CapacityRow
|
|
icon={Package}
|
|
label="Containers"
|
|
current={warehouse.currentContainers}
|
|
capacity={warehouse.capacityContainers}
|
|
/>
|
|
</Stack>
|
|
|
|
<Group justify="flex-end" gap="xs" mt="auto">
|
|
<Tooltip label="View warehouse" withArrow>
|
|
<ActionIcon variant="light" color="gray" onClick={() => onView(warehouse)} aria-label="View warehouse">
|
|
<Eye size={16} />
|
|
</ActionIcon>
|
|
</Tooltip>
|
|
<Tooltip label="Edit warehouse" withArrow>
|
|
<ActionIcon variant="light" color="orange" onClick={() => onEdit(warehouse)} aria-label="Edit warehouse">
|
|
<Pencil size={16} />
|
|
</ActionIcon>
|
|
</Tooltip>
|
|
</Group>
|
|
</Stack>
|
|
</Card>
|
|
))}
|
|
</SimpleGrid>
|
|
);
|
|
}
|
|
|
|
const capacityPercent = (current?: number | null, capacity?: number | null) => {
|
|
if (!capacity || capacity <= 0) return 0;
|
|
return Math.min(100, Math.max(0, ((current ?? 0) / capacity) * 100));
|
|
};
|
|
|
|
function CapacityRow({
|
|
icon: Icon,
|
|
label,
|
|
current,
|
|
capacity,
|
|
}: {
|
|
icon: typeof Weight;
|
|
label: string;
|
|
current?: number | null;
|
|
capacity?: number | null;
|
|
}) {
|
|
const percent = capacityPercent(current, capacity);
|
|
const color = percent >= 90 ? 'red' : percent >= 70 ? 'orange' : 'green';
|
|
|
|
return (
|
|
<Stack gap={6}>
|
|
<Group justify="space-between" wrap="nowrap">
|
|
<Group gap={7} c="dimmed" wrap="nowrap">
|
|
<Icon size={15} />
|
|
<Text size="xs" fw={700} tt="uppercase">
|
|
{label}
|
|
</Text>
|
|
</Group>
|
|
<Text size="sm" fw={700}>
|
|
{formatCapacity(Number(current) || 0, capacity)}
|
|
</Text>
|
|
</Group>
|
|
<Progress value={percent} color={color} size="xs" radius="xl" bg="var(--mantine-color-gray-1)" />
|
|
</Stack>
|
|
);
|
|
}
|