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": [
|
||||
"./src/@core/utils/validators"
|
||||
],
|
||||
"@form-schema/*": [
|
||||
"../../packages/form-schema/src/*"
|
||||
],
|
||||
"vue": [
|
||||
"./node_modules/vue"
|
||||
]
|
||||
},
|
||||
"lib": [
|
||||
@@ -63,7 +69,8 @@
|
||||
"./src/**/*.vue",
|
||||
"./themeConfig.ts",
|
||||
"./auto-imports.d.ts",
|
||||
"./components.d.ts"
|
||||
"./components.d.ts",
|
||||
"../../packages/form-schema/src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"./dist",
|
||||
|
||||
@@ -110,6 +110,9 @@ export default defineConfig({
|
||||
import.meta.url,
|
||||
),
|
||||
),
|
||||
"@form-schema": fileURLToPath(
|
||||
new URL("../../packages/form-schema/src", import.meta.url),
|
||||
),
|
||||
},
|
||||
},
|
||||
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 effectScope: UnwrapRef<typeof import('vue')['effectScope']>
|
||||
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 extractErrorBody: UnwrapRef<typeof import('./src/composables/useFormDraft')['extractErrorBody']>
|
||||
readonly extractRetryAfterSeconds: UnwrapRef<typeof import('./src/composables/useFormDraft')['extractRetryAfterSeconds']>
|
||||
readonly formatDate: UnwrapRef<typeof import('./src/@core/utils/formatters')['formatDate']>
|
||||
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 getActivePinia: UnwrapRef<typeof import('pinia')['getActivePinia']>
|
||||
readonly getCurrentInstance: UnwrapRef<typeof import('vue')['getCurrentInstance']>
|
||||
readonly getCurrentScope: UnwrapRef<typeof import('vue')['getCurrentScope']>
|
||||
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 hexToRgb: UnwrapRef<typeof import('./src/@core/utils/colorConverter')['hexToRgb']>
|
||||
readonly ignorableWatch: UnwrapRef<typeof import('@vueuse/core')['ignorableWatch']>
|
||||
@@ -456,14 +453,12 @@ declare module 'vue' {
|
||||
readonly isDefined: UnwrapRef<typeof import('@vueuse/core')['isDefined']>
|
||||
readonly isEmpty: UnwrapRef<typeof import('./src/@core/utils/helpers')['isEmpty']>
|
||||
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 isObject: UnwrapRef<typeof import('./src/@core/utils/helpers')['isObject']>
|
||||
readonly isProxy: UnwrapRef<typeof import('vue')['isProxy']>
|
||||
readonly isReactive: UnwrapRef<typeof import('vue')['isReactive']>
|
||||
readonly isReadonly: UnwrapRef<typeof import('vue')['isReadonly']>
|
||||
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 kFormatter: UnwrapRef<typeof import('./src/@core/utils/formatters')['kFormatter']>
|
||||
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 resolveVuetifyTheme: UnwrapRef<typeof import('./src/@core/utils/vuetify')['resolveVuetifyTheme']>
|
||||
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 setMapStoreSuffix: UnwrapRef<typeof import('pinia')['setMapStoreSuffix']>
|
||||
readonly shallowReactive: UnwrapRef<typeof import('vue')['shallowReactive']>
|
||||
@@ -623,7 +617,6 @@ declare module 'vue' {
|
||||
readonly useFocus: UnwrapRef<typeof import('@vueuse/core')['useFocus']>
|
||||
readonly useFocusWithin: UnwrapRef<typeof import('@vueuse/core')['useFocusWithin']>
|
||||
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 useFullscreen: UnwrapRef<typeof import('@vueuse/core')['useFullscreen']>
|
||||
readonly useGamepad: UnwrapRef<typeof import('@vueuse/core')['useGamepad']>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormSubmissionDuplicate } from '@/types/formBuilder'
|
||||
import type { PublicFormSubmissionDuplicate } from '@form-schema/types/formBuilder'
|
||||
|
||||
const props = defineProps<{
|
||||
data: PublicFormSubmissionDuplicate | null
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
<script setup lang="ts">
|
||||
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
||||
import { usePublicFormToken } from '@/composables/publicFormInjection'
|
||||
import type { PublicFormField, PublicFormTimeSlot } from '@/types/formBuilder'
|
||||
import { getValidatorsForField, runValidators } from '@/utils/formValidation'
|
||||
import type { PublicFormField, PublicFormTimeSlot } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField, runValidators } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { FieldOption, PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField, runValidators } from '@/utils/formValidation'
|
||||
import type { FieldOption, PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField, runValidators } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
|
||||
defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { FieldOption, PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { FieldOption, PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
|
||||
defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { FieldOption, PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { FieldOption, PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -16,9 +16,9 @@ import FieldTagPicker from './FieldTagPicker.vue'
|
||||
import FieldText from './FieldText.vue'
|
||||
import FieldTextarea from './FieldTextarea.vue'
|
||||
import FieldUrl from './FieldUrl.vue'
|
||||
import { evaluateConditionalLogic } from '@/composables/useConditionalLogic'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import type { FormFieldDisplayWidth, FormValues, PublicFormField } from '@/types/formBuilder'
|
||||
import { evaluateConditionalLogic } from '@form-schema/composables/useConditionalLogic'
|
||||
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||
import type { FormFieldDisplayWidth, FormValues, PublicFormField } from '@form-schema/types/formBuilder'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -3,8 +3,8 @@ import draggable from 'vuedraggable'
|
||||
import { useDisplay } from 'vuetify'
|
||||
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
||||
import { usePublicFormToken } from '@/composables/publicFormInjection'
|
||||
import type { PublicFormField, PublicFormSectionOption, SectionPriorityValue } from '@/types/formBuilder'
|
||||
import { getValidatorsForField, runValidators } from '@/utils/formValidation'
|
||||
import type { PublicFormField, PublicFormSectionOption, SectionPriorityValue } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField, runValidators } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { FieldOption, PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { FieldOption, PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { AvailableTag, PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { AvailableTag, PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import { getValidatorsForField } from '@/utils/formValidation'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
import { getValidatorsForField } from '@form-schema/utils/formValidation'
|
||||
|
||||
const props = defineProps<{
|
||||
field: PublicFormField
|
||||
|
||||
@@ -3,16 +3,16 @@ import DuplicateSubmissionHint from './DuplicateSubmissionHint.vue'
|
||||
import IdentityMatchBanner from './IdentityMatchBanner.vue'
|
||||
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
||||
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
||||
import { formatFieldValue } from '@/composables/formatFieldValue'
|
||||
import type { FormStep } from '@/composables/useFormSteps'
|
||||
import { formatFieldValue } from '@form-schema/composables/formatFieldValue'
|
||||
import type { FormStep } from '@form-schema/composables/useFormSteps'
|
||||
import { usePublicFormToken } from '@/composables/publicFormInjection'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||
import type {
|
||||
FormValues,
|
||||
PublicFormField,
|
||||
PublicFormSubmissionDuplicate,
|
||||
PublicFormSubmissionIdentityMatch,
|
||||
} from '@/types/formBuilder'
|
||||
} from '@form-schema/types/formBuilder'
|
||||
|
||||
const props = defineProps<{
|
||||
steps: FormStep[]
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import type { FormErrorCode } from '@/types/formBuilder'
|
||||
import type { FormErrorCode } from '@form-schema/types/formBuilder'
|
||||
|
||||
const props = defineProps<{
|
||||
errorCode?: FormErrorCode | string
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { useDisplay } from 'vuetify'
|
||||
import type { FormStep } from '@/composables/useFormSteps'
|
||||
import type { FormStep } from '@form-schema/composables/useFormSteps'
|
||||
|
||||
const props = defineProps<{
|
||||
steps: FormStep[]
|
||||
|
||||
@@ -9,7 +9,7 @@ import type {
|
||||
SaveDraftBody,
|
||||
StartDraftBody,
|
||||
SubmitBody,
|
||||
} from '@/types/formBuilder'
|
||||
} from '@form-schema/types/formBuilder'
|
||||
|
||||
interface ApiResponse<T> {
|
||||
data: T
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import type { Ref } from 'vue'
|
||||
import { apiClient } from '@/lib/axios'
|
||||
import type { PublicFormSectionOption } from '@/types/formBuilder'
|
||||
import type { PublicFormSectionOption } from '@form-schema/types/formBuilder'
|
||||
|
||||
interface ApiResponse<T> {
|
||||
data: T
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useQuery } from '@tanstack/vue-query'
|
||||
import type { Ref } from 'vue'
|
||||
import { apiClient } from '@/lib/axios'
|
||||
import type { PublicFormTimeSlot } from '@/types/formBuilder'
|
||||
import type { PublicFormTimeSlot } from '@form-schema/types/formBuilder'
|
||||
|
||||
interface ApiResponse<T> {
|
||||
data: T
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
useSaveFormDraft,
|
||||
useSubmitForm,
|
||||
} 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. */
|
||||
export function draftIdempotencyKey(token: string): string {
|
||||
|
||||
@@ -9,11 +9,11 @@ import { extractErrorBody, useFetchPublicFormSchema } from '@/composables/api/us
|
||||
import { usePublicFormSections } from '@/composables/api/usePublicFormSections'
|
||||
import { usePublicFormTimeSlots } from '@/composables/api/usePublicFormTimeSlots'
|
||||
import { useFormDraft } from '@/composables/useFormDraft'
|
||||
import { isStepValid, useFormSteps } from '@/composables/useFormSteps'
|
||||
import { formatFieldValue } from '@/composables/formatFieldValue'
|
||||
import { isStepValid, useFormSteps } from '@form-schema/composables/useFormSteps'
|
||||
import { formatFieldValue } from '@form-schema/composables/formatFieldValue'
|
||||
import { providePublicFormToken } from '@/composables/publicFormInjection'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import type { FormErrorCode, PublicFormField } from '@/types/formBuilder'
|
||||
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||
import type { FormErrorCode, PublicFormField } from '@form-schema/types/formBuilder'
|
||||
|
||||
definePage({
|
||||
name: 'public-form-register',
|
||||
|
||||
@@ -21,8 +21,8 @@ vi.mock('@/composables/publicFormInjection', () => ({
|
||||
}))
|
||||
|
||||
import FieldAvailabilityPicker from '@/components/public-form/FieldAvailabilityPicker.vue'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import type { PublicFormField, PublicFormTimeSlot } from '@/types/formBuilder'
|
||||
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||
import type { PublicFormField, PublicFormTimeSlot } from '@form-schema/types/formBuilder'
|
||||
|
||||
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
||||
return {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import FieldRenderer from '@/components/public-form/FieldRenderer.vue'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||
import type { PublicFormField } from '@form-schema/types/formBuilder'
|
||||
|
||||
function makeField(partial: Partial<PublicFormField>): PublicFormField {
|
||||
return {
|
||||
|
||||
@@ -36,8 +36,8 @@ vi.mock('vuedraggable', () => ({
|
||||
}))
|
||||
|
||||
import FieldSectionPriority from '@/components/public-form/FieldSectionPriority.vue'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import type { PublicFormField, PublicFormSectionOption } from '@/types/formBuilder'
|
||||
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||
import type { PublicFormField, PublicFormSectionOption } from '@form-schema/types/formBuilder'
|
||||
|
||||
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
||||
return {
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import FieldTagPicker from '@/components/public-form/FieldTagPicker.vue'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import type { AvailableTag, PublicFormField } from '@/types/formBuilder'
|
||||
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||
import type { AvailableTag, PublicFormField } from '@form-schema/types/formBuilder'
|
||||
|
||||
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
||||
return {
|
||||
|
||||
@@ -9,7 +9,7 @@ vi.mock('@/lib/axios', () => ({
|
||||
|
||||
import { apiClient } from '@/lib/axios'
|
||||
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> }
|
||||
const mocked = apiClient as unknown as MockedApi
|
||||
|
||||
@@ -9,7 +9,7 @@ vi.mock('@/lib/axios', () => ({
|
||||
|
||||
import { apiClient } from '@/lib/axios'
|
||||
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> }
|
||||
const mocked = apiClient as unknown as MockedApi
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { evaluateConditionalLogic } from '@/composables/useConditionalLogic'
|
||||
import type { ConditionalLogic } from '@/types/formBuilder'
|
||||
import { evaluateConditionalLogic } from '@form-schema/composables/useConditionalLogic'
|
||||
import type { ConditionalLogic } from '@form-schema/types/formBuilder'
|
||||
|
||||
describe('evaluateConditionalLogic', () => {
|
||||
it('returns true when logic is null or undefined', () => {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { mount } from '@vue/test-utils'
|
||||
import { describe, expect, it } from 'vitest'
|
||||
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) {
|
||||
return mount(DuplicateSubmissionHint, {
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
import { describe, expect, it } from 'vitest'
|
||||
import { formatFieldValue } from '@/composables/formatFieldValue'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import { formatFieldValue } from '@form-schema/composables/formatFieldValue'
|
||||
import { FormFieldType } from '@form-schema/types/formBuilder'
|
||||
import type {
|
||||
AvailableTag,
|
||||
PublicFormField,
|
||||
PublicFormSectionOption,
|
||||
PublicFormTimeSlot,
|
||||
} from '@/types/formBuilder'
|
||||
} from '@form-schema/types/formBuilder'
|
||||
|
||||
function field(partial: Partial<PublicFormField> = {}): PublicFormField {
|
||||
return {
|
||||
|
||||
@@ -40,6 +40,12 @@
|
||||
"@validators": [
|
||||
"./src/@core/utils/validators"
|
||||
],
|
||||
"@form-schema/*": [
|
||||
"../../packages/form-schema/src/*"
|
||||
],
|
||||
"vue": [
|
||||
"./node_modules/vue"
|
||||
],
|
||||
},
|
||||
"lib": [
|
||||
"esnext",
|
||||
@@ -63,7 +69,8 @@
|
||||
"./src/**/*.vue",
|
||||
"./themeConfig.ts",
|
||||
"./auto-imports.d.ts",
|
||||
"./components.d.ts"
|
||||
"./components.d.ts",
|
||||
"../../packages/form-schema/src/**/*"
|
||||
],
|
||||
"exclude": [
|
||||
"./dist",
|
||||
|
||||
@@ -84,6 +84,7 @@ export default defineConfig({
|
||||
'@images': fileURLToPath(new URL('./src/assets/images/', 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)),
|
||||
'@form-schema': fileURLToPath(new URL('../../packages/form-schema/src', import.meta.url)),
|
||||
},
|
||||
},
|
||||
server: {
|
||||
|
||||
@@ -23,6 +23,7 @@ export default defineConfig({
|
||||
'@images': fileURLToPath(new URL('./src/assets/images/', import.meta.url)),
|
||||
'@styles': fileURLToPath(new URL('./src/assets/styles/', 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: {
|
||||
|
||||
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 {
|
||||
PublicFormField,
|
||||
PublicFormSectionOption,
|
||||
PublicFormTimeSlot,
|
||||
SectionPriorityValue,
|
||||
} from '@/types/formBuilder'
|
||||
} from '../types/formBuilder'
|
||||
|
||||
const EMPTY = '—'
|
||||
const LOADING = 'Laden…'
|
||||
@@ -4,7 +4,7 @@ import type {
|
||||
ConditionalOperator,
|
||||
ConditionalRule,
|
||||
FormValues,
|
||||
} from '@/types/formBuilder'
|
||||
} from '../types/formBuilder'
|
||||
|
||||
function isEmptyValue(v: unknown): boolean {
|
||||
if (v === null || v === undefined) return true
|
||||
@@ -1,9 +1,9 @@
|
||||
import { computed } from 'vue'
|
||||
import type { ComputedRef, Ref } from 'vue'
|
||||
import { evaluateConditionalLogic } from '@/composables/useConditionalLogic'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import type { FormValues, PublicFormField, PublicFormSchema } from '@/types/formBuilder'
|
||||
import { isFieldValueEmpty } from '@/utils/formValidation'
|
||||
import { evaluateConditionalLogic } from './useConditionalLogic'
|
||||
import { FormFieldType } from '../types/formBuilder'
|
||||
import type { FormValues, PublicFormField, PublicFormSchema } from '../types/formBuilder'
|
||||
import { isFieldValueEmpty } from '../utils/formValidation'
|
||||
|
||||
export type StepKind = 'submitter' | 'section' | 'heading_group' | 'flat' | 'review'
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { emailValidator, regexValidator, requiredValidator, urlValidator } from '@core/utils/validators'
|
||||
import { FormFieldType } from '@/types/formBuilder'
|
||||
import type { PublicFormField } from '@/types/formBuilder'
|
||||
import { FormFieldType } from '../types/formBuilder'
|
||||
import type { PublicFormField } from '../types/formBuilder'
|
||||
|
||||
export type Validator = (value: unknown) => true | string
|
||||
|
||||
Reference in New Issue
Block a user