feat(skilltree): add central auth pages with shared components

- Add SkillTreeLogo to shared-branding (emerald green theme)
- Add login, register, forgot-password pages using shared-auth-ui
- Initialize authStore in layout alongside skillStore
- Add shared-auth-ui, shared-i18n, shared-branding dependencies
- German translations as default

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Till-JS 2026-01-29 13:55:22 +01:00
parent f3424c26aa
commit 51ec8f8419
11 changed files with 182 additions and 2 deletions

View file

@ -32,6 +32,9 @@
},
"dependencies": {
"@manacore/shared-auth": "workspace:*",
"@manacore/shared-auth-ui": "workspace:*",
"@manacore/shared-branding": "workspace:*",
"@manacore/shared-i18n": "workspace:*",
"@manacore/shared-icons": "workspace:*",
"@manacore/shared-tailwind": "workspace:*",
"@manacore/shared-theme": "workspace:*",

View file

@ -0,0 +1,30 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { ForgotPasswordPage } from '@manacore/shared-auth-ui';
import { getForgotPasswordTranslations } from '@manacore/shared-i18n';
import { SkillTreeLogo } from '@manacore/shared-branding';
import { authStore } from '$lib/stores/auth.svelte';
// Get translations (default to German for this app)
const translations = $derived(getForgotPasswordTranslations('de'));
async function handleForgotPassword(email: string) {
return authStore.resetPassword(email);
}
</script>
<svelte:head>
<title>{translations.titleForm} | SkillTree</title>
</svelte:head>
<ForgotPasswordPage
appName="SkillTree"
logo={SkillTreeLogo}
primaryColor="#10b981"
onForgotPassword={handleForgotPassword}
{goto}
loginPath="/login"
lightBackground="#d1fae5"
darkBackground="#064e3b"
{translations}
/>

View file

@ -0,0 +1,64 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { page } from '$app/stores';
import { browser } from '$app/environment';
import { LoginPage } from '@manacore/shared-auth-ui';
import { getLoginTranslations } from '@manacore/shared-i18n';
import { SkillTreeLogo } from '@manacore/shared-branding';
import { authStore } from '$lib/stores/auth.svelte';
import { apiClient } from '$lib/api/client';
// Get redirect URL from query params or sessionStorage
const redirectTo = $derived.by(() => {
const queryRedirect = $page.url.searchParams.get('redirectTo');
if (queryRedirect) return queryRedirect;
if (browser) {
const sessionRedirect = sessionStorage.getItem('auth-return-url');
if (sessionRedirect) {
sessionStorage.removeItem('auth-return-url');
return sessionRedirect;
}
}
return '/';
});
// Get translations (default to German for this app)
const translations = $derived(getLoginTranslations('de'));
// Read verification status from query params
const verified = $derived($page.url.searchParams.get('verified') === 'true');
const initialEmail = $derived($page.url.searchParams.get('email') || '');
async function handleSignIn(email: string, password: string) {
const result = await authStore.signIn(email, password);
if (result.success) {
const token = await authStore.getValidToken();
apiClient.setAccessToken(token);
}
return result;
}
</script>
<svelte:head>
<title>{translations.title} | SkillTree</title>
</svelte:head>
<LoginPage
appName="SkillTree"
logo={SkillTreeLogo}
primaryColor="#10b981"
onSignIn={handleSignIn}
{goto}
enableGoogle={false}
enableApple={false}
successRedirect={redirectTo}
registerPath="/register"
forgotPasswordPath="/forgot-password"
lightBackground="#d1fae5"
darkBackground="#064e3b"
{translations}
{verified}
{initialEmail}
/>

View file

@ -0,0 +1,44 @@
<script lang="ts">
import { goto } from '$app/navigation';
import { browser } from '$app/environment';
import { RegisterPage } from '@manacore/shared-auth-ui';
import { getRegisterTranslations } from '@manacore/shared-i18n';
import { SkillTreeLogo } from '@manacore/shared-branding';
import { authStore } from '$lib/stores/auth.svelte';
// Get redirect URL from sessionStorage
const redirectTo = $derived.by(() => {
if (browser) {
const sessionRedirect = sessionStorage.getItem('auth-return-url');
if (sessionRedirect) {
sessionStorage.removeItem('auth-return-url');
return sessionRedirect;
}
}
return '/';
});
// Get translations (default to German for this app)
const translations = $derived(getRegisterTranslations('de'));
async function handleSignUp(email: string, password: string) {
return authStore.signUp(email, password);
}
</script>
<svelte:head>
<title>{translations.title} | SkillTree</title>
</svelte:head>
<RegisterPage
appName="SkillTree"
logo={SkillTreeLogo}
primaryColor="#10b981"
onSignUp={handleSignUp}
{goto}
successRedirect={redirectTo}
loginPath="/login"
lightBackground="#d1fae5"
darkBackground="#064e3b"
{translations}
/>

View file

@ -2,13 +2,14 @@
import '../app.css';
import { onMount } from 'svelte';
import { skillStore } from '$lib/stores/skills.svelte';
import { authStore } from '$lib/stores/auth.svelte';
let { children } = $props();
let loading = $state(true);
onMount(async () => {
await skillStore.initialize();
await Promise.all([authStore.initialize(), skillStore.initialize()]);
loading = false;
});
</script>

View file

@ -233,6 +233,19 @@ export const APP_BRANDING: Record<AppId, AppBranding> = {
logoStroke: true,
logoStrokeWidth: 1.5,
},
skilltree: {
id: 'skilltree',
name: 'SkillTree',
tagline: 'Level Up Your Life',
primaryColor: '#10b981',
secondaryColor: '#34d399',
// Tree/branch icon representing skill progression
logoPath:
'M12 3v1.5M12 21v-1.5M12 9a3 3 0 100-6 3 3 0 000 6zm0 0v3m0 6a3 3 0 100-6 3 3 0 000 6zm-6-3h1.5m10.5 0h1.5M6 12a3 3 0 100-6 3 3 0 000 6zm0 0h3m6 0h3m-3 0a3 3 0 100-6 3 3 0 000 6z',
logoViewBox: '0 0 24 24',
logoStroke: true,
logoStrokeWidth: 1.5,
},
};
/**

View file

@ -32,6 +32,7 @@ export {
InventoryLogo,
ClockLogo,
QuestionsLogo,
SkillTreeLogo,
} from './logos';
// Configuration

View file

@ -0,0 +1,13 @@
<script lang="ts">
import AppLogo from '../AppLogo.svelte';
interface Props {
size?: number;
color?: string;
class?: string;
}
let { size = 55, color, class: className = '' }: Props = $props();
</script>
<AppLogo app="skilltree" {size} {color} class={className} />

View file

@ -19,3 +19,4 @@ export { default as MoodlitLogo } from './MoodlitLogo.svelte';
export { default as InventoryLogo } from './InventoryLogo.svelte';
export { default as ClockLogo } from './ClockLogo.svelte';
export { default as QuestionsLogo } from './QuestionsLogo.svelte';
export { default as SkillTreeLogo } from './SkillTreeLogo.svelte';

View file

@ -19,7 +19,8 @@ export type AppId =
| 'mail'
| 'moodlit'
| 'inventory'
| 'questions';
| 'questions'
| 'skilltree';
/**
* App branding configuration

9
pnpm-lock.yaml generated
View file

@ -3691,6 +3691,15 @@ importers:
'@manacore/shared-auth':
specifier: workspace:*
version: link:../../../../packages/shared-auth
'@manacore/shared-auth-ui':
specifier: workspace:*
version: link:../../../../packages/shared-auth-ui
'@manacore/shared-branding':
specifier: workspace:*
version: link:../../../../packages/shared-branding
'@manacore/shared-i18n':
specifier: workspace:*
version: link:../../../../packages/shared-i18n
'@manacore/shared-icons':
specifier: workspace:*
version: link:../../../../packages/shared-icons