updates on the api integration and the design changes on the whole website
This commit is contained in:
@@ -79,7 +79,7 @@ import DebounceSearchBar from 'components/nearle_components/DebounceSearchBar';
|
||||
import PageHeader from 'components/nearle_components/PageHeader';
|
||||
import StatCard from 'components/nearle_components/StatCard';
|
||||
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query';
|
||||
import { getalltenants, gettenantsummary } from 'pages/api/api';
|
||||
import { getalltenants } from 'pages/api/api';
|
||||
import {
|
||||
getAdminPricing,
|
||||
getDoormilePricing,
|
||||
@@ -440,7 +440,11 @@ const TenantCustomersDialog = ({ tenant, open, onClose }) => {
|
||||
id: tenantCustId(row),
|
||||
firstname: row.firstname || '',
|
||||
lastname: row.lastname || '',
|
||||
phone: row.phone || '',
|
||||
// GET/POST /admin/tenantcustomers has a write/read asymmetry confirmed
|
||||
// live: POST accepts `phone`, but the stored/returned field is
|
||||
// `contactno`. The form field itself stays named `phone` (matching
|
||||
// what create/update accept) — only the read source differs.
|
||||
phone: row.contactno || '',
|
||||
email: row.email || ''
|
||||
});
|
||||
setFormOpen(true);
|
||||
@@ -470,7 +474,7 @@ const TenantCustomersDialog = ({ tenant, open, onClose }) => {
|
||||
{c.firstname} {c.lastname}
|
||||
</Typography>
|
||||
<Typography variant="caption" sx={{ color: DT.textSecondary }}>
|
||||
{[c.phone, c.email].filter(Boolean).join(' · ')}
|
||||
{[c.contactno, c.email].filter(Boolean).join(' · ')}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Stack direction="row" spacing={0.5}>
|
||||
@@ -580,11 +584,9 @@ const Clients1 = () => {
|
||||
setPage(0);
|
||||
};
|
||||
const handleChange1 = (event, newValue1) => {
|
||||
console.log('newValue1', newValue1);
|
||||
setValue1(newValue1);
|
||||
};
|
||||
const handleChange2 = (event, newValue2) => {
|
||||
console.log('newValue2', newValue2);
|
||||
setValue2(newValue2);
|
||||
};
|
||||
|
||||
@@ -600,9 +602,6 @@ const Clients1 = () => {
|
||||
const handleCollapseToggle2 = (rowIndex) => {
|
||||
setOpenRowIndex2((prevIndex) => (prevIndex === rowIndex ? null : rowIndex));
|
||||
};
|
||||
useEffect(() => {
|
||||
console.log('selectedCustomer', selectedCustomer);
|
||||
}, [selectedCustomer]);
|
||||
useEffect(() => {
|
||||
setOpenRowIndex1(-1);
|
||||
setOpenRowIndex2(-1);
|
||||
@@ -700,15 +699,38 @@ const Clients1 = () => {
|
||||
queryFn: getalltenants
|
||||
});
|
||||
|
||||
// ==============================|| gettenantsummary ||============================== //
|
||||
const {
|
||||
data: summaryData = [],
|
||||
isLoading: summaryDataIsLoading,
|
||||
refetch: summaryDataRefetch
|
||||
} = useQuery({
|
||||
queryKey: ['gettenantsummary', appId, status],
|
||||
queryFn: gettenantsummary
|
||||
});
|
||||
// No tenant-tab-counts endpoint in the new API (gettenantsummary always
|
||||
// returns null) — derive the Active/Pending/Inactive counts client-side
|
||||
// from the full tenant list instead, which getalltenants already fetches.
|
||||
const tenantStatusCounts = React.useMemo(() => {
|
||||
const counts = { active: 0, pending: 0, inactive: 0 };
|
||||
(tenantList || []).forEach((t) => {
|
||||
const s = String(t.status || '').toLowerCase();
|
||||
if (s === 'active') counts.active += 1;
|
||||
else if (s === 'pending') counts.pending += 1;
|
||||
else if (s === 'inactive') counts.inactive += 1;
|
||||
});
|
||||
return counts;
|
||||
}, [tenantList]);
|
||||
|
||||
// getalltenants() takes no filter args (see api.js) — the status tabs and
|
||||
// search box only ever changed the query-cache key, never the actual
|
||||
// request, so every tab showed the exact same unfiltered list with just a
|
||||
// relabeled status badge. Filter the already-fetched list client-side
|
||||
// instead, matching the same real `status` field tenantStatusCounts uses.
|
||||
const filteredTenantList = React.useMemo(() => {
|
||||
const activeStatus = STATUS_TABS[value0]?.status;
|
||||
const q = debouncedSearch.trim().toLowerCase();
|
||||
return (tenantList || []).filter((t) => {
|
||||
if (String(t.status || '').toLowerCase() !== activeStatus) return false;
|
||||
if (!q) return true;
|
||||
return (
|
||||
t.tenantname?.toLowerCase().includes(q) ||
|
||||
t.primaryemail?.toLowerCase().includes(q) ||
|
||||
t.primarycontact?.includes(debouncedSearch)
|
||||
);
|
||||
});
|
||||
}, [tenantList, value0, debouncedSearch]);
|
||||
|
||||
/* ============================================= || fetchclientpricelist || ============================================= */
|
||||
|
||||
@@ -718,7 +740,7 @@ const Clients1 = () => {
|
||||
const pricing = await getAdminPricing();
|
||||
setClientpricelist((pricing || []).filter((p) => p.tenantid === selectedTenid));
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
// swallow
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
@@ -731,7 +753,7 @@ const Clients1 = () => {
|
||||
const pricing = await getAdminPricing();
|
||||
setTenanatPricing((pricing || []).filter((p) => p.tenantid === id));
|
||||
} catch (error) {
|
||||
console.log('fetchTenanatPricing', error);
|
||||
// swallow
|
||||
}
|
||||
};
|
||||
useEffect(() => {
|
||||
@@ -760,10 +782,8 @@ const Clients1 = () => {
|
||||
2000
|
||||
);
|
||||
getalltenantsRefetch();
|
||||
summaryDataRefetch();
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
opentoast(err.message, 'error', 1500);
|
||||
} finally {
|
||||
setisloader(false);
|
||||
@@ -776,7 +796,7 @@ const Clients1 = () => {
|
||||
const pricing = await getDoormilePricing();
|
||||
setAppPricing((pricing || []).filter((p) => !id || p.applocationid === id));
|
||||
} catch (error) {
|
||||
console.log('appPricingRes', error);
|
||||
// swallow
|
||||
}
|
||||
};
|
||||
/* ============================================= || createpricing || ============================================= */
|
||||
@@ -793,10 +813,14 @@ const Clients1 = () => {
|
||||
priceperkg: +selectedPricing.priceperkg || 0,
|
||||
basedistance: +selectedPricing.basedistance || 0,
|
||||
handlingcharges: +selectedPricing.handlingcharges || 0,
|
||||
effectivefrom: dayjs().format('YYYY-MM-DD'),
|
||||
// Confirmed live: POST /admin/pricing rejects a bare YYYY-MM-DD date
|
||||
// for effectivefrom ("invalid request body") — needs a full ISO
|
||||
// datetime. Status also confirmed to need title case ("Active"),
|
||||
// matching every real pricing row.
|
||||
effectivefrom: dayjs().toISOString(),
|
||||
currency: 'INR',
|
||||
priority: 1,
|
||||
status: 'active'
|
||||
status: 'Active'
|
||||
});
|
||||
if (res.success) {
|
||||
enqueueSnackbar('Price Created Successfully', {
|
||||
@@ -812,7 +836,6 @@ const Clients1 = () => {
|
||||
opentoast(res.message || 'Failed to save pricing', 'error', 2000);
|
||||
}
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
opentoast(err.response?.data?.message || err.message || 'Failed to save pricing', 'error', 2000);
|
||||
}
|
||||
};
|
||||
@@ -826,9 +849,7 @@ const Clients1 = () => {
|
||||
opentoast(updateRes.message || 'Update Failed', 'error', 1500);
|
||||
}
|
||||
getalltenantsRefetch();
|
||||
summaryDataRefetch();
|
||||
} catch (err) {
|
||||
console.log('updateClient', err);
|
||||
opentoast(err.response?.data?.message || err.message || 'Update Failed', 'error', 1500);
|
||||
}
|
||||
};
|
||||
@@ -837,7 +858,7 @@ const Clients1 = () => {
|
||||
|
||||
return (
|
||||
<>
|
||||
{(getalltenantsIsLoading || summaryDataIsLoading || isloader) && <Loader />}
|
||||
{(getalltenantsIsLoading || isloader) && <Loader />}
|
||||
{/* ============================================= || Header | ============================================= */}
|
||||
<PageHeader
|
||||
title="Tenants"
|
||||
@@ -864,7 +885,7 @@ const Clients1 = () => {
|
||||
<Grid container spacing={{ xs: 2, md: 2.5 }} sx={{ mb: { xs: 1.5, md: 2 } }}>
|
||||
{KPI_META.map((item, idx) => {
|
||||
const Icon = item.icon;
|
||||
const value = summaryData?.[item.countKey] ?? 0;
|
||||
const value = tenantStatusCounts[item.countKey] ?? 0;
|
||||
return (
|
||||
<Grid item key={item.key} xs={12} sm={4}>
|
||||
<Box
|
||||
@@ -878,7 +899,7 @@ const Clients1 = () => {
|
||||
value={value ?? 0}
|
||||
icon={<Icon size={20} />}
|
||||
color={idx === 0 ? '#662582' : item.color}
|
||||
loading={summaryDataIsLoading}
|
||||
loading={getalltenantsIsLoading}
|
||||
/>
|
||||
</Box>
|
||||
</Grid>
|
||||
@@ -925,7 +946,7 @@ const Clients1 = () => {
|
||||
const meta = STATUS_META[t.status];
|
||||
const Icon = meta.icon;
|
||||
const active = value0 === idx;
|
||||
const count = summaryData?.[t.countKey] ?? 0;
|
||||
const count = tenantStatusCounts[t.countKey] ?? 0;
|
||||
return (
|
||||
<Box
|
||||
key={t.status}
|
||||
@@ -987,7 +1008,7 @@ const Clients1 = () => {
|
||||
border: 'none'
|
||||
}}
|
||||
>
|
||||
{summaryDataIsLoading ? <Skeleton variant="text" width={14} height={10} /> : count}
|
||||
{getalltenantsIsLoading ? <Skeleton variant="text" width={14} height={10} /> : count}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
@@ -1064,14 +1085,14 @@ const Clients1 = () => {
|
||||
<TableCell>Status</TableCell>
|
||||
<TableCell>Client</TableCell>
|
||||
<TableCell>Contact</TableCell>
|
||||
<TableCell>Address</TableCell>
|
||||
<TableCell>Delivery OTP</TableCell>
|
||||
<TableCell align="right">Actions</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>
|
||||
{getalltenantsIsLoading && <OrdersTableSkeleton />}
|
||||
|
||||
{tenantList?.length == 0 && !isloader ? (
|
||||
{filteredTenantList.length == 0 && !isloader ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} sx={{ py: 6 }}>
|
||||
<Stack alignItems="center" spacing={1.5}>
|
||||
@@ -1088,9 +1109,9 @@ const Clients1 = () => {
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
tenantList?.map((row, index) => {
|
||||
const rowStatusKey = (value0 === 0 ? 'active' : value0 === 1 ? 'pending' : 'inactive');
|
||||
const rowStatusMeta = STATUS_META[rowStatusKey];
|
||||
filteredTenantList.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row, index) => {
|
||||
const rowStatusKey = String(row.status || '').toLowerCase();
|
||||
const rowStatusMeta = STATUS_META[rowStatusKey] || STATUS_META.active;
|
||||
const RowStatusIcon = rowStatusMeta.icon;
|
||||
return (
|
||||
<Fragment key={row.tenantid ?? index}>
|
||||
@@ -1162,20 +1183,9 @@ const Clients1 = () => {
|
||||
</Stack>
|
||||
</TableCell>
|
||||
<TableCell sx={{ maxWidth: 280 }}>
|
||||
<Tooltip title={row.address || ''} placement="top">
|
||||
<Typography
|
||||
variant="caption"
|
||||
sx={{
|
||||
color: DT.textSecondary,
|
||||
display: '-webkit-box',
|
||||
WebkitLineClamp: 2,
|
||||
WebkitBoxOrient: 'vertical',
|
||||
overflow: 'hidden'
|
||||
}}
|
||||
>
|
||||
{row.address || '—'}
|
||||
</Typography>
|
||||
</Tooltip>
|
||||
<Typography variant="caption" sx={{ color: DT.textSecondary }}>
|
||||
{row.requiredeliveryotp ? 'Yes' : 'No'}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
<TableCell align="right">
|
||||
<Stack direction="row" spacing={0.75} justifyContent="flex-end">
|
||||
@@ -1217,7 +1227,7 @@ const Clients1 = () => {
|
||||
setDialogopen(true);
|
||||
setSelectedtenid(row.tenantid);
|
||||
setAppId(row.applocationid);
|
||||
getAppPricing(row.applolcationid);
|
||||
getAppPricing(row.applocationid);
|
||||
}}
|
||||
>
|
||||
<IssuesCloseOutlined style={{ fontSize: 14 }} />
|
||||
@@ -1407,7 +1417,7 @@ const Clients1 = () => {
|
||||
<Grid item xs={6}>
|
||||
<Grid container spacing={4} alignItems="center">
|
||||
<Grid item>
|
||||
<FiUser
|
||||
<FaRegCheckCircle
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'lighter'
|
||||
@@ -1415,9 +1425,9 @@ const Clients1 = () => {
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Typography align="left">{row.firstname}</Typography>
|
||||
<Typography align="left">{row.status || '—'}</Typography>
|
||||
<Typography align="left" variant="caption" color="secondary">
|
||||
Contact Person
|
||||
Status
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -1461,121 +1471,26 @@ const Clients1 = () => {
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={4} alignItems="center">
|
||||
<Grid item>
|
||||
<FaRegAddressCard
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'lighter'
|
||||
}}
|
||||
/>
|
||||
{row.requiredeliveryotp ? (
|
||||
<MdCheckCircle
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'lighter'
|
||||
}}
|
||||
/>
|
||||
) : (
|
||||
<MdCancel
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'lighter'
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
</Grid>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Typography align="left">{row.address}</Typography>
|
||||
<Typography align="left">{row.requiredeliveryotp ? 'Yes' : 'No'}</Typography>
|
||||
<Typography align="left" variant="caption" color="secondary">
|
||||
Address
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
{/* <Grid item xs={12}>
|
||||
<Grid container spacing={4} alignItems="center">
|
||||
<Grid item>
|
||||
<TbCategoryMinus
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'lighter'
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Typography align="left">{row.subcategoryname}</Typography>
|
||||
<Typography align="left" variant="caption" color="secondary">
|
||||
Category
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<MdMyLocation
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'lighter'
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Typography align="left" variant="subtitle1">
|
||||
{row.suburb}
|
||||
</Typography>
|
||||
<Typography align="left" variant="caption" color="secondary">
|
||||
Location
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid> */}
|
||||
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={4} alignItems="center">
|
||||
<Grid item>
|
||||
<GiModernCity
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'lighter'
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Typography align="left">{row.city}</Typography>
|
||||
<Typography align="left" variant="caption" color="secondary">
|
||||
City
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<MdNumbers
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'lighter'
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Typography align="left" variant="subtitle1">
|
||||
{row.postcode}
|
||||
</Typography>
|
||||
<Typography align="left" variant="caption" color="secondary">
|
||||
PostCode
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<Grid container spacing={4} alignItems="center">
|
||||
<Grid item>
|
||||
<TbWorldLatitude
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'lighter'
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Typography align="left">{row.latitude}</Typography>
|
||||
<Typography align="left" variant="caption" color="secondary">
|
||||
Latitude
|
||||
</Typography>
|
||||
</Grid>
|
||||
<Grid item>
|
||||
<TbWorldLongitude
|
||||
style={{
|
||||
fontSize: '20px',
|
||||
fontWeight: 'lighter'
|
||||
}}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs zeroMinWidth>
|
||||
<Typography align="left" variant="subtitle1">
|
||||
{row.longitude}
|
||||
</Typography>
|
||||
<Typography align="left" variant="caption" color="secondary">
|
||||
Longitude
|
||||
Delivery OTP Required
|
||||
</Typography>
|
||||
</Grid>
|
||||
</Grid>
|
||||
@@ -1681,7 +1596,7 @@ const Clients1 = () => {
|
||||
flexDirection: 'row'
|
||||
}}
|
||||
onClick={() => {
|
||||
getAppPricing(selectedCustomer.applolcationid);
|
||||
getAppPricing(selectedCustomer.applocationid);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
@@ -2105,7 +2020,7 @@ const Clients1 = () => {
|
||||
</Stack>
|
||||
)}
|
||||
|
||||
{tenantList?.length == 0 && !isloader ? (
|
||||
{filteredTenantList.length == 0 && !isloader ? (
|
||||
<Stack alignItems="center" spacing={1.5} sx={{ py: 6 }}>
|
||||
<Avatar sx={{ width: 64, height: 64, bgcolor: soft('#94a3b8'), color: DT.textMuted }}>
|
||||
<MdGroups size={28} />
|
||||
@@ -2118,9 +2033,9 @@ const Clients1 = () => {
|
||||
</Typography>
|
||||
</Stack>
|
||||
) : (
|
||||
tenantList?.map((row, index) => {
|
||||
const rowStatusKey = value0 === 0 ? 'active' : value0 === 1 ? 'pending' : 'inactive';
|
||||
const rowStatusMeta = STATUS_META[rowStatusKey];
|
||||
filteredTenantList.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row, index) => {
|
||||
const rowStatusKey = String(row.status || '').toLowerCase();
|
||||
const rowStatusMeta = STATUS_META[rowStatusKey] || STATUS_META.active;
|
||||
const RowStatusIcon = rowStatusMeta.icon;
|
||||
const expanded = openRowIndex1 === index;
|
||||
return (
|
||||
@@ -2199,7 +2114,7 @@ const Clients1 = () => {
|
||||
setDialogopen(true);
|
||||
setSelectedtenid(row.tenantid);
|
||||
setAppId(row.applocationid);
|
||||
getAppPricing(row.applolcationid);
|
||||
getAppPricing(row.applocationid);
|
||||
}}
|
||||
>
|
||||
<IssuesCloseOutlined style={{ fontSize: 14 }} />
|
||||
@@ -2280,7 +2195,7 @@ const Clients1 = () => {
|
||||
setSelectedCustomer(row);
|
||||
setSelectedtenid(row.tenantid);
|
||||
setAppId(row.applocationid);
|
||||
getAppPricing(row.applolcationid);
|
||||
getAppPricing(row.applocationid);
|
||||
setDialogopen(true);
|
||||
}}
|
||||
>
|
||||
@@ -2295,21 +2210,13 @@ const Clients1 = () => {
|
||||
<MobileField label="Tenant ID" value={`#${row.tenantid}`} />
|
||||
<MobileField label="Contact" value={row.primarycontact || '—'} />
|
||||
<MobileField label="Email" value={row.primaryemail || '—'} full />
|
||||
<MobileField label="Address" full>
|
||||
<Typography sx={{ fontSize: 13, fontWeight: 600, color: DT.textPrimary }}>
|
||||
{row.address || '—'}
|
||||
</Typography>
|
||||
</MobileField>
|
||||
<MobileField label="Status" value={row.status || '—'} />
|
||||
</MobileFieldGrid>
|
||||
|
||||
{expanded && (
|
||||
<Box sx={{ mt: 1.25, pt: 1.25, borderTop: `1px solid ${DT.divider}` }}>
|
||||
<MobileFieldGrid>
|
||||
<MobileField label="Contact Person" value={row.firstname || '—'} />
|
||||
<MobileField label="City" value={row.city || '—'} />
|
||||
<MobileField label="Postcode" value={row.postcode || '—'} />
|
||||
<MobileField label="Latitude" value={row.latitude || '—'} />
|
||||
<MobileField label="Longitude" value={row.longitude || '—'} />
|
||||
<MobileField label="Delivery OTP Required" value={row.requiredeliveryotp ? 'Yes' : 'No'} />
|
||||
</MobileFieldGrid>
|
||||
</Box>
|
||||
)}
|
||||
@@ -2321,13 +2228,13 @@ const Clients1 = () => {
|
||||
)}
|
||||
|
||||
{/* ============================================= || Pagination| ============================================= */}
|
||||
{!searchword && tenantList?.length > 0 && (
|
||||
{!searchword && filteredTenantList.length > 0 && (
|
||||
<>
|
||||
<Divider sx={{ borderColor: DT.borderSubtle }} />
|
||||
<TablePagination
|
||||
rowsPerPageOptions={[5, 10, 25, 50, 100]}
|
||||
component="div"
|
||||
count={value0 == 0 ? summaryData?.active : value0 == 1 ? summaryData?.pending : value0 == 2 ? summaryData?.inactive : 0}
|
||||
count={filteredTenantList.length ?? 0}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
|
||||
Reference in New Issue
Block a user