148 lines
4.4 KiB
Svelte
148 lines
4.4 KiB
Svelte
<script lang="ts">
|
||
import { onMount } from 'svelte';
|
||
import { page } from '$app/stores';
|
||
import { api, type TallyEntry } from '$lib/api/client';
|
||
import Header from '$lib/components/Header.svelte';
|
||
import MonthPicker from '$lib/components/MonthPicker.svelte';
|
||
|
||
let token = '';
|
||
let entries: TallyEntry[] = [];
|
||
let loading = true;
|
||
let selectedMonth = new Date().toISOString().slice(0, 7);
|
||
let deletingId: number | null = null;
|
||
let confirmId: number | null = null;
|
||
|
||
$: token = $page.url.searchParams.get('token') ?? '';
|
||
|
||
onMount(async () => {
|
||
if (!token) return;
|
||
await loadEntries();
|
||
});
|
||
|
||
async function loadEntries() {
|
||
loading = true;
|
||
entries = await api.companyAdmin.getTallyEntries(token, selectedMonth);
|
||
loading = false;
|
||
}
|
||
|
||
async function changeMonth(e: CustomEvent<string>) {
|
||
selectedMonth = e.detail;
|
||
await loadEntries();
|
||
}
|
||
|
||
function askConfirm(id: number) {
|
||
confirmId = id;
|
||
}
|
||
|
||
async function confirmDelete() {
|
||
if (confirmId === null) return;
|
||
const idToDelete = confirmId;
|
||
deletingId = idToDelete;
|
||
confirmId = null;
|
||
await api.companyAdmin.deleteTallyEntry(token, idToDelete);
|
||
deletingId = null;
|
||
await loadEntries();
|
||
}
|
||
|
||
function formatPrice(cents: number): string {
|
||
return (cents / 100).toFixed(2).replace('.', ',') + ' €';
|
||
}
|
||
|
||
function formatDateTime(iso: string): string {
|
||
const d = new Date(iso);
|
||
return d.toLocaleDateString('de-DE') + ' ' + d.toLocaleTimeString('de-DE', { hour: '2-digit', minute: '2-digit' });
|
||
}
|
||
</script>
|
||
|
||
<svelte:head>
|
||
<title>Einträge löschen – Qaffee</title>
|
||
</svelte:head>
|
||
|
||
<Header title="Einträge verwalten" backHref="/admin/company?token={token}" />
|
||
|
||
<div class="admin-container">
|
||
{#if !token}
|
||
<p>Kein Zugangstoken angegeben.</p>
|
||
{:else}
|
||
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 24px;">
|
||
<h2>Produkteinträge</h2>
|
||
<MonthPicker bind:value={selectedMonth} on:change={changeMonth} />
|
||
</div>
|
||
|
||
{#if loading}
|
||
<p>Laden...</p>
|
||
{:else if entries.length === 0}
|
||
<p style="color: var(--color-text-muted); text-align: center; margin-top: 40px;">
|
||
Keine Einträge in diesem Monat.
|
||
</p>
|
||
{:else}
|
||
<p style="color: var(--color-text-muted); margin-bottom: 12px; font-size: 0.9rem;">
|
||
{entries.length} Einträge · {formatPrice(entries.reduce((s, e) => s + e.priceCents, 0))} gesamt
|
||
</p>
|
||
<table class="admin-table">
|
||
<thead>
|
||
<tr>
|
||
<th>Zeitpunkt</th>
|
||
<th>Mitarbeiter</th>
|
||
<th>Produkt</th>
|
||
<th style="text-align: right;">Preis</th>
|
||
<th></th>
|
||
</tr>
|
||
</thead>
|
||
<tbody>
|
||
{#each entries as entry}
|
||
<tr>
|
||
<td style="color: var(--color-text-muted); font-size: 0.85rem; white-space: nowrap;">
|
||
{formatDateTime(entry.createdAt)}
|
||
</td>
|
||
<td>{entry.employeeFirstName} {entry.employeeLastName}</td>
|
||
<td>{entry.productName}</td>
|
||
<td style="text-align: right;" class="price">{formatPrice(entry.priceCents)}</td>
|
||
<td>
|
||
{#if deletingId === entry.id}
|
||
<span style="font-size: 0.85rem; color: var(--color-text-muted);">Löschen...</span>
|
||
{:else}
|
||
<button
|
||
class="btn btn-secondary"
|
||
style="padding: 5px 10px; font-size: 0.8rem; color: #e05555; border-color: #e05555;"
|
||
on:click={() => askConfirm(entry.id)}
|
||
>
|
||
Löschen
|
||
</button>
|
||
{/if}
|
||
</td>
|
||
</tr>
|
||
{/each}
|
||
</tbody>
|
||
</table>
|
||
{/if}
|
||
{/if}
|
||
</div>
|
||
|
||
{#if confirmId !== null}
|
||
{@const confirmEntry = entries.find(e => e.id === confirmId)}
|
||
<div class="modal-overlay" on:click|self={() => confirmId = null}>
|
||
<div class="modal">
|
||
<h2>Eintrag löschen?</h2>
|
||
{#if confirmEntry}
|
||
<p style="margin: 16px 0; color: var(--color-text-muted);">
|
||
<strong style="color: var(--color-text-muted);">{confirmEntry.employeeFirstName} {confirmEntry.employeeLastName}</strong>
|
||
– {confirmEntry.productName} ({formatPrice(confirmEntry.priceCents)})
|
||
am {formatDateTime(confirmEntry.createdAt)}
|
||
</p>
|
||
{/if}
|
||
<p style="margin-bottom: 20px; font-size: 0.9rem;">Dieser Eintrag wird unwiderruflich gelöscht.</p>
|
||
<div class="modal-actions">
|
||
<button class="btn btn-secondary" on:click={() => confirmId = null}>Abbrechen</button>
|
||
<button
|
||
class="btn btn-primary"
|
||
style="background: #e05555; border-color: #e05555;"
|
||
on:click={confirmDelete}
|
||
>
|
||
Löschen
|
||
</button>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
{/if}
|