Browse Source

enhancement(nc-gui): validation logic in survey form

pull/5328/head
Wing-Kam Wong 1 year ago
parent
commit
ede69ecdb9
  1. 55
      packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue

55
packages/nc-gui/pages/[projectType]/form/[viewId]/index/survey.vue

@ -4,15 +4,19 @@ import { RelationTypes, UITypes, isVirtualCol } from 'nocodb-sdk'
import { SwipeDirection, breakpointsTailwind } from '@vueuse/core' import { SwipeDirection, breakpointsTailwind } from '@vueuse/core'
import { import {
DropZoneRef, DropZoneRef,
IsSurveyFormInj,
computed, computed,
isValidURL,
onKeyStroke, onKeyStroke,
onMounted, onMounted,
provide, provide,
ref, ref,
useBreakpoints, useBreakpoints,
useI18n,
usePointerSwipe, usePointerSwipe,
useSharedFormStoreOrThrow, useSharedFormStoreOrThrow,
useStepper, useStepper,
validateEmail,
} from '#imports' } from '#imports'
enum TransitionDirection { enum TransitionDirection {
@ -32,6 +36,8 @@ const { md } = useBreakpoints(breakpointsTailwind)
const { v$, formState, formColumns, submitForm, submitted, secondsRemain, sharedFormView, sharedViewMeta, onReset } = const { v$, formState, formColumns, submitForm, submitted, secondsRemain, sharedFormView, sharedViewMeta, onReset } =
useSharedFormStoreOrThrow() useSharedFormStoreOrThrow()
const { t } = useI18n()
const isTransitioning = ref(false) const isTransitioning = ref(false)
const transitionName = ref<TransitionDirection>(TransitionDirection.Left) const transitionName = ref<TransitionDirection>(TransitionDirection.Left)
@ -40,10 +46,14 @@ const animationTarget = ref<AnimationTarget>(AnimationTarget.ArrowRight)
const isAnimating = ref(false) const isAnimating = ref(false)
const editEnabled = ref<boolean[]>([])
const el = ref<HTMLDivElement>() const el = ref<HTMLDivElement>()
provide(DropZoneRef, el) provide(DropZoneRef, el)
provide(IsSurveyFormInj, ref(true))
const transitionDuration = computed(() => sharedViewMeta.value.transitionDuration || 50) const transitionDuration = computed(() => sharedViewMeta.value.transitionDuration || 50)
const steps = computed(() => { const steps = computed(() => {
@ -64,6 +74,8 @@ const { index, goToPrevious, goToNext, isFirst, isLast, goTo } = useStepper(step
const field = computed(() => formColumns.value?.[index.value]) const field = computed(() => formColumns.value?.[index.value])
const columnValidationError = ref(false)
function isRequired(column: ColumnType, required = false) { function isRequired(column: ColumnType, required = false) {
let columnObj = column let columnObj = column
if ( if (
@ -105,7 +117,29 @@ function animate(target: AnimationTarget) {
}, transitionDuration.value / 2) }, transitionDuration.value / 2)
} }
async function validateColumn() {
const f = field.value!
if (parseProp(f.meta)?.validate && formState.value[f.title!]) {
if (f.uidt === UITypes.Email) {
if (!validateEmail(formState.value[f.title!])) {
columnValidationError.value = true
message.error(t('msg.error.invalidEmail'))
return false
}
} else if (f.uidt === UITypes.URL) {
if (!isValidURL(formState.value[f.title!])) {
columnValidationError.value = true
message.error(t('msg.error.invalidURL'))
return false
}
}
}
return true
}
async function goNext(animationTarget?: AnimationTarget) { async function goNext(animationTarget?: AnimationTarget) {
columnValidationError.value = false
if (isLast.value || submitted.value) return if (isLast.value || submitted.value) return
if (!field.value || !field.value.title) return if (!field.value || !field.value.title) return
@ -117,6 +151,8 @@ async function goNext(animationTarget?: AnimationTarget) {
if (!isValid) return if (!isValid) return
} }
if (!(await validateColumn())) return
animate(animationTarget || AnimationTarget.ArrowRight) animate(animationTarget || AnimationTarget.ArrowRight)
setTimeout( setTimeout(
@ -159,9 +195,8 @@ function resetForm() {
goTo(steps.value[0]) goTo(steps.value[0])
} }
function submit() { async function submit() {
if (submitted.value) return if (submitted.value || !(await validateColumn())) return
submitForm() submitForm()
} }
@ -177,7 +212,7 @@ onKeyStroke(['Enter', 'Space'], () => {
if (isLast.value) { if (isLast.value) {
submit() submit()
} else { } else {
goNext(AnimationTarget.OkButton) goNext(AnimationTarget.OkButton, true)
} }
}) })
@ -263,7 +298,10 @@ onMounted(() => {
class="nc-input" class="nc-input"
:data-testid="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`" :data-testid="`nc-survey-form__input-${field.title.replaceAll(' ', '')}`"
:column="field" :column="field"
:edit-enabled="true" :edit-enabled="editEnabled[index]"
@click="editEnabled[index] = true"
@cancel="editEnabled[index] = false"
@update:edit-enabled="editEnabled[index] = $event"
/> />
<div class="flex flex-col gap-2 text-slate-500 dark:text-slate-300 text-[0.75rem] my-2 px-1"> <div class="flex flex-col gap-2 text-slate-500 dark:text-slate-300 text-[0.75rem] my-2 px-1">
@ -315,7 +353,7 @@ onMounted(() => {
class="bg-opacity-100 scaling-btn flex items-center gap-1" class="bg-opacity-100 scaling-btn flex items-center gap-1"
data-testid="nc-survey-form__btn-next" data-testid="nc-survey-form__btn-next"
:class="[ :class="[
v$.localState[field.title]?.$error ? 'after:!bg-gray-100 after:!ring-red-500' : '', v$.localState[field.title]?.$error || columnValidationError ? 'after:!bg-gray-100 after:!ring-red-500' : '',
animationTarget === AnimationTarget.OkButton && isAnimating animationTarget === AnimationTarget.OkButton && isAnimating
? 'transform translate-y-[2px] translate-x-[2px] after:(!ring !ring-accent !ring-opacity-100)' ? 'transform translate-y-[2px] translate-x-[2px] after:(!ring !ring-accent !ring-opacity-100)'
: '', : '',
@ -327,7 +365,10 @@ onMounted(() => {
</Transition> </Transition>
<Transition name="slide-right" mode="out-in"> <Transition name="slide-right" mode="out-in">
<MdiCloseCircleOutline v-if="v$.localState[field.title]?.$error" class="text-red-500 md:text-md" /> <MdiCloseCircleOutline
v-if="v$.localState[field.title]?.$error || columnValidationError"
class="text-red-500 md:text-md"
/>
<MdiCheck v-else class="text-white md:text-md" /> <MdiCheck v-else class="text-white md:text-md" />
</Transition> </Transition>
</button> </button>

Loading…
Cancel
Save