mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-27 11:25:53 +07:00
change all page (add indicator spinner rowclick, popover autoclose after search)
This commit is contained in:
parent
239147f7ec
commit
ea1369f69b
@ -2,6 +2,7 @@ import PlusIcon from "@lucide/svelte/icons/plus";
|
|||||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
import { API } from "$lib/config/api";
|
import { API } from "$lib/config/api";
|
||||||
|
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||||
|
|
||||||
export const searchFields = [
|
export const searchFields = [
|
||||||
{
|
{
|
||||||
@ -72,8 +73,13 @@ export const detailSections = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function accountActions(masterDetail) {
|
export function accountActions(masterDetail, handlers) {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
Icon: RefreshIcon,
|
||||||
|
label: 'Refresh Data',
|
||||||
|
onClick: handlers.refresh,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
label: 'Add Account',
|
label: 'Add Account',
|
||||||
|
|||||||
@ -26,8 +26,14 @@
|
|||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
const result = await formState.save(masterDetail.mode);
|
const result = await formState.save(masterDetail.mode);
|
||||||
|
|
||||||
toast('Account Created!');
|
if (result.status === 'success') {
|
||||||
masterDetail?.exitForm(true);
|
toast('Account Created!');
|
||||||
|
masterDetail?.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to save account');
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to save account';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
|
|||||||
@ -8,20 +8,23 @@
|
|||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
import UserRoundXIcon from "@lucide/svelte/icons/user-round-x";
|
import UserRoundXIcon from "@lucide/svelte/icons/user-round-x";
|
||||||
|
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const search = useSearch(searchFields, getAccounts);
|
const search = useSearch(searchFields, getAccounts);
|
||||||
const initialForm = props.masterDetail.formState.form;
|
const handlers = {
|
||||||
const actions = accountActions(props.masterDetail, initialForm)
|
refresh: () => {search.handleSearch()},
|
||||||
|
};
|
||||||
|
const actions = accountActions(props.masterDetail, handlers)
|
||||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields}
|
<ReusableSearchParam {searchFields}
|
||||||
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
|
bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}
|
||||||
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@ -38,10 +41,13 @@
|
|||||||
>
|
>
|
||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
{#if props.masterDetail.isFormMode}
|
{#if props.masterDetail.isFormMode}
|
||||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
{#each "ACCOUNT".split("") as c}
|
<MoveLeftIcon />
|
||||||
<span class="leading-none">{c}</span>
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
{/each}
|
{#each "ACCOUNT".split("") as c}
|
||||||
|
<span class="leading-none">{c}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import UserRoundXIcon from "@lucide/svelte/icons/user-round-x";
|
import UserRoundXIcon from "@lucide/svelte/icons/user-round-x";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -46,7 +47,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={masterDetail.selectedItem.data.AccountName}
|
title={masterDetail.selectedItem.data.AccountName}
|
||||||
|
|||||||
@ -22,9 +22,9 @@
|
|||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields}
|
<ReusableSearchParam {searchFields}
|
||||||
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
|
bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}
|
||||||
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|||||||
@ -9,6 +9,7 @@
|
|||||||
import * as Card from "$lib/components/ui/card/index.js";
|
import * as Card from "$lib/components/ui/card/index.js";
|
||||||
import { Badge } from "$lib/components/ui/badge/index.js";
|
import { Badge } from "$lib/components/ui/badge/index.js";
|
||||||
import * as Table from "$lib/components/ui/table/index.js";
|
import * as Table from "$lib/components/ui/table/index.js";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -97,7 +98,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={masterDetail.selectedItem.data.NameFirst}
|
title={masterDetail.selectedItem.data.NameFirst}
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import PlusIcon from "@lucide/svelte/icons/plus";
|
import PlusIcon from "@lucide/svelte/icons/plus";
|
||||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
|
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||||
|
|
||||||
export const searchFields = [
|
export const searchFields = [
|
||||||
{
|
{
|
||||||
@ -35,8 +36,13 @@ export const detailSections = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function containerActions(masterDetail) {
|
export function containerActions(masterDetail, handlers) {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
Icon: RefreshIcon,
|
||||||
|
label: 'Refresh Data',
|
||||||
|
onClick: handlers.refresh,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
label: 'Add Location',
|
label: 'Add Location',
|
||||||
|
|||||||
@ -26,8 +26,14 @@
|
|||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
const result = await formState.save(masterDetail.mode);
|
const result = await formState.save(masterDetail.mode);
|
||||||
|
|
||||||
toast('Container Created!');
|
if (result.status === 'success') {
|
||||||
masterDetail?.exitForm(true);
|
toast('Container Created!');
|
||||||
|
masterDetail?.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to save container');
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to save container';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
|
|||||||
@ -8,20 +8,23 @@
|
|||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
import ArchiveXIcon from "@lucide/svelte/icons/archive-x";
|
import ArchiveXIcon from "@lucide/svelte/icons/archive-x";
|
||||||
|
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const search = useSearch(searchFields, getContainers);
|
const search = useSearch(searchFields, getContainers);
|
||||||
const initialForm = props.masterDetail.formState.form;
|
const handlers = {
|
||||||
const actions = containerActions(props.masterDetail, initialForm)
|
refresh: () => {search.handleSearch()},
|
||||||
|
};
|
||||||
|
const actions = containerActions(props.masterDetail, handlers)
|
||||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields}
|
<ReusableSearchParam {searchFields}
|
||||||
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
|
bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}
|
||||||
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@ -38,10 +41,13 @@
|
|||||||
>
|
>
|
||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
{#if props.masterDetail.isFormMode}
|
{#if props.masterDetail.isFormMode}
|
||||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
{#each "CONTAINER".split("") as c}
|
<MoveLeftIcon />
|
||||||
<span class="leading-none">{c}</span>
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
{/each}
|
{#each "CONTAINER".split("") as c}
|
||||||
|
<span class="leading-none">{c}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ArchiveXIcon from "@lucide/svelte/icons/archive-x";
|
import ArchiveXIcon from "@lucide/svelte/icons/archive-x";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -46,7 +47,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={masterDetail.selectedItem.data.ConName}
|
title={masterDetail.selectedItem.data.ConName}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import PlusIcon from "@lucide/svelte/icons/plus";
|
|||||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
import { API } from "$lib/config/api";
|
import { API } from "$lib/config/api";
|
||||||
|
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||||
|
|
||||||
export const searchFields = [
|
export const searchFields = [
|
||||||
{
|
{
|
||||||
@ -28,8 +29,13 @@ export const detailSections = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function departmentActions(masterDetail) {
|
export function departmentActions(masterDetail, handlers) {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
Icon: RefreshIcon,
|
||||||
|
label: 'Refresh Data',
|
||||||
|
onClick: handlers.refresh,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
label: 'Add Department',
|
label: 'Add Department',
|
||||||
|
|||||||
@ -26,8 +26,14 @@
|
|||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
const result = await formState.save(masterDetail.mode);
|
const result = await formState.save(masterDetail.mode);
|
||||||
|
|
||||||
toast('Department Created!');
|
if (result.status === 'success') {
|
||||||
masterDetail?.exitForm(true);
|
toast('Department Created!');
|
||||||
|
masterDetail?.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to save department');
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to save department';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
|
|||||||
@ -8,20 +8,23 @@
|
|||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
import FileXIcon from "@lucide/svelte/icons/file-x";
|
import FileXIcon from "@lucide/svelte/icons/file-x";
|
||||||
|
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const search = useSearch(searchFields, getDepartments);
|
const search = useSearch(searchFields, getDepartments);
|
||||||
const initialForm = props.masterDetail.formState.form;
|
const handlers = {
|
||||||
const actions = departmentActions(props.masterDetail, initialForm)
|
refresh: () => {search.handleSearch()},
|
||||||
|
};
|
||||||
|
const actions = departmentActions(props.masterDetail, handlers)
|
||||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields}
|
<ReusableSearchParam {searchFields}
|
||||||
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
|
bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}
|
||||||
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@ -38,10 +41,13 @@
|
|||||||
>
|
>
|
||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
{#if props.masterDetail.isFormMode}
|
{#if props.masterDetail.isFormMode}
|
||||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
{#each "DEPARTMENT".split("") as c}
|
<MoveLeftIcon />
|
||||||
<span class="leading-none">{c}</span>
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
{/each}
|
{#each "DEPARTMENT".split("") as c}
|
||||||
|
<span class="leading-none">{c}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import FileXIcon from "@lucide/svelte/icons/file-x";
|
import FileXIcon from "@lucide/svelte/icons/file-x";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -46,7 +47,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={masterDetail.selectedItem.data.DepartmentName}
|
title={masterDetail.selectedItem.data.DepartmentName}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import PlusIcon from "@lucide/svelte/icons/plus";
|
|||||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
import { API } from "$lib/config/api";
|
import { API } from "$lib/config/api";
|
||||||
|
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||||
|
|
||||||
export const searchFields = [
|
export const searchFields = [
|
||||||
{
|
{
|
||||||
@ -30,8 +31,13 @@ export const detailSections = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function disciplineActions(masterDetail) {
|
export function disciplineActions(masterDetail, handlers) {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
Icon: RefreshIcon,
|
||||||
|
label: 'Refresh Data',
|
||||||
|
onClick: handlers.refresh,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
label: 'Add Discipline',
|
label: 'Add Discipline',
|
||||||
|
|||||||
@ -26,8 +26,14 @@
|
|||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
const result = await formState.save(masterDetail.mode);
|
const result = await formState.save(masterDetail.mode);
|
||||||
|
|
||||||
toast('Discipline Created!');
|
if (result.status === 'success') {
|
||||||
masterDetail?.exitForm(true);
|
toast('Discipline Created!');
|
||||||
|
masterDetail?.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to save discipline');
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to save discipline';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
|
|||||||
@ -8,20 +8,23 @@
|
|||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
import BookXIcon from "@lucide/svelte/icons/book-x";
|
import BookXIcon from "@lucide/svelte/icons/book-x";
|
||||||
|
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const search = useSearch(searchFields, getDisciplines);
|
const search = useSearch(searchFields, getDisciplines);
|
||||||
const initialForm = props.masterDetail.formState.form;
|
const handlers = {
|
||||||
const actions = disciplineActions(props.masterDetail, initialForm)
|
refresh: () => {search.handleSearch()},
|
||||||
|
};
|
||||||
|
const actions = disciplineActions(props.masterDetail, handlers)
|
||||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields}
|
<ReusableSearchParam {searchFields}
|
||||||
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
|
bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}
|
||||||
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@ -38,10 +41,13 @@
|
|||||||
>
|
>
|
||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
{#if props.masterDetail.isFormMode}
|
{#if props.masterDetail.isFormMode}
|
||||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
{#each "DISCIPLINE".split("") as c}
|
<MoveLeftIcon />
|
||||||
<span class="leading-none">{c}</span>
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
{/each}
|
{#each "DISCIPLINE".split("") as c}
|
||||||
|
<span class="leading-none">{c}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import BookXIcon from "@lucide/svelte/icons/book-x";
|
import BookXIcon from "@lucide/svelte/icons/book-x";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -46,7 +47,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={masterDetail.selectedItem.data.DisciplineName}
|
title={masterDetail.selectedItem.data.DisciplineName}
|
||||||
|
|||||||
@ -22,9 +22,9 @@
|
|||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields}
|
<ReusableSearchParam {searchFields}
|
||||||
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
|
bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}
|
||||||
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import MapPinXIcon from "@lucide/svelte/icons/map-pin-x";
|
import MapPinXIcon from "@lucide/svelte/icons/map-pin-x";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -46,7 +47,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={masterDetail.selectedItem.data.LocFull}
|
title={masterDetail.selectedItem.data.LocFull}
|
||||||
|
|||||||
@ -26,8 +26,14 @@
|
|||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
const result = await formState.save(masterDetail.mode);
|
const result = await formState.save(masterDetail.mode);
|
||||||
|
|
||||||
toast('Occupation Created!');
|
if (result.status === 'success') {
|
||||||
masterDetail?.exitForm(true);
|
toast('Occupation Created!');
|
||||||
|
masterDetail?.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to save occupation');
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to save occupation';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import PlusIcon from "@lucide/svelte/icons/plus";
|
|||||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
import { API } from "$lib/config/api";
|
import { API } from "$lib/config/api";
|
||||||
|
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||||
|
|
||||||
export const searchFields = [
|
export const searchFields = [
|
||||||
{
|
{
|
||||||
@ -35,8 +36,13 @@ export const detailSections = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function siteActions(masterDetail) {
|
export function siteActions(masterDetail, handlers) {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
Icon: RefreshIcon,
|
||||||
|
label: 'Refresh Data',
|
||||||
|
onClick: handlers.refresh,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
label: 'Add Site',
|
label: 'Add Site',
|
||||||
|
|||||||
@ -26,8 +26,14 @@
|
|||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
const result = await formState.save(masterDetail.mode);
|
const result = await formState.save(masterDetail.mode);
|
||||||
|
|
||||||
toast('Site Created!');
|
if (result.status === 'success') {
|
||||||
masterDetail?.exitForm(true);
|
toast('Site Created!');
|
||||||
|
masterDetail?.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to save site');
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to save site';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
|
|||||||
@ -8,20 +8,23 @@
|
|||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
import ClipboardXIcon from "@lucide/svelte/icons/clipboard-x";
|
import ClipboardXIcon from "@lucide/svelte/icons/clipboard-x";
|
||||||
|
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const search = useSearch(searchFields, getSites);
|
const search = useSearch(searchFields, getSites);
|
||||||
const initialForm = props.masterDetail.formState.form;
|
const handlers = {
|
||||||
const actions = siteActions(props.masterDetail, initialForm)
|
refresh: () => {search.handleSearch()},
|
||||||
|
};
|
||||||
|
const actions = siteActions(props.masterDetail, handlers)
|
||||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields}
|
<ReusableSearchParam {searchFields}
|
||||||
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
|
bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}
|
||||||
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@ -38,10 +41,13 @@
|
|||||||
>
|
>
|
||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
{#if props.masterDetail.isFormMode}
|
{#if props.masterDetail.isFormMode}
|
||||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
{#each "SITE".split("") as c}
|
<MoveLeftIcon />
|
||||||
<span class="leading-none">{c}</span>
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
{/each}
|
{#each "SITE".split("") as c}
|
||||||
|
<span class="leading-none">{c}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ClipboardXIcon from "@lucide/svelte/icons/clipboard-x";
|
import ClipboardXIcon from "@lucide/svelte/icons/clipboard-x";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -46,7 +47,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={masterDetail.selectedItem.data.SiteName}
|
title={masterDetail.selectedItem.data.SiteName}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import PlusIcon from "@lucide/svelte/icons/plus";
|
|||||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
import { API } from "$lib/config/api";
|
import { API } from "$lib/config/api";
|
||||||
|
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||||
|
|
||||||
export const searchFields = [
|
export const searchFields = [
|
||||||
{
|
{
|
||||||
@ -36,8 +37,13 @@ export const detailSections = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function testActions(masterDetail) {
|
export function testActions(masterDetail, handlers) {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
Icon: RefreshIcon,
|
||||||
|
label: 'Refresh Data',
|
||||||
|
onClick: handlers.refresh,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
label: 'Add Test',
|
label: 'Add Test',
|
||||||
|
|||||||
@ -132,8 +132,14 @@
|
|||||||
|
|
||||||
const result = await formState.save(masterDetail.mode, payload);
|
const result = await formState.save(masterDetail.mode, payload);
|
||||||
|
|
||||||
toast('Test Created!');
|
if (result.status === 'success') {
|
||||||
masterDetail?.exitForm(true);
|
toast('Test Created!');
|
||||||
|
masterDetail?.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to save test');
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to save test';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
|
|||||||
@ -125,8 +125,15 @@
|
|||||||
|
|
||||||
const result = await formState.save(masterDetail.mode, payload);
|
const result = await formState.save(masterDetail.mode, payload);
|
||||||
|
|
||||||
toast('Test Updated!');
|
if (result.status === 'success') {
|
||||||
masterDetail?.exitForm(true);
|
console.log('Test updated successfully');
|
||||||
|
toast('Test Updated!');
|
||||||
|
masterDetail.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to update test:', result.message);
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to update test';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
|
|||||||
@ -8,20 +8,23 @@
|
|||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
import FlaskConicalOffIcon from "@lucide/svelte/icons/flask-conical-off";
|
import FlaskConicalOffIcon from "@lucide/svelte/icons/flask-conical-off";
|
||||||
|
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const search = useSearch(searchFields, getTests);
|
const search = useSearch(searchFields, getTests);
|
||||||
const initialForm = props.masterDetail.formState.form;
|
const handlers = {
|
||||||
const actions = testActions(props.masterDetail, initialForm)
|
refresh: () => {search.handleSearch()},
|
||||||
|
};
|
||||||
|
const actions = testActions(props.masterDetail, handlers)
|
||||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields}
|
<ReusableSearchParam {searchFields}
|
||||||
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
|
bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}
|
||||||
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@ -38,10 +41,13 @@
|
|||||||
>
|
>
|
||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
{#if props.masterDetail.isFormMode}
|
{#if props.masterDetail.isFormMode}
|
||||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
{#each "TEST".split("") as c}
|
<MoveLeftIcon />
|
||||||
<span class="leading-none">{c}</span>
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
{/each}
|
{#each "TEST".split("") as c}
|
||||||
|
<span class="leading-none">{c}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import FlaskConicalOffIcon from "@lucide/svelte/icons/flask-conical-off";
|
import FlaskConicalOffIcon from "@lucide/svelte/icons/flask-conical-off";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -46,7 +47,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={masterDetail.selectedItem?.data?.TestSiteName}
|
title={masterDetail.selectedItem?.data?.TestSiteName}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import PlusIcon from "@lucide/svelte/icons/plus";
|
|||||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
import { API } from "$lib/config/api";
|
import { API } from "$lib/config/api";
|
||||||
|
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||||
|
|
||||||
export const searchFields = [
|
export const searchFields = [
|
||||||
{
|
{
|
||||||
@ -35,8 +36,13 @@ export const detailSections = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function testMapActions(masterDetail) {
|
export function testMapActions(masterDetail, handlers) {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
Icon: RefreshIcon,
|
||||||
|
label: 'Refresh Data',
|
||||||
|
onClick: handlers.refresh,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
label: 'Add TestMap',
|
label: 'Add TestMap',
|
||||||
|
|||||||
@ -115,8 +115,14 @@
|
|||||||
console.log(payload)
|
console.log(payload)
|
||||||
const result = await formState.save(masterDetail.mode, payload);
|
const result = await formState.save(masterDetail.mode, payload);
|
||||||
|
|
||||||
toast('Test Map Created!');
|
if (result.status === 'success') {
|
||||||
masterDetail?.exitForm(true);
|
toast('Test Map Created!');
|
||||||
|
masterDetail?.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to save test map');
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to save test map';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
|
|||||||
@ -13,16 +13,18 @@
|
|||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const search = useSearch(searchFields, getTestMaps);
|
const search = useSearch(searchFields, getTestMaps);
|
||||||
const initialForm = props.masterDetail.formState.form;
|
const handlers = {
|
||||||
const actions = testMapActions(props.masterDetail, initialForm)
|
refresh: () => {search.handleSearch()},
|
||||||
|
};
|
||||||
|
const actions = testMapActions(props.masterDetail, handlers)
|
||||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields}
|
<ReusableSearchParam {searchFields}
|
||||||
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
|
bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}
|
||||||
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@ -42,7 +44,7 @@
|
|||||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
<MoveLeftIcon />
|
<MoveLeftIcon />
|
||||||
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
{#each "BACK TO TEST MAP".split("") as c}
|
{#each "TEST MAP".split("") as c}
|
||||||
<span class="leading-none">{c}</span>
|
<span class="leading-none">{c}</span>
|
||||||
{/each}
|
{/each}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,123 +0,0 @@
|
|||||||
<script>
|
|
||||||
import { formatUTCDate } from "$lib/utils/formatUTCDate";
|
|
||||||
import { detailSections, viewActions } from "$lib/components/dictionary/testmap/config/testmap-config";
|
|
||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
|
||||||
import MapIcon from "@lucide/svelte/icons/map";
|
|
||||||
|
|
||||||
let props = $props();
|
|
||||||
|
|
||||||
const { masterDetail, formFields, formActions, schema } = props.context;
|
|
||||||
|
|
||||||
let testMap = $derived(masterDetail?.selectedItem?.data);
|
|
||||||
$inspect(testMap)
|
|
||||||
|
|
||||||
const handlers = {
|
|
||||||
editTest: () => masterDetail.enterEdit("data"),
|
|
||||||
};
|
|
||||||
|
|
||||||
const actions = viewActions(handlers);
|
|
||||||
|
|
||||||
function getFieldValue(field) {
|
|
||||||
if (!testMap) return "-";
|
|
||||||
|
|
||||||
if (field.keys) {
|
|
||||||
return field.keys
|
|
||||||
.map(k => field.parentKey ? testMap[field.parentKey]?.[k] : testMap[k])
|
|
||||||
.filter(val => val && val.trim() !== "")
|
|
||||||
.join(" / ");
|
|
||||||
}
|
|
||||||
|
|
||||||
return field.parentKey ? testMap[field.parentKey]?.[field.key] : testMap[field.key];
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
{#snippet DetailsTable({ value, label })}
|
|
||||||
<div class="space-y-1.5">
|
|
||||||
<dt class="text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
|
||||||
{label}
|
|
||||||
</dt>
|
|
||||||
<dd>
|
|
||||||
{#if value && Array.isArray(value) && value.length > 0}
|
|
||||||
<table class="w-full text-sm border border-border rounded-md overflow-hidden">
|
|
||||||
<thead class="bg-muted text-muted-foreground">
|
|
||||||
<tr>
|
|
||||||
<th class="px-3 py-2 text-left font-medium text-xs uppercase tracking-wider">Host Test Code</th>
|
|
||||||
<th class="px-3 py-2 text-left font-medium text-xs uppercase tracking-wider">Host Test Name</th>
|
|
||||||
<th class="px-3 py-2 text-left font-medium text-xs uppercase tracking-wider">Client Test Code</th>
|
|
||||||
<th class="px-3 py-2 text-left font-medium text-xs uppercase tracking-wider">Client Test Name</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{#each value as row, i}
|
|
||||||
<tr class={i % 2 === 0 ? 'bg-background' : 'bg-muted/30'}>
|
|
||||||
<td class="px-3 py-2">{row.HostTestCode ?? '-'}</td>
|
|
||||||
<td class="px-3 py-2">{row.HostTestName ?? '-'}</td>
|
|
||||||
<td class="px-3 py-2">{row.ClientTestCode ?? '-'}</td>
|
|
||||||
<td class="px-3 py-2">{row.ClientTestName ?? '-'}</td>
|
|
||||||
</tr>
|
|
||||||
{/each}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
{:else}
|
|
||||||
<span class="text-sm font-medium">-</span>
|
|
||||||
{/if}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
{/snippet}
|
|
||||||
|
|
||||||
{#snippet Fieldset({ value, label, isUTCDate = false })}
|
|
||||||
<div class="space-y-1.5">
|
|
||||||
<dt class="text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
|
||||||
{label}
|
|
||||||
</dt>
|
|
||||||
<dd class="text-sm font-medium">
|
|
||||||
{#if isUTCDate}
|
|
||||||
{formatUTCDate(value)}
|
|
||||||
{:else}
|
|
||||||
{value ?? "-"}
|
|
||||||
{/if}
|
|
||||||
</dd>
|
|
||||||
</div>
|
|
||||||
{/snippet}
|
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
|
||||||
<TopbarWrapper
|
|
||||||
title={masterDetail.selectedItem?.data?.HostType}
|
|
||||||
{actions}
|
|
||||||
/>
|
|
||||||
<div class="flex-1 min-h-0 overflow-y-auto space-y-4">
|
|
||||||
{#each detailSections as section}
|
|
||||||
<div class="p-4">
|
|
||||||
<div class={section.class}>
|
|
||||||
{#each section.fields as field}
|
|
||||||
{#if field.fullWidth}
|
|
||||||
<div class="col-span-2">
|
|
||||||
{#if field.key === "details"}
|
|
||||||
{@render DetailsTable({ label: field.label, value: getFieldValue(field) })}
|
|
||||||
{:else}
|
|
||||||
{@render Fieldset({ label: field.label, value: getFieldValue(field), isUTCDate: field.isUTCDate })}
|
|
||||||
{/if}
|
|
||||||
</div>
|
|
||||||
{:else if field.key === "details"}
|
|
||||||
{@render DetailsTable({
|
|
||||||
label: field.label,
|
|
||||||
value: getFieldValue(field),
|
|
||||||
})}
|
|
||||||
{:else}
|
|
||||||
{@render Fieldset({
|
|
||||||
label: field.label,
|
|
||||||
value: getFieldValue(field),
|
|
||||||
isUTCDate: field.isUTCDate
|
|
||||||
})}
|
|
||||||
{/if}
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{/each}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{:else}
|
|
||||||
<ReusableEmpty icon={MapIcon} desc="Select a test map to see details"/>
|
|
||||||
{/if}
|
|
||||||
@ -5,6 +5,7 @@
|
|||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import MapIcon from "@lucide/svelte/icons/map";
|
import MapIcon from "@lucide/svelte/icons/map";
|
||||||
import * as Table from "$lib/components/ui/table/index.js";
|
import * as Table from "$lib/components/ui/table/index.js";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -85,7 +86,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={masterDetail.selectedItem?.data?.HostType}
|
title={masterDetail.selectedItem?.data?.HostType}
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import PlusIcon from "@lucide/svelte/icons/plus";
|
|||||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||||
import { API } from "$lib/config/api";
|
import { API } from "$lib/config/api";
|
||||||
|
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||||
|
|
||||||
export const searchFields = [
|
export const searchFields = [
|
||||||
{
|
{
|
||||||
@ -30,8 +31,13 @@ export const detailSections = [
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
export function workstationActions(masterDetail) {
|
export function workstationActions(masterDetail, handlers) {
|
||||||
return [
|
return [
|
||||||
|
{
|
||||||
|
Icon: RefreshIcon,
|
||||||
|
label: 'Refresh Data',
|
||||||
|
onClick: handlers.refresh,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
Icon: PlusIcon,
|
Icon: PlusIcon,
|
||||||
label: 'Add Workstation',
|
label: 'Add Workstation',
|
||||||
|
|||||||
@ -26,8 +26,14 @@
|
|||||||
async function handleSave() {
|
async function handleSave() {
|
||||||
const result = await formState.save(masterDetail.mode);
|
const result = await formState.save(masterDetail.mode);
|
||||||
|
|
||||||
toast('Workstation Created!');
|
if (result.status === 'success') {
|
||||||
masterDetail?.exitForm(true);
|
toast('Workstation Created!');
|
||||||
|
masterDetail?.exitForm(true);
|
||||||
|
} else {
|
||||||
|
console.error('Failed to save workstation');
|
||||||
|
const errorMessages = result.messages ? Object.values(result.messages).join('\n') : 'Failed to save workstation';
|
||||||
|
toast.error(errorMessages)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const primaryAction = $derived({
|
const primaryAction = $derived({
|
||||||
|
|||||||
@ -8,20 +8,23 @@
|
|||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||||
import MonitorXIcon from "@lucide/svelte/icons/monitor-x";
|
import MonitorXIcon from "@lucide/svelte/icons/monitor-x";
|
||||||
|
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
const search = useSearch(searchFields, getWorkstations);
|
const search = useSearch(searchFields, getWorkstations);
|
||||||
const initialForm = props.masterDetail.formState.form;
|
const handlers = {
|
||||||
const actions = workstationActions(props.masterDetail, initialForm)
|
refresh: () => {search.handleSearch()},
|
||||||
|
};
|
||||||
|
const actions = workstationActions(props.masterDetail, handlers)
|
||||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||||
|
|
||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields}
|
<ReusableSearchParam {searchFields}
|
||||||
bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}
|
bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}
|
||||||
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
selectOptions={search.selectOptions} loadingOptions={search.loadingOptions} fetchOptions={search.fetchOptions}
|
||||||
/>
|
/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
@ -38,10 +41,13 @@
|
|||||||
>
|
>
|
||||||
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
<div class={`flex w-full ${props.masterDetail.isFormMode ? "flex-col justify-center h-full items-center" : "flex-col justify-start h-full"}`} >
|
||||||
{#if props.masterDetail.isFormMode}
|
{#if props.masterDetail.isFormMode}
|
||||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||||
{#each "WORKSTATION".split("") as c}
|
<MoveLeftIcon />
|
||||||
<span class="leading-none">{c}</span>
|
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||||
{/each}
|
{#each "WORKSTATION".split("") as c}
|
||||||
|
<span class="leading-none">{c}</span>
|
||||||
|
{/each}
|
||||||
|
</div>
|
||||||
</span>
|
</span>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
import MonitorXIcon from "@lucide/svelte/icons/monitor-x";
|
import MonitorXIcon from "@lucide/svelte/icons/monitor-x";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -46,7 +47,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper
|
<TopbarWrapper
|
||||||
title={masterDetail.selectedItem.data.WorkstationName}
|
title={masterDetail.selectedItem.data.WorkstationName}
|
||||||
|
|||||||
@ -21,8 +21,8 @@
|
|||||||
let activeRowId = $state(null);
|
let activeRowId = $state(null);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#snippet searchParamSnippet()}
|
{#snippet searchParamSnippet(close)}
|
||||||
<ReusableSearchParam {searchFields} bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}/>
|
<ReusableSearchParam {searchFields} bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}/>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
|
|||||||
@ -4,6 +4,7 @@
|
|||||||
import { detailSections, viewActions } from "$lib/components/patient/list/config/patient-config";
|
import { detailSections, viewActions } from "$lib/components/patient/list/config/patient-config";
|
||||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||||
|
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||||
|
|
||||||
let props = $props();
|
let props = $props();
|
||||||
|
|
||||||
@ -95,7 +96,11 @@
|
|||||||
</div>
|
</div>
|
||||||
{/snippet}
|
{/snippet}
|
||||||
|
|
||||||
{#if masterDetail.selectedItem}
|
{#if masterDetail.isLoadingDetail}
|
||||||
|
<div class="h-full w-full flex items-center justify-center">
|
||||||
|
<Spinner class="size-6" />
|
||||||
|
</div>
|
||||||
|
{:else if masterDetail.selectedItem}
|
||||||
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
<div class="flex flex-col px-2 py-1 gap-2 h-full w-full">
|
||||||
<TopbarWrapper title={fullName} subtitle={prefix} {actions} />
|
<TopbarWrapper title={fullName} subtitle={prefix} {actions} />
|
||||||
<div class="flex-1 min-h-0 overflow-y-auto space-y-4">
|
<div class="flex-1 min-h-0 overflow-y-auto space-y-4">
|
||||||
|
|||||||
@ -30,7 +30,6 @@
|
|||||||
{/snippet}
|
{/snippet}
|
||||||
</Popover.Trigger>
|
</Popover.Trigger>
|
||||||
<Popover.Content collisionPadding={props.collisionPadding ?? 0} class={props.popoverWidth ?? "w-72"}>
|
<Popover.Content collisionPadding={props.collisionPadding ?? 0} class={props.popoverWidth ?? "w-72"}>
|
||||||
<!-- {@render props.popoverContent()} -->
|
|
||||||
{@render props.popoverContent(() => open = false)}
|
{@render props.popoverContent(() => open = false)}
|
||||||
</Popover.Content>
|
</Popover.Content>
|
||||||
</Popover.Root>
|
</Popover.Root>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user