mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-28 13:57:43 +02:00
Commit Message feat: implement comprehensive shared packages architecture for monorepo SUMMARY: Introduce 10 shared packages to unify common code across all 4 web apps, reducing ~3,000 lines of duplicated code and establishing consistent patterns for authentication, UI components, theming, and utilities. NEW SHARED PACKAGES: - @manacore/shared-auth: Unified auth logic (token management, JWT utils, fetch interceptor, storage/device/network adapters) - @manacore/shared-auth-ui: Reusable auth UI (LoginPage, RegisterPage, OAuth buttons for Google/Apple) - @manacore/shared-tailwind: Unified Tailwind config with 4 themes (lume, nature, stone, ocean) and light/dark mode support - @manacore/shared-icons: Phosphor-based icon library (40+ icons) - @manacore/shared-ui: Atomic design system (Text, Button, Badge, Toggle, Input, Modal) - @manacore/shared-i18n: Unified i18n setup with locale detection - @manacore/shared-config: Environment validation with Zod - @manacore/shared-subscriptio n-types: Subscription type definitions - @manacore/shared-subscriptio n-ui: Subscription UI components (planned) EXTENDED PACKAGES: - @manacore/shared-types: Added auth.ts, theme.ts, ui.ts, common.ts - @manacore/shared-utils: Added format.ts, validation.ts APP MIGRATIONS: - memoro/web: Migrated login (549→46 LOC), tailwind (165→12 LOC), removed 15+ duplicate components - manacore/web: Migrated to client-side auth with shared-auth, added new components (Icon, ThemeToggle, Logo) - manadeck/web: Replaced local authService/tokenManager with shared-auth, migrated auth pages - maerchenzauber/web: Added auth setup, stores, components, routes DELETED FILES (migrated to shared packages): - OAuth buttons (Google/Apple) from memoro, manacore, manadeck - Local authService, tokenManager, deviceManager, jwt utils - Duplicate Modal, Toggle, Text components - iconPaths and ManaIcon components - Subscription-related components (CostCard, PackageCard, etc.) BENEFITS: - 92% reduction in login page code - 93% reduction in tailwind config code - Consistent theming across all apps - Single source of truth for auth logic - Easier maintenance and updates BREAKING CHANGES: - Icon imports now from @manacore/shared-icons - Modal imports from @manacore/shared-ui - OAuth config via setGoogleCl ientId()/setAppleConfig()
This commit is contained in:
parent
725db638ea
commit
ef70a1af0b
198 changed files with 11113 additions and 3656 deletions
134
packages/shared-subscription-ui/src/SubscriptionCard.svelte
Normal file
134
packages/shared-subscription-ui/src/SubscriptionCard.svelte
Normal file
|
|
@ -0,0 +1,134 @@
|
|||
<script lang="ts">
|
||||
import type { SubscriptionPlan } from '@manacore/shared-subscription-types';
|
||||
import SubscriptionButton from './SubscriptionButton.svelte';
|
||||
import ManaIcon from './ManaIcon.svelte';
|
||||
|
||||
interface Props {
|
||||
plan: SubscriptionPlan;
|
||||
onSelect: (planId: string) => void;
|
||||
isCurrentPlan?: boolean;
|
||||
isLegacy?: boolean;
|
||||
// i18n labels
|
||||
currentPlanLabel?: string;
|
||||
legacyPlanLabel?: string;
|
||||
popularLabel?: string;
|
||||
perMonthLabel?: string;
|
||||
perYearLabel?: string;
|
||||
monthlyEquivalentLabel?: string;
|
||||
buyLabel?: string;
|
||||
yourPlanLabel?: string;
|
||||
yourLegacyPlanLabel?: string;
|
||||
}
|
||||
|
||||
let {
|
||||
plan,
|
||||
onSelect,
|
||||
isCurrentPlan = false,
|
||||
isLegacy = false,
|
||||
currentPlanLabel = 'Current Plan',
|
||||
legacyPlanLabel = 'Legacy Plan',
|
||||
popularLabel = 'Popular',
|
||||
perMonthLabel = 'pro Monat',
|
||||
perYearLabel = 'pro Jahr',
|
||||
monthlyEquivalentLabel = '/Monat',
|
||||
buyLabel = 'Kaufen',
|
||||
yourPlanLabel = 'Dein Plan',
|
||||
yourLegacyPlanLabel = 'Dein Legacy-Plan'
|
||||
}: Props = $props();
|
||||
|
||||
function formatPrice(plan: SubscriptionPlan) {
|
||||
return plan.priceString || `${plan.price.toFixed(2).replace('.', ',')}€`;
|
||||
}
|
||||
|
||||
// Tier-specific background colors and sizes for Mana icon
|
||||
function getTierStyles() {
|
||||
const id = plan.id.toLowerCase();
|
||||
if (id.includes('free')) return { bg: '#F5F5F5', icon: '#9E9E9E', bgSize: '30%' };
|
||||
if (id.includes('small')) return { bg: '#E3F2FD', icon: '#2196F3', bgSize: '45%' };
|
||||
if (id.includes('medium')) return { bg: '#BBDEFB', icon: '#1976D2', bgSize: '60%' };
|
||||
if (id.includes('large')) return { bg: '#90CAF9', icon: '#1565C0', bgSize: '75%' };
|
||||
if (id.includes('giant')) return { bg: '#64B5F6', icon: '#0D47A1', bgSize: '90%' };
|
||||
return { bg: '#E1F5FE', icon: '#0288D1', bgSize: '50%' };
|
||||
}
|
||||
|
||||
const tierStyles = $derived(getTierStyles());
|
||||
|
||||
// Hover state
|
||||
let isHovered = $state(false);
|
||||
</script>
|
||||
|
||||
<div
|
||||
class="relative rounded-xl p-4 transition-all duration-200 bg-content border hover:-translate-y-0.5 hover:shadow-lg"
|
||||
class:border-2={isCurrentPlan}
|
||||
class:border-mana={isCurrentPlan || plan.popular}
|
||||
class:border-theme={!isCurrentPlan && !plan.popular}
|
||||
onmouseenter={() => (isHovered = true)}
|
||||
onmouseleave={() => (isHovered = false)}
|
||||
>
|
||||
{#if isCurrentPlan}
|
||||
<div class="absolute -top-3 left-4 rounded-xl bg-mana px-3 py-1 text-xs font-bold text-white">
|
||||
{isLegacy ? legacyPlanLabel : currentPlanLabel}
|
||||
</div>
|
||||
{/if}
|
||||
{#if plan.popular && !isCurrentPlan}
|
||||
<div class="absolute -top-3 right-4 rounded-xl bg-mana px-3 py-1 text-xs font-bold text-white">
|
||||
{popularLabel}
|
||||
</div>
|
||||
{/if}
|
||||
|
||||
<!-- Tier Name -->
|
||||
<h3 class="mb-4 text-center text-lg font-bold text-theme">
|
||||
{plan.name}
|
||||
</h3>
|
||||
|
||||
<!-- Three column layout -->
|
||||
<div class="mb-5 flex justify-between gap-2">
|
||||
<!-- Mana Icon with background -->
|
||||
<div class="flex aspect-square flex-1 items-center justify-center rounded-xl bg-menu" style="min-height: 80px;">
|
||||
<div
|
||||
class="flex items-center justify-center rounded-lg"
|
||||
style="width: {tierStyles.bgSize}; height: {tierStyles.bgSize}; background-color: {tierStyles.bg};"
|
||||
>
|
||||
<ManaIcon size={32} color={tierStyles.icon} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mana Amount -->
|
||||
<div class="flex aspect-square flex-1 flex-col items-center justify-center rounded-xl bg-menu" style="min-height: 80px;">
|
||||
<p class="mb-0.5 text-2xl font-bold text-theme">
|
||||
{plan.monthlyMana}
|
||||
</p>
|
||||
<p class="text-center text-xs text-theme-secondary">{perMonthLabel}</p>
|
||||
</div>
|
||||
|
||||
<!-- Price -->
|
||||
<div class="flex aspect-square flex-1 flex-col items-center justify-center rounded-xl bg-menu" style="min-height: 80px;">
|
||||
<p class="text-xl font-bold text-theme">
|
||||
{formatPrice(plan)}
|
||||
</p>
|
||||
<p class="mt-0.5 text-xs text-theme-secondary">
|
||||
{plan.billingCycle === 'yearly' ? perYearLabel : perMonthLabel}
|
||||
</p>
|
||||
{#if plan.billingCycle === 'yearly' && plan.monthlyEquivalent}
|
||||
<p class="mt-0 text-[9px] text-theme-secondary">
|
||||
({plan.monthlyEquivalent.toFixed(2).replace('.', ',')}€{monthlyEquivalentLabel})
|
||||
</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Button only show if NOT free plan -->
|
||||
{#if !plan.id.toLowerCase().includes('free')}
|
||||
<SubscriptionButton
|
||||
label={isCurrentPlan
|
||||
? isLegacy
|
||||
? yourLegacyPlanLabel
|
||||
: yourPlanLabel
|
||||
: buyLabel}
|
||||
onclick={() => onSelect(plan.id)}
|
||||
iconName={isCurrentPlan ? 'checkmark-circle-outline' : 'arrow-forward-outline'}
|
||||
variant={isCurrentPlan ? 'secondary' : plan.popular ? 'accent' : 'primary'}
|
||||
disabled={isCurrentPlan}
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue