Compare commits
18 Commits
feature/68
...
44f495ca8b
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
44f495ca8b | ||
|
|
74bf49552b | ||
|
|
1de4f8a605 | ||
|
|
f8d888a5be | ||
|
|
29f0ec8a05 | ||
|
|
5db17880f9 | ||
|
|
ce02c1bf39 | ||
|
|
e1c09ddc7f | ||
|
|
93408c5825 | ||
|
|
2a2ce240e1 | ||
|
|
0bd7a70c96 | ||
|
|
a570dff4e9 | ||
|
|
fcff7fbdb1 | ||
|
|
5cf6947040 | ||
|
|
d053f6dc40 | ||
|
|
afebaf4c53 | ||
|
|
1bfe0ab022 | ||
|
|
6ebae19984 |
@@ -93,7 +93,7 @@ function handlePointerUp(event: PointerEvent) {
|
|||||||
let hoveredId = $state<string | null>(null);
|
let hoveredId = $state<string | null>(null);
|
||||||
|
|
||||||
const containerStyle = $derived(
|
const containerStyle = $derived(
|
||||||
`position: absolute; top: 0; left: 0; width: 100%; height: 100%;${canAnnotate ? ' cursor: crosshair;' : ''}`
|
`position: absolute; top: 0; left: 0; width: 100%; height: 100%;${canAnnotate ? ' cursor: crosshair; touch-action: none;' : ''}`
|
||||||
);
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@@ -113,8 +113,8 @@ const containerStyle = $derived(
|
|||||||
aria-label="Kommentare anzeigen"
|
aria-label="Kommentare anzeigen"
|
||||||
onclick={() => onAnnotationClick?.(annotation.id)}
|
onclick={() => onAnnotationClick?.(annotation.id)}
|
||||||
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') onAnnotationClick?.(annotation.id); }}
|
onkeydown={(e) => { if (e.key === 'Enter' || e.key === ' ') onAnnotationClick?.(annotation.id); }}
|
||||||
onmouseenter={() => (hoveredId = annotation.id)}
|
onpointerenter={() => (hoveredId = annotation.id)}
|
||||||
onmouseleave={() => (hoveredId = null)}
|
onpointerleave={() => (hoveredId = null)}
|
||||||
style="
|
style="
|
||||||
position: absolute;
|
position: absolute;
|
||||||
left: {annotation.x * 100}%;
|
left: {annotation.x * 100}%;
|
||||||
|
|||||||
@@ -188,7 +188,7 @@ onMount(() => {
|
|||||||
></textarea>
|
></textarea>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<button
|
<button
|
||||||
class="rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-primary/80 disabled:opacity-40"
|
class="rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-primary-fg hover:bg-primary/80 disabled:opacity-40"
|
||||||
disabled={posting}
|
disabled={posting}
|
||||||
onclick={() => saveEdit(comment.id)}
|
onclick={() => saveEdit(comment.id)}
|
||||||
>
|
>
|
||||||
@@ -291,7 +291,7 @@ onMount(() => {
|
|||||||
></textarea>
|
></textarea>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<button
|
<button
|
||||||
class="rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-primary/80 disabled:opacity-40"
|
class="rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-primary-fg hover:bg-primary/80 disabled:opacity-40"
|
||||||
disabled={posting}
|
disabled={posting}
|
||||||
onclick={() => postReply(thread.id)}
|
onclick={() => postReply(thread.id)}
|
||||||
>
|
>
|
||||||
@@ -321,7 +321,7 @@ onMount(() => {
|
|||||||
></textarea>
|
></textarea>
|
||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
class="rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-white hover:bg-primary/80 disabled:opacity-40"
|
class="rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-primary-fg hover:bg-primary/80 disabled:opacity-40"
|
||||||
disabled={posting || !newText.trim()}
|
disabled={posting || !newText.trim()}
|
||||||
onclick={postComment}
|
onclick={postComment}
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ function handleCountChange(count: number) {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="fixed right-0 bottom-0 left-0 z-30 flex flex-col border-t border-line bg-surface shadow-[0_-4px_16px_rgba(0,0,0,0.08)]"
|
class="z-30 flex shrink-0 flex-col border-t border-line bg-surface shadow-[0_-4px_16px_rgba(0,0,0,0.08)]"
|
||||||
style="height: {panelHeight}px"
|
style="height: {panelHeight}px"
|
||||||
data-testid="bottom-panel"
|
data-testid="bottom-panel"
|
||||||
>
|
>
|
||||||
@@ -127,35 +127,37 @@ function handleCountChange(count: number) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Tab bar -->
|
<!-- Tab bar -->
|
||||||
<div class="flex shrink-0 items-center border-b border-line bg-surface px-4">
|
<div class="flex shrink-0 items-center border-b border-line bg-surface">
|
||||||
{#each tabs as tab (tab.id)}
|
<!-- Scrollable tabs area — hides scrollbar visually -->
|
||||||
<button
|
<div
|
||||||
onclick={() => openTab(tab.id)}
|
class="flex flex-1 items-center overflow-x-auto px-2 [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
|
||||||
class="mr-1 px-3 py-2.5 font-sans text-xs font-medium transition-colors {activeTab === tab.id && open
|
>
|
||||||
? 'border-b-2 border-primary text-ink'
|
{#each tabs as tab (tab.id)}
|
||||||
: 'text-ink-3 hover:text-ink'}"
|
<button
|
||||||
aria-pressed={activeTab === tab.id && open}
|
onclick={() => openTab(tab.id)}
|
||||||
>
|
class="mr-1 shrink-0 px-3 py-2.5 font-sans text-xs font-medium transition-colors {activeTab === tab.id && open
|
||||||
{tab.label()}
|
? 'border-b-2 border-primary text-ink'
|
||||||
{#if tab.id === 'discussion'}
|
: 'text-ink-3 hover:text-ink'}"
|
||||||
<span
|
aria-pressed={activeTab === tab.id && open}
|
||||||
data-testid="discussion-count-badge"
|
>
|
||||||
class="ml-1.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 font-sans text-[10px] font-bold text-primary-fg"
|
{tab.label()}
|
||||||
>{discussionCount}</span
|
{#if tab.id === 'discussion'}
|
||||||
>
|
<span
|
||||||
{/if}
|
data-testid="discussion-count-badge"
|
||||||
</button>
|
class="ml-1.5 inline-flex h-4 min-w-4 items-center justify-center rounded-full bg-primary px-1 font-sans text-[10px] font-bold text-primary-fg"
|
||||||
{/each}
|
>{discussionCount}</span
|
||||||
|
>
|
||||||
<!-- spacer -->
|
{/if}
|
||||||
<div class="flex-1"></div>
|
</button>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
|
||||||
{#if open}
|
{#if open}
|
||||||
<button
|
<button
|
||||||
onclick={closePanel}
|
onclick={closePanel}
|
||||||
data-testid="panel-close-btn"
|
data-testid="panel-close-btn"
|
||||||
aria-label="Panel schließen"
|
aria-label="Panel schließen"
|
||||||
class="rounded p-1.5 text-ink-3 transition-colors hover:bg-muted hover:text-ink"
|
class="mr-2 shrink-0 rounded p-1.5 text-ink-3 transition-colors hover:bg-muted hover:text-ink"
|
||||||
>
|
>
|
||||||
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
<svg class="h-4 w-4" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
<path stroke-linecap="round" stroke-linejoin="round" d="M6 18L18 6M6 6l12 12" />
|
||||||
|
|||||||
@@ -58,7 +58,7 @@ const compactMeta = $derived.by(() => {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="z-20 flex shrink-0 items-center justify-between border-b border-line bg-surface px-6 py-3 shadow-sm"
|
class="z-20 flex shrink-0 items-center justify-between border-b border-line bg-surface px-3 py-3 shadow-sm sm:px-6"
|
||||||
data-topbar
|
data-topbar
|
||||||
>
|
>
|
||||||
<!-- Left: back + title -->
|
<!-- Left: back + title -->
|
||||||
@@ -102,8 +102,8 @@ const compactMeta = $derived.by(() => {
|
|||||||
onclick={() => (annotateMode = !annotateMode)}
|
onclick={() => (annotateMode = !annotateMode)}
|
||||||
aria-label={annotateMode ? m.doc_panel_annotate_stop() : m.doc_panel_annotate()}
|
aria-label={annotateMode ? m.doc_panel_annotate_stop() : m.doc_panel_annotate()}
|
||||||
class="flex items-center gap-1.5 rounded px-3 py-1.5 font-sans text-xs font-medium transition {annotateMode
|
class="flex items-center gap-1.5 rounded px-3 py-1.5 font-sans text-xs font-medium transition {annotateMode
|
||||||
? 'bg-primary text-white'
|
? 'bg-primary text-primary-fg'
|
||||||
: 'border border-primary text-ink hover:bg-primary hover:text-white'}"
|
: 'border border-primary text-ink hover:bg-primary hover:text-primary-fg'}"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Note/Note-Add-MD.svg"
|
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Note/Note-Add-MD.svg"
|
||||||
@@ -111,14 +111,17 @@ const compactMeta = $derived.by(() => {
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class="h-4 w-4 {annotateMode ? 'invert' : ''}"
|
class="h-4 w-4 {annotateMode ? 'invert' : ''}"
|
||||||
/>
|
/>
|
||||||
{annotateMode ? m.doc_panel_annotate_stop() : m.doc_panel_annotate()}
|
<span class="hidden sm:inline"
|
||||||
|
>{annotateMode ? m.doc_panel_annotate_stop() : m.doc_panel_annotate()}</span
|
||||||
|
>
|
||||||
</button>
|
</button>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
{#if canWrite}
|
{#if canWrite}
|
||||||
<a
|
<a
|
||||||
href="/documents/{doc.id}/edit"
|
href="/documents/{doc.id}/edit"
|
||||||
class="flex items-center gap-2 rounded border border-primary bg-transparent px-3 py-1.5 text-xs font-medium text-ink transition hover:bg-primary hover:text-white"
|
aria-label={m.btn_edit()}
|
||||||
|
class="flex items-center gap-2 rounded border border-primary bg-transparent px-3 py-1.5 text-xs font-medium text-ink transition hover:bg-primary hover:text-primary-fg"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Edit-Content-MD.svg"
|
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Edit-Content-MD.svg"
|
||||||
@@ -126,7 +129,7 @@ const compactMeta = $derived.by(() => {
|
|||||||
aria-hidden="true"
|
aria-hidden="true"
|
||||||
class="h-4 w-4"
|
class="h-4 w-4"
|
||||||
/>
|
/>
|
||||||
{m.btn_edit()}
|
<span class="hidden sm:inline">{m.btn_edit()}</span>
|
||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
18
frontend/src/lib/components/LanguageSwitcher.svelte
Normal file
18
frontend/src/lib/components/LanguageSwitcher.svelte
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
<script lang="ts">
|
||||||
|
import { setLocale, getLocale } from '$lib/paraglide/runtime';
|
||||||
|
|
||||||
|
const locales = ['DE', 'EN', 'ES'] as const;
|
||||||
|
const localeMap = { DE: 'de', EN: 'en', ES: 'es' } as const;
|
||||||
|
const activeLocale = $derived(getLocale().toUpperCase());
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#each locales as locale (locale)}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => setLocale(localeMap[locale])}
|
||||||
|
class="font-sans tracking-widest transition-colors
|
||||||
|
{activeLocale === locale ? 'font-bold text-ink' : 'font-normal text-ink-3 hover:text-ink'}"
|
||||||
|
>
|
||||||
|
{locale}
|
||||||
|
</button>
|
||||||
|
{/each}
|
||||||
@@ -328,7 +328,7 @@ $effect(() => {
|
|||||||
<button
|
<button
|
||||||
onclick={applyCompare}
|
onclick={applyCompare}
|
||||||
disabled={!compareA || !compareB || compareA === compareB}
|
disabled={!compareA || !compareB || compareA === compareB}
|
||||||
class="w-full rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-white transition hover:bg-primary/80 disabled:cursor-not-allowed disabled:opacity-40"
|
class="w-full rounded bg-primary px-3 py-1.5 font-sans text-xs font-medium text-primary-fg transition hover:bg-primary/80 disabled:cursor-not-allowed disabled:opacity-40"
|
||||||
>
|
>
|
||||||
{m.history_compare_apply()}
|
{m.history_compare_apply()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ let { doc }: { doc: Doc } = $props();
|
|||||||
{#each doc.tags as tag (tag.id)}
|
{#each doc.tags as tag (tag.id)}
|
||||||
<a
|
<a
|
||||||
href="/?tag={encodeURIComponent(tag.name)}"
|
href="/?tag={encodeURIComponent(tag.name)}"
|
||||||
class="inline-flex items-center rounded bg-muted px-2 py-0.5 text-xs font-bold tracking-wide text-ink uppercase transition-colors hover:bg-primary hover:text-white"
|
class="inline-flex items-center rounded bg-muted px-2 py-0.5 text-xs font-bold tracking-wide text-ink uppercase transition-colors hover:bg-primary hover:text-primary-fg"
|
||||||
title={m.doc_tag_filter_title({ name: tag.name })}
|
title={m.doc_tag_filter_title({ name: tag.name })}
|
||||||
>
|
>
|
||||||
{tag.name}
|
{tag.name}
|
||||||
@@ -131,7 +131,7 @@ let { doc }: { doc: Doc } = $props();
|
|||||||
>
|
>
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<div
|
<div
|
||||||
class="flex h-8 w-8 items-center justify-center rounded-full bg-primary font-serif text-sm text-white"
|
class="flex h-8 w-8 items-center justify-center rounded-full bg-primary font-serif text-sm text-primary-fg"
|
||||||
>
|
>
|
||||||
{doc.sender.firstName[0]}{doc.sender.lastName[0]}
|
{doc.sender.firstName[0]}{doc.sender.lastName[0]}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -2,17 +2,13 @@
|
|||||||
import './layout.css';
|
import './layout.css';
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
import { onMount } from 'svelte';
|
import { onMount } from 'svelte';
|
||||||
import { setLocale, getLocale } from '$lib/paraglide/runtime';
|
import LanguageSwitcher from '$lib/components/LanguageSwitcher.svelte';
|
||||||
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
|
import ThemeToggle from '$lib/components/ThemeToggle.svelte';
|
||||||
import AppNav from './AppNav.svelte';
|
import AppNav from './AppNav.svelte';
|
||||||
import UserMenu from './UserMenu.svelte';
|
import UserMenu from './UserMenu.svelte';
|
||||||
|
|
||||||
let { children, data } = $props();
|
let { children, data } = $props();
|
||||||
|
|
||||||
const locales = ['DE', 'EN', 'ES'] as const;
|
|
||||||
const localeMap = { DE: 'de', EN: 'en', ES: 'es' } as const;
|
|
||||||
const activeLocale = $derived(getLocale().toUpperCase());
|
|
||||||
|
|
||||||
const isAdmin = $derived(
|
const isAdmin = $derived(
|
||||||
data?.user?.groups?.some((g: { permissions: string[] }) => g.permissions.includes('ADMIN'))
|
data?.user?.groups?.some((g: { permissions: string[] }) => g.permissions.includes('ADMIN'))
|
||||||
);
|
);
|
||||||
@@ -39,9 +35,6 @@ const userInitials = $derived.by(() => {
|
|||||||
<div class="min-h-screen bg-canvas" data-hydrated={hydrated || undefined}>
|
<div class="min-h-screen bg-canvas" data-hydrated={hydrated || undefined}>
|
||||||
{#if !isAuthPage}
|
{#if !isAuthPage}
|
||||||
<header class="sticky top-0 z-50 border-b border-line-2 bg-surface">
|
<header class="sticky top-0 z-50 border-b border-line-2 bg-surface">
|
||||||
<!-- De Gruyter Brill purple accent strip -->
|
|
||||||
<div class="h-1 bg-brand-purple"></div>
|
|
||||||
|
|
||||||
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
<div class="mx-auto max-w-7xl px-4 sm:px-6 lg:px-8">
|
||||||
<div class="flex h-16 justify-between">
|
<div class="flex h-16 justify-between">
|
||||||
<!-- Logo & Nav -->
|
<!-- Logo & Nav -->
|
||||||
@@ -49,20 +42,11 @@ const userInitials = $derived.by(() => {
|
|||||||
|
|
||||||
<!-- Right Side -->
|
<!-- Right Side -->
|
||||||
<div class="flex items-center gap-3">
|
<div class="flex items-center gap-3">
|
||||||
<!-- Language selector -->
|
<!-- Language selector (desktop only — mobile lives in nav drawer) -->
|
||||||
<div class="flex items-center gap-1 border-r border-line pr-3">
|
<div
|
||||||
{#each locales as locale (locale)}
|
class="hidden items-center gap-1 border-r border-line pr-3 sm:flex [&_button]:px-1.5 [&_button]:py-1 [&_button]:text-xs"
|
||||||
<button
|
>
|
||||||
type="button"
|
<LanguageSwitcher />
|
||||||
onclick={() => setLocale(localeMap[locale])}
|
|
||||||
class="px-1.5 py-1 font-sans text-xs tracking-widest transition-colors
|
|
||||||
{activeLocale === locale
|
|
||||||
? 'font-bold text-ink'
|
|
||||||
: 'font-normal text-ink-3 hover:text-ink'}"
|
|
||||||
>
|
|
||||||
{locale}
|
|
||||||
</button>
|
|
||||||
{/each}
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Theme toggle -->
|
<!-- Theme toggle -->
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ $effect(() => {
|
|||||||
});
|
});
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<main class="mx-auto max-w-7xl py-8 font-sans sm:px-6 lg:px-8">
|
<main class="mx-auto max-w-7xl px-4 py-8 font-sans sm:px-6 lg:px-8">
|
||||||
<SearchFilterBar
|
<SearchFilterBar
|
||||||
bind:q={q}
|
bind:q={q}
|
||||||
bind:from={from}
|
bind:from={from}
|
||||||
|
|||||||
@@ -1,12 +1,35 @@
|
|||||||
<script lang="ts">
|
<script lang="ts">
|
||||||
import { page } from '$app/state';
|
import { page } from '$app/state';
|
||||||
|
import { untrack } from 'svelte';
|
||||||
import { m } from '$lib/paraglide/messages.js';
|
import { m } from '$lib/paraglide/messages.js';
|
||||||
|
import LanguageSwitcher from '$lib/components/LanguageSwitcher.svelte';
|
||||||
|
|
||||||
let { isAdmin = false }: { isAdmin?: boolean } = $props();
|
let { isAdmin = false }: { isAdmin?: boolean } = $props();
|
||||||
|
|
||||||
|
let mobileNavOpen = $state(false);
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
// Read pathname to establish the reactive dependency.
|
||||||
|
// Write via untrack so the effect doesn't re-run on its own write.
|
||||||
|
void page.url.pathname;
|
||||||
|
untrack(() => {
|
||||||
|
mobileNavOpen = false;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
function closeMobileNav() {
|
||||||
|
mobileNavOpen = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
function handleOverlayKeydown(event: KeyboardEvent) {
|
||||||
|
if (event.key === 'Escape') {
|
||||||
|
mobileNavOpen = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="flex">
|
<div class="flex items-center">
|
||||||
<div class="mr-10 flex flex-shrink-0 items-center">
|
<div class="mr-10 hidden flex-shrink-0 items-center md:flex">
|
||||||
<a href="/" class="flex items-center" aria-label="Familienarchiv">
|
<a href="/" class="flex items-center" aria-label="Familienarchiv">
|
||||||
<span class="font-sans text-xl font-bold tracking-widest text-ink uppercase"
|
<span class="font-sans text-xl font-bold tracking-widest text-ink uppercase"
|
||||||
>Familienarchiv</span
|
>Familienarchiv</span
|
||||||
@@ -14,6 +37,7 @@ let { isAdmin = false }: { isAdmin?: boolean } = $props();
|
|||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Desktop nav -->
|
||||||
<nav class="hidden items-center sm:flex sm:space-x-1">
|
<nav class="hidden items-center sm:flex sm:space-x-1">
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
@@ -56,4 +80,115 @@ let { isAdmin = false }: { isAdmin?: boolean } = $props();
|
|||||||
</a>
|
</a>
|
||||||
{/if}
|
{/if}
|
||||||
</nav>
|
</nav>
|
||||||
|
|
||||||
|
<!-- Hamburger toggle (mobile only) -->
|
||||||
|
<button
|
||||||
|
class="ml-auto flex h-11 w-11 items-center justify-center rounded text-ink-2 transition-colors hover:bg-muted hover:text-ink sm:hidden"
|
||||||
|
aria-label={mobileNavOpen ? 'Menü schließen' : 'Menü öffnen'}
|
||||||
|
aria-expanded={mobileNavOpen}
|
||||||
|
aria-controls="mobile-nav"
|
||||||
|
onclick={() => (mobileNavOpen = !mobileNavOpen)}
|
||||||
|
>
|
||||||
|
{#if mobileNavOpen}
|
||||||
|
<!-- X icon -->
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="22"
|
||||||
|
height="22"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<line x1="18" y1="6" x2="6" y2="18" />
|
||||||
|
<line x1="6" y1="6" x2="18" y2="18" />
|
||||||
|
</svg>
|
||||||
|
{:else}
|
||||||
|
<!-- Hamburger icon -->
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
width="22"
|
||||||
|
height="22"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<line x1="3" y1="6" x2="21" y2="6" />
|
||||||
|
<line x1="3" y1="12" x2="21" y2="12" />
|
||||||
|
<line x1="3" y1="18" x2="21" y2="18" />
|
||||||
|
</svg>
|
||||||
|
{/if}
|
||||||
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Mobile nav overlay -->
|
||||||
|
{#if mobileNavOpen}
|
||||||
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
|
<div class="fixed inset-0 top-[68px] z-40 sm:hidden" onkeydown={handleOverlayKeydown}>
|
||||||
|
<!-- Backdrop -->
|
||||||
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||||
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
|
<div class="absolute inset-0 bg-black/20" onclick={closeMobileNav}></div>
|
||||||
|
|
||||||
|
<!-- Panel -->
|
||||||
|
<div class="relative border-b border-line bg-surface shadow-md">
|
||||||
|
<nav id="mobile-nav">
|
||||||
|
<a
|
||||||
|
href="/"
|
||||||
|
class="block flex min-h-[44px] w-full items-center px-4 py-3 font-sans text-sm font-bold tracking-widest uppercase transition-colors
|
||||||
|
{page.url.pathname === '/' || page.url.pathname.startsWith('/documents')
|
||||||
|
? 'bg-nav-active text-ink'
|
||||||
|
: 'text-ink-2 hover:bg-muted hover:text-ink'}"
|
||||||
|
>
|
||||||
|
{m.nav_documents()}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="/persons"
|
||||||
|
class="block flex min-h-[44px] w-full items-center px-4 py-3 font-sans text-sm font-bold tracking-widest uppercase transition-colors
|
||||||
|
{page.url.pathname.startsWith('/persons')
|
||||||
|
? 'bg-nav-active text-ink'
|
||||||
|
: 'text-ink-2 hover:bg-muted hover:text-ink'}"
|
||||||
|
>
|
||||||
|
{m.nav_persons()}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
<a
|
||||||
|
href="/conversations"
|
||||||
|
class="block flex min-h-[44px] w-full items-center px-4 py-3 font-sans text-sm font-bold tracking-widest uppercase transition-colors
|
||||||
|
{page.url.pathname.startsWith('/conversations')
|
||||||
|
? 'bg-nav-active text-ink'
|
||||||
|
: 'text-ink-2 hover:bg-muted hover:text-ink'}"
|
||||||
|
>
|
||||||
|
{m.nav_conversations()}
|
||||||
|
</a>
|
||||||
|
|
||||||
|
{#if isAdmin}
|
||||||
|
<a
|
||||||
|
href="/admin"
|
||||||
|
class="block flex min-h-[44px] w-full items-center px-4 py-3 font-sans text-sm font-bold tracking-widest uppercase transition-colors
|
||||||
|
{page.url.pathname.startsWith('/admin')
|
||||||
|
? 'bg-nav-active text-ink'
|
||||||
|
: 'text-ink-2 hover:bg-muted hover:text-ink'}"
|
||||||
|
>
|
||||||
|
{m.nav_admin()}
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<!-- Language switcher -->
|
||||||
|
<div
|
||||||
|
class="flex items-center gap-2 border-t border-line px-4 py-3 [&_button]:min-h-[44px] [&_button]:px-3 [&_button]:text-sm"
|
||||||
|
>
|
||||||
|
<LanguageSwitcher />
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|||||||
@@ -121,7 +121,7 @@ let {
|
|||||||
{#each doc.tags as tag (tag.id)}
|
{#each doc.tags as tag (tag.id)}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
class="relative z-10 inline-flex cursor-pointer items-center rounded bg-muted px-2 py-1 text-[10px] font-bold tracking-widest text-ink uppercase transition-colors hover:bg-primary hover:text-white"
|
class="relative z-10 inline-flex cursor-pointer items-center rounded bg-muted px-2 py-1 text-[10px] font-bold tracking-widest text-ink uppercase transition-colors hover:bg-primary hover:text-primary-fg"
|
||||||
onclick={(e) => {
|
onclick={(e) => {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
|
|||||||
@@ -146,7 +146,7 @@ $effect(() => {
|
|||||||
? 'border-primary bg-accent-bg py-10 text-primary'
|
? 'border-primary bg-accent-bg py-10 text-primary'
|
||||||
: windowDragging
|
: windowDragging
|
||||||
? 'border-primary/60 bg-accent-bg/50 py-10 text-primary/80'
|
? 'border-primary/60 bg-accent-bg/50 py-10 text-primary/80'
|
||||||
: 'border-ink/20 py-6 text-ink-3 hover:border-primary hover:text-primary'}"
|
: 'border-ink/30 py-6 text-ink-3 hover:border-primary hover:text-primary'}"
|
||||||
ondragover={handleDragOver}
|
ondragover={handleDragOver}
|
||||||
ondragleave={handleDragLeave}
|
ondragleave={handleDragLeave}
|
||||||
ondrop={handleDrop}
|
ondrop={handleDrop}
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ function clickOutside(node: HTMLElement) {
|
|||||||
aria-expanded={userMenuOpen}
|
aria-expanded={userMenuOpen}
|
||||||
aria-haspopup="true"
|
aria-haspopup="true"
|
||||||
onclick={() => (userMenuOpen = !userMenuOpen)}
|
onclick={() => (userMenuOpen = !userMenuOpen)}
|
||||||
class="flex h-8 w-8 items-center justify-center rounded-full bg-primary font-sans text-xs font-bold text-white transition-opacity hover:opacity-80"
|
class="flex h-8 w-8 items-center justify-center rounded-full bg-primary font-sans text-xs font-bold text-primary-fg transition-opacity hover:opacity-80"
|
||||||
>
|
>
|
||||||
{userInitials}
|
{userInitials}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -11,37 +11,37 @@ let { data, form } = $props();
|
|||||||
let activeTab = $state('users');
|
let activeTab = $state('users');
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mx-auto max-w-7xl py-8 font-sans sm:px-6 lg:px-8">
|
<div class="mx-auto max-w-7xl px-4 py-8 font-sans sm:px-6 lg:px-8">
|
||||||
<div class="mb-8 flex items-center justify-between">
|
<div class="mb-8 flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<h1 class="font-serif text-3xl text-ink">{m.admin_heading()}</h1>
|
<h1 class="font-serif text-3xl text-ink">{m.admin_heading()}</h1>
|
||||||
|
|
||||||
<!-- Tabs -->
|
<!-- Tabs -->
|
||||||
<div class="flex rounded-lg border border-line bg-surface p-1 shadow-sm">
|
<div class="grid grid-cols-2 rounded-lg border border-line bg-surface p-1 shadow-sm sm:flex">
|
||||||
<button
|
<button
|
||||||
class="rounded-md px-4 py-2 text-sm font-bold tracking-wide uppercase transition {activeTab ===
|
class="rounded-md px-2 py-2 text-sm font-bold tracking-wide uppercase transition sm:px-4 {activeTab ===
|
||||||
'users'
|
'users'
|
||||||
? 'bg-primary text-white'
|
? 'bg-primary text-primary-fg'
|
||||||
: 'text-ink-2 hover:text-ink'}"
|
: 'text-ink-2 hover:text-ink'}"
|
||||||
onclick={() => (activeTab = 'users')}>{m.admin_tab_users()}</button
|
onclick={() => (activeTab = 'users')}>{m.admin_tab_users()}</button
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="rounded-md px-4 py-2 text-sm font-bold tracking-wide uppercase transition {activeTab ===
|
class="rounded-md px-2 py-2 text-sm font-bold tracking-wide uppercase transition sm:px-4 {activeTab ===
|
||||||
'groups'
|
'groups'
|
||||||
? 'bg-primary text-white'
|
? 'bg-primary text-primary-fg'
|
||||||
: 'text-ink-2 hover:text-ink'}"
|
: 'text-ink-2 hover:text-ink'}"
|
||||||
onclick={() => (activeTab = 'groups')}>{m.admin_tab_groups()}</button
|
onclick={() => (activeTab = 'groups')}>{m.admin_tab_groups()}</button
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="rounded-md px-4 py-2 text-sm font-bold tracking-wide uppercase transition {activeTab ===
|
class="rounded-md px-2 py-2 text-sm font-bold tracking-wide uppercase transition sm:px-4 {activeTab ===
|
||||||
'tags'
|
'tags'
|
||||||
? 'bg-primary text-white'
|
? 'bg-primary text-primary-fg'
|
||||||
: 'text-ink-2 hover:text-ink'}"
|
: 'text-ink-2 hover:text-ink'}"
|
||||||
onclick={() => (activeTab = 'tags')}>{m.admin_tab_tags()}</button
|
onclick={() => (activeTab = 'tags')}>{m.admin_tab_tags()}</button
|
||||||
>
|
>
|
||||||
<button
|
<button
|
||||||
class="rounded-md px-4 py-2 text-sm font-bold tracking-wide uppercase transition {activeTab ===
|
class="rounded-md px-2 py-2 text-sm font-bold tracking-wide uppercase transition sm:px-4 {activeTab ===
|
||||||
'system'
|
'system'
|
||||||
? 'bg-primary text-white'
|
? 'bg-primary text-primary-fg'
|
||||||
: 'text-ink-2 hover:text-ink'}"
|
: 'text-ink-2 hover:text-ink'}"
|
||||||
onclick={() => (activeTab = 'system')}>{m.admin_tab_system()}</button
|
onclick={() => (activeTab = 'system')}>{m.admin_tab_system()}</button
|
||||||
>
|
>
|
||||||
|
|||||||
@@ -212,7 +212,7 @@ function cancelEditGroup() {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="w-full rounded bg-primary px-6 py-2 text-sm font-bold text-white uppercase hover:bg-accent hover:text-ink md:w-auto"
|
class="w-full rounded bg-primary px-6 py-2 text-sm font-bold text-primary-fg uppercase hover:bg-accent hover:text-ink md:w-auto"
|
||||||
>
|
>
|
||||||
{m.btn_create()}
|
{m.btn_create()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -41,7 +41,7 @@ async function backfillFileHashes() {
|
|||||||
<button
|
<button
|
||||||
onclick={backfillVersions}
|
onclick={backfillVersions}
|
||||||
disabled={backfillLoading}
|
disabled={backfillLoading}
|
||||||
class="rounded bg-primary px-6 py-2 text-sm font-bold text-white uppercase transition hover:bg-accent hover:text-ink disabled:cursor-not-allowed disabled:opacity-50"
|
class="rounded bg-primary px-6 py-2 text-sm font-bold text-primary-fg uppercase transition hover:bg-accent hover:text-ink disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{backfillLoading ? '…' : m.admin_system_backfill_btn()}
|
{backfillLoading ? '…' : m.admin_system_backfill_btn()}
|
||||||
</button>
|
</button>
|
||||||
@@ -60,7 +60,7 @@ async function backfillFileHashes() {
|
|||||||
<button
|
<button
|
||||||
onclick={backfillFileHashes}
|
onclick={backfillFileHashes}
|
||||||
disabled={backfillHashesLoading}
|
disabled={backfillHashesLoading}
|
||||||
class="rounded bg-primary px-6 py-2 text-sm font-bold text-white uppercase transition hover:bg-accent hover:text-ink disabled:cursor-not-allowed disabled:opacity-50"
|
class="rounded bg-primary px-6 py-2 text-sm font-bold text-primary-fg uppercase transition hover:bg-accent hover:text-ink disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
>
|
>
|
||||||
{backfillHashesLoading ? '…' : m.admin_system_backfill_hashes_btn()}
|
{backfillHashesLoading ? '…' : m.admin_system_backfill_hashes_btn()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ let {
|
|||||||
<h2 class="text-lg font-bold text-ink-2">{m.admin_section_users()}</h2>
|
<h2 class="text-lg font-bold text-ink-2">{m.admin_section_users()}</h2>
|
||||||
<a
|
<a
|
||||||
href="/admin/users/new"
|
href="/admin/users/new"
|
||||||
class="inline-flex items-center gap-1 rounded-sm bg-primary px-4 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
|
class="inline-flex items-center gap-1 rounded-sm bg-primary px-4 py-2 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-opacity hover:opacity-80"
|
||||||
>
|
>
|
||||||
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
<svg class="h-4 w-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||||
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4" />
|
||||||
|
|||||||
@@ -85,7 +85,7 @@ const selectedGroupIds = $derived(data.editUser.groups?.map((g: { id: string })
|
|||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
|
class="rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-opacity hover:opacity-80"
|
||||||
>
|
>
|
||||||
{m.btn_save()}
|
{m.btn_save()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ let { data, form } = $props();
|
|||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
|
class="rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-opacity hover:opacity-80"
|
||||||
>
|
>
|
||||||
{m.btn_create()}
|
{m.btn_create()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ let {
|
|||||||
<button
|
<button
|
||||||
data-testid="conv-swap-btn"
|
data-testid="conv-swap-btn"
|
||||||
onclick={onswapPersons}
|
onclick={onswapPersons}
|
||||||
class="flex w-full items-center justify-center gap-2 border border-line px-3 py-2.5 text-xs font-bold tracking-widest text-ink uppercase transition-colors hover:bg-primary hover:text-white md:w-auto {senderId &&
|
class="flex w-full items-center justify-center gap-2 border border-line px-3 py-2.5 text-xs font-bold tracking-widest text-ink uppercase transition-colors hover:bg-primary hover:text-primary-fg md:w-auto {senderId &&
|
||||||
receiverId
|
receiverId
|
||||||
? ''
|
? ''
|
||||||
: 'invisible'}"
|
: 'invisible'}"
|
||||||
@@ -121,7 +121,7 @@ let {
|
|||||||
<div>
|
<div>
|
||||||
<button
|
<button
|
||||||
onclick={ontoggleSort}
|
onclick={ontoggleSort}
|
||||||
class="flex h-[42px] w-full items-center justify-center border border-line text-xs font-bold tracking-wide text-ink uppercase transition-colors hover:bg-primary hover:text-white"
|
class="flex h-[42px] w-full items-center justify-center border border-line text-xs font-bold tracking-wide text-ink uppercase transition-colors hover:bg-primary hover:text-primary-fg"
|
||||||
>
|
>
|
||||||
<span class="mr-2">{m.conv_sort_label()}</span>
|
<span class="mr-2">{m.conv_sort_label()}</span>
|
||||||
<span>{sortDir === 'DESC' ? m.conv_sort_newest() : m.conv_sort_oldest()}</span>
|
<span>{sortDir === 'DESC' ? m.conv_sort_newest() : m.conv_sort_oldest()}</span>
|
||||||
|
|||||||
@@ -2,14 +2,24 @@
|
|||||||
import { m } from '$lib/paraglide/messages.js';
|
import { m } from '$lib/paraglide/messages.js';
|
||||||
|
|
||||||
let { originalFilename }: { originalFilename: string } = $props();
|
let { originalFilename }: { originalFilename: string } = $props();
|
||||||
|
|
||||||
|
let selectedFilename = $state<string | null>(null);
|
||||||
|
|
||||||
|
function handleFileChange(e: Event) {
|
||||||
|
const file = (e.target as HTMLInputElement).files?.[0];
|
||||||
|
selectedFilename = file?.name ?? null;
|
||||||
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="rounded-sm border border-line bg-surface p-6 shadow-sm">
|
<div class="rounded-sm border border-line bg-surface shadow-sm">
|
||||||
<h2 class="mb-5 text-xs font-bold tracking-widest text-ink-3 uppercase">
|
<div class="border-b border-line px-6 py-4">
|
||||||
{m.doc_section_file()}
|
<h2 class="text-xs font-bold tracking-widest text-ink-3 uppercase">
|
||||||
</h2>
|
{m.doc_section_file()}
|
||||||
|
</h2>
|
||||||
|
</div>
|
||||||
|
|
||||||
<div class="mb-4 flex items-center gap-3 rounded bg-muted px-3 py-2 text-sm text-ink-2">
|
<!-- Current file -->
|
||||||
|
<div class="flex items-center gap-3 border-b border-line px-6 py-3 text-sm text-ink-2">
|
||||||
<img
|
<img
|
||||||
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/PDF-Document-MD.svg"
|
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/PDF-Document-MD.svg"
|
||||||
alt=""
|
alt=""
|
||||||
@@ -22,19 +32,31 @@ let { originalFilename }: { originalFilename: string } = $props();
|
|||||||
>
|
>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<label for="file-upload" class="mb-1 block text-sm font-medium text-ink-2">
|
<!-- Replace file upload zone -->
|
||||||
{m.doc_file_replace_label()}
|
<label
|
||||||
<span class="font-normal text-ink-3">({m.doc_file_replace_note()})</span>
|
for="file-upload"
|
||||||
|
class="flex cursor-pointer flex-col items-center gap-3 px-6 py-8 transition-colors hover:bg-muted/40"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
class="h-8 w-8 text-ink-3"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<path
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
stroke-width="1.5"
|
||||||
|
d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
{#if selectedFilename}
|
||||||
|
<span class="text-sm font-medium text-ink">{selectedFilename}</span>
|
||||||
|
{:else}
|
||||||
|
<span class="text-sm font-medium text-ink-2">{m.doc_file_replace_label()}</span>
|
||||||
|
<span class="text-xs text-ink-3">{m.doc_file_replace_note()}</span>
|
||||||
|
{/if}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input id="file-upload" type="file" name="file" onchange={handleFileChange} class="sr-only" />
|
||||||
id="file-upload"
|
|
||||||
type="file"
|
|
||||||
name="file"
|
|
||||||
class="block w-full cursor-pointer text-sm
|
|
||||||
text-ink-2 file:mr-4 file:rounded
|
|
||||||
file:border-0 file:bg-muted
|
|
||||||
file:px-4 file:py-2
|
|
||||||
file:text-sm file:font-semibold
|
|
||||||
file:text-ink hover:file:bg-muted"
|
|
||||||
/>
|
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -7,73 +7,79 @@ let confirmDelete = $state(false);
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div
|
<div
|
||||||
class="sticky bottom-0 z-10 -mx-4 flex items-center justify-between border-t border-line bg-surface px-6 py-4 shadow-[0_-2px_8px_rgba(0,0,0,0.06)]"
|
class="sticky bottom-0 z-10 -mx-4 border-t border-line bg-surface px-4 py-3 shadow-[0_-2px_8px_rgba(0,0,0,0.06)] sm:px-6 sm:py-4"
|
||||||
>
|
>
|
||||||
<!-- Left: delete -->
|
<!-- Desktop: delete left, cancel+buttons right -->
|
||||||
<div class="flex items-center gap-3">
|
<!-- Mobile: action buttons stacked full-width, delete+cancel row at bottom -->
|
||||||
{#if confirmDelete}
|
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||||
<span class="font-sans text-sm text-red-700">{m.doc_delete_confirm()}</span>
|
<!-- Primary actions first (top on mobile, right on desktop) -->
|
||||||
|
<div class="flex flex-col gap-2 sm:order-last sm:flex-row sm:items-center sm:gap-4">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
form="delete-form"
|
form="mark-for-review-form"
|
||||||
class="rounded bg-red-600 px-4 py-1.5 text-sm font-bold text-white transition-colors hover:bg-red-700"
|
class="w-full rounded-sm border border-line px-4 py-2.5 font-sans text-xs font-bold tracking-widest text-ink-2 uppercase transition-colors hover:bg-muted sm:w-auto sm:py-2"
|
||||||
>
|
>
|
||||||
{m.btn_delete()}
|
{m.btn_mark_for_review()}
|
||||||
</button>
|
</button>
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="submit"
|
||||||
onclick={() => (confirmDelete = false)}
|
class="w-full rounded bg-primary px-6 py-2.5 font-sans text-sm font-bold tracking-widest text-primary-fg uppercase transition-colors hover:bg-primary/80 sm:w-auto sm:py-2"
|
||||||
class="text-sm text-ink-2 transition-colors hover:text-ink"
|
|
||||||
>
|
>
|
||||||
{m.btn_cancel()}
|
{m.btn_save()}
|
||||||
</button>
|
</button>
|
||||||
{:else}
|
</div>
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onclick={() => (confirmDelete = true)}
|
|
||||||
class="flex items-center gap-1.5 rounded border border-red-300 px-4 py-1.5 text-sm font-bold text-red-600 transition-colors hover:border-red-600 hover:bg-red-50"
|
|
||||||
>
|
|
||||||
<svg
|
|
||||||
xmlns="http://www.w3.org/2000/svg"
|
|
||||||
class="h-4 w-4"
|
|
||||||
viewBox="0 0 24 24"
|
|
||||||
fill="none"
|
|
||||||
stroke="currentColor"
|
|
||||||
stroke-width="2"
|
|
||||||
stroke-linecap="round"
|
|
||||||
stroke-linejoin="round"
|
|
||||||
aria-hidden="true"
|
|
||||||
>
|
|
||||||
<polyline points="3 6 5 6 21 6" />
|
|
||||||
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
|
|
||||||
<path d="M10 11v6M14 11v6" />
|
|
||||||
<path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2" />
|
|
||||||
</svg>
|
|
||||||
{m.btn_delete()}
|
|
||||||
</button>
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Right: cancel + mark for review + save -->
|
<!-- Secondary: delete + cancel (row on both mobile and desktop) -->
|
||||||
<div class="flex items-center gap-4">
|
<div class="flex items-center justify-between sm:justify-start sm:gap-4">
|
||||||
<a
|
{#if confirmDelete}
|
||||||
href="/documents/{docId}"
|
<span class="font-sans text-sm text-red-700">{m.doc_delete_confirm()}</span>
|
||||||
class="text-sm font-medium text-ink-2 transition-colors hover:text-ink"
|
<div class="flex items-center gap-3">
|
||||||
>
|
<button
|
||||||
{m.btn_cancel()}
|
type="submit"
|
||||||
</a>
|
form="delete-form"
|
||||||
<button
|
class="rounded bg-red-600 px-4 py-1.5 text-sm font-bold text-white transition-colors hover:bg-red-700"
|
||||||
type="submit"
|
>
|
||||||
form="mark-for-review-form"
|
{m.btn_delete()}
|
||||||
class="rounded-sm border border-gray-300 px-4 py-2 font-sans text-xs font-bold tracking-widest text-gray-600 uppercase transition-colors hover:bg-gray-50"
|
</button>
|
||||||
>
|
<button
|
||||||
{m.btn_mark_for_review()}
|
type="button"
|
||||||
</button>
|
onclick={() => (confirmDelete = false)}
|
||||||
<button
|
class="text-sm text-ink-2 transition-colors hover:text-ink"
|
||||||
type="submit"
|
>
|
||||||
class="rounded bg-primary px-6 py-2 text-sm font-bold tracking-widest text-white uppercase transition-colors hover:bg-primary/80"
|
{m.btn_cancel()}
|
||||||
>
|
</button>
|
||||||
{m.btn_save()}
|
</div>
|
||||||
</button>
|
{:else}
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onclick={() => (confirmDelete = true)}
|
||||||
|
class="flex items-center gap-1.5 rounded border border-red-300 px-4 py-1.5 text-sm font-bold text-red-600 transition-colors hover:border-red-600 hover:bg-red-50"
|
||||||
|
>
|
||||||
|
<svg
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
class="h-4 w-4"
|
||||||
|
viewBox="0 0 24 24"
|
||||||
|
fill="none"
|
||||||
|
stroke="currentColor"
|
||||||
|
stroke-width="2"
|
||||||
|
stroke-linecap="round"
|
||||||
|
stroke-linejoin="round"
|
||||||
|
aria-hidden="true"
|
||||||
|
>
|
||||||
|
<polyline points="3 6 5 6 21 6" />
|
||||||
|
<path d="M19 6l-1 14a2 2 0 0 1-2 2H8a2 2 0 0 1-2-2L5 6" />
|
||||||
|
<path d="M10 11v6M14 11v6" />
|
||||||
|
<path d="M9 6V4a1 1 0 0 1 1-1h4a1 1 0 0 1 1 1v2" />
|
||||||
|
</svg>
|
||||||
|
{m.btn_delete()}
|
||||||
|
</button>
|
||||||
|
<a
|
||||||
|
href="/documents/{docId}"
|
||||||
|
class="text-sm font-medium text-ink-2 transition-colors hover:text-ink"
|
||||||
|
>
|
||||||
|
{m.btn_cancel()}
|
||||||
|
</a>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -120,30 +120,35 @@ $effect(() => {
|
|||||||
|
|
||||||
<!-- Sticky Save Bar -->
|
<!-- Sticky Save Bar -->
|
||||||
<div
|
<div
|
||||||
class="sticky bottom-0 z-10 -mx-4 flex items-center justify-between border-t border-line bg-surface px-6 py-4 shadow-[0_-2px_8px_rgba(0,0,0,0.06)]"
|
class="sticky bottom-0 z-10 -mx-4 border-t border-line bg-surface px-4 py-3 shadow-[0_-2px_8px_rgba(0,0,0,0.06)] sm:px-6 sm:py-4"
|
||||||
>
|
>
|
||||||
<a href="/" class="text-sm font-medium text-ink-2 transition-colors hover:text-ink">
|
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:justify-between">
|
||||||
{m.btn_cancel()}
|
<a
|
||||||
</a>
|
href="/"
|
||||||
<div class="flex items-center gap-3">
|
class="order-last text-center text-sm font-medium text-ink-2 transition-colors hover:text-ink sm:order-first sm:text-left"
|
||||||
<button
|
|
||||||
type="submit"
|
|
||||||
name="metadataComplete"
|
|
||||||
value="false"
|
|
||||||
formaction="?/save"
|
|
||||||
class="rounded-sm border border-gray-300 px-5 py-2 font-sans text-xs font-bold tracking-widest text-gray-600 uppercase transition-colors hover:bg-gray-50"
|
|
||||||
>
|
>
|
||||||
{m.btn_save()}
|
{m.btn_cancel()}
|
||||||
</button>
|
</a>
|
||||||
<button
|
<div class="flex flex-col gap-2 sm:flex-row sm:items-center sm:gap-3">
|
||||||
type="submit"
|
<button
|
||||||
name="metadataComplete"
|
type="submit"
|
||||||
value="true"
|
name="metadataComplete"
|
||||||
formaction="?/saveReviewed"
|
value="false"
|
||||||
class="rounded-sm bg-brand-navy px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-colors hover:bg-brand-navy/90"
|
formaction="?/save"
|
||||||
>
|
class="w-full rounded-sm border border-line px-5 py-2.5 font-sans text-xs font-bold tracking-widest text-ink-2 uppercase transition-colors hover:bg-muted sm:w-auto sm:py-2"
|
||||||
{m.btn_save_and_mark_reviewed()}
|
>
|
||||||
</button>
|
{m.btn_save()}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
name="metadataComplete"
|
||||||
|
value="true"
|
||||||
|
formaction="?/saveReviewed"
|
||||||
|
class="w-full rounded-sm bg-primary px-5 py-2.5 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-colors hover:bg-primary/90 sm:w-auto sm:py-2"
|
||||||
|
>
|
||||||
|
{m.btn_save_and_mark_reviewed()}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ function formatUploadDate(createdAt: string): string {
|
|||||||
<!-- Back Link -->
|
<!-- Back Link -->
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
class="group mb-4 inline-flex items-center font-sans text-xs font-bold tracking-widest text-gray-500 uppercase transition-colors hover:text-brand-navy"
|
class="group mb-4 inline-flex items-center font-sans text-xs font-bold tracking-widest text-ink-2 uppercase transition-colors hover:text-ink"
|
||||||
>
|
>
|
||||||
<img
|
<img
|
||||||
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Arrow/Arrow-Left-MD.svg"
|
src="/degruyter-icons/Simple/Medium-24px/SVG/Action/Arrow/Arrow-Left-MD.svg"
|
||||||
@@ -47,7 +47,7 @@ function formatUploadDate(createdAt: string): string {
|
|||||||
{#if count > 0}
|
{#if count > 0}
|
||||||
<a
|
<a
|
||||||
href="/enrich/{documents[0].id}"
|
href="/enrich/{documents[0].id}"
|
||||||
class="bg-brand-navy px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-colors hover:bg-brand-navy/90"
|
class="bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-colors hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{m.enrich_list_start()}
|
{m.enrich_list_start()}
|
||||||
</a>
|
</a>
|
||||||
@@ -76,8 +76,8 @@ function formatUploadDate(createdAt: string): string {
|
|||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<!-- Document Rows -->
|
<!-- Document Rows -->
|
||||||
<div class="border-brand-sand border bg-white shadow-sm">
|
<div class="border border-line bg-white shadow-sm">
|
||||||
<ul class="divide-brand-sand divide-y">
|
<ul class="divide-y divide-line-2">
|
||||||
{#each documents as doc (doc.id)}
|
{#each documents as doc (doc.id)}
|
||||||
<li class="group hover:bg-brand-sand/30 transition-colors duration-200">
|
<li class="group hover:bg-brand-sand/30 transition-colors duration-200">
|
||||||
<a href="/enrich/{doc.id}" class="flex items-center justify-between p-6">
|
<a href="/enrich/{doc.id}" class="flex items-center justify-between p-6">
|
||||||
|
|||||||
@@ -156,7 +156,7 @@ let selectedReceivers = $state(untrack(() => doc.receivers ?? []));
|
|||||||
type="submit"
|
type="submit"
|
||||||
form="save-form"
|
form="save-form"
|
||||||
formaction="?/saveAndReview"
|
formaction="?/saveAndReview"
|
||||||
class="rounded-sm bg-brand-navy px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-colors hover:bg-brand-navy/90"
|
class="rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-colors hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{m.btn_save_and_mark_reviewed()}
|
{m.btn_save_and_mark_reviewed()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -17,21 +17,21 @@ import { m } from '$lib/paraglide/messages.js';
|
|||||||
{m.enrich_done_heading()}
|
{m.enrich_done_heading()}
|
||||||
</h1>
|
</h1>
|
||||||
|
|
||||||
<p class="mt-2 max-w-xs font-sans text-sm text-gray-500">
|
<p class="mt-2 max-w-xs font-sans text-sm text-ink-2">
|
||||||
{m.enrich_done_body()}
|
{m.enrich_done_body()}
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
<div class="mt-8 flex flex-col items-center gap-4">
|
<div class="mt-8 flex flex-col items-center gap-4">
|
||||||
<a
|
<a
|
||||||
href="/"
|
href="/"
|
||||||
class="bg-brand-navy px-6 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-colors hover:bg-brand-navy/90"
|
class="bg-primary px-6 py-2 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-colors hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{m.btn_back_to_overview()}
|
{m.btn_back_to_overview()}
|
||||||
</a>
|
</a>
|
||||||
|
|
||||||
<a
|
<a
|
||||||
href="/enrich"
|
href="/enrich"
|
||||||
class="font-sans text-xs text-gray-400 underline-offset-4 transition-colors hover:text-brand-navy hover:underline"
|
class="font-sans text-xs text-ink-2 underline-offset-4 transition-colors hover:text-ink hover:underline"
|
||||||
>
|
>
|
||||||
{m.enrich_back_to_list()}
|
{m.enrich_back_to_list()}
|
||||||
</a>
|
</a>
|
||||||
|
|||||||
@@ -5,9 +5,6 @@ let { form }: { form?: { error?: string; success?: boolean } } = $props();
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative flex min-h-screen flex-col bg-surface">
|
<div class="relative flex min-h-screen flex-col bg-surface">
|
||||||
<!-- Accent strip -->
|
|
||||||
<div class="h-1 bg-brand-purple"></div>
|
|
||||||
|
|
||||||
<div class="flex flex-1 items-center justify-center px-4">
|
<div class="flex flex-1 items-center justify-center px-4">
|
||||||
<div class="w-full max-w-sm">
|
<div class="w-full max-w-sm">
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
@@ -57,7 +54,7 @@ let { form }: { form?: { error?: string; success?: boolean } } = $props();
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="mt-2 w-full bg-primary py-2.5 font-sans text-xs font-bold tracking-widest text-white uppercase transition-colors hover:bg-primary/90"
|
class="mt-2 w-full bg-primary py-2.5 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-colors hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{m.forgot_password_submit()}
|
{m.forgot_password_submit()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -10,7 +10,6 @@
|
|||||||
--palette-mint: #a1dcd8;
|
--palette-mint: #a1dcd8;
|
||||||
--palette-turquoise: #00c7b1;
|
--palette-turquoise: #00c7b1;
|
||||||
--palette-sand: #f0efe9;
|
--palette-sand: #f0efe9;
|
||||||
--palette-purple: #b4b9ff;
|
|
||||||
|
|
||||||
/* Typography */
|
/* Typography */
|
||||||
--font-sans: 'Montserrat', ui-sans-serif, system-ui, sans-serif;
|
--font-sans: 'Montserrat', ui-sans-serif, system-ui, sans-serif;
|
||||||
@@ -58,7 +57,6 @@
|
|||||||
--color-pdf-text: var(--c-pdf-text);
|
--color-pdf-text: var(--c-pdf-text);
|
||||||
|
|
||||||
/* Static brand tokens (not themed) */
|
/* Static brand tokens (not themed) */
|
||||||
--color-brand-purple: var(--palette-purple);
|
|
||||||
--color-brand-navy: var(--palette-navy);
|
--color-brand-navy: var(--palette-navy);
|
||||||
--color-brand-mint: var(--palette-mint);
|
--color-brand-mint: var(--palette-mint);
|
||||||
}
|
}
|
||||||
@@ -74,8 +72,8 @@
|
|||||||
--c-line-2: #eeede8;
|
--c-line-2: #eeede8;
|
||||||
|
|
||||||
--c-ink: #012851;
|
--c-ink: #012851;
|
||||||
--c-ink-2: #6b7280;
|
--c-ink-2: #4b5563; /* gray-600 — 7.6:1 on white, 6.6:1 on canvas — WCAG AA ✓ */
|
||||||
--c-ink-3: #9ca3af;
|
--c-ink-3: #6b7280; /* gray-500 — 4.8:1 on white — WCAG AA ✓; use only on surface, not canvas */
|
||||||
|
|
||||||
--c-accent: #a1dcd8;
|
--c-accent: #a1dcd8;
|
||||||
--c-accent-bg: rgba(161, 220, 216, 0.15);
|
--c-accent-bg: rgba(161, 220, 216, 0.15);
|
||||||
@@ -98,12 +96,12 @@
|
|||||||
--c-overlay: #242424;
|
--c-overlay: #242424;
|
||||||
--c-muted: #252525;
|
--c-muted: #252525;
|
||||||
|
|
||||||
--c-line: #2e2e2e;
|
--c-line: #3d3d3d;
|
||||||
--c-line-2: #222222;
|
--c-line-2: #2e2e2e;
|
||||||
|
|
||||||
--c-ink: #f0efe9;
|
--c-ink: #f0efe9;
|
||||||
--c-ink-2: #9ca3af;
|
--c-ink-2: #9ca3af; /* gray-400 — 7.5:1 on dark surface — WCAG AAA ✓ */
|
||||||
--c-ink-3: #6b7280;
|
--c-ink-3: #8b97a5; /* gray-450 — 6.5:1 on dark surface — WCAG AA ✓ */
|
||||||
|
|
||||||
--c-accent: #00c7b1;
|
--c-accent: #00c7b1;
|
||||||
--c-accent-bg: rgba(0, 199, 177, 0.12);
|
--c-accent-bg: rgba(0, 199, 177, 0.12);
|
||||||
@@ -126,8 +124,8 @@
|
|||||||
--c-overlay: #242424;
|
--c-overlay: #242424;
|
||||||
--c-muted: #252525;
|
--c-muted: #252525;
|
||||||
|
|
||||||
--c-line: #2e2e2e;
|
--c-line: #3d3d3d;
|
||||||
--c-line-2: #222222;
|
--c-line-2: #2e2e2e;
|
||||||
|
|
||||||
--c-ink: #f0efe9;
|
--c-ink: #f0efe9;
|
||||||
--c-ink-2: #9ca3af;
|
--c-ink-2: #9ca3af;
|
||||||
|
|||||||
@@ -10,9 +10,6 @@ const activeLocale = $derived(getLocale().toUpperCase());
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative flex min-h-screen flex-col bg-canvas">
|
<div class="relative flex min-h-screen flex-col bg-canvas">
|
||||||
<!-- DGB purple accent strip -->
|
|
||||||
<div class="h-1 bg-brand-purple"></div>
|
|
||||||
|
|
||||||
<!-- Language switcher -->
|
<!-- Language switcher -->
|
||||||
<div class="absolute top-4 right-4 flex items-center gap-1">
|
<div class="absolute top-4 right-4 flex items-center gap-1">
|
||||||
{#each locales as locale (locale)}
|
{#each locales as locale (locale)}
|
||||||
@@ -85,7 +82,7 @@ const activeLocale = $derived(getLocale().toUpperCase());
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="mt-2 w-full bg-primary py-2.5 font-sans text-xs font-bold tracking-widest text-white uppercase transition-colors hover:bg-primary/90"
|
class="mt-2 w-full bg-primary py-2.5 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-colors hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{m.login_btn_submit()}
|
{m.login_btn_submit()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ function handleSearch() {
|
|||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="mx-auto max-w-7xl py-12 sm:px-6 lg:px-8">
|
<div class="mx-auto max-w-7xl px-4 py-12 sm:px-6 lg:px-8">
|
||||||
<!-- Header Area -->
|
<!-- Header Area -->
|
||||||
<div
|
<div
|
||||||
class="mb-10 flex flex-col justify-between gap-6 border-b border-ink/10 pb-6 md:flex-row md:items-end"
|
class="mb-10 flex flex-col justify-between gap-6 border-b border-ink/10 pb-6 md:flex-row md:items-end"
|
||||||
@@ -107,7 +107,7 @@ function handleSearch() {
|
|||||||
<!-- Avatar -->
|
<!-- Avatar -->
|
||||||
<div class="flex-shrink-0">
|
<div class="flex-shrink-0">
|
||||||
<div
|
<div
|
||||||
class="flex h-12 w-12 items-center justify-center rounded-full bg-primary font-serif text-lg text-white transition-colors group-hover:bg-accent group-hover:text-ink"
|
class="flex h-12 w-12 items-center justify-center rounded-full bg-primary font-serif text-lg text-primary-fg transition-colors group-hover:bg-accent group-hover:text-ink"
|
||||||
>
|
>
|
||||||
{person.firstName[0]}{person.lastName[0]}
|
{person.firstName[0]}{person.lastName[0]}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -143,7 +143,7 @@ $effect(() => {
|
|||||||
<div class="flex gap-3">
|
<div class="flex gap-3">
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="rounded bg-primary px-5 py-2 text-sm font-bold tracking-widest text-white uppercase transition-colors hover:bg-primary/80"
|
class="rounded bg-primary px-5 py-2 text-sm font-bold tracking-widest text-primary-fg uppercase transition-colors hover:bg-primary/80"
|
||||||
>
|
>
|
||||||
{m.btn_save()}
|
{m.btn_save()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ const visibleDocuments = $derived(
|
|||||||
<div class="mb-10">
|
<div class="mb-10">
|
||||||
<div class="mb-6 flex items-center gap-3 border-b border-ink/10 pb-2">
|
<div class="mb-6 flex items-center gap-3 border-b border-ink/10 pb-2">
|
||||||
<h2 class="font-serif text-xl text-ink">{heading}</h2>
|
<h2 class="font-serif text-xl text-ink">{heading}</h2>
|
||||||
<span class="rounded-full bg-primary px-2 py-1 text-xs font-bold text-white">
|
<span class="rounded-full bg-primary px-2 py-1 text-xs font-bold text-primary-fg">
|
||||||
{documents.length}
|
{documents.length}
|
||||||
</span>
|
</span>
|
||||||
{#if yearRange}
|
{#if yearRange}
|
||||||
|
|||||||
@@ -89,7 +89,7 @@ let { form } = $props();
|
|||||||
</a>
|
</a>
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="rounded bg-primary px-6 py-2 text-sm font-bold tracking-widest text-white uppercase transition-colors hover:bg-primary/80"
|
class="rounded bg-primary px-6 py-2 text-sm font-bold tracking-widest text-primary-fg uppercase transition-colors hover:bg-primary/80"
|
||||||
>
|
>
|
||||||
{m.btn_create()}
|
{m.btn_create()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -70,7 +70,7 @@ let {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="mt-5 rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
|
class="mt-5 rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-opacity hover:opacity-80"
|
||||||
>
|
>
|
||||||
{m.btn_save()}
|
{m.btn_save()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -115,7 +115,7 @@ function handleBirthDateInput(e: Event) {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="mt-5 rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-white uppercase transition-opacity hover:opacity-80"
|
class="mt-5 rounded-sm bg-primary px-5 py-2 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-opacity hover:opacity-80"
|
||||||
>
|
>
|
||||||
{m.btn_save()}
|
{m.btn_save()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -12,9 +12,6 @@ let {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<div class="relative flex min-h-screen flex-col bg-surface">
|
<div class="relative flex min-h-screen flex-col bg-surface">
|
||||||
<!-- Accent strip -->
|
|
||||||
<div class="h-1 bg-brand-purple"></div>
|
|
||||||
|
|
||||||
<div class="flex flex-1 items-center justify-center px-4">
|
<div class="flex flex-1 items-center justify-center px-4">
|
||||||
<div class="w-full max-w-sm">
|
<div class="w-full max-w-sm">
|
||||||
<!-- Logo -->
|
<!-- Logo -->
|
||||||
@@ -86,7 +83,7 @@ let {
|
|||||||
|
|
||||||
<button
|
<button
|
||||||
type="submit"
|
type="submit"
|
||||||
class="mt-2 w-full bg-primary py-2.5 font-sans text-xs font-bold tracking-widest text-white uppercase transition-colors hover:bg-primary/90"
|
class="mt-2 w-full bg-primary py-2.5 font-sans text-xs font-bold tracking-widest text-primary-fg uppercase transition-colors hover:bg-primary/90"
|
||||||
>
|
>
|
||||||
{m.reset_password_submit()}
|
{m.reset_password_submit()}
|
||||||
</button>
|
</button>
|
||||||
|
|||||||
@@ -45,13 +45,13 @@ const initials = $derived.by(() => {
|
|||||||
<div class="mb-5 flex justify-center">
|
<div class="mb-5 flex justify-center">
|
||||||
{#if initials}
|
{#if initials}
|
||||||
<div
|
<div
|
||||||
class="flex h-16 w-16 items-center justify-center rounded-full bg-primary text-white"
|
class="flex h-16 w-16 items-center justify-center rounded-full bg-primary text-primary-fg"
|
||||||
>
|
>
|
||||||
<span class="font-serif text-xl font-bold">{initials}</span>
|
<span class="font-serif text-xl font-bold">{initials}</span>
|
||||||
</div>
|
</div>
|
||||||
{:else}
|
{:else}
|
||||||
<div
|
<div
|
||||||
class="flex h-16 w-16 items-center justify-center rounded-full bg-primary text-white"
|
class="flex h-16 w-16 items-center justify-center rounded-full bg-primary text-primary-fg"
|
||||||
>
|
>
|
||||||
<svg
|
<svg
|
||||||
class="h-8 w-8"
|
class="h-8 w-8"
|
||||||
|
|||||||
Reference in New Issue
Block a user