feat(plans): reactivate + delete-when-unused; card layout fixes overlap
Addresses three issues with the plan catalog screen: 1. Retired plans had NO actions (the action cell was gated on "current version", which a retired plan lacks) — so there was no way to make one in-force again. Add POST /:planId/reactivate (inverse of retire) + a Reactivate button on retired plans. 2. No delete. Add DELETE /:planId, allowed ONLY when zero subscriptions reference the planId (any version) — a referenced plan version must survive for reproducible repricing/audit, so an in-use delete returns 409 and the UI says "retire it instead". The Delete button only shows when the plan has 0 subscribers. 3. The 6-column table overflowed max-w-3xl: action buttons overlapped and the status badges wrapped to a second line. Replace it with a CARD list (one card per planId, grouped across versions): name + status on top, price · hours · effective on a wrap row, "used by N" expandable to holder names, and actions on their own bordered row — nothing overlaps, badges stay inline. Build+lint 12/12 (i18n parity). Verified on a DB copy: unused plans report deletable; retire→reactivate flips active back. Claude-Session: https://claude.ai/code/session_01Xcm6ikLgGoCxxHrxtjkk5V
This commit is contained in:
@@ -614,6 +614,13 @@ export function createSubscriptionPlan(body: {
|
||||
export function retireSubscriptionPlan(planId: string): Promise<{ planId: string; retired: boolean }> {
|
||||
return apiFetch(`/api/subscription-plans/${encodeURIComponent(planId)}/retire`, { method: "POST" });
|
||||
}
|
||||
export function reactivateSubscriptionPlan(planId: string): Promise<{ planId: string; reactivated: boolean }> {
|
||||
return apiFetch(`/api/subscription-plans/${encodeURIComponent(planId)}/reactivate`, { method: "POST" });
|
||||
}
|
||||
/** Delete a plan (all versions). Rejects (409 plan_in_use) if any subscription uses it. */
|
||||
export function deleteSubscriptionPlan(planId: string): Promise<{ planId: string; deleted: boolean }> {
|
||||
return apiFetch(`/api/subscription-plans/${encodeURIComponent(planId)}`, { method: "DELETE" });
|
||||
}
|
||||
/** Live quote for the sell form (server-computed; the operator can't override it). */
|
||||
export function quoteSubscription(body: {
|
||||
planId: string;
|
||||
|
||||
Reference in New Issue
Block a user