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:
Till-JS 2025-11-24 21:09:20 +01:00
parent 725db638ea
commit ef70a1af0b
198 changed files with 11113 additions and 3656 deletions

View file

@ -0,0 +1,71 @@
<script lang="ts">
import type { HTMLInputAttributes } from 'svelte/elements';
interface Props {
value: string;
oninput?: (value: string) => void;
onchange?: (value: string) => void;
label?: string;
placeholder?: string;
type?: 'text' | 'email' | 'password' | 'number' | 'tel' | 'url';
error?: string;
disabled?: boolean;
required?: boolean;
autocomplete?: HTMLInputAttributes['autocomplete'];
class?: string;
}
let {
value = $bindable(),
oninput,
onchange,
label,
placeholder,
type = 'text',
error,
disabled = false,
required = false,
autocomplete,
class: className = ''
}: Props = $props();
function handleInput(e: Event) {
const target = e.target as HTMLInputElement;
value = target.value;
oninput?.(target.value);
}
function handleChange(e: Event) {
const target = e.target as HTMLInputElement;
onchange?.(target.value);
}
</script>
<div class="flex flex-col gap-1.5 {className}">
{#if label}
<label class="text-sm font-medium text-theme">
{label}
{#if required}
<span class="text-red-500">*</span>
{/if}
</label>
{/if}
<input
{type}
{value}
{placeholder}
{disabled}
{required}
autocomplete={autocomplete as HTMLInputAttributes['autocomplete']}
oninput={handleInput}
onchange={handleChange}
class="w-full rounded-lg border px-4 py-2.5 text-theme bg-content transition-colors focus:outline-none focus:ring-2 focus:ring-primary/50 disabled:opacity-50 disabled:cursor-not-allowed {error
? 'border-red-500 focus:ring-red-500/50'
: 'border-theme'}"
/>
{#if error}
<p class="text-sm text-red-500">{error}</p>
{/if}
</div>

View file

@ -0,0 +1,37 @@
<script lang="ts">
interface Props {
isOn: boolean;
onToggle: (value: boolean) => void;
disabled?: boolean;
size?: 'sm' | 'md';
}
let { isOn = false, onToggle, disabled = false, size = 'md' }: Props = $props();
function handleToggle() {
if (!disabled) {
onToggle(!isOn);
}
}
const sizeClasses = {
sm: { track: 'h-6 w-10', thumb: 'h-4 w-4 top-1 left-1', translate: 'translate-x-4' },
md: { track: 'h-8 w-14', thumb: 'h-6 w-6 top-1 left-1', translate: 'translate-x-6' }
};
</script>
<button
onclick={handleToggle}
class="relative {sizeClasses[size].track} flex-shrink-0 rounded-full transition-colors {isOn
? 'bg-primary'
: 'bg-menu'} {disabled ? 'cursor-not-allowed opacity-50' : 'cursor-pointer'}"
role="switch"
aria-checked={isOn}
{disabled}
>
<span
class="absolute {sizeClasses[size].thumb} rounded-full bg-white shadow-md transition-transform {isOn
? sizeClasses[size].translate
: 'translate-x-0'}"
></span>
</button>

View file

@ -0,0 +1,2 @@
export { default as Toggle } from './Toggle.svelte';
export { default as Input } from './Input.svelte';