order detail view is finished
This commit is contained in:
parent
2e57c47c7f
commit
8b022bfbce
@ -1,24 +1,37 @@
|
||||
import { formatValue } from '../../lib/format';
|
||||
import { Store, Calendar, Package, ShoppingBag } from 'lucide-react';
|
||||
import { Store, Calendar, ShoppingBag, Clock, Truck, Package } from 'lucide-react';
|
||||
import './card.css';
|
||||
|
||||
export function OrderCard({ row, fields }: { row: Record<string, any>; fields: any[] }) {
|
||||
export function OrderCard({ row, fields, isDetailView = false }: { row: Record<string, any>; fields: any[]; isDetailView?: boolean }) {
|
||||
const orderIdStr = row.order_id || '—';
|
||||
|
||||
// Extract store lookup object if it exists
|
||||
const storeObj = (row.select_store || row.store) as Record<string, unknown> | null;
|
||||
const storeVal = formatValue(storeObj?.business_name || row.business_name || row.store_name || '—');
|
||||
const storeVal = formatValue(storeObj?.business_name || storeObj?.store_name || row.business_name || row.store_name || '—');
|
||||
const routeName = formatValue(storeObj?.route_name || storeObj?.route || row.route_name || row.route || '—');
|
||||
const areaName = formatValue(storeObj?.area || row.area || row.area_name || '—');
|
||||
const distName = formatValue(storeObj?.distributor_name || '—');
|
||||
|
||||
// Extract User object
|
||||
const userKey = Object.keys(row).find(k => k.includes('user_id'));
|
||||
const userObj = userKey ? (row[userKey] as any) : null;
|
||||
const userName = formatValue(userObj?.name || row.sales_officer || row.user_name || '—');
|
||||
const userEmail = formatValue(userObj?.email || row.performed_by_email || row.user_email || '—');
|
||||
const initials = userName !== '—' ? String(userName).substring(0, 2).toUpperCase() : 'SO';
|
||||
|
||||
// Extract date
|
||||
const dateKey = Object.keys(row).find(k => k.includes('date_of_order'));
|
||||
let dateVal = dateKey && row[dateKey] ? formatValue(row[dateKey]) : '—';
|
||||
|
||||
// Try to append time if it exists
|
||||
let timeVal = '—';
|
||||
const timeKey = Object.keys(row).find(k => k.includes('time_of_order'));
|
||||
if (timeKey && row[timeKey]) {
|
||||
dateVal += ` ${formatValue(row[timeKey])}`;
|
||||
timeVal = formatValue(row[timeKey]);
|
||||
}
|
||||
|
||||
let dateTimeStr = dateVal;
|
||||
if (timeVal !== '—') dateTimeStr += ` ${timeVal}`;
|
||||
|
||||
const stateName = String(row.current_state_name || '');
|
||||
const isProductive = stateName.toLowerCase().includes('productive') || stateName.toLowerCase().includes('closed') || stateName.toLowerCase().includes('ordered');
|
||||
const productiveLabel = stateName || 'Pending';
|
||||
@ -30,13 +43,20 @@ export function OrderCard({ row, fields }: { row: Record<string, any>; fields: a
|
||||
const gridField = fields.find(f => f.data_type === 'grid' || String(f.field_key).includes('order_details'));
|
||||
const gridValRaw = gridField ? row[gridField.field_key] : (row.order_details || row.order_details_2 || row.order_details_3);
|
||||
const gridVal = Array.isArray(gridValRaw) ? gridValRaw : [];
|
||||
const totalItems = gridVal.length;
|
||||
|
||||
// Total Kgs
|
||||
// Totals
|
||||
let totalKgsVal = Number(row.total_kgs || row.total_kgs_2 || row.total_kgs_3 || row.total_weight || row.weight || 0);
|
||||
if (totalKgsVal === 0 && gridVal.length > 0) {
|
||||
gridVal.forEach(p => { totalKgsVal += Number(p.row_kgs || p.kgs || p.weight || 0); })
|
||||
let totalBagsVal = Number(row.total_bags || row.total_bags_2 || row.total_bags_3 || row.total_quantity || row.quantity || 0);
|
||||
|
||||
if (totalKgsVal === 0 && totalBagsVal === 0 && gridVal.length > 0) {
|
||||
gridVal.forEach(p => {
|
||||
totalKgsVal += Number(p.row_kgs || p.kgs || p.weight || 0);
|
||||
totalBagsVal += Number(p.bags || p.total_bags || p.quantity || 0);
|
||||
});
|
||||
}
|
||||
const totalKgs = formatValue(totalKgsVal);
|
||||
const totalBags = formatValue(totalBagsVal);
|
||||
|
||||
return (
|
||||
<div className="z-card">
|
||||
@ -55,19 +75,19 @@ export function OrderCard({ row, fields }: { row: Record<string, any>; fields: a
|
||||
<h2 className="z-card-store-name">{storeVal}</h2>
|
||||
</div>
|
||||
|
||||
{!isDetailView && (
|
||||
<>
|
||||
<div className="z-card-divider"></div>
|
||||
|
||||
<div className="z-card-footer">
|
||||
<div className="z-card-footer-item">
|
||||
<div className="z-card-footer-label"><Calendar size={14} /> DATE & TIME</div>
|
||||
<span className="z-card-footer-value">{dateVal}</span>
|
||||
<span className="z-card-footer-value">{dateTimeStr}</span>
|
||||
</div>
|
||||
<div className="z-card-footer-item" style={{ textAlign: 'left', width: '96px' }}>
|
||||
<div className="z-card-footer-label"><Package size={14} /> TOTAL KGS</div>
|
||||
<span className="z-card-footer-value">{totalKgs} kg</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{gridVal.length > 0 && (
|
||||
<div className="z-card-details" style={{ display: 'block', borderTop: '1px solid #f1f5f9' }}>
|
||||
<p className="z-card-meta" style={{ marginBottom: '8px' }}>Order Products</p>
|
||||
@ -93,6 +113,104 @@ export function OrderCard({ row, fields }: { row: Record<string, any>; fields: a
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
{isDetailView && (
|
||||
<>
|
||||
{distName !== '—' && (
|
||||
<div className="z-card-store-row" style={{ marginBottom: '16px', marginTop: '12px' }}>
|
||||
<Truck size={18} className="z-card-store-icon" />
|
||||
<h2 className="z-card-store-name text-gray-700 text-sm">{distName}</h2>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Route & Assignment */}
|
||||
<div className="z-card-route">
|
||||
<div>
|
||||
<p className="z-card-grid-item-label">Route</p>
|
||||
<p className="z-card-route-val">{routeName}</p>
|
||||
</div>
|
||||
<div>
|
||||
<p className="z-card-grid-item-label">Area</p>
|
||||
<p className="z-card-route-val">{areaName}</p>
|
||||
</div>
|
||||
<div className="z-card-so-box">
|
||||
<p className="z-card-grid-item-label">Sales Officer</p>
|
||||
<div className="z-card-so-info">
|
||||
<div className="z-card-so-avatar">
|
||||
{initials}
|
||||
</div>
|
||||
<div className="z-card-so-details">
|
||||
<p className="z-card-so-name">{userName}</p>
|
||||
{userEmail !== '—' && (
|
||||
<p className="z-card-so-email">{userEmail}</p>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="z-card-route" style={{ marginTop: '1rem', paddingTop: '16px', gridTemplateColumns: 'repeat(2, 1fr)' }}>
|
||||
<div>
|
||||
<p className="z-card-grid-item-label flex items-center gap-1"><Calendar size={12} /> DATE</p>
|
||||
<p className="z-card-route-val">{dateVal}</p>
|
||||
</div>
|
||||
{timeVal !== '—' && (
|
||||
<div>
|
||||
<p className="z-card-grid-item-label flex items-center gap-1"><Clock size={12} /> TIME</p>
|
||||
<p className="z-card-route-val">{timeVal}</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{gridVal.length > 0 && (
|
||||
<div className="mt-6 border-t border-gray-100 pt-6">
|
||||
<div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center', marginBottom: '12px' }}>
|
||||
<p className="z-card-meta" style={{ margin: 0, color: '#334155', fontWeight: 700 }}>Order Products</p>
|
||||
<span className="text-xs font-bold text-gray-800 bg-gray-100 px-3 py-1 rounded-full border border-gray-200">
|
||||
{totalItems} {totalItems === 1 ? 'Item' : 'Items'}
|
||||
</span>
|
||||
</div>
|
||||
<div style={{ display: 'flex', flexDirection: 'column', gap: '8px' }}>
|
||||
{gridVal.map((item, i) => {
|
||||
const getVal = (key: string) => {
|
||||
if (item[key] !== undefined) return item[key];
|
||||
const keyPattern = new RegExp(`^${key}(_\\d+)?$`);
|
||||
const match = Object.keys(item).find(k => keyPattern.test(k) || k.includes(key));
|
||||
return match ? item[match] : undefined;
|
||||
};
|
||||
|
||||
const productName = formatValue(getVal('product_name') || getVal('product_category') || 'Unknown Product');
|
||||
const bags = formatValue(getVal('bags') || getVal('total_bags') || getVal('quantity') || 0);
|
||||
|
||||
return (
|
||||
<div key={i} style={{ display: 'flex', justifyContent: 'space-between', fontSize: '14px', background: '#f8fafc', padding: '12px 16px', borderRadius: '8px', border: '1px solid #e2e8f0' }}>
|
||||
<span style={{ fontWeight: 700, color: '#1e293b' }}>{productName}</span>
|
||||
<span style={{ fontWeight: 800, color: '#3730a3' }}>{bags} Bags</span>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Logistics Totals at the bottom */}
|
||||
{(Number(totalKgsVal) > 0 || Number(totalBagsVal) > 0) && (
|
||||
<div className="mt-4 flex justify-between items-center bg-gray-50 p-4 rounded-xl border border-gray-200 shadow-sm">
|
||||
<div className="flex flex-col">
|
||||
<span className="text-[11px] uppercase tracking-wider font-bold text-gray-700">Total Weight</span>
|
||||
<span className="text-xl font-black text-gray-900 mt-1">{totalKgs} <span className="text-sm font-bold text-gray-600">Kgs</span></span>
|
||||
</div>
|
||||
<div className="flex flex-col text-right">
|
||||
<span className="text-[11px] uppercase tracking-wider font-bold text-gray-700">Total Quantity</span>
|
||||
<span className="text-xl font-black text-gray-900 mt-1">{totalBags} <span className="text-sm font-bold text-gray-600">Bags</span></span>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,20 +1,27 @@
|
||||
import { orderBookingClient } from '../../api/clients';
|
||||
import { ORDER_BOOKING } from '../../api/config';
|
||||
import { DetailView } from './DetailView';
|
||||
import { useDetailViewData } from './useDetailViewData';
|
||||
import { OrderCard } from '../cards/OrderCard';
|
||||
|
||||
export interface WiredDetailViewProps {
|
||||
instanceId: string | number;
|
||||
columns?: 1 | 2 | 3;
|
||||
}
|
||||
|
||||
export function OrderDetail({ instanceId, columns = 2 }: WiredDetailViewProps) {
|
||||
export function OrderDetail({ instanceId }: WiredDetailViewProps) {
|
||||
const { data, config, loading, error } = useDetailViewData(
|
||||
orderBookingClient,
|
||||
ORDER_BOOKING.detailViews.ORDERS,
|
||||
instanceId
|
||||
);
|
||||
|
||||
if (loading) return <div className="p-4 text-gray-500">Loading Order Details...</div>;
|
||||
if (error) return <div className="p-4 text-red-500">{error}</div>;
|
||||
if (!data) return <div className="p-4 text-gray-500">No data found.</div>;
|
||||
|
||||
return (
|
||||
<DetailView
|
||||
client={orderBookingClient}
|
||||
dvUid={ORDER_BOOKING.detailViews.ORDERS}
|
||||
instanceId={instanceId}
|
||||
title="Order Details"
|
||||
columns={columns}
|
||||
/>
|
||||
<div className="pb-24">
|
||||
<OrderCard row={data} fields={config?.fields || []} isDetailView={true} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user