my stock update page ui

This commit is contained in:
2026-10-05 19:20:08 +05:30
parent bcb5d1de28
commit b7f9a6aaeb
2 changed files with 37 additions and 51 deletions

View File

@@ -260,10 +260,17 @@ export function ProductDrawer({
{/* ── Health score ──────────────────────────────────────────────────
Under Sizes and above the shop detail: it is what a SHOPPER reads,
and it belongs with the other shopper-facing facts. */}
{/* The toggle is behind `canManage` for the same reason hiding a product
is: it writes `products.showhealthscore`, which is per TENANT and not
per outlet, so a branch turning a rating off would turn it off at
every shop the business runs. Absent rather than disabled — a greyed
switch invites "why", and the answer is that it is not a decision one
outlet makes. The panel itself still renders: a branch user sees what
a customer sees, they just cannot change it. */}
<HealthScorePanel
product={product}
category={product.categoryname || product.Subcategoryname}
{...(product.tenantid && product.productid
{...(canManage && product.tenantid && product.productid
? { onToggleShown: (show: boolean) => showScore.mutate(show) }
: {})}
/>

View File

@@ -227,14 +227,14 @@ function RequestsPanel({
};
const decide = useMutation({
mutationFn: ({ requestid, action }: { requestid: number; action: 'approve' | 'reject' | 'arrive' }) =>
action === 'approve'
? stockApi.approve(requestid)
: action === 'arrive'
? stockApi.confirmArrival(requestid)
: stockApi.reject(requestid),
/* No 'arrive' here. Confirming arrival writes stock onto a branch's
shelf and belongs to the branch — see the row actions below. Narrowing
the type rather than leaving the branch unreachable, so a future caller
cannot quietly reintroduce it from this screen. */
mutationFn: ({ requestid, action }: { requestid: number; action: 'approve' | 'reject' }) =>
action === 'approve' ? stockApi.approve(requestid) : stockApi.reject(requestid),
onSuccess: (_data, { action }) => {
const verb = action === 'approve' ? 'approved' : action === 'arrive' ? 'confirmed as arrived' : 'rejected';
const verb = action === 'approve' ? 'approved' : 'rejected';
setOutcome(`Request ${verb}.`);
},
onError: () => {
@@ -292,26 +292,14 @@ function RequestsPanel({
[requests, selection],
);
const selectedInTransit = useMemo(
() =>
requests
.filter((r) => selection.has(r.requestid) && r.status === 'Approved')
.map((r) => r.requestid),
[requests, selection],
);
const decideBatch = useMutation({
mutationFn: ({ ids, action }: { ids: number[]; action: 'approve' | 'reject' | 'arrive' }) =>
action === 'approve'
? stockApi.approveMany(ids)
: action === 'arrive'
? stockApi.confirmArrivalMany(ids)
: stockApi.rejectMany(ids),
mutationFn: ({ ids, action }: { ids: number[]; action: 'approve' | 'reject' }) =>
action === 'approve' ? stockApi.approveMany(ids) : stockApi.rejectMany(ids),
onSuccess: (result, { ids, action }) => {
const done = result?.updated?.length ?? ids.length;
const failed = result?.failed?.length ?? 0;
const verb =
action === 'approve' ? 'approved' : action === 'arrive' ? 'added to shelf' : 'rejected';
const verb = action === 'approve' ? 'approved' : 'rejected';
setOutcome(
failed === 0
? `${count(done)} request${done === 1 ? '' : 's'} ${verb}.`
@@ -490,21 +478,11 @@ function RequestsPanel({
/>
</>
) : null}
{selectedInTransit.length > 0 ? (
<Button
label={
decideBatch.isPending
? 'Working…'
: `Confirm arrival of ${count(selectedInTransit.length)}`
}
variant="primary"
size="sm"
icon={<CheckCheck size={14} />}
isLoading={decideBatch.isPending}
isDisabled={decideBatch.isPending}
onClick={() => decideBatch.mutate({ ids: selectedInTransit, action: 'arrive' })}
/>
) : null}
{/* No bulk "confirm arrival" either, for the same reason as
the row action: the stock is written when the BRANCH says
the goods arrived, and an admin confirming forty deliveries
they never saw is the version of this that puts stock on a
shelf that is empty. */}
</HStack>
</HStack>
</div>
@@ -710,19 +688,20 @@ function RequestsPanel({
/>
</HStack>
) : request.status === 'Approved' ? (
<Button
label="Confirm arrival"
variant="primary"
size="sm"
style={{ height: 28, padding: '0 10px', fontSize: 12 }}
icon={<CheckCheck size={13} />}
isDisabled={decide.isPending && isActing}
isLoading={decide.isPending && isActing}
onClick={() => {
setActing(request.requestid);
decide.mutate({ requestid: request.requestid, action: 'arrive' });
}}
/>
/* No action here, deliberately.
Confirming arrival is what WRITES THE STOCK onto
a branch's shelf, and only the branch can know
the goods actually turned up. For the admin this
row is an acknowledgement — they agreed to send
it — so the status is shown and nothing is
offered. The control lives on the Store user's
own products screen.
Absent rather than disabled: a greyed button
invites "why", and the answer is that it is not
this person's call. */
null
) : (
<span style={{ fontSize: 12, color: 'var(--color-ink-4)' }}>
{request.status === 'Received' ? 'Completed' : 'Declined'}