Add a site-wide dark mode
Build and Push to Digital Ocean Container Registry / build-and-push (push) Failing after 4s
Build and Push to Digital Ocean Container Registry / migrate (push) Skipped

This commit is contained in:
NeilClack
2026-09-11 12:49:46 -05:00
parent a909db39b1
commit 960673bbc6
28 changed files with 745 additions and 340 deletions
+13
View File
@@ -3,6 +3,19 @@
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" /> <meta name="viewport" content="width=device-width, initial-scale=1" />
<script>
(function () {
try {
const theme = localStorage.getItem('theme');
const dark =
theme === 'dark' ||
(!theme && window.matchMedia('(prefers-color-scheme: dark)').matches);
document.documentElement.classList.toggle('dark', dark);
} catch (e) {
// ignore
}
})();
</script>
%sveltekit.head% %sveltekit.head%
</head> </head>
+2 -2
View File
@@ -14,9 +14,9 @@
<section class="mt-8"> <section class="mt-8">
<div> <div>
<h2 class="text-lg font-semibold text-slate-900">{title}</h2> <h2 class="text-lg font-semibold text-slate-900 dark:text-slate-100">{title}</h2>
{#if description} {#if description}
<p class="mt-1 text-sm text-slate-500">{description}</p> <p class="mt-1 text-sm text-slate-500 dark:text-slate-400">{description}</p>
{/if} {/if}
</div> </div>
<div class="mt-4"> <div class="mt-4">
+14 -10
View File
@@ -95,14 +95,14 @@
<div class="mx-auto w-full"> <div class="mx-auto w-full">
<div class="mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between"> <div class="mb-4 flex flex-col gap-3 sm:flex-row sm:items-center sm:justify-between">
<h2 class="text-xl font-semibold text-slate-900">{title}</h2> <h2 class="text-xl font-semibold text-slate-900 dark:text-slate-100">{title}</h2>
<div class="flex w-full flex-col gap-3 sm:w-auto sm:flex-row sm:items-center"> <div class="flex w-full flex-col gap-3 sm:w-auto sm:flex-row sm:items-center">
<input <input
type="search" type="search"
bind:value={search} bind:value={search}
placeholder={searchPlaceholder} placeholder={searchPlaceholder}
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 sm:w-64" 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 sm:w-64 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if onNew} {#if onNew}
<button <button
@@ -128,11 +128,13 @@
</div> </div>
</div> </div>
<div class="w-full overflow-x-auto rounded-lg border border-slate-200 bg-white shadow-sm"> <div
<table class="w-full min-w-max text-left text-sm text-slate-700"> class="w-full overflow-x-auto rounded-lg border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900"
>
<table class="w-full min-w-max text-left text-sm text-slate-700 dark:text-slate-300">
<thead> <thead>
<tr <tr
class="border-b border-slate-200 bg-slate-100 text-xs font-semibold tracking-wide text-slate-500" class="border-b border-slate-200 bg-slate-100 text-xs font-semibold tracking-wide text-slate-500 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400"
> >
{#if draggable} {#if draggable}
<th scope="col" class="w-10 px-4 py-3"></th> <th scope="col" class="w-10 px-4 py-3"></th>
@@ -145,8 +147,10 @@
<tbody> <tbody>
{#each rows as record, i (rowKey(record, i))} {#each rows as record, i (rowKey(record, i))}
<tr <tr
class="transition {i % 2 === 0 ? 'bg-white' : 'bg-slate-50'} {onRowClick class="transition {i % 2 === 0
? 'cursor-pointer hover:bg-indigo-50' ? 'bg-white dark:bg-slate-900'
: 'bg-slate-50 dark:bg-slate-950'} {onRowClick
? 'cursor-pointer hover:bg-indigo-50 dark:hover:bg-indigo-950'
: ''}" : ''}"
{draggable} {draggable}
ondragstart={(e) => handleDragStart(e, i)} ondragstart={(e) => handleDragStart(e, i)}
@@ -158,7 +162,7 @@
{#if draggable} {#if draggable}
<td class="px-4 py-3" onclick={(e) => e.stopPropagation()} aria-hidden="true"> <td class="px-4 py-3" onclick={(e) => e.stopPropagation()} aria-hidden="true">
<span <span
class="cursor-grab text-slate-400 select-none active:cursor-grabbing" class="cursor-grab text-slate-400 select-none active:cursor-grabbing dark:text-slate-500"
title="Drag to reorder">⠿</span title="Drag to reorder">⠿</span
> >
</td> </td>
@@ -175,7 +179,7 @@
copyValue(column.value(record)); copyValue(column.value(record));
}} }}
title="Copy to clipboard" title="Copy to clipboard"
class="ml-1 inline-flex text-slate-400 transition hover:text-indigo-600" class="ml-1 inline-flex text-slate-400 transition hover:text-indigo-600 dark:text-slate-500 dark:hover:text-indigo-400"
> >
<svg <svg
viewBox="0 0 16 16" viewBox="0 0 16 16"
@@ -203,7 +207,7 @@
<tr> <tr>
<td <td
colspan={columns.length + (draggable ? 1 : 0)} colspan={columns.length + (draggable ? 1 : 0)}
class="bg-white px-4 py-10 text-center text-slate-500" class="bg-white dark:bg-slate-900 px-4 py-10 text-center text-slate-500 dark:text-slate-400"
> >
No results found. No results found.
</td> </td>
+5 -5
View File
@@ -53,18 +53,18 @@
role="dialog" role="dialog"
aria-modal="true" aria-modal="true"
aria-label={title} aria-label={title}
class="relative z-10 flex max-h-[90vh] w-full max-w-2xl flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-xl" class="relative z-10 flex max-h-[90vh] w-full max-w-2xl flex-col overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-xl dark:border-slate-800 dark:bg-slate-900"
> >
<div <div
class="flex shrink-0 items-center justify-between gap-3 border-b border-slate-200 px-6 py-4" class="flex shrink-0 items-center justify-between gap-3 border-b border-slate-200 px-6 py-4 dark:border-slate-800"
> >
<h2 class="text-lg font-bold text-slate-900">{title}</h2> <h2 class="text-lg font-bold text-slate-900 dark:text-slate-100">{title}</h2>
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
{#if canDelete && recordId} {#if canDelete && recordId}
<button <button
type="button" type="button"
onclick={confirmDelete} onclick={confirmDelete}
class="rounded-md bg-red-50 px-3 py-1.5 text-sm font-medium text-red-600 ring-1 ring-red-200 transition ring-inset hover:bg-red-600 hover:text-white" class="rounded-md bg-red-50 px-3 py-1.5 text-sm font-medium text-red-600 ring-1 ring-red-200 transition ring-inset hover:bg-red-600 hover:text-white dark:bg-red-950/50 dark:text-red-400 dark:ring-red-900"
> >
Delete Delete
</button> </button>
@@ -73,7 +73,7 @@
type="button" type="button"
onclick={onClose} onclick={onClose}
aria-label="Close" aria-label="Close"
class="rounded-md p-1.5 text-2xl leading-none text-slate-400 transition hover:bg-slate-100 hover:text-red-600" class="rounded-md p-1.5 text-2xl leading-none text-slate-400 transition hover:bg-slate-100 hover:text-red-600 dark:text-slate-500 dark:hover:bg-slate-800"
> >
&times; &times;
</button> </button>
+3 -3
View File
@@ -26,15 +26,15 @@
{#if visible} {#if visible}
<div <div
role="alert" role="alert"
class="fixed right-4 bottom-4 z-50 flex items-center gap-3 rounded-lg border border-slate-700 bg-slate-900 px-4 py-3 text-sm text-white shadow-lg" class="fixed right-4 bottom-4 z-50 flex items-center gap-3 rounded-lg border border-slate-700 bg-slate-900 px-4 py-3 text-sm text-white shadow-lg dark:border-slate-300 dark:bg-slate-950"
> >
<span>{message}</span> <span>{message}</span>
<span class="text-xs text-slate-400">{remaining}s</span> <span class="text-xs text-slate-400 dark:text-slate-500">{remaining}s</span>
<button <button
type="button" type="button"
onclick={dismiss} onclick={dismiss}
aria-label="Dismiss" aria-label="Dismiss"
class="rounded p-1 text-slate-400 transition hover:text-white" class="rounded p-1 text-slate-400 transition hover:text-white dark:text-slate-500"
> >
&times; &times;
</button> </button>
+3 -1
View File
@@ -3,7 +3,9 @@
</script> </script>
<div class="mx-auto w-full max-w-md px-4 py-8 sm:py-12"> <div class="mx-auto w-full max-w-md px-4 py-8 sm:py-12">
<div class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm sm:p-8"> <div
class="rounded-2xl border border-slate-200 bg-white p-6 shadow-sm sm:p-8 dark:border-slate-800 dark:bg-slate-900"
>
{@render children()} {@render children()}
</div> </div>
</div> </div>
@@ -16,8 +16,12 @@
<div> <div>
<div> <div>
<h1 class="text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl">Dashboard</h1> <h1 class="text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl dark:text-slate-100">
<p class="mt-1 text-sm text-slate-500 sm:text-base">Manage your account and license keys.</p> Dashboard
</h1>
<p class="mt-1 text-sm text-slate-500 sm:text-base dark:text-slate-400">
Manage your account and license keys.
</p>
</div> </div>
<!-- <!--
@@ -31,11 +35,13 @@
<DashboardSection title="License Keys" description="Your active and inactive license keys."> <DashboardSection title="License Keys" description="Your active and inactive license keys.">
{#if data.licenseKeys.length > 0} {#if data.licenseKeys.length > 0}
<div class="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm"> <div
class="overflow-hidden rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900"
>
<!-- Desktop / tablet: table --> <!-- Desktop / tablet: table -->
<table class="hidden w-full text-left text-sm sm:table"> <table class="hidden w-full text-left text-sm sm:table">
<thead <thead
class="border-b border-slate-200 bg-slate-50 text-xs tracking-wide text-slate-500 uppercase" class="border-b border-slate-200 bg-slate-50 text-xs tracking-wide text-slate-500 uppercase dark:border-slate-800 dark:bg-slate-950 dark:text-slate-400"
> >
<tr> <tr>
<th class="px-6 py-3 font-semibold">Key</th> <th class="px-6 py-3 font-semibold">Key</th>
@@ -46,19 +52,27 @@
</thead> </thead>
<tbody class="divide-y divide-slate-100"> <tbody class="divide-y divide-slate-100">
{#each data.licenseKeys as key (key.key)} {#each data.licenseKeys as key (key.key)}
<tr class="hover:bg-slate-50"> <tr class="hover:bg-slate-50 dark:hover:bg-slate-800">
<td class="px-6 py-4 font-mono text-slate-800">{key.key}</td> <td class="px-6 py-4 font-mono text-slate-800 dark:text-slate-200">{key.key}</td>
<td class="px-6 py-4"> <td class="px-6 py-4">
{#if key.active} {#if key.active}
<span class="text-lg text-green-600" title="Active" aria-label="Active">☑</span> <span
class="text-lg text-green-600 dark:text-green-400"
title="Active"
aria-label="Active">☑</span
>
{:else} {:else}
<span class="text-lg text-slate-300" title="Inactive" aria-label="Inactive" <span
>☐</span class="text-lg text-slate-300 dark:text-slate-600"
title="Inactive"
aria-label="Inactive">☐</span
> >
{/if} {/if}
</td> </td>
<td class="px-6 py-4 text-slate-600">{formatDate(key.createdAt)}</td> <td class="px-6 py-4 text-slate-600 dark:text-slate-400"
<td class="px-6 py-4 text-slate-600"> >{formatDate(key.createdAt)}</td
>
<td class="px-6 py-4 text-slate-600 dark:text-slate-400">
{key.lastValidatedAt ? formatDate(key.lastValidatedAt) : 'Never'} {key.lastValidatedAt ? formatDate(key.lastValidatedAt) : 'Never'}
</td> </td>
</tr> </tr>
@@ -71,19 +85,29 @@
{#each data.licenseKeys as key (key.key)} {#each data.licenseKeys as key (key.key)}
<li class="flex flex-col gap-2 px-4 py-4"> <li class="flex flex-col gap-2 px-4 py-4">
<div class="flex items-center justify-between gap-4"> <div class="flex items-center justify-between gap-4">
<span class="min-w-0 font-mono text-sm break-all text-slate-800">{key.key}</span> <span class="min-w-0 font-mono text-sm break-all text-slate-800 dark:text-slate-200"
>{key.key}</span
>
<span class="shrink-0"> <span class="shrink-0">
{#if key.active} {#if key.active}
<span class="text-lg text-green-600" title="Active" aria-label="Active">☑</span> <span
class="text-lg text-green-600 dark:text-green-400"
title="Active"
aria-label="Active">☑</span
>
{:else} {:else}
<span class="text-lg text-slate-300" title="Inactive" aria-label="Inactive" <span
>☐</span class="text-lg text-slate-300 dark:text-slate-600"
title="Inactive"
aria-label="Inactive">☐</span
> >
{/if} {/if}
</span> </span>
</div> </div>
<span class="text-xs text-slate-500">Created {formatDate(key.createdAt)}</span> <span class="text-xs text-slate-500 dark:text-slate-400"
<span class="text-xs text-slate-500"> >Created {formatDate(key.createdAt)}</span
>
<span class="text-xs text-slate-500 dark:text-slate-400">
Last verified {key.lastValidatedAt ? formatDate(key.lastValidatedAt) : 'Never'} Last verified {key.lastValidatedAt ? formatDate(key.lastValidatedAt) : 'Never'}
</span> </span>
</li> </li>
@@ -91,8 +115,12 @@
</ul> </ul>
</div> </div>
{:else} {:else}
<div class="rounded-2xl border border-dashed border-slate-300 bg-white p-8 text-center"> <div
<p class="text-sm text-slate-600">You don't have any license keys yet.</p> class="rounded-2xl border border-dashed border-slate-300 bg-white p-8 text-center dark:border-slate-700 dark:bg-slate-900"
>
<p class="text-sm text-slate-600 dark:text-slate-400">
You don't have any license keys yet.
</p>
</div> </div>
{/if} {/if}
</DashboardSection> </DashboardSection>
@@ -62,26 +62,40 @@
</script> </script>
<div class="mx-auto w-full max-w-3xl"> <div class="mx-auto w-full max-w-3xl">
<h1 class="text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl">Profile</h1> <h1 class="text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl dark:text-slate-100">
<p class="mt-1 text-sm text-slate-500 sm:text-base">Manage your account details and addresses.</p> Profile
</h1>
<p class="mt-1 text-sm text-slate-500 sm:text-base dark:text-slate-400">
Manage your account details and addresses.
</p>
<!-- Email (read-only) --> <!-- Email (read-only) -->
<div class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6"> <div
<p class="text-xs tracking-wide text-slate-500 uppercase">Email</p> class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6 dark:border-slate-800 dark:bg-slate-900"
<p class="mt-1 text-base break-words text-slate-900">{data.user.email}</p> >
<p class="text-xs tracking-wide text-slate-500 uppercase dark:text-slate-400">Email</p>
<p class="mt-1 text-base break-words text-slate-900 dark:text-slate-100">{data.user.email}</p>
</div> </div>
<!-- Profile Form --> <!-- Profile Form -->
<section class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6"> <section
<h2 class="text-lg font-semibold text-slate-900">Profile</h2> class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6 dark:border-slate-800 dark:bg-slate-900"
>
<h2 class="text-lg font-semibold text-slate-900 dark:text-slate-100">Profile</h2>
{#if form?.action === 'updateProfile' && form?.success} {#if form?.action === 'updateProfile' && form?.success}
<div class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800" role="status"> <div
class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800 dark:bg-green-950/50 dark:text-green-300"
role="status"
>
Profile updated successfully. Profile updated successfully.
</div> </div>
{/if} {/if}
{#if form?.action === 'updateProfile' && form?.error} {#if form?.action === 'updateProfile' && form?.error}
<div class="mt-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700" role="alert"> <div
class="mt-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-950/50 dark:text-red-300"
role="alert"
>
{form.error} {form.error}
</div> </div>
{/if} {/if}
@@ -89,30 +103,36 @@
<form method="POST" action="?/updateProfile" class="mt-5 flex flex-col gap-4"> <form method="POST" action="?/updateProfile" class="mt-5 flex flex-col gap-4">
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="firstName" class="text-sm font-medium text-slate-700"> First Name </label> <label for="firstName" class="text-sm font-medium text-slate-700 dark:text-slate-300">
First Name
</label>
<input <input
type="text" type="text"
id="firstName" id="firstName"
name="firstName" name="firstName"
value={data.user.profile?.firstName ?? ''} value={data.user.profile?.firstName ?? ''}
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="lastName" class="text-sm font-medium text-slate-700"> Last Name </label> <label for="lastName" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Last Name
</label>
<input <input
type="text" type="text"
id="lastName" id="lastName"
name="lastName" name="lastName"
value={data.user.profile?.lastName ?? ''} value={data.user.profile?.lastName ?? ''}
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="phoneNumber" class="text-sm font-medium text-slate-700"> Phone Number </label> <label for="phoneNumber" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Phone Number
</label>
<input <input
type="tel" type="tel"
id="phoneNumber" id="phoneNumber"
@@ -120,7 +140,7 @@
bind:value={phoneNumber} bind:value={phoneNumber}
oninput={handlePhoneInput} oninput={handlePhoneInput}
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div> <div>
@@ -135,38 +155,49 @@
</section> </section>
<!-- Addresses --> <!-- Addresses -->
<section class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6"> <section
class="mt-6 rounded-2xl border border-slate-200 bg-white p-5 shadow-sm sm:p-6 dark:border-slate-800 dark:bg-slate-900"
>
<div class="flex items-center justify-between gap-4"> <div class="flex items-center justify-between gap-4">
<h2 class="text-lg font-semibold text-slate-900">Addresses</h2> <h2 class="text-lg font-semibold text-slate-900 dark:text-slate-100">Addresses</h2>
<button <button
type="button" type="button"
onclick={() => (showAddressForm ? closeAddressForm() : openAddAddress())} onclick={() => (showAddressForm ? closeAddressForm() : openAddAddress())}
class="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50" class="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
{showAddressForm ? 'Cancel' : 'Add Address'} {showAddressForm ? 'Cancel' : 'Add Address'}
</button> </button>
</div> </div>
{#if form?.action === 'addAddress' && form?.success} {#if form?.action === 'addAddress' && form?.success}
<div class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800" role="status"> <div
class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800 dark:bg-green-950/50 dark:text-green-300"
role="status"
>
Address added successfully. Address added successfully.
</div> </div>
{/if} {/if}
{#if form?.action === 'updateAddress' && form?.success} {#if form?.action === 'updateAddress' && form?.success}
<div class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800" role="status"> <div
class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800 dark:bg-green-950/50 dark:text-green-300"
role="status"
>
Address updated successfully. Address updated successfully.
</div> </div>
{/if} {/if}
{#if form?.action === 'deleteAddress' && form?.success} {#if form?.action === 'deleteAddress' && form?.success}
<div class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800" role="status"> <div
class="mt-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800 dark:bg-green-950/50 dark:text-green-300"
role="status"
>
Address deleted successfully. Address deleted successfully.
</div> </div>
{/if} {/if}
{#if (form?.action === 'addAddress' || {#if (form?.action === 'addAddress' || form?.action === 'updateAddress' || form?.action === 'deleteAddress') && form?.error}
form?.action === 'updateAddress' || <div
form?.action === 'deleteAddress') && class="mt-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-950/50 dark:text-red-300"
form?.error} role="alert"
<div class="mt-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700" role="alert"> >
{form.error} {form.error}
</div> </div>
{/if} {/if}
@@ -181,61 +212,71 @@
<input type="hidden" name="id" value={editingAddressId} /> <input type="hidden" name="id" value={editingAddressId} />
{/if} {/if}
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="street" class="text-sm font-medium text-slate-700">Street</label> <label for="street" class="text-sm font-medium text-slate-700 dark:text-slate-300"
>Street</label
>
<input <input
type="text" type="text"
id="street" id="street"
name="street" name="street"
bind:value={addrStreet} bind:value={addrStreet}
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="city" class="text-sm font-medium text-slate-700">City</label> <label for="city" class="text-sm font-medium text-slate-700 dark:text-slate-300"
>City</label
>
<input <input
type="text" type="text"
id="city" id="city"
name="city" name="city"
bind:value={addrCity} bind:value={addrCity}
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="state" class="text-sm font-medium text-slate-700">State</label> <label for="state" class="text-sm font-medium text-slate-700 dark:text-slate-300"
>State</label
>
<input <input
type="text" type="text"
id="state" id="state"
name="state" name="state"
bind:value={addrState} bind:value={addrState}
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
</div> </div>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="postalCode" class="text-sm font-medium text-slate-700"> Postal Code </label> <label for="postalCode" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Postal Code
</label>
<input <input
type="text" type="text"
id="postalCode" id="postalCode"
name="postalCode" name="postalCode"
bind:value={addrPostal} bind:value={addrPostal}
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="country" class="text-sm font-medium text-slate-700">Country</label> <label for="country" class="text-sm font-medium text-slate-700 dark:text-slate-300"
>Country</label
>
<input <input
type="text" type="text"
id="country" id="country"
name="country" name="country"
bind:value={addrCountry} bind:value={addrCountry}
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
</div> </div>
@@ -253,18 +294,20 @@
{#if data.user.profile?.addresses && data.user.profile.addresses.length > 0} {#if data.user.profile?.addresses && data.user.profile.addresses.length > 0}
<div class="mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2"> <div class="mt-5 grid grid-cols-1 gap-4 sm:grid-cols-2">
{#each data.user.profile.addresses as address (address.id)} {#each data.user.profile.addresses as address (address.id)}
<div class="rounded-lg border border-slate-200 bg-slate-50 p-4"> <div
<p class="text-sm font-medium text-slate-900">{address.street}</p> class="rounded-lg border border-slate-200 bg-slate-50 p-4 dark:border-slate-800 dark:bg-slate-950"
<p class="mt-1 text-sm text-slate-600"> >
<p class="text-sm font-medium text-slate-900 dark:text-slate-100">{address.street}</p>
<p class="mt-1 text-sm text-slate-600 dark:text-slate-400">
{address.city}, {address.state} {address.city}, {address.state}
{address.postalCode} {address.postalCode}
</p> </p>
<p class="text-sm text-slate-600">{address.country}</p> <p class="text-sm text-slate-600 dark:text-slate-400">{address.country}</p>
<div class="mt-3 flex gap-2"> <div class="mt-3 flex gap-2">
<button <button
type="button" type="button"
onclick={() => openEditAddress(address)} onclick={() => openEditAddress(address)}
class="rounded-md border border-slate-300 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-50" class="rounded-md border border-slate-300 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Edit Edit
</button> </button>
@@ -278,7 +321,7 @@
<input type="hidden" name="id" value={address.id} /> <input type="hidden" name="id" value={address.id} />
<button <button
type="submit" type="submit"
class="rounded-md border border-red-300 bg-white px-3 py-1.5 text-xs font-medium text-red-600 hover:bg-red-50" class="rounded-md border border-red-300 bg-white px-3 py-1.5 text-xs font-medium text-red-600 hover:bg-red-50 dark:border-red-800 dark:bg-slate-900 dark:text-red-400 dark:hover:bg-red-950/50"
> >
Delete Delete
</button> </button>
@@ -288,7 +331,7 @@
{/each} {/each}
</div> </div>
{:else if !showAddressForm} {:else if !showAddressForm}
<p class="mt-5 text-sm text-slate-500">No addresses on file.</p> <p class="mt-5 text-sm text-slate-500 dark:text-slate-400">No addresses on file.</p>
{/if} {/if}
</section> </section>
</div> </div>
@@ -43,7 +43,7 @@
<Window> <Window>
<form method="POST" class="flex flex-col items-center gap-5"> <form method="POST" class="flex flex-col items-center gap-5">
<h1 class="text-center text-2xl font-bold text-slate-900"> <h1 class="text-center text-2xl font-bold text-slate-900 dark:text-slate-100">
Please finish setting up your profile Please finish setting up your profile
</h1> </h1>
@@ -55,44 +55,50 @@
/> />
{:else} {:else}
<div <div
class="flex h-24 w-24 items-center justify-center rounded-full bg-indigo-100 text-3xl font-semibold text-indigo-700 sm:h-28 sm:w-28" class="flex h-24 w-24 items-center justify-center rounded-full bg-indigo-100 text-3xl font-semibold text-indigo-700 sm:h-28 sm:w-28 dark:bg-indigo-900 dark:text-indigo-300"
> >
{data.user?.name?.charAt(0)?.toUpperCase() ?? '?'} {data.user?.name?.charAt(0)?.toUpperCase() ?? '?'}
</div> </div>
{/if} {/if}
<p class="text-base text-slate-900">{data.user?.email}</p> <p class="text-base text-slate-900 dark:text-slate-100">{data.user?.email}</p>
<div class="flex w-full flex-col gap-4"> <div class="flex w-full flex-col gap-4">
<input type="hidden" name="userId" value={data.user?.id} /> <input type="hidden" name="userId" value={data.user?.id} />
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="firstName" class="text-sm font-medium text-slate-700"> First Name </label> <label for="firstName" class="text-sm font-medium text-slate-700 dark:text-slate-300">
First Name
</label>
<input <input
id="firstName" id="firstName"
type="text" type="text"
name="firstName" name="firstName"
placeholder="First Name" placeholder="First Name"
bind:value={firstName} bind:value={firstName}
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="lastName" class="text-sm font-medium text-slate-700"> Last Name </label> <label for="lastName" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Last Name
</label>
<input <input
id="lastName" id="lastName"
type="text" type="text"
name="lastName" name="lastName"
placeholder="Last Name" placeholder="Last Name"
bind:value={lastName} bind:value={lastName}
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="phoneNumber" class="text-sm font-medium text-slate-700"> Phone Number </label> <label for="phoneNumber" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Phone Number
</label>
<input <input
id="phoneNumber" id="phoneNumber"
type="tel" type="tel"
@@ -100,7 +106,7 @@
placeholder="(555) 555-5555" placeholder="(555) 555-5555"
bind:value={phoneNumber} bind:value={phoneNumber}
oninput={handlePhoneInput} oninput={handlePhoneInput}
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
</div> </div>
+1 -1
View File
@@ -4,7 +4,7 @@
let { children }: { children: Snippet } = $props(); let { children }: { children: Snippet } = $props();
</script> </script>
<div class="min-h-screen bg-slate-50 text-slate-900"> <div class="min-h-screen bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100">
<main class="mx-auto w-full max-w-6xl px-4 py-6 sm:px-6 sm:py-8"> <main class="mx-auto w-full max-w-6xl px-4 py-6 sm:px-6 sm:py-8">
{@render children()} {@render children()}
</main> </main>
@@ -24,19 +24,26 @@
<div class="mx-auto w-full"> <div class="mx-auto w-full">
<div class="flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between"> <div class="flex flex-col gap-2 sm:flex-row sm:items-end sm:justify-between">
<div> <div>
<h1 class="text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl">Audit Log</h1> <h1 class="text-2xl font-bold tracking-tight text-slate-900 sm:text-3xl dark:text-slate-100">
<p class="mt-1 text-sm text-slate-500"> Audit Log
</h1>
<p class="mt-1 text-sm text-slate-500 dark:text-slate-400">
Every database change, newest first. You can see activity from roles beneath your own. Every database change, newest first. You can see activity from roles beneath your own.
</p> </p>
</div> </div>
<p class="text-sm text-slate-500">{first}–{last} of {data.total}</p> <p class="text-sm text-slate-500 dark:text-slate-400">{first}–{last} of {data.total}</p>
</div> </div>
<!-- Filters --> <!-- Filters -->
<form method="GET" class="mt-6 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm"> <form
method="GET"
class="mt-6 rounded-2xl border border-slate-200 bg-white p-4 shadow-sm dark:border-slate-800 dark:bg-slate-900"
>
<div class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4"> <div class="grid grid-cols-1 gap-3 sm:grid-cols-2 lg:grid-cols-4">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label for="q" class="text-xs font-medium tracking-wide text-slate-500 uppercase" <label
for="q"
class="text-xs font-medium tracking-wide text-slate-500 uppercase dark:text-slate-400"
>Search</label >Search</label
> >
<input <input
@@ -45,19 +52,21 @@
type="search" type="search"
value={data.filters.q} value={data.filters.q}
placeholder="User, email, page, action…" placeholder="User, email, page, action…"
class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label for="role" class="text-xs font-medium tracking-wide text-slate-500 uppercase" <label
for="role"
class="text-xs font-medium tracking-wide text-slate-500 uppercase dark:text-slate-400"
>Role</label >Role</label
> >
<select <select
id="role" id="role"
name="role" name="role"
value={data.filters.role} value={data.filters.role}
class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
> >
<option value="">All roles</option> <option value="">All roles</option>
{#each data.roleOptions as role (role.id)} {#each data.roleOptions as role (role.id)}
@@ -67,14 +76,16 @@
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label for="action" class="text-xs font-medium tracking-wide text-slate-500 uppercase" <label
for="action"
class="text-xs font-medium tracking-wide text-slate-500 uppercase dark:text-slate-400"
>Action</label >Action</label
> >
<select <select
id="action" id="action"
name="action" name="action"
value={data.filters.action} value={data.filters.action}
class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
> >
<option value="">All actions</option> <option value="">All actions</option>
{#each data.actionOptions as action (action)} {#each data.actionOptions as action (action)}
@@ -84,14 +95,16 @@
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label for="entity" class="text-xs font-medium tracking-wide text-slate-500 uppercase" <label
for="entity"
class="text-xs font-medium tracking-wide text-slate-500 uppercase dark:text-slate-400"
>Entity</label >Entity</label
> >
<select <select
id="entity" id="entity"
name="entity" name="entity"
value={data.filters.entity} value={data.filters.entity}
class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
> >
<option value="">All entities</option> <option value="">All entities</option>
{#each data.entityOptions as entity (entity)} {#each data.entityOptions as entity (entity)}
@@ -101,7 +114,9 @@
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label for="page" class="text-xs font-medium tracking-wide text-slate-500 uppercase" <label
for="page"
class="text-xs font-medium tracking-wide text-slate-500 uppercase dark:text-slate-400"
>Page</label >Page</label
> >
<input <input
@@ -110,12 +125,14 @@
type="text" type="text"
value={data.filters.page} value={data.filters.page}
placeholder="/admin/users" placeholder="/admin/users"
class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label for="from" class="text-xs font-medium tracking-wide text-slate-500 uppercase" <label
for="from"
class="text-xs font-medium tracking-wide text-slate-500 uppercase dark:text-slate-400"
>From</label >From</label
> >
<input <input
@@ -123,31 +140,36 @@
name="from" name="from"
type="date" type="date"
value={data.filters.from} value={data.filters.from}
class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label for="to" class="text-xs font-medium tracking-wide text-slate-500 uppercase">To</label <label
for="to"
class="text-xs font-medium tracking-wide text-slate-500 uppercase dark:text-slate-400"
>To</label
> >
<input <input
id="to" id="to"
name="to" name="to"
type="date" type="date"
value={data.filters.to} value={data.filters.to}
class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<label for="sort" class="text-xs font-medium tracking-wide text-slate-500 uppercase" <label
for="sort"
class="text-xs font-medium tracking-wide text-slate-500 uppercase dark:text-slate-400"
>Order</label >Order</label
> >
<select <select
id="sort" id="sort"
name="sort" name="sort"
value={data.filters.sort} value={data.filters.sort}
class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-10 rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
> >
<option value="desc">Newest first</option> <option value="desc">Newest first</option>
<option value="asc">Oldest first</option> <option value="asc">Oldest first</option>
@@ -158,7 +180,7 @@
<div class="mt-4 flex items-center justify-end gap-3"> <div class="mt-4 flex items-center justify-end gap-3">
<a <a
href={resolve('/admin/audit-log')} href={resolve('/admin/audit-log')}
class="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-50" class="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Clear Clear
</a> </a>
@@ -172,11 +194,13 @@
</form> </form>
<!-- Results --> <!-- Results -->
<div class="mt-6 overflow-x-auto rounded-2xl border border-slate-200 bg-white shadow-sm"> <div
<table class="w-full min-w-max text-left text-sm text-slate-700"> class="mt-6 overflow-x-auto rounded-2xl border border-slate-200 bg-white shadow-sm dark:border-slate-800 dark:bg-slate-900"
>
<table class="w-full min-w-max text-left text-sm text-slate-700 dark:text-slate-300">
<thead> <thead>
<tr <tr
class="border-b border-slate-200 bg-slate-50 text-xs font-semibold tracking-wide text-slate-500 uppercase" class="border-b border-slate-200 bg-slate-50 text-xs font-semibold tracking-wide text-slate-500 uppercase dark:border-slate-800 dark:bg-slate-950 dark:text-slate-400"
> >
<th scope="col" class="px-4 py-3">When</th> <th scope="col" class="px-4 py-3">When</th>
<th scope="col" class="px-4 py-3">User</th> <th scope="col" class="px-4 py-3">User</th>
@@ -191,44 +215,47 @@
</thead> </thead>
<tbody class="divide-y divide-slate-100"> <tbody class="divide-y divide-slate-100">
{#each data.records as record (record.id)} {#each data.records as record (record.id)}
<tr class="align-top hover:bg-slate-50"> <tr class="align-top hover:bg-slate-50 dark:hover:bg-slate-800">
<td class="px-4 py-3 whitespace-nowrap text-slate-500"> <td class="px-4 py-3 whitespace-nowrap text-slate-500 dark:text-slate-400">
{formatWhen(record.createdAt)} {formatWhen(record.createdAt)}
</td> </td>
<td class="px-4 py-3 font-mono text-xs text-slate-600"> <td class="px-4 py-3 font-mono text-xs text-slate-600 dark:text-slate-400">
{record.userId ?? '—'} {record.userId ?? '—'}
</td> </td>
<td class="px-4 py-3 text-slate-700">{record.email ?? '—'}</td> <td class="px-4 py-3 text-slate-700 dark:text-slate-300">{record.email ?? '—'}</td>
<td class="px-4 py-3"> <td class="px-4 py-3">
<span <span
class="inline-flex items-center rounded-full bg-slate-100 px-2.5 py-0.5 text-xs font-medium text-slate-700" class="inline-flex items-center rounded-full bg-slate-100 px-2.5 py-0.5 text-xs font-medium text-slate-700 dark:bg-slate-800 dark:text-slate-300"
> >
{record.roleName ?? (record.roleId != null ? `Role ${record.roleId}` : 'System')} {record.roleName ?? (record.roleId != null ? `Role ${record.roleId}` : 'System')}
</span> </span>
</td> </td>
<td class="px-4 py-3 font-mono text-xs text-slate-600">{record.page}</td> <td class="px-4 py-3 font-mono text-xs text-slate-600 dark:text-slate-400"
>{record.page}</td
>
<td class="px-4 py-3"> <td class="px-4 py-3">
<span <span
class="inline-flex items-center rounded-full bg-indigo-50 px-2.5 py-0.5 text-xs font-medium text-indigo-700" class="inline-flex items-center rounded-full bg-indigo-50 px-2.5 py-0.5 text-xs font-medium text-indigo-700 dark:bg-indigo-950 dark:text-indigo-300"
> >
{record.action} {record.action}
</span> </span>
</td> </td>
<td class="px-4 py-3 text-slate-700"> <td class="px-4 py-3 text-slate-700 dark:text-slate-300">
{record.entity ?? '—'}{#if record.entityId}<span class="text-slate-400" {record.entity ?? '—'}{#if record.entityId}<span
>#{record.entityId}</span class="text-slate-400 dark:text-slate-500">#{record.entityId}</span
>{/if} >{/if}
</td> </td>
<td class="max-w-xs px-4 py-3"> <td class="max-w-xs px-4 py-3">
{#if record.before == null} {#if record.before == null}
<span class="text-slate-400">—</span> <span class="text-slate-400 dark:text-slate-500">—</span>
{:else} {:else}
<details> <details>
<summary class="cursor-pointer text-xs text-indigo-600 hover:text-indigo-700" <summary
class="cursor-pointer text-xs text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300"
>view</summary >view</summary
> >
<pre <pre
class="mt-2 max-h-64 overflow-auto rounded-md bg-slate-50 p-2 text-xs whitespace-pre-wrap text-slate-700">{formatJson( class="mt-2 max-h-64 overflow-auto rounded-md bg-slate-50 p-2 text-xs whitespace-pre-wrap text-slate-700 dark:bg-slate-950 dark:text-slate-300">{formatJson(
record.before record.before
)}</pre> )}</pre>
</details> </details>
@@ -236,14 +263,15 @@
</td> </td>
<td class="max-w-xs px-4 py-3"> <td class="max-w-xs px-4 py-3">
{#if record.after == null} {#if record.after == null}
<span class="text-slate-400">—</span> <span class="text-slate-400 dark:text-slate-500">—</span>
{:else} {:else}
<details> <details>
<summary class="cursor-pointer text-xs text-indigo-600 hover:text-indigo-700" <summary
class="cursor-pointer text-xs text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300"
>view</summary >view</summary
> >
<pre <pre
class="mt-2 max-h-64 overflow-auto rounded-md bg-slate-50 p-2 text-xs whitespace-pre-wrap text-slate-700">{formatJson( class="mt-2 max-h-64 overflow-auto rounded-md bg-slate-50 p-2 text-xs whitespace-pre-wrap text-slate-700 dark:bg-slate-950 dark:text-slate-300">{formatJson(
record.after record.after
)}</pre> )}</pre>
</details> </details>
@@ -252,7 +280,9 @@
</tr> </tr>
{:else} {:else}
<tr> <tr>
<td colspan="9" class="px-4 py-12 text-center text-slate-500">No events found.</td> <td colspan="9" class="px-4 py-12 text-center text-slate-500 dark:text-slate-400"
>No events found.</td
>
</tr> </tr>
{/each} {/each}
</tbody> </tbody>
@@ -262,7 +292,9 @@
<!-- Pagination --> <!-- Pagination -->
{#if data.totalPages > 1} {#if data.totalPages > 1}
<div class="mt-4 flex items-center justify-between"> <div class="mt-4 flex items-center justify-between">
<p class="text-sm text-slate-500">Page {data.page} of {data.totalPages}</p> <p class="text-sm text-slate-500 dark:text-slate-400">
Page {data.page} of {data.totalPages}
</p>
<form method="GET" action={resolve('/admin/audit-log')} class="flex gap-2"> <form method="GET" action={resolve('/admin/audit-log')} class="flex gap-2">
{#if data.filters.q}<input type="hidden" name="q" value={data.filters.q} />{/if} {#if data.filters.q}<input type="hidden" name="q" value={data.filters.q} />{/if}
{#if data.filters.role}<input type="hidden" name="role" value={data.filters.role} />{/if} {#if data.filters.role}<input type="hidden" name="role" value={data.filters.role} />{/if}
@@ -285,7 +317,7 @@
type="submit" type="submit"
name="p" name="p"
value={data.page - 1} value={data.page - 1}
class="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-50" class="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Previous Previous
</button> </button>
@@ -295,7 +327,7 @@
type="submit" type="submit"
name="p" name="p"
value={data.page + 1} value={data.page + 1}
class="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-50" class="rounded-lg border border-slate-300 bg-white px-4 py-2 text-sm font-medium text-slate-700 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Next Next
</button> </button>
@@ -133,20 +133,25 @@
<AdminModal title="Create License Key" open={createOpen} onClose={closeCreate}> <AdminModal title="Create License Key" open={createOpen} onClose={closeCreate}>
{#if createSuccess} {#if createSuccess}
<div <div
class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700" class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700 dark:border-green-900 dark:bg-green-950/50 dark:text-green-300"
> >
{createSuccess} {createSuccess}
</div> </div>
{/if} {/if}
{#if createError} {#if createError}
<div class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"> <div
class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-900 dark:bg-red-950/50 dark:text-red-400"
>
{createError} {createError}
</div> </div>
{/if} {/if}
<form method="POST" action="?/createKey" use:enhance class="flex flex-col gap-4"> <form method="POST" action="?/createKey" use:enhance class="flex flex-col gap-4">
<div> <div>
<label for="field-email" class="mb-1 block text-sm font-medium text-slate-700">Email</label> <label
for="field-email"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Email</label
>
<input <input
id="field-email" id="field-email"
type="email" type="email"
@@ -154,9 +159,9 @@
required required
bind:value={email} bind:value={email}
placeholder="user@example.com" placeholder="user@example.com"
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
<p class="mt-1 text-sm text-slate-500"> <p class="mt-1 text-sm text-slate-500 dark:text-slate-400">
If no account exists for this email yet, the key will be attached when they register. If no account exists for this email yet, the key will be attached when they register.
</p> </p>
</div> </div>
@@ -165,7 +170,7 @@
<button <button
type="button" type="button"
onclick={closeCreate} onclick={closeCreate}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Cancel Cancel
</button> </button>
@@ -182,23 +187,25 @@
<AdminModal title="Replace License Key" open={replaceOpen} onClose={closeReplace}> <AdminModal title="Replace License Key" open={replaceOpen} onClose={closeReplace}>
{#if replaceSuccess} {#if replaceSuccess}
<div <div
class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700" class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700 dark:border-green-900 dark:bg-green-950/50 dark:text-green-300"
> >
{replaceSuccess} {replaceSuccess}
</div> </div>
{/if} {/if}
{#if replaceError} {#if replaceError}
<div class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"> <div
class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-900 dark:bg-red-950/50 dark:text-red-400"
>
{replaceError} {replaceError}
</div> </div>
{/if} {/if}
{#if replaceRecord} {#if replaceRecord}
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<p class="text-sm text-slate-700"> <p class="text-sm text-slate-700 dark:text-slate-300">
Generate a new key to replace <strong>{String(replaceRecord.key)}</strong>? Generate a new key to replace <strong>{String(replaceRecord.key)}</strong>?
</p> </p>
<p class="text-sm text-slate-500"> <p class="text-sm text-slate-500 dark:text-slate-400">
The existing key will be deleted and a new one will be created. The existing key will be deleted and a new one will be created.
</p> </p>
@@ -208,7 +215,7 @@
<button <button
type="button" type="button"
onclick={closeReplace} onclick={closeReplace}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Cancel Cancel
</button> </button>
@@ -116,13 +116,15 @@
> >
{#if successMessage} {#if successMessage}
<div <div
class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700" class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700 dark:border-green-900 dark:bg-green-950/50 dark:text-green-300"
> >
{successMessage} {successMessage}
</div> </div>
{/if} {/if}
{#if formError} {#if formError}
<div class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"> <div
class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-900 dark:bg-red-950/50 dark:text-red-400"
>
{formError} {formError}
</div> </div>
{/if} {/if}
@@ -133,17 +135,20 @@
{/if} {/if}
<div> <div>
<label for="field-name" class="mb-1 block text-sm font-medium text-slate-700">Name</label> <label
for="field-name"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Name</label
>
<input <input
id="field-name" id="field-name"
type="text" type="text"
name="name" name="name"
required required
bind:value={name} 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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.name} {#if fieldErrors.name}
<p class="mt-1 text-sm text-red-600">{fieldErrors.name}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.name}</p>
{/if} {/if}
</div> </div>
@@ -151,7 +156,7 @@
<button <button
type="button" type="button"
onclick={closeModal} 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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Cancel Cancel
</button> </button>
@@ -116,13 +116,15 @@
> >
{#if successMessage} {#if successMessage}
<div <div
class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700" class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700 dark:border-green-900 dark:bg-green-950/50 dark:text-green-300"
> >
{successMessage} {successMessage}
</div> </div>
{/if} {/if}
{#if formError} {#if formError}
<div class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"> <div
class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-900 dark:bg-red-950/50 dark:text-red-400"
>
{formError} {formError}
</div> </div>
{/if} {/if}
@@ -133,17 +135,20 @@
{/if} {/if}
<div> <div>
<label for="field-name" class="mb-1 block text-sm font-medium text-slate-700">Name</label> <label
for="field-name"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Name</label
>
<input <input
id="field-name" id="field-name"
type="text" type="text"
name="name" name="name"
required required
bind:value={name} 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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.name} {#if fieldErrors.name}
<p class="mt-1 text-sm text-red-600">{fieldErrors.name}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.name}</p>
{/if} {/if}
</div> </div>
@@ -151,7 +156,7 @@
<button <button
type="button" type="button"
onclick={closeModal} 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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Cancel Cancel
</button> </button>
@@ -192,13 +192,15 @@
> >
{#if successMessage} {#if successMessage}
<div <div
class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700" class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700 dark:border-green-900 dark:bg-green-950/50 dark:text-green-300"
> >
{successMessage} {successMessage}
</div> </div>
{/if} {/if}
{#if formError} {#if formError}
<div class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"> <div
class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-900 dark:bg-red-950/50 dark:text-red-400"
>
{formError} {formError}
</div> </div>
{/if} {/if}
@@ -209,31 +211,34 @@
{/if} {/if}
<div> <div>
<label for="field-name" class="mb-1 block text-sm font-medium text-slate-700">Name</label> <label
for="field-name"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Name</label
>
<input <input
id="field-name" id="field-name"
type="text" type="text"
name="name" name="name"
required required
bind:value={name} 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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.name} {#if fieldErrors.name}
<p class="mt-1 text-sm text-red-600">{fieldErrors.name}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.name}</p>
{/if} {/if}
</div> </div>
<div> <div>
<label <label
for="field-solfeggioFrequencyId" for="field-solfeggioFrequencyId"
class="mb-1 block text-sm font-medium text-slate-700">Frequency</label class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Frequency</label
> >
<select <select
id="field-solfeggioFrequencyId" id="field-solfeggioFrequencyId"
name="solfeggioFrequencyId" name="solfeggioFrequencyId"
required required
bind:value={solfeggioFrequencyId} bind:value={solfeggioFrequencyId}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
> >
<option value="">-- Select Frequency --</option> <option value="">-- Select Frequency --</option>
{#each data.frequencies as freq (String(freq.id))} {#each data.frequencies as freq (String(freq.id))}
@@ -241,20 +246,23 @@
{/each} {/each}
</select> </select>
{#if fieldErrors.solfeggioFrequencyId} {#if fieldErrors.solfeggioFrequencyId}
<p class="mt-1 text-sm text-red-600">{fieldErrors.solfeggioFrequencyId}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">
{fieldErrors.solfeggioFrequencyId}
</p>
{/if} {/if}
</div> </div>
<div> <div>
<label for="field-categoryId" class="mb-1 block text-sm font-medium text-slate-700" <label
>Category</label for="field-categoryId"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Category</label
> >
<select <select
id="field-categoryId" id="field-categoryId"
name="categoryId" name="categoryId"
required required
bind:value={categoryId} bind:value={categoryId}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
> >
<option value="">-- Select Category --</option> <option value="">-- Select Category --</option>
{#each data.categories as cat (String(cat.id))} {#each data.categories as cat (String(cat.id))}
@@ -262,20 +270,21 @@
{/each} {/each}
</select> </select>
{#if fieldErrors.categoryId} {#if fieldErrors.categoryId}
<p class="mt-1 text-sm text-red-600">{fieldErrors.categoryId}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.categoryId}</p>
{/if} {/if}
</div> </div>
<div> <div>
<label for="field-groupId" class="mb-1 block text-sm font-medium text-slate-700" <label
>Group</label for="field-groupId"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Group</label
> >
<select <select
id="field-groupId" id="field-groupId"
name="groupId" name="groupId"
required required
bind:value={groupId} bind:value={groupId}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
> >
<option value="">-- Select Group --</option> <option value="">-- Select Group --</option>
{#each data.groups as group (String(group.id))} {#each data.groups as group (String(group.id))}
@@ -283,42 +292,45 @@
{/each} {/each}
</select> </select>
{#if fieldErrors.groupId} {#if fieldErrors.groupId}
<p class="mt-1 text-sm text-red-600">{fieldErrors.groupId}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.groupId}</p>
{/if} {/if}
</div> </div>
<div> <div>
<label for="field-content" class="mb-1 block text-sm font-medium text-slate-700" <label
>Content</label for="field-content"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Content</label
> >
<div class="rounded-md border border-slate-300 bg-white"> <div
<div class="flex gap-1 border-b border-slate-200 px-2 py-1.5"> class="rounded-md border border-slate-300 bg-white dark:border-slate-700 dark:bg-slate-900"
>
<div class="flex gap-1 border-b border-slate-200 px-2 py-1.5 dark:border-slate-800">
<button <button
type="button" type="button"
onclick={toggleBold} onclick={toggleBold}
title="Bold" title="Bold"
class="rounded px-2 py-1 text-sm font-bold text-slate-600 transition hover:bg-slate-100" class="rounded px-2 py-1 text-sm font-bold text-slate-600 transition hover:bg-slate-100 dark:text-slate-400 dark:hover:bg-slate-800"
>B</button >B</button
> >
<button <button
type="button" type="button"
onclick={toggleItalic} onclick={toggleItalic}
title="Italic" title="Italic"
class="rounded px-2 py-1 text-sm text-slate-600 italic transition hover:bg-slate-100" class="rounded px-2 py-1 text-sm text-slate-600 italic transition hover:bg-slate-100 dark:text-slate-400 dark:hover:bg-slate-800"
>I</button >I</button
> >
<button <button
type="button" type="button"
onclick={toggleHighlight} onclick={toggleHighlight}
title="Highlight" title="Highlight"
class="rounded px-2 py-1 text-sm text-slate-600 transition hover:bg-slate-100" class="rounded px-2 py-1 text-sm text-slate-600 transition hover:bg-slate-100 dark:text-slate-400 dark:hover:bg-slate-800"
>H</button >H</button
> >
<button <button
type="button" type="button"
onclick={setColor} onclick={setColor}
title="Cycle text color" title="Cycle text color"
class="rounded px-2 py-1 text-sm text-slate-600 transition hover:bg-slate-100" class="rounded px-2 py-1 text-sm text-slate-600 transition hover:bg-slate-100 dark:text-slate-400 dark:hover:bg-slate-800"
>A</button >A</button
> >
</div> </div>
@@ -329,13 +341,13 @@
role="textbox" role="textbox"
tabindex="0" tabindex="0"
contenteditable="true" contenteditable="true"
class="px-3 py-2 text-sm text-slate-900 focus:outline-none" class="px-3 py-2 text-sm text-slate-900 focus:outline-none dark:text-slate-100"
></div> ></div>
{/key} {/key}
<input type="hidden" name="content" value={content} /> <input type="hidden" name="content" value={content} />
</div> </div>
{#if fieldErrors.content} {#if fieldErrors.content}
<p class="mt-1 text-sm text-red-600">{fieldErrors.content}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.content}</p>
{/if} {/if}
</div> </div>
@@ -343,7 +355,7 @@
<button <button
type="button" type="button"
onclick={closeModal} 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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Cancel Cancel
</button> </button>
@@ -148,14 +148,8 @@ export const actions = {
}; };
} }
const { const { id, profileId, isDefaultMailing, isDefaultBilling, isDefaultShipping, ...addressData } =
id, form.data;
profileId,
isDefaultMailing,
isDefaultBilling,
isDefaultShipping,
...addressData
} = form.data;
if (!profileId) { if (!profileId) {
return { success: false, action: 'upsertAddress', addressError: 'Missing profile ID' }; return { success: false, action: 'upsertAddress', addressError: 'Missing profile ID' };
@@ -71,13 +71,15 @@
<AdminModal title="Create Profile" {open} {onClose}> <AdminModal title="Create Profile" {open} {onClose}>
{#if successMessage} {#if successMessage}
<div <div
class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700" class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700 dark:border-green-900 dark:bg-green-950/50 dark:text-green-300"
> >
{successMessage} {successMessage}
</div> </div>
{/if} {/if}
{#if formError} {#if formError}
<div class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"> <div
class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-900 dark:bg-red-950/50 dark:text-red-400"
>
{formError} {formError}
</div> </div>
{/if} {/if}
@@ -89,7 +91,7 @@
<button <button
type="button" type="button"
onclick={onClose} onclick={onClose}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Cancel Cancel
</button> </button>
@@ -156,13 +156,15 @@
<AdminModal title="Edit Profile" {open} {onClose} {canDelete} recordId={String(record.id ?? '')}> <AdminModal title="Edit Profile" {open} {onClose} {canDelete} recordId={String(record.id ?? '')}>
{#if successMessage} {#if successMessage}
<div <div
class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700" class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700 dark:border-green-900 dark:bg-green-950/50 dark:text-green-300"
> >
{successMessage} {successMessage}
</div> </div>
{/if} {/if}
{#if formError} {#if formError}
<div class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"> <div
class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-900 dark:bg-red-950/50 dark:text-red-400"
>
{formError} {formError}
</div> </div>
{/if} {/if}
@@ -176,7 +178,7 @@
<button <button
type="button" type="button"
onclick={onClose} onclick={onClose}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Cancel Cancel
</button> </button>
@@ -189,13 +191,13 @@
</div> </div>
</form> </form>
<div class="mt-8 border-t border-slate-200 pt-6"> <div class="mt-8 border-t border-slate-200 pt-6 dark:border-slate-800">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<h3 class="text-sm font-semibold text-slate-900">Addresses</h3> <h3 class="text-sm font-semibold text-slate-900 dark:text-slate-100">Addresses</h3>
<button <button
type="button" type="button"
onclick={toggleAddressForm} onclick={toggleAddressForm}
class="rounded-md border border-slate-300 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-50" class="rounded-md border border-slate-300 bg-white px-3 py-1.5 text-xs font-medium text-slate-700 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
{addressFormOpen ? 'Cancel' : 'Add Address +'} {addressFormOpen ? 'Cancel' : 'Add Address +'}
</button> </button>
@@ -208,13 +210,17 @@
<input type="hidden" name="id" value={editingAddressId} /> <input type="hidden" name="id" value={editingAddressId} />
{/if} {/if}
{#if addressError} {#if addressError}
<div class="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"> <div
class="rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-900 dark:bg-red-950/50 dark:text-red-400"
>
{addressError} {addressError}
</div> </div>
{/if} {/if}
<div class="grid gap-4 sm:grid-cols-2"> <div class="grid gap-4 sm:grid-cols-2">
<div> <div>
<label for="addr-street" class="mb-1 block text-sm font-medium text-slate-700" <label
for="addr-street"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300"
>Street</label >Street</label
> >
<input <input
@@ -223,11 +229,13 @@
name="street" name="street"
required required
bind:value={addrStreet} bind:value={addrStreet}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
</div> </div>
<div> <div>
<label for="addr-city" class="mb-1 block text-sm font-medium text-slate-700">City</label <label
for="addr-city"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">City</label
> >
<input <input
id="addr-city" id="addr-city"
@@ -235,12 +243,13 @@
name="city" name="city"
required required
bind:value={addrCity} bind:value={addrCity}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
</div> </div>
<div> <div>
<label for="addr-state" class="mb-1 block text-sm font-medium text-slate-700" <label
>State</label for="addr-state"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">State</label
> >
<input <input
id="addr-state" id="addr-state"
@@ -248,11 +257,13 @@
name="state" name="state"
required required
bind:value={addrState} bind:value={addrState}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
</div> </div>
<div> <div>
<label for="addr-postal" class="mb-1 block text-sm font-medium text-slate-700" <label
for="addr-postal"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300"
>Postal Code</label >Postal Code</label
> >
<input <input
@@ -261,11 +272,13 @@
name="postalCode" name="postalCode"
required required
bind:value={addrPostal} bind:value={addrPostal}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
</div> </div>
<div> <div>
<label for="addr-country" class="mb-1 block text-sm font-medium text-slate-700" <label
for="addr-country"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300"
>Country</label >Country</label
> >
<input <input
@@ -274,11 +287,11 @@
name="country" name="country"
required required
bind:value={addrCountry} bind:value={addrCountry}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
</div> </div>
</div> </div>
<div class="flex flex-wrap gap-4 text-sm text-slate-700"> <div class="flex flex-wrap gap-4 text-sm text-slate-700 dark:text-slate-300">
<label class="flex items-center gap-2"> <label class="flex items-center gap-2">
<input type="checkbox" name="isDefaultMailing" bind:checked={addrMailing} /> <input type="checkbox" name="isDefaultMailing" bind:checked={addrMailing} />
Default Mailing Default Mailing
@@ -295,7 +308,7 @@
<div class="flex justify-end"> <div class="flex justify-end">
<button <button
type="submit" type="submit"
class="rounded-md bg-slate-800 px-4 py-2 text-sm font-medium text-white transition hover:bg-slate-700" class="rounded-md bg-slate-800 px-4 py-2 text-sm font-medium text-white transition hover:bg-slate-700 dark:bg-slate-700 dark:hover:bg-slate-600"
> >
{editingAddressId ? 'Update Address' : 'Save Address'} {editingAddressId ? 'Update Address' : 'Save Address'}
</button> </button>
@@ -304,11 +317,11 @@
{/if} {/if}
{#if addresses.length > 0} {#if addresses.length > 0}
<div class="mt-4 overflow-x-auto rounded-md border border-slate-200"> <div class="mt-4 overflow-x-auto rounded-md border border-slate-200 dark:border-slate-800">
<table class="w-full min-w-max text-left text-sm text-slate-700"> <table class="w-full min-w-max text-left text-sm text-slate-700 dark:text-slate-300">
<thead> <thead>
<tr <tr
class="border-b border-slate-200 bg-slate-100 text-xs font-semibold tracking-wide text-slate-500" class="border-b border-slate-200 bg-slate-100 text-xs font-semibold tracking-wide text-slate-500 dark:border-slate-800 dark:bg-slate-800 dark:text-slate-400"
> >
<th scope="col" class="px-3 py-2">Street</th> <th scope="col" class="px-3 py-2">Street</th>
<th scope="col" class="px-3 py-2">City</th> <th scope="col" class="px-3 py-2">City</th>
@@ -323,7 +336,7 @@
</thead> </thead>
<tbody> <tbody>
{#each addresses as addr, i (String(addr.id ?? i))} {#each addresses as addr, i (String(addr.id ?? i))}
<tr class="bg-white"> <tr class="bg-white dark:bg-slate-900">
<td class="px-3 py-2 whitespace-nowrap">{addr.street as string}</td> <td class="px-3 py-2 whitespace-nowrap">{addr.street as string}</td>
<td class="px-3 py-2 whitespace-nowrap">{addr.city as string}</td> <td class="px-3 py-2 whitespace-nowrap">{addr.city as string}</td>
<td class="px-3 py-2 whitespace-nowrap">{addr.state as string}</td> <td class="px-3 py-2 whitespace-nowrap">{addr.state as string}</td>
@@ -337,7 +350,7 @@
<button <button
type="button" type="button"
onclick={() => openEditAddress(addr)} onclick={() => openEditAddress(addr)}
class="rounded-md border border-slate-300 bg-white px-2 py-1 text-xs font-medium text-slate-700 transition hover:bg-slate-50" class="rounded-md border border-slate-300 bg-white px-2 py-1 text-xs font-medium text-slate-700 transition hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Edit Edit
</button> </button>
@@ -345,7 +358,7 @@
<input type="hidden" name="id" value={String(addr.id ?? '')} /> <input type="hidden" name="id" value={String(addr.id ?? '')} />
<button <button
type="submit" type="submit"
class="rounded-md border border-red-300 bg-white px-2 py-1 text-xs font-medium text-red-600 transition hover:bg-red-50" class="rounded-md border border-red-300 bg-white px-2 py-1 text-xs font-medium text-red-600 transition hover:bg-red-50 dark:border-red-800 dark:bg-slate-900 dark:text-red-400 dark:hover:bg-red-950/50"
> >
Delete Delete
</button> </button>
@@ -358,7 +371,7 @@
</table> </table>
</div> </div>
{:else} {:else}
<p class="mt-2 text-sm text-slate-600">No addresses on file.</p> <p class="mt-2 text-sm text-slate-600 dark:text-slate-400">No addresses on file.</p>
{/if} {/if}
</div> </div>
</AdminModal> </AdminModal>
@@ -24,7 +24,10 @@
<div class="flex flex-col gap-4"> <div class="flex flex-col gap-4">
<div> <div>
<label for="field-email" class="mb-1 block text-sm font-medium text-slate-700">Email</label> <label
for="field-email"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Email</label
>
<input <input
id="field-email" id="field-email"
type="email" type="email"
@@ -32,16 +35,17 @@
required required
bind:value={email} bind:value={email}
placeholder="user@example.com" placeholder="user@example.com"
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.email} {#if fieldErrors.email}
<p class="mt-1 text-sm text-red-600">{fieldErrors.email}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.email}</p>
{/if} {/if}
</div> </div>
<div> <div>
<label for="field-firstName" class="mb-1 block text-sm font-medium text-slate-700" <label
>First Name</label for="field-firstName"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">First Name</label
> >
<input <input
id="field-firstName" id="field-firstName"
@@ -49,16 +53,17 @@
name="firstName" name="firstName"
required required
bind:value={firstName} bind:value={firstName}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.firstName} {#if fieldErrors.firstName}
<p class="mt-1 text-sm text-red-600">{fieldErrors.firstName}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.firstName}</p>
{/if} {/if}
</div> </div>
<div> <div>
<label for="field-lastName" class="mb-1 block text-sm font-medium text-slate-700" <label
>Last Name</label for="field-lastName"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Last Name</label
> >
<input <input
id="field-lastName" id="field-lastName"
@@ -66,15 +71,18 @@
name="lastName" name="lastName"
required required
bind:value={lastName} bind:value={lastName}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.lastName} {#if fieldErrors.lastName}
<p class="mt-1 text-sm text-red-600">{fieldErrors.lastName}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.lastName}</p>
{/if} {/if}
</div> </div>
<div> <div>
<label for="field-phoneNumber" class="mb-1 block text-sm font-medium text-slate-700"> <label
for="field-phoneNumber"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300"
>
Phone Number Phone Number
</label> </label>
<input <input
@@ -83,10 +91,10 @@
name="phoneNumber" name="phoneNumber"
required required
bind:value={phoneNumber} bind:value={phoneNumber}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.phoneNumber} {#if fieldErrors.phoneNumber}
<p class="mt-1 text-sm text-red-600">{fieldErrors.phoneNumber}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.phoneNumber}</p>
{/if} {/if}
</div> </div>
</div> </div>
@@ -112,13 +112,15 @@
> >
{#if successMessage} {#if successMessage}
<div <div
class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700" class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700 dark:border-green-900 dark:bg-green-950/50 dark:text-green-300"
> >
{successMessage} {successMessage}
</div> </div>
{/if} {/if}
{#if formError} {#if formError}
<div class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"> <div
class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-900 dark:bg-red-950/50 dark:text-red-400"
>
{formError} {formError}
</div> </div>
{/if} {/if}
@@ -129,22 +131,28 @@
{/if} {/if}
<div> <div>
<label for="field-name" class="mb-1 block text-sm font-medium text-slate-700">Name</label> <label
for="field-name"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Name</label
>
<input <input
id="field-name" id="field-name"
type="text" type="text"
name="name" name="name"
required required
bind:value={name} 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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.name} {#if fieldErrors.name}
<p class="mt-1 text-sm text-red-600">{fieldErrors.name}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.name}</p>
{/if} {/if}
</div> </div>
<div> <div>
<label for="field-value" class="mb-1 block text-sm font-medium text-slate-700">Value</label> <label
for="field-value"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Value</label
>
<input <input
id="field-value" id="field-value"
type="number" type="number"
@@ -153,10 +161,10 @@
min="1" min="1"
step="1" step="1"
bind:value bind:value
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.value} {#if fieldErrors.value}
<p class="mt-1 text-sm text-red-600">{fieldErrors.value}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.value}</p>
{/if} {/if}
</div> </div>
@@ -164,7 +172,7 @@
<button <button
type="button" type="button"
onclick={closeModal} 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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Cancel Cancel
</button> </button>
+23 -12
View File
@@ -131,13 +131,15 @@
> >
{#if successMessage} {#if successMessage}
<div <div
class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700" class="mb-4 rounded-md border border-green-200 bg-green-50 px-3 py-2 text-sm text-green-700 dark:border-green-900 dark:bg-green-950/50 dark:text-green-300"
> >
{successMessage} {successMessage}
</div> </div>
{/if} {/if}
{#if formError} {#if formError}
<div class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600"> <div
class="mb-4 rounded-md border border-red-200 bg-red-50 px-3 py-2 text-sm text-red-600 dark:border-red-900 dark:bg-red-950/50 dark:text-red-400"
>
{formError} {formError}
</div> </div>
{/if} {/if}
@@ -148,43 +150,52 @@
{/if} {/if}
<div> <div>
<label for="field-name" class="mb-1 block text-sm font-medium text-slate-700">Name</label> <label
for="field-name"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Name</label
>
<input <input
id="field-name" id="field-name"
type="text" type="text"
name="name" name="name"
required required
bind:value={name} 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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.name} {#if fieldErrors.name}
<p class="mt-1 text-sm text-red-600">{fieldErrors.name}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.name}</p>
{/if} {/if}
</div> </div>
<div> <div>
<label for="field-email" class="mb-1 block text-sm font-medium text-slate-700">Email</label> <label
for="field-email"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Email</label
>
<input <input
id="field-email" id="field-email"
type="email" type="email"
name="email" name="email"
required required
bind:value={email} bind:value={email}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
/> />
{#if fieldErrors.email} {#if fieldErrors.email}
<p class="mt-1 text-sm text-red-600">{fieldErrors.email}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.email}</p>
{/if} {/if}
</div> </div>
<div> <div>
<label for="field-roleId" class="mb-1 block text-sm font-medium text-slate-700">Role</label> <label
for="field-roleId"
class="mb-1 block text-sm font-medium text-slate-700 dark:text-slate-300">Role</label
>
<select <select
id="field-roleId" id="field-roleId"
name="roleId" name="roleId"
required required
bind:value={roleId} bind:value={roleId}
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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-100"
> >
<option value="">-- Select Role --</option> <option value="">-- Select Role --</option>
{#each data.roleOptions as role (String(role.id))} {#each data.roleOptions as role (String(role.id))}
@@ -192,7 +203,7 @@
{/each} {/each}
</select> </select>
{#if fieldErrors.roleId} {#if fieldErrors.roleId}
<p class="mt-1 text-sm text-red-600">{fieldErrors.roleId}</p> <p class="mt-1 text-sm text-red-600 dark:text-red-400">{fieldErrors.roleId}</p>
{/if} {/if}
</div> </div>
@@ -200,7 +211,7 @@
<button <button
type="button" type="button"
onclick={closeModal} 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" 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 dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Cancel Cancel
</button> </button>
+156 -22
View File
@@ -3,6 +3,7 @@
import favicon from '$lib/assets/favicon.svg'; import favicon from '$lib/assets/favicon.svg';
import { page } from '$app/state'; import { page } from '$app/state';
import { resolve } from '$app/paths'; import { resolve } from '$app/paths';
import { onMount } from 'svelte';
import type { Snippet } from 'svelte'; import type { Snippet } from 'svelte';
import type { LayoutData } from './$types'; import type { LayoutData } from './$types';
import './layout.css'; import './layout.css';
@@ -12,6 +13,17 @@
let menuOpen = $state(false); let menuOpen = $state(false);
let adminOpen = $state(false); let adminOpen = $state(false);
let dark = $state(false);
onMount(() => {
dark = document.documentElement.classList.contains('dark');
});
function toggleTheme() {
dark = !dark;
document.documentElement.classList.toggle('dark', dark);
localStorage.setItem('theme', dark ? 'dark' : 'light');
}
const isAdmin = $derived((data.userRoleId ?? 4) <= 3); const isAdmin = $derived((data.userRoleId ?? 4) <= 3);
const adminModels = [ const adminModels = [
@@ -49,8 +61,8 @@
function activeClass(path: string) { function activeClass(path: string) {
return isActive(path) return isActive(path)
? 'bg-indigo-50 text-indigo-700' ? 'bg-indigo-50 text-indigo-700 dark:bg-indigo-950 dark:text-indigo-300'
: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900'; : 'text-slate-600 dark:text-slate-400 hover:bg-slate-100 dark:hover:bg-slate-800 hover:text-slate-900 dark:hover:text-slate-100';
} }
function closeMenu() { function closeMenu() {
@@ -75,7 +87,9 @@
<meta name="twitter:card" content="summary" /> <meta name="twitter:card" content="summary" />
</svelte:head> </svelte:head>
<header class="sticky top-0 z-40 border-b border-slate-200 bg-white/90 backdrop-blur"> <header
class="sticky top-0 z-40 border-b border-slate-200 bg-white/90 backdrop-blur dark:border-slate-800 dark:bg-slate-900/90"
>
<div class="mx-auto flex h-14 max-w-6xl items-center justify-between gap-4 px-4"> <div class="mx-auto flex h-14 max-w-6xl items-center justify-between gap-4 px-4">
<a href={resolve('/')} class="flex shrink-0 items-center gap-2"> <a href={resolve('/')} class="flex shrink-0 items-center gap-2">
<span <span
@@ -96,7 +110,9 @@
/></svg /></svg
> >
</span> </span>
<span class="text-base font-semibold tracking-tight text-slate-900">Radiant Wave</span> <span class="text-base font-semibold tracking-tight text-slate-900 dark:text-slate-100"
>Radiant Wave</span
>
</a> </a>
{#if $session.data?.user} {#if $session.data?.user}
@@ -128,8 +144,8 @@
onclick={() => (adminOpen = !adminOpen)} onclick={() => (adminOpen = !adminOpen)}
class={`flex items-center gap-1 rounded-md px-3 py-2 text-sm font-medium transition ${ class={`flex items-center gap-1 rounded-md px-3 py-2 text-sm font-medium transition ${
isActive('/admin') isActive('/admin')
? 'bg-indigo-50 text-indigo-700' ? 'bg-indigo-50 text-indigo-700 dark:bg-indigo-950 dark:text-indigo-300'
: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100'
}`} }`}
> >
Admin Admin
@@ -150,7 +166,7 @@
{#if adminOpen} {#if adminOpen}
<div <div
class="absolute top-full left-0 mt-1 min-w-56 rounded-lg border border-slate-200 bg-white p-1 shadow-lg" class="absolute top-full left-0 mt-1 min-w-56 rounded-lg border border-slate-200 bg-white p-1 shadow-lg dark:border-slate-800 dark:bg-slate-900"
> >
{#each adminModels as model (model.slug)} {#each adminModels as model (model.slug)}
<a <a
@@ -158,8 +174,8 @@
onclick={() => (adminOpen = false)} onclick={() => (adminOpen = false)}
class={`block rounded-md px-3 py-2 text-sm transition ${ class={`block rounded-md px-3 py-2 text-sm transition ${
isActive('/admin/' + model.slug) isActive('/admin/' + model.slug)
? 'bg-indigo-50 text-indigo-700' ? 'bg-indigo-50 text-indigo-700 dark:bg-indigo-950 dark:text-indigo-300'
: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100'
}`} }`}
> >
{model.displayName} {model.displayName}
@@ -170,11 +186,47 @@
</div> </div>
{/if} {/if}
<span class="mx-2 h-5 w-px bg-slate-200" aria-hidden="true"></span> <button
type="button"
aria-label="Toggle dark mode"
aria-pressed={dark}
onclick={toggleTheme}
class="flex h-9 w-9 items-center justify-center rounded-md text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100"
>
{#if dark}
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><circle cx="12" cy="12" r="4" /><path
d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"
/></svg
>
{:else}
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" /></svg
>
{/if}
</button>
<span class="mx-2 h-5 w-px bg-slate-200 dark:bg-slate-700" aria-hidden="true"></span>
<button <button
type="button" type="button"
onclick={handleSignOut} onclick={handleSignOut}
class="rounded-md px-3 py-2 text-sm font-medium text-slate-500 transition hover:bg-slate-100 hover:text-slate-900" class="rounded-md px-3 py-2 text-sm font-medium text-slate-500 transition hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100"
> >
Sign Out Sign Out
</button> </button>
@@ -193,15 +245,87 @@
> >
Sign In Sign In
</a> </a>
<button
type="button"
aria-label="Toggle dark mode"
aria-pressed={dark}
onclick={toggleTheme}
class="ml-1 flex h-9 w-9 items-center justify-center rounded-md text-slate-600 transition hover:bg-slate-100 hover:text-slate-900 dark:text-slate-400 dark:hover:bg-slate-800 dark:hover:text-slate-100"
>
{#if dark}
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><circle cx="12" cy="12" r="4" /><path
d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"
/></svg
>
{:else}
<svg
xmlns="http://www.w3.org/2000/svg"
width="18"
height="18"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" /></svg
>
{/if}
</button>
</nav> </nav>
{/if} {/if}
<button
type="button"
aria-label="Toggle dark mode"
aria-pressed={dark}
onclick={toggleTheme}
class="flex h-10 w-10 items-center justify-center rounded-lg text-slate-700 hover:bg-slate-100 md:hidden dark:text-slate-300 dark:hover:bg-slate-800"
>
{#if dark}
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"
><circle cx="12" cy="12" r="4" /><path
d="M12 2v2M12 20v2M4.93 4.93l1.41 1.41M17.66 17.66l1.41 1.41M2 12h2M20 12h2M6.34 17.66l-1.41 1.41M19.07 4.93l-1.41 1.41"
/></svg
>
{:else}
<svg
xmlns="http://www.w3.org/2000/svg"
width="20"
height="20"
viewBox="0 0 24 24"
fill="none"
stroke="currentColor"
stroke-width="2"
stroke-linecap="round"
stroke-linejoin="round"><path d="M12 3a6 6 0 0 0 9 9 9 9 0 1 1-9-9Z" /></svg
>
{/if}
</button>
<button <button
type="button" type="button"
aria-label="Toggle navigation menu" aria-label="Toggle navigation menu"
aria-expanded={menuOpen} aria-expanded={menuOpen}
onclick={() => (menuOpen = !menuOpen)} onclick={() => (menuOpen = !menuOpen)}
class="flex h-10 w-10 items-center justify-center rounded-lg text-slate-700 hover:bg-slate-100 md:hidden" class="flex h-10 w-10 items-center justify-center rounded-lg text-slate-700 hover:bg-slate-100 md:hidden dark:text-slate-300 dark:hover:bg-slate-800"
> >
{#if menuOpen} {#if menuOpen}
<svg <svg
@@ -240,7 +364,7 @@
{#if menuOpen} {#if menuOpen}
<div <div
class="fixed inset-0 top-14 z-50 overflow-y-auto border-t border-slate-200 bg-white shadow-lg md:hidden" class="fixed inset-0 top-14 z-50 overflow-y-auto border-t border-slate-200 bg-white shadow-lg md:hidden dark:border-slate-800 dark:bg-slate-900"
> >
<nav class="mx-auto max-w-6xl px-4 py-4"> <nav class="mx-auto max-w-6xl px-4 py-4">
{#if $session.data?.user} {#if $session.data?.user}
@@ -268,15 +392,15 @@
</a> </a>
{#if isAdmin} {#if isAdmin}
<div class="mt-2 border-t border-slate-200 pt-2"> <div class="mt-2 border-t border-slate-200 pt-2 dark:border-slate-800">
<button <button
type="button" type="button"
aria-expanded={adminOpen} aria-expanded={adminOpen}
onclick={() => (adminOpen = !adminOpen)} onclick={() => (adminOpen = !adminOpen)}
class={`flex w-full items-center justify-between rounded-lg px-4 py-3 text-base font-semibold transition ${ class={`flex w-full items-center justify-between rounded-lg px-4 py-3 text-base font-semibold transition ${
isActive('/admin') isActive('/admin')
? 'bg-indigo-50 text-indigo-700' ? 'bg-indigo-50 text-indigo-700 dark:bg-indigo-950 dark:text-indigo-300'
: 'text-slate-700 hover:bg-indigo-50' : 'text-slate-700 hover:bg-indigo-50 dark:text-slate-300 dark:hover:bg-indigo-950'
}`} }`}
> >
<span>Admin</span> <span>Admin</span>
@@ -314,7 +438,7 @@
<button <button
type="button" type="button"
onclick={handleSignOut} onclick={handleSignOut}
class="mt-2 rounded-lg border-t border-slate-200 px-4 py-3 text-base font-medium text-slate-500 hover:bg-slate-100" class="mt-2 rounded-lg border-t border-slate-200 px-4 py-3 text-base font-medium text-slate-500 hover:bg-slate-100 dark:border-slate-800 dark:text-slate-400 dark:hover:bg-slate-800"
> >
Sign Out Sign Out
</button> </button>
@@ -341,21 +465,31 @@
</div> </div>
{/if} {/if}
<div class="flex min-h-[calc(100dvh-3.5rem)] flex-col bg-slate-50 text-slate-900"> <div
class="flex min-h-[calc(100dvh-3.5rem)] flex-col bg-slate-50 text-slate-900 dark:bg-slate-950 dark:text-slate-100"
>
<main class="flex-1"> <main class="flex-1">
{@render children()} {@render children()}
</main> </main>
<footer class="sticky bottom-0 z-10 border-t border-slate-200 bg-white"> <footer
class="sticky bottom-0 z-10 border-t border-slate-200 bg-white dark:border-slate-800 dark:bg-slate-900"
>
<div <div
class="mx-auto flex max-w-6xl flex-col items-center justify-between gap-2 px-4 py-6 text-sm text-slate-500 sm:flex-row" class="mx-auto flex max-w-6xl flex-col items-center justify-between gap-2 px-4 py-6 text-sm text-slate-500 sm:flex-row dark:text-slate-400"
> >
<p>&copy; {new Date().getFullYear()} Radiant Wave Technologies. All rights reserved.</p> <p>&copy; {new Date().getFullYear()} Radiant Wave Technologies. All rights reserved.</p>
{#if $session.data?.user} {#if $session.data?.user}
<a href={resolve('/profile')} class="transition hover:text-slate-900"> <a
href={resolve('/profile')}
class="transition hover:text-slate-900 dark:hover:text-slate-100"
>
Manage your profile Manage your profile
</a> </a>
{:else} {:else}
<a href={resolve('/login')} class="transition hover:text-slate-900">Sign in</a> <a
href={resolve('/login')}
class="transition hover:text-slate-900 dark:hover:text-slate-100">Sign in</a
>
{/if} {/if}
</div> </div>
</footer> </footer>
+4 -2
View File
@@ -9,7 +9,9 @@
<section class="py-16 sm:py-24"> <section class="py-16 sm:py-24">
<div class="mx-auto w-full max-w-6xl px-4"> <div class="mx-auto w-full max-w-6xl px-4">
<div class="mx-auto flex max-w-3xl flex-col items-center text-center"> <div class="mx-auto flex max-w-3xl flex-col items-center text-center">
<h1 class="mt-6 text-4xl font-bold tracking-tight text-slate-900 sm:text-5xl"> <h1
class="mt-6 text-4xl font-bold tracking-tight text-slate-900 sm:text-5xl dark:text-slate-100"
>
Welcome to the Radiant Wave Portal Welcome to the Radiant Wave Portal
</h1> </h1>
@@ -30,7 +32,7 @@
</a> </a>
<a <a
href={resolve('/register')} href={resolve('/register')}
class="inline-flex h-12 w-full items-center justify-center rounded-lg border border-slate-300 bg-white px-8 text-base font-medium text-slate-700 transition hover:bg-slate-50 sm:w-auto" class="inline-flex h-12 w-full items-center justify-center rounded-lg border border-slate-300 bg-white px-8 text-base font-medium text-slate-700 transition hover:bg-slate-50 sm:w-auto dark:border-slate-700 dark:bg-slate-900 dark:text-slate-300 dark:hover:bg-slate-800"
> >
Create Account Create Account
</a> </a>
+14 -9
View File
@@ -8,34 +8,39 @@
<Window> <Window>
{#if form?.success} {#if form?.success}
<div class="rounded-lg bg-green-50 px-4 py-6 text-center"> <div class="rounded-lg bg-green-50 px-4 py-6 text-center dark:bg-green-950/50">
<h1 class="text-xl font-bold text-slate-900">Check your email</h1> <h1 class="text-xl font-bold text-slate-900 dark:text-slate-100">Check your email</h1>
<p class="mt-3 text-sm text-slate-600"> <p class="mt-3 text-sm text-slate-600 dark:text-slate-400">
If an account with that email exists, we've sent a password reset link. If an account with that email exists, we've sent a password reset link.
</p> </p>
</div> </div>
{:else} {:else}
{#if form?.error && !form?.success} {#if form?.error && !form?.success}
<div class="mb-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700" role="alert"> <div
class="mb-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-950/50 dark:text-red-300"
role="alert"
>
{form.error} {form.error}
</div> </div>
{/if} {/if}
<form method="POST" class="flex flex-col gap-4"> <form method="POST" class="flex flex-col gap-4">
<h1 class="text-2xl font-bold text-slate-900">Forgot Password</h1> <h1 class="text-2xl font-bold text-slate-900 dark:text-slate-100">Forgot Password</h1>
<p class="text-sm text-slate-600"> <p class="text-sm text-slate-600 dark:text-slate-400">
Enter your email and we'll send you a link to reset your password. Enter your email and we'll send you a link to reset your password.
</p> </p>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="email" class="text-sm font-medium text-slate-700"> Email Address </label> <label for="email" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Email Address
</label>
<input <input
id="email" id="email"
type="email" type="email"
name="email" name="email"
placeholder="you@example.com" placeholder="you@example.com"
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
@@ -49,7 +54,7 @@
<a <a
href={resolve('/login')} href={resolve('/login')}
class="mt-6 block text-center text-sm text-indigo-600 hover:text-indigo-700" class="mt-6 block text-center text-sm text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300"
> >
Back to Login Back to Login
</a> </a>
+25
View File
@@ -2,8 +2,28 @@
@plugin '@tailwindcss/forms'; @plugin '@tailwindcss/forms';
@plugin '@tailwindcss/typography'; @plugin '@tailwindcss/typography';
@custom-variant dark (&:where(.dark, .dark *));
@layer base { @layer base {
html {
color-scheme: light;
}
html.dark {
color-scheme: dark;
}
.dark input:not([type='checkbox']):not([type='radio']),
.dark textarea,
.dark select {
background-color: var(--color-slate-900);
color: var(--color-slate-100);
border-color: var(--color-slate-700);
}
body { body {
background-color: var(--color-slate-50);
color: var(--color-slate-900);
font-family: font-family:
ui-sans-serif, ui-sans-serif,
system-ui, system-ui,
@@ -16,4 +36,9 @@
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
} }
html.dark body {
background-color: var(--color-slate-950);
color: var(--color-slate-100);
}
} }
+35 -17
View File
@@ -28,49 +28,59 @@
<Window> <Window>
{#if verified} {#if verified}
<div class="mb-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800" role="status"> <div
class="mb-4 rounded-lg bg-green-50 px-4 py-3 text-sm text-green-800 dark:bg-green-950/50 dark:text-green-300"
role="status"
>
Your email has been verified. You can now log in. Your email has been verified. You can now log in.
</div> </div>
{/if} {/if}
{#if error} {#if error}
<div class="mb-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700" role="alert"> <div
class="mb-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-950/50 dark:text-red-300"
role="alert"
>
{error} {error}
</div> </div>
{/if} {/if}
<form onsubmit={handleSubmit} class="flex flex-col gap-4"> <form onsubmit={handleSubmit} class="flex flex-col gap-4">
<h1 class="text-2xl font-bold text-slate-900">Login</h1> <h1 class="text-2xl font-bold text-slate-900 dark:text-slate-100">Login</h1>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="email" class="text-sm font-medium text-slate-700"> Email Address </label> <label for="email" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Email Address
</label>
<input <input
id="email" id="email"
type="email" type="email"
placeholder="Email Address" placeholder="Email Address"
bind:value={email} bind:value={email}
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="password" class="text-sm font-medium text-slate-700"> Password </label> <label for="password" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Password
</label>
<input <input
id="password" id="password"
type="password" type="password"
placeholder="Password" placeholder="Password"
bind:value={password} bind:value={password}
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<label class="flex items-center gap-2 text-sm text-slate-600"> <label class="flex items-center gap-2 text-sm text-slate-600 dark:text-slate-400">
<input <input
type="checkbox" type="checkbox"
bind:checked={rememberMe} bind:checked={rememberMe}
class="h-4 w-4 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500" class="h-4 w-4 rounded border-slate-300 text-indigo-600 focus:ring-indigo-500 dark:border-slate-700 dark:text-indigo-400"
/> />
Remember me Remember me
</label> </label>
@@ -84,12 +94,18 @@
</form> </form>
<div class="mt-6 flex flex-col gap-2 text-center text-sm"> <div class="mt-6 flex flex-col gap-2 text-center text-sm">
<a href={resolve('/forgot-password')} class="text-indigo-600 hover:text-indigo-700"> <a
href={resolve('/forgot-password')}
class="text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Forgot Password? Forgot Password?
</a> </a>
<span class="text-slate-500"> <span class="text-slate-500 dark:text-slate-400">
Don't have an account? Don't have an account?
<a href={resolve('/register')} class="font-medium text-indigo-600 hover:text-indigo-700"> <a
href={resolve('/register')}
class="font-medium text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Register Register
</a> </a>
</span> </span>
@@ -97,18 +113,20 @@
<div class="mt-6"> <div class="mt-6">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="h-px flex-1 bg-slate-200"></div> <div class="h-px flex-1 bg-slate-200 dark:bg-slate-700"></div>
<span class="text-sm text-slate-500">Or</span> <span class="text-sm text-slate-500 dark:text-slate-400">Or</span>
<div class="h-px flex-1 bg-slate-200"></div> <div class="h-px flex-1 bg-slate-200 dark:bg-slate-700"></div>
</div> </div>
<button <button
type="button" type="button"
aria-label="Sign In with Google"
onclick={async () => { onclick={async () => {
await authClient.signIn.social({ provider: 'google', callbackURL: '/dashboard' }); await authClient.signIn.social({ provider: 'google', callbackURL: '/dashboard' });
}} }}
class="mx-auto mt-4 flex h-11 items-center justify-center rounded-lg bg-white hover:bg-slate-50" class="mx-auto mt-4 flex h-11 items-center justify-center rounded-lg bg-white hover:bg-slate-50 dark:bg-slate-900 dark:hover:bg-slate-800"
> >
<img src="light/web_light_sq_SI.svg" alt="Sign In with Google" class="" /> <img src="light/web_light_sq_SI.svg" alt="Sign In with Google" class="dark:hidden" />
<img src="dark/web_dark_sq_SI.svg" alt="" aria-hidden="true" class="hidden dark:block" />
</button> </button>
</div> </div>
</Window> </Window>
+46 -23
View File
@@ -9,9 +9,9 @@
<Window> <Window>
{#if form?.success} {#if form?.success}
<div class="rounded-lg bg-green-50 px-4 py-6 text-center"> <div class="rounded-lg bg-green-50 px-4 py-6 text-center dark:bg-green-950/50">
<h1 class="text-xl font-bold text-slate-900">Account created!</h1> <h1 class="text-xl font-bold text-slate-900 dark:text-slate-100">Account created!</h1>
<p class="mt-3 text-sm text-slate-600"> <p class="mt-3 text-sm text-slate-600 dark:text-slate-400">
We've sent a verification email. Check your inbox and click the link to verify your email We've sent a verification email. Check your inbox and click the link to verify your email
address, then log in. address, then log in.
</p> </p>
@@ -24,72 +24,85 @@
</div> </div>
{:else} {:else}
{#if form?.error} {#if form?.error}
<div class="mb-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700" role="alert"> <div
class="mb-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-950/50 dark:text-red-300"
role="alert"
>
{form.error} {form.error}
</div> </div>
{/if} {/if}
<form method="POST" class="flex flex-col gap-4"> <form method="POST" class="flex flex-col gap-4">
<h1 class="text-2xl font-bold text-slate-900">Create Account</h1> <h1 class="text-2xl font-bold text-slate-900 dark:text-slate-100">Create Account</h1>
<div class="grid grid-cols-1 gap-4 sm:grid-cols-2"> <div class="grid grid-cols-1 gap-4 sm:grid-cols-2">
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="firstName" class="text-sm font-medium text-slate-700"> First Name </label> <label for="firstName" class="text-sm font-medium text-slate-700 dark:text-slate-300">
First Name
</label>
<input <input
id="firstName" id="firstName"
type="text" type="text"
name="firstName" name="firstName"
placeholder="First Name" placeholder="First Name"
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="lastName" class="text-sm font-medium text-slate-700"> Last Name </label> <label for="lastName" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Last Name
</label>
<input <input
id="lastName" id="lastName"
type="text" type="text"
name="lastName" name="lastName"
placeholder="Last Name" placeholder="Last Name"
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="email" class="text-sm font-medium text-slate-700"> Email Address </label> <label for="email" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Email Address
</label>
<input <input
id="email" id="email"
type="email" type="email"
name="email" name="email"
placeholder="Email Address" placeholder="Email Address"
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="password" class="text-sm font-medium text-slate-700"> Password </label> <label for="password" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Password
</label>
<input <input
id="password" id="password"
type="password" type="password"
name="password" name="password"
placeholder="Password" placeholder="Password"
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="password2" class="text-sm font-medium text-slate-700"> Verify Password </label> <label for="password2" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Verify Password
</label>
<input <input
id="password2" id="password2"
type="password" type="password"
name="password2" name="password2"
placeholder="Verify Password" placeholder="Verify Password"
required required
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
@@ -101,26 +114,36 @@
</button> </button>
</form> </form>
<div class="mt-6 border-t border-slate-200 pt-6"> <div class="mt-6 border-t border-slate-200 pt-6 dark:border-slate-800">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="h-px flex-1 bg-slate-200"></div> <div class="h-px flex-1 bg-slate-200 dark:bg-slate-700"></div>
<span class="text-sm text-slate-500">Or</span> <span class="text-sm text-slate-500 dark:text-slate-400">Or</span>
<div class="h-px flex-1 bg-slate-200"></div> <div class="h-px flex-1 bg-slate-200 dark:bg-slate-700"></div>
</div> </div>
<button <button
type="button" type="button"
aria-label="Sign Up with Google"
onclick={async () => { onclick={async () => {
await authClient.signIn.social({ provider: 'google', callbackURL: '/profile/setup' }); await authClient.signIn.social({ provider: 'google', callbackURL: '/profile/setup' });
}} }}
class="mt-4 flex h-11 w-full items-center justify-center rounded-lg border border-slate-300 bg-white hover:bg-slate-50" class="mt-4 flex h-11 w-full items-center justify-center rounded-lg border border-slate-300 bg-white hover:bg-slate-50 dark:border-slate-700 dark:bg-slate-900 dark:hover:bg-slate-800"
> >
<img src="light/web_light_sq_SU.svg" alt="Sign Up with Google" class="h-6" /> <img src="light/web_light_sq_SU.svg" alt="Sign Up with Google" class="h-6 dark:hidden" />
<img
src="dark/web_dark_sq_SU.svg"
alt=""
aria-hidden="true"
class="hidden h-6 dark:block"
/>
</button> </button>
</div> </div>
<p class="mt-6 text-center text-sm text-slate-500"> <p class="mt-6 text-center text-sm text-slate-500 dark:text-slate-400">
Already have an account? Already have an account?
<a href={resolve('/login')} class="font-medium text-indigo-600 hover:text-indigo-700"> <a
href={resolve('/login')}
class="font-medium text-indigo-600 hover:text-indigo-700 dark:text-indigo-400 dark:hover:text-indigo-300"
>
Login Login
</a> </a>
</p> </p>
+12 -7
View File
@@ -7,23 +7,28 @@
<Window> <Window>
{#if form?.error} {#if form?.error}
<div class="mb-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700" role="alert"> <div
class="mb-4 rounded-lg bg-red-50 px-4 py-3 text-sm text-red-700 dark:bg-red-950/50 dark:text-red-300"
role="alert"
>
{form.error} {form.error}
</div> </div>
{/if} {/if}
<form method="POST" class="flex flex-col gap-4"> <form method="POST" class="flex flex-col gap-4">
<h1 class="text-2xl font-bold text-slate-900"> <h1 class="text-2xl font-bold text-slate-900 dark:text-slate-100">
{data.invite ? 'Set Your Password' : 'Reset Password'} {data.invite ? 'Set Your Password' : 'Reset Password'}
</h1> </h1>
{#if data.invite} {#if data.invite}
<p class="text-sm text-slate-600"> <p class="text-sm text-slate-600 dark:text-slate-400">
Welcome! Choose a password to finish setting up your account. Welcome! Choose a password to finish setting up your account.
</p> </p>
{/if} {/if}
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="password" class="text-sm font-medium text-slate-700"> New Password </label> <label for="password" class="text-sm font-medium text-slate-700 dark:text-slate-300">
New Password
</label>
<input <input
id="password" id="password"
type="password" type="password"
@@ -31,12 +36,12 @@
placeholder="••••••••" placeholder="••••••••"
required required
minlength="8" minlength="8"
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>
<div class="flex flex-col gap-1.5"> <div class="flex flex-col gap-1.5">
<label for="password2" class="text-sm font-medium text-slate-700"> <label for="password2" class="text-sm font-medium text-slate-700 dark:text-slate-300">
Confirm New Password Confirm New Password
</label> </label>
<input <input
@@ -46,7 +51,7 @@
placeholder="••••••••" placeholder="••••••••"
required required
minlength="8" minlength="8"
class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500" class="h-11 w-full rounded-lg border-slate-300 px-3 text-sm text-slate-900 focus:border-indigo-500 focus:ring-indigo-500 dark:border-slate-700 dark:text-slate-100"
/> />
</div> </div>