Files
qaffee/frontend/src/routes/admin/company/+page.svelte
2026-03-31 14:48:36 +02:00

229 lines
6.9 KiB
Svelte
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script lang="ts">
import { onMount } from 'svelte';
import { page } from '$app/stores';
import { api, type Employee, type MonthlyReport, type EmployeeReportLine } from '$lib/api/client';
let token = '';
let employees: Employee[] = [];
let report: MonthlyReport | null = null;
let selectedEmployee: EmployeeReportLine | null = null;
let loading = true;
let selectedMonth = new Date().toISOString().slice(0, 7);
// Modal state
let showModal = false;
let editId: number | null = null;
let formFirstName = '';
let formLastName = '';
$: token = $page.url.searchParams.get('token') ?? '';
onMount(async () => {
if (!token) return;
await loadData();
});
async function loadData() {
loading = true;
[employees, report] = await Promise.all([
api.companyAdmin.getEmployees(token),
api.companyAdmin.getReport(token, selectedMonth)
]);
loading = false;
}
async function changeMonth() {
report = await api.companyAdmin.getReport(token, selectedMonth);
selectedEmployee = null;
}
function openCreate() {
editId = null;
formFirstName = '';
formLastName = '';
showModal = true;
}
function openEdit(emp: Employee) {
editId = emp.id;
formFirstName = emp.firstName;
formLastName = emp.lastName;
showModal = true;
}
async function saveEmployee() {
if (editId) {
await api.companyAdmin.updateEmployee(token, editId, formFirstName, formLastName);
} else {
await api.companyAdmin.createEmployee(token, formFirstName, formLastName);
}
showModal = false;
await loadData();
}
async function toggleEmployee(id: number) {
await api.companyAdmin.toggleEmployee(token, id);
await loadData();
}
async function showEmployeeDetail(employeeId: number) {
selectedEmployee = await api.companyAdmin.getEmployeeReport(token, employeeId, selectedMonth);
}
function formatPrice(cents: number): string {
return (cents / 100).toFixed(2).replace('.', ',') + ' €';
}
</script>
<svelte:head>
<title>Firmen-Admin - Strichliste</title>
</svelte:head>
<div class="admin-container">
<div class="admin-header">
<h1>Firmen-Administration</h1>
</div>
{#if !token}
<p>Kein Zugangstoken angegeben.</p>
{:else if loading}
<p>Laden...</p>
{:else}
<!-- Mitarbeiterverwaltung -->
<section style="margin-bottom: 40px;">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
<h2>Mitarbeiter</h2>
<button class="btn btn-primary" on:click={openCreate}>+ Mitarbeiter anlegen</button>
</div>
<table class="admin-table">
<thead>
<tr>
<th>Name</th>
<th>Status</th>
<th>Aktionen</th>
</tr>
</thead>
<tbody>
{#each employees as emp}
<tr>
<td>{emp.firstName} {emp.lastName}</td>
<td>
<span class="badge" class:badge-active={emp.active} class:badge-inactive={!emp.active}>
{emp.active ? 'Aktiv' : 'Inaktiv'}
</span>
</td>
<td>
<button class="btn btn-secondary" style="padding: 6px 12px; font-size: 0.85rem;" on:click={() => openEdit(emp)}>Bearbeiten</button>
<button class="btn btn-secondary" style="padding: 6px 12px; font-size: 0.85rem; margin-left: 8px;" on:click={() => toggleEmployee(emp.id)}>
{emp.active ? 'Deaktivieren' : 'Aktivieren'}
</button>
</td>
</tr>
{/each}
</tbody>
</table>
</section>
<!-- Monatsauswertung -->
<section>
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 16px;">
<h2>Monatsauswertung</h2>
<div class="month-selector">
<input type="month" bind:value={selectedMonth} on:change={changeMonth} />
</div>
</div>
{#if report}
<table class="admin-table">
<thead>
<tr>
<th>Mitarbeiter</th>
<th style="text-align: right;">Anzahl</th>
<th style="text-align: right;">Summe</th>
<th>Detail</th>
</tr>
</thead>
<tbody>
{#each report.employees as line}
<tr>
<td>{line.firstName} {line.lastName}</td>
<td style="text-align: right;">{line.totalCount}×</td>
<td style="text-align: right;" class="price">{formatPrice(line.totalCents)}</td>
<td>
<button class="btn btn-secondary" style="padding: 6px 12px; font-size: 0.85rem;" on:click={() => showEmployeeDetail(line.employeeId)}>Details</button>
</td>
</tr>
{/each}
{#if report.employees.length > 0}
<tr style="font-weight: 700;">
<td>Gesamt</td>
<td></td>
<td style="text-align: right;" class="price">{formatPrice(report.totalCents)}</td>
<td></td>
</tr>
{:else}
<tr><td colspan="4" style="text-align: center; color: var(--color-text-muted);">Keine Einträge in diesem Monat</td></tr>
{/if}
</tbody>
</table>
{/if}
{#if selectedEmployee}
<div style="margin-top: 24px; background: var(--color-bg-secondary); padding: 20px; border-radius: var(--radius);">
<div style="display: flex; justify-content: space-between; align-items: center;">
<h3>Detail: {selectedEmployee.firstName} {selectedEmployee.lastName}</h3>
<button class="btn btn-secondary" style="padding: 6px 12px; font-size: 0.85rem;" on:click={() => selectedEmployee = null}>Schließen</button>
</div>
<table class="admin-table" style="margin-top: 12px;">
<thead>
<tr>
<th>Produkt</th>
<th style="text-align: right;">Einzelpreis</th>
<th style="text-align: right;">Anzahl</th>
<th style="text-align: right;">Summe</th>
</tr>
</thead>
<tbody>
{#each selectedEmployee.products as prod}
<tr>
<td>{prod.productName}</td>
<td style="text-align: right;" class="price">{formatPrice(prod.priceCents)}</td>
<td style="text-align: right;">{prod.count}×</td>
<td style="text-align: right;" class="price">{formatPrice(prod.totalCents)}</td>
</tr>
{/each}
<tr style="font-weight: 700;">
<td>Gesamt</td>
<td></td>
<td style="text-align: right;">{selectedEmployee.totalCount}×</td>
<td style="text-align: right;" class="price">{formatPrice(selectedEmployee.totalCents)}</td>
</tr>
</tbody>
</table>
</div>
{/if}
</section>
{/if}
</div>
{#if showModal}
<div class="modal-overlay" on:click|self={() => showModal = false}>
<div class="modal">
<h2>{editId ? 'Mitarbeiter bearbeiten' : 'Neuer Mitarbeiter'}</h2>
<div class="form-group">
<label for="firstName">Vorname</label>
<input id="firstName" type="text" bind:value={formFirstName} />
</div>
<div class="form-group">
<label for="lastName">Nachname</label>
<input id="lastName" type="text" bind:value={formLastName} />
</div>
<div class="modal-actions">
<button class="btn btn-secondary" on:click={() => showModal = false}>Abbrechen</button>
<button class="btn btn-primary" on:click={saveEmployee}>Speichern</button>
</div>
</div>
</div>
{/if}