mirror of
https://github.com/faiztyanirh/clqms-shadcn-v1.git
synced 2026-04-22 09:35:34 +07:00
refactor create contact ui
This commit is contained in:
parent
45a6f116cc
commit
ec14173256
@ -1,10 +1,21 @@
|
||||
import { API } from "$lib/config/api";
|
||||
import { Contact } from "@lucide/svelte";
|
||||
import EraserIcon from "@lucide/svelte/icons/eraser";
|
||||
import { z } from "zod";
|
||||
import { cleanEmptyStrings } from "$lib/utils/cleanEmptyStrings";
|
||||
|
||||
export const contactSchema = z.object({
|
||||
NameFirst: z.string().min(1, "Required"),
|
||||
Initial: z.string().min(1, "Required"),
|
||||
EmailAddress1: z.string().trim().optional().refine((val) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),"Invalid email format"),
|
||||
EmailAddress2: z.string().trim().optional().refine((val) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),"Invalid email format"),
|
||||
MobilePhone1: z.string().max(14, "Max 14 chars").regex(/^$|^[0-9]+$/, "Can only contain numbers"),
|
||||
MobilePhone2: z.string().max(14, "Max 14 chars").regex(/^$|^[0-9]+$/, "Can only contain numbers"),
|
||||
Phone: z.string().max(14, "Max 14 chars").regex(/^$|^[0-9]+$/, "Can only contain numbers"),
|
||||
});
|
||||
|
||||
export const contactDetailSchema = z.object({
|
||||
ContactEmail: z.string().trim().optional().refine((val) => !val || /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(val),"Invalid email format"),
|
||||
});
|
||||
|
||||
export const contactInitialForm = {
|
||||
@ -24,8 +35,8 @@ export const contactInitialForm = {
|
||||
};
|
||||
|
||||
export const contactDetailInitialForm = {
|
||||
SiteID: '',
|
||||
ContactDetID: '',
|
||||
SiteID: '',
|
||||
ContactCode: '',
|
||||
ContactEmail: '',
|
||||
OccupationID: '',
|
||||
@ -36,6 +47,15 @@ export const contactDetailInitialForm = {
|
||||
export const contactDefaultErrors = {
|
||||
NameFirst: "Required",
|
||||
Initial: "Required",
|
||||
EmailAddress1: null,
|
||||
EmailAddress2: null,
|
||||
MobilePhone1: null,
|
||||
MobilePhone2: null,
|
||||
Phone: null,
|
||||
};
|
||||
|
||||
export const contactDetailDefaultErrors = {
|
||||
ContactEmail: null,
|
||||
};
|
||||
|
||||
export const contactFormFields = [
|
||||
@ -102,13 +122,15 @@ export const contactFormFields = [
|
||||
key: "EmailAddress1",
|
||||
label: "Email Address 1",
|
||||
required: false,
|
||||
type: "text",
|
||||
type: "email",
|
||||
validateOn: ["input"],
|
||||
},
|
||||
{
|
||||
key: "EmailAddress2",
|
||||
label: "Email Address 2",
|
||||
required: false,
|
||||
type: "text",
|
||||
type: "email",
|
||||
validateOn: ["input"],
|
||||
},
|
||||
]
|
||||
},
|
||||
@ -119,19 +141,22 @@ export const contactFormFields = [
|
||||
key: "MobilePhone1",
|
||||
label: "Mobile Phone 1",
|
||||
required: false,
|
||||
type: "text",
|
||||
type: "text",
|
||||
validateOn: ["input"],
|
||||
},
|
||||
{
|
||||
key: "MobilePhone2",
|
||||
label: "Mobile Phone 2",
|
||||
required: false,
|
||||
type: "text",
|
||||
type: "text",
|
||||
validateOn: ["input"],
|
||||
},
|
||||
{
|
||||
key: "Phone",
|
||||
label: "Phone",
|
||||
required: false,
|
||||
type: "text",
|
||||
validateOn: ["input"],
|
||||
},
|
||||
]
|
||||
},
|
||||
@ -163,6 +188,7 @@ export const contactFormFields = [
|
||||
|
||||
export const contactDetailFormFields = [
|
||||
{
|
||||
title: "Contact Detail Information",
|
||||
rows: [
|
||||
{
|
||||
type: "row",
|
||||
@ -221,4 +247,23 @@ export function getContactFormActions(handlers) {
|
||||
onClick: handlers.clearForm,
|
||||
},
|
||||
];
|
||||
}
|
||||
|
||||
export function buildContactPayload({
|
||||
mainForm,
|
||||
tempDetailContact,
|
||||
}) {
|
||||
let payload = {
|
||||
...mainForm,
|
||||
Details: tempDetailContact.map((item) => ({
|
||||
SiteID: item.SiteID,
|
||||
ContactCode: item.ContactCode,
|
||||
ContactEmail: item.ContactEmail,
|
||||
Department: item.Department,
|
||||
OccupationID: item.OccupationID,
|
||||
JobTitle: item.JobTitle,
|
||||
})),
|
||||
};
|
||||
|
||||
return cleanEmptyStrings(payload);
|
||||
}
|
||||
@ -4,6 +4,14 @@
|
||||
import DictionaryFormRenderer from "$lib/components/reusable/form/dictionary-form-renderer.svelte";
|
||||
import { toast } from "svelte-sonner";
|
||||
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
||||
import { useForm } from "$lib/components/composable/use-form.svelte";
|
||||
import { contactDetailSchema, contactDetailInitialForm, contactDetailDefaultErrors, contactDetailFormFields, buildContactPayload } from "$lib/components/dictionary/contact/config/contact-form-config";
|
||||
import { Separator } from '$lib/components/ui/separator/index.js';
|
||||
import { Button } from '$lib/components/ui/button/index.js';
|
||||
import * as Table from '$lib/components/ui/table/index.js';
|
||||
import PencilIcon from "@lucide/svelte/icons/pencil";
|
||||
import Trash2Icon from "@lucide/svelte/icons/trash-2";
|
||||
import { untrack } from "svelte";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -11,11 +19,23 @@
|
||||
|
||||
const { formState } = masterDetail;
|
||||
|
||||
let editingId = $state(null);
|
||||
let idCounter = $state(0);
|
||||
let tempDetailContact = $state([]);
|
||||
|
||||
const contactDetailFormState = useForm({
|
||||
schema: contactDetailSchema,
|
||||
initialForm: contactDetailInitialForm,
|
||||
defaultErrors: contactDetailDefaultErrors,
|
||||
});
|
||||
|
||||
const helpers = useDictionaryForm(formState);
|
||||
|
||||
const handlers = {
|
||||
clearForm: () => {
|
||||
formState.reset();
|
||||
contactDetailFormState.reset();
|
||||
tempDetailContact = [];
|
||||
}
|
||||
};
|
||||
|
||||
@ -23,11 +43,80 @@
|
||||
|
||||
let showConfirm = $state(false);
|
||||
|
||||
async function handleSave() {
|
||||
const result = await formState.save(masterDetail.mode);
|
||||
function snapshotForm() {
|
||||
return untrack(() => {
|
||||
const f = contactDetailFormState.form;
|
||||
return {
|
||||
SiteID: f.SiteID ?? "",
|
||||
ContactCode: f.ContactCode ?? "",
|
||||
ContactEmail: f.ContactEmail ?? "",
|
||||
Department: f.Department ?? "",
|
||||
OccupationID: f.OccupationID ?? "",
|
||||
JobTitle: f.JobTitle ?? "",
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
toast('Contact Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
function resetContactDetailForm() {
|
||||
contactDetailFormState.reset();
|
||||
editingId = null;
|
||||
}
|
||||
|
||||
function handleInsertDetail() {
|
||||
const row = {
|
||||
id: ++idCounter,
|
||||
...snapshotForm()
|
||||
};
|
||||
|
||||
tempDetailContact = [...tempDetailContact, row];
|
||||
|
||||
resetContactDetailForm();
|
||||
}
|
||||
|
||||
async function handleEditDetail(row) {
|
||||
editingId = row.id;
|
||||
|
||||
untrack(() => {
|
||||
const f = contactDetailFormState.form;
|
||||
|
||||
f.SiteID = row.SiteID;
|
||||
f.ContactCode = row.ContactCode;
|
||||
f.ContactEmail = row.ContactEmail;
|
||||
f.Department = row.Department;
|
||||
f.OccupationID = row.OccupationID;
|
||||
f.JobTitle = row.JobTitle;
|
||||
});
|
||||
}
|
||||
|
||||
function handleUpdateDetail() {
|
||||
tempDetailContact = tempDetailContact.map((row) =>
|
||||
row.id === editingId ? { id: row.id, ...snapshotForm() } : row
|
||||
);
|
||||
resetContactDetailForm();
|
||||
}
|
||||
|
||||
function handleCancelEditDetail() {
|
||||
resetContactDetailForm();
|
||||
}
|
||||
|
||||
function handleRemoveDetail(id) {
|
||||
tempDetailContact = tempDetailContact.filter((row) => row.id !== id);
|
||||
if (editingId === id) {
|
||||
resetContactDetailForm();
|
||||
}
|
||||
}
|
||||
|
||||
async function handleSave() {
|
||||
const mainForm = masterDetail.formState.form;
|
||||
const payload = buildContactPayload({
|
||||
mainForm,
|
||||
tempDetailContact,
|
||||
});
|
||||
console.log(payload)
|
||||
// const result = await formState.save(masterDetail.mode);
|
||||
|
||||
// toast('Contact Created!');
|
||||
// masterDetail?.exitForm(true);
|
||||
}
|
||||
|
||||
const primaryAction = $derived({
|
||||
@ -46,6 +135,83 @@
|
||||
formFields={formFields}
|
||||
mode="create"
|
||||
/>
|
||||
|
||||
<Separator class="my-4"/>
|
||||
|
||||
<div>
|
||||
<DictionaryFormRenderer
|
||||
formState={contactDetailFormState}
|
||||
formFields={contactDetailFormFields}
|
||||
mode="create"
|
||||
/>
|
||||
<div class="flex gap-2 mt-1 ms-2">
|
||||
{#if editingId !== null}
|
||||
<Button size="sm" class="cursor-pointer" onclick={handleUpdateDetail}>Update</Button>
|
||||
<Button size="sm" variant="outline" class="cursor-pointer" onclick={handleCancelEditDetail}>
|
||||
Cancel
|
||||
</Button>
|
||||
{:else}
|
||||
<Button size="sm" class="cursor-pointer" onclick={handleInsertDetail}>Insert</Button>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="mt-4">
|
||||
<Separator />
|
||||
<Table.Root>
|
||||
<Table.Header>
|
||||
<Table.Row class="hover:bg-transparent">
|
||||
<Table.Head>Site</Table.Head>
|
||||
<Table.Head>Code</Table.Head>
|
||||
<Table.Head>Department</Table.Head>
|
||||
<Table.Head>Occupation</Table.Head>
|
||||
<Table.Head>Job Title</Table.Head>
|
||||
<Table.Head>Email</Table.Head>
|
||||
<Table.Head class="w-[80px]"></Table.Head>
|
||||
</Table.Row>
|
||||
</Table.Header>
|
||||
<Table.Body>
|
||||
{#if tempDetailContact.length === 0}
|
||||
<Table.Row>
|
||||
<Table.Cell colspan={9} class="text-center text-muted-foreground py-6">
|
||||
No data. Fill the form above and click Insert.
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{:else}
|
||||
{#each tempDetailContact as row (row.id)}
|
||||
<Table.Row>
|
||||
<Table.Cell>{row.SiteID}</Table.Cell>
|
||||
<Table.Cell>{row.ContactCode}</Table.Cell>
|
||||
<Table.Cell>{row.Department}</Table.Cell>
|
||||
<Table.Cell>{row.OccupationID}</Table.Cell>
|
||||
<Table.Cell>{row.JobTitle}</Table.Cell>
|
||||
<Table.Cell>{row.ContactEmail}</Table.Cell>
|
||||
<Table.Cell class="w-[80px]">
|
||||
<div class="flex gap-1">
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="h-7 w-7 cursor-pointer"
|
||||
onclick={() => handleEditDetail(row)}
|
||||
>
|
||||
<PencilIcon class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost"
|
||||
class="h-7 w-7 cursor-pointer"
|
||||
onclick={() => handleRemoveDetail(row.id)}
|
||||
>
|
||||
<Trash2Icon class="h-3.5 w-3.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</Table.Cell>
|
||||
</Table.Row>
|
||||
{/each}
|
||||
{/if}
|
||||
</Table.Body>
|
||||
</Table.Root>
|
||||
</div>
|
||||
</FormPageContainer>
|
||||
|
||||
<ReusableAlertDialog
|
||||
|
||||
309
src/lib/components/dictionary/contact/page/edit-page copy.svelte
Normal file
309
src/lib/components/dictionary/contact/page/edit-page copy.svelte
Normal file
@ -0,0 +1,309 @@
|
||||
<script>
|
||||
import { useDictionaryForm } from "$lib/components/composable/use-dictionary-form.svelte";
|
||||
import FormPageContainer from "$lib/components/reusable/form/form-page-container.svelte";
|
||||
import DictionaryFormRenderer from "$lib/components/reusable/form/dictionary-form-renderer.svelte";
|
||||
import { toast } from "svelte-sonner";
|
||||
import { untrack } from "svelte";
|
||||
import { API } from "$lib/config/api";
|
||||
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
||||
import { Separator } from "$lib/components/ui/separator/index.js";
|
||||
import PlusIcon from "@lucide/svelte/icons/plus";
|
||||
import * as Card from "$lib/components/ui/card/index.js";
|
||||
import { Badge } from "$lib/components/ui/badge/index.js";
|
||||
import TopbarWrapper from "$lib/components/topbar/topbar-wrapper.svelte";
|
||||
import { contactDetailInitialForm, contactDetailFormFields } from "$lib/components/dictionary/contact/config/contact-form-config";
|
||||
import { Button } from "$lib/components/ui/button/index.js";
|
||||
import { useForm } from "$lib/components/composable/use-form.svelte";
|
||||
import XIcon from "@lucide/svelte/icons/x";
|
||||
import Edit2Icon from "@lucide/svelte/icons/edit-2";
|
||||
import { getChangedFields } from "$lib/utils/getChangedFields";
|
||||
|
||||
let props = $props();
|
||||
|
||||
const { masterDetail, formFields, formActions, schema, initialForm } = props.context;
|
||||
|
||||
const { formState } = masterDetail;
|
||||
|
||||
const detailFormState = useForm({
|
||||
schema: null,
|
||||
initialForm: contactDetailInitialForm,
|
||||
defaultErrors: {},
|
||||
mode: 'create',
|
||||
modeOpt: 'default',
|
||||
saveEndpoint: null,
|
||||
editEndpoint: null,
|
||||
});
|
||||
|
||||
let showDetailForm = $state(false);
|
||||
|
||||
let editingDetailIndex = $state(null);
|
||||
|
||||
let isEditingDetail = $derived(editingDetailIndex !== null);
|
||||
|
||||
const helpers = useDictionaryForm(formState);
|
||||
|
||||
let showConfirm = $state(false);
|
||||
|
||||
function getLabel(fieldKey, value) {
|
||||
if (!detailFormState.selectOptions?.[fieldKey]) return value;
|
||||
const option = detailFormState.selectOptions[fieldKey].find(opt => opt.value === value);
|
||||
return option?.label || value || "-";
|
||||
}
|
||||
|
||||
$effect(() => {
|
||||
untrack(() => {
|
||||
formFields.forEach(group => {
|
||||
group.rows.forEach(row => {
|
||||
row.columns.forEach(col => {
|
||||
if (col.type === "group") {
|
||||
col.columns.forEach(child => {
|
||||
if (child.type === "select" && child.optionsEndpoint) {
|
||||
formState.fetchOptions(child, formState.form);
|
||||
}
|
||||
});
|
||||
} else if ((col.type === "select") && col.optionsEndpoint) {
|
||||
formState.fetchOptions(col, formState.form);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
$effect(() => {
|
||||
untrack(() => {
|
||||
contactDetailFormFields.forEach(group => {
|
||||
group.rows.forEach(row => {
|
||||
row.columns.forEach(col => {
|
||||
if (col.type === "select" && col.optionsEndpoint) {
|
||||
detailFormState.fetchOptions(col, detailFormState.form);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
});
|
||||
let editedDetails = $state(masterDetail.selectedItem?.data?.Details || []);
|
||||
|
||||
async function handleEdit() {
|
||||
const originalDetails = masterDetail.selectedItem?.data?.Details || [];
|
||||
|
||||
const currentPayload = $state.snapshot({ ...formState.form, Details: editedDetails });
|
||||
const originalPayload = $state.snapshot({ ...masterDetail.formSnapshot, Details: originalDetails });
|
||||
console.log('Current Payload:', editedDetails);
|
||||
console.log('Original Payload:', originalDetails);
|
||||
|
||||
// const customPayload = {
|
||||
// ...formState.form,
|
||||
// Details: masterDetail.selectedItem?.data?.Details || []
|
||||
// };
|
||||
// console.log('Custom Payload for Edit:', JSON.stringify(customPayload));
|
||||
// const result = await formState.save(masterDetail.mode, customPayload);
|
||||
|
||||
// ***
|
||||
const changedFields = getChangedFields(originalPayload, currentPayload);
|
||||
console.log('Changed Fields:', JSON.stringify(changedFields));
|
||||
|
||||
// if (Object.keys(changedFields).length === 0) {
|
||||
// toast('No changes detected');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const payload = {
|
||||
// ContactID: formState.form.ContactID,
|
||||
// ...changedFields
|
||||
// };
|
||||
|
||||
// console.log('Custom Payload for Edit:', payload);
|
||||
// ***
|
||||
|
||||
// const result = await formState.save(masterDetail.mode, payload);
|
||||
|
||||
// if (result.status === 'success') {
|
||||
// console.log('Contact updated successfully');
|
||||
// toast('Contact Updated!');
|
||||
// masterDetail.exitForm(true);
|
||||
// } else {
|
||||
// console.error('Failed to update contact:', result.message);
|
||||
// }
|
||||
}
|
||||
|
||||
const primaryAction = $derived({
|
||||
label: 'Edit',
|
||||
onClick: handleEdit,
|
||||
disabled: helpers.hasErrors || formState.isSaving.current,
|
||||
loading: formState.isSaving.current
|
||||
});
|
||||
|
||||
const secondaryActions = [];
|
||||
|
||||
const actionsDetail = [
|
||||
{
|
||||
Icon: PlusIcon,
|
||||
label: 'Add Contact Detail',
|
||||
onClick: () => addDetail(),
|
||||
},
|
||||
];
|
||||
|
||||
function addDetail() {
|
||||
editingDetailIndex = null; // Mode create baru
|
||||
detailFormState.reset(); // Reset form ke initialForm
|
||||
detailFormState.setForm({ ...contactDetailInitialForm }); // Set form kosong
|
||||
showDetailForm = true;
|
||||
}
|
||||
|
||||
async function saveDetail() {
|
||||
// Ambil current form dari detailFormState.form
|
||||
const newDetail = { ...detailFormState.form };
|
||||
|
||||
if (isEditingDetail) {
|
||||
// Mode edit: update detail yang ada
|
||||
masterDetail.selectedItem.data.Details[editingDetailIndex] = newDetail;
|
||||
toast('Contact Detail Updated!');
|
||||
} else {
|
||||
// Mode create: tambah detail baru
|
||||
if (!masterDetail.selectedItem.data.Details) {
|
||||
masterDetail.selectedItem.data.Details = [];
|
||||
}
|
||||
masterDetail.selectedItem.data.Details.push(newDetail);
|
||||
toast('Contact Detail Added!');
|
||||
}
|
||||
|
||||
// Reset form dan tutup form
|
||||
detailFormState.reset();
|
||||
detailFormState.setForm({ ...contactDetailInitialForm });
|
||||
editingDetailIndex = null;
|
||||
showDetailForm = false;
|
||||
}
|
||||
|
||||
function editDetail(index) {
|
||||
const detailToEdit = masterDetail.selectedItem.data.Details[index];
|
||||
editingDetailIndex = index; // Set mode edit dengan index
|
||||
detailFormState.setForm({ ...detailToEdit }); // Load data ke form
|
||||
showDetailForm = true;
|
||||
}
|
||||
|
||||
function cancelDetail() {
|
||||
detailFormState.reset();
|
||||
detailFormState.setForm({ ...contactDetailInitialForm });
|
||||
editingDetailIndex = null;
|
||||
showDetailForm = false;
|
||||
}
|
||||
|
||||
function removeDetail(index) {
|
||||
masterDetail.selectedItem.data.Details = masterDetail.selectedItem.data.Details.filter((_, i) => i !== index);
|
||||
toast('Contact Detail Removed!');
|
||||
|
||||
// Jika sedang mengedit detail yang dihapus, reset form
|
||||
if (editingDetailIndex === index) {
|
||||
cancelDetail();
|
||||
} else if (editingDetailIndex !== null && editingDetailIndex > index) {
|
||||
// Adjust index jika mengedit detail setelah yang dihapus
|
||||
editingDetailIndex--;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<FormPageContainer title="Edit Contact" {primaryAction} {secondaryActions}>
|
||||
<DictionaryFormRenderer
|
||||
{formState}
|
||||
formFields={formFields}
|
||||
mode="edit"
|
||||
/>
|
||||
<Separator class="my-4"/>
|
||||
<div class="flex flex-col px-2 py-1 gap-2 h-fit w-full">
|
||||
<TopbarWrapper
|
||||
title="Contact Detail"
|
||||
actions={actionsDetail}
|
||||
/>
|
||||
<div class="flex flex-col gap-4">
|
||||
{#if showDetailForm}
|
||||
<Card.Root class="w-full gap-2 2xl:gap-4 py-2 2xl:py-4">
|
||||
<Card.Content class="space-y-3">
|
||||
<DictionaryFormRenderer
|
||||
formState={detailFormState}
|
||||
formFields={contactDetailFormFields}
|
||||
/>
|
||||
</Card.Content>
|
||||
<Card.Footer class="flex justify-end flex-end gap-2">
|
||||
<Button size="sm" onclick={saveDetail}>
|
||||
{isEditingDetail ? 'Update Detail' : 'Save Detail'}
|
||||
</Button>
|
||||
<Button size="sm" variant="outline" onclick={cancelDetail}>Cancel</Button>
|
||||
</Card.Footer>
|
||||
</Card.Root>
|
||||
{/if}
|
||||
{#each masterDetail.selectedItem?.data?.Details as contactdetail, index}
|
||||
<Card.Root class="w-full gap-2 2xl:gap-4 py-2 2xl:py-4">
|
||||
<Card.Header>
|
||||
<div class="flex items-start justify-between">
|
||||
<div class="space-y-1">
|
||||
<Card.Title class="text-sm font-medium">
|
||||
{contactdetail.ContactCode || "null"}
|
||||
</Card.Title>
|
||||
<Card.Description class="text-sm font-medium">
|
||||
{contactdetail.ContactEmail || "null"}
|
||||
</Card.Description>
|
||||
</div>
|
||||
<div class="flex items-center gap-4">
|
||||
<Badge variant="outline" class="text-xs">
|
||||
{getLabel('SiteID', contactdetail.SiteID)}
|
||||
</Badge>
|
||||
<div>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost" class="size-7"
|
||||
onclick={() => editDetail(index)}
|
||||
>
|
||||
<Edit2Icon class="h-4 w-4" />
|
||||
</Button>
|
||||
<Button
|
||||
size="icon"
|
||||
variant="ghost" class="size-7"
|
||||
onclick={() => removeDetail(index)}
|
||||
>
|
||||
<XIcon class="h-4 w-4" />
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Header>
|
||||
<Card.Content class="space-y-3">
|
||||
<div class="grid grid-cols-3 gap-3">
|
||||
<div class="space-y-1">
|
||||
<p class="text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Department
|
||||
</p>
|
||||
<p class="text-sm font-medium">
|
||||
{getLabel('Department', contactdetail.Department)}
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<p class="text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Job Title
|
||||
</p>
|
||||
<p class="text-sm font-medium">
|
||||
{contactdetail.JobTitle || "-"}
|
||||
</p>
|
||||
</div>
|
||||
<div class="space-y-1">
|
||||
<p class="text-xs font-medium text-muted-foreground uppercase tracking-wider">
|
||||
Occupation
|
||||
</p>
|
||||
<p class="text-sm font-medium">
|
||||
<!-- {contactdetail.OccupationID || "-"} -->
|
||||
{getLabel('OccupationID', contactdetail.OccupationID)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
</Card.Content>
|
||||
</Card.Root>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
</FormPageContainer>
|
||||
|
||||
<ReusableAlertDialog
|
||||
bind:open={masterDetail.showExitConfirm}
|
||||
onConfirm={masterDetail.confirmExit}
|
||||
/>
|
||||
@ -16,6 +16,7 @@
|
||||
import { useForm } from "$lib/components/composable/use-form.svelte";
|
||||
import XIcon from "@lucide/svelte/icons/x";
|
||||
import Edit2Icon from "@lucide/svelte/icons/edit-2";
|
||||
import { getChangedFields } from "$lib/utils/getChangedFields";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -43,6 +44,10 @@
|
||||
|
||||
let showConfirm = $state(false);
|
||||
|
||||
let editingId = $state(null);
|
||||
let idCounter = $state(0);
|
||||
let tempMap = $state([]);
|
||||
|
||||
function getLabel(fieldKey, value) {
|
||||
if (!detailFormState.selectOptions?.[fieldKey]) return value;
|
||||
const option = detailFormState.selectOptions[fieldKey].find(opt => opt.value === value);
|
||||
@ -83,21 +88,49 @@
|
||||
});
|
||||
});
|
||||
|
||||
async function handleEdit() {
|
||||
const customPayload = {
|
||||
...formState.form,
|
||||
Details: masterDetail.selectedItem?.data?.Details || []
|
||||
};
|
||||
let editedDetails = $state(masterDetail.selectedItem?.data?.Details || []);
|
||||
|
||||
const result = await formState.save(masterDetail.mode, customPayload);
|
||||
async function handleEdit() {
|
||||
const originalDetails = masterDetail.selectedItem?.data?.Details || [];
|
||||
|
||||
const currentPayload = $state.snapshot({ ...formState.form, Details: editedDetails });
|
||||
const originalPayload = $state.snapshot({ ...masterDetail.formSnapshot, Details: originalDetails });
|
||||
console.log('Current Payload:', editedDetails);
|
||||
console.log('Original Payload:', originalDetails);
|
||||
|
||||
if (result.status === 'success') {
|
||||
console.log('Contact updated successfully');
|
||||
toast('Contact Updated!');
|
||||
masterDetail.exitForm(true);
|
||||
} else {
|
||||
console.error('Failed to update contact:', result.message);
|
||||
}
|
||||
// const customPayload = {
|
||||
// ...formState.form,
|
||||
// Details: masterDetail.selectedItem?.data?.Details || []
|
||||
// };
|
||||
// console.log('Custom Payload for Edit:', JSON.stringify(customPayload));
|
||||
// const result = await formState.save(masterDetail.mode, customPayload);
|
||||
|
||||
// ***
|
||||
const changedFields = getChangedFields(originalPayload, currentPayload);
|
||||
console.log('Changed Fields:', JSON.stringify(changedFields));
|
||||
|
||||
// if (Object.keys(changedFields).length === 0) {
|
||||
// toast('No changes detected');
|
||||
// return;
|
||||
// }
|
||||
|
||||
// const payload = {
|
||||
// ContactID: formState.form.ContactID,
|
||||
// ...changedFields
|
||||
// };
|
||||
|
||||
// console.log('Custom Payload for Edit:', payload);
|
||||
// ***
|
||||
|
||||
// const result = await formState.save(masterDetail.mode, payload);
|
||||
|
||||
// if (result.status === 'success') {
|
||||
// console.log('Contact updated successfully');
|
||||
// toast('Contact Updated!');
|
||||
// masterDetail.exitForm(true);
|
||||
// } else {
|
||||
// console.error('Failed to update contact:', result.message);
|
||||
// }
|
||||
}
|
||||
|
||||
const primaryAction = $derived({
|
||||
|
||||
@ -125,10 +125,10 @@
|
||||
tempMap,
|
||||
});
|
||||
console.log(payload)
|
||||
const result = await formState.save(masterDetail.mode, payload);
|
||||
// const result = await formState.save(masterDetail.mode, payload);
|
||||
|
||||
toast('Test Map Created!');
|
||||
masterDetail?.exitForm(true);
|
||||
// toast('Test Map Created!');
|
||||
// masterDetail?.exitForm(true);
|
||||
}
|
||||
|
||||
const primaryAction = $derived({
|
||||
@ -223,7 +223,6 @@
|
||||
// formState.form.ClientTestName = 'nyaho';
|
||||
// }
|
||||
// })
|
||||
$inspect(mapFormFieldsTransformed)
|
||||
</script>
|
||||
|
||||
<FormPageContainer title="Create Test Map" {primaryAction} {secondaryActions} {actions}>
|
||||
|
||||
@ -103,7 +103,7 @@ export function viewActions(handlers){
|
||||
return [
|
||||
{
|
||||
Icon: PencilIcon,
|
||||
label: 'Edit Patient',
|
||||
label: 'Edit Visit',
|
||||
onClick: handlers.editPatient,
|
||||
},
|
||||
]
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
import { API } from "$lib/config/api";
|
||||
import ReusableAlertDialog from "$lib/components/reusable/reusable-alert-dialog.svelte";
|
||||
import { buildPatientPayload } from "$lib/components/patient/list/config/patient-form-config";
|
||||
import { getChangedFields } from "$lib/utils/getChangedFields";
|
||||
|
||||
let props = $props();
|
||||
|
||||
@ -60,16 +61,6 @@
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
function getChangedFields(original, current) {
|
||||
const changed = {};
|
||||
for (const key in current) {
|
||||
if (JSON.stringify(current[key]) !== JSON.stringify(original[key])) {
|
||||
changed[key] = current[key];
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
async function handleEdit() {
|
||||
const currentPayload = buildPatientPayload(formState.form);
|
||||
|
||||
9
src/lib/utils/getChangedFields.js
Normal file
9
src/lib/utils/getChangedFields.js
Normal file
@ -0,0 +1,9 @@
|
||||
export function getChangedFields(original, current) {
|
||||
const changed = {};
|
||||
for (const key in current) {
|
||||
if (JSON.stringify(current[key]) !== JSON.stringify(original[key])) {
|
||||
changed[key] = current[key];
|
||||
}
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user