From b4ce8523b01633f4409d32383d65eaf7bf10c5e7 Mon Sep 17 00:00:00 2001 From: Till JS Date: Thu, 16 Apr 2026 12:51:31 +0200 Subject: [PATCH] feat(credits): merge subscription management into Credits & Abo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The standalone "Abonnement" workbench app (lib/modules/subscription/) duplicated billing functionality that should live alongside credits. Users saw two separate apps for the same domain. Replace the placeholder SubscriptionPage in the Abo tab with the real subscription management from the subscription module: - Current plan status with cancel/reactivate - Plan selection with billing interval toggle (monatlich/jährlich) - Stripe checkout integration - Invoice history (collapsed
) - Stripe billing portal link Delete: - lib/modules/subscription/ (merged into credits) - app-registry 'subscription' registration - CreditCard icon import (no longer used) The Credits & Abo app now has 5 tabs: 1. Übersicht — balance + recent transactions + quick-buy 2. Abo — current subscription + plan picker + invoices 3. Verlauf — full transaction table 4. Kaufen — one-time credit packages (Stripe) 5. Kosten — per-operation pricing breakdown Co-Authored-By: Claude Opus 4.6 (1M context) --- .../apps/web/src/lib/app-registry/apps.ts | 11 - .../src/lib/modules/credits/ListView.svelte | 736 +++++++++++++--- .../lib/modules/subscription/ListView.svelte | 792 ------------------ 3 files changed, 632 insertions(+), 907 deletions(-) delete mode 100644 apps/mana/apps/web/src/lib/modules/subscription/ListView.svelte diff --git a/apps/mana/apps/web/src/lib/app-registry/apps.ts b/apps/mana/apps/web/src/lib/app-registry/apps.ts index abb981a5b..cc61f2864 100644 --- a/apps/mana/apps/web/src/lib/app-registry/apps.ts +++ b/apps/mana/apps/web/src/lib/app-registry/apps.ts @@ -67,7 +67,6 @@ import { Key, Question, ChatCircleDots, - CreditCard, SquaresFour, Scroll, Spiral, @@ -1189,13 +1188,3 @@ registerApp({ list: { load: () => import('$lib/modules/feedback/ListView.svelte') }, }, }); - -registerApp({ - id: 'subscription', - name: 'Abonnement', - color: '#10B981', - icon: CreditCard, - views: { - list: { load: () => import('$lib/modules/subscription/ListView.svelte') }, - }, -}); diff --git a/apps/mana/apps/web/src/lib/modules/credits/ListView.svelte b/apps/mana/apps/web/src/lib/modules/credits/ListView.svelte index 89fba5632..1eed84f96 100644 --- a/apps/mana/apps/web/src/lib/modules/credits/ListView.svelte +++ b/apps/mana/apps/web/src/lib/modules/credits/ListView.svelte @@ -1,7 +1,7 @@
- {#each [{ key: 'overview', label: 'Übersicht' }, { key: 'subscriptions', label: 'Abonnements' }, { key: 'transactions', label: 'Transaktionen' }, { key: 'packages', label: 'Kaufen' }, { key: 'costs', label: 'Kosten' }] as tab} + {#each [{ key: 'overview', label: 'Übersicht' }, { key: 'subscriptions', label: 'Abo' }, { key: 'transactions', label: 'Verlauf' }, { key: 'packages', label: 'Kaufen' }, { key: 'costs', label: 'Kosten' }] as tab} +
+
+
+ Zeitraum + {sub.billingInterval === 'year' ? 'Jährlich' : 'Monatlich'} +
+
+ Periode + + {formatDateShort(sub.currentPeriodStart)} – {formatDateShort(sub.currentPeriodEnd)} + +
+
+ {#if sub.cancelAtPeriodEnd} + Endet am + {formatDateShort(sub.currentPeriodEnd)} + + {:else} + Verlängert am + {formatDateShort(sub.currentPeriodEnd)} + + {/if} +
+
+ + {:else} +
+
+ Free Plan + Aktuell +
+ 150 Mana / Monat +
+ {/if} + + +
+ + +
+ + +
+ {#each subPlans as plan} + {@const isCurrent = currentSub?.plan?.id === plan.id} + {@const price = + billingInterval === 'year' ? plan.priceYearlyEuroCents : plan.priceMonthlyEuroCents} + + {/each} +
+ + + {#if invoices.length > 0} +
+ Rechnungen ({invoices.length}) +
+ {#each invoices as inv} +
+
+ {inv.number || '-'} + {formatDateShort(inv.createdAt)} +
+
+ {formatPrice(inv.amountPaidEuroCents)} + + {inv.status === 'paid' ? 'Bezahlt' : inv.status} + + {#if inv.invoicePdfUrl} + PDF + {/if} +
+
+ {/each} +
+
+ {/if} {:else if activeTab === 'transactions'} @@ -485,9 +715,7 @@ {#if toastMessage} -
- {toastMessage} -
+
{toastMessage}
{/if}