refactor(form-schema): extract schema types and schema-driven behaviors to shared package
Moves formBuilder types, formValidation, useConditionalLogic, useFormSteps, and formatFieldValue from apps/portal/src to packages/form-schema/src. Adds @form-schema path alias to both apps/portal and apps/app. Vue field components remain per-app to allow independent visual evolution. Behavior-neutral: all 35 Vitest tests green. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -39,6 +39,12 @@
|
|||||||
],
|
],
|
||||||
"@validators": [
|
"@validators": [
|
||||||
"./src/@core/utils/validators"
|
"./src/@core/utils/validators"
|
||||||
|
],
|
||||||
|
"@form-schema/*": [
|
||||||
|
"../../packages/form-schema/src/*"
|
||||||
|
],
|
||||||
|
"vue": [
|
||||||
|
"./node_modules/vue"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"lib": [
|
"lib": [
|
||||||
@@ -63,7 +69,8 @@
|
|||||||
"./src/**/*.vue",
|
"./src/**/*.vue",
|
||||||
"./themeConfig.ts",
|
"./themeConfig.ts",
|
||||||
"./auto-imports.d.ts",
|
"./auto-imports.d.ts",
|
||||||
"./components.d.ts"
|
"./components.d.ts",
|
||||||
|
"../../packages/form-schema/src/**/*"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"./dist",
|
"./dist",
|
||||||
|
|||||||
@@ -110,6 +110,9 @@ export default defineConfig({
|
|||||||
import.meta.url,
|
import.meta.url,
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
|
"@form-schema": fileURLToPath(
|
||||||
|
new URL("../../packages/form-schema/src", import.meta.url),
|
||||||
|
),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
|||||||
7
apps/portal/auto-imports.d.ts
vendored
7
apps/portal/auto-imports.d.ts
vendored
@@ -434,19 +434,16 @@ declare module 'vue' {
|
|||||||
readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
|
readonly eagerComputed: UnwrapRef<typeof import('@vueuse/core')['eagerComputed']>
|
||||||
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
readonly effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||||
readonly emailValidator: UnwrapRef<typeof import('./src/@core/utils/validators')['emailValidator']>
|
readonly emailValidator: UnwrapRef<typeof import('./src/@core/utils/validators')['emailValidator']>
|
||||||
readonly evaluateConditionalLogic: UnwrapRef<typeof import('./src/composables/useConditionalLogic')['evaluateConditionalLogic']>
|
|
||||||
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
|
readonly extendRef: UnwrapRef<typeof import('@vueuse/core')['extendRef']>
|
||||||
readonly extractErrorBody: UnwrapRef<typeof import('./src/composables/useFormDraft')['extractErrorBody']>
|
readonly extractErrorBody: UnwrapRef<typeof import('./src/composables/useFormDraft')['extractErrorBody']>
|
||||||
readonly extractRetryAfterSeconds: UnwrapRef<typeof import('./src/composables/useFormDraft')['extractRetryAfterSeconds']>
|
readonly extractRetryAfterSeconds: UnwrapRef<typeof import('./src/composables/useFormDraft')['extractRetryAfterSeconds']>
|
||||||
readonly formatDate: UnwrapRef<typeof import('./src/@core/utils/formatters')['formatDate']>
|
readonly formatDate: UnwrapRef<typeof import('./src/@core/utils/formatters')['formatDate']>
|
||||||
readonly formatDateToMonthShort: UnwrapRef<typeof import('./src/@core/utils/formatters')['formatDateToMonthShort']>
|
readonly formatDateToMonthShort: UnwrapRef<typeof import('./src/@core/utils/formatters')['formatDateToMonthShort']>
|
||||||
readonly formatFieldValue: UnwrapRef<typeof import('./src/composables/formatFieldValue')['formatFieldValue']>
|
|
||||||
readonly generateDeviceFingerprint: UnwrapRef<typeof import('./src/utils/deviceFingerprint')['generateDeviceFingerprint']>
|
readonly generateDeviceFingerprint: UnwrapRef<typeof import('./src/utils/deviceFingerprint')['generateDeviceFingerprint']>
|
||||||
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
readonly getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
||||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||||
readonly getDeviceName: UnwrapRef<typeof import('./src/utils/deviceFingerprint')['getDeviceName']>
|
readonly getDeviceName: UnwrapRef<typeof import('./src/utils/deviceFingerprint')['getDeviceName']>
|
||||||
readonly getValidatorsForField: UnwrapRef<typeof import('./src/utils/formValidation')['getValidatorsForField']>
|
|
||||||
readonly h: UnwrapRef<typeof import('vue')['h']>
|
readonly h: UnwrapRef<typeof import('vue')['h']>
|
||||||
readonly hexToRgb: UnwrapRef<typeof import('./src/@core/utils/colorConverter')['hexToRgb']>
|
readonly hexToRgb: UnwrapRef<typeof import('./src/@core/utils/colorConverter')['hexToRgb']>
|
||||||
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
|
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
|
||||||
@@ -456,14 +453,12 @@ declare module 'vue' {
|
|||||||
readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
|
readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
|
||||||
readonly isEmpty: UnwrapRef<typeof import('./src/@core/utils/helpers')['isEmpty']>
|
readonly isEmpty: UnwrapRef<typeof import('./src/@core/utils/helpers')['isEmpty']>
|
||||||
readonly isEmptyArray: UnwrapRef<typeof import('./src/@core/utils/helpers')['isEmptyArray']>
|
readonly isEmptyArray: UnwrapRef<typeof import('./src/@core/utils/helpers')['isEmptyArray']>
|
||||||
readonly isFieldValueEmpty: UnwrapRef<typeof import('./src/utils/formValidation')['isFieldValueEmpty']>
|
|
||||||
readonly isNullOrUndefined: UnwrapRef<typeof import('./src/@core/utils/helpers')['isNullOrUndefined']>
|
readonly isNullOrUndefined: UnwrapRef<typeof import('./src/@core/utils/helpers')['isNullOrUndefined']>
|
||||||
readonly isObject: UnwrapRef<typeof import('./src/@core/utils/helpers')['isObject']>
|
readonly isObject: UnwrapRef<typeof import('./src/@core/utils/helpers')['isObject']>
|
||||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||||
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
readonly isRef: UnwrapRef<typeof import('vue')['isRef']>
|
||||||
readonly isStepValid: UnwrapRef<typeof import('./src/composables/useFormSteps')['isStepValid']>
|
|
||||||
readonly isToday: UnwrapRef<typeof import('./src/@core/utils/helpers')['isToday']>
|
readonly isToday: UnwrapRef<typeof import('./src/@core/utils/helpers')['isToday']>
|
||||||
readonly kFormatter: UnwrapRef<typeof import('./src/@core/utils/formatters')['kFormatter']>
|
readonly kFormatter: UnwrapRef<typeof import('./src/@core/utils/formatters')['kFormatter']>
|
||||||
readonly lengthValidator: UnwrapRef<typeof import('./src/@core/utils/validators')['lengthValidator']>
|
readonly lengthValidator: UnwrapRef<typeof import('./src/@core/utils/validators')['lengthValidator']>
|
||||||
@@ -526,7 +521,6 @@ declare module 'vue' {
|
|||||||
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
|
readonly resolveUnref: UnwrapRef<typeof import('@vueuse/core')['resolveUnref']>
|
||||||
readonly resolveVuetifyTheme: UnwrapRef<typeof import('./src/@core/utils/vuetify')['resolveVuetifyTheme']>
|
readonly resolveVuetifyTheme: UnwrapRef<typeof import('./src/@core/utils/vuetify')['resolveVuetifyTheme']>
|
||||||
readonly rgbaToHex: UnwrapRef<typeof import('./src/@core/utils/colorConverter')['rgbaToHex']>
|
readonly rgbaToHex: UnwrapRef<typeof import('./src/@core/utils/colorConverter')['rgbaToHex']>
|
||||||
readonly runValidators: UnwrapRef<typeof import('./src/utils/formValidation')['runValidators']>
|
|
||||||
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
|
readonly setActivePinia: UnwrapRef<typeof import('pinia')['setActivePinia']>
|
||||||
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
readonly setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
||||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||||
@@ -623,7 +617,6 @@ declare module 'vue' {
|
|||||||
readonly useFocus: UnwrapRef<typeof import('@vueuse/core')['useFocus']>
|
readonly useFocus: UnwrapRef<typeof import('@vueuse/core')['useFocus']>
|
||||||
readonly useFocusWithin: UnwrapRef<typeof import('@vueuse/core')['useFocusWithin']>
|
readonly useFocusWithin: UnwrapRef<typeof import('@vueuse/core')['useFocusWithin']>
|
||||||
readonly useFormDraft: UnwrapRef<typeof import('./src/composables/useFormDraft')['useFormDraft']>
|
readonly useFormDraft: UnwrapRef<typeof import('./src/composables/useFormDraft')['useFormDraft']>
|
||||||
readonly useFormSteps: UnwrapRef<typeof import('./src/composables/useFormSteps')['useFormSteps']>
|
|
||||||
readonly useFps: UnwrapRef<typeof import('@vueuse/core')['useFps']>
|
readonly useFps: UnwrapRef<typeof import('@vueuse/core')['useFps']>
|
||||||
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
|
readonly useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
|
||||||
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
|
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormSubmissionDuplicate } from '@/types/formBuilder'
|
import type { PublicFormSubmissionDuplicate } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
data: PublicFormSubmissionDuplicate | null
|
data: PublicFormSubmissionDuplicate | null
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
||||||
import { usePublicFormToken } from '@/composables/publicFormInjection'
|
import { usePublicFormToken } from '@/composables/publicFormInjection'
|
||||||
import type { PublicFormField, PublicFormTimeSlot } from '@/types/formBuilder'
|
import type { PublicFormField, PublicFormTimeSlot } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField, runValidators } from '@/utils/formValidation'
|
import { getValidatorsForField, runValidators } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FieldOption, PublicFormField } from '@/types/formBuilder'
|
import type { FieldOption, PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField, runValidators } from '@/utils/formValidation'
|
import { getValidatorsForField, runValidators } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FieldOption, PublicFormField } from '@/types/formBuilder'
|
import type { FieldOption, PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
defineProps<{
|
defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FieldOption, PublicFormField } from '@/types/formBuilder'
|
import type { FieldOption, PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -16,9 +16,9 @@ import FieldTagPicker from './FieldTagPicker.vue'
|
|||||||
import FieldText from './FieldText.vue'
|
import FieldText from './FieldText.vue'
|
||||||
import FieldTextarea from './FieldTextarea.vue'
|
import FieldTextarea from './FieldTextarea.vue'
|
||||||
import FieldUrl from './FieldUrl.vue'
|
import FieldUrl from './FieldUrl.vue'
|
||||||
import { evaluateConditionalLogic } from '@/composables/useConditionalLogic'
|
import { evaluateConditionalLogic } from '@form-schema/composables/useConditionalLogic'
|
||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||||
import type { FormFieldDisplayWidth, FormValues, PublicFormField } from '@/types/formBuilder'
|
import type { FormFieldDisplayWidth, FormValues, PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -3,8 +3,8 @@ import draggable from 'vuedraggable'
|
|||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
||||||
import { usePublicFormToken } from '@/composables/publicFormInjection'
|
import { usePublicFormToken } from '@/composables/publicFormInjection'
|
||||||
import type { PublicFormField, PublicFormSectionOption, SectionPriorityValue } from '@/types/formBuilder'
|
import type { PublicFormField, PublicFormSectionOption, SectionPriorityValue } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField, runValidators } from '@/utils/formValidation'
|
import { getValidatorsForField, runValidators } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FieldOption, PublicFormField } from '@/types/formBuilder'
|
import type { FieldOption, PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { AvailableTag, PublicFormField } from '@/types/formBuilder'
|
import type { AvailableTag, PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
import { getValidatorsForField } from '@/utils/formValidation'
|
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
field: PublicFormField
|
field: PublicFormField
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ import DuplicateSubmissionHint from './DuplicateSubmissionHint.vue'
|
|||||||
import IdentityMatchBanner from './IdentityMatchBanner.vue'
|
import IdentityMatchBanner from './IdentityMatchBanner.vue'
|
||||||
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
||||||
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
||||||
import { formatFieldValue } from '@/composables/formatFieldValue'
|
import { formatFieldValue } from '@form-schema/composables/formatFieldValue'
|
||||||
import type { FormStep } from '@/composables/useFormSteps'
|
import type { FormStep } from '@form-schema/composables/useFormSteps'
|
||||||
import { usePublicFormToken } from '@/composables/publicFormInjection'
|
import { usePublicFormToken } from '@/composables/publicFormInjection'
|
||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||||
import type {
|
import type {
|
||||||
FormValues,
|
FormValues,
|
||||||
PublicFormField,
|
PublicFormField,
|
||||||
PublicFormSubmissionDuplicate,
|
PublicFormSubmissionDuplicate,
|
||||||
PublicFormSubmissionIdentityMatch,
|
PublicFormSubmissionIdentityMatch,
|
||||||
} from '@/types/formBuilder'
|
} from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
steps: FormStep[]
|
steps: FormStep[]
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { FormErrorCode } from '@/types/formBuilder'
|
import type { FormErrorCode } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
errorCode?: FormErrorCode | string
|
errorCode?: FormErrorCode | string
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { useDisplay } from 'vuetify'
|
import { useDisplay } from 'vuetify'
|
||||||
import type { FormStep } from '@/composables/useFormSteps'
|
import type { FormStep } from '@form-schema/composables/useFormSteps'
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
steps: FormStep[]
|
steps: FormStep[]
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import type {
|
|||||||
SaveDraftBody,
|
SaveDraftBody,
|
||||||
StartDraftBody,
|
StartDraftBody,
|
||||||
SubmitBody,
|
SubmitBody,
|
||||||
} from '@/types/formBuilder'
|
} from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
interface ApiResponse<T> {
|
interface ApiResponse<T> {
|
||||||
data: T
|
data: T
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useQuery } from '@tanstack/vue-query'
|
import { useQuery } from '@tanstack/vue-query'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import { apiClient } from '@/lib/axios'
|
import { apiClient } from '@/lib/axios'
|
||||||
import type { PublicFormSectionOption } from '@/types/formBuilder'
|
import type { PublicFormSectionOption } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
interface ApiResponse<T> {
|
interface ApiResponse<T> {
|
||||||
data: T
|
data: T
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { useQuery } from '@tanstack/vue-query'
|
import { useQuery } from '@tanstack/vue-query'
|
||||||
import type { Ref } from 'vue'
|
import type { Ref } from 'vue'
|
||||||
import { apiClient } from '@/lib/axios'
|
import { apiClient } from '@/lib/axios'
|
||||||
import type { PublicFormTimeSlot } from '@/types/formBuilder'
|
import type { PublicFormTimeSlot } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
interface ApiResponse<T> {
|
interface ApiResponse<T> {
|
||||||
data: T
|
data: T
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
useSaveFormDraft,
|
useSaveFormDraft,
|
||||||
useSubmitForm,
|
useSubmitForm,
|
||||||
} from '@/composables/api/usePublicForm'
|
} from '@/composables/api/usePublicForm'
|
||||||
import type { FormValues, PublicFormSubmission, SaveDraftBody } from '@/types/formBuilder'
|
import type { FormValues, PublicFormSubmission, SaveDraftBody } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
/** sessionStorage key for reusing an idempotency key across reloads. */
|
/** sessionStorage key for reusing an idempotency key across reloads. */
|
||||||
export function draftIdempotencyKey(token: string): string {
|
export function draftIdempotencyKey(token: string): string {
|
||||||
|
|||||||
@@ -9,11 +9,11 @@ import { extractErrorBody, useFetchPublicFormSchema } from '@/composables/api/us
|
|||||||
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
||||||
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
||||||
import { useFormDraft } from '@/composables/useFormDraft'
|
import { useFormDraft } from '@/composables/useFormDraft'
|
||||||
import { isStepValid, useFormSteps } from '@/composables/useFormSteps'
|
import { isStepValid, useFormSteps } from '@form-schema/composables/useFormSteps'
|
||||||
import { formatFieldValue } from '@/composables/formatFieldValue'
|
import { formatFieldValue } from '@form-schema/composables/formatFieldValue'
|
||||||
import { providePublicFormToken } from '@/composables/publicFormInjection'
|
import { providePublicFormToken } from '@/composables/publicFormInjection'
|
||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||||
import type { FormErrorCode, PublicFormField } from '@/types/formBuilder'
|
import type { FormErrorCode, PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
definePage({
|
definePage({
|
||||||
name: 'public-form-register',
|
name: 'public-form-register',
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ vi.mock('@/composables/publicFormInjection', () => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
import FieldAvailabilityPicker from '@/components/public-form/FieldAvailabilityPicker.vue'
|
import FieldAvailabilityPicker from '@/components/public-form/FieldAvailabilityPicker.vue'
|
||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||||
import type { PublicFormField, PublicFormTimeSlot } from '@/types/formBuilder'
|
import type { PublicFormField, PublicFormTimeSlot } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import FieldRenderer from '@/components/public-form/FieldRenderer.vue'
|
import FieldRenderer from '@/components/public-form/FieldRenderer.vue'
|
||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
function makeField(partial: Partial<PublicFormField>): PublicFormField {
|
function makeField(partial: Partial<PublicFormField>): PublicFormField {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -36,8 +36,8 @@ vi.mock('vuedraggable', () => ({
|
|||||||
}))
|
}))
|
||||||
|
|
||||||
import FieldSectionPriority from '@/components/public-form/FieldSectionPriority.vue'
|
import FieldSectionPriority from '@/components/public-form/FieldSectionPriority.vue'
|
||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||||
import type { PublicFormField, PublicFormSectionOption } from '@/types/formBuilder'
|
import type { PublicFormField, PublicFormSectionOption } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import FieldTagPicker from '@/components/public-form/FieldTagPicker.vue'
|
import FieldTagPicker from '@/components/public-form/FieldTagPicker.vue'
|
||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||||
import type { AvailableTag, PublicFormField } from '@/types/formBuilder'
|
import type { AvailableTag, PublicFormField } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ vi.mock('@/lib/axios', () => ({
|
|||||||
|
|
||||||
import { apiClient } from '@/lib/axios'
|
import { apiClient } from '@/lib/axios'
|
||||||
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
||||||
import type { PublicFormSectionOption } from '@/types/formBuilder'
|
import type { PublicFormSectionOption } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
interface MockedApi { get: ReturnType<typeof vi.fn> }
|
interface MockedApi { get: ReturnType<typeof vi.fn> }
|
||||||
const mocked = apiClient as unknown as MockedApi
|
const mocked = apiClient as unknown as MockedApi
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ vi.mock('@/lib/axios', () => ({
|
|||||||
|
|
||||||
import { apiClient } from '@/lib/axios'
|
import { apiClient } from '@/lib/axios'
|
||||||
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
||||||
import type { PublicFormTimeSlot } from '@/types/formBuilder'
|
import type { PublicFormTimeSlot } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
interface MockedApi { get: ReturnType<typeof vi.fn> }
|
interface MockedApi { get: ReturnType<typeof vi.fn> }
|
||||||
const mocked = apiClient as unknown as MockedApi
|
const mocked = apiClient as unknown as MockedApi
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { evaluateConditionalLogic } from '@/composables/useConditionalLogic'
|
import { evaluateConditionalLogic } from '@form-schema/composables/useConditionalLogic'
|
||||||
import type { ConditionalLogic } from '@/types/formBuilder'
|
import type { ConditionalLogic } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
describe('evaluateConditionalLogic', () => {
|
describe('evaluateConditionalLogic', () => {
|
||||||
it('returns true when logic is null or undefined', () => {
|
it('returns true when logic is null or undefined', () => {
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import { mount } from '@vue/test-utils'
|
import { mount } from '@vue/test-utils'
|
||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import DuplicateSubmissionHint from '@/components/public-form/DuplicateSubmissionHint.vue'
|
import DuplicateSubmissionHint from '@/components/public-form/DuplicateSubmissionHint.vue'
|
||||||
import type { PublicFormSubmissionDuplicate } from '@/types/formBuilder'
|
import type { PublicFormSubmissionDuplicate } from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
function mountHint(data: PublicFormSubmissionDuplicate | null) {
|
function mountHint(data: PublicFormSubmissionDuplicate | null) {
|
||||||
return mount(DuplicateSubmissionHint, {
|
return mount(DuplicateSubmissionHint, {
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
import { describe, expect, it } from 'vitest'
|
import { describe, expect, it } from 'vitest'
|
||||||
import { formatFieldValue } from '@/composables/formatFieldValue'
|
import { formatFieldValue } from '@form-schema/composables/formatFieldValue'
|
||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||||
import type {
|
import type {
|
||||||
AvailableTag,
|
AvailableTag,
|
||||||
PublicFormField,
|
PublicFormField,
|
||||||
PublicFormSectionOption,
|
PublicFormSectionOption,
|
||||||
PublicFormTimeSlot,
|
PublicFormTimeSlot,
|
||||||
} from '@/types/formBuilder'
|
} from '@form-schema/types/formBuilder'
|
||||||
|
|
||||||
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -40,6 +40,12 @@
|
|||||||
"@validators": [
|
"@validators": [
|
||||||
"./src/@core/utils/validators"
|
"./src/@core/utils/validators"
|
||||||
],
|
],
|
||||||
|
"@form-schema/*": [
|
||||||
|
"../../packages/form-schema/src/*"
|
||||||
|
],
|
||||||
|
"vue": [
|
||||||
|
"./node_modules/vue"
|
||||||
|
],
|
||||||
},
|
},
|
||||||
"lib": [
|
"lib": [
|
||||||
"esnext",
|
"esnext",
|
||||||
@@ -63,7 +69,8 @@
|
|||||||
"./src/**/*.vue",
|
"./src/**/*.vue",
|
||||||
"./themeConfig.ts",
|
"./themeConfig.ts",
|
||||||
"./auto-imports.d.ts",
|
"./auto-imports.d.ts",
|
||||||
"./components.d.ts"
|
"./components.d.ts",
|
||||||
|
"../../packages/form-schema/src/**/*"
|
||||||
],
|
],
|
||||||
"exclude": [
|
"exclude": [
|
||||||
"./dist",
|
"./dist",
|
||||||
|
|||||||
@@ -84,6 +84,7 @@ export default defineConfig({
|
|||||||
'@images': fileURLToPath(new URL('./src/assets/images/', import.meta.url)),
|
'@images': fileURLToPath(new URL('./src/assets/images/', import.meta.url)),
|
||||||
'@styles': fileURLToPath(new URL('./src/assets/styles/', import.meta.url)),
|
'@styles': fileURLToPath(new URL('./src/assets/styles/', import.meta.url)),
|
||||||
'@configured-variables': fileURLToPath(new URL('./src/assets/styles/variables/_template.scss', import.meta.url)),
|
'@configured-variables': fileURLToPath(new URL('./src/assets/styles/variables/_template.scss', import.meta.url)),
|
||||||
|
'@form-schema': fileURLToPath(new URL('../../packages/form-schema/src', import.meta.url)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
server: {
|
server: {
|
||||||
|
|||||||
@@ -23,6 +23,7 @@ export default defineConfig({
|
|||||||
'@images': fileURLToPath(new URL('./src/assets/images/', import.meta.url)),
|
'@images': fileURLToPath(new URL('./src/assets/images/', import.meta.url)),
|
||||||
'@styles': fileURLToPath(new URL('./src/assets/styles/', import.meta.url)),
|
'@styles': fileURLToPath(new URL('./src/assets/styles/', import.meta.url)),
|
||||||
'@validators': fileURLToPath(new URL('./src/@core/utils/validators', import.meta.url)),
|
'@validators': fileURLToPath(new URL('./src/@core/utils/validators', import.meta.url)),
|
||||||
|
'@form-schema': fileURLToPath(new URL('../../packages/form-schema/src', import.meta.url)),
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
test: {
|
test: {
|
||||||
|
|||||||
19
packages/form-schema/README.md
Normal file
19
packages/form-schema/README.md
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
# @form-schema
|
||||||
|
|
||||||
|
Shared schema contract and schema-driven behaviors for Crewli form rendering. Consumed by apps/portal (public submission) and apps/app (organizer builder + submissions review).
|
||||||
|
|
||||||
|
## What lives here
|
||||||
|
|
||||||
|
- `types/` — TypeScript types and enum constants mirroring backend `FormBuilder` enums and `PublicForm(Schema|Submission)Resource`
|
||||||
|
- `utils/formValidation.ts` — validation rule builders driven by `FormFieldValidationRules`
|
||||||
|
- `composables/useConditionalLogic.ts` — evaluate `conditional_logic.show_when` against field values
|
||||||
|
- `composables/useFormSteps.ts` — step navigation logic for multi-step forms
|
||||||
|
- `composables/formatFieldValue.ts` — render a stored submission value as a human-readable string
|
||||||
|
|
||||||
|
## What does NOT live here
|
||||||
|
|
||||||
|
Vue components. Each app renders its own UI. Portal has full-fidelity submit components; app has builder-preview and submissions-review components. Sharing renderers would couple the two apps' visual styles, which we explicitly want to avoid.
|
||||||
|
|
||||||
|
## Contract stability
|
||||||
|
|
||||||
|
This is an alias-only shared directory inside the monorepo — no npm package, no semver. Breaking changes (new field type, new validation key, new conditional operator) require updating both apps in the same PR. TypeScript will flag missing cases in dispatchers.
|
||||||
@@ -1,10 +1,10 @@
|
|||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '../types/formBuilder'
|
||||||
import type {
|
import type {
|
||||||
PublicFormField,
|
PublicFormField,
|
||||||
PublicFormSectionOption,
|
PublicFormSectionOption,
|
||||||
PublicFormTimeSlot,
|
PublicFormTimeSlot,
|
||||||
SectionPriorityValue,
|
SectionPriorityValue,
|
||||||
} from '@/types/formBuilder'
|
} from '../types/formBuilder'
|
||||||
|
|
||||||
const EMPTY = '—'
|
const EMPTY = '—'
|
||||||
const LOADING = 'Laden…'
|
const LOADING = 'Laden…'
|
||||||
@@ -4,7 +4,7 @@ import type {
|
|||||||
ConditionalOperator,
|
ConditionalOperator,
|
||||||
ConditionalRule,
|
ConditionalRule,
|
||||||
FormValues,
|
FormValues,
|
||||||
} from '@/types/formBuilder'
|
} from '../types/formBuilder'
|
||||||
|
|
||||||
function isEmptyValue(v: unknown): boolean {
|
function isEmptyValue(v: unknown): boolean {
|
||||||
if (v === null || v === undefined) return true
|
if (v === null || v === undefined) return true
|
||||||
@@ -1,9 +1,9 @@
|
|||||||
import { computed } from 'vue'
|
import { computed } from 'vue'
|
||||||
import type { ComputedRef, Ref } from 'vue'
|
import type { ComputedRef, Ref } from 'vue'
|
||||||
import { evaluateConditionalLogic } from '@/composables/useConditionalLogic'
|
import { evaluateConditionalLogic } from './useConditionalLogic'
|
||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '../types/formBuilder'
|
||||||
import type { FormValues, PublicFormField, PublicFormSchema } from '@/types/formBuilder'
|
import type { FormValues, PublicFormField, PublicFormSchema } from '../types/formBuilder'
|
||||||
import { isFieldValueEmpty } from '@/utils/formValidation'
|
import { isFieldValueEmpty } from '../utils/formValidation'
|
||||||
|
|
||||||
export type StepKind = 'submitter' | 'section' | 'heading_group' | 'flat' | 'review'
|
export type StepKind = 'submitter' | 'section' | 'heading_group' | 'flat' | 'review'
|
||||||
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
import { emailValidator, regexValidator, requiredValidator, urlValidator } from '@core/utils/validators'
|
import { emailValidator, regexValidator, requiredValidator, urlValidator } from '@core/utils/validators'
|
||||||
import { FormFieldType } from '@/types/formBuilder'
|
import { FormFieldType } from '../types/formBuilder'
|
||||||
import type { PublicFormField } from '@/types/formBuilder'
|
import type { PublicFormField } from '../types/formBuilder'
|
||||||
|
|
||||||
export type Validator = (value: unknown) => true | string
|
export type Validator = (value: unknown) => true | string
|
||||||
|
|
||||||
Reference in New Issue
Block a user