Files
QuantEngineByItz/oms-wms-erp/PHASE3-STEP1-TYPED-FIELDS-COMPLETE.md
T
kjh2064 b34b0dd7d6
Validators (Pushes and Pull Requests) / UI & Storage Validation (pull_request) Failing after 12s
Validators (Pushes and Pull Requests) / Database & Schema Validation (pull_request) Successful in 13s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (pull_request) Failing after 28s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (pull_request) Has been skipped
Validators (Pushes and Pull Requests) / CI Workflow Lint (pull_request) Failing after 10s
Validators (Pushes and Pull Requests) / Security & Secrets (pull_request) Successful in 12s
Validators (Pushes and Pull Requests) / Notify PR Results (pull_request) Successful in 2s
Frontend CI Pipeline / ci-frontend-8-steps (pull_request) Failing after 2m50s
Add OMS WMS ERP platform
2026-07-27 00:45:39 +09:00

15 KiB

Phase 3 Step 1: Complete Remaining Typed Fields (7 Components)

Status: 🚀 START
Phase: 3 / 11
Step: 1 / 4
Target Completion: 2026-08-28 (2 days)
Overall Phase 3: 2 weeks (2026-08-27 to 2026-09-10)


Overview

Phase 3 Goal: Build Smart Components & Domain Fields layers

Step 1 Focus: Complete Typed Fields layer (12/12 components total)

  • Phase 2: 5 completed (TextField, DateField, CurrencyField, SelectField, StatusField)
  • 🔄 Phase 3 Step 1: 7 remaining (NumberField, PercentageField, PhoneField, EmailField, URLField, TextareaField, CheckboxField)

After Step 1: Full Typed Fields layer ready for Domain Fields (Step 2)


The 7 Remaining Typed Fields

1. NumberField — Numeric input with min/max validation

<template>
  <div class="form-group">
    <label v-if="label">{{ label }}</label>
    <input
      type="number"
      :value="modelValue"
      :min="minValue"
      :max="maxValue"
      :step="step"
      @input="$emit('update:modelValue', Number($event.target.value))"
    />
  </div>
</template>

<script setup lang="ts">
interface Props {
  modelValue: number
  label?: string
  minValue?: number
  maxValue?: number
  step?: number // default: 1
  disabled?: boolean
  required?: boolean
  errorMessage?: string
}

withDefaults(defineProps<Props>(), {
  step: 1
})

defineEmits<{
  'update:modelValue': [value: number]
}>()
</script>

Validation Rules:

  • required: number cannot be empty
  • min: value >= minValue
  • max: value <= maxValue
  • integer: no decimals (if step=1)

Use Cases: Quantity, Age, Count


2. PercentageField — Percentage input (0-100)

<template>
  <div class="input-group">
    <input
      type="number"
      :value="modelValue"
      min="0"
      max="100"
      step="0.01"
      @input="$emit('update:modelValue', Number($event.target.value))"
    />
    <span class="input-group-text">%</span>
  </div>
</template>

<script setup lang="ts">
interface Props {
  modelValue: number // 0-100
  label?: string
  disabled?: boolean
  required?: boolean
  decimals?: number // default: 2
}

withDefaults(defineProps<Props>(), {
  decimals: 2
})

defineEmits<{
  'update:modelValue': [value: number]
}>()
</script>

Validation Rules:

  • required
  • min: 0
  • max: 100
  • precision: decimal places

Use Cases: Discount %, Markup %, Tax Rate


3. PhoneField — Phone number with international format

<template>
  <div class="form-group">
    <label v-if="label">{{ label }}</label>
    <input
      type="tel"
      :value="displayValue"
      placeholder="+82 10 1234 5678"
      @input="handleInput"
    />
  </div>
</template>

<script setup lang="ts">
import { ref, computed } from 'vue'

interface Props {
  modelValue: string
  label?: string
  countryCode?: string // default: 'KR'
  disabled?: boolean
  required?: boolean
}

const props = withDefaults(defineProps<Props>(), {
  countryCode: 'KR'
})

