Major changes since initial commit: Schema: version branching (parentVersionId, branchLabel), share links, guest comments, track status enum (sketch/in_progress/final/released), track sections, cover art for projects and tracks. API: 29+ endpoints — auth, projects, tracks, versions, comments, share links (public + management), uploads (cover), activity feed, onboarding demo seed. Email templates in German with brand styling. Web: SvelteKit 5 workspace layout with persistent sidebar, breadcrumb top-bar, collapsible right panel. SoundCloud-style waveform player with round play button, avatar comment markers, keyboard shortcuts (Space/JKL/C). Full German UI. Cover art with gradient fallback. Track status pills. Activity feed dashboard. Welcome modal with demo-seed trigger. Landing page with 7-section scroll layout. Login on /login. Public /listen/:token page for guest feedback. Visual: Inter Variable font, Magenta→Orange gradient accent, warm dark neutrals, Lucide-style inline SVG icon set, spring animations on modals, glass-effect toasts, responsive from 360px to 2560px+. PWA: manifest, service worker, icons, iOS/Android installable. Production: adapter-node, server-side API proxy hook, docker-compose with Postgres + MinIO + auto-migration + health checks. Env example included. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
116 lines
2.7 KiB
Svelte
116 lines
2.7 KiB
Svelte
<script lang="ts">
|
|
import { user } from '$lib/stores/auth.js';
|
|
import { api } from '$lib/api/client.js';
|
|
import { toastSuccess } from '$lib/stores/toast.js';
|
|
import Button from '$lib/components/ui/Button.svelte';
|
|
import Input from '$lib/components/ui/Input.svelte';
|
|
import Avatar from '$lib/components/ui/Avatar.svelte';
|
|
import TopBar from '$lib/components/workspace/TopBar.svelte';
|
|
|
|
let name = $state('');
|
|
let saving = $state(false);
|
|
|
|
$effect(() => {
|
|
if ($user && !name) name = $user.name;
|
|
});
|
|
|
|
async function save() {
|
|
if (!name.trim()) return;
|
|
saving = true;
|
|
try {
|
|
const res = await api.patch<{ user: typeof $user }>('/auth/me', { name: name.trim() });
|
|
user.set(res.user);
|
|
toastSuccess('Profil gespeichert');
|
|
} finally {
|
|
saving = false;
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<TopBar crumbs={[{ label: 'Konto' }]} />
|
|
|
|
<div class="page">
|
|
<header>
|
|
<h1>Konto</h1>
|
|
<p class="sub">Dein Profil — sichtbar für andere im Projekt.</p>
|
|
</header>
|
|
|
|
{#if $user}
|
|
<section class="card">
|
|
<h2>Profil</h2>
|
|
<div class="profile-row">
|
|
<Avatar name={$user.name} src={$user.avatarUrl ?? null} size="lg" />
|
|
<div class="form">
|
|
<Input label="Anzeige-Name" bind:value={name} />
|
|
<p class="email-line">E-Mail: <span>{$user.email}</span></p>
|
|
<Button onclick={save} loading={saving} disabled={!name.trim() || name === $user.name}>
|
|
Speichern
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</section>
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
.page {
|
|
padding: var(--space-6);
|
|
max-width: 720px;
|
|
}
|
|
@media (max-width: 640px) {
|
|
.page {
|
|
padding: var(--space-4);
|
|
}
|
|
.profile-row {
|
|
flex-direction: column;
|
|
align-items: stretch;
|
|
}
|
|
}
|
|
header {
|
|
margin-bottom: var(--space-8);
|
|
}
|
|
h1 {
|
|
margin: 0 0 var(--space-1);
|
|
font-size: var(--text-2xl);
|
|
}
|
|
.sub {
|
|
color: var(--color-text-tertiary);
|
|
font-size: var(--text-sm);
|
|
margin: 0;
|
|
}
|
|
.card {
|
|
background: var(--color-bg-raised);
|
|
border: 1px solid var(--color-border);
|
|
border-radius: var(--radius-lg);
|
|
padding: var(--space-6);
|
|
}
|
|
h2 {
|
|
margin: 0 0 var(--space-5);
|
|
font-size: var(--text-lg);
|
|
}
|
|
.profile-row {
|
|
display: flex;
|
|
align-items: flex-start;
|
|
gap: var(--space-5);
|
|
}
|
|
.form {
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: var(--space-4);
|
|
align-items: flex-start;
|
|
}
|
|
.form :global(.input-group) {
|
|
width: 100%;
|
|
}
|
|
.email-line {
|
|
color: var(--color-text-tertiary);
|
|
font-size: var(--text-sm);
|
|
margin: 0;
|
|
}
|
|
.email-line span {
|
|
color: var(--color-text-primary);
|
|
font-family: var(--font-mono);
|
|
}
|
|
</style>
|