From ee186c8c2503b900406969467c01afce5c56a664 Mon Sep 17 00:00:00 2001 From: Omar Kassem Date: Thu, 20 Feb 2025 11:59:24 +0200 Subject: [PATCH 1/4] Feature[FormValidator]:Add init state to form validator for improved validation logic --- .../src/components/form_validator.vue | 20 ++++++++++++++++--- .../playground/src/hooks/form_validator.ts | 1 + 2 files changed, 18 insertions(+), 3 deletions(-) diff --git a/packages/playground/src/components/form_validator.vue b/packages/playground/src/components/form_validator.vue index 26d51520c0..388da76c41 100644 --- a/packages/playground/src/components/form_validator.vue +++ b/packages/playground/src/components/form_validator.vue @@ -28,7 +28,20 @@ export default { }), ); watch(valid, valid => emit("update:modelValue", valid), { immediate: true }); - + const invalid = computed(() => [...statusMap.value.values()].some(status => status === ValidatorStatus.Invalid)); + const pending = computed(() => [...statusMap.value.values()].some(status => status === ValidatorStatus.Pending)); + /** + * The validation is considered "init" if: + * - There are no invalid (`ValidatorStatus.Invalid`) statuses. + * - There are no pending (`ValidatorStatus.Pending`) statuses. + * - At least one status is `ValidatorStatus.Init`. + */ + const init = computed( + () => + !invalid.value && + !pending.value && + [...statusMap.value.values()].some(status => status === ValidatorStatus.Init), + ); const form: FormValidatorService = { register(uid, service) { statusMap.value.set(uid, ValidatorStatus.Init); @@ -72,8 +85,9 @@ export default { get: uid => serviceMap.value.get(uid), valid, - invalid: computed(() => [...statusMap.value.values()].some(status => status === ValidatorStatus.Invalid)), - pending: computed(() => [...statusMap.value.values()].some(status => status === ValidatorStatus.Pending)), + invalid, + pending, + init, validOnInit: props.validOnInit, inputs: computed(() => [...serviceMap.value.values()] as any), }; diff --git a/packages/playground/src/hooks/form_validator.ts b/packages/playground/src/hooks/form_validator.ts index 91f3eb2e73..73c1070377 100644 --- a/packages/playground/src/hooks/form_validator.ts +++ b/packages/playground/src/hooks/form_validator.ts @@ -19,6 +19,7 @@ export interface FormValidatorService { valid: ComputedRef; invalid: ComputedRef; pending: ComputedRef; + init: ComputedRef; validOnInit: boolean; inputs: ComputedRef; } From 2c2ece10df569a99ddcda44f033c439a0c38b98c Mon Sep 17 00:00:00 2001 From: Omar Kassem Date: Thu, 20 Feb 2025 12:08:48 +0200 Subject: [PATCH 2/4] Fix[DomainName:validationStatus]: improved validation and form status binding. - replace VForm with formValidator - use inputValidator for the inner fileds - improve status binding --- .../components/node_selector/TfDomainName.vue | 141 +++++++++++------- 1 file changed, 83 insertions(+), 58 deletions(-) diff --git a/packages/playground/src/components/node_selector/TfDomainName.vue b/packages/playground/src/components/node_selector/TfDomainName.vue index d7c225ed0e..74497e4864 100644 --- a/packages/playground/src/components/node_selector/TfDomainName.vue +++ b/packages/playground/src/components/node_selector/TfDomainName.vue @@ -9,22 +9,24 @@
- + - + v-model:value="customDomain" + ref="customInputRef" + #="{ props }" + > + @@ -33,42 +35,48 @@ tooltip="Creates a subdomain for your instance on the selected domain to be able to access your instance from the browser." v-if="!disableSelectedDomain" > - - - - + + + + + @@ -88,7 +96,7 @@ {{ customDomain }} pointing to {{ selectedDomain.publicConfig.ipv4.split("/")[0] }} - +
@@ -101,12 +109,14 @@ import { onMounted } from "vue"; // eslint-disable-next-line @typescript-eslint/no-unused-vars import type { VInput } from "vuetify/components/VInput"; -import type { InputValidatorService } from "@/hooks/input_validator"; +import { type InputValidatorService, useInputRef } from "@/hooks/input_validator"; import { useAsync, usePagination, useWatchDeep } from "../../hooks"; -import { useForm, ValidatorStatus } from "../../hooks/form_validator"; +import { useForm, useFormRef, ValidatorStatus } from "../../hooks/form_validator"; import { useGrid } from "../../stores"; import type { DomainInfo, NetworkFeatures, SelectionDetailsFilters } from "../../types/nodeSelector"; +// eslint-disable-next-line @typescript-eslint/no-unused-vars +import type { INode } from "../../utils/filter_nodes"; import { getNodePageCount, loadNodes } from "../../utils/nodeSelector"; export default { @@ -159,11 +169,12 @@ export default { const selectedDomain = ref(null); const loadDomains = () => domainsTask.value.run(gridStore, filters.value); + const domainInput = useInputRef(); const reloadDomains = async (_filters: FilterOptions = filters.value) => { + domainInput.value?.reset(); if (selectedDomain.value) { selectedDomain.value = null; bindModelValue(); - bindStatus(); } await pageCountTask.value.run(gridStore, _filters); pagination.value.reset(pageCountTask.value.data as number); @@ -184,11 +195,7 @@ export default { { immediate: true, deep: true, ignoreFields: ["page"] }, ); const customDomain = ref(""); - - const domainNameValid = ref(null); - watch(domainNameValid, valid => { - bindStatus(valid === null ? ValidatorStatus.Init : valid ? ValidatorStatus.Valid : ValidatorStatus.Invalid); - }); + const domainFormRef = useFormRef(); const disableSelectedDomain = computed(() => enableCustomDomain.value && props.filters.ipv4 === true); const useFQDN = computed(() => enableCustomDomain.value && (props.useFqdn || props.filters.ipv4 === false)); @@ -228,12 +235,12 @@ export default { }; onMounted(() => { + bindStatus(); loadDomains(); form?.register(uid.toString(), fakeService); }); onUnmounted(() => form?.unregister(uid.toString())); - onMounted(bindStatus); function bindStatus(status?: ValidatorStatus): void { const s = status || ValidatorStatus.Init; fakeService.status = s; @@ -241,12 +248,28 @@ export default { ctx.emit("update:status", s); } + const status = computed(() => { + const _domain = domainFormRef?.value; + if (!_domain) return ValidatorStatus.Init; + + switch (true) { + case _domain.valid.value: + return ValidatorStatus.Valid; + case _domain.invalid.value: + return ValidatorStatus.Invalid; + case _domain.pending.value: + return ValidatorStatus.Pending; + default: + return ValidatorStatus.Init; + } + }); + + watch(status, () => bindStatus(status.value), { immediate: true }); + return { pagination, input, - domainNameValid, - enableCustomDomain, customDomain, @@ -256,6 +279,8 @@ export default { loadDomains, reloadDomains, + domainFormRef, + domainInput, disableSelectedDomain, useFQDN, }; From 18a4f67763a794ba13622058daa4e923122dc68e Mon Sep 17 00:00:00 2001 From: Omar Kassem Date: Thu, 20 Feb 2025 13:08:13 +0200 Subject: [PATCH 3/4] Fix[DomainName:validationStatus]: refactor validation logic to use boolean casting for status checks --- .../components/node_selector/TfDomainName.vue | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/packages/playground/src/components/node_selector/TfDomainName.vue b/packages/playground/src/components/node_selector/TfDomainName.vue index 74497e4864..4179784e0f 100644 --- a/packages/playground/src/components/node_selector/TfDomainName.vue +++ b/packages/playground/src/components/node_selector/TfDomainName.vue @@ -253,17 +253,26 @@ export default { if (!_domain) return ValidatorStatus.Init; switch (true) { - case _domain.valid.value: + case _domain.valid as unknown as boolean: return ValidatorStatus.Valid; - case _domain.invalid.value: + case _domain.invalid as unknown as boolean: return ValidatorStatus.Invalid; - case _domain.pending.value: + case _domain.pending as unknown as boolean: return ValidatorStatus.Pending; default: return ValidatorStatus.Init; } }); - + // const status = computed(() => { + // console.log( + // domainFormRef.value?.valid,domainFormRef.value?.init,domainFormRef.value?.invalid,domainFormRef.value?.pending + // ) + // if (!domainFormRef?.value) return ValidatorStatus.Init; + // if (domainFormRef.value?.valid) return ValidatorStatus.Valid; + // if (domainFormRef.value?.invalid) return ValidatorStatus.Invalid; + // if (domainFormRef.value?.pending) return ValidatorStatus.Pending; + // else return ValidatorStatus.Init; + // }); watch(status, () => bindStatus(status.value), { immediate: true }); return { From c80f86091c7be9ac6ab0270cc49ced81ec6a3372 Mon Sep 17 00:00:00 2001 From: Omar Kassem Date: Tue, 25 Feb 2025 12:32:03 +0200 Subject: [PATCH 4/4] Chore: remove commented code --- .../src/components/node_selector/TfDomainName.vue | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/packages/playground/src/components/node_selector/TfDomainName.vue b/packages/playground/src/components/node_selector/TfDomainName.vue index 4179784e0f..1324dd0264 100644 --- a/packages/playground/src/components/node_selector/TfDomainName.vue +++ b/packages/playground/src/components/node_selector/TfDomainName.vue @@ -263,16 +263,7 @@ export default { return ValidatorStatus.Init; } }); - // const status = computed(() => { - // console.log( - // domainFormRef.value?.valid,domainFormRef.value?.init,domainFormRef.value?.invalid,domainFormRef.value?.pending - // ) - // if (!domainFormRef?.value) return ValidatorStatus.Init; - // if (domainFormRef.value?.valid) return ValidatorStatus.Valid; - // if (domainFormRef.value?.invalid) return ValidatorStatus.Invalid; - // if (domainFormRef.value?.pending) return ValidatorStatus.Pending; - // else return ValidatorStatus.Init; - // }); + watch(status, () => bindStatus(status.value), { immediate: true }); return {