tenant profile

This commit is contained in:
2026-09-02 12:29:17 +05:30
parent 52be0e30ac
commit d22b79e109
7 changed files with 651 additions and 221 deletions

View File

@@ -1,4 +1,5 @@
import { useMemo, useState } from 'react';
import { useSearchParams } from 'react-router-dom';
import { useMutation, useQueryClient } from '@tanstack/react-query';
import { Badge } from '@astryxdesign/core/Badge';
import { Button } from '@astryxdesign/core/Button';
@@ -32,6 +33,9 @@ import { useStockRequests, useStockStatement } from '@/queries/hooks';
import { useBranchScope } from '../BranchScope';
import { CataloguePanel } from '../CataloguePanel';
import { ProductsPanel } from '../ProductsPanel';
import { SetupReturnBar } from '@/features/onboarding/SetupReturnBar';
import type { StepId } from '@/features/onboarding/onboardingState';
import { useLocationProducts } from '@/queries/hooks';
import { branchLabel, count, money } from '../format';
type Tab = 'products' | 'catalogue' | 'stock' | 'requests';
@@ -53,10 +57,39 @@ type Tab = 'products' | 'catalogue' | 'stock' | 'requests';
*/
export function InventoryPage() {
const { branches, selected, current, tenantid } = useBranchScope();
const [tab, setTab] = useState<Tab>('products');
/**
* The tab comes from the URL, so a link can name one.
*
* It could not before — the tab was local state seeded with 'products' and
* nothing ever read the query string. Setup's "add from the catalogue" link
* pointed at `?tab=catalogue` and landed the merchant on Products, which is
* the bug that made that step look broken.
*/
const [params, setParams] = useSearchParams();
const asked = params.get('tab');
const [tab, setTab] = useState<Tab>(() =>
asked === 'catalogue' || asked === 'stock' || asked === 'requests' ? asked : 'products',
);
/* A tab chosen by hand replaces the one in the URL, so a reload or a shared
link shows the panel actually being looked at. `replace` because flipping
between tabs is not navigation and should not fill up the Back button. */
function chooseTab(next: Tab) {
setTab(next);
const carry = new URLSearchParams(params);
carry.set('tab', next);
setParams(carry, { replace: true });
}
/* Set when setup sent them here, and it names the step to return to. */
const fromSetup = params.get('setup') as StepId | null;
const setupProducts = useLocationProducts(tenantid || undefined, undefined, 0, {
allBranches: true,
});
// Owned here so the button can live in the tab row while the drawer it opens
// stays inside the panel that uses it.
const [isUploading, setUploading] = useState(false);
const [isUploading, setUploading] = useState(() => params.get('upload') === '1');
const requests = useStockRequests(
tenantid ? { tenantid, locationid: selected ?? undefined } : undefined,
@@ -69,6 +102,12 @@ export function InventoryPage() {
return (
<VStack gap={3}>
{/* Above the header, because it is about the errand rather than the page:
it is the only thing on screen that knows setup is unfinished. */}
{fromSetup ? (
<SetupReturnBar step={fromSetup} productCount={(setupProducts.data ?? []).length} />
) : null}
<PageHeader
title="Inventory"
count={selected === null ? `${branches.length} branches` : current?.locationname}
@@ -106,26 +145,26 @@ export function InventoryPage() {
label="Products"
icon={<PackageSearch size={14} />}
isActive={tab === 'products'}
onClick={() => setTab('products')}
onClick={() => chooseTab('products')}
/>
<TabButton
label="Catalogue"
icon={<DownloadCloud size={14} />}
isActive={tab === 'catalogue'}
onClick={() => setTab('catalogue')}
onClick={() => chooseTab('catalogue')}
/>
<TabButton
label="Stock requests"
icon={<ClipboardList size={14} />}
isActive={tab === 'requests'}
onClick={() => setTab('requests')}
onClick={() => chooseTab('requests')}
badge={pending.length || undefined}
/>
<TabButton
label="Stock"
icon={<Layers size={14} />}
isActive={tab === 'stock'}
onClick={() => setTab('stock')}
onClick={() => chooseTab('stock')}
/>
</HStack>
}