mirror of
https://github.com/Memo-2023/mana-monorepo.git
synced 2026-05-26 09:14:39 +02:00
feat(mana-games): add auth, settings, themes, help, submit, and onboarding pages
Adds login/register/forgot-password auth routes using shared-auth-ui, settings page with theme/language/account controls, themes browser, help page, community submit form, profile, and app onboarding modal. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
732f2b8edd
commit
0806600bc0
14 changed files with 602 additions and 0 deletions
|
|
@ -0,0 +1,41 @@
|
||||||
|
import { createAppOnboardingStore, type AppOnboardingStep } from '@manacore/shared-app-onboarding';
|
||||||
|
import { userSettings } from './user-settings.svelte';
|
||||||
|
|
||||||
|
const onboardingSteps: AppOnboardingStep[] = [
|
||||||
|
{
|
||||||
|
id: 'features',
|
||||||
|
type: 'info',
|
||||||
|
question: 'Willkommen bei Mana Games!',
|
||||||
|
description: 'Das erwartet dich:',
|
||||||
|
emoji: '🎮',
|
||||||
|
gradient: { from: 'green-500', to: 'green-700' },
|
||||||
|
bullets: [
|
||||||
|
'22+ Browser-Spiele direkt spielbar',
|
||||||
|
'KI-Spielgenerator: Erstelle eigene Games',
|
||||||
|
'Statistiken: Highscores & Spielzeit',
|
||||||
|
'Community: Reiche eigene Spiele ein',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'welcome',
|
||||||
|
type: 'info',
|
||||||
|
question: "Los geht's!",
|
||||||
|
description: 'Tipps:',
|
||||||
|
emoji: '🕹️',
|
||||||
|
gradient: { from: 'primary', to: 'primary/70' },
|
||||||
|
bullets: [
|
||||||
|
'Cmd/Ctrl+K für Schnellsuche',
|
||||||
|
'Spiele laufen komplett im Browser',
|
||||||
|
'Stats werden lokal gespeichert',
|
||||||
|
'Anmelden synchronisiert deine Daten',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
export const gamesOnboarding = createAppOnboardingStore({
|
||||||
|
appId: 'mana-games',
|
||||||
|
steps: onboardingSteps,
|
||||||
|
userSettings,
|
||||||
|
onComplete: async () => {},
|
||||||
|
onSkip: async () => {},
|
||||||
|
});
|
||||||
|
|
@ -26,6 +26,8 @@
|
||||||
import { setLocale, supportedLocales } from '$lib/i18n';
|
import { setLocale, supportedLocales } from '$lib/i18n';
|
||||||
import { SessionExpiredBanner, AuthGate, GuestWelcomeModal } from '@manacore/shared-auth-ui';
|
import { SessionExpiredBanner, AuthGate, GuestWelcomeModal } from '@manacore/shared-auth-ui';
|
||||||
import { shouldShowGuestWelcome } from '@manacore/shared-auth-ui';
|
import { shouldShowGuestWelcome } from '@manacore/shared-auth-ui';
|
||||||
|
import { gamesOnboarding } from '$lib/stores/app-onboarding.svelte';
|
||||||
|
import { MiniOnboardingModal } from '@manacore/shared-app-onboarding';
|
||||||
import { gamesStore } from '$lib/data/local-store';
|
import { gamesStore } from '$lib/data/local-store';
|
||||||
import {
|
import {
|
||||||
tagLocalStore,
|
tagLocalStore,
|
||||||
|
|
@ -241,6 +243,10 @@
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{#if gamesOnboarding.shouldShow}
|
||||||
|
<MiniOnboardingModal store={gamesOnboarding} appName="Mana Games" appEmoji="🎮" />
|
||||||
|
{/if}
|
||||||
|
|
||||||
<GuestWelcomeModal
|
<GuestWelcomeModal
|
||||||
appId="mana-games"
|
appId="mana-games"
|
||||||
visible={showGuestWelcome}
|
visible={showGuestWelcome}
|
||||||
|
|
|
||||||
47
games/mana-games/apps/web/src/routes/(app)/help/+page.svelte
Normal file
47
games/mana-games/apps/web/src/routes/(app)/help/+page.svelte
Normal file
|
|
@ -0,0 +1,47 @@
|
||||||
|
<svelte:head>
|
||||||
|
<title>Hilfe - Mana Games</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="max-w-2xl mx-auto space-y-6">
|
||||||
|
<h1 class="text-2xl font-bold text-foreground">Hilfe</h1>
|
||||||
|
|
||||||
|
<section class="space-y-4">
|
||||||
|
<div class="rounded-xl border border-border bg-card p-4">
|
||||||
|
<h2 class="font-semibold text-foreground mb-2">Wie spiele ich?</h2>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
Wähle ein Spiel auf der Startseite aus und klicke darauf. Das Spiel läuft direkt im Browser.
|
||||||
|
Die Steuerung wird auf der Spielseite angezeigt.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-xl border border-border bg-card p-4">
|
||||||
|
<h2 class="font-semibold text-foreground mb-2">KI-Spielgenerator</h2>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
Unter "Erstellen" kannst du eigene Spiele beschreiben und von verschiedenen KI-Modellen
|
||||||
|
generieren lassen. Generierte Spiele werden lokal in deinem Browser gespeichert.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-xl border border-border bg-card p-4">
|
||||||
|
<h2 class="font-semibold text-foreground mb-2">Statistiken</h2>
|
||||||
|
<p class="text-sm text-muted-foreground">
|
||||||
|
Deine Highscores, Spielzeiten und Fortschritte werden automatisch gespeichert. Melde dich
|
||||||
|
an, um sie geräteübergreifend zu synchronisieren.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="rounded-xl border border-border bg-card p-4">
|
||||||
|
<h2 class="font-semibold text-foreground mb-2">Tastaturkürzel</h2>
|
||||||
|
<div class="grid grid-cols-2 gap-2 mt-2">
|
||||||
|
{#each [['Cmd/Ctrl+K', 'Schnellsuche'], ['Esc', 'Suche schließen']] as [key, desc]}
|
||||||
|
<div class="flex items-center gap-2">
|
||||||
|
<kbd class="px-2 py-0.5 rounded bg-muted text-xs font-mono text-muted-foreground"
|
||||||
|
>{key}</kbd
|
||||||
|
>
|
||||||
|
<span class="text-sm text-foreground">{desc}</span>
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<svelte:head>
|
||||||
|
<title>Mana - Mana Games</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="max-w-2xl mx-auto text-center py-12">
|
||||||
|
<p class="text-4xl mb-4">💎</p>
|
||||||
|
<h1 class="text-2xl font-bold text-foreground">Mana</h1>
|
||||||
|
<p class="text-muted-foreground mt-2">Demnächst verfügbar.</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { ProfilePage } from '@manacore/shared-profile-ui';
|
||||||
|
import { authStore } from '$lib/stores/auth.svelte';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Profil - Mana Games</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
{#if authStore.isAuthenticated}
|
||||||
|
<ProfilePage {authStore} {goto} />
|
||||||
|
{:else}
|
||||||
|
<div class="max-w-2xl mx-auto text-center py-12">
|
||||||
|
<p class="text-muted-foreground">Bitte melde dich an.</p>
|
||||||
|
<a href="/login" class="text-primary hover:underline mt-2 inline-block">Anmelden</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
165
games/mana-games/apps/web/src/routes/(app)/settings/+page.svelte
Normal file
165
games/mana-games/apps/web/src/routes/(app)/settings/+page.svelte
Normal file
|
|
@ -0,0 +1,165 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { _ } from 'svelte-i18n';
|
||||||
|
import { locale } from 'svelte-i18n';
|
||||||
|
import { theme } from '$lib/stores/theme.svelte';
|
||||||
|
import { authStore } from '$lib/stores/auth.svelte';
|
||||||
|
import { setLocale, supportedLocales } from '$lib/i18n';
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { gameStatsCollection } from '$lib/data/local-store';
|
||||||
|
|
||||||
|
async function clearStats() {
|
||||||
|
const all = await gameStatsCollection.getAll();
|
||||||
|
for (const stat of all) {
|
||||||
|
await gameStatsCollection.remove(stat.id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleLogout() {
|
||||||
|
await authStore.signOut();
|
||||||
|
goto('/login');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>{$_('nav.settings')} - Mana Games</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="settings-page">
|
||||||
|
<header class="mb-8">
|
||||||
|
<h1 class="text-2xl font-bold text-foreground">{$_('nav.settings')}</h1>
|
||||||
|
<p class="text-muted-foreground text-sm mt-1">Passe Mana Games an deine Bedürfnisse an</p>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<!-- Theme -->
|
||||||
|
<section class="settings-section">
|
||||||
|
<h2 class="text-lg font-bold text-foreground mb-4">Darstellung</h2>
|
||||||
|
|
||||||
|
<div class="setting-row">
|
||||||
|
<div>
|
||||||
|
<div class="setting-label">Farbmodus</div>
|
||||||
|
<div class="setting-desc">Hell, Dunkel oder System</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex gap-1">
|
||||||
|
{#each ['light', 'dark', 'system'] as mode}
|
||||||
|
<button
|
||||||
|
class="px-3 py-1.5 text-sm rounded-lg transition-colors {theme.mode === mode
|
||||||
|
? 'bg-primary text-primary-foreground'
|
||||||
|
: 'bg-muted text-muted-foreground hover:bg-muted/80'}"
|
||||||
|
onclick={() => theme.setMode(mode as 'light' | 'dark' | 'system')}
|
||||||
|
>
|
||||||
|
{mode === 'light' ? 'Hell' : mode === 'dark' ? 'Dunkel' : 'System'}
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Language -->
|
||||||
|
<section class="settings-section">
|
||||||
|
<h2 class="text-lg font-bold text-foreground mb-4">Sprache</h2>
|
||||||
|
|
||||||
|
<div class="setting-row">
|
||||||
|
<div>
|
||||||
|
<div class="setting-label">App-Sprache</div>
|
||||||
|
<div class="setting-desc">Sprache der Benutzeroberfläche</div>
|
||||||
|
</div>
|
||||||
|
<select
|
||||||
|
value={$locale}
|
||||||
|
onchange={(e) => setLocale((e.target as HTMLSelectElement).value as any)}
|
||||||
|
class="h-9 px-3 rounded-lg bg-background border border-border text-foreground text-sm"
|
||||||
|
>
|
||||||
|
{#each supportedLocales as loc}
|
||||||
|
<option value={loc}>{loc === 'de' ? 'Deutsch' : 'English'}</option>
|
||||||
|
{/each}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Account -->
|
||||||
|
<section class="settings-section">
|
||||||
|
<h2 class="text-lg font-bold text-foreground mb-4">Konto</h2>
|
||||||
|
|
||||||
|
{#if authStore.isAuthenticated}
|
||||||
|
<div class="setting-row">
|
||||||
|
<div>
|
||||||
|
<div class="setting-label">Eingeloggt als</div>
|
||||||
|
<div class="setting-desc">{authStore.user?.email}</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="px-4 py-2 rounded-lg bg-red-500/10 text-red-400 hover:bg-red-500/20 transition-colors text-sm"
|
||||||
|
onclick={handleLogout}
|
||||||
|
>
|
||||||
|
Abmelden
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<div class="setting-row">
|
||||||
|
<div>
|
||||||
|
<div class="setting-label">Gast-Modus</div>
|
||||||
|
<div class="setting-desc">Melde dich an, um Stats zu synchronisieren</div>
|
||||||
|
</div>
|
||||||
|
<a
|
||||||
|
href="/login"
|
||||||
|
class="px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors text-sm"
|
||||||
|
>
|
||||||
|
Anmelden
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- Data -->
|
||||||
|
<section class="settings-section">
|
||||||
|
<h2 class="text-lg font-bold text-foreground mb-4">Daten</h2>
|
||||||
|
|
||||||
|
<div class="setting-row">
|
||||||
|
<div>
|
||||||
|
<div class="setting-label">Spielstatistiken löschen</div>
|
||||||
|
<div class="setting-desc">Alle Highscores und Spielzeiten zurücksetzen</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
class="px-4 py-2 rounded-lg bg-red-500/10 text-red-400 hover:bg-red-500/20 transition-colors text-sm"
|
||||||
|
onclick={clearStats}
|
||||||
|
>
|
||||||
|
Löschen
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.settings-page {
|
||||||
|
max-width: 600px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section {
|
||||||
|
margin-bottom: 2rem;
|
||||||
|
padding-bottom: 2rem;
|
||||||
|
border-bottom: 1px solid hsl(var(--border));
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-section:last-child {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
gap: 1rem;
|
||||||
|
padding: 0.75rem 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-label {
|
||||||
|
font-size: 0.875rem;
|
||||||
|
font-weight: 500;
|
||||||
|
color: hsl(var(--foreground));
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-desc {
|
||||||
|
font-size: 0.75rem;
|
||||||
|
color: hsl(var(--muted-foreground));
|
||||||
|
margin-top: 2px;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
202
games/mana-games/apps/web/src/routes/(app)/submit/+page.svelte
Normal file
202
games/mana-games/apps/web/src/routes/(app)/submit/+page.svelte
Normal file
|
|
@ -0,0 +1,202 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { _ } from 'svelte-i18n';
|
||||||
|
import { authStore } from '$lib/stores/auth.svelte';
|
||||||
|
|
||||||
|
const BACKEND_URL = import.meta.env.DEV
|
||||||
|
? 'http://localhost:3011'
|
||||||
|
: import.meta.env.PUBLIC_MANA_GAMES_BACKEND_URL || '';
|
||||||
|
|
||||||
|
let title = $state('');
|
||||||
|
let description = $state('');
|
||||||
|
let controls = $state('');
|
||||||
|
let difficulty = $state<'Einfach' | 'Mittel' | 'Schwer'>('Mittel');
|
||||||
|
let tags = $state('');
|
||||||
|
let htmlCode = $state('');
|
||||||
|
let authorName = $state('');
|
||||||
|
let isSubmitting = $state(false);
|
||||||
|
let submitResult = $state<{ success: boolean; message: string } | null>(null);
|
||||||
|
|
||||||
|
async function handleSubmit() {
|
||||||
|
if (!title.trim() || !htmlCode.trim() || !authorName.trim()) return;
|
||||||
|
|
||||||
|
isSubmitting = true;
|
||||||
|
submitResult = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await fetch(`${BACKEND_URL}/api/games/submit`, {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({
|
||||||
|
title,
|
||||||
|
description,
|
||||||
|
controls,
|
||||||
|
difficulty,
|
||||||
|
complexity: 'Mittel',
|
||||||
|
tags: tags
|
||||||
|
.split(',')
|
||||||
|
.map((t) => t.trim())
|
||||||
|
.filter(Boolean),
|
||||||
|
author: { name: authorName },
|
||||||
|
files: { html: htmlCode },
|
||||||
|
submittedAt: new Date().toISOString(),
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
|
||||||
|
const data = await response.json();
|
||||||
|
submitResult = {
|
||||||
|
success: data.success,
|
||||||
|
message: data.success
|
||||||
|
? `Eingereicht! PR #${data.prNumber} erstellt.`
|
||||||
|
: data.error || 'Fehler beim Einreichen.',
|
||||||
|
};
|
||||||
|
|
||||||
|
if (data.success) {
|
||||||
|
title = '';
|
||||||
|
description = '';
|
||||||
|
controls = '';
|
||||||
|
tags = '';
|
||||||
|
htmlCode = '';
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
submitResult = { success: false, message: 'Verbindungsfehler zum Backend.' };
|
||||||
|
} finally {
|
||||||
|
isSubmitting = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Spiel einreichen - Mana Games</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="max-w-2xl mx-auto space-y-6">
|
||||||
|
<div>
|
||||||
|
<h1 class="text-2xl font-bold text-foreground">Spiel einreichen</h1>
|
||||||
|
<p class="text-muted-foreground mt-1">Reiche dein eigenes HTML5-Spiel bei der Community ein.</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if !authStore.isAuthenticated}
|
||||||
|
<div class="rounded-xl border border-border bg-card p-6 text-center">
|
||||||
|
<p class="text-muted-foreground mb-4">Bitte melde dich an, um ein Spiel einzureichen.</p>
|
||||||
|
<a
|
||||||
|
href="/login"
|
||||||
|
class="inline-block px-4 py-2 rounded-lg bg-primary text-primary-foreground hover:bg-primary/90 transition-colors"
|
||||||
|
>
|
||||||
|
Anmelden
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
<form
|
||||||
|
onsubmit={(e) => {
|
||||||
|
e.preventDefault();
|
||||||
|
handleSubmit();
|
||||||
|
}}
|
||||||
|
class="space-y-4"
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<label for="title" class="block text-sm font-medium text-foreground mb-1">Titel *</label>
|
||||||
|
<input
|
||||||
|
id="title"
|
||||||
|
type="text"
|
||||||
|
bind:value={title}
|
||||||
|
required
|
||||||
|
class="w-full rounded-lg border border-border bg-background px-4 py-2 text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="author" class="block text-sm font-medium text-foreground mb-1">Autor *</label>
|
||||||
|
<input
|
||||||
|
id="author"
|
||||||
|
type="text"
|
||||||
|
bind:value={authorName}
|
||||||
|
required
|
||||||
|
class="w-full rounded-lg border border-border bg-background px-4 py-2 text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="desc" class="block text-sm font-medium text-foreground mb-1">Beschreibung</label
|
||||||
|
>
|
||||||
|
<textarea
|
||||||
|
id="desc"
|
||||||
|
bind:value={description}
|
||||||
|
rows="3"
|
||||||
|
class="w-full rounded-lg border border-border bg-background px-4 py-2 text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50 resize-none"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 gap-4">
|
||||||
|
<div>
|
||||||
|
<label for="controls" class="block text-sm font-medium text-foreground mb-1"
|
||||||
|
>Steuerung</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="controls"
|
||||||
|
type="text"
|
||||||
|
bind:value={controls}
|
||||||
|
placeholder="Pfeiltasten, Maus..."
|
||||||
|
class="w-full rounded-lg border border-border bg-background px-4 py-2 text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<label for="difficulty" class="block text-sm font-medium text-foreground mb-1"
|
||||||
|
>Schwierigkeit</label
|
||||||
|
>
|
||||||
|
<select
|
||||||
|
id="difficulty"
|
||||||
|
bind:value={difficulty}
|
||||||
|
class="w-full rounded-lg border border-border bg-background px-4 py-2 text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50"
|
||||||
|
>
|
||||||
|
<option value="Einfach">Einfach</option>
|
||||||
|
<option value="Mittel">Mittel</option>
|
||||||
|
<option value="Schwer">Schwer</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="tags" class="block text-sm font-medium text-foreground mb-1"
|
||||||
|
>Tags (kommagetrennt)</label
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
id="tags"
|
||||||
|
type="text"
|
||||||
|
bind:value={tags}
|
||||||
|
placeholder="Arcade, Action, Puzzle"
|
||||||
|
class="w-full rounded-lg border border-border bg-background px-4 py-2 text-foreground focus:outline-none focus:ring-2 focus:ring-primary/50"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<label for="html" class="block text-sm font-medium text-foreground mb-1">HTML-Code *</label>
|
||||||
|
<textarea
|
||||||
|
id="html"
|
||||||
|
bind:value={htmlCode}
|
||||||
|
rows="12"
|
||||||
|
required
|
||||||
|
placeholder="<!DOCTYPE html>..."
|
||||||
|
class="w-full rounded-lg border border-border bg-background px-4 py-2 text-foreground font-mono text-sm focus:outline-none focus:ring-2 focus:ring-primary/50 resize-none"
|
||||||
|
></textarea>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{#if submitResult}
|
||||||
|
<div
|
||||||
|
class="rounded-lg border p-3 text-sm {submitResult.success
|
||||||
|
? 'border-green-500/30 bg-green-500/10 text-green-400'
|
||||||
|
: 'border-red-500/30 bg-red-500/10 text-red-400'}"
|
||||||
|
>
|
||||||
|
{submitResult.message}
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
disabled={!title.trim() || !htmlCode.trim() || !authorName.trim() || isSubmitting}
|
||||||
|
class="w-full px-4 py-2.5 rounded-lg bg-primary text-primary-foreground font-medium hover:bg-primary/90 transition-colors disabled:opacity-50 disabled:cursor-not-allowed"
|
||||||
|
>
|
||||||
|
{isSubmitting ? 'Wird eingereicht...' : 'Spiel einreichen'}
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
<svelte:head>
|
||||||
|
<title>Tags - Mana Games</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="max-w-2xl mx-auto text-center py-12">
|
||||||
|
<p class="text-4xl mb-4">🏷️</p>
|
||||||
|
<h1 class="text-2xl font-bold text-foreground">Tags</h1>
|
||||||
|
<p class="text-muted-foreground mt-2">Demnächst verfügbar.</p>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { theme } from '$lib/stores/theme.svelte';
|
||||||
|
import { THEME_DEFINITIONS, EXTENDED_THEME_VARIANTS } from '@manacore/shared-theme';
|
||||||
|
import type { ThemeVariant } from '@manacore/shared-theme';
|
||||||
|
|
||||||
|
const allThemes = EXTENDED_THEME_VARIANTS;
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Themes - Mana Games</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="max-w-4xl mx-auto space-y-6">
|
||||||
|
<div>
|
||||||
|
<h1 class="text-2xl font-bold text-foreground">Themes</h1>
|
||||||
|
<p class="text-muted-foreground mt-1">Wähle ein Theme für Mana Games</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="grid grid-cols-2 sm:grid-cols-3 lg:grid-cols-4 gap-3">
|
||||||
|
{#each allThemes as variant}
|
||||||
|
{@const def = THEME_DEFINITIONS[variant]}
|
||||||
|
{#if def}
|
||||||
|
<button
|
||||||
|
onclick={() => theme.setVariant(variant)}
|
||||||
|
class="rounded-xl border p-4 text-left transition-all hover:-translate-y-0.5 {theme.variant ===
|
||||||
|
variant
|
||||||
|
? 'border-primary bg-primary/5 ring-2 ring-primary/30'
|
||||||
|
: 'border-border bg-card hover:border-primary/30'}"
|
||||||
|
>
|
||||||
|
<div class="text-2xl mb-2">{def.icon || '🎨'}</div>
|
||||||
|
<div class="font-medium text-foreground text-sm">{def.label}</div>
|
||||||
|
</button>
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { ForgotPasswordPage } from '@manacore/shared-auth-ui';
|
||||||
|
import { authStore } from '$lib/stores/auth.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Mana Games - Passwort vergessen</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<ForgotPasswordPage {authStore} {goto} appName="Mana Games" loginHref="/login" />
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { LoginPage } from '@manacore/shared-auth-ui';
|
||||||
|
import { authStore } from '$lib/stores/auth.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Mana Games - Login</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<LoginPage
|
||||||
|
{authStore}
|
||||||
|
{goto}
|
||||||
|
appName="Mana Games"
|
||||||
|
registerHref="/register"
|
||||||
|
forgotPasswordHref="/forgot-password"
|
||||||
|
primaryColor="#00ff88"
|
||||||
|
/>
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
import { RegisterPage } from '@manacore/shared-auth-ui';
|
||||||
|
import { authStore } from '$lib/stores/auth.svelte';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Mana Games - Registrieren</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<RegisterPage {authStore} {goto} appName="Mana Games" loginHref="/login" primaryColor="#00ff88" />
|
||||||
|
|
@ -0,0 +1,15 @@
|
||||||
|
<script lang="ts">
|
||||||
|
import { goto } from '$app/navigation';
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<svelte:head>
|
||||||
|
<title>Mana Games - Passwort zurücksetzen</title>
|
||||||
|
</svelte:head>
|
||||||
|
|
||||||
|
<div class="min-h-screen flex items-center justify-center">
|
||||||
|
<div class="max-w-md w-full p-6 text-center">
|
||||||
|
<h1 class="text-xl font-bold text-foreground mb-4">Passwort zurücksetzen</h1>
|
||||||
|
<p class="text-muted-foreground mb-6">Funktion wird eingerichtet.</p>
|
||||||
|
<a href="/login" class="text-primary hover:underline">Zurück zum Login</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
14
games/mana-games/apps/web/src/routes/health/+server.ts
Normal file
14
games/mana-games/apps/web/src/routes/health/+server.ts
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
import type { RequestHandler } from './$types';
|
||||||
|
|
||||||
|
export const GET: RequestHandler = async () => {
|
||||||
|
return new Response(
|
||||||
|
JSON.stringify({
|
||||||
|
status: 'ok',
|
||||||
|
service: 'mana-games-web',
|
||||||
|
timestamp: new Date().toISOString(),
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
headers: { 'Content-Type': 'application/json' },
|
||||||
|
}
|
||||||
|
);
|
||||||
|
};
|
||||||
Loading…
Add table
Add a link
Reference in a new issue