201 lines
8.5 KiB
TypeScript
201 lines
8.5 KiB
TypeScript
import React, { useState } from 'react';
|
||
import { useDemoStore, DeliveryItem } from '../../mock/store';
|
||
import { Badge } from '../../shared/components/Badge';
|
||
import { Button } from '../../shared/components/Button';
|
||
import { formatCurrency } from '../../shared/utils/formatters';
|
||
import {
|
||
Truck,
|
||
MapPin,
|
||
Phone,
|
||
CheckCircle2,
|
||
Smartphone,
|
||
} from 'lucide-react';
|
||
|
||
export interface DeliveryViewProps {
|
||
initialMobile?: boolean;
|
||
}
|
||
|
||
export const DeliveryView: React.FC<DeliveryViewProps> = ({ initialMobile = false }) => {
|
||
const { deliveries, updateDeliveryStatus } = useDemoStore();
|
||
const [courierMobileMode, setCourierMobileMode] = useState(initialMobile);
|
||
|
||
const getStatusBadge = (status: DeliveryItem['status']) => {
|
||
switch (status) {
|
||
case 'in_transit':
|
||
return <Badge variant="info" size="sm">Курьер в пути</Badge>;
|
||
case 'delivered':
|
||
return <Badge variant="green" size="sm">Доставлен</Badge>;
|
||
case 'pending':
|
||
default:
|
||
return <Badge variant="warning" size="sm">Ожидает курьера</Badge>;
|
||
}
|
||
};
|
||
|
||
return (
|
||
<div className="space-y-4 animate-fade-in">
|
||
<div className="flex items-center justify-between bg-krona-surface p-3.5 rounded-xl border border-krona-border shadow-krona-soft">
|
||
<div>
|
||
<h2 className="text-sm font-bold text-krona-primary">
|
||
Управление доставкой & Мониторинг курьеров
|
||
</h2>
|
||
<p className="text-xs text-krona-secondary">
|
||
Логистический контур: распределение заказов, расчет времени прибытия (ETA) и контроль налички
|
||
</p>
|
||
</div>
|
||
|
||
<Button
|
||
variant={courierMobileMode ? 'gold' : 'secondary'}
|
||
size="sm"
|
||
onClick={() => setCourierMobileMode(!courierMobileMode)}
|
||
>
|
||
<Smartphone className="w-3.5 h-3.5 mr-1.5" />
|
||
{courierMobileMode ? 'Обычный диспетчерский вид' : 'Экран курьера (Mobile View)'}
|
||
</Button>
|
||
</div>
|
||
|
||
{courierMobileMode ? (
|
||
/* Courier Mobile-First Screen */
|
||
<div className="max-w-md mx-auto bg-krona-surface border-2 border-krona-gold/60 rounded-2xl p-4 shadow-xl space-y-4">
|
||
<div className="flex items-center justify-between pb-3 border-b border-krona-border">
|
||
<div className="flex items-center gap-2">
|
||
<div className="w-8 h-8 rounded-full bg-krona-gold/20 flex items-center justify-center text-krona-gold">
|
||
<Truck className="w-4 h-4" />
|
||
</div>
|
||
<div>
|
||
<h4 className="text-xs font-bold text-krona-primary">Алексей Смирнов (Авто)</h4>
|
||
<span className="text-[10px] text-krona-secondary">Смена активна • Наличные: 24 500 ₽</span>
|
||
</div>
|
||
</div>
|
||
<Badge variant="green" size="sm">Онлайн</Badge>
|
||
</div>
|
||
|
||
<div className="space-y-3">
|
||
<span className="text-xs font-semibold uppercase tracking-wider text-krona-secondary block">
|
||
Мои текущие доставки ({deliveries.filter((d) => d.status !== 'delivered').length})
|
||
</span>
|
||
|
||
{deliveries.map((del) => (
|
||
<div
|
||
key={del.id}
|
||
className="p-3.5 rounded-xl border border-krona-border bg-krona-workspace space-y-2.5"
|
||
>
|
||
<div className="flex items-center justify-between">
|
||
<span className="font-bold text-xs text-krona-primary">{del.orderId}</span>
|
||
{getStatusBadge(del.status)}
|
||
</div>
|
||
|
||
<div className="space-y-1 text-xs">
|
||
<div className="flex items-start gap-1.5 text-krona-primary font-medium">
|
||
<MapPin className="w-3.5 h-3.5 text-krona-danger shrink-0 mt-0.5" />
|
||
<span>{del.address}</span>
|
||
</div>
|
||
|
||
<div className="flex items-center justify-between pt-1 text-[11px] text-krona-secondary">
|
||
<span>{del.recipientName}</span>
|
||
<a href={`tel:${del.phone}`} className="text-krona-green font-semibold flex items-center gap-1">
|
||
<Phone className="w-3 h-3" />
|
||
{del.phone}
|
||
</a>
|
||
</div>
|
||
|
||
<div className="flex items-center justify-between pt-1 border-t border-krona-border/60">
|
||
<span className="text-krona-secondary">К оплате:</span>
|
||
<span className="font-bold text-krona-primary">{formatCurrency(del.amount)} ({del.paymentMethod})</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-2 flex gap-2">
|
||
{del.status === 'pending' && (
|
||
<Button
|
||
variant="primary"
|
||
size="sm"
|
||
className="w-full text-xs"
|
||
onClick={() => updateDeliveryStatus(del.id, 'in_transit')}
|
||
>
|
||
Заказ забрал, еду к клиенту
|
||
</Button>
|
||
)}
|
||
|
||
{del.status === 'in_transit' && (
|
||
<Button
|
||
variant="gold"
|
||
size="sm"
|
||
className="w-full text-xs"
|
||
onClick={() => updateDeliveryStatus(del.id, 'delivered')}
|
||
>
|
||
<CheckCircle2 className="w-3.5 h-3.5 mr-1" />
|
||
Заказ вручен и оплачен
|
||
</Button>
|
||
)}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
</div>
|
||
) : (
|
||
/* Dispatcher Desktop Grid */
|
||
<div className="grid grid-cols-1 md:grid-cols-3 gap-4">
|
||
{deliveries.map((del) => (
|
||
<div
|
||
key={del.id}
|
||
className="p-4 rounded-xl border border-krona-border bg-krona-surface shadow-krona-soft space-y-3"
|
||
>
|
||
<div className="flex items-center justify-between pb-2 border-b border-krona-border/60">
|
||
<span className="font-bold text-sm text-krona-primary">{del.orderId}</span>
|
||
{getStatusBadge(del.status)}
|
||
</div>
|
||
|
||
<div className="space-y-1.5 text-xs">
|
||
<div className="flex items-start gap-1.5">
|
||
<MapPin className="w-3.5 h-3.5 text-krona-danger shrink-0 mt-0.5" />
|
||
<span className="text-krona-primary font-medium leading-tight">{del.address}</span>
|
||
</div>
|
||
|
||
<div className="pt-1 text-krona-secondary">
|
||
<div>Получатель: <strong className="text-krona-primary">{del.recipientName}</strong></div>
|
||
<div>Телефон: <span className="font-mono">{del.phone}</span></div>
|
||
<div>Курьер: <strong className="text-krona-primary">{del.courierName}</strong></div>
|
||
</div>
|
||
|
||
<div className="flex justify-between pt-1 border-t border-krona-border/40 font-bold">
|
||
<span>Сумма:</span>
|
||
<span className="text-krona-green">{formatCurrency(del.amount)}</span>
|
||
</div>
|
||
</div>
|
||
|
||
<div className="pt-2 border-t border-krona-border/60 flex justify-end gap-2">
|
||
{del.status === 'pending' && (
|
||
<Button
|
||
variant="primary"
|
||
size="sm"
|
||
className="w-full text-xs"
|
||
onClick={() => updateDeliveryStatus(del.id, 'in_transit')}
|
||
>
|
||
Передать курьеру
|
||
</Button>
|
||
)}
|
||
|
||
{del.status === 'in_transit' && (
|
||
<Button
|
||
variant="secondary"
|
||
size="sm"
|
||
className="w-full text-xs"
|
||
onClick={() => updateDeliveryStatus(del.id, 'delivered')}
|
||
>
|
||
Отметить вручение
|
||
</Button>
|
||
)}
|
||
|
||
{del.status === 'delivered' && (
|
||
<div className="w-full text-center text-xs text-krona-green font-semibold py-1">
|
||
✓ Успешно доставлен
|
||
</div>
|
||
)}
|
||
</div>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
};
|