defineEmits<{
  'update:modelValue': [value: string]
}>()

const formatPhone = (value: string) => {
  // Format: +82 10 1234 5678 (Korean)
  // Remove non-digits
  const digits = value.replace(/\D/g, '')
  
  if (digits.length <= 2) return digits
  if (digits.length <= 6) return `+${digits.slice(0, 2)} ${digits.slice(2)}`
  return `+${digits.slice(0, 2)} ${digits.slice(2, 4)} ${digits.slice(4, 8)} ${digits.slice(8)}`
}

const displayValue = computed(() => formatPhone(props.modelValue))

const handleInput = (e: Event) => {
  const value = (e.target as HTMLInputElement).value
  // Store only digits
  const digits = value.replace(/\D/g, '')
  emit('update:modelValue', digits)
}
</script>

Validation Rules:

  • required
  • length: 10-15 digits
  • pattern: valid phone format
  • country-specific (KR, US, JP, etc.)

Use Cases: Customer Phone, Supplier Contact


4. EmailField — Email input with validation

<template>
  <div class="form-group">
    <label v-if="label">{{ label }}</label>
    <input
      type="email"
      :value="modelValue"
      placeholder="user@example.com"
      @input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
      @blur="validateEmail"
    />
  </div>
</template>

<script setup lang="ts">
interface Props {
  modelValue: string
  label?: string
  disabled?: boolean
  required?: boolean
  helpText?: string
  errorMessage?: string
}

defineProps<Props>()

defineEmits<{
  'update:modelValue': [value: string]
  blur: []
}>()

const validateEmail = (email: string) => {
  const regex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
  return regex.test(email)
}
</script>

Validation Rules:

  • required
  • email: valid email format
  • length: max 254 chars (RFC 5321)

Use Cases: User Email, Customer Email


5. URLField — URL input with validation

<template>
  <div class="form-group">
    <label v-if="label">{{ label }}</label>
    <input
      type="url"
      :value="modelValue"
      placeholder="https://example.com"
      @input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
    />
  </div>
</template>

<script setup lang="ts">
interface Props {
  modelValue: string
  label?: string
  disabled?: boolean
  required?: boolean
  protocol?: string // default: 'https'
  errorMessage?: string
}

withDefaults(defineProps<Props>(), {
  protocol: 'https'
})

defineEmits<{
  'update:modelValue': [value: string]
}>()

const validateURL = (url: string) => {
  try {
    new URL(url)
    return true
  } catch {
    return false
  }
}
</script>

Validation Rules:

  • required
  • url: valid URL format
  • protocol: https, http, ftp
  • length: max 2048 chars

Use Cases: Website URL, API Endpoint


6. TextareaField — Multi-line text input

<template>
  <div class="form-group">
    <label v-if="label">{{ label }}</label>
    <textarea
      :value="modelValue"
      :placeholder="placeholder"
      :rows="rows"
      :maxlength="maxLength"
      :disabled="disabled"
      @input="$emit('update:modelValue', ($event.target as HTMLTextAreaElement).value)"
      @blur="$emit('blur')"
    />
    <small v-if="showCounter" class="form-text">
      {{ modelValue.length }} / {{ maxLength }}
    </small>
  </div>
</template>

<script setup lang="ts">
interface Props {
  modelValue: string
  label?: string
  placeholder?: string
  rows?: number // default: 4
  maxLength?: number // default: 1000
  disabled?: boolean
  required?: boolean
  showCounter?: boolean // default: true
  helpText?: string
  errorMessage?: string
}

withDefaults(defineProps<Props>(), {
  rows: 4,
  maxLength: 1000,
  showCounter: true
})

defineEmits<{
  'update:modelValue': [value: string]
  blur: []
}>()
</script>

Validation Rules:

  • required
  • minLength: configurable
  • maxLength: default 1000
  • wordCount: optional limit

Use Cases: Description, Notes, Comments, Address


7. CheckboxField — Boolean checkbox with label

