241 lines
7.6 KiB
TypeScript
241 lines
7.6 KiB
TypeScript
'use client';
|
|
|
|
import { useState } from 'react';
|
|
import { Collection } from '@/lib/types';
|
|
import { cn, formatDate, formatCurrency, parseCoordinates } from '@/lib/utils';
|
|
import {
|
|
Check,
|
|
AlertTriangle,
|
|
Navigation,
|
|
ChevronDown,
|
|
ChevronUp,
|
|
Package,
|
|
Wrench,
|
|
Hammer,
|
|
ShoppingCart,
|
|
AlertCircle,
|
|
} from 'lucide-react';
|
|
|
|
interface CollectionItemProps {
|
|
collection: Collection;
|
|
onCollect: (collection: Collection) => void;
|
|
onProblem: (collection: Collection) => void;
|
|
onNavigate: (lat: number, lng: number, name: string) => void;
|
|
}
|
|
|
|
function getItemType(collection: Collection): {
|
|
type: string;
|
|
icon: React.ReactNode;
|
|
color: string;
|
|
label: string;
|
|
} {
|
|
if (collection.status === 'problem') {
|
|
return {
|
|
type: 'problem',
|
|
icon: <AlertTriangle size={16} />,
|
|
color: 'text-red-500',
|
|
label: 'Problem',
|
|
};
|
|
}
|
|
|
|
const orderNumber = collection.expand?.orders_orderitem?.expand?.orders_order?.order_number || '';
|
|
const desc = collection.description || collection.expand?.orders_orderitem?.description || '';
|
|
|
|
if (orderNumber.toLowerCase().includes('breakdown')) {
|
|
return {
|
|
type: 'breakdown',
|
|
icon: <Hammer size={16} />,
|
|
color: 'text-orange-600',
|
|
label: 'Breakdown',
|
|
};
|
|
}
|
|
|
|
if (orderNumber.toLowerCase().includes('repair')) {
|
|
return {
|
|
type: 'repair',
|
|
icon: <Wrench size={16} />,
|
|
color: 'text-amber-500',
|
|
label: 'Repair',
|
|
};
|
|
}
|
|
|
|
if (collection.is_manual) {
|
|
return {
|
|
type: 'manual',
|
|
icon: <Package size={16} />,
|
|
color: 'text-purple-500',
|
|
label: 'Manual',
|
|
};
|
|
}
|
|
|
|
return {
|
|
type: 'normal',
|
|
icon: <ShoppingCart size={16} />,
|
|
color: 'text-orange-500',
|
|
label: 'Order',
|
|
};
|
|
}
|
|
|
|
export default function CollectionItemCard({
|
|
collection,
|
|
onCollect,
|
|
onProblem,
|
|
onNavigate,
|
|
}: CollectionItemProps) {
|
|
const [expanded, setExpanded] = useState(false);
|
|
|
|
const supplier = collection.expand?.wfdash_suppliers;
|
|
const orderItem = collection.expand?.orders_orderitem;
|
|
const itemType = getItemType(collection);
|
|
|
|
// Parse coordinates from text field
|
|
const coords = parseCoordinates(supplier?.coordinates);
|
|
const hasCoords = coords !== null;
|
|
|
|
const description = collection.is_manual
|
|
? collection.description
|
|
: orderItem?.description || 'Unknown item';
|
|
|
|
const quantity = collection.quantity || orderItem?.quantity || 0;
|
|
const unit = collection.unit || orderItem?.unit || 'units';
|
|
|
|
const orderNumber = orderItem?.expand?.orders_order?.order_number || '';
|
|
const poNumber = orderItem?.expand?.orders_purchaseorder?.po_number || '';
|
|
|
|
return (
|
|
<div
|
|
className={cn(
|
|
'bg-white rounded-xl card-shadow mb-3 overflow-hidden animate-fade-in',
|
|
collection.status === 'problem' && 'border-l-4 border-red-500',
|
|
collection.status === 'assigned' && 'border-l-4 border-orange-500',
|
|
collection.status === 'pending' && 'border-l-4 border-gray-300'
|
|
)}
|
|
>
|
|
{/* Header */}
|
|
<div
|
|
className="p-4 cursor-pointer active:bg-gray-50 transition-colors"
|
|
onClick={() => setExpanded(!expanded)}
|
|
>
|
|
<div className="flex items-start justify-between">
|
|
<div className="flex-1 min-w-0">
|
|
<div className="flex items-center gap-2 mb-1">
|
|
<span className={cn('flex-shrink-0', itemType.color)}>
|
|
{itemType.icon}
|
|
</span>
|
|
<h3 className="font-semibold text-sm leading-tight truncate">
|
|
{description}
|
|
</h3>
|
|
<span
|
|
className={cn(
|
|
'text-[10px] px-1.5 py-0.5 rounded-full font-medium flex-shrink-0',
|
|
itemType.type === 'problem' && 'bg-red-100 text-red-700',
|
|
itemType.type === 'breakdown' && 'bg-orange-100 text-orange-700',
|
|
itemType.type === 'repair' && 'bg-amber-100 text-amber-700',
|
|
itemType.type === 'manual' && 'bg-purple-100 text-purple-700',
|
|
itemType.type === 'normal' && 'bg-orange-100 text-orange-700'
|
|
)}
|
|
>
|
|
{itemType.label}
|
|
</span>
|
|
</div>
|
|
|
|
<div className="text-xs text-gray-500 mt-1 space-y-0.5">
|
|
<p className="font-medium text-gray-700">
|
|
{supplier?.suppliername || 'Unknown Supplier'}
|
|
</p>
|
|
<p>Expected: {quantity} {unit}</p>
|
|
{orderNumber && <p>Order: {orderNumber}</p>}
|
|
{poNumber && <p>PO: {poNumber}</p>}
|
|
{collection.planned_date && (
|
|
<p>Planned: {formatDate(collection.planned_date)}</p>
|
|
)}
|
|
{orderItem?.selling_price && (
|
|
<p className="text-green-600 font-medium">
|
|
{formatCurrency(orderItem.selling_price * quantity)}
|
|
</p>
|
|
)}
|
|
</div>
|
|
</div>
|
|
|
|
<div className="flex flex-col items-end gap-2 ml-2">
|
|
{expanded ? (
|
|
<ChevronUp size={20} className="text-gray-400" />
|
|
) : (
|
|
<ChevronDown size={20} className="text-gray-400" />
|
|
)}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Expanded Details */}
|
|
{expanded && (
|
|
<div className="px-4 pb-4 border-t border-gray-100 pt-3">
|
|
{collection.notes && (
|
|
<div className="bg-yellow-50 border border-yellow-200 rounded-lg p-2 mb-3">
|
|
<p className="text-xs text-yellow-800 flex items-start gap-1.5">
|
|
<AlertCircle size={14} className="flex-shrink-0 mt-0.5" />
|
|
<span>{collection.notes}</span>
|
|
</p>
|
|
</div>
|
|
)}
|
|
|
|
{supplier?.supplieraddress && (
|
|
<p className="text-xs text-gray-500 mb-3">
|
|
<strong>Address:</strong> {supplier.supplieraddress}
|
|
</p>
|
|
)}
|
|
|
|
{/* Action Buttons */}
|
|
<div className="grid grid-cols-3 gap-2">
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onCollect(collection);
|
|
}}
|
|
className={cn(
|
|
'flex items-center justify-center gap-1.5 py-2.5 px-3 rounded-lg text-sm font-medium',
|
|
'bg-green-500 text-white active:bg-green-600 transition-colors',
|
|
'touch-manipulation'
|
|
)}
|
|
>
|
|
<Check size={16} strokeWidth={2.5} />
|
|
<span>Collect</span>
|
|
</button>
|
|
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onProblem(collection);
|
|
}}
|
|
className={cn(
|
|
'flex items-center justify-center gap-1.5 py-2.5 px-3 rounded-lg text-sm font-medium',
|
|
'bg-red-500 text-white active:bg-red-600 transition-colors',
|
|
'touch-manipulation'
|
|
)}
|
|
>
|
|
<AlertTriangle size={16} strokeWidth={2.5} />
|
|
<span>Problem</span>
|
|
</button>
|
|
|
|
{hasCoords && coords && supplier && (
|
|
<button
|
|
onClick={(e) => {
|
|
e.stopPropagation();
|
|
onNavigate(coords.latitude, coords.longitude, supplier.suppliername);
|
|
}}
|
|
className={cn(
|
|
'flex items-center justify-center gap-1.5 py-2.5 px-3 rounded-lg text-sm font-medium',
|
|
'bg-orange-500 text-white active:bg-orange-600 transition-colors',
|
|
'touch-manipulation'
|
|
)}
|
|
>
|
|
<Navigation size={16} strokeWidth={2.5} />
|
|
<span>Navigate</span>
|
|
</button>
|
|
)}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
}
|