mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-22 09:35:34 +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 PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import { API } from "$lib/config/api";
|
||||
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||
|
||||
export const searchFields = [
|
||||
{
|
||||
@ -72,8 +73,13 @@ export const detailSections = [
|
||||
},
|
||||
];
|
||||
|
||||
export function accountActions(masterDetail) {
|
||||
export function accountActions(masterDetail, handlers) {
|
||||
return [
|
||||
{
|
||||
Icon: RefreshIcon,
|
||||
label: 'Refresh Data',
|
||||
onClick: handlers.refresh,
|
||||
},
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add Account',
|
||||
|
||||
@ -26,8 +26,14 @@
|
||||
async function handleSave() {
|
||||
const result = await formState.save(masterDetail.mode);
|
||||
|
||||
toast('Account Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
if (result.status === 'success') {
|
||||
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({
|
||||
|
||||
@ -8,20 +8,23 @@
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||
import UserRoundXIcon from "@lucide/svelte/icons/user-round-x";
|
||||
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||
|
||||
let props = $props();
|
||||
|
||||
const search = useSearch(searchFields, getAccounts);
|
||||
const initialForm = props.masterDetail.formState.form;
|
||||
const actions = accountActions(props.masterDetail, initialForm)
|
||||
const handlers = {
|
||||
refresh: () => {search.handleSearch()},
|
||||
};
|
||||
const actions = accountActions(props.masterDetail, handlers)
|
||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<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}
|
||||
/>
|
||||
{/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"}`} >
|
||||
{#if props.masterDetail.isFormMode}
|
||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
||||
{#each "ACCOUNT".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||
<MoveLeftIcon />
|
||||
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||
{#each "ACCOUNT".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import UserRoundXIcon from "@lucide/svelte/icons/user-round-x";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -46,7 +47,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper
|
||||
title={masterDetail.selectedItem.data.AccountName}
|
||||
|
||||
@ -22,9 +22,9 @@
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<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}
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
import * as Card from "$lib/components/ui/card/index.js";
|
||||
import { Badge } from "$lib/components/ui/badge/index.js";
|
||||
import * as Table from "$lib/components/ui/table/index.js";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -97,7 +98,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper
|
||||
title={masterDetail.selectedItem.data.NameFirst}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import PlusIcon from "@lucide/svelte/icons/plus";
|
||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||
|
||||
export const searchFields = [
|
||||
{
|
||||
@ -35,8 +36,13 @@ export const detailSections = [
|
||||
},
|
||||
];
|
||||
|
||||
export function containerActions(masterDetail) {
|
||||
export function containerActions(masterDetail, handlers) {
|
||||
return [
|
||||
{
|
||||
Icon: RefreshIcon,
|
||||
label: 'Refresh Data',
|
||||
onClick: handlers.refresh,
|
||||
},
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add Location',
|
||||
|
||||
@ -26,8 +26,14 @@
|
||||
async function handleSave() {
|
||||
const result = await formState.save(masterDetail.mode);
|
||||
|
||||
toast('Container Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
if (result.status === 'success') {
|
||||
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({
|
||||
|
||||
@ -8,20 +8,23 @@
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||
import ArchiveXIcon from "@lucide/svelte/icons/archive-x";
|
||||
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||
|
||||
let props = $props();
|
||||
|
||||
const search = useSearch(searchFields, getContainers);
|
||||
const initialForm = props.masterDetail.formState.form;
|
||||
const actions = containerActions(props.masterDetail, initialForm)
|
||||
const handlers = {
|
||||
refresh: () => {search.handleSearch()},
|
||||
};
|
||||
const actions = containerActions(props.masterDetail, handlers)
|
||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<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}
|
||||
/>
|
||||
{/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"}`} >
|
||||
{#if props.masterDetail.isFormMode}
|
||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
||||
{#each "CONTAINER".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||
<MoveLeftIcon />
|
||||
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||
{#each "CONTAINER".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import ArchiveXIcon from "@lucide/svelte/icons/archive-x";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -46,7 +47,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper
|
||||
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 PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import { API } from "$lib/config/api";
|
||||
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||
|
||||
export const searchFields = [
|
||||
{
|
||||
@ -28,8 +29,13 @@ export const detailSections = [
|
||||
},
|
||||
];
|
||||
|
||||
export function departmentActions(masterDetail) {
|
||||
export function departmentActions(masterDetail, handlers) {
|
||||
return [
|
||||
{
|
||||
Icon: RefreshIcon,
|
||||
label: 'Refresh Data',
|
||||
onClick: handlers.refresh,
|
||||
},
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add Department',
|
||||
|
||||
@ -26,8 +26,14 @@
|
||||
async function handleSave() {
|
||||
const result = await formState.save(masterDetail.mode);
|
||||
|
||||
toast('Department Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
if (result.status === 'success') {
|
||||
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({
|
||||
|
||||
@ -8,20 +8,23 @@
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||
import FileXIcon from "@lucide/svelte/icons/file-x";
|
||||
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||
|
||||
let props = $props();
|
||||
|
||||
const search = useSearch(searchFields, getDepartments);
|
||||
const initialForm = props.masterDetail.formState.form;
|
||||
const actions = departmentActions(props.masterDetail, initialForm)
|
||||
const handlers = {
|
||||
refresh: () => {search.handleSearch()},
|
||||
};
|
||||
const actions = departmentActions(props.masterDetail, handlers)
|
||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<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}
|
||||
/>
|
||||
{/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"}`} >
|
||||
{#if props.masterDetail.isFormMode}
|
||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
||||
{#each "DEPARTMENT".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||
<MoveLeftIcon />
|
||||
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||
{#each "DEPARTMENT".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import FileXIcon from "@lucide/svelte/icons/file-x";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -46,7 +47,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper
|
||||
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 PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import { API } from "$lib/config/api";
|
||||
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||
|
||||
export const searchFields = [
|
||||
{
|
||||
@ -30,8 +31,13 @@ export const detailSections = [
|
||||
},
|
||||
];
|
||||
|
||||
export function disciplineActions(masterDetail) {
|
||||
export function disciplineActions(masterDetail, handlers) {
|
||||
return [
|
||||
{
|
||||
Icon: RefreshIcon,
|
||||
label: 'Refresh Data',
|
||||
onClick: handlers.refresh,
|
||||
},
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add Discipline',
|
||||
|
||||
@ -26,8 +26,14 @@
|
||||
async function handleSave() {
|
||||
const result = await formState.save(masterDetail.mode);
|
||||
|
||||
toast('Discipline Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
if (result.status === 'success') {
|
||||
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({
|
||||
|
||||
@ -8,20 +8,23 @@
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||
import BookXIcon from "@lucide/svelte/icons/book-x";
|
||||
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||
|
||||
let props = $props();
|
||||
|
||||
const search = useSearch(searchFields, getDisciplines);
|
||||
const initialForm = props.masterDetail.formState.form;
|
||||
const actions = disciplineActions(props.masterDetail, initialForm)
|
||||
const handlers = {
|
||||
refresh: () => {search.handleSearch()},
|
||||
};
|
||||
const actions = disciplineActions(props.masterDetail, handlers)
|
||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<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}
|
||||
/>
|
||||
{/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"}`} >
|
||||
{#if props.masterDetail.isFormMode}
|
||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
||||
{#each "DISCIPLINE".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||
<MoveLeftIcon />
|
||||
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||
{#each "DISCIPLINE".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import BookXIcon from "@lucide/svelte/icons/book-x";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -46,7 +47,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper
|
||||
title={masterDetail.selectedItem.data.DisciplineName}
|
||||
|
||||
@ -22,9 +22,9 @@
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<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}
|
||||
/>
|
||||
{/snippet}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import MapPinXIcon from "@lucide/svelte/icons/map-pin-x";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -46,7 +47,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper
|
||||
title={masterDetail.selectedItem.data.LocFull}
|
||||
|
||||
@ -26,8 +26,14 @@
|
||||
async function handleSave() {
|
||||
const result = await formState.save(masterDetail.mode);
|
||||
|
||||
toast('Occupation Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
if (result.status === 'success') {
|
||||
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({
|
||||
|
||||
@ -2,6 +2,7 @@ import PlusIcon from "@lucide/svelte/icons/plus";
|
||||
import Settings2Icon from "@lucide/svelte/icons/settings-2";
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import { API } from "$lib/config/api";
|
||||
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||
|
||||
export const searchFields = [
|
||||
{
|
||||
@ -35,8 +36,13 @@ export const detailSections = [
|
||||
},
|
||||
];
|
||||
|
||||
export function siteActions(masterDetail) {
|
||||
export function siteActions(masterDetail, handlers) {
|
||||
return [
|
||||
{
|
||||
Icon: RefreshIcon,
|
||||
label: 'Refresh Data',
|
||||
onClick: handlers.refresh,
|
||||
},
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add Site',
|
||||
|
||||
@ -26,8 +26,14 @@
|
||||
async function handleSave() {
|
||||
const result = await formState.save(masterDetail.mode);
|
||||
|
||||
toast('Site Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
if (result.status === 'success') {
|
||||
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({
|
||||
|
||||
@ -8,20 +8,23 @@
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||
import ClipboardXIcon from "@lucide/svelte/icons/clipboard-x";
|
||||
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||
|
||||
let props = $props();
|
||||
|
||||
const search = useSearch(searchFields, getSites);
|
||||
const initialForm = props.masterDetail.formState.form;
|
||||
const actions = siteActions(props.masterDetail, initialForm)
|
||||
const handlers = {
|
||||
refresh: () => {search.handleSearch()},
|
||||
};
|
||||
const actions = siteActions(props.masterDetail, handlers)
|
||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<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}
|
||||
/>
|
||||
{/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"}`} >
|
||||
{#if props.masterDetail.isFormMode}
|
||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
||||
{#each "SITE".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||
<MoveLeftIcon />
|
||||
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||
{#each "SITE".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import ClipboardXIcon from "@lucide/svelte/icons/clipboard-x";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -46,7 +47,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper
|
||||
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 PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import { API } from "$lib/config/api";
|
||||
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||
|
||||
export const searchFields = [
|
||||
{
|
||||
@ -36,8 +37,13 @@ export const detailSections = [
|
||||
},
|
||||
];
|
||||
|
||||
export function testActions(masterDetail) {
|
||||
export function testActions(masterDetail, handlers) {
|
||||
return [
|
||||
{
|
||||
Icon: RefreshIcon,
|
||||
label: 'Refresh Data',
|
||||
onClick: handlers.refresh,
|
||||
},
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add Test',
|
||||
|
||||
@ -132,8 +132,14 @@
|
||||
|
||||
const result = await formState.save(masterDetail.mode, payload);
|
||||
|
||||
toast('Test Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
if (result.status === 'success') {
|
||||
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({
|
||||
|
||||
@ -125,8 +125,15 @@
|
||||
|
||||
const result = await formState.save(masterDetail.mode, payload);
|
||||
|
||||
toast('Test Updated!');
|
||||
masterDetail?.exitForm(true);
|
||||
if (result.status === 'success') {
|
||||
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({
|
||||
|
||||
@ -8,20 +8,23 @@
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||
import FlaskConicalOffIcon from "@lucide/svelte/icons/flask-conical-off";
|
||||
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||
|
||||
let props = $props();
|
||||
|
||||
const search = useSearch(searchFields, getTests);
|
||||
const initialForm = props.masterDetail.formState.form;
|
||||
const actions = testActions(props.masterDetail, initialForm)
|
||||
const handlers = {
|
||||
refresh: () => {search.handleSearch()},
|
||||
};
|
||||
const actions = testActions(props.masterDetail, handlers)
|
||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<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}
|
||||
/>
|
||||
{/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"}`} >
|
||||
{#if props.masterDetail.isFormMode}
|
||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
||||
{#each "TEST".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||
<MoveLeftIcon />
|
||||
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||
{#each "TEST".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import FlaskConicalOffIcon from "@lucide/svelte/icons/flask-conical-off";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -46,7 +47,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper
|
||||
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 PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import { API } from "$lib/config/api";
|
||||
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||
|
||||
export const searchFields = [
|
||||
{
|
||||
@ -35,8 +36,13 @@ export const detailSections = [
|
||||
},
|
||||
];
|
||||
|
||||
export function testMapActions(masterDetail) {
|
||||
export function testMapActions(masterDetail, handlers) {
|
||||
return [
|
||||
{
|
||||
Icon: RefreshIcon,
|
||||
label: 'Refresh Data',
|
||||
onClick: handlers.refresh,
|
||||
},
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add TestMap',
|
||||
|
||||
@ -115,8 +115,14 @@
|
||||
console.log(payload)
|
||||
const result = await formState.save(masterDetail.mode, payload);
|
||||
|
||||
toast('Test Map Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
if (result.status === 'success') {
|
||||
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({
|
||||
|
||||
@ -13,16 +13,18 @@
|
||||
let props = $props();
|
||||
|
||||
const search = useSearch(searchFields, getTestMaps);
|
||||
const initialForm = props.masterDetail.formState.form;
|
||||
const actions = testMapActions(props.masterDetail, initialForm)
|
||||
const handlers = {
|
||||
refresh: () => {search.handleSearch()},
|
||||
};
|
||||
const actions = testMapActions(props.masterDetail, handlers)
|
||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<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}
|
||||
/>
|
||||
{/snippet}
|
||||
@ -42,7 +44,7 @@
|
||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||
<MoveLeftIcon />
|
||||
<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>
|
||||
{/each}
|
||||
</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 MapIcon from "@lucide/svelte/icons/map";
|
||||
import * as Table from "$lib/components/ui/table/index.js";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -85,7 +86,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper
|
||||
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 PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import { API } from "$lib/config/api";
|
||||
import RefreshIcon from "@lucide/svelte/icons/refresh-cw";
|
||||
|
||||
export const searchFields = [
|
||||
{
|
||||
@ -30,8 +31,13 @@ export const detailSections = [
|
||||
},
|
||||
];
|
||||
|
||||
export function workstationActions(masterDetail) {
|
||||
export function workstationActions(masterDetail, handlers) {
|
||||
return [
|
||||
{
|
||||
Icon: RefreshIcon,
|
||||
label: 'Refresh Data',
|
||||
onClick: handlers.refresh,
|
||||
},
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add Workstation',
|
||||
|
||||
@ -26,8 +26,14 @@
|
||||
async function handleSave() {
|
||||
const result = await formState.save(masterDetail.mode);
|
||||
|
||||
toast('Workstation Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
if (result.status === 'success') {
|
||||
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({
|
||||
|
||||
@ -8,20 +8,23 @@
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import ReusableDataTable from "$lib/components/reusable/reusable-data-table.svelte";
|
||||
import MonitorXIcon from "@lucide/svelte/icons/monitor-x";
|
||||
import MoveLeftIcon from "@lucide/svelte/icons/move-left";
|
||||
|
||||
let props = $props();
|
||||
|
||||
const search = useSearch(searchFields, getWorkstations);
|
||||
const initialForm = props.masterDetail.formState.form;
|
||||
const actions = workstationActions(props.masterDetail, initialForm)
|
||||
const handlers = {
|
||||
refresh: () => {search.handleSearch()},
|
||||
};
|
||||
const actions = workstationActions(props.masterDetail, handlers)
|
||||
actions.find(a => a.label === 'Search Parameters').popoverContent = searchParamSnippet;
|
||||
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<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}
|
||||
/>
|
||||
{/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"}`} >
|
||||
{#if props.masterDetail.isFormMode}
|
||||
<span class="flex flex-col items-center justify-center gap-4 tracking-widest font-semibold select-none">
|
||||
{#each "WORKSTATION".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
<span class="flex flex-col items-center justify-start gap-4 tracking-widest font-semibold select-none h-full">
|
||||
<MoveLeftIcon />
|
||||
<div class="flex flex-col items-center justify-center flex-grow gap-4">
|
||||
{#each "WORKSTATION".split("") as c}
|
||||
<span class="leading-none">{c}</span>
|
||||
{/each}
|
||||
</div>
|
||||
</span>
|
||||
{/if}
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import MonitorXIcon from "@lucide/svelte/icons/monitor-x";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -46,7 +47,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper
|
||||
title={masterDetail.selectedItem.data.WorkstationName}
|
||||
|
||||
@ -21,8 +21,8 @@
|
||||
let activeRowId = $state(null);
|
||||
</script>
|
||||
|
||||
{#snippet searchParamSnippet()}
|
||||
<ReusableSearchParam {searchFields} bind:searchQuery={search.searchQuery} onSearch={search.handleSearch} onReset={search.handleReset} isLoading={search.isLoading}/>
|
||||
{#snippet searchParamSnippet(close)}
|
||||
<ReusableSearchParam {searchFields} bind:searchQuery={search.searchQuery} onSearch={() => search.handleSearch(close)} onReset={search.handleReset} isLoading={search.isLoading}/>
|
||||
{/snippet}
|
||||
|
||||
<div
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
import { detailSections, viewActions } from "$lib/components/patient/list/config/patient-config";
|
||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||
import ReusableEmpty from "$lib/components/reusable/reusable-empty.svelte";
|
||||
import { Spinner } from "$lib/components/ui/spinner/index.js";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -95,7 +96,11 @@
|
||||
</div>
|
||||
{/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">
|
||||
<TopbarWrapper title={fullName} subtitle={prefix} {actions} />
|
||||
<div class="flex-1 min-h-0 overflow-y-auto space-y-4">
|
||||
|
||||
@ -30,7 +30,6 @@
|
||||
{/snippet}
|
||||
</Popover.Trigger>
|
||||
<Popover.Content collisionPadding={props.collisionPadding ?? 0} class={props.popoverWidth ?? "w-72"}>
|
||||
<!-- {@render props.popoverContent()} -->
|
||||
{@render props.popoverContent(() => open = false)}
|
||||
</Popover.Content>
|
||||
</Popover.Root>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user