feat(components): implement 4-layer input component architecture and 52-section specification with harness CLI v3.0 verification
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been cancelled
Validators (Pushes and Pull Requests) / Security & Secrets (push) Has been cancelled
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been cancelled
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been cancelled
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Has been cancelled
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been cancelled
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been cancelled
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Has been cancelled
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 10s
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 10s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been cancelled
Validators (Pushes and Pull Requests) / Security & Secrets (push) Has been cancelled
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been cancelled
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been cancelled
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Has been cancelled
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been cancelled
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been cancelled
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Has been cancelled
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 10s
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 10s
This commit is contained in:
@@ -0,0 +1,22 @@
|
||||
<!-- Business Composite Component Layer -->
|
||||
<template>
|
||||
<div class="business-address-editor border rounded p-4 bg-slate-50">
|
||||
<h4 class="font-bold text-sm text-slate-800 mb-2">주소 편집기 (AddressEditor)</h4>
|
||||
<div class="flex gap-2 mb-2">
|
||||
<input type="text" :value="postalCode" placeholder="우편번호" readonly class="w-32 border px-2 py-1 bg-white text-xs" />
|
||||
<button type="button" class="bg-blue-600 text-white text-xs px-3 py-1 rounded" @click="$emit('search-postal')">우편번호 검색</button>
|
||||
</div>
|
||||
<input type="text" :value="address1" placeholder="기본주소" readonly class="w-full border px-2 py-1 bg-white text-xs mb-2" />
|
||||
<input type="text" :value="address2" placeholder="상세주소 입력" class="w-full border px-2 py-1 bg-white text-xs" @input="$emit('update:address2', ($event.target as HTMLInputElement).value)" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
postalCode?: string;
|
||||
address1?: string;
|
||||
address2?: string;
|
||||
}>();
|
||||
|
||||
defineEmits(['search-postal', 'update:address2']);
|
||||
</script>
|
||||
@@ -0,0 +1,46 @@
|
||||
<!-- Domain Field Component Layer -->
|
||||
<template>
|
||||
<div class="domain-quantity-field">
|
||||
<label class="domain-label">{{ label || '수량' }} <span v-if="required" class="req">*</span></label>
|
||||
<div class="input-unit-group flex gap-2">
|
||||
<input
|
||||
type="text"
|
||||
:value="amount"
|
||||
placeholder="0"
|
||||
class="quantity-input flex-1 border rounded px-3 py-1 text-right font-mono"
|
||||
@input="handleAmountInput"
|
||||
/>
|
||||
<select :value="unitCode" class="unit-select border rounded px-2" @change="handleUnitChange">
|
||||
<option v-for="unit in availableUnits" :key="unit" :value="unit">{{ unit }}</option>
|
||||
</select>
|
||||
</div>
|
||||
<div v-if="stockContext" class="stock-info text-xs text-slate-500 mt-1">
|
||||
가용재고: <strong>{{ stockContext.availableAmount }} {{ unitCode }}</strong>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
label?: string;
|
||||
required?: boolean;
|
||||
amount: string;
|
||||
unitCode: string;
|
||||
availableUnits: string[];
|
||||
stockContext?: { availableAmount: string };
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:amount', 'update:unitCode']);
|
||||
|
||||
const handleAmountInput = (e: Event) => {
|
||||
emit('update:amount', (e.target as HTMLInputElement).value);
|
||||
};
|
||||
|
||||
const handleUnitChange = (e: Event) => {
|
||||
emit('update:unitCode', (e.target as HTMLSelectElement).value);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.req { color: #DC2626; }
|
||||
</style>
|
||||
@@ -0,0 +1,59 @@
|
||||
<!-- Typed Field Component Layer -->
|
||||
<template>
|
||||
<div class="typed-string-field">
|
||||
<label v-if="label" :for="id" class="field-label">{{ label }}</label>
|
||||
<input
|
||||
:id="id"
|
||||
type="text"
|
||||
:value="modelValue"
|
||||
:placeholder="placeholder"
|
||||
:readonly="readonly"
|
||||
:disabled="disabled"
|
||||
class="string-input"
|
||||
@input="handleInput"
|
||||
/>
|
||||
<div v-if="errorMessage" class="field-error-msg">{{ errorMessage }}</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
const props = defineProps<{
|
||||
id?: string;
|
||||
label?: string;
|
||||
modelValue?: string;
|
||||
placeholder?: string;
|
||||
readonly?: boolean;
|
||||
disabled?: boolean;
|
||||
errorMessage?: string;
|
||||
}>();
|
||||
|
||||
const emit = defineEmits(['update:modelValue', 'change']);
|
||||
|
||||
const handleInput = (e: Event) => {
|
||||
const val = (e.target as HTMLInputElement).value;
|
||||
emit('update:modelValue', val);
|
||||
emit('change', val);
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.field-label {
|
||||
display: block;
|
||||
font-size: 12px;
|
||||
font-weight: 700;
|
||||
color: #334155;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.string-input {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
padding: 0 10px;
|
||||
border: 1px solid #CBD5E1;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.field-error-msg {
|
||||
font-size: 11px;
|
||||
color: #DC2626;
|
||||
margin-top: 2px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!-- Primitive UI Components Layer -->
|
||||
<template>
|
||||
<div class="primitive-input-wrapper">
|
||||
<input
|
||||
:id="id"
|
||||
:type="type || 'text'"
|
||||
:value="modelValue"
|
||||
:placeholder="placeholder"
|
||||
:disabled="disabled"
|
||||
:readonly="readonly"
|
||||
class="primitive-input"
|
||||
@input="$emit('update:modelValue', ($event.target as HTMLInputElement).value)"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
defineProps<{
|
||||
id?: string;
|
||||
type?: string;
|
||||
modelValue?: string | number;
|
||||
placeholder?: string;
|
||||
disabled?: boolean;
|
||||
readonly?: boolean;
|
||||
}>();
|
||||
|
||||
defineEmits(['update:modelValue']);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.primitive-input {
|
||||
width: 100%;
|
||||
height: 36px;
|
||||
padding: 0 12px;
|
||||
border: 1px solid #CBD5E1;
|
||||
border-radius: 4px;
|
||||
font-size: 13px;
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
/**
|
||||
* src/frontend/src/types/enterpriseTemplateContracts.ts
|
||||
* OMS·WMS·ERP 공통 CRUD 화면 템플릿 표준 계약 및 타입 정의
|
||||
* OMS·WMS·ERP 공통 CRUD 화면 템플릿 & 입력 컴포넌트 4계층 표준 계약 및 타입 정의
|
||||
* Specification: docs/ENTERPRISE_CRUD_DESIGN_SPECIFICATION.md
|
||||
* Roadmap: docs/ROADMAP_ENTERPRISE_TEMPLATES_WBS.md
|
||||
*/
|
||||
@@ -19,20 +19,41 @@ export type EnterpriseTemplateId =
|
||||
| 'TPL-APPROVAL-01' // 승인·반려
|
||||
| 'TPL-HISTORY-01'; // 변경 이력
|
||||
|
||||
/** 입력 필드 공통 상태 계약 */
|
||||
/** 13가지 입력 필드 공통 상태 계약 */
|
||||
export type FieldStatus =
|
||||
| 'idle'
|
||||
| 'focused'
|
||||
| 'dirty'
|
||||
| 'validating'
|
||||
| 'valid'
|
||||
| 'warning'
|
||||
| 'invalid'
|
||||
| 'saving'
|
||||
| 'saved'
|
||||
| 'conflict'
|
||||
| 'blocked'
|
||||
| 'readonly'
|
||||
| 'disabled';
|
||||
| 'disabled'
|
||||
| 'blocked';
|
||||
|
||||
/** 8가지 입력 값 출처 계약 */
|
||||
export type ValueSource =
|
||||
| 'user'
|
||||
| 'default'
|
||||
| 'scanner'
|
||||
| 'import'
|
||||
| 'integration'
|
||||
| 'system'
|
||||
| 'calculation'
|
||||
| 'ai';
|
||||
|
||||
export interface FieldMessage {
|
||||
code: string;
|
||||
severity: 'error' | 'warning' | 'information';
|
||||
message: string;
|
||||
remediation?: string;
|
||||
blocking: boolean;
|
||||
rejectedValue?: unknown;
|
||||
}
|
||||
|
||||
export interface FieldError {
|
||||
code: string;
|
||||
@@ -49,22 +70,25 @@ export interface FieldWarning {
|
||||
message: string;
|
||||
}
|
||||
|
||||
export interface FieldState<T = unknown> {
|
||||
value: T | null;
|
||||
export interface FieldState<TValue = unknown> {
|
||||
value: TValue | null;
|
||||
initialValue: TValue | null;
|
||||
rawValue?: string;
|
||||
displayValue?: string;
|
||||
initialValue: T | null;
|
||||
normalizedValue?: TValue | null;
|
||||
status: FieldStatus;
|
||||
dirty: boolean;
|
||||
touched: boolean;
|
||||
dirty: boolean;
|
||||
required: boolean;
|
||||
source: 'user' | 'scanner' | 'import' | 'api' | 'system' | 'ai';
|
||||
source: ValueSource;
|
||||
sourceReference?: string;
|
||||
confidence?: number;
|
||||
errors: FieldError[];
|
||||
warnings: FieldWarning[];
|
||||
recordVersion?: string;
|
||||
errors: FieldMessage[];
|
||||
warnings: FieldMessage[];
|
||||
information: FieldMessage[];
|
||||
lastChangedAt?: string;
|
||||
lastChangedBy?: string;
|
||||
recordVersion?: number;
|
||||
}
|
||||
|
||||
/** Grid 컬럼 정의 계약 */
|
||||
@@ -94,7 +118,13 @@ export interface GridColumnDefinition {
|
||||
permission?: string;
|
||||
}
|
||||
|
||||
/** 변경 감지 및 동시 수정 3-Way Diff 모델 */
|
||||
/** 4계층 컴포넌트 아키텍처 분류 */
|
||||
export type ComponentLayer =
|
||||
| 'primitive'
|
||||
| 'typed-field'
|
||||
| 'domain-field'
|
||||
| 'business-composite';
|
||||
|
||||
export interface ChangeFieldDiff {
|
||||
path: string;
|
||||
before: unknown;
|
||||
@@ -110,7 +140,6 @@ export interface ChangeSet {
|
||||
reasonText?: string;
|
||||
}
|
||||
|
||||
/** 표준 API 오류 응답 계약 */
|
||||
export interface ApiErrorResponse {
|
||||
code: string;
|
||||
message: string;
|
||||
@@ -135,7 +164,6 @@ export interface ApiErrorResponse {
|
||||
occurredAt: string;
|
||||
}
|
||||
|
||||
/** 감사 및 이력 이벤트 계약 */
|
||||
export interface AuditEvent {
|
||||
eventId: string;
|
||||
correlationId: string;
|
||||
|
||||
Reference in New Issue
Block a user