mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-19 19:21:25 +02:00
SUMMARY:
Consolidate shared UI components into @manacore/shared-ui and add
AppSlider to all login screens for a consistent Mana ecosystem experience.
CHANGES:
1. UI Components Migration:
- Added Card.svelte to @manacore/shared-ui with variants (elevated, outlined, ghost)
- Migrated Manacore (7 files) and Manadeck (7 files) to use shared-ui
- Removed local ui/ directories from both apps (8 components total)
2. AppSlider Unification:
- Created shared AppSlider in @manacore/shared-ui with configurable props
- Props: apps[], title, isDark, statusLabels, comingSoonLabel, openAppLabel, onAppClick
- Supports both i18n and static text configurations
- Updated Memoro AppSlider to use shared component with svelte-i18n
- Updated Manacore AppSlider to use shared component
- Created new AppSlider for ManaDeck and Märchenzauber
3. Login Page Enhancements:
- Extended LoginPage in @manacore/shared-auth-ui with new snippets:
- appSlider: Renders AppSlider at bottom (initial mode only)
- headerControls: Renders controls (theme toggle, etc.) top-right
- Updated all app login pages to include AppSlider:
- ManaCore: indigo theme (#6366f1)
- ManaDeck: violet theme (#8b5cf6)
- Märchenzauber: pink theme (#FF6B9D)
4. Subscription Page Consolidation:
- Created SubscriptionPage component in @manacore/shared-subscription-ui
- Moved subscription data (plans, packages, costs) to shared package
- Reduced subscription page code from ~100 to ~18 lines per app
FILES CHANGED:
- packages/shared-ui: Added Card, AppSlider, updated exports
- packages/shared-auth-ui: Extended LoginPage with snippets
- packages/shared-subscription-ui: Added SubscriptionPage, data files
- manacore/web: Migrated 7 files to shared-ui, updated login
- manadeck/web: Migrated 7 files to shared-ui, added AppSlider, updated login
- maerchenzauber/web: Added AppSlider, updated login
- memoro/web: Updated AppSlider to use shared component
DELETED (moved to shared packages):
- manacore/web/src/lib/components/ui/* (3 files)
- manadeck/web/src/lib/components/ui/* (5 files)
- memoro/web/src/lib/data/*.json (3 files)
- Various pnpm-lock.yaml and pnpm-workspace.yaml files
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-Authored-By: Claude <noreply@anthropic.com>
138 lines
4.3 KiB
Svelte
138 lines
4.3 KiB
Svelte
<script lang="ts">
|
|
import type { SubscriptionPlan, ManaPackage, UsageData, CostItem, BillingCycle } from '@manacore/shared-subscription-types';
|
|
import BillingToggle from '../BillingToggle.svelte';
|
|
import SubscriptionCard from '../SubscriptionCard.svelte';
|
|
import PackageCard from '../PackageCard.svelte';
|
|
import UsageCard from '../UsageCard.svelte';
|
|
import CostCard from '../CostCard.svelte';
|
|
|
|
// Import default data
|
|
import defaultSubscriptionData from '../data/subscriptionData.json';
|
|
import defaultAppCosts from '../data/appCosts.json';
|
|
import defaultUsageData from '../data/defaultUsageData.json';
|
|
|
|
interface Props {
|
|
/** App name for the page title */
|
|
appName: string;
|
|
/** Handler when user selects a subscription plan */
|
|
onSubscribe: (planId: string) => void;
|
|
/** Handler when user selects a mana package */
|
|
onBuyPackage: (packageId: string) => void;
|
|
/** Current plan ID (e.g., 'free', 'Mana_Stream_Small_v1') */
|
|
currentPlanId?: string;
|
|
/** Current user's usage data (optional, uses defaults if not provided) */
|
|
usageData?: UsageData;
|
|
/** Custom subscription plans (optional, uses defaults if not provided) */
|
|
subscriptions?: SubscriptionPlan[];
|
|
/** Custom mana packages (optional, uses defaults if not provided) */
|
|
packages?: ManaPackage[];
|
|
/** Custom cost items (optional, uses defaults if not provided) */
|
|
costs?: CostItem[];
|
|
/** Page title */
|
|
pageTitle?: string;
|
|
/** Subscriptions section title */
|
|
subscriptionsTitle?: string;
|
|
/** One-time purchases section title */
|
|
packagesTitle?: string;
|
|
/** Yearly discount label */
|
|
yearlyDiscount?: string;
|
|
}
|
|
|
|
let {
|
|
appName,
|
|
onSubscribe,
|
|
onBuyPackage,
|
|
currentPlanId = 'free',
|
|
usageData = defaultUsageData.usage as UsageData,
|
|
subscriptions = defaultSubscriptionData.subscriptions as SubscriptionPlan[],
|
|
packages = defaultSubscriptionData.packages as ManaPackage[],
|
|
costs = defaultAppCosts.costs as CostItem[],
|
|
pageTitle = 'Mana kaufen',
|
|
subscriptionsTitle = 'Abonnements',
|
|
packagesTitle = 'Einmalkäufe',
|
|
yearlyDiscount = '33%'
|
|
}: Props = $props();
|
|
|
|
// State
|
|
let billingCycle = $state<BillingCycle>('monthly');
|
|
|
|
// Get current plan name for display
|
|
const currentPlanName = $derived(() => {
|
|
const plan = subscriptions.find(p => p.id === currentPlanId);
|
|
return plan?.name || 'Free';
|
|
});
|
|
|
|
// Get all subscription plans for current billing cycle
|
|
function getSubscriptionPlans() {
|
|
return subscriptions.filter(
|
|
(plan) => plan.id !== 'free' && plan.billingCycle === billingCycle
|
|
);
|
|
}
|
|
|
|
// Check if a plan is the current plan
|
|
function isCurrentPlan(planId: string) {
|
|
if (currentPlanId === 'free' && planId === 'free') return true;
|
|
return planId === currentPlanId;
|
|
}
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>Mana - {appName}</title>
|
|
</svelte:head>
|
|
|
|
<div class="flex h-full flex-col">
|
|
<!-- Content Area -->
|
|
<div class="flex-1 overflow-y-auto">
|
|
<div class="mx-auto max-w-5xl pb-12">
|
|
<h1 class="mb-8 text-3xl font-bold text-theme">{pageTitle}</h1>
|
|
|
|
<!-- Active Section (Usage & Costs) -->
|
|
<section class="mb-8">
|
|
<div class="mb-4">
|
|
<UsageCard {usageData} currentPlan={currentPlanName()} />
|
|
</div>
|
|
|
|
<div class="mb-4">
|
|
<CostCard {costs} />
|
|
</div>
|
|
</section>
|
|
|
|
<!-- Billing Toggle -->
|
|
<BillingToggle {billingCycle} onChange={(cycle: BillingCycle) => (billingCycle = cycle)} {yearlyDiscount} />
|
|
|
|
<!-- Subscriptions Section -->
|
|
<section class="mb-12 pt-2">
|
|
<h2 class="mb-6 text-2xl font-bold text-theme">{subscriptionsTitle}</h2>
|
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 xl:grid-cols-3">
|
|
<!-- Free Tier -->
|
|
<SubscriptionCard
|
|
plan={subscriptions.find((plan) => plan.id === 'free')!}
|
|
onSelect={onSubscribe}
|
|
isCurrentPlan={isCurrentPlan('free')}
|
|
/>
|
|
|
|
<!-- All Paid Subscriptions -->
|
|
{#each getSubscriptionPlans() as plan}
|
|
<SubscriptionCard
|
|
{plan}
|
|
onSelect={onSubscribe}
|
|
isCurrentPlan={isCurrentPlan(plan.id)}
|
|
/>
|
|
{/each}
|
|
</div>
|
|
</section>
|
|
|
|
<!-- One-time Purchases Section -->
|
|
<section class="mb-12">
|
|
<h2 class="mb-6 text-2xl font-bold text-theme">{packagesTitle}</h2>
|
|
|
|
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
{#each packages as pkg}
|
|
<PackageCard package={pkg} onSelect={onBuyPackage} />
|
|
{/each}
|
|
</div>
|
|
</section>
|
|
</div>
|
|
</div>
|
|
</div>
|