feat: implement PersonModal component for adding and editing person details

This commit is contained in:
Norbert Maciaszek
2025-11-17 21:18:23 +01:00
parent c8a8b8df1b
commit 31404b874a
2 changed files with 82 additions and 1 deletions

View File

@@ -0,0 +1,80 @@
<script lang="ts">
import type { ComponentProps } from 'svelte';
import type { DB } from '$lib/integrations/db';
import Modal from '../atoms/Modal.svelte';
import Button from '../atoms/Button.svelte';
import { page } from '$app/state';
type CreatePerson = Parameters<typeof DB.createPerson>[0];
type Props = {
onSave: (data: CreatePerson) => Promise<void>;
editPerson?: Person;
} & Omit<ComponentProps<typeof Modal>, 'children'>;
let { onSave, isOpen, onClose, editPerson }: Props = $props();
let person = $state<Person>(editPerson ?? ({} as Person));
let isLoading = $state(false);
if (!person?.name) {
person.name = '';
person.notes = '';
person.years = [page.params.year || new Date().getFullYear().toString()];
}
const handleSubmit = async (e: Event) => {
e.preventDefault();
if (!person) return;
isLoading = true;
try {
await onSave(person);
} catch (error) {
console.error(error);
} finally {
isLoading = false;
}
};
</script>
<Modal {isOpen} {onClose} title={person ? 'Edytuj osobę' : 'Dodaj nową osobę'}>
<form onsubmit={handleSubmit} class="space-y-4">
<div>
<label for="name" class="mb-2 block text-sm font-medium text-gray-700">
Imię i nazwisko *
</label>
<input
id="name"
type="text"
class="w-full rounded-xl border border-gray-300 px-4 py-2 transition-all outline-none focus:border-transparent focus:ring-2 focus:ring-blue-500"
required
disabled={isLoading}
bind:value={person.name}
/>
</div>
<div>
<label for="notes" class="mb-2 block text-sm font-medium text-gray-700"> Notatki </label>
<textarea
id="notes"
rows={4}
class="w-full resize-none rounded-xl border border-gray-300 px-4 py-2 transition-all outline-none focus:border-transparent focus:ring-2 focus:ring-blue-500"
disabled={isLoading}
bind:value={person.notes}
></textarea>
</div>
</form>
{#snippet footer()}
<div class="flex justify-end gap-3">
<Button variant="secondary" onClick={onClose} disabled={isLoading}>Anuluj</Button>
<Button
variant="primary"
onClick={handleSubmit}
disabled={isLoading || !person?.name?.trim()}
>
{isLoading ? 'Zapisywanie...' : 'Zapisz'}
</Button>
</div>
{/snippet}
</Modal>

View File

@@ -18,7 +18,8 @@ export const DB = {
return await pb.collection('gifts_person').getFirstListItem(`name = "${name}"`); return await pb.collection('gifts_person').getFirstListItem(`name = "${name}"`);
}, },
createPerson: async (data: Pick<DB.Person, 'name' | 'notes' | 'years'>): Promise<DB.Person> => { createPerson: async (data: Pick<DB.Person, 'name' | 'notes' | 'years'>): Promise<DB.Person> => {
return await pb.collection('gifts_person').create({ ...data }); const year = await pb.collection('gifts_year').getFirstListItem(`year = ${data.years[0]}`);
return await pb.collection('gifts_person').create({ ...data, years: [year.id] });
}, },
updatePerson: async (id: string, data: Pick<DB.Person, 'name' | 'notes'>): Promise<Person> => { updatePerson: async (id: string, data: Pick<DB.Person, 'name' | 'notes'>): Promise<Person> => {
return await pb.collection('gifts_person').update(id, data); return await pb.collection('gifts_person').update(id, data);