Build Admin pages
This commit is contained in:
@@ -0,0 +1,104 @@
|
||||
<script lang="ts">
|
||||
import { fieldNameToLabel } from '$lib/svelte-admin/introspect';
|
||||
import type { PrismaScalarType, RelationOptionConfig } from '$lib/svelte-admin/types';
|
||||
import ComboBox from './ComboBox.svelte';
|
||||
|
||||
type RelationOptionsData = Record<string, { label: string; value: string | number }[]>;
|
||||
|
||||
type Props = {
|
||||
fields: string[];
|
||||
fieldTypes: Record<string, PrismaScalarType>;
|
||||
relationOptions?: Record<string, RelationOptionConfig>;
|
||||
relationOptionsData?: RelationOptionsData;
|
||||
record?: Record<string, unknown>;
|
||||
form?: Record<string, unknown>;
|
||||
action: string;
|
||||
submitLabel?: string;
|
||||
};
|
||||
|
||||
let {
|
||||
fields,
|
||||
fieldTypes,
|
||||
relationOptions = {},
|
||||
relationOptionsData = {},
|
||||
record,
|
||||
form,
|
||||
action,
|
||||
submitLabel = 'Save'
|
||||
}: Props = $props();
|
||||
|
||||
function getValue(field: string): unknown {
|
||||
return form?.[field] ?? record?.[field] ?? '';
|
||||
}
|
||||
|
||||
function isRelation(field: string): boolean {
|
||||
return field in relationOptions;
|
||||
}
|
||||
</script>
|
||||
|
||||
<form method="POST" {action} class="space-y-5">
|
||||
{#if record?.id || form?.id}
|
||||
<input type="hidden" name="id" value={String(form?.id ?? record?.id)} />
|
||||
{/if}
|
||||
|
||||
{#each fields as field}
|
||||
{@const type = fieldTypes[field] ?? 'String'}
|
||||
{@const label = fieldNameToLabel(field)}
|
||||
|
||||
<div>
|
||||
<label for={field} class="mb-1 block text-sm font-medium text-gray-700">{label}</label>
|
||||
|
||||
{#if isRelation(field)}
|
||||
<ComboBox
|
||||
id={field}
|
||||
name={field}
|
||||
options={relationOptionsData[field] ?? []}
|
||||
placeholder="Select {label}"
|
||||
value={getValue(field) as string | number | undefined}
|
||||
/>
|
||||
{:else if type === 'Boolean'}
|
||||
<input
|
||||
type="checkbox"
|
||||
id={field}
|
||||
name={field}
|
||||
checked={getValue(field) === true ||
|
||||
getValue(field) === 'on' ||
|
||||
getValue(field) === 'true'}
|
||||
class="h-4 w-4 rounded border-gray-300 text-blue-600 focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{:else if type === 'DateTime'}
|
||||
<input
|
||||
type="datetime-local"
|
||||
id={field}
|
||||
name={field}
|
||||
value={getValue(field) ? String(getValue(field)).slice(0, 16) : ''}
|
||||
class="w-full rounded-lg border border-gray-300 px-3 py-2 transition-shadow outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{:else if type === 'Int' || type === 'Float'}
|
||||
<input
|
||||
type="number"
|
||||
id={field}
|
||||
name={field}
|
||||
value={getValue(field) ?? ''}
|
||||
step={type === 'Float' ? '0.01' : '1'}
|
||||
class="w-full rounded-lg border border-gray-300 px-3 py-2 transition-shadow outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{:else}
|
||||
<input
|
||||
type="text"
|
||||
id={field}
|
||||
name={field}
|
||||
value={getValue(field) ?? ''}
|
||||
class="w-full rounded-lg border border-gray-300 px-3 py-2 transition-shadow outline-none focus:border-blue-500 focus:ring-2 focus:ring-blue-500"
|
||||
/>
|
||||
{/if}
|
||||
</div>
|
||||
{/each}
|
||||
|
||||
<button
|
||||
type="submit"
|
||||
class="rounded-lg bg-blue-600 px-4 py-2.5 font-medium text-white transition-colors hover:bg-blue-700 focus:ring-4 focus:ring-blue-200"
|
||||
>
|
||||
{submitLabel}
|
||||
</button>
|
||||
</form>
|
||||
Reference in New Issue
Block a user