123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275 |
- import { ref, computed, onMounted, onBeforeUnmount, watch } from "vue";
- import { useModalsStore } from "@/stores/modals";
- export const useForm = (
- inputOptions: Record<
- string,
- | {
- value: any;
- validate?: (value: any) => boolean | string;
- }
- | any
- >,
- cb: (
- response: {
- status: string;
- messages: Record<string, string>;
- values: Record<string, any>;
- },
- resolve: (value?: undefined) => void,
- reject: (value: Error) => void
- ) => void,
- options?: {
- modalUuid?: string;
- preventCloseUnsaved?: boolean;
- }
- ) => {
- const { openModal, preventCloseUnsaved } = useModalsStore();
- const inputs = ref(
- Object.fromEntries(
- Object.entries(inputOptions).map(([name, input]) => {
- if (typeof input !== "object") input = { value: input };
- return [
- name,
- {
- ...input,
- originalValue: JSON.parse(JSON.stringify(input.value)),
- errors: [],
- ref: ref(),
- sourceChanged: false,
- ignoreUnsaved: input.ignoreUnsaved === true,
- required:
- input.required === undefined ? true : input.required
- }
- ];
- })
- )
- );
- const unsavedChanges = computed(() => {
- const changed: string[] = [];
- Object.entries(inputs.value).forEach(([name, input]) => {
- if (
- !input.ignoreUnsaved &&
- JSON.stringify(input.value) !==
- JSON.stringify(input.originalValue)
- )
- changed.push(name);
- });
- return changed;
- });
- const sourceChanged = computed(() => {
- const _sourceChanged: string[] = [];
- Object.entries(inputs.value).forEach(([name, input]) => {
- if (
- input.sourceChanged &&
- unsavedChanges.value.find(change => change === name)
- )
- _sourceChanged.push(name);
- });
- return _sourceChanged;
- });
- const saveButton = ref();
- const useCallback = (status: string, messages?: Record<string, string>) =>
- new Promise((resolve, reject: (reason: Error) => void) => {
- cb(
- {
- status,
- messages: { ...messages },
- values: Object.fromEntries(
- Object.entries(inputs.value).map(([name, input]) => [
- name,
- input.value
- ])
- )
- },
- resolve,
- reject
- );
- });
- const resetOriginalValues = () => {
- inputs.value = Object.fromEntries(
- Object.entries(inputs.value).map(([name, input]) => [
- name,
- {
- ...input,
- originalValue: JSON.parse(JSON.stringify(input.value)),
- sourceChanged: false
- }
- ])
- );
- };
- const validate = () => {
- const invalid: Record<string, string[]> = {};
- Object.entries(inputs.value).forEach(([name, input]) => {
- input.errors = [];
- if (
- input.required &&
- (input.value === undefined ||
- input.value === "" ||
- input.value === null)
- )
- input.errors.push(`Invalid ${name}. Please provide value`);
- if (input.validate) {
- const valid = input.validate(input.value, inputs);
- if (valid !== true) {
- input.errors.push(
- valid === false ? `Invalid ${name}` : valid
- );
- }
- }
- if (input.errors.length > 0)
- invalid[name] = input.errors.join(", ");
- });
- return invalid;
- };
- const save = (saveCb?: () => void) => {
- if (saveButton.value) saveButton.value.status = "disabled";
- const errors = validate();
- const errorCount = Object.keys(errors).length;
- if (errorCount === 0 && unsavedChanges.value.length > 0) {
- const onSave = () => {
- useCallback("success")
- .then(() => {
- resetOriginalValues();
- if (saveCb) saveCb();
- saveButton.value?.handleSuccessfulSave();
- })
- .catch((err: Error) =>
- useCallback("error", { error: err.message }).then(
- () => {
- saveButton.value?.handleFailedSave();
- }
- )
- );
- };
- if (sourceChanged.value.length > 0)
- openModal({
- modal: "confirm",
- props: {
- message:
- "Updates have been made whilst you were making changes. Are you sure you want to continue?",
- onCompleted: onSave
- }
- });
- else onSave();
- } else if (errorCount === 0) {
- useCallback("unchanged", { unchanged: "No changes have been made" })
- .then(() => {
- if (saveCb) saveCb();
- saveButton.value?.handleSuccessfulSave();
- })
- .catch((err: Error) =>
- useCallback("error", { error: err.message }).then(() => {
- saveButton.value?.handleFailedSave();
- })
- );
- } else {
- useCallback("error", {
- ...errors,
- error: `${errorCount} ${
- errorCount === 1 ? "input" : "inputs"
- } failed validation.`
- }).then(() => {
- saveButton.value?.handleFailedSave();
- });
- }
- };
- const setValue = (value: Record<string, any>, reset?: boolean) => {
- Object.entries(value).forEach(([name, inputValue]) => {
- if (inputs.value[name]) {
- inputs.value[name].value = JSON.parse(
- JSON.stringify(inputValue)
- );
- if (reset) {
- inputs.value[name].sourceChanged = false;
- inputs.value[name].originalValue = JSON.parse(
- JSON.stringify(inputValue)
- );
- }
- }
- });
- };
- const setOriginalValue = (value: Record<string, any>) => {
- Object.entries(value).forEach(([name, inputValue]) => {
- if (inputs.value[name]) {
- if (
- JSON.stringify(inputValue) !==
- JSON.stringify(inputs.value[name].originalValue)
- ) {
- if (unsavedChanges.value.find(change => change === name))
- inputs.value[name].sourceChanged = true;
- else
- inputs.value[name].value = JSON.parse(
- JSON.stringify(inputValue)
- );
- inputs.value[name].originalValue = JSON.parse(
- JSON.stringify(inputValue)
- );
- }
- }
- });
- };
- const setModelValues = (model: Record<string, any>, keys: string[]) => {
- const getModelValue = (key, modelObject) => {
- let value = JSON.parse(JSON.stringify(modelObject));
- key.split(".").forEach(property => {
- value = value[property];
- });
- return value;
- };
- const setMappedValues = modelObject => {
- setOriginalValue(
- Object.fromEntries(
- keys.map(key => [key, getModelValue(key, modelObject)])
- )
- );
- };
- setMappedValues(model);
- watch(model, setMappedValues);
- };
- onMounted(() => {
- if (
- options &&
- options.modalUuid &&
- options.preventCloseUnsaved !== false
- )
- preventCloseUnsaved[options.modalUuid] = () =>
- unsavedChanges.value.length > 0;
- });
- onBeforeUnmount(() => {
- if (
- options &&
- options.modalUuid &&
- options.preventCloseUnsaved !== false
- )
- delete preventCloseUnsaved[options.modalUuid];
- });
- return {
- inputs,
- unsavedChanges,
- saveButton,
- save,
- setValue,
- setOriginalValue,
- setModelValues
- };
- };
|