Wizard: gift_expiry_days now lives behind a preset toggle (15/30/60/90/180 + Custom) instead of a naked number input. Operator clicks a chip; the value flows back into the existing campaign param. Inventory page (/campaigns/gifts): - Cross-campaign view of every wrapper token with status taxonomy (active / redeemed / expired / revoked / pending). Each card on the counters strip is a click-to-filter shortcut. - "Réassignables" highlighted in amber when > 0 — these are gifts whose wrapper expired or was revoked but the Giftbit URL is still unredeemed, ready for a fresh recipient. - Search across name/email/url/token; per-status and per-campaign filter dropdowns. - One-click copy on the Giftbit URL with a tailored toast that walks the operator through the reassignment workflow (paste into manual- add dialog of a new campaign). - Revoke action with confirmation; explicit about what survives (the Giftbit URL stays valid on their side) vs what changes (our wrapper stops redirecting). Backend: - GET /campaigns/gifts flattens every recipient with a gift across every campaign — single-shot, no pagination yet (we're under 10k gifts total). - POST /campaigns/:id/recipients/:row/revoke sets gift_revoked=true and broadcasts the recipient-update SSE event. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com> |
||
|---|---|---|
| .. | ||
| data | ||
| lib | ||
| preview | ||
| public | ||
| scripts | ||
| templates | ||
| .env.example | ||
| docker-compose.yml | ||
| package-lock.json | ||
| package.json | ||
| server.js | ||