Add OMS WMS ERP platform
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
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
This commit is contained in:
@@ -0,0 +1,613 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user