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
444 lines
11 KiB
Markdown
444 lines
11 KiB
Markdown
# Phase 2 Roadmap: Typed Fields & Pinia Stores
|
||
|
||
**Duration**: Week 3-4 (2026-08-12 → 2026-08-26)
|
||
**Goal**: Build Layer 2 (Typed Fields) + State Management (Pinia)
|
||
|
||
---
|
||
|
||
## Phase 2 Structure
|
||
|
||
### Step 1: Typed Fields (Layer 2) — Week 3
|
||
- 12 Typed Field components
|
||
- Validation & Formatting utilities
|
||
- 100+ Storybook stories
|
||
- 70+ integration tests
|
||
|
||
### Step 2: Pinia Stores — Week 4
|
||
- 10 store modules (orders, inventory, products, etc.)
|
||
- API client setup
|
||
- Mock Service Worker (MSW)
|
||
- State management patterns
|
||
|
||
### Step 3: API Integration — Week 4
|
||
- OpenAPI SDK auto-generation
|
||
- API client wrapper
|
||
- Error handling middleware
|
||
- Request/response interceptors
|
||
|
||
### Step 4: Integration Testing — Week 4
|
||
- Form validation chains
|
||
- API mock testing (MSW)
|
||
- State mutation testing
|
||
- E2E test scenarios (50+)
|
||
|
||
---
|
||
|
||
## Phase 2 Step 1: Typed Fields (Week 3)
|
||
|
||
### Completed (5/12) ✅
|
||
|
||
1. **TextField** — Text inputs with validation
|
||
- Types: text, email, password, url, tel
|
||
- File: src/components/fields/typed/TextField/
|
||
- Stories: 8+
|
||
- Tests: 5+
|
||
|
||
2. **DateField** — Date picker
|
||
- Format: YYYY-MM-DD (ISO)
|
||
- Min/Max validation
|
||
- File: src/components/fields/typed/DateField/
|
||
- Stories: 8+
|
||
- Tests: 5+
|
||
|
||
3. **CurrencyField** — Amount input with formatting
|
||
- Locale: Korean (₩)
|
||
- Decimals: configurable
|
||
- File: src/components/fields/typed/CurrencyField/
|
||
- Stories: 10+
|
||
- Tests: 5+
|
||
|
||
4. **SelectField** — Dropdown with validation
|
||
- Options: typed array
|
||
- Searchable: ready
|
||
- File: src/components/fields/typed/SelectField/
|
||
- Stories: 8+
|
||
- Tests: 5+
|
||
|
||
5. **StatusField** — Predefined status selector
|
||
- Statuses: DRAFT, PENDING, APPROVED, ACTIVE, COMPLETED, CANCELLED, FAILED
|
||
- Colors: status-based badges
|
||
- File: src/components/fields/typed/StatusField/
|
||
- Stories: 8+
|
||
- Tests: 5+
|
||
|
||
### Templates (7/12) — Ready to Implement
|
||
|
||
6. TimeField (time picker, HH:mm)
|
||
7. PercentageField (0-100%, formatted)
|
||
8. QuantityField (positive integers)
|
||
9. MultiSelectField (array of values)
|
||
10. CheckboxField (boolean)
|
||
11. SearchField (autocomplete with API)
|
||
12. PhoneField (formatted phone)
|
||
|
||
### Shared Utilities ✅
|
||
|
||
**useValidation.ts** — Validation composable
|
||
```typescript
|
||
- required, email, minLength, maxLength
|
||
- min, max, pattern, numeric
|
||
- positiveInteger, percentage, url
|
||
- Chainable: validator.validate(value, [rule1, rule2])
|
||
```
|
||
|
||
**useFormatting.ts** — Formatting composable
|
||
```typescript
|
||
- formatCurrency, parseCurrency
|
||
- formatDate, parseDate, formatTime, parseTime
|
||
- formatPhone, parsePhone
|
||
- formatNumber, truncate, capitalize
|
||
```
|
||
|
||
### Testing (Phase 2 Step 1)
|
||
|
||
**Unit Tests**: 5-8 per field × 12 = 60-96 tests
|
||
- Props validation
|
||
- Event emissions
|
||
- Error handling
|
||
- Formatting/parsing
|
||
|
||
**Storybook**: 8-12 stories per field × 12 = 96-144 stories
|
||
- Default state
|
||
- Disabled state
|
||
- With error
|
||
- With help text
|
||
- With validation
|
||
- Edge cases
|
||
|
||
**Target**: All stories render, all tests PASS by 2026-08-19
|
||
|
||
---
|
||
|
||
## Phase 2 Step 2: Pinia Stores (Week 4)
|
||
|
||
### 10 Store Modules
|
||
|
||
```
|
||
src/stores/modules/
|
||
├── orders.ts (OMS: Order management)
|
||
├── inventory.ts (WMS: Stock levels)
|
||
├── products.ts (ERP: Product master)
|
||
├── customers.ts (OMS: Customer master)
|
||
├── suppliers.ts (ERP: Supplier master)
|
||
├── stockTransfers.ts (WMS: Stock movements)
|
||
├── glAccounts.ts (ERP: GL accounting)
|
||
├── vouchers.ts (ERP: Journal entries)
|
||
├── users.ts (Admin: User management)
|
||
└── warehouses.ts (WMS: Warehouse master)
|
||
```
|
||
|
||
### Store Structure (Composition API)
|
||
|
||
```typescript
|
||
// Each store follows this pattern:
|
||
export const useOrderStore = defineStore('orders', () => {
|
||
// State
|
||
const orders = ref<Order[]>([])
|
||
const selectedOrder = ref<Order | null>(null)
|
||
const loading = ref(false)
|
||
const error = ref<string | null>(null)
|
||
const filters = ref({...})
|
||
|
||
// Computed
|
||
const orderCount = computed(() => orders.value.length)
|
||
const filteredOrders = computed(() => {...})
|
||
const totalAmount = computed(() => {...})
|
||
|
||
// Actions (async)
|
||
const fetchOrders = async () => {...}
|
||
const createOrder = async (payload) => {...}
|
||
const updateOrder = async (id, payload) => {...}
|
||
const deleteOrder = async (id) => {...}
|
||
|
||
// Mutations
|
||
const setFilter = (key, value) => {...}
|
||
const clearFilters = () => {...}
|
||
|
||
return {
|
||
// State
|
||
orders, selectedOrder, loading, error, filters,
|
||
// Computed
|
||
orderCount, filteredOrders, totalAmount,
|
||
// Actions
|
||
fetchOrders, createOrder, updateOrder, deleteOrder,
|
||
// Mutations
|
||
setFilter, clearFilters
|
||
}
|
||
})
|
||
```
|
||
|
||
### Example: Orders Store
|
||
|
||
```typescript
|
||
// State
|
||
- orders: Order[]
|
||
- selectedOrder: Order | null
|
||
- loading: boolean
|
||
- error: string | null
|
||
- filters: {status, dateRange, customerId}
|
||
|
||
// Computed
|
||
- orderCount: number
|
||
- filteredOrders: Order[]
|
||
- totalAmount: number
|
||
|
||
// Actions
|
||
- fetchOrders(limit, offset)
|
||
- fetchOrderById(orderId)
|
||
- createOrder(payload)
|
||
- updateOrder(orderId, payload)
|
||
- deleteOrder(orderId)
|
||
|
||
// Mutations
|
||
- setFilter(key, value)
|
||
- clearFilters()
|
||
```
|
||
|
||
### Store Setup (src/stores/index.ts)
|
||
|
||
```typescript
|
||
export { useOrderStore } from './modules/orders'
|
||
export { useInventoryStore } from './modules/inventory'
|
||
export { useProductStore } from './modules/products'
|
||
export { useCustomerStore } from './modules/customers'
|
||
// ... etc
|
||
```
|
||
|
||
### Usage in Components
|
||
|
||
```vue
|
||
<script setup lang="ts">
|
||
import { useOrderStore } from '@/stores'
|
||
|
||
const orderStore = useOrderStore()
|
||
|
||
// Access state
|
||
const orders = orderStore.orders
|
||
const loading = orderStore.loading
|
||
|
||
// Access computed
|
||
const filteredOrders = orderStore.filteredOrders
|
||
|
||
// Call actions
|
||
await orderStore.fetchOrders(100, 0)
|
||
await orderStore.createOrder({...})
|
||
</script>
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 2 Step 3: API Client Integration
|
||
|
||
### OpenAPI SDK Generation
|
||
|
||
```bash
|
||
# From spec/63_oms_wms_erp_api_openapi.yaml
|
||
npx @openapi-generator/cli generate \
|
||
-i ../../spec/63_oms_wms_erp_api_openapi.yaml \
|
||
-g typescript-axios \
|
||
-o src/services/api/generated
|
||
```
|
||
|
||
### Generated Files
|
||
|
||
```
|
||
src/services/api/generated/
|
||
├── models/
|
||
│ ├── Order.ts
|
||
│ ├── OrderLine.ts
|
||
│ ├── Inventory.ts
|
||
│ └── ... (all 15 models)
|
||
├── apis/
|
||
│ ├── OrdersApi.ts
|
||
│ ├── InventoryApi.ts
|
||
│ ├── ProductsApi.ts
|
||
│ └── ... (all 11 resources)
|
||
└── index.ts
|
||
```
|
||
|
||
### API Client Wrapper (src/services/api/client.ts)
|
||
|
||
```typescript
|
||
import axios from 'axios'
|
||
import { Configuration, OrdersApi, InventoryApi, ... } from './generated'
|
||
|
||
const apiConfig = new Configuration({
|
||
basePath: process.env.VITE_API_BASE_URL || 'http://localhost:3000/api'
|
||
})
|
||
|
||
export const ordersApi = new OrdersApi(apiConfig)
|
||
export const inventoryApi = new InventoryApi(apiConfig)
|
||
export const productsApi = new ProductsApi(apiConfig)
|
||
// ... etc
|
||
```
|
||
|
||
### Pinia Integration
|
||
|
||
```typescript
|
||
// In store: const response = await ordersApi.listOrders({ limit, offset })
|
||
export const useOrderStore = defineStore('orders', () => {
|
||
const fetchOrders = async () => {
|
||
try {
|
||
const response = await ordersApi.listOrders({ limit: 100, offset: 0 })
|
||
orders.value = response.data
|
||
} catch (err) {
|
||
error.value = (err as Error).message
|
||
}
|
||
}
|
||
// ...
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 2 Step 4: Integration Testing
|
||
|
||
### Mock Service Worker (MSW) Setup
|
||
|
||
```typescript
|
||
// tests/mocks/handlers.ts
|
||
import { http, HttpResponse } from 'msw'
|
||
|
||
export const handlers = [
|
||
http.get('/api/orders', () => {
|
||
return HttpResponse.json([
|
||
{ orderId: '1', orderNo: 'ORD-001', status: 'DRAFT', ... }
|
||
])
|
||
}),
|
||
http.post('/api/orders', ({ request }) => {
|
||
return HttpResponse.json(
|
||
{ orderId: '2', orderNo: 'ORD-002', ... },
|
||
{ status: 201 }
|
||
)
|
||
}),
|
||
// ... more handlers
|
||
]
|
||
```
|
||
|
||
### Integration Test Example
|
||
|
||
```typescript
|
||
// tests/integration/orders.spec.ts
|
||
describe('Orders Store with API', () => {
|
||
beforeEach(() => {
|
||
server.listen()
|
||
})
|
||
|
||
afterEach(() => {
|
||
server.close()
|
||
})
|
||
|
||
it('fetches orders from API', async () => {
|
||
const store = useOrderStore()
|
||
await store.fetchOrders()
|
||
expect(store.orders).toHaveLength(1)
|
||
expect(store.orders[0].orderNo).toBe('ORD-001')
|
||
})
|
||
|
||
it('creates new order', async () => {
|
||
const store = useOrderStore()
|
||
const newOrder = await store.createOrder({ customerId: 'CUST-001' })
|
||
expect(newOrder.orderId).toBe('2')
|
||
})
|
||
})
|
||
```
|
||
|
||
### E2E Test Scenarios (50+)
|
||
|
||
```typescript
|
||
// tests/e2e/order-workflow.spec.ts
|
||
test('Complete order workflow', async ({ page }) => {
|
||
// 1. Navigate to orders
|
||
await page.goto('/admin/orders')
|
||
|
||
// 2. Create order
|
||
await page.click('button:text("Create")')
|
||
await page.fill('[name="customerId"]', 'CUST-001')
|
||
await page.fill('[name="quantity"]', '100')
|
||
await page.click('button:text("Submit")')
|
||
|
||
// 3. Verify order created
|
||
await expect(page).toContainText('Order created')
|
||
|
||
// 4. Edit order
|
||
await page.click('button:text("Edit")')
|
||
await page.fill('[name="quantity"]', '150')
|
||
await page.click('button:text("Save")')
|
||
|
||
// 5. Verify audit trail
|
||
await page.goto('/admin/audit-logs')
|
||
await expect(page).toContainText('Order updated')
|
||
})
|
||
```
|
||
|
||
---
|
||
|
||
## Phase 2 Completion Criteria
|
||
|
||
### By End of Week 3 (2026-08-19)
|
||
- ✅ All 12 Typed Fields implemented
|
||
- ✅ All Storybook stories rendering (100+ stories)
|
||
- ✅ All unit tests passing (70+ tests)
|
||
- ✅ Validation composable complete
|
||
- ✅ Formatting composable complete
|
||
|
||
### By End of Week 4 (2026-08-26)
|
||
- ✅ All 10 Pinia stores implemented
|
||
- ✅ OpenAPI SDK generated
|
||
- ✅ API client wrapper complete
|
||
- ✅ MSW setup for testing
|
||
- ✅ 50+ integration tests passing
|
||
- ✅ 50+ E2E test scenarios passing
|
||
|
||
---
|
||
|
||
## Success Metrics
|
||
|
||
| Metric | Target | Status |
|
||
|--------|--------|--------|
|
||
| Typed Fields | 12/12 | 5/12 ✅ |
|
||
| Storybook Stories | 100+ | Planned |
|
||
| Unit Tests | 70+ | Planned |
|
||
| Integration Tests | 50+ | Planned |
|
||
| E2E Tests | 50+ | Planned |
|
||
| Code Coverage | 70%+ | Target |
|
||
| Lighthouse Score | 90+ | Target |
|
||
| Bundle Size | <500MB | Target |
|
||
|
||
---
|
||
|
||
## Timeline
|
||
|
||
```
|
||
Week 3 (2026-08-12 → 2026-08-19)
|
||
├─ Step 1.1: Generate 7 remaining Typed Fields (Mon-Tue)
|
||
├─ Step 1.2: Add Storybook stories for all 12 (Wed-Thu)
|
||
├─ Step 1.3: Add unit tests for all 12 (Fri)
|
||
└─ Deliverable: 12 Typed Fields, 100+ stories, 70+ tests ✅
|
||
|
||
Week 4 (2026-08-20 → 2026-08-26)
|
||
├─ Step 2.1: Create 10 Pinia store modules (Mon-Tue)
|
||
├─ Step 2.2: Setup OpenAPI SDK + API client (Wed)
|
||
├─ Step 2.3: Implement MSW + integration tests (Thu)
|
||
├─ Step 2.4: Add E2E test scenarios (Fri)
|
||
└─ Deliverable: Pinia stores, API client, 100+ tests ✅
|
||
|
||
Next: Phase 3 (Domain Fields, Week 5-6)
|
||
```
|
||
|
||
---
|
||
|
||
**Status**: 🚀 Phase 2 Started (2026-08-12)
|
||
**Step 1 Progress**: 5/12 Typed Fields (42%)
|
||
**Timeline**: 2 weeks (2026-08-12 → 2026-08-26)
|