Files
portal/src/routes/(protected)/admin/user-roles/+page.svelte
T

161 lines
3.9 KiB
Svelte

<script lang="ts">
import { enhance } from '$app/forms';
import type { PageProps } from './$types';
import DataTable, { type DataTableColumn } from '$lib/components/DataTable.svelte';
import AdminModal from '$lib/components/admin/AdminModal.svelte';
type ActionResult = {
success?: boolean;
action?: string;
error?: string;
fieldErrors?: Record<string, string>;
} | null;
let { data, form }: PageProps = $props();
let canDelete = $derived((data.adminUser?.roleId ?? 4) <= 2);
const columns: DataTableColumn[] = [
{ label: 'Id', value: (r) => r.id },
{ label: 'Name', value: (r) => r.name }
];
let modalOpen = $state(false);
let editingRecord = $state<Record<string, unknown> | null>(null);
let name = $state('');
let formError = $state('');
let fieldErrors = $state<Record<string, string>>({});
let successMessage = $state('');
let lastForm = $state<ActionResult>(null);
let closeTimer: ReturnType<typeof setTimeout> | undefined;
function clearCloseTimer() {
if (closeTimer) {
clearTimeout(closeTimer);
closeTimer = undefined;
}
}
function resetModalState() {
clearCloseTimer();
formError = '';
fieldErrors = {};
successMessage = '';
}
$effect(() => {
if (!form || form === lastForm) return;
lastForm = form;
if (form.success) {
formError = '';
fieldErrors = {};
successMessage = editingRecord ? 'Role updated' : 'Role created';
closeTimer = setTimeout(() => {
modalOpen = false;
successMessage = '';
closeTimer = undefined;
}, 1200);
} else {
successMessage = '';
formError = form.error ?? '';
fieldErrors = form.fieldErrors ?? {};
}
});
function openCreate() {
resetModalState();
editingRecord = null;
name = '';
lastForm = form;
modalOpen = true;
}
function openEdit(record: Record<string, unknown>) {
resetModalState();
editingRecord = record;
name = String(record.name ?? '');
lastForm = form;
modalOpen = true;
}
function closeModal() {
clearCloseTimer();
modalOpen = false;
editingRecord = null;
}
</script>
<div>
<DataTable
title="User Roles"
{columns}
records={data.records}
newLabel="New Role"
searchPlaceholder="Search roles..."
onRowClick={openEdit}
onNew={openCreate}
/>
</div>
{#if modalOpen}
<AdminModal
title={editingRecord ? 'Edit User Role' : 'Create User Role'}
open={modalOpen}
onClose={closeModal}
{canDelete}
recordId={editingRecord?.id as string | undefined}
>
{#if successMessage}
<div
class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700"
>
{successMessage}
</div>
{/if}
{#if formError}
<div class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600">
{formError}
</div>
{/if}
<form method="POST" action="?/upsert" use:enhance class="flex flex-col gap-4">
{#if editingRecord}
<input type="hidden" name="id" value={String(editingRecord.id)} />
{/if}
<div>
<label for="field-name" class="mb-1 block text-sm font-medium text-slate-700">Name</label>
<input
id="field-name"
type="text"
name="name"
required
bind:value={name}
class="w-full rounded-md border border-slate-300 bg-white px-3 py-2 text-sm text-slate-900 shadow-sm transition focus:border-indigo-500 focus:outline-none"
/>
{#if fieldErrors.name}
<p class="mt-1 text-sm text-red-600">{fieldErrors.name}</p>
{/if}
</div>
<div class="mt-2 flex justify-end gap-3">
<button
type="button"
onclick={closeModal}
class="rounded-md border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-50"
>
Cancel
</button>
<button
type="submit"
class="rounded-md bg-indigo-600 px-4 py-2 text-sm font-medium text-white transition hover:bg-indigo-700"
>
{editingRecord ? 'Save Role' : 'Create Role'}
</button>
</div>
</form>
</AdminModal>
{/if}