implemenation on the bot
This commit is contained in:
@@ -56,17 +56,39 @@ import StatCard from 'components/nearle_components/StatCard';
|
||||
import { PageShell, KpiGrid, FilterToolbar, DataCard } from 'components/nearle_components/PageLayout';
|
||||
import AddressAutocomplete from 'components/nearle_components/AddressAutocomplete';
|
||||
import { MobileCard, MobileCardList, MobileField, MobileFieldGrid } from 'components/nearle_components/MobileCard';
|
||||
import { getAdminCustomers, updateAdminCustomer } from 'pages/api/doormileApi';
|
||||
import { getTenantCustomers, updateTenantCustomer } from 'pages/api/doormileApi';
|
||||
import { useIsNarrowViewport } from 'hooks/useIsNarrowViewport';
|
||||
import { DT } from 'themes/dt/tokens';
|
||||
import { TableScroll, AccentAvatar } from 'themes/dt/primitives';
|
||||
|
||||
// ============================================================================
|
||||
// GET /admin/customers confirmed live: { appcustomerid, createdat, email,
|
||||
// name, phone, totalbookings } — no city/address/firstname field exists at
|
||||
// all. PATCH /admin/customers/:id is the only mutation the API exposes;
|
||||
// there is no create/delete here by design ("Tenant-scoped through their
|
||||
// bookings" — a customer only exists once they've ordered through a tenant).
|
||||
// This page reads GET /admin/tenantcustomers, NOT /admin/customers.
|
||||
//
|
||||
// Why: /admin/customers cannot be written to. `POST /admin/customers` returns
|
||||
// **405 Method Not Allowed** (confirmed live) — the route exists and POST is
|
||||
// not among its methods. On that resource a customer appears only as a side
|
||||
// effect of a booking ("creates a Guest customer if unknown"). So every create
|
||||
// in this console — this page's own dialog, the sidebar's Create Customer page,
|
||||
// and the assistant — writes to /admin/tenantcustomers, and reading
|
||||
// /admin/customers here meant none of them ever showed up.
|
||||
//
|
||||
// The two stores have separate id sequences: a customer created via the
|
||||
// assistant came back as id 2 while the /admin/customers list ran to id 44.
|
||||
// That is why the edit dialog below had to move to updateTenantCustomer as
|
||||
// well — left on updateAdminCustomer it would PATCH /admin/customers/:id with
|
||||
// a tenant-customer id, which is a 404 at best and edits a DIFFERENT PERSON at
|
||||
// worst.
|
||||
//
|
||||
// A real GET /admin/customers response also disproves the note that used to
|
||||
// live here claiming no address/firstname field exists — it carries firstname,
|
||||
// lastname, address, doorno, landmark, suburb, city, state, postcode, latitude
|
||||
// and longitude (all empty on every live record).
|
||||
//
|
||||
// The accessors below deliberately read BOTH shapes. /admin/customers returns a
|
||||
// composed `name` and `appcustomerid`; tenant customers use firstname/lastname
|
||||
// and a different id field, and Tenants.js is not reliable evidence either (it
|
||||
// writes `phone` but renders `contactno`). Falling back across both means a
|
||||
// field-name difference costs one column, not a table of blanks.
|
||||
//
|
||||
// The edit dialog replicates nearle_console_express's customer edit dialog
|
||||
// field-for-field (Name, Contact, Address, Location, City, State, Postcode,
|
||||
@@ -78,10 +100,11 @@ import { TableScroll, AccentAvatar } from 'themes/dt/primitives';
|
||||
|
||||
const BRAND = DT.brand;
|
||||
|
||||
const custId = (row) => row.appcustomerid;
|
||||
const custName = (row) => row.name || '—';
|
||||
const custPhone = (row) => row.phone || '—';
|
||||
const custId = (row) => row.appcustomerid ?? row.tenantcustomerid ?? row.customerid ?? row.id;
|
||||
const custName = (row) => row.name || [row.firstname, row.lastname].filter(Boolean).join(' ') || '—';
|
||||
const custPhone = (row) => row.phone || row.contactno || '—';
|
||||
const custEmail = (row) => row.email || '—';
|
||||
// Tenant customers carry no booking count; 0 is honest rather than blank.
|
||||
const custBookings = (row) => row.totalbookings ?? 0;
|
||||
|
||||
// TanStack Table v9 registers features explicitly — only sorting/resizing/
|
||||
@@ -122,7 +145,7 @@ const Customers = () => {
|
||||
const [pagination, setPagination] = useState({ pageIndex: 0, pageSize: 10 });
|
||||
const [columnSizing, setColumnSizing] = useState({});
|
||||
|
||||
const { data: customers = [], isLoading } = useQuery({ queryKey: ['admin-customers'], queryFn: getAdminCustomers });
|
||||
const { data: customers = [], isLoading } = useQuery({ queryKey: ['tenant-customers'], queryFn: getTenantCustomers });
|
||||
|
||||
const rows = useMemo(() => {
|
||||
if (!debouncedSearch) return customers;
|
||||
@@ -144,11 +167,24 @@ const Customers = () => {
|
||||
enqueueSnackbar(message, { variant, anchorOrigin: { vertical: 'top', horizontal: 'right' }, autoHideDuration: 2000 });
|
||||
|
||||
const saveMutation = useMutation({
|
||||
mutationFn: () => updateAdminCustomer(custId(editRow), form),
|
||||
mutationFn: () => {
|
||||
// Tenant customers store firstname/lastname, but this dialog edits one
|
||||
// "Customer Name" field. Split on the first space so a round-trip keeps
|
||||
// both parts instead of blanking the surname.
|
||||
const [firstname, ...rest] = String(form.name || '')
|
||||
.trim()
|
||||
.split(/\s+/);
|
||||
return updateTenantCustomer(custId(editRow), {
|
||||
firstname: firstname || '',
|
||||
lastname: rest.join(' '),
|
||||
phone: form.phone || '',
|
||||
email: form.email || ''
|
||||
});
|
||||
},
|
||||
onSuccess: (res) => {
|
||||
if (res.success) {
|
||||
opentoast('Customer updated', 'success');
|
||||
queryClient.invalidateQueries({ queryKey: ['admin-customers'] });
|
||||
queryClient.invalidateQueries({ queryKey: ['tenant-customers'] });
|
||||
setEditRow(null);
|
||||
} else {
|
||||
opentoast(res.message || 'Failed to update customer');
|
||||
@@ -159,10 +195,11 @@ const Customers = () => {
|
||||
|
||||
const openEdit = (row) => {
|
||||
setEditRow(row);
|
||||
// Seeded through the accessors so the dialog works on either record shape.
|
||||
setForm({
|
||||
name: row.name || '',
|
||||
phone: row.phone || '',
|
||||
email: row.email || ''
|
||||
name: custName(row) === '—' ? '' : custName(row),
|
||||
phone: custPhone(row) === '—' ? '' : custPhone(row),
|
||||
email: custEmail(row) === '—' ? '' : custEmail(row)
|
||||
});
|
||||
setAddressInput(row.address || '');
|
||||
setPickAddress({
|
||||
|
||||
Reference in New Issue
Block a user