mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-29 18:18:02 +07:00
initial dict location done
This commit is contained in:
parent
9cada5fbd4
commit
b82441d9af
@ -29,7 +29,7 @@ export function useMasterDetail(options = {}) {
|
|||||||
const isDirty = $derived(
|
const isDirty = $derived(
|
||||||
JSON.stringify(formState.form) !== JSON.stringify(formSnapshot)
|
JSON.stringify(formState.form) !== JSON.stringify(formSnapshot)
|
||||||
);
|
);
|
||||||
// $inspect(formState.form)
|
// $inspect(formSnapshot)
|
||||||
async function select(item) {
|
async function select(item) {
|
||||||
mode = "view";
|
mode = "view";
|
||||||
|
|
||||||
@ -63,6 +63,7 @@ export function useMasterDetail(options = {}) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function enterEdit(param) {
|
function enterEdit(param) {
|
||||||
|
console.log('f');
|
||||||
if (!selectedItem) return;
|
if (!selectedItem) return;
|
||||||
mode = "edit";
|
mode = "edit";
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,9 @@ import { z } from "zod";
|
|||||||
export const locationSchema = z.object({
|
export const locationSchema = z.object({
|
||||||
LocCode: z.string().min(1, "Required"),
|
LocCode: z.string().min(1, "Required"),
|
||||||
LocFull: z.string().min(1, "Required"),
|
LocFull: z.string().min(1, "Required"),
|
||||||
|
Email: z.string().trim().optional().refine((val) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),"Invalid email format"),
|
||||||
|
Phone: z.string().max(14, "Max 14 chars").regex(/^$|^[0-9]+$/, "Can only contain numbers"),
|
||||||
|
ZIP: z.string().regex(/^$|^[0-9]+$/, "Can only contain numbers"),
|
||||||
});
|
});
|
||||||
|
|
||||||
export const locationInitialForm = {
|
export const locationInitialForm = {
|
||||||
@ -27,6 +30,9 @@ export const locationInitialForm = {
|
|||||||
export const locationDefaultErrors = {
|
export const locationDefaultErrors = {
|
||||||
LocCode: "Required",
|
LocCode: "Required",
|
||||||
LocFull: "Required",
|
LocFull: "Required",
|
||||||
|
Email: null,
|
||||||
|
Phone: null,
|
||||||
|
ZIP: null,
|
||||||
};
|
};
|
||||||
|
|
||||||
export const locationFormFields = [
|
export const locationFormFields = [
|
||||||
@ -42,6 +48,8 @@ export const locationFormFields = [
|
|||||||
required: false,
|
required: false,
|
||||||
type: "select",
|
type: "select",
|
||||||
optionsEndpoint: `${API.BASE_URL}${API.SITE}`,
|
optionsEndpoint: `${API.BASE_URL}${API.SITE}`,
|
||||||
|
valueKey: "SiteID",
|
||||||
|
labelKey: (item) => `${item.SiteCode} - ${item.SiteName}`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "LocCode",
|
key: "LocCode",
|
||||||
@ -59,7 +67,8 @@ export const locationFormFields = [
|
|||||||
key: "LocType",
|
key: "LocType",
|
||||||
label: "Location Type",
|
label: "Location Type",
|
||||||
required: false,
|
required: false,
|
||||||
type: "text",
|
type: "select",
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.VALUESET}/location_type`,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "LocFull",
|
key: "LocFull",
|
||||||
@ -121,6 +130,7 @@ export const locationFormFields = [
|
|||||||
label: "ZIP",
|
label: "ZIP",
|
||||||
required: false,
|
required: false,
|
||||||
type: "text",
|
type: "text",
|
||||||
|
validateOn: ["input"]
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@ -136,13 +146,15 @@ export const locationFormFields = [
|
|||||||
key: "Phone",
|
key: "Phone",
|
||||||
label: "Phone",
|
label: "Phone",
|
||||||
required: false,
|
required: false,
|
||||||
type: "text"
|
type: "text",
|
||||||
|
validateOn: ["input"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: "Email",
|
key: "Email",
|
||||||
label: "Email",
|
label: "Email",
|
||||||
required: false,
|
required: false,
|
||||||
type: "text",
|
type: "text",
|
||||||
|
validateOn: ["input"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
|
|||||||
@ -1,7 +1,7 @@
|
|||||||
<script>
|
<script>
|
||||||
import { useDictionaryForm } from "$lib/components/composable/use-dictionary-form.svelte";
|
import { useDictionaryForm } from "$lib/components/composable/use-dictionary-form.svelte";
|
||||||
import FormPageContainer from "$lib/components/patient/reusable/form-page-container.svelte";
|
import FormPageContainer from "$lib/components/reusable/form/form-page-container.svelte";
|
||||||
import PatientFormRenderer from "$lib/components/patient/reusable/patient-form-renderer.svelte";
|
import DictionaryFormRenderer from "$lib/components/reusable/form/dictionary-form-renderer.svelte";
|
||||||
import { toast } from "svelte-sonner";
|
import { toast } from "svelte-sonner";
|
||||||
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
||||||
|
|
||||||
@ -47,11 +47,11 @@
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
$inspect(formState.errors)
|
$inspect(formState.form)
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<FormPageContainer title="Create Location" {primaryAction} {secondaryActions} {actions}>
|
<FormPageContainer title="Create Location" {primaryAction} {secondaryActions} {actions}>
|
||||||
<PatientFormRenderer
|
<DictionaryFormRenderer
|
||||||
{formState}
|
{formState}
|
||||||
formFields={formFields}
|
formFields={formFields}
|
||||||
mode="create"
|
mode="create"
|
||||||
|
|||||||
@ -0,0 +1,85 @@
|
|||||||
|
<script>
|
||||||
|
import { useDictionaryForm } from "$lib/components/composable/use-dictionary-form.svelte";
|
||||||
|
import FormPageContainer from "$lib/components/reusable/form/form-page-container.svelte";
|
||||||
|
import DictionaryFormRenderer from "$lib/components/reusable/form/dictionary-form-renderer.svelte";
|
||||||
|
import { toast } from "svelte-sonner";
|
||||||
|
import { untrack } from "svelte";
|
||||||
|
import { API } from "$lib/config/api";
|
||||||
|
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
||||||
|
|
||||||
|
let props = $props();
|
||||||
|
|
||||||
|
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
|
||||||
|
|
||||||
|
const { formState } = masterDetail;
|
||||||
|
|
||||||
|
const helpers = useDictionaryForm(formState);
|
||||||
|
|
||||||
|
let showConfirm = $state(false);
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
untrack(() => {
|
||||||
|
formFields.forEach(group => {
|
||||||
|
group.rows.forEach(row => {
|
||||||
|
row.columns.forEach(col => {
|
||||||
|
if (col.type === "group") {
|
||||||
|
col.columns.forEach(child => {
|
||||||
|
if (child.type === "select" && child.optionsEndpoint) {
|
||||||
|
formState.fetchOptions(child, formState.form);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else if ((col.type === "select" || col.type === "identity") && col.optionsEndpoint) {
|
||||||
|
formState.fetchOptions(col, formState.form);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
if (formState.form.Province) {
|
||||||
|
formState.fetchOptions(
|
||||||
|
{
|
||||||
|
key: "City",
|
||||||
|
optionsEndpoint: `${API.BASE_URL}${API.CITY}`,
|
||||||
|
dependsOn: "Province",
|
||||||
|
endpointParamKey: "Parent"
|
||||||
|
},
|
||||||
|
formState.form
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
async function handleEdit() {
|
||||||
|
const result = await formState.save(masterDetail.mode);
|
||||||
|
|
||||||
|
if (result.status === 'success') {
|
||||||
|
console.log('Location updated successfully');
|
||||||
|
toast('Location Updated!');
|
||||||
|
masterDetail.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to update location:', result.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const primaryAction = $derived({
|
||||||
|
label: 'Edit',
|
||||||
|
onClick: handleEdit,
|
||||||
|
disabled: helpers.hasErrors || formState.isSaving.current,
|
||||||
|
loading: formState.isSaving.current
|
||||||
|
});
|
||||||
|
|
||||||
|
const secondaryActions = [];
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<FormPageContainer title="Edit Location" {primaryAction} {secondaryActions}>
|
||||||
|
<DictionaryFormRenderer
|
||||||
|
{formState}
|
||||||
|
formFields={formFields}
|
||||||
|
mode="edit"
|
||||||
|
/>
|
||||||
|
</FormPageContainer>
|
||||||
|
|
||||||
|
<ReusableAlertDialog
|
||||||
|
bind:open={masterDetail.showExitConfirm}
|
||||||
|
onConfirm={masterDetail.confirmExit}
|
||||||
|
/>
|
||||||
@ -12,7 +12,7 @@
|
|||||||
let location = $derived(masterDetail?.selectedItem?.data);
|
let location = $derived(masterDetail?.selectedItem?.data);
|
||||||
|
|
||||||
const handlers = {
|
const handlers = {
|
||||||
editPatient: () => masterDetail.enterEdit("data"),
|
editLocation: () => masterDetail.enterEdit("data"),
|
||||||
};
|
};
|
||||||
|
|
||||||
const actions = viewActions(handlers);
|
const actions = viewActions(handlers);
|
||||||
|
|||||||
@ -11,7 +11,6 @@
|
|||||||
import CustodianModal from "$lib/components/patient/list/modal/custodian-modal.svelte";
|
import CustodianModal from "$lib/components/patient/list/modal/custodian-modal.svelte";
|
||||||
import LinktoModal from "$lib/components/patient/list/modal/linkto-modal.svelte";
|
import LinktoModal from "$lib/components/patient/list/modal/linkto-modal.svelte";
|
||||||
import ReusableUpload from "$lib/components/reusable/reusable-upload.svelte";
|
import ReusableUpload from "$lib/components/reusable/reusable-upload.svelte";
|
||||||
import ChevronUpIcon from "@lucide/svelte/icons/chevron-up";
|
|
||||||
import CheckIcon from "@lucide/svelte/icons/check";
|
import CheckIcon from "@lucide/svelte/icons/check";
|
||||||
import XIcon from "@lucide/svelte/icons/x";
|
import XIcon from "@lucide/svelte/icons/x";
|
||||||
|
|
||||||
|
|||||||
233
src/lib/components/reusable/form/dictionary-form-renderer.svelte
Normal file
233
src/lib/components/reusable/form/dictionary-form-renderer.svelte
Normal file
@ -0,0 +1,233 @@
|
|||||||
|
<script>
|
||||||
|
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
|
||||||
|
import * as Select from "$lib/components/ui/select/index.js";
|
||||||
|
import { Toggle } from "$lib/components/ui/toggle/index.js";
|
||||||
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
|
import { Input } from "$lib/components/ui/input/index.js";
|
||||||
|
import { Label } from "$lib/components/ui/label/index.js";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
|
let {
|
||||||
|
formState,
|
||||||
|
formFields,
|
||||||
|
mode = 'create'
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
let searchQuery = $state({});
|
||||||
|
|
||||||
|
function getFilteredOptions(key) {
|
||||||
|
const query = searchQuery[key] || "";
|
||||||
|
if (!query) return formState.selectOptions[key] ?? [];
|
||||||
|
|
||||||
|
return (formState.selectOptions[key] ?? []).filter(opt =>
|
||||||
|
opt.label.toLowerCase().includes(query.toLowerCase())
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect(() => {
|
||||||
|
initializeDefaultValues();
|
||||||
|
});
|
||||||
|
|
||||||
|
async function initializeDefaultValues() {
|
||||||
|
for (const group of formFields) {
|
||||||
|
for (const row of group.rows) {
|
||||||
|
for (const col of row.columns) {
|
||||||
|
if (col.type === "group") {
|
||||||
|
for (const child of col.columns) {
|
||||||
|
await handleDefaultValue(child);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
await handleDefaultValue(col);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleDefaultValue(field) {
|
||||||
|
if (!field.defaultValue || !field.optionsEndpoint) return;
|
||||||
|
|
||||||
|
await formState.fetchOptions(field, formState.form);
|
||||||
|
|
||||||
|
if (!formState.form[field.key]) {
|
||||||
|
formState.form[field.key] = field.defaultValue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
{#snippet Fieldset({ key, label, required, type, optionsEndpoint, options, validateOn, dependsOn, endpointParamKey, valueKey, labelKey })}
|
||||||
|
<div class="flex w-full flex-col gap-1.5">
|
||||||
|
<div class="flex justify-between items-center w-full">
|
||||||
|
<Label>{label}</Label>
|
||||||
|
{#if required}
|
||||||
|
<span class="text-destructive text-xl leading-none h-3.5">*</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="relative flex flex-col items-center w-full">
|
||||||
|
{#if type === "text"}
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
bind:value={formState.form[key]}
|
||||||
|
oninput={() => {
|
||||||
|
if (validateOn?.includes("input")) {
|
||||||
|
formState.validateField(key, formState.form[key], false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onblur={() => {
|
||||||
|
if (validateOn?.includes("blur")) {
|
||||||
|
validateFieldAsync(key, mode, originalData?.[key]);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else if type === "email"}
|
||||||
|
<Input
|
||||||
|
type="email"
|
||||||
|
bind:value={formState.form[key]}
|
||||||
|
oninput={() => {
|
||||||
|
if (validateOn?.includes("input")) {
|
||||||
|
formState.validateField(key, formState.form[key], false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onblur={() => {
|
||||||
|
if (validateOn?.includes("blur")) {
|
||||||
|
formState.validateField(key, formState.form[key], false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else if type === "number"}
|
||||||
|
<Input
|
||||||
|
type="number"
|
||||||
|
bind:value={formState.form[key]}
|
||||||
|
oninput={() => {
|
||||||
|
if (validateOn?.includes("input")) {
|
||||||
|
formState.validateField(key, formState.form[key], false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onblur={() => {
|
||||||
|
if (validateOn?.includes("blur")) {
|
||||||
|
formState.validateField(key, formState.form[key], false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{:else if type === "textarea"}
|
||||||
|
<textarea
|
||||||
|
class="flex min-h-[80px] w-full rounded-md border border-input bg-transparent px-3 py-2 text-sm shadow-sm placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50"
|
||||||
|
oninput={() => {
|
||||||
|
if (validateOn?.includes("input")) {
|
||||||
|
formState.validateField(key, formState.form[key], false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onblur={() => {
|
||||||
|
if (validateOn?.includes("blur")) {
|
||||||
|
formState.validateField(key, formState.form[key], false);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
bind:value={formState.form[key]}
|
||||||
|
></textarea>
|
||||||
|
{:else if type === "select"}
|
||||||
|
{@const selectedLabel = formState.selectOptions[key]?.find(opt => opt.value === formState.form[key])?.label || "Choose"}
|
||||||
|
{@const filteredOptions = getFilteredOptions(key)}
|
||||||
|
<Select.Root type="single" bind:value={formState.form[key]}
|
||||||
|
onValueChange={(val) => {
|
||||||
|
formState.form[key] = val;
|
||||||
|
if (validateOn?.includes("input")) {
|
||||||
|
formState.validateField(key, formState.form[key], false);
|
||||||
|
}
|
||||||
|
if (key === "Province") {
|
||||||
|
formState.form.City = "";
|
||||||
|
formState.selectOptions.City = [];
|
||||||
|
formState.lastFetched.City = null;
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
onOpenChange={(open) => {
|
||||||
|
if (open && optionsEndpoint) {
|
||||||
|
formState.fetchOptions(
|
||||||
|
{ key, optionsEndpoint, dependsOn, endpointParamKey, valueKey, labelKey },
|
||||||
|
formState.form
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Select.Trigger class="w-full truncate">
|
||||||
|
{selectedLabel}
|
||||||
|
</Select.Trigger>
|
||||||
|
<Select.Content>
|
||||||
|
<div class="p-2">
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Search..."
|
||||||
|
class="w-full border rounded px-2 py-1 text-sm"
|
||||||
|
bind:value={searchQuery[key]}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{#if formState.loadingOptions[key]}
|
||||||
|
<Select.Item disabled value="loading">Loading...</Select.Item>
|
||||||
|
{:else}
|
||||||
|
{#if !required}
|
||||||
|
<Select.Item value="">- None -</Select.Item>
|
||||||
|
{/if}
|
||||||
|
{#each filteredOptions as option}
|
||||||
|
<Select.Item value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</Select.Item>
|
||||||
|
{/each}
|
||||||
|
{/if}
|
||||||
|
</Select.Content>
|
||||||
|
</Select.Root>
|
||||||
|
{:else}
|
||||||
|
<Input
|
||||||
|
type="text"
|
||||||
|
bind:value={formState.form[key]}
|
||||||
|
placeholder="Custom field type: {type}"
|
||||||
|
/>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
<div class="absolute top-8 min-h-[1rem] w-full">
|
||||||
|
{#if formState.errors[key]}
|
||||||
|
<span class="text-destructive text-sm leading-none">
|
||||||
|
{formState.errors[key]}
|
||||||
|
</span>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{/snippet}
|
||||||
|
|
||||||
|
<div class="flex-1 min-h-0 overflow-y-auto p-2 space-y-6">
|
||||||
|
{#each formFields as group}
|
||||||
|
<div class="space-y-6">
|
||||||
|
{#if group.title}
|
||||||
|
<div class="text-md 2xl:text-lg font-semibold italic">
|
||||||
|
<span class="border-b-2 border-primary">{group.title}</span>
|
||||||
|
</div>
|
||||||
|
{/if}
|
||||||
|
|
||||||
|
{#each group.rows as row}
|
||||||
|
<div
|
||||||
|
class="grid grid-cols-1 space-y-2 gap-6 md:gap-4"
|
||||||
|
class:md:grid-cols-1={row.columns.length === 1}
|
||||||
|
class:md:grid-cols-2={row.columns.length === 2}
|
||||||
|
class:md:grid-cols-3={row.columns.length === 3}
|
||||||
|
>
|
||||||
|
{#each row.columns as col}
|
||||||
|
{#if col.type === "group"}
|
||||||
|
<div
|
||||||
|
class="grid grid-cols-1 gap-6 md:gap-2"
|
||||||
|
class:md:grid-cols-1={col.columns.length === 1}
|
||||||
|
class:md:grid-cols-2={col.columns.length === 2}
|
||||||
|
class:md:grid-cols-3={col.columns.length === 3}
|
||||||
|
>
|
||||||
|
{#each col.columns as child}
|
||||||
|
{@render Fieldset(child)}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{:else}
|
||||||
|
{@render Fieldset(col)}
|
||||||
|
{/if}
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
61
src/lib/components/reusable/form/form-page-container.svelte
Normal file
61
src/lib/components/reusable/form/form-page-container.svelte
Normal file
@ -0,0 +1,61 @@
|
|||||||
|
<script>
|
||||||
|
import * as DropdownMenu from "$lib/components/ui/dropdown-menu/index.js";
|
||||||
|
import { Button } from "$lib/components/ui/button/index.js";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
|
import ChevronUpIcon from "@lucide/svelte/icons/chevron-up";
|
||||||
|
|
||||||
|
let {
|
||||||
|
title,
|
||||||
|
primaryAction,
|
||||||
|
secondaryActions,
|
||||||
|
actions,
|
||||||
|
children
|
||||||
|
} = $props();
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div class="flex flex-col p-2 gap-4 h-full w-full">
|
||||||
|
<TopbarWrapper actions={actions} title={title}/>
|
||||||
|
{@render children()}
|
||||||
|
<div class="mt-auto flex justify-end items-center pt-2">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
class="cursor-pointer {secondaryActions.length ? 'rounded-r-none' : ''}"
|
||||||
|
disabled={primaryAction.disabled}
|
||||||
|
onclick={primaryAction.onClick}
|
||||||
|
>
|
||||||
|
{#if primaryAction.loading}
|
||||||
|
<Spinner />
|
||||||
|
{:else}
|
||||||
|
{primaryAction.label}
|
||||||
|
{/if}
|
||||||
|
</Button>
|
||||||
|
{#if secondaryActions.length}
|
||||||
|
<DropdownMenu.Root>
|
||||||
|
<DropdownMenu.Trigger>
|
||||||
|
<Button
|
||||||
|
size="icon"
|
||||||
|
class="size-8 rounded-l-none"
|
||||||
|
disabled={primaryAction.disabled}
|
||||||
|
>
|
||||||
|
<ChevronUpIcon />
|
||||||
|
</Button>
|
||||||
|
</DropdownMenu.Trigger>
|
||||||
|
|
||||||
|
<DropdownMenu.Content collisionPadding={8}>
|
||||||
|
<DropdownMenu.Group>
|
||||||
|
{#each secondaryActions as action}
|
||||||
|
<DropdownMenu.Item
|
||||||
|
disabled={action.disabled}
|
||||||
|
onclick={action.onClick}
|
||||||
|
>
|
||||||
|
{action.label}
|
||||||
|
</DropdownMenu.Item>
|
||||||
|
{/each}
|
||||||
|
</DropdownMenu.Group>
|
||||||
|
</DropdownMenu.Content>
|
||||||
|
</DropdownMenu.Root>
|
||||||
|
{/if}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
@ -118,7 +118,7 @@
|
|||||||
</Table.Root>
|
</Table.Root>
|
||||||
<div class="flex items-center justify-between p-2 mt-auto">
|
<div class="flex items-center justify-between p-2 mt-auto">
|
||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<p class="text-sm font-medium">Rows per page</p>
|
<p class="text-sm font-medium">Rows</p>
|
||||||
<Select.Root
|
<Select.Root
|
||||||
allowDeselect={false}
|
allowDeselect={false}
|
||||||
type="single"
|
type="single"
|
||||||
@ -127,11 +127,11 @@
|
|||||||
table.setPageSize(Number(value));
|
table.setPageSize(Number(value));
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Select.Trigger class="h-8 w-[70px]">
|
<Select.Trigger class="h-7 w-[70px]">
|
||||||
{String(table.getState().pagination.pageSize)}
|
{String(table.getState().pagination.pageSize)}
|
||||||
</Select.Trigger>
|
</Select.Trigger>
|
||||||
<Select.Content side="top">
|
<Select.Content side="top">
|
||||||
{#each [1, 2, 3, 4, 5] as pageSize (pageSize)}
|
{#each [5, 10, 15, 20, 25] as pageSize (pageSize)}
|
||||||
<Select.Item value={`${pageSize}`}>
|
<Select.Item value={`${pageSize}`}>
|
||||||
{pageSize}
|
{pageSize}
|
||||||
</Select.Item>
|
</Select.Item>
|
||||||
@ -147,7 +147,7 @@
|
|||||||
<div class="flex items-center space-x-2">
|
<div class="flex items-center space-x-2">
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="hidden size-8 p-0 lg:flex"
|
class="hidden size-7 p-0 lg:flex"
|
||||||
onclick={() => table.setPageIndex(0)}
|
onclick={() => table.setPageIndex(0)}
|
||||||
disabled={!table.getCanPreviousPage()}
|
disabled={!table.getCanPreviousPage()}
|
||||||
>
|
>
|
||||||
@ -156,7 +156,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="size-8 p-0"
|
class="size-7 p-0"
|
||||||
onclick={() => table.previousPage()}
|
onclick={() => table.previousPage()}
|
||||||
disabled={!table.getCanPreviousPage()}
|
disabled={!table.getCanPreviousPage()}
|
||||||
>
|
>
|
||||||
@ -165,7 +165,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="size-8 p-0"
|
class="size-7 p-0"
|
||||||
onclick={() => table.nextPage()}
|
onclick={() => table.nextPage()}
|
||||||
disabled={!table.getCanNextPage()}
|
disabled={!table.getCanNextPage()}
|
||||||
>
|
>
|
||||||
@ -174,7 +174,7 @@
|
|||||||
</Button>
|
</Button>
|
||||||
<Button
|
<Button
|
||||||
variant="outline"
|
variant="outline"
|
||||||
class="hidden size-8 p-0 lg:flex"
|
class="hidden size-7 p-0 lg:flex"
|
||||||
onclick={() => table.setPageIndex(table.getPageCount() - 1)}
|
onclick={() => table.setPageIndex(table.getPageCount() - 1)}
|
||||||
disabled={!table.getCanNextPage()}
|
disabled={!table.getCanNextPage()}
|
||||||
>
|
>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user