Files
Sloneczko-manager/src/lib/components/CabinsDialog.svelte
Kazimierz Ciołek b38aa268ef Add some features
2026-07-07 11:32:06 +02:00

185 lines
4.6 KiB
Svelte

<script lang="ts">
import type { Cabin } from "$lib/types";
import * as db from "$lib/db";
interface Props {
cabins: Cabin[];
onclose: () => void;
onchanged: () => void;
}
let { cabins, onclose, onchanged }: Props = $props();
let newName = $state("");
let newCapacity = $state<number | null>(null);
let error = $state<string | null>(null);
function showModal(el: HTMLDialogElement) {
el.showModal();
}
async function add(event: SubmitEvent) {
event.preventDefault();
error = null;
const name = newName.trim();
if (!name) return;
try {
await db.createCabin(
name,
newCapacity == null || Number.isNaN(newCapacity) ? null : newCapacity
);
newName = "";
newCapacity = null;
onchanged();
} catch (e) {
error = `Nie udało się dodać domku: ${e}`;
}
}
async function persist(cabin: Cabin) {
error = null;
if (!cabin.name.trim()) return;
try {
await db.updateCabin({ ...cabin, name: cabin.name.trim() });
onchanged();
} catch (e) {
error = `Nie udało się zapisać zmian: ${e}`;
}
}
async function move(index: number, dir: -1 | 1) {
const other = index + dir;
if (other < 0 || other >= cabins.length) return;
const a = cabins[index];
const b = cabins[other];
// sort_order może się powtarzać — nadaj jednoznaczne wartości wg pozycji na liście
try {
await db.updateCabin({ ...a, sort_order: other + 1 });
await db.updateCabin({ ...b, sort_order: index + 1 });
onchanged();
} catch (e) {
error = `Nie udało się zmienić kolejności: ${e}`;
}
}
async function remove(cabin: Cabin) {
if (!confirm(`Usunąć domek „${cabin.name}"? Usunie to również wszystkie jego rezerwacje.`)) {
return;
}
try {
await db.deleteCabin(cabin.id);
onchanged();
} catch (e) {
error = `Nie udało się usunąć domku: ${e}`;
}
}
</script>
<dialog {@attach showModal} {onclose}>
<h2>Domki</h2>
{#if cabins.length === 0}
<p class="hint">Nie ma jeszcze żadnych domków — dodaj pierwszy poniżej.</p>
{:else}
<div class="list">
<span class="head">Nazwa</span>
<span class="head">Pojemność</span>
<span class="head"></span>
{#each cabins as cabin, i (cabin.id)}
<input
type="text"
bind:value={cabin.name}
onchange={() => persist(cabin)}
aria-label="Nazwa domku"
/>
<input
type="number"
min="1"
bind:value={cabin.capacity}
onchange={() => persist(cabin)}
aria-label="Pojemność domku"
placeholder="—"
/>
<span class="row-actions">
<button type="button" class="btn sm" onclick={() => move(i, -1)} disabled={i === 0} aria-label="Przesuń wyżej"></button>
<button type="button" class="btn sm" onclick={() => move(i, 1)} disabled={i === cabins.length - 1} aria-label="Przesuń niżej"></button>
<button type="button" class="btn sm danger" onclick={() => remove(cabin)}>Usuń</button>
</span>
{/each}
</div>
{/if}
<form class="add" onsubmit={add}>
<input type="text" bind:value={newName} placeholder="Nazwa nowego domku" required />
<input type="number" min="1" bind:value={newCapacity} placeholder="Osób" />
<button type="submit" class="btn primary">Dodaj</button>
</form>
{#if error}
<p class="error">{error}</p>
{/if}
<div class="actions">
<button type="button" class="btn" onclick={onclose}>Zamknij</button>
</div>
</dialog>
<style>
dialog {
width: min(480px, calc(100vw - 32px));
padding: 20px;
}
h2 {
margin: 0 0 14px;
font-size: 17px;
}
.hint {
font-size: 13px;
color: var(--muted);
}
.list {
display: grid;
grid-template-columns: 1fr 80px auto;
gap: 6px 8px;
align-items: center;
max-height: 320px;
overflow-y: auto;
}
.head {
font-size: 11px;
color: var(--muted);
}
input {
font: inherit;
font-size: 14px;
color: var(--text);
background: var(--bg);
border: 1px solid var(--border);
border-radius: 6px;
padding: 6px 8px;
min-width: 0;
}
.row-actions {
display: flex;
gap: 4px;
}
.add {
display: grid;
grid-template-columns: 1fr 80px auto;
gap: 8px;
margin-top: 14px;
padding-top: 14px;
border-top: 1px solid var(--border);
}
.error {
margin: 10px 0 0;
font-size: 13px;
color: var(--danger);
}
.actions {
display: flex;
justify-content: flex-end;
margin-top: 16px;
}
</style>