Files
Sakuin/frontend/src/routes/+layout.svelte
Tetardtek f1cff74d83
All checks were successful
CI/CD — Build & Deploy / Build & Deploy (push) Successful in 29s
init: scaffold complet Sakuin — backend NestJS + frontend SvelteKit + CI/CD + deploy VPS
Backend: 5 modules (auth, user, work, list, health), AniList GraphQL proxy,
SuperOAuth PKCE introspection, XP system, migrations TypeORM.
Frontend: SvelteKit adapter-node, PWA manifest, dark theme, pages home/search/list/profile/callback.
Infra: CI/CD Gitea vps-runner, Apache vhost SSL, pm2 sakuin-backend + sakuin-frontend, port 4002.
License: BSL 1.1 (Apache 2.0 en 2028).
2026-03-25 01:43:32 +01:00

114 lines
2.3 KiB
Svelte

<script lang="ts">
import '../app.css';
import { login, logout } from '$lib/auth';
let { children } = $props();
let user = $state<any>(null);
let loading = $state(true);
async function loadUser() {
try {
const { api } = await import('$lib/api');
user = await api.me();
} catch {
user = null;
} finally {
loading = false;
}
}
$effect(() => {
loadUser();
});
</script>
<nav class="navbar">
<div class="container nav-inner">
<a href="/" class="logo">索引 <span>Sakuin</span></a>
<div class="nav-links">
<a href="/search">Rechercher</a>
{#if user}
<a href="/list">Ma Liste</a>
<a href="/profile">Profil</a>
<div class="user-badge">
{#if user.avatarUrl}
<img src={user.avatarUrl} alt="" class="avatar" />
{/if}
<span class="username">{user.username}</span>
<span class="level badge badge-accent">Lv.{user.level}</span>
<button class="btn-ghost btn-sm" onclick={logout}>Déconnexion</button>
</div>
{:else if !loading}
<button class="btn-primary" onclick={login}>Connexion</button>
{/if}
</div>
</div>
</nav>
<main class="container main-content">
{@render children()}
</main>
<style>
.navbar {
background: var(--bg-secondary);
border-bottom: 1px solid var(--border);
padding: 0.75rem 0;
position: sticky;
top: 0;
z-index: 100;
}
.nav-inner {
display: flex;
align-items: center;
justify-content: space-between;
}
.logo {
font-size: 1.25rem;
font-weight: 700;
color: var(--text-primary);
}
.logo span {
color: var(--accent);
}
.nav-links {
display: flex;
align-items: center;
gap: 1rem;
}
.nav-links a {
color: var(--text-secondary);
font-size: 0.875rem;
font-weight: 500;
}
.nav-links a:hover {
color: var(--text-primary);
}
.user-badge {
display: flex;
align-items: center;
gap: 0.5rem;
}
.avatar {
width: 28px;
height: 28px;
border-radius: 50%;
}
.username {
font-size: 0.875rem;
font-weight: 500;
}
.level {
font-size: 0.7rem;
}
.btn-sm {
padding: 0.25rem 0.5rem;
font-size: 0.75rem;
}
.main-content {
padding-top: 2rem;
padding-bottom: 4rem;
}
</style>