rewrite website
All checks were successful
deploy / build-and-deploy (push) Successful in 16s

This commit is contained in:
2025-10-13 17:22:49 +02:00
parent a6d910f56a
commit 55c0852b7e
263 changed files with 17905 additions and 14451 deletions

View File

@@ -0,0 +1,81 @@
<script lang="ts">
import type { Writable } from 'svelte/store';
import SortableTr from '@components/admin/table/SortableTr.svelte';
import SortableTh from '@components/admin/table/SortableTh.svelte';
import Icon from '@iconify/svelte';
import type { Snippet } from 'svelte';
import { getObjectEntryByKey } from '@util/objects.ts';
// types
interface Props<T> {
data: Writable<T[]>;
count?: boolean;
keys: {
key: string;
label: string;
width?: number;
sortable?: boolean;
transform?: Snippet<[T]>;
}[];
onClick?: (t: T) => void;
onEdit?: (t: T) => void;
onDelete?: (t: T) => void;
}
// input
let { data, count, keys, onClick, onEdit, onDelete }: Props<any> = $props();
</script>
<div class="h-screen overflow-x-auto">
<table class="table table-pin-rows">
<thead>
<SortableTr {data}>
{#if count}
<SortableTh style="width: 5%">#</SortableTh>
{/if}
{#each keys as key (key.key)}
<SortableTh style={key.width ? `width: ${key.width}%` : undefined} key={key.sortable ? key.key : undefined}
>{key.label}</SortableTh
>
{/each}
{#if onEdit || onDelete}
<SortableTh style="width: 5%"></SortableTh>
{/if}
</SortableTr>
</thead>
<tbody>
{#each $data as d, i (d)}
<tr class="hover:bg-base-200" onclick={() => onClick?.(d)}>
{#if count}
<td>{i + 1}</td>
{/if}
{#each keys as key (key.key)}
<td>
{#if key.transform}
{@render key.transform(getObjectEntryByKey(key.key, d))}
{:else}
{getObjectEntryByKey(key.key, d)}
{/if}
</td>
{/each}
{#if onEdit || onDelete}
<td>
{#if onEdit}
<button class="cursor-pointer" onclick={() => onEdit(d)}>
<Icon icon="heroicons:pencil-square" />
</button>
{/if}
{#if onDelete}
<button class="cursor-pointer" onclick={() => onDelete(d)}>
<Icon icon="heroicons:trash" />
</button>
{/if}
</td>
{/if}
</tr>
{/each}
</tbody>
</table>
</div>

View File

@@ -0,0 +1,48 @@
<script lang="ts">
import { getContext, type Snippet } from 'svelte';
import type { Writable } from 'svelte/store';
import Icon from '@iconify/svelte';
// types
interface Props {
key?: string;
children?: Snippet;
}
interface SortableHeaderContext {
headerKey: Writable<string>;
onSort: (key: string, order: 'asc' | 'desc') => void;
}
// inputs
const { key, children, ...restProps }: Props & Record<string, any> = $props();
let { headerKey, onSort }: SortableHeaderContext = getContext('sortableHeader');
let asc = $state(false);
// callbacks
function onButtonClick() {
if (key == undefined) return;
$headerKey = key;
asc = !asc;
onSort(key, asc ? 'asc' : 'desc');
}
</script>
<th {...restProps}>
{#if key}
<button class="flex items-center gap-1" onclick={() => onButtonClick()}>
<span>{@render children?.()}</span>
{#if $headerKey === key && asc}
<Icon icon="heroicons:chevron-up-16-solid" />
{:else}
<Icon icon="heroicons:chevron-down-16-solid" />
{/if}
</button>
{:else}
{@render children?.()}
{/if}
</th>

View File

@@ -0,0 +1,48 @@
<script lang="ts">
import { setContext, type Snippet } from 'svelte';
import { type Writable, writable } from 'svelte/store';
import { getObjectEntryByKey } from '@util/objects.ts';
// types
interface Props {
data: Writable<{ [key: string]: any }[]>;
children: Snippet;
}
// inputs
const { data, children, ...restProps }: Props & Record<string, any> = $props();
setContext('sortableHeader', {
headerKey: writable(null),
onSort: onSort
});
// functions
function onSort(key: string, order: 'asc' | 'desc') {
data.update((old) => {
old.sort((a, b) => {
let entryA = getObjectEntryByKey(key, a);
let entryB = getObjectEntryByKey(key, b);
if (entryA === undefined || entryB === undefined) return 0;
if (typeof entryA === 'string') entryA = entryA.toLowerCase();
if (typeof entryB === 'string') entryB = entryB.toLowerCase();
if (order === 'asc') {
return entryA < entryB ? -1 : 1;
} else if (order === 'desc') {
return entryA > entryB ? -1 : 1;
} else {
return 0;
}
});
return old;
});
}
</script>
<tr {...restProps}>
{@render children()}
</tr>