updates on the customer app and map stuck issue

This commit is contained in:
2026-09-25 15:10:17 +05:30
parent d875900cb0
commit 3e7aaf1e25
5 changed files with 215 additions and 46 deletions

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useState } from 'react';
import React, { useEffect, useMemo, useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { Mail, Package, Pencil, Phone, Plus, Users } from 'lucide-react';
import {
@@ -22,22 +22,38 @@ const custId = (row) => row.appcustomerid ?? row.tenantcustomerid ?? row.custome
const custName = (row) => row.name || [row.firstname, row.lastname].filter(Boolean).join(' ') || '—';
const custPhone = (row) => row.phone || row.contactno || '';
const custEmail = (row) => row.email || '';
/** Address accessor for search and preview */
const custAddress = (row) => [row.address, row.deliveryaddress, row.suburb, row.city, row.postcode].filter(Boolean).join(', ');
/** Tenant customers carry no booking count; 0 is honest rather than blank. */
const custBookings = (row) => row.totalbookings ?? 0;
const readSavedContacts = () => {
try {
const data = localStorage.getItem('doormile_saved_contacts');
return data ? JSON.parse(data) : [];
} catch {
return [];
}
};
export default function Customers({ embedded = false }) {
const navigate = useNavigate();
const { data: customers = [], isLoading, isFetching } = useTenantCustomers();
const updateCustomer = useUpdateTenantCustomer();
const [localSavedContacts] = useState(() => {
try {
const data = localStorage.getItem('doormile_saved_contacts');
return data ? JSON.parse(data) : [];
} catch {
return [];
}
});
const [localSavedContacts, setLocalSavedContacts] = useState(readSavedContacts);
useEffect(() => {
const handleUpdate = () => setLocalSavedContacts(readSavedContacts());
window.addEventListener('doormile_saved_contacts_updated', handleUpdate);
window.addEventListener('storage', handleUpdate);
window.addEventListener('focus', handleUpdate);
return () => {
window.removeEventListener('doormile_saved_contacts_updated', handleUpdate);
window.removeEventListener('storage', handleUpdate);
window.removeEventListener('focus', handleUpdate);
};
}, []);
const mergedCustomers = useMemo(() => {
const list = [...(customers || [])];
@@ -86,7 +102,7 @@ export default function Customers({ embedded = false }) {
let list = mergedCustomers;
if (activeFilter === 'withEmail') list = list.filter((row) => Boolean(row.email));
else if (activeFilter === 'withPhone') list = list.filter((row) => Boolean(row.phone));
return list.filter((row) => matchesQuery(row, [custName, custPhone, custEmail], debouncedSearch));
return list.filter((row) => matchesQuery(row, [custName, custPhone, custEmail, custAddress], debouncedSearch));
}, [mergedCustomers, activeFilter, debouncedSearch]);
const toggleFilter = (key) => setActiveFilter((current) => (current === key ? 'all' : key));
@@ -97,13 +113,39 @@ export default function Customers({ embedded = false }) {
};
const save = () => {
const id = custId(editRow);
// If it's a locally saved contact, update localStorage directly
if (String(id).startsWith('saved_')) {
const cleanPhone = String(form.phone || '').replace(/\D/g, '').slice(-10);
try {
const stored = JSON.parse(localStorage.getItem('doormile_saved_contacts') || '[]');
const updated = stored.map((item) => {
if (item.id === id || String(item.phone || item.contactno).replace(/\D/g, '').slice(-10) === cleanPhone) {
return {
...item,
name: form.name,
firstname: form.name,
phone: form.phone,
contactno: form.phone,
email: form.email
};
}
return item;
});
localStorage.setItem('doormile_saved_contacts', JSON.stringify(updated));
window.dispatchEvent(new Event('doormile_saved_contacts_updated'));
} catch {}
setEditRow(null);
return;
}
/* The record stores firstname/lastname but the dialog edits one name field.
Splitting on the first space keeps the surname through a round trip
instead of blanking it. */
const [firstname, ...rest] = String(form.name || '').trim().split(/\s+/);
updateCustomer.mutate(
{
id: custId(editRow),
id,
data: {
firstname: firstname || '',
lastname: rest.join(' '),
@@ -122,15 +164,28 @@ export default function Customers({ embedded = false }) {
header: 'Customer',
sortable: true,
accessor: custName,
cell: (row) => (
<div className="flex items-center gap-2.5 min-w-0">
<Avatar name={custName(row)} size="sm" />
<div className="min-w-0">
<p className="truncate text-body-sm font-semibold text-ink-1">{custName(row)}</p>
<p className="text-caption text-ink-3">ID #{custId(row) ?? '—'}</p>
cell: (row) => {
const addr = custAddress(row);
const isSaved = row.isSavedForLater || String(custId(row)).startsWith('saved_');
return (
<div className="flex items-center gap-2.5 min-w-0">
<Avatar name={custName(row)} size="sm" />
<div className="min-w-0">
<div className="flex items-center gap-1.5">
<p className="truncate text-body-sm font-semibold text-ink-1">{custName(row)}</p>
{isSaved && (
<span className="text-[10px] bg-emerald-50 text-emerald-700 border border-emerald-200 px-1.5 py-0.5 rounded font-semibold shrink-0">
Saved
</span>
)}
</div>
<p className="text-caption text-ink-3 truncate max-w-xs" title={addr || `ID #${custId(row) ?? '—'}`}>
{addr ? `📍 ${addr}` : `ID #${custId(row) ?? '—'}`}
</p>
</div>
</div>
</div>
),
);
},
},
{
key: 'contact',
@@ -219,7 +274,7 @@ export default function Customers({ embedded = false }) {
search={search}
onSearchChange={setSearch}
placeholder="Search by name, phone or email"
count={{ shown: rows.length, total: customers.length, noun: 'customers' }}
count={{ shown: rows.length, total: mergedCustomers.length, noun: 'customers' }}
actions={
<Button onClick={() => navigate('/doormile/customer/create')}>
<Plus className="mr-1.5 h-4 w-4" /> New customer