Residents frontend page #82
5 changed files with 70 additions and 14 deletions
|
|
@ -24,7 +24,9 @@
|
|||
"sms_button_submit": "Send Message",
|
||||
"residents_title": "Residents",
|
||||
"residents_button_new": "New Resident",
|
||||
"residents_modal_title": "Create a Resident",
|
||||
"residents_table_edit": "Edit",
|
||||
"residents_modal_title_new": "Create a Resident",
|
||||
"residents_modal_title_edit": "Edit Resident",
|
||||
"residents_modal_submit": "Submit",
|
||||
"residents_modal_label_name": "Name",
|
||||
"residents_modal_label_phone": "Phone Number",
|
||||
|
|
|
|||
|
|
@ -1,23 +1,34 @@
|
|||
<script lang="ts" module>
|
||||
export type ResidentItem = Pick<Resident, 'id' | 'name' | 'phoneNumber'>;
|
||||
</script>
|
||||
|
||||
<script lang="ts">
|
||||
import type { Resident } from '@prisma/client';
|
||||
import clsx from 'clsx';
|
||||
import { UserRoundPen } from 'lucide-svelte';
|
||||
import type { SvelteHTMLElements } from 'svelte/elements';
|
||||
import { twMerge } from 'tailwind-merge';
|
||||
import Button from '../Actions/Button.svelte';
|
||||
import { messages } from '$lib/i18n';
|
||||
|
||||
type Props = {
|
||||
items?: Pick<Resident, 'id' | 'name' | 'phoneNumber'>[];
|
||||
items?: ResidentItem[];
|
||||
onEdit?: (resident: ResidentItem) => void;
|
||||
} & Omit<SvelteHTMLElements['table'], 'children'>;
|
||||
|
||||
let { items, class: className, ...props }: Props = $props();
|
||||
let { items, onEdit, class: className, ...props }: Props = $props();
|
||||
</script>
|
||||
|
||||
<table {...props} class={twMerge('table', clsx(className))}>
|
||||
<!-- head -->
|
||||
<thead>
|
||||
<tr class="bg-base-100">
|
||||
<th></th>
|
||||
<th>#</th>
|
||||
<th>Name</th>
|
||||
<th>Phone Number</th>
|
||||
{#if onEdit}
|
||||
<th class="flex justify-end"><UserRoundPen class="mx-3" /></th>
|
||||
{/if}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
|
|
@ -28,6 +39,13 @@
|
|||
<th>{index + 1}</th>
|
||||
<td>{resident.name}</td>
|
||||
<td>{resident.phoneNumber}</td>
|
||||
{#if onEdit}
|
||||
<td class="text-end">
|
||||
<Button size="sm" color="accent" onclick={() => onEdit(resident)}>
|
||||
{messages.residents_table_edit()}
|
||||
</Button>
|
||||
</td>
|
||||
{/if}
|
||||
</tr>
|
||||
{/each}
|
||||
{/if}
|
||||
|
|
|
|||
|
|
@ -1 +1,2 @@
|
|||
export { default as ResidentTable } from './ResidentTable.svelte';
|
||||
export * from './ResidentTable.svelte';
|
||||
|
|
|
|||
|
|
@ -32,6 +32,11 @@ export const actions = {
|
|||
return fail(400, { error: 'message_missing' });
|
||||
}
|
||||
|
||||
const id = form.get('id');
|
||||
if (id && typeof id !== 'string') {
|
||||
return fail(400, { error: 'invalid_id' });
|
||||
}
|
||||
|
||||
const name = form.get('name');
|
||||
if (typeof name !== 'string') {
|
||||
return fail(400, { error: 'invalid_name' });
|
||||
|
|
@ -47,12 +52,19 @@ export const actions = {
|
|||
return fail(400, { error: 'invalid_phone' });
|
||||
}
|
||||
|
||||
await prisma.resident.create({
|
||||
data: {
|
||||
await prisma.resident.upsert({
|
||||
where: {
|
||||
id: id ?? '',
|
||||
},
|
||||
create: {
|
||||
name: name,
|
||||
phoneNumber: phone,
|
||||
tenantId: event.locals.tenant.id,
|
||||
},
|
||||
update: {
|
||||
name: name,
|
||||
phoneNumber: phone,
|
||||
},
|
||||
});
|
||||
},
|
||||
};
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@
|
|||
import { enhance } from '$app/forms';
|
||||
import { Button, Modal, ModalActions, ModalBody } from '$lib/components/Actions';
|
||||
import { TextInput } from '$lib/components/DataInput';
|
||||
import { ResidentTable } from '$lib/components/Residents';
|
||||
import { ResidentTable, type ResidentItem } from '$lib/components/Residents';
|
||||
import { messages } from '$lib/i18n';
|
||||
import { Phone, UserRound, UserRoundPlus } from 'lucide-svelte';
|
||||
import { fade } from 'svelte/transition';
|
||||
|
|
@ -17,25 +17,42 @@
|
|||
|
||||
let residents = $derived(data.residents);
|
||||
|
||||
let dialog = $state<HTMLDialogElement | undefined>(undefined);
|
||||
let dialog: HTMLDialogElement | undefined = $state(undefined);
|
||||
let form: HTMLFormElement | undefined = $state(undefined);
|
||||
|
||||
let resident: ResidentItem | undefined = $state(undefined);
|
||||
</script>
|
||||
|
||||
<Modal backdrop bind:dialog>
|
||||
<Modal
|
||||
backdrop
|
||||
bind:dialog
|
||||
onclose={() => {
|
||||
resident = undefined;
|
||||
form?.reset();
|
||||
}}
|
||||
>
|
||||
<ModalBody>
|
||||
<div class="flex items-center justify-between">
|
||||
<h2 class="text-2xl">{messages.residents_modal_title()}</h2>
|
||||
<h2 class="text-2xl">
|
||||
{resident
|
||||
? messages.residents_modal_title_edit()
|
||||
: messages.residents_modal_title_new()}
|
||||
</h2>
|
||||
<form method="dialog">
|
||||
<button class="btn btn-square btn-ghost btn-sm">✕</button>
|
||||
</form>
|
||||
</div>
|
||||
<form method="POST" use:enhance>
|
||||
<TextInput bordered name="name">
|
||||
<form method="POST" bind:this={form} use:enhance>
|
||||
{#if resident}
|
||||
<input type="hidden" name="id" value={resident.id} />
|
||||
{/if}
|
||||
<TextInput bordered name="name" value={resident?.name}>
|
||||
{#snippet label()}
|
||||
<UserRound size="18" />
|
||||
{messages.residents_modal_label_name()}
|
||||
{/snippet}
|
||||
</TextInput>
|
||||
<TextInput bordered name="phoneNumber" type={'tel'}>
|
||||
<TextInput bordered name="phoneNumber" type={'tel'} value={resident?.phoneNumber}>
|
||||
{#snippet label()}
|
||||
<Phone size="18" />
|
||||
{messages.residents_modal_label_phone()}
|
||||
|
|
@ -61,5 +78,11 @@
|
|||
>
|
||||
</div>
|
||||
<div class="divider"></div>
|
||||
<ResidentTable items={residents} />
|
||||
<ResidentTable
|
||||
items={residents}
|
||||
onEdit={(r) => {
|
||||
resident = r;
|
||||
dialog?.showModal();
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue