docs(spec): add Re-Send button on PO Sent tab of buyer-tasks
Co-authored-by: Orca <help@stably.ai>
This commit is contained in:
parent
7f477ca949
commit
3a954982d0
1 changed files with 166 additions and 0 deletions
|
|
@ -0,0 +1,166 @@
|
|||
# Re-Send button on PO Sent tab of buyer-tasks page
|
||||
|
||||
**Date:** 2026-08-06
|
||||
**Status:** Approved (brainstorming complete)
|
||||
**Scope:** Two component files (`buyer-task-group-card.tsx`, `buyer-tasks-page.tsx`) + their test files. No backend change. No new types.
|
||||
|
||||
## Context
|
||||
|
||||
Today the PO Sent tab of `src/app/(app)/orders/tasks/page.tsx` (rendered via `BuyerTasksPage` → `BuyerTaskGroupCard`) shows a single action button per card:
|
||||
|
||||
- **Default**: purple "Mark as ready" — opens the `MarkReadyDialog` to flag the PO group as ready for collection.
|
||||
- **`poStatus === "mail_failed"`**: red "Resend" — **buggy**: opens the same `MarkReadyDialog` even though the label says "Resend". The operator can't actually resend from the buyer-tasks page today; they have to navigate to the order detail page.
|
||||
|
||||
The user has asked for two things:
|
||||
|
||||
1. Add a **Re-Send** button next to "Mark as ready" on the PO Sent tab. Clicking it opens the existing resend modal so the operator can resend the PO to the same or a different email.
|
||||
2. Match the Mark-as-ready button's shape but in a different color. Use a **theme token** (the app's `info` token) so the color works across all themes.
|
||||
|
||||
Bundled in the same change: fix the mail-failed "Resend" label to actually open the resend modal instead of the Mark Ready dialog.
|
||||
|
||||
## Approach (chosen)
|
||||
|
||||
**Reuse the existing `SendPoModal`.** It already supports a resend flow:
|
||||
|
||||
- For a PO with `status === "sent"` or `"mail_failed"`, the modal pre-fills `to / cc / bcc / subject / body` from the previously-persisted `email_payload` and shows the previous `email_error` inline.
|
||||
- The title flips from "Send" to "Resend" and the submit button reads "Resend".
|
||||
- Submitting POSTs to the same `/api/v1/purchase-orders/[id]/send-email` route the order detail page uses — no new endpoint needed.
|
||||
|
||||
The buyer-tasks page already opens this modal for the `po_draft` step. Wiring it for `po_sent` is a single handler + a prop on the card.
|
||||
|
||||
The Mark-as-ready purple hardcoded color is replaced with the `primary` theme token so both buttons follow the same theme discipline.
|
||||
|
||||
Approaches considered and rejected:
|
||||
|
||||
- *Build a new "Re-Send" modal.* Redundant — the existing one already handles resend.
|
||||
- *Use a hardcoded teal/Tailwind color.* Violates the theme-token rule the user has set; won't work in dark mode or future themes.
|
||||
- *Add the button to all three tabs.* Only `po_sent` (and `po_draft` already) make sense — there's no PO to resend in `needs_po`.
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
BuyerTasksPage
|
||||
├─ state: markReadyFor: BuyerTaskGroup | null (existing — Mark Ready dialog)
|
||||
├─ state: sendPoFor: BuyerTaskGroup | null (existing — SendPoModal; wired for po_draft)
|
||||
└─ handleAction(group)
|
||||
├─ step === "needs_po" → setBusyGroupId → POST generate-po (existing)
|
||||
├─ step === "po_draft" → setSendPoFor(group) (existing)
|
||||
└─ step === "po_sent" → setMarkReadyFor(group) (existing)
|
||||
[NEW] handleResend(group)
|
||||
└─ step === "po_sent" → setSendPoFor(group) (NEW — reuses po_draft flow)
|
||||
```
|
||||
|
||||
```
|
||||
BuyerTaskGroupCard
|
||||
└─ For step === "po_sent" only: render TWO buttons
|
||||
├─ Left: "Re-Send" → onResend(group)
|
||||
│ - default: bg-info text-info-foreground hover:bg-info/90
|
||||
│ - mail_failed: bg-destructive text-destructive-foreground hover:bg-destructive/90
|
||||
└─ Right: "Mark as ready" → onAction(group)
|
||||
- bg-primary text-primary-foreground hover:bg-primary/90 (was: bg-purple-600)
|
||||
|
||||
For step === "needs_po" / "po_draft": single button (unchanged)
|
||||
```
|
||||
|
||||
## Components
|
||||
|
||||
### `BuyerTaskGroupCard` — `src/components/buyer-tasks/buyer-task-group-card.tsx`
|
||||
|
||||
**Prop changes:**
|
||||
- Add `onResend?: (group: BuyerTaskGroup) => void`. Optional, only used by `po_sent` step.
|
||||
|
||||
**Render change for `po_sent`:**
|
||||
- Wrap the two buttons in a flex container (`flex items-center gap-2`) so they sit side-by-side, right-aligned next to the supplier info.
|
||||
- Re-Send button:
|
||||
- Label: `"Re-Send"` (with hyphen to match the user's exact wording).
|
||||
- Default style: `bg-info text-info-foreground hover:bg-info/90`.
|
||||
- When `poStatus === "mail_failed"`: override to `bg-destructive text-destructive-foreground hover:bg-destructive/90` and label becomes `"Resend"` (no hyphen — matches the existing error-state style elsewhere in the app).
|
||||
- Mark-as-ready button:
|
||||
- Label: `"Mark as ready"` (existing).
|
||||
- Style: migrate from hardcoded `bg-purple-600 hover:bg-purple-700 text-white` to themed `bg-primary text-primary-foreground hover:bg-primary/90`.
|
||||
|
||||
**Render change for other steps:** unchanged.
|
||||
|
||||
### `BuyerTasksPage` — `src/components/buyer-tasks/buyer-tasks-page.tsx`
|
||||
|
||||
**New handler `handleResend`:**
|
||||
```ts
|
||||
function handleResend(group: BuyerTaskGroup) {
|
||||
if (group.step !== "po_sent") return;
|
||||
setSendPoFor(group);
|
||||
}
|
||||
```
|
||||
|
||||
The existing `SendPoModal` render block (`sendPoFor && <SendPoModal ... />`) is already wired for both Send (po_draft) and Resend (po_sent) modes — `poStatus` on the group sets the modal's prefill behavior via the stub `purchaseOrder` prop. No change needed to the JSX; we just trigger the same state from a new entry point.
|
||||
|
||||
**Pass `onResend` to `BuyerTaskGroupCard`:**
|
||||
```tsx
|
||||
<BuyerTaskGroupCard
|
||||
key={group.groupId}
|
||||
group={group}
|
||||
onAction={handleAction}
|
||||
onResend={handleResend} // NEW
|
||||
disabled={busyGroupId === group.groupId}
|
||||
/>
|
||||
```
|
||||
|
||||
### Files NOT changing
|
||||
|
||||
- `src/components/purchase-orders/send-po-modal.tsx` — already handles resend mode; no change.
|
||||
- `src/app/api/v1/purchase-orders/[id]/send-email/route.ts` — already accepts resends; no change.
|
||||
- `src/lib/server/buyer-tasks-service.ts` — no change. The `poStatus` field is already on `BuyerTaskGroup`.
|
||||
- Any PocketBase schema — no change.
|
||||
- Any backend route — no change.
|
||||
|
||||
## Data flow
|
||||
|
||||
Unchanged. The card → page → existing SendPoModal → existing API route pipeline is reused as-is. The only new path is:
|
||||
|
||||
```
|
||||
BuyerTaskGroupCard [Re-Send click]
|
||||
→ BuyerTasksPage.handleResend(group)
|
||||
→ setSendPoFor(group)
|
||||
→ <SendPoModal open purchaseOrder={...group as PO stub...} />
|
||||
→ POST /api/v1/purchase-orders/[id]/send-email
|
||||
→ (existing pipeline)
|
||||
```
|
||||
|
||||
## Error handling
|
||||
|
||||
- `SendPoModal` already surfaces pre-send validation failures (`!res.ok`, `webhookFired === false`) as error toasts. No new error paths.
|
||||
- The existing `mail_failed` inline banner (`role="alert"`) stays above the items list, visible whether the operator clicks Re-Send or Mark as ready.
|
||||
- Re-Send doesn't gate on `poStatus === "sent"` — the operator can re-trigger an in-flight send if they want to. The route handles concurrent sends the same way the order-detail flow does.
|
||||
|
||||
## Testing
|
||||
|
||||
### Existing tests to update
|
||||
|
||||
- `src/components/buyer-tasks/buyer-task-group-card.test.tsx`
|
||||
- Add: `po_sent` renders two buttons (Re-Send + Mark as ready).
|
||||
- Add: Re-Send button uses `bg-info`; Mark as ready uses `bg-primary` (no more hardcoded purple).
|
||||
- Add: Re-Send click fires `onResend`; Mark as ready click still fires `onAction`.
|
||||
- Add: `poStatus === "mail_failed"` Re-Send button uses `bg-destructive` and label becomes `"Resend"`.
|
||||
- Existing: `po_draft` still renders a single "Send PO" button (no regression).
|
||||
|
||||
- `src/components/buyer-tasks/buyer-tasks-page.test.tsx`
|
||||
- Add: clicking Re-Send on a `po_sent` card opens `SendPoModal` with the group's PO stub.
|
||||
- Existing: clicking Mark as ready still opens `MarkReadyDialog` (no regression).
|
||||
|
||||
### Not changing
|
||||
|
||||
- Service-layer / API route tests — the API surface didn't change.
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Migrating other hardcoded colors elsewhere in the app to theme tokens. Only the Mark as ready button is in scope here.
|
||||
- Adding a Re-Send button to the `needs_po` tab (no PO exists yet).
|
||||
- A "send to multiple addresses" feature.
|
||||
- A "schedule send for later" feature.
|
||||
- A bulk-resend flow.
|
||||
|
||||
## Implementation order
|
||||
|
||||
1. Update `BuyerTaskGroupCard` to accept `onResend` and render two buttons for `po_sent`; migrate the Mark-as-ready color.
|
||||
2. Update `BuyerTasksPage` to add `handleResend` and pass it to the card.
|
||||
3. Update the two test files with the new assertions.
|
||||
4. Run the full test suite + lint + type-check.
|
||||
Loading…
Reference in a new issue