Skip to main content
All Examples

Job Application

Multi-step form with async email validation, cross-field date constraints, file uploads, and per-step error indicators.

Validation
Multi-step
UI Components
/
import { Effect, Random, Schema as S } from 'effect'
import { Calendar, Runtime, Ui } from 'foldkit'

import { Message } from './message'
import { Model, NotSubmitted } from './model'
import {
  Attachments,
  CoverLetter,
  Education,
  PersonalInfo,
  Skills,
  WorkHistory,
} from './step'
import { update } from './update'
import { view } from './view'

// FLAGS

export const Flags = S.Struct({
  today: Calendar.CalendarDate,
  initialWorkHistoryEntryId: S.String,
  initialEducationEntryId: S.String,
  initialSkillsEntryId: S.String,
})
export type Flags = typeof Flags.Type

export const flags: Effect.Effect<Flags> = Effect.gen(function* () {
  const today = yield* Calendar.today.local
  const initialWorkHistoryEntryId = yield* Random.nextUUIDv4
  const initialEducationEntryId = yield* Random.nextUUIDv4
  const initialSkillsEntryId = yield* Random.nextUUIDv4
  return {
    today,
    initialWorkHistoryEntryId,
    initialEducationEntryId,
    initialSkillsEntryId,
  }
})

// INIT

export const init: Runtime.ProgramInit<Model, Message, Flags> = ({
  today,
  initialWorkHistoryEntryId,
  initialEducationEntryId,
  initialSkillsEntryId,
}) => [
  {
    currentStep: 'PersonalInfo',
    personalInfo: PersonalInfo.init(today),
    workHistory: WorkHistory.init(today, initialWorkHistoryEntryId),
    education: Education.init(today, initialEducationEntryId),
    skills: Skills.init(initialSkillsEntryId),
    coverLetter: CoverLetter.init(),
    attachments: Attachments.init(),
    isPreviewVisible: false,
    submission: NotSubmitted(),
    stepMenu: Ui.Menu.init({ id: 'step-menu' }),
  },
  [],
]

export { Message, Model, update, view }