import { useMemo, useState } from 'react'; import { useMutation, useQueryClient } from '@tanstack/react-query'; import { VStack } from '@astryxdesign/core/VStack'; import { Bike, Check, Truck } from 'lucide-react'; import { errorMessage } from '@/api/client'; import { tenantsApi } from '@/api/tenants'; import { useAllPartners, useAppRegions } from '@/queries/hooks'; import { queryKeys } from '@/queries/keys'; import { Drawer } from '@/features/store-admin/Drawer'; import { Alert, DrawerButton, DrawerCard, Note, Row, Section } from '@/features/store-admin/drawerKit'; /** * Which delivery partner supplies a merchant's riders. * * ── Why this is the platform's decision ───────────────────────────────────── * * `partnerid` is kept out of the merchant-editable allowlist on the server, so * this cannot be done from the shop's own profile — a merchant who could set it * would move themselves under another partner's riders and billing. It is * changed here, by whoever is looking at that merchant's record. * * ── Why "own riders" is an option and not an absence ──────────────────────── * * Sending `partnerid: 0` is a real instruction: it means the shop delivers with * riders it hired itself. The server reads a zero as sent rather than as a * missing field, which is exactly why the endpoint is separate — everywhere * else in the tenant API a zero means "not supplied", and there it would * silently unassign somebody. * * One partner per merchant, which is what `tenants.partnerid` allows and what * the assign picker later branches on. A partner serving several merchants is * the ordinary case in the other direction: 44 supplies 48 shops. */ export function PartnerAssignDrawer({ tenantid, tenantname, currentPartnerId, onClose, }: { tenantid: number; tenantname: string; currentPartnerId: number; onClose: () => void; }) { const client = useQueryClient(); const partners = useAllPartners(); const regions = useAppRegions(); const [chosen, setChosen] = useState(currentPartnerId); const [error, setError] = useState(null); const regionName = useMemo(() => { const map = new Map(); for (const region of regions.data ?? []) { map.set(region.applocationid, region.locationname ?? `Region ${region.applocationid}`); } return map; }, [regions.data]); const save = useMutation({ mutationFn: () => tenantsApi.assignPartner(tenantid, chosen), onSuccess: async () => { await client.invalidateQueries({ queryKey: queryKeys.tenants.all }); onClose(); }, onError: (cause) => setError(errorMessage(cause)), }); const current = partners.data.find((p) => p.partnerid === currentPartnerId); return ( } isDisabled={save.isPending || chosen === currentPartnerId} onClick={() => save.mutate()} /> } > {error ? {error} : null} }> A partner supplies riders to this shop. With one set, the assign screen offers the partner’s riders alongside any the shop hired itself; without one, only its own.
0 ? `Partner ${currentPartnerId}` : 'Own riders only')} />
{/* "Own riders" first and always present. It is not the empty state — a shop that hires its own riders is a real arrangement, and the option has to be as reachable as any partner. */} setChosen(0)} /> {partners.isLoading ? ( ) : ( partners.data.map((partner) => ( setChosen(partner.partnerid)} /> )) )}
); } function Choice({ label, detail, isChosen, onChoose, }: { label: string; detail: string; isChosen: boolean; onChoose: () => void; }) { return ( ); }