Files
edr-platform/apps/edr-freight-web/backoffice/src/components/warehouses/WarehouseCardView.tsx
2026-06-24 15:23:50 +03:00

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>
);
}