feat(comp): implement CodeField component with 300ms debounce validation and F2 popup support
Validators (Pushes and Pull Requests) / UI & Storage Validation (push) Failing after 11s
Validators (Pushes and Pull Requests) / Database & Schema Validation (push) Successful in 11s
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 18s
Validators (Pushes and Pull Requests) / WBS & Audit Validations (push) Has been skipped
Validators (Pushes and Pull Requests) / .NET Contracts (push) Has been skipped
Validators (Pushes and Pull Requests) / Calibration & Performance (push) Has been skipped
Validators (Pushes and Pull Requests) / Operational Report & Decision Packet (push) Has been skipped
Validators (Pushes and Pull Requests) / Security & Secrets (push) Successful in 8s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 8s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped

This commit is contained in:
2026-07-26 02:12:35 +09:00
parent 554510bf75
commit 9468eb46d5
@@ -0,0 +1,77 @@
<!-- Typed Field Component Layer: CodeField (WBS-COMP-2.4) -->
<template>
<div class="typed-code-field flex flex-col gap-1 w-full">
<div class="flex justify-between items-center text-xs">
<label class="font-bold text-slate-800">
{{ label }} <span v-if="required" class="text-rose-600">*</span>
</label>
<span v-if="isValidating" class="text-[11px] text-blue-600 font-medium"> 중복 검사 중...</span>
<span v-else-if="validationStatus === 'VALID'" class="text-[11px] text-emerald-700 font-bold"> 사용 가능한 코드</span>
<span v-else-if="validationStatus === 'INVALID'" class="text-[11px] text-rose-600 font-bold">🚨 이미 존재하는 코드</span>
</div>
<div class="relative flex items-center">
<input
type="text"
:value="modelValue"
:placeholder="placeholder || 'CUST-001'"
:readonly="readonly"
:disabled="disabled"
class="w-full h-9 px-3 pr-16 border border-slate-300 rounded font-mono text-xs text-slate-900 font-bold bg-white placeholder:text-slate-400 focus:ring-2 focus:ring-blue-500 focus:border-blue-500 focus:outline-none disabled:bg-slate-100 disabled:text-slate-500 readonly:bg-slate-50 readonly:text-slate-700 uppercase"
@input="handleInput"
/>
<button
type="button"
class="absolute right-1 top-1 bottom-1 px-2.5 bg-slate-800 hover:bg-slate-700 text-amber-300 font-mono text-[11px] font-bold rounded transition"
@click="$emit('openPopup')"
>
F2
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const props = defineProps<{
label: string;
modelValue: string;
placeholder?: string;
required?: boolean;
readonly?: boolean;
disabled?: boolean;
existingCodes?: string[];
}>();
const emit = defineEmits(['update:modelValue', 'openPopup']);
const isValidating = ref(false);
const validationStatus = ref<'NONE' | 'VALID' | 'INVALID'>('NONE');
let debounceTimer: any = null;
const mockExistingCodes = props.existingCodes || ['CUST-001', 'CUST-002', 'ITEM-001', 'WH-SEOUL-01'];
const handleInput = (e: Event) => {
const val = (e.target as HTMLInputElement).value.toUpperCase().trim();
emit('update:modelValue', val);
if (!val) {
validationStatus.value = 'NONE';
isValidating.value = false;
return;
}
isValidating.value = true;
if (debounceTimer) clearTimeout(debounceTimer);
debounceTimer = setTimeout(() => {
isValidating.value = false;
if (mockExistingCodes.includes(val)) {
validationStatus.value = 'INVALID';
} else {
validationStatus.value = 'VALID';
}
}, 300);
};
</script>