110 lines
3.8 KiB
Svelte
110 lines
3.8 KiB
Svelte
<script lang="ts">
|
|
import { goto } from '$app/navigation';
|
|
import { liveQuery } from 'dexie';
|
|
import { db } from '$lib/db/schema';
|
|
import { upsertContext, contextNameExists } from '$lib/db/repositories';
|
|
import { newId } from '$lib/db/helpers';
|
|
import PersonList from '$lib/components/PersonList.svelte';
|
|
import type { PersonMeta, PersonSubType } from '@ka-note/shared';
|
|
|
|
const allPersons = liveQuery(() =>
|
|
db.contexts.filter(c => !c.deletedAt && c.type === 'person').sortBy('sortOrder')
|
|
);
|
|
|
|
const persons = $derived($allPersons ?? []);
|
|
|
|
type Tab = 'favorites' | 'all' | PersonSubType;
|
|
|
|
const tabs: { key: Tab; label: string }[] = [
|
|
{ key: 'favorites', label: '★' },
|
|
{ key: 'all', label: 'Alle' },
|
|
{ key: 'family', label: 'Familie' },
|
|
{ key: 'colleague', label: 'Kollegen' },
|
|
{ key: 'employee', label: 'Mitarbeiter' },
|
|
{ key: 'contact', label: 'Kontakte' },
|
|
{ key: 'acquaintance', label: 'Bekannte' }
|
|
];
|
|
|
|
let activeTab = $state<Tab>('all');
|
|
|
|
function sortAlpha(list: typeof persons) {
|
|
return [...list].sort((a, b) =>
|
|
a.name.replace('Person ', '').localeCompare(b.name.replace('Person ', ''), 'de')
|
|
);
|
|
}
|
|
|
|
const filtered = $derived(() => {
|
|
if (activeTab === 'favorites') return persons.filter(c => c.isFavorite);
|
|
if (activeTab === 'all') return sortAlpha(persons);
|
|
return sortAlpha(persons.filter(c => ((c.meta as PersonMeta | null)?.personSubType ?? 'contact') === activeTab));
|
|
});
|
|
|
|
function tabCount(key: Tab): number {
|
|
if (key === 'favorites') return persons.filter(c => c.isFavorite).length;
|
|
if (key === 'all') return persons.length;
|
|
return persons.filter(c => ((c.meta as PersonMeta | null)?.personSubType ?? 'contact') === key).length;
|
|
}
|
|
|
|
let creating = $state(false);
|
|
let newName = $state('');
|
|
let createError = $state('');
|
|
|
|
async function create() {
|
|
const name = newName.trim();
|
|
if (!name) return;
|
|
const fullName = `Person ${name}`;
|
|
if (await contextNameExists(fullName, 'person')) {
|
|
createError = `"${name}" existiert bereits`;
|
|
return;
|
|
}
|
|
const id = newId();
|
|
await upsertContext({ id, name: fullName, type: 'person', meta: { fullName: name, email: '', phone: '', duSince: '' } });
|
|
newName = '';
|
|
creating = false;
|
|
createError = '';
|
|
goto(`/context/${id}`);
|
|
}
|
|
|
|
function onKeydown(e: KeyboardEvent) {
|
|
if (e.key === 'Enter') { e.preventDefault(); create(); }
|
|
else if (e.key === 'Escape') { creating = false; newName = ''; createError = ''; }
|
|
}
|
|
</script>
|
|
|
|
<div class="mx-auto max-w-3xl p-6">
|
|
<div class="mb-4 flex items-center gap-3">
|
|
<h1 class="flex-1 text-2xl font-bold text-white">Alle Personen</h1>
|
|
<button class="rounded bg-accent px-3 py-1.5 text-sm font-medium text-white hover:bg-accent/80" onclick={() => creating = true}>+ Neu</button>
|
|
</div>
|
|
|
|
{#if creating}
|
|
<input
|
|
class="mb-4 w-full rounded bg-white/10 px-3 py-2 text-sm text-white outline-none placeholder:text-muted"
|
|
placeholder="Name der Person..."
|
|
bind:value={newName}
|
|
onkeydown={onKeydown}
|
|
oninput={() => createError = ''}
|
|
onblur={() => setTimeout(() => { creating = false; newName = ''; createError = ''; }, 150)}
|
|
autofocus
|
|
/>
|
|
{#if createError}<p class="mb-3 -mt-2 text-xs text-red-400">{createError}</p>{/if}
|
|
{/if}
|
|
|
|
<div class="mb-4 flex gap-1 overflow-x-auto border-b border-[#333] pb-2 scrollbar-none">
|
|
{#each tabs as tab}
|
|
{@const count = tabCount(tab.key)}
|
|
{#if count > 0 || tab.key === 'all'}
|
|
<button
|
|
class="shrink-0 rounded px-3 py-1 text-sm transition-colors {activeTab === tab.key ? 'bg-accent text-white' : 'text-muted hover:text-white'} {tab.key === 'favorites' ? 'text-yellow-400' : ''}"
|
|
onclick={() => activeTab = tab.key}
|
|
>
|
|
{tab.label}
|
|
{#if tab.key !== 'favorites'}<span class="ml-1 opacity-60">{count}</span>{/if}
|
|
</button>
|
|
{/if}
|
|
{/each}
|
|
</div>
|
|
|
|
<PersonList persons={filtered()} sortable={activeTab === 'favorites'} />
|
|
</div>
|