Skip to content

Commit

Permalink
fix: proper association of newly created foreign object inside an upd…
Browse files Browse the repository at this point in the history
…ate view (#1390)

* Allow passing superForm as prop

* Return created control's id along with form on createAppliedControl form action

* Fix external value assignment in AutocompleteSelect prop

This has proven to be a MAJOR headache. Looking forward to the svelte 5
migration.

* Push newly created applied control to $formStore.applied_controls

* Return written object in defaultWriteFormAction function

* Push newly created evidence to $formStore.evidences
  • Loading branch information
nas-tabchiche authored Jan 24, 2025
1 parent 6b0fb66 commit 3504e24
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 42 deletions.
82 changes: 46 additions & 36 deletions frontend/src/lib/components/Forms/AutocompleteSelect.svelte
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<script lang="ts">
import { formFieldProxy } from 'sveltekit-superforms';
import type { CacheLock } from '$lib/utils/types';
import { onMount } from 'svelte';
import { safeTranslate } from '$lib/utils/i18n';
import type { CacheLock } from '$lib/utils/types';
import { beforeUpdate, onMount } from 'svelte';
import { formFieldProxy } from 'sveltekit-superforms';
interface Option {
label: string;
Expand All @@ -22,6 +22,7 @@
export let hidden = false;
export let translateOptions = true;
export let options: Option[] = [];
export let allowUserOptions: boolean | 'append' = false;
export let cacheLock: CacheLock = {
Expand All @@ -32,18 +33,24 @@
const { value, errors, constraints } = formFieldProxy(form, field);
export let options: Option[] = [];
$: optionHashmap = options.reduce((acc, option) => {
acc[option.value] = option;
return acc;
}, {});
import MultiSelect from 'svelte-multiselect';
import { createEventDispatcher } from 'svelte';
import MultiSelect from 'svelte-multiselect';
let selected: typeof options = options.length === 1 && $constraints?.required ? [options[0]] : [];
let selectedValues: (string | undefined)[] = [];
let isInternalUpdate = false;
const default_value = nullable ? null : selectedValues[0];
$: cachedValue = selected.map((option) => option.value);
const multiSelectOptions = {
minSelect: $constraints && $constraints.required === true ? 1 : 0,
maxSelect: multiple ? undefined : 1,
liSelectedClass: multiple ? '!chip !variant-filled' : '!bg-transparent',
inputClass: 'focus:!ring-0 focus:!outline-none',
outerDivClass: '!select',
closeDropdownOnSelect: !multiple
};
const dispatch = createEventDispatcher();
onMount(async () => {
const cacheResult = await cacheLock.promise;
Expand All @@ -52,15 +59,19 @@
}
});
if ($value) {
selected = options.filter((item) => $value.includes(item.value));
}
let selectedValues: (string | undefined)[] = [];
$: selectedValues = selected.map((item) => item.value || item.label || item);
// Handle external updates to $value
beforeUpdate(() => {
if (!isInternalUpdate && $value) {
selected = options.filter((item) =>
Array.isArray($value) ? $value.includes(item.value) : item.value === $value
);
}
});
const default_value = nullable ? null : selectedValues[0];
function handleSelectChange() {
dispatch('change', $value);
dispatch('cache', selected);
}
function arraysEqual(arr1: (string | undefined)[], arr2: (string | undefined)[]) {
if (arr1?.length !== arr2?.length) return false;
Expand All @@ -75,30 +86,29 @@
return true;
}
if ($value) {
selected = options.filter((item) => $value.includes(item.value));
}
$: optionHashmap = options.reduce((acc, option) => {
acc[option.value] = option;
return acc;
}, {});
$: cachedValue = selected.map((option) => option.value);
$: selectedValues = selected.map((item) => item.value || item.label || item);
$: {
if (!arraysEqual(selectedValues, $value)) {
if (!isInternalUpdate && !arraysEqual(selectedValues, $value)) {
isInternalUpdate = true;
$value = multiple ? selectedValues : (selectedValues[0] ?? default_value);
handleSelectChange();
isInternalUpdate = false;
}
}
$: disabled = selected.length && options.length === 1 && $constraints?.required;
const multiSelectOptions = {
minSelect: $constraints && $constraints.required === true ? 1 : 0,
maxSelect: multiple ? undefined : 1,
liSelectedClass: multiple ? '!chip !variant-filled' : '!bg-transparent',
inputClass: 'focus:!ring-0 focus:!outline-none',
outerDivClass: '!select',
closeDropdownOnSelect: !multiple
};
const dispatch = createEventDispatcher();
function handleSelectChange() {
dispatch('change', $value);
dispatch('cache', selected);
}
</script>

<div {hidden}>
Expand Down
3 changes: 2 additions & 1 deletion frontend/src/lib/components/Forms/Form.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
}
}
export const _form = superForm(data, {
export let _form = superForm(data, {
dataType: dataType,
invalidateAll: invalidateAll,
applyAction: applyAction,
Expand Down Expand Up @@ -68,6 +68,7 @@
form={_form}
initialData={data.data}
data={$form}
formData={$form}
message={$message}
errors={$errors}
allErrors={$allErrors}
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/lib/utils/actions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ export async function defaultWriteFormAction({
if (redirectToWrittenObject) {
return { form, redirect: `/${urlModel}/${writtenObject.id}` };
}
return { form };
return { form, object: writtenObject };
}

export async function nestedWriteFormAction({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,10 +281,11 @@ export const actions: Actions = {
},
event
);
return { form };
return { form, newControl: measure.id };
},
createEvidence: async (event) => {
return nestedWriteFormAction({ event, action: 'create' });
const result = await nestedWriteFormAction({ event, action: 'create' });
return { form: result.form, newEvidence: result.object.id };
},
createSuggestedControls: async (event) => {
const formData = await event.request.formData();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@
import ConfirmModal from '$lib/components/Modals/ConfirmModal.svelte';
import { zod } from 'sveltekit-superforms/adapters';
import Checkbox from '$lib/components/Forms/Checkbox.svelte';
import { superForm } from 'sveltekit-superforms';
import { invalidateAll } from '$app/navigation';
function cancel(): void {
var currentUrl = window.location.href;
Expand Down Expand Up @@ -131,6 +133,18 @@
modalStore.trigger(modal);
}
const requirementAssessmentForm = superForm(data.form, {
dataType: 'json',
invalidateAll: true,
applyAction: true,
resetForm: false,
validators: zod(schema),
taintedMessage: m.taintedFormMessage(),
validationMethod: 'auto'
});
const formStore = requirementAssessmentForm.form;
$: if (createAppliedControlsLoading === true && form) createAppliedControlsLoading = false;
$: mappingInference = {
Expand Down Expand Up @@ -162,6 +176,14 @@
complianceResultColorMap[mappingInference.result] === '#000000' ? 'text-white' : '';
let tabSet = $page.data.user.is_third_party ? 1 : 0;
$: if (form && form.newControl) {
$formStore.applied_controls.push(form.newControl);
}
$: if (form && form.newEvidence) {
$formStore.evidences.push(form.newEvidence);
}
</script>

<div class="card space-y-2 p-4 bg-white shadow">
Expand Down Expand Up @@ -318,11 +340,10 @@
<div class="mt-4">
<SuperForm
class="flex flex-col"
_form={requirementAssessmentForm}
data={data.form}
dataType="json"
let:form
let:data
validators={zod(schema)}
action="?/updateRequirementAssessment"
{...$$restProps}
>
Expand Down

0 comments on commit 3504e24

Please sign in to comment.