updatess on the ui changs
This commit is contained in:
@@ -24,18 +24,45 @@ import {
|
||||
MdEdit,
|
||||
MdDeleteOutline,
|
||||
MdPeopleAlt,
|
||||
MdVisibility,
|
||||
MdVisibilityOff,
|
||||
MdBlock,
|
||||
MdClose
|
||||
MdArrowUpward,
|
||||
MdArrowDownward,
|
||||
MdUnfoldMore,
|
||||
// Outline-only set for the Actions column — kept separate from the filled
|
||||
// icons above (MdPlace/MdEdit/MdPeopleAlt), which stay filled for the
|
||||
// Locations/Customers/Pricing dialogs elsewhere in this file. Mixing
|
||||
// filled and outlined glyphs in one row is what read as inconsistent.
|
||||
MdOutlineBlock,
|
||||
MdOutlineVisibility,
|
||||
MdOutlineVisibilityOff,
|
||||
MdOutlineEdit,
|
||||
MdOutlineClose,
|
||||
MdOutlineMoreVert,
|
||||
MdOutlinePlace,
|
||||
MdOutlinePeopleAlt
|
||||
} from 'react-icons/md';
|
||||
import { LuMail } from 'react-icons/lu';
|
||||
import { BiUser } from 'react-icons/bi';
|
||||
|
||||
// TanStack Table v9 — same engine orders.js/riders.js use, so sorting/
|
||||
// resizing/pagination behave identically across the console's list pages.
|
||||
import {
|
||||
useTable,
|
||||
flexRender,
|
||||
tableFeatures,
|
||||
rowSortingFeature,
|
||||
createSortedRowModel,
|
||||
sortFns,
|
||||
columnSizingFeature,
|
||||
columnResizingFeature,
|
||||
rowPaginationFeature,
|
||||
createPaginatedRowModel
|
||||
} from '@tanstack/react-table';
|
||||
|
||||
import { Grid } from '@astryxdesign/core/Grid';
|
||||
import { HStack } from '@astryxdesign/core/HStack';
|
||||
import { VStack } from '@astryxdesign/core/VStack';
|
||||
import { Card } from '@astryxdesign/core/Card';
|
||||
import { Center } from '@astryxdesign/core/Center';
|
||||
import { Text } from '@astryxdesign/core/Text';
|
||||
import { Button } from '@astryxdesign/core/Button';
|
||||
import { IconButton } from '@astryxdesign/core/IconButton';
|
||||
@@ -46,6 +73,7 @@ import { NumberInput } from '@astryxdesign/core/NumberInput';
|
||||
import { Selector } from '@astryxdesign/core/Selector';
|
||||
import { TabList, Tab } from '@astryxdesign/core/TabList';
|
||||
import { Pagination } from '@astryxdesign/core/Pagination';
|
||||
import { DropdownMenu } from '@astryxdesign/core/DropdownMenu';
|
||||
import { Dialog, DialogHeader } from '@astryxdesign/core/Dialog';
|
||||
import { Layout, LayoutContent, LayoutFooter } from '@astryxdesign/core/Layout';
|
||||
import { EmptyState } from '@astryxdesign/core/EmptyState';
|
||||
@@ -103,6 +131,27 @@ const KPI_META = [
|
||||
|
||||
const VEHICLE_TYPE_OPTIONS = ['Bike', 'Scooter', 'Bicycle', 'Car', 'Van'].map((v) => ({ value: v, label: v }));
|
||||
|
||||
// TanStack Table v9 registers features explicitly — only sorting/resizing/
|
||||
// pagination ship here (no row-selection or column-visibility, since this
|
||||
// table has no bulk-select or column-hide flow).
|
||||
const TENANTS_TABLE_FEATURES = tableFeatures({
|
||||
rowSortingFeature,
|
||||
sortedRowModel: createSortedRowModel(),
|
||||
sortFns,
|
||||
columnSizingFeature,
|
||||
columnResizingFeature,
|
||||
rowPaginationFeature,
|
||||
paginatedRowModel: createPaginatedRowModel()
|
||||
});
|
||||
|
||||
// Uppercase micro-label for the table's column headings — same convention
|
||||
// orders.js's and riders.js's tables use.
|
||||
const SectionLabel = ({ children }) => (
|
||||
<Text type="label" weight="bold" color="secondary" style={{ textTransform: 'uppercase', letterSpacing: 0.4 }}>
|
||||
{children}
|
||||
</Text>
|
||||
);
|
||||
|
||||
// ==============================|| Tenant locations (branches/sites) dialog ||============================== //
|
||||
// Self-contained — deliberately does not touch the existing openRowIndex1/2 +
|
||||
// Tabs collapse machinery above, which is fragile legacy state. GET/POST/PUT
|
||||
@@ -547,6 +596,14 @@ const Clients1 = () => {
|
||||
const [latlong, setLatlong] = useState({});
|
||||
const [editClient, setEditClient] = useState({});
|
||||
const [page, setPage] = useState(0);
|
||||
// Table sorting/resizing state — mirrors orders.js's/riders.js's slices.
|
||||
// Pagination itself stays on the existing page/rowsPerPage state (below)
|
||||
// rather than a new `pagination` object, since page/rowsPerPage already
|
||||
// feed the tenantList query key and several onClick handlers — bridged
|
||||
// into TanStack's { pageIndex, pageSize } shape via paginationState/
|
||||
// handlePaginationChange further down.
|
||||
const [sorting, setSorting] = useState([]);
|
||||
const [columnSizing, setColumnSizing] = useState({});
|
||||
|
||||
const dialogclose = () => {
|
||||
setDialogopen(false);
|
||||
@@ -822,15 +879,43 @@ const Clients1 = () => {
|
||||
|
||||
const activeTabMeta = STATUS_META[STATUS_TABS[value0]?.status] || STATUS_META.active;
|
||||
|
||||
const renderRowActions = (row, index, expanded, size = 'sm') => (
|
||||
<HStack gap={0.75} justify="end">
|
||||
// Icon buttons only, kept to 3 max + one overflow menu: status action,
|
||||
// view details, edit, then "More" (Locations/Customers — secondary,
|
||||
// low-frequency actions) behind a kebab menu.
|
||||
//
|
||||
// Sizing: `size="md"` is Astryx's own 32px button height (confirmed in
|
||||
// Button's sizeStyles — sm=28px/md=32px/lg=36px) and icon-only buttons are
|
||||
// always square, so this is a native 32×32px button, not a manually forced
|
||||
// one. Its border-radius is Astryx's own default --radius-element (8px —
|
||||
// see the identical note in dispatch/Dispatch.css), so no radius override
|
||||
// is needed either — both requirements were already the design system's
|
||||
// own defaults once the size prop was corrected.
|
||||
//
|
||||
// Icon family: every icon here is the Material "outline" variant
|
||||
// (MdOutline*). Mixing MdBlock/MdVisibility/MdEdit/MdClose/MdMoreVert
|
||||
// (filled) with MdOutlineCheckCircle (outline) — the state before this
|
||||
// pass — is what actually read as inconsistent, not the icons' size or
|
||||
// position. MdPlace/MdEdit/MdPeopleAlt stay filled where they're used
|
||||
// elsewhere in this file (Locations/Customers/Pricing dialogs) — only this
|
||||
// row switches to the Outline set.
|
||||
//
|
||||
// Colour: the status-action slot (Inactivate/Approve/Activate — whichever
|
||||
// one applies to the active tab) is the one action with real consequence
|
||||
// for the tenant's account state, so it keeps the `destructive` (red-
|
||||
// tinted) treatment across all three tab variants for a consistent,
|
||||
// predictable "this is the serious one" signal. View/Edit/More stay
|
||||
// `ghost` (transparent, neutral) — `primary` only kicks in on View/Edit
|
||||
// when their panel is the one currently open, an existing state indicator
|
||||
// preserved as-is, not a new style.
|
||||
const renderRowActions = (row, index, expanded, size = 'md') => (
|
||||
<HStack gap={2} vAlign="center" justify="end" style={{ paddingRight: 'var(--spacing-4)' }}>
|
||||
{value0 == 0 && (
|
||||
<IconButton
|
||||
label="Inactivate"
|
||||
tooltip="Inactivate"
|
||||
size={size}
|
||||
variant="destructive"
|
||||
icon={<MdBlock size={14} />}
|
||||
icon={<MdOutlineBlock size={17} />}
|
||||
onClick={() => {
|
||||
setSelectedCustomer(row);
|
||||
setSelectedtenid(row.tenantid);
|
||||
@@ -846,8 +931,8 @@ const Clients1 = () => {
|
||||
label="Approve"
|
||||
tooltip="Approve"
|
||||
size={size}
|
||||
variant="ghost"
|
||||
icon={<FaRegCheckCircle size={13} />}
|
||||
variant="destructive"
|
||||
icon={<MdOutlineCheckCircle size={17} />}
|
||||
onClick={() => {
|
||||
setSelectedCustomer(row);
|
||||
setDialogopen(true);
|
||||
@@ -862,8 +947,8 @@ const Clients1 = () => {
|
||||
label="Activate"
|
||||
tooltip="Activate"
|
||||
size={size}
|
||||
variant="ghost"
|
||||
icon={<FaRegCheckCircle size={13} />}
|
||||
variant="destructive"
|
||||
icon={<MdOutlineCheckCircle size={17} />}
|
||||
onClick={() => {
|
||||
setSelectedCustomer(row);
|
||||
setSelectedtenid(row.tenantid);
|
||||
@@ -879,7 +964,7 @@ const Clients1 = () => {
|
||||
tooltip={expanded ? 'Hide details' : 'View details'}
|
||||
size={size}
|
||||
variant={expanded ? 'primary' : 'ghost'}
|
||||
icon={expanded ? <MdVisibilityOff size={14} /> : <MdVisibility size={14} />}
|
||||
icon={expanded ? <MdOutlineVisibilityOff size={17} /> : <MdOutlineVisibility size={17} />}
|
||||
onClick={() => {
|
||||
setSelectedCustomer(row);
|
||||
handleCollapseToggle1(index);
|
||||
@@ -887,29 +972,13 @@ const Clients1 = () => {
|
||||
setSelectedtenid(row.tenantid);
|
||||
}}
|
||||
/>
|
||||
<IconButton
|
||||
label="Locations"
|
||||
tooltip="Locations"
|
||||
size={size}
|
||||
variant="ghost"
|
||||
icon={<MdPlace size={14} />}
|
||||
onClick={() => setLocationsDialogTenant(row)}
|
||||
/>
|
||||
<IconButton
|
||||
label="Customers"
|
||||
tooltip="Customers"
|
||||
size={size}
|
||||
variant="ghost"
|
||||
icon={<MdPeopleAlt size={14} />}
|
||||
onClick={() => setCustomersDialogTenant(row)}
|
||||
/>
|
||||
{value0 !== 1 && (
|
||||
<IconButton
|
||||
label={openRowIndex2 === index ? 'Close' : 'Edit'}
|
||||
tooltip={openRowIndex2 === index ? 'Close' : 'Edit'}
|
||||
size={size}
|
||||
variant={openRowIndex2 === index ? 'primary' : 'ghost'}
|
||||
icon={openRowIndex2 === index ? <MdClose size={14} /> : <MdEdit size={14} />}
|
||||
icon={openRowIndex2 === index ? <MdOutlineClose size={17} /> : <MdOutlineEdit size={17} />}
|
||||
onClick={() => {
|
||||
setSelectedCustomer(row);
|
||||
handleCollapseToggle2(index);
|
||||
@@ -919,6 +988,22 @@ const Clients1 = () => {
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
<DropdownMenu
|
||||
button={{
|
||||
label: 'More actions',
|
||||
tooltip: 'More actions',
|
||||
variant: 'ghost',
|
||||
size,
|
||||
icon: <MdOutlineMoreVert size={17} />,
|
||||
isIconOnly: true
|
||||
}}
|
||||
menuWidth={180}
|
||||
placement="below"
|
||||
items={[
|
||||
{ label: 'Locations', icon: <MdOutlinePlace size={16} />, onClick: () => setLocationsDialogTenant(row) },
|
||||
{ label: 'Customers', icon: <MdOutlinePeopleAlt size={16} />, onClick: () => setCustomersDialogTenant(row) }
|
||||
]}
|
||||
/>
|
||||
</HStack>
|
||||
);
|
||||
|
||||
@@ -1049,6 +1134,146 @@ const Clients1 = () => {
|
||||
</MainCard>
|
||||
);
|
||||
|
||||
// Bridges TanStack's { pageIndex, pageSize } pagination shape onto the
|
||||
// existing page/rowsPerPage state, so every other reader of those two
|
||||
// values (the tenantList query key, handleChange, the page-size onChange
|
||||
// below) keeps working unchanged.
|
||||
const paginationState = { pageIndex: page, pageSize: rowsPerPage };
|
||||
const handlePaginationChange = (updater) => {
|
||||
const next = typeof updater === 'function' ? updater(paginationState) : updater;
|
||||
if (next.pageIndex !== page) setPage(next.pageIndex);
|
||||
if (next.pageSize !== rowsPerPage) setRowsPerPage(next.pageSize);
|
||||
};
|
||||
|
||||
// The column model — everything a column IS declared once here instead of
|
||||
// a hardcoded header row + hand-written body row, same shape as orders.js's
|
||||
// and riders.js's `columns`. Deliberately NOT wrapped in useMemo: the
|
||||
// Actions column's cell calls `renderRowActions`, which closes over value0/
|
||||
// openRowIndex2/several setters that change often — memoizing here would
|
||||
// need an unmanageable dependency list, and recomputing ~6 column defs a
|
||||
// render is cheap. Row-expand state is keyed by `row.tenantid` (not
|
||||
// position), so it stays correct once sorting/pagination reorder rows.
|
||||
const columns = [
|
||||
{
|
||||
id: 'serial',
|
||||
size: 44,
|
||||
minSize: 36,
|
||||
maxSize: 52,
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
meta: { label: '#' },
|
||||
header: () => <SectionLabel>#</SectionLabel>,
|
||||
cell: ({ table: t, row }) => (
|
||||
<Text type="supporting" weight="bold" color="secondary" hasTabularNumbers>
|
||||
{String(page * rowsPerPage + t.getRowModel().rows.indexOf(row) + 1).padStart(2, '0')}
|
||||
</Text>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'status',
|
||||
accessorKey: 'status',
|
||||
size: 140,
|
||||
minSize: 110,
|
||||
meta: { label: 'Status' },
|
||||
header: () => <SectionLabel>Status</SectionLabel>,
|
||||
cell: ({ getValue }) => {
|
||||
const meta = STATUS_META[String(getValue() || '').toLowerCase()] || STATUS_META.active;
|
||||
const Icon = meta.icon;
|
||||
return <Badge variant={meta.badge} icon={<Icon size={12} />} label={meta.label} />;
|
||||
}
|
||||
},
|
||||
{
|
||||
id: 'client',
|
||||
accessorKey: 'tenantname',
|
||||
size: 260,
|
||||
minSize: 200,
|
||||
meta: { label: 'Client' },
|
||||
header: () => <SectionLabel>Client</SectionLabel>,
|
||||
cell: ({ getValue, row }) => (
|
||||
<HStack gap={1.5} vAlign="center">
|
||||
<AccentAvatar color={BRAND} selected size={32}>
|
||||
<MdPersonPin size={16} />
|
||||
</AccentAvatar>
|
||||
<VStack gap={0} padding={0}>
|
||||
<Text weight="semibold" style={{ whiteSpace: 'nowrap' }}>
|
||||
{getValue()}
|
||||
</Text>
|
||||
<Text type="supporting" color="secondary">
|
||||
ID #{row.original.tenantid}
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'contact',
|
||||
accessorKey: 'primarycontact',
|
||||
size: 220,
|
||||
minSize: 160,
|
||||
meta: { label: 'Contact' },
|
||||
header: () => <SectionLabel>Contact</SectionLabel>,
|
||||
cell: ({ getValue, row }) => (
|
||||
<VStack gap={0} padding={0}>
|
||||
<Text weight="semibold" style={{ whiteSpace: 'nowrap' }}>
|
||||
{getValue() || '—'}
|
||||
</Text>
|
||||
<Text type="supporting" color="secondary">
|
||||
{row.original.primaryemail || '—'}
|
||||
</Text>
|
||||
</VStack>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'deliveryOtp',
|
||||
accessorFn: (row) => (row.requiredeliveryotp ? 'Yes' : 'No'),
|
||||
size: 150,
|
||||
minSize: 130,
|
||||
meta: { label: 'Delivery OTP', align: 'center' },
|
||||
header: () => <SectionLabel>Delivery OTP</SectionLabel>,
|
||||
cell: ({ getValue }) => (
|
||||
<Text type="supporting" color="secondary">
|
||||
{getValue()}
|
||||
</Text>
|
||||
)
|
||||
},
|
||||
{
|
||||
id: 'actions',
|
||||
// Up to 4 controls: status action + view details + edit + a "More"
|
||||
// overflow menu (Locations/Customers) — wide enough that 32px buttons
|
||||
// with 8px gaps never wrap to a second line.
|
||||
size: 220,
|
||||
minSize: 190,
|
||||
enableSorting: false,
|
||||
enableResizing: false,
|
||||
meta: { label: 'Actions', align: 'right' },
|
||||
header: () => <SectionLabel>Actions</SectionLabel>,
|
||||
cell: ({ row }) => renderRowActions(row.original, row.original.tenantid, openRowIndex1 === row.original.tenantid)
|
||||
}
|
||||
];
|
||||
|
||||
const table = useTable({
|
||||
features: TENANTS_TABLE_FEATURES,
|
||||
// filteredTenantList is already useMemo'd above (stable reference across
|
||||
// renders) — required for TanStack's row-model memoization to actually
|
||||
// hold and for pagination clicks to stick (an unmemoized array here
|
||||
// reads as "data changed" on every render and auto-resets to page 1,
|
||||
// the exact bug fixed on riders.js's table).
|
||||
data: filteredTenantList,
|
||||
columns,
|
||||
getRowId: (row, index) => String(row.tenantid ?? `row-${index}`),
|
||||
columnResizeMode: 'onChange',
|
||||
state: { sorting, pagination: paginationState, columnSizing },
|
||||
onSortingChange: setSorting,
|
||||
onPaginationChange: handlePaginationChange,
|
||||
onColumnSizingChange: setColumnSizing
|
||||
});
|
||||
|
||||
// Skeleton rows and the empty state span exactly the columns that are
|
||||
// actually rendered. Read off `columns.length` directly (not
|
||||
// `table.getVisibleLeafColumns()`): that method only exists when
|
||||
// columnVisibilityFeature is registered, which this table doesn't need.
|
||||
const colCount = columns.length;
|
||||
|
||||
return (
|
||||
<>
|
||||
{(getalltenantsIsLoading || isloader) && <Loader />}
|
||||
@@ -1140,24 +1365,108 @@ const Clients1 = () => {
|
||||
}
|
||||
>
|
||||
{!isMobile && (
|
||||
<TableScroll minWidth={960}>
|
||||
<Table density="balanced" dividers="rows" hasHover>
|
||||
<TableScroll minWidth={table.getTotalSize()}>
|
||||
{/* table-layout: fixed makes the declared column widths
|
||||
authoritative — same reasoning as orders.js's/riders.js's
|
||||
tables. Header and body are both rendered FROM the column
|
||||
model, so they cannot drift apart. */}
|
||||
<Table
|
||||
density="balanced"
|
||||
dividers="rows"
|
||||
hasHover
|
||||
verticalAlign="middle"
|
||||
aria-label="Tenants"
|
||||
style={{ tableLayout: 'fixed', width: '100%', minWidth: table.getTotalSize() }}
|
||||
>
|
||||
<TableHeader>
|
||||
<TableRow>
|
||||
<TableHeaderCell style={{ minWidth: 40, width: 40, maxWidth: 40 }}>#</TableHeaderCell>
|
||||
<TableHeaderCell>Status</TableHeaderCell>
|
||||
<TableHeaderCell>Client</TableHeaderCell>
|
||||
<TableHeaderCell>Contact</TableHeaderCell>
|
||||
<TableHeaderCell>Delivery OTP</TableHeaderCell>
|
||||
<TableHeaderCell>Actions</TableHeaderCell>
|
||||
</TableRow>
|
||||
{table.getHeaderGroups().map((headerGroup) => (
|
||||
<TableRow key={headerGroup.id} isHeaderRow>
|
||||
{headerGroup.headers.map((header) => {
|
||||
const align = header.column.columnDef.meta?.align;
|
||||
const canSort = header.column.getCanSort();
|
||||
const sorted = header.column.getIsSorted();
|
||||
return (
|
||||
<TableHeaderCell
|
||||
key={header.id}
|
||||
scope="col"
|
||||
aria-sort={sorted === 'asc' ? 'ascending' : sorted === 'desc' ? 'descending' : canSort ? 'none' : undefined}
|
||||
style={{
|
||||
width: header.getSize(),
|
||||
minWidth: header.column.columnDef.minSize,
|
||||
maxWidth: header.column.columnDef.maxSize || header.getSize(),
|
||||
textAlign: align,
|
||||
position: 'relative'
|
||||
}}
|
||||
>
|
||||
<HStack
|
||||
gap={1}
|
||||
vAlign="center"
|
||||
justify={align === 'right' ? 'end' : align === 'center' ? 'center' : 'start'}
|
||||
role={canSort ? 'button' : undefined}
|
||||
tabIndex={canSort ? 0 : undefined}
|
||||
onClick={canSort ? header.column.getToggleSortingHandler() : undefined}
|
||||
onKeyDown={
|
||||
canSort
|
||||
? (e) => {
|
||||
if (e.key === 'Enter' || e.key === ' ') {
|
||||
e.preventDefault();
|
||||
header.column.toggleSorting();
|
||||
}
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
style={canSort ? { cursor: 'pointer', userSelect: 'none' } : undefined}
|
||||
>
|
||||
{flexRender(header.column.columnDef.header, header.getContext())}
|
||||
{canSort &&
|
||||
(sorted === 'asc' ? (
|
||||
<MdArrowUpward size={12} color={DT.textPrimary} style={{ flexShrink: 0 }} />
|
||||
) : sorted === 'desc' ? (
|
||||
<MdArrowDownward size={12} color={DT.textPrimary} style={{ flexShrink: 0 }} />
|
||||
) : (
|
||||
<MdUnfoldMore size={12} color={DT.textMuted} style={{ flexShrink: 0, opacity: 0.6 }} />
|
||||
))}
|
||||
</HStack>
|
||||
|
||||
{header.column.getCanResize() && (
|
||||
<Center
|
||||
aria-hidden="true"
|
||||
onMouseDown={header.getResizeHandler()}
|
||||
onTouchStart={header.getResizeHandler()}
|
||||
style={{
|
||||
position: 'absolute',
|
||||
top: 0,
|
||||
right: 0,
|
||||
height: '100%',
|
||||
width: 10,
|
||||
cursor: 'col-resize',
|
||||
userSelect: 'none',
|
||||
touchAction: 'none'
|
||||
}}
|
||||
>
|
||||
<Center
|
||||
style={{
|
||||
width: 2,
|
||||
height: '60%',
|
||||
borderRadius: 'var(--radius-element)',
|
||||
backgroundColor: header.column.getIsResizing() ? DT.brand : DT.borderSubtle,
|
||||
transition: 'background-color 120ms ease'
|
||||
}}
|
||||
/>
|
||||
</Center>
|
||||
)}
|
||||
</TableHeaderCell>
|
||||
);
|
||||
})}
|
||||
</TableRow>
|
||||
))}
|
||||
</TableHeader>
|
||||
<TableBody>
|
||||
{getalltenantsIsLoading && <TableLoader rows={5} columns={6} />}
|
||||
{getalltenantsIsLoading && <TableLoader rows={5} columns={colCount} />}
|
||||
|
||||
{filteredTenantList.length == 0 && !isloader ? (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6}>
|
||||
<TableCell colSpan={colCount}>
|
||||
<EmptyState
|
||||
title="No tenants to show"
|
||||
description={`No ${activeTabMeta.label.toLowerCase()} tenants for this filter.`}
|
||||
@@ -1166,65 +1475,37 @@ const Clients1 = () => {
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
) : (
|
||||
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;
|
||||
table.getRowModel().rows.map((tableRow) => {
|
||||
const row = tableRow.original;
|
||||
const expanded1 = openRowIndex1 === row.tenantid;
|
||||
const expanded2 = openRowIndex2 === row.tenantid;
|
||||
return (
|
||||
<Fragment key={row.tenantid ?? index}>
|
||||
<Fragment key={tableRow.id}>
|
||||
<TableRow>
|
||||
<TableCell>
|
||||
<Text type="supporting" weight="bold" color="secondary">
|
||||
{String(index + 1 + page * rowsPerPage).padStart(2, '0')}
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Badge variant={rowStatusMeta.badge} icon={<RowStatusIcon size={12} />} label={rowStatusMeta.label} />
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<HStack gap={1.5} vAlign="center">
|
||||
<AccentAvatar color={BRAND} selected size={32}>
|
||||
<MdPersonPin size={16} />
|
||||
</AccentAvatar>
|
||||
<VStack gap={0} padding={0}>
|
||||
<Text weight="semibold" style={{ whiteSpace: 'nowrap' }}>
|
||||
{row.tenantname}
|
||||
</Text>
|
||||
<Text type="supporting" color="secondary">
|
||||
ID #{row.tenantid}
|
||||
</Text>
|
||||
</VStack>
|
||||
</HStack>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<VStack gap={0} padding={0}>
|
||||
<Text weight="semibold" style={{ whiteSpace: 'nowrap' }}>
|
||||
{row.primarycontact || '—'}
|
||||
</Text>
|
||||
<Text type="supporting" color="secondary">
|
||||
{row.primaryemail || '—'}
|
||||
</Text>
|
||||
</VStack>
|
||||
</TableCell>
|
||||
<TableCell>
|
||||
<Text type="supporting" color="secondary">
|
||||
{row.requiredeliveryotp ? 'Yes' : 'No'}
|
||||
</Text>
|
||||
</TableCell>
|
||||
<TableCell>{renderRowActions(row, index, openRowIndex1 === index)}</TableCell>
|
||||
{tableRow.getAllCells().map((cell) => (
|
||||
<TableCell
|
||||
key={cell.id}
|
||||
style={{
|
||||
textAlign: cell.column.columnDef.meta?.align,
|
||||
maxWidth: cell.column.columnDef.maxSize || cell.column.getSize()
|
||||
}}
|
||||
>
|
||||
{flexRender(cell.column.columnDef.cell, cell.getContext())}
|
||||
</TableCell>
|
||||
))}
|
||||
</TableRow>
|
||||
|
||||
{openRowIndex1 === index && (
|
||||
{expanded1 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} style={{ padding: 12 }}>
|
||||
<TableCell colSpan={colCount} style={{ padding: 12 }}>
|
||||
{renderDetailsPanel(row)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)}
|
||||
|
||||
{openRowIndex2 === index && (
|
||||
{expanded2 && (
|
||||
<TableRow>
|
||||
<TableCell colSpan={6} style={{ padding: 12 }}>
|
||||
<TableCell colSpan={colCount} style={{ padding: 12 }}>
|
||||
{renderEditPanel(row)}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
@@ -1247,14 +1528,15 @@ const Clients1 = () => {
|
||||
icon={<MdGroups size={28} />}
|
||||
/>
|
||||
) : (
|
||||
filteredTenantList.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage).map((row, index) => {
|
||||
table.getRowModel().rows.map((tableRow) => {
|
||||
const row = tableRow.original;
|
||||
const rowStatusKey = String(row.status || '').toLowerCase();
|
||||
const rowStatusMeta = STATUS_META[rowStatusKey] || STATUS_META.active;
|
||||
const RowStatusIcon = rowStatusMeta.icon;
|
||||
const expanded = openRowIndex1 === index;
|
||||
const expanded = openRowIndex1 === row.tenantid;
|
||||
return (
|
||||
<MobileCard
|
||||
key={row.tenantid ?? index}
|
||||
key={tableRow.id}
|
||||
accent={rowStatusMeta.color}
|
||||
header={
|
||||
<HStack justify="between" vAlign="start" gap={1}>
|
||||
@@ -1269,7 +1551,7 @@ const Clients1 = () => {
|
||||
<Badge variant={rowStatusMeta.badge} icon={<RowStatusIcon size={10} />} label={rowStatusMeta.label} />
|
||||
</VStack>
|
||||
</HStack>
|
||||
{renderRowActions(row, index, expanded)}
|
||||
{renderRowActions(row, row.tenantid, expanded)}
|
||||
</HStack>
|
||||
}
|
||||
>
|
||||
@@ -1294,10 +1576,20 @@ const Clients1 = () => {
|
||||
</MobileCardList>
|
||||
)}
|
||||
|
||||
{!searchword && filteredTenantList.length > 0 && (
|
||||
{/* Page-number pagination, matching orders.js's/riders.js's
|
||||
footer — previously hidden while `searchword` was set (a
|
||||
search result past the first page had no way to reach it),
|
||||
and used the "count"-only variant (no page-number buttons)
|
||||
instead of the numbered pager the other two tables use. */}
|
||||
{filteredTenantList.length > 0 && (
|
||||
<>
|
||||
<Divider />
|
||||
<HStack justify="end" padding={3}>
|
||||
<HStack justify="between" vAlign="center" gap={2} wrap="wrap" paddingInline={4} paddingBlock={2}>
|
||||
<Text type="supporting" weight="bold" color="secondary" hasTabularNumbers>
|
||||
{`${page * rowsPerPage + 1}–${Math.min((page + 1) * rowsPerPage, filteredTenantList.length)} of ${
|
||||
filteredTenantList.length
|
||||
} tenant${filteredTenantList.length === 1 ? '' : 's'}`}
|
||||
</Text>
|
||||
<Pagination
|
||||
page={page + 1}
|
||||
onChange={(p) => setPage(p - 1)}
|
||||
@@ -1308,7 +1600,8 @@ const Clients1 = () => {
|
||||
setRowsPerPage(size);
|
||||
setPage(0);
|
||||
}}
|
||||
variant="count"
|
||||
size="sm"
|
||||
label="Tenants pagination"
|
||||
/>
|
||||
</HStack>
|
||||
</>
|
||||
|
||||
Reference in New Issue
Block a user