<template>
  <div class="form-check">
    <input
      :id="`checkbox-${id}`"
      type="checkbox"
      :checked="modelValue"
      class="form-check-input"
      :disabled="disabled"
      @change="$emit('update:modelValue', ($event.target as HTMLInputElement).checked)"
    />
    <label :for="`checkbox-${id}`" class="form-check-label">
      {{ label }}
      <span v-if="required" class="text-danger">*</span>
    </label>
    <small v-if="helpText" class="form-text d-block">{{ helpText }}</small>
  </div>
</template>

<script setup lang="ts">
import { ref } from 'vue'

interface Props {
  modelValue: boolean
  label: string
  disabled?: boolean
  required?: boolean
  helpText?: string
}

defineProps<Props>()

defineEmits<{
  'update:modelValue': [value: boolean]
}>()

const id = ref(`checkbox-${Math.random().toString(36).slice(2, 11)}`)
</script>

Validation Rules:

  • required: must be checked
  • value: true/false only

Use Cases: Terms & Conditions, Feature Toggles, Agreements


Implementation Strategy

Step 1.1: Create Field Components (2 hours)

# Create each field component
# File: src/components/fields/typed/[Field]/[Field].vue

# NumberField
src/components/fields/typed/NumberField/
  ├── NumberField.vue
  ├── NumberField.stories.ts (5+ stories)
  └── NumberField.spec.ts (unit tests)

# PercentageField
src/components/fields/typed/PercentageField/
  ├── PercentageField.vue
  ├── PercentageField.stories.ts
  └── PercentageField.spec.ts

# ... repeat for PhoneField, EmailField, URLField, TextareaField, CheckboxField

Step 1.2: Validation Rules (1 hour)

// src/composables/useValidation.ts - Add new rules
const rules = {
  number: (min?, max?) => ({...}),
  percentage: () => ({...}),
  phone: (countryCode?) => ({...}),
  email: () => ({...}),
  url: () => ({...}),
  textarea: (minLength?, maxLength?) => ({...}),
  checkbox: () => ({...})
}

Step 1.3: Formatting Functions (1 hour)

// src/composables/useFormatting.ts - Add new formatters
const fmt = useFormatting()
fmt.formatPhone('01012345678') // "+82 10 1234 5678"
fmt.parsePhone('+82 10 1234 5678') // "01012345678"
fmt.formatPercentage(0.75, 2) // "75.00%"

Step 1.4: Storybook Documentation (1 hour)

// Each field: 5+ stories
// Example: NumberField.stories.ts
export default {
  title: 'Fields/Typed/NumberField',
  component: NumberField,
  argTypes: {
    minValue: { control: 'number' },
    maxValue: { control: 'number' },
    step: { control: 'number' }
  }
}

export const Default = {...}
export const WithValidation = {...}
export const WithMinMax = {...}
export const Disabled = {...}
export const Error = {...}

Step 1.5: Unit Tests (1 hour)

# Each field: 10-15 unit tests
# Example: NumberField.spec.ts

describe('NumberField', () => {
  it('validates min/max bounds')
  it('formats decimal places correctly')
  it('emits update events')
  it('handles disable state')
  it('shows error messages')
  // ... etc
})

Implementation Timeline

Task Duration Status
NumberField 30 min Start
PercentageField 30 min After NumberField
PhoneField 40 min After PercentageField
EmailField 30 min After PhoneField
URLField 30 min After EmailField
TextareaField 30 min After URLField
CheckboxField 30 min After TextareaField
Total 4.5 hours Start now

Estimated Completion: 2026-08-28 (end of day)


Validation Matrix

Field Required Email Phone URL Min/Max Pattern Custom
Number
Percent (0-100)
Phone Country-specific
Email Length limit
URL Protocol check
Textarea Word count
Checkbox Acceptance

Story Examples

NumberField Stories (5+)

export const Default = Template.bind({})
Default.args = {
  modelValue: 100,
  label: 'Quantity',
  minValue: 1,
  maxValue: 9999
}

export const WithDecimals = Template.bind({})
WithDecimals.args = {
  modelValue: 19.99,
  label: 'Price',
  step: 0.01,
  minValue: 0
}

export const Disabled = Template.bind({})
Disabled.args = {
  modelValue: 42,
  disabled: true
}

export const Error = Template.bind({})
Error.args = {
  modelValue: 5000,
  errorMessage: 'Quantity cannot exceed 1000'
}

PhoneField Stories (5+)

export const Default = Template.bind({})
Default.args = {
  modelValue: '01012345678',
  label: 'Contact Phone'
}

export const Korea = Template.bind({})
Korea.args = {
  modelValue: '01012345678',
  countryCode: 'KR'
}

export const US = Template.bind({})
US.args = {
  modelValue: '2025551234',
  countryCode: 'US'
}

Testing Strategy

Unit Tests (Per Field: 10-15 tests)

// NumberField.spec.ts example
describe('NumberField', () => {
  it('renders input with correct value')
  it('emits update:modelValue on input')
  it('validates min boundary')
  it('validates max boundary')
  it('handles decimal step')
  it('shows error message when invalid')
  it('respects disabled state')
  it('focuses on click')
  it('handles keyboard input')
  it('handles paste event')
})

Storybook Visual Testing

npm run storybook
# Manually verify: rendering, validation, error states, accessibility

Integration Tests (New)

// tests/integration/typed-fields.spec.ts
describe('Typed Fields Integration', () => {
  it('NumberField with validation rules')
  it('PhoneField with formatting')
  it('EmailField with API lookup')
  it('URLField with protocol validation')
  it('CheckboxField with toggle state')
})

Files to Create

Phase 3 Step 1 Deliverables:

src/components/fields/typed/
├── NumberField/
│   ├── NumberField.vue
│   ├── NumberField.stories.ts
│   └── NumberField.spec.ts
├── PercentageField/
│   ├── PercentageField.vue
│   ├── PercentageField.stories.ts
│   └── PercentageField.spec.ts
├── PhoneField/
│   ├── PhoneField.vue
│   ├── PhoneField.stories.ts
│   └── PhoneField.spec.ts
├── EmailField/
│   ├── EmailField.vue
│   ├── EmailField.stories.ts
│   └── EmailField.spec.ts
├── URLField/
│   ├── URLField.vue
│   ├── URLField.stories.ts
│   └── URLField.spec.ts
├── TextareaField/
│   ├── TextareaField.vue
│   ├── TextareaField.stories.ts
│   └── TextareaField.spec.ts
└── CheckboxField/
    ├── CheckboxField.vue
    ├── CheckboxField.stories.ts
    └── CheckboxField.spec.ts

tests/integration/
└── typed-fields.spec.ts (new integration tests)

src/composables/
├── useValidation.ts (updated: new rules)
└── useFormatting.ts (updated: new formatters)

Documentation/
└── PHASE3-STEP1-COMPLETION.md (completion checklist)

Quality Checklist (Per Field)

  • Component renders correctly
  • Props are typed (no any)
  • Emits work (update:modelValue, blur)
  • Validation rules integrated
  • Formatting applied
  • Error messages display
  • Disabled state respected
  • Accessibility (labels, ARIA, keyboard)
  • 5+ Storybook stories
  • 10+ unit tests passing
  • TypeScript strict: 0 errors
  • No console warnings

Exit Criteria (Step 1 Complete)

  • All 7 fields implemented
  • All 35+ Storybook stories created
  • All 70+ unit tests passing
  • Validation + formatting integrated
  • TypeScript strict: 0 errors
  • npm run verify passes
  • Integration tests added
  • PHASE3-STEP1-COMPLETION.md filled

Next Phase (After Step 1)

Step 2: Smart Components Layer (Domain Fields)

  • OrderLineField (with Product lookup)
  • CustomerField (with Customer lookup)
  • ProductField (with SKU validation)
  • WarehouseField
  • etc.

Ready to implement Phase 3 Step 1?

Estimated time: 4.5 hours
Target completion: 2026-08-28 (end of day)
Proceed with NumberField implementation? → Yes