Files
QuantEngineByItz/oms-wms-erp/DEVELOPMENT.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

614 lines
11 KiB
Markdown

# Development Guide
## Quick Start
### 1. First-Time Setup
```bash
# Clone repository
git clone <repo-url>
cd oms-wms-erp
# Install dependencies (once)
npm install
# Or use Makefile
make install
```
### 2. Start Development
**Option A: Using npm**
```bash
# Terminal 1: Start Vite dev server
npm run dev
# → http://localhost:5173
# Terminal 2: Start Storybook
npm run storybook
# → http://localhost:6006
```
**Option B: Using Makefile**
```bash
# Terminal 1
make dev
# Terminal 2
make storybook
```
### 3. Quality Checks
```bash
# Before committing, run:
make verify
# Or individual checks:
npm run lint # ESLint + Prettier
npm run type-check # TypeScript
npm run test:unit # Unit tests
npm run build # Production build
```
---
## Project Structure
```
src/
├── components/
│ ├── primitives/ # Layer 1: UI building blocks
│ │ ├── Button/
│ │ ├── Input/
│ │ ├── Select/
│ │ └── ... (30 total)
│ ├── fields/
│ │ ├── typed/ # Layer 2: Type-safe inputs
│ │ │ ├── TextField/
│ │ │ └── ... (12 total)
│ │ └── domain/ # Layer 3: Business-specific
│ │ ├── OrderLineField/
│ │ └── ... (12 total)
│ └── composites/ # Layer 4: Full workflows
│ ├── Order/
│ └── ... (11 total)
├── stores/ # Pinia state management
│ └── modules/
│ ├── orders.ts
│ ├── inventory.ts
│ └── ... (10 total)
├── views/ # Page components
│ ├── Home.vue
│ ├── Order/OrderList.vue
│ ├── Order/OrderForm.vue
│ └── ...
├── services/ # API client, validators, formatters
│ ├── api/
│ ├── validators/
│ └── formatters/
├── router.ts # Vue Router configuration
├── App.vue # Root component
└── main.ts # Entry point
```
---
## Creating New Components
### 1. Primitive Component (Layer 1)
**Example: Create TextareaBase**
```bash
# Create folder
mkdir -p src/components/primitives/Textarea
# Create component files
cat > src/components/primitives/Textarea/TextareaBase.vue << 'EOF'
<template>
<div class="mb-3">
<label v-if="label" :for="id" class="form-label">
{{ label }}
<span v-if="required" class="text-danger">*</span>
</label>
<textarea
:id="id"
:value="modelValue"
:placeholder="placeholder"
:rows="rows"
:disabled="disabled"
:class="['form-control', { 'is-invalid': errorMessage }]"
@input="$emit('update:modelValue', ($event.target as HTMLTextAreaElement).value)"
@blur="$emit('blur')"
/>
<div v-if="errorMessage" :id="`error-${id}`" class="invalid-feedback d-block">
{{ errorMessage }}
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue'
interface Props {
modelValue: string
label?: string
placeholder?: string
rows?: number
disabled?: boolean
required?: boolean
errorMessage?: string
}
withDefaults(defineProps<Props>(), {
rows: 4
})
const id = ref(`textarea-${Math.random().toString(36).slice(2, 11)}`)
defineEmits<{
'update:modelValue': [value: string]
blur: []
}>()
</script>
EOF
# Create Storybook stories
cat > src/components/primitives/Textarea/TextareaBase.stories.ts << 'EOF'
import type { Meta, StoryObj } from '@storybook/vue3'
import TextareaBase from './TextareaBase.vue'
const meta = {
title: 'Primitives/Textarea',
component: TextareaBase,
tags: ['autodocs']
} satisfies Meta<typeof TextareaBase>
export default meta
type Story = StoryObj<typeof meta>
export const Default: Story = {
args: {
label: 'Comments',
placeholder: 'Enter your comments here...',
rows: 4
}
}
export const Error: Story = {
args: {
label: 'Comments',
errorMessage: 'This field is required'
}
}
EOF
# Create unit tests
cat > src/components/primitives/Textarea/TextareaBase.spec.ts << 'EOF'
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
import TextareaBase from './TextareaBase.vue'
describe('TextareaBase', () => {
it('renders with label', () => {
const wrapper = mount(TextareaBase, {
props: {
label: 'Comments'
}
})
expect(wrapper.text()).toContain('Comments')
})
it('emits update:modelValue on input', async () => {
const wrapper = mount(TextareaBase, {
props: {
modelValue: ''
}
})
await wrapper.find('textarea').setValue('test text')
expect(wrapper.emitted('update:modelValue')).toBeDefined()
})
})
EOF
```
### 2. Typed Field Component (Layer 2)
**Example: Create TimeField**
```bash
mkdir -p src/components/fields/typed/TimeField
# Create TimeField.vue (similar to DateField but for time input)
# Create TimeField.stories.ts (time picker stories)
# Create TimeField.spec.ts (validation tests)
```
### 3. Domain Field Component (Layer 3)
**Example: Create PriceField**
```bash
mkdir -p src/components/fields/domain/PriceField
# Create PriceField.vue (extends CurrencyField with KIS tick rules)
# Create PriceField.stories.ts (price input with suggestions)
# Create PriceField.spec.ts (tick rule validation)
```
### 4. Business Composite Component (Layer 4)
**Example: Create CustomerForm**
```bash
mkdir -p src/components/composites/Customer
# Create CustomerForm.vue (full CRUD form)
# Create CustomerForm.stories.ts (create/edit modes)
# Create CustomerForm.spec.ts (form submission, validation)
```
---
## Component Best Practices
### Props & Events
```typescript
// Good: Strongly typed
interface Props {
modelValue: string
label?: string
disabled?: boolean
errorMessage?: string
}
// ❌ Bad: Loose typing
props: {
value: String, // No type definition
options: Array // Unclear structure
}
```
### Slots
```vue
<template>
<!-- Named slots for flexibility -->
<div class="card">
<div class="card-header">
<slot name="header">Default Header</slot>
</div>
<div class="card-body">
<slot />
</div>
<div class="card-footer">
<slot name="footer" />
</div>
</div>
</template>
```
### Accessibility
```vue
<template>
<!-- Always use labels -->
<label :for="id" class="form-label">Name</label>
<input :id="id" :aria-describedby="errorId" />
<!-- Describe error messages -->
<div :id="errorId" v-if="error" class="invalid-feedback">
{{ error }}
</div>
</template>
```
### Testing
```typescript
import { describe, it, expect } from 'vitest'
import { mount } from '@vue/test-utils'
describe('MyComponent', () => {
it('renders with props', () => {
const wrapper = mount(MyComponent, {
props: { label: 'Test' }
})
expect(wrapper.find('label').text()).toBe('Test')
})
it('emits events', async () => {
const wrapper = mount(MyComponent)
await wrapper.find('button').trigger('click')
expect(wrapper.emitted('click')).toHaveLength(1)
})
})
```
---
## Storybook Stories
### Basic Story
```typescript
import type { Meta, StoryObj } from '@storybook/vue3'
import MyComponent from './MyComponent.vue'
const meta = {
title: 'Primitives/MyComponent',
component: MyComponent,
tags: ['autodocs'],
argTypes: {
variant: {
control: 'select',
options: ['primary', 'secondary', 'danger']
},
size: {
control: 'select',
options: ['sm', 'md', 'lg']
}
}
} satisfies Meta<typeof MyComponent>
export default meta
type Story = StoryObj<typeof meta>
export const Primary: Story = {
args: {
variant: 'primary',
size: 'md'
},
slots: {
default: 'Click me'
}
}
export const Secondary: Story = {
args: {
variant: 'secondary'
}
}
```
### Story with Controls
```typescript
export const Interactive: Story = {
args: {
label: 'Username',
placeholder: 'Enter username',
disabled: false,
required: true
},
argTypes: {
disabled: { control: 'boolean' },
required: { control: 'boolean' }
}
}
```
---
## Running Tests
### Unit Tests
```bash
# Run all tests
npm run test:unit
# Run specific test
npm run test:unit -- ButtonBase.spec.ts
# Watch mode (re-run on file changes)
npm run test:unit -- --watch
# Coverage report
npm run test:unit -- --coverage
```
### Integration Tests
```bash
# Run with Vitest + MSW mocks
npm run test:integration
```
### E2E Tests
```bash
# Run Playwright tests
npm run test:e2e
# Run specific test
npm run test:e2e -- order-crud.spec.ts
# Debug mode
npm run test:e2e -- --debug
# UI mode (interactive)
npm run test:e2e -- --ui
```
---
## Code Quality
### ESLint
```bash
# Check all files
npm run lint
# Fix automatically
npm run lint -- --fix
# Check specific file
npx eslint src/components/Button/ButtonBase.vue
```
### TypeScript
```bash
# Run type checker
npm run type-check
# Show errors
npx vue-tsc --noEmit --pretty
```
### Prettier
```bash
# Format all files
npm run format
# Check formatting
npm run format:check
```
---
## Build & Deployment
### Development Build
```bash
npm run build:dev
# Creates dist/ with source maps
```
### Production Build
```bash
npm run build
# Creates optimized dist/ <500KB (gzipped)
```
### Preview Production Build
```bash
npm run preview
# Serves dist/ on http://localhost:4173/
```
### Build Storybook
```bash
npm run build-storybook
# Creates storybook-static/
# Deploy to GitHub Pages or Chromatic
```
---
## Troubleshooting
### Port Already in Use
```bash
# Use different port
npm run dev -- --port 5174
npm run storybook -- -p 6007
```
### Clear Cache
```bash
# Remove Vite cache
rm -rf node_modules/.vite
# Remove node_modules completely
rm -rf node_modules/
npm install
```
### TypeScript Errors
```bash
# See detailed errors
npm run type-check
# Fix common issues
# 1. Remove unused imports
# 2. Add type definitions
# 3. Fix implicit `any` types
```
### Test Timeouts
```bash
# Increase timeout
npm run test:unit -- --timeout=20000
```
---
## Git Workflow
### Before Committing
```bash
# Run all checks
make verify
# This runs:
# 1. ESLint (auto-fix)
# 2. TypeScript check
# 3. Unit tests
# 4. Production build
```
### Commit Message Format
```
feat(component): Add new Button component
- Implement primary, secondary, danger variants
- Add loading and disabled states
- Add 7 Storybook stories
- Add 8 unit tests (100% coverage)
- Add WCAG 2.1 AA accessibility
Closes #123
```
### Pre-commit Hooks
Husky automatically runs lint-staged before each commit:
- ESLint auto-fixes staged files
- Prettier formats staged files
- Commit blocked if errors found
---
## Phase 1 Checklist
- [ ] Vite + Vue 3 + TypeScript configured
- [ ] Storybook 7.0 with 8 addons
- [ ] ESLint + Prettier setup
- [ ] Pre-commit hooks (husky + lint-staged)
- [ ] 5 initial Primitive components
- [ ] 8 unit tests passing
- [ ] 7 Storybook stories
- [ ] npm run dev works (http://localhost:5173)
- [ ] npm run storybook works (http://localhost:6006)
- [ ] npm run build succeeds (<500KB)
- [ ] CI/CD pipeline configured (GitHub Actions)
- [ ] All developers can build locally
---
## Next Steps
1. **Phase 2**: Create remaining 25 Primitive components (Weeks 3-4)
2. **Phase 3**: Create Typed & Domain Fields + Pinia (Weeks 5-6)
3. **Phase 4**: Create Business Composites + E2E tests (Weeks 7-8)
---
**Questions?** See README.md or PHASE1-STEP2-VERIFICATION.md