feat(templates): complete all 11 enterprise CRUD templates (TPL-CREATE-03, TPL-BULK-01, TPL-DELETE-01, TPL-HISTORY-01) according to master WBS schedule
Validators (Pushes and Pull Requests) / Core Validators & Database Setup (push) Failing after 20s
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) / 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 9s
Validators (Pushes and Pull Requests) / CI Workflow Lint (push) Failing after 9s
Validators (Pushes and Pull Requests) / Notify PR Results (push) Has been skipped

This commit is contained in:
2026-07-26 01:50:06 +09:00
parent 356c1717ce
commit ce22fc34e7
4 changed files with 222 additions and 0 deletions
@@ -0,0 +1,43 @@
<!-- Enterprise Template Implementation: TPL-BULK-01 (일괄 수정 템플릿) -->
<template>
<div class="tpl-bulk-01-container flex flex-col h-full w-full bg-slate-100 p-4 gap-3 overflow-y-auto">
<!-- Page Header -->
<div class="page-header flex justify-between items-center bg-white p-3.5 rounded-md border border-slate-200 shadow-sm">
<div class="flex items-center gap-2">
<h2 class="text-base font-bold text-slate-800">일괄 상태 담당자 변경 (`TPL-BULK-01`)</h2>
<span class="px-2 py-0.5 rounded bg-cyan-100 text-cyan-800 text-xs font-bold">대량 일괄 작업</span>
</div>
<span class="text-xs text-slate-500">선택 대상: <strong class="text-cyan-700 font-bold">83 </strong></span>
</div>
<!-- Impact & Target Summary -->
<div class="bg-white p-4 rounded-md border border-slate-200 shadow-sm flex flex-col gap-3">
<h3 class="font-bold text-xs text-slate-700 border-b pb-1">1. 일괄 변경 항목 지정</h3>
<div class="grid grid-cols-2 gap-3">
<StringField label="새 담당자" v-model="newManager" placeholder="이담당" required />
<DateField label="변경 예정일" v-model="scheduledDate" showTodayBtn />
</div>
<div class="p-3 bg-cyan-50 border border-cyan-200 rounded text-xs text-cyan-900 mt-2">
📊 <strong>예상 영향:</strong> 전체 83 <strong class="text-emerald-700">71 처리 가능</strong> / <strong class="text-rose-700">12 이미 완료되어 제외됨</strong>. (100 초과 비동기 Job 전환)
</div>
</div>
<!-- Action Bar -->
<div class="bg-white p-3 rounded-md border border-slate-200 shadow-md flex justify-between items-center">
<span class="text-xs text-slate-500">Async Job Threshold: <code class="font-mono text-slate-700">> 100 items</code></span>
<button class="px-4 py-1.5 bg-cyan-700 hover:bg-cyan-800 text-white text-xs font-bold rounded shadow-sm">
71 일괄 변경 실행
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import StringField from '../../components/fields/StringField.vue';
import DateField from '../../components/fields/DateField.vue';
const newManager = ref('이담당');
const scheduledDate = ref('2026-07-27');
</script>
@@ -0,0 +1,78 @@
<!-- Enterprise Template Implementation: TPL-CREATE-03 (단계형 등록 템플릿) -->
<template>
<div class="tpl-create-03-container flex flex-col h-full w-full bg-slate-100 p-4 gap-3 overflow-y-auto">
<!-- Page Header -->
<div class="page-header flex justify-between items-center bg-white p-3.5 rounded-md border border-slate-200 shadow-sm">
<div class="flex items-center gap-2">
<h2 class="text-base font-bold text-slate-800">복합 반품/계약 등록 (`TPL-CREATE-03`)</h2>
<span class="px-2 py-0.5 rounded bg-teal-100 text-teal-800 text-xs font-bold">Step Form Wizard</span>
</div>
<span class="text-xs text-slate-500">진행 단계: <strong class="text-teal-700 font-bold">Step {{ currentStep }} / 3</strong></span>
</div>
<!-- Step Progress Bar -->
<div class="bg-white p-3 rounded-md border border-slate-200 shadow-sm flex justify-around text-xs">
<div :class="['step-item font-bold flex items-center gap-1', currentStep >= 1 ? 'text-teal-600' : 'text-slate-400']">
<span class="w-5 h-5 rounded-full border flex items-center justify-center font-mono">1</span>
<span>기본 정보</span>
</div>
<div :class="['step-item font-bold flex items-center gap-1', currentStep >= 2 ? 'text-teal-600' : 'text-slate-400']">
<span class="w-5 h-5 rounded-full border flex items-center justify-center font-mono">2</span>
<span>품목 조건</span>
</div>
<div :class="['step-item font-bold flex items-center gap-1', currentStep >= 3 ? 'text-teal-600' : 'text-slate-400']">
<span class="w-5 h-5 rounded-full border flex items-center justify-center font-mono">3</span>
<span>검토 최종 제출</span>
</div>
</div>
<!-- Step Content -->
<div class="bg-white p-4 rounded-md border border-slate-200 shadow-sm flex flex-col gap-3 min-h-[220px]">
<div v-if="currentStep === 1" class="flex flex-col gap-3">
<h3 class="font-bold text-xs text-slate-700">1단계: 반품 대상 계약 선택</h3>
<StringField label="원 주문번호" v-model="originalOrderNo" placeholder="SO-2026-000128" required />
<StringField label="반품 사유" v-model="returnReason" placeholder="상품 파손 및 규격 불일치" required />
</div>
<div v-else-if="currentStep === 2" class="flex flex-col gap-3">
<h3 class="font-bold text-xs text-slate-700">2단계: 수량 환불 조건 지정</h3>
<QuantityField label="반품 수량" amount="10" unitCode="EA" :availableUnits="['EA', 'BOX']" />
<MoneyField label="예상 환불 금액" amount="1275000" currencyCode="KRW" />
</div>
<div v-else class="flex flex-col gap-3">
<h3 class="font-bold text-xs text-slate-700">3단계: 요약 검토 확정</h3>
<div class="bg-slate-50 p-3 rounded text-xs leading-relaxed text-slate-700">
<div> 주문: <strong>{{ originalOrderNo }}</strong></div>
<div>반품 사유: <strong>{{ returnReason }}</strong></div>
<div>환불 예상금액: <strong class="text-blue-700">1,275,000 KRW</strong></div>
</div>
</div>
</div>
<!-- Sticky Action Bar -->
<div class="sticky bottom-0 bg-white p-3 rounded-md border border-slate-200 shadow-md flex justify-between items-center">
<button v-if="currentStep > 1" class="px-3.5 py-1.5 bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-bold rounded" @click="currentStep--">
이전 단계
</button>
<div v-else></div>
<div class="flex gap-2">
<button v-if="currentStep < 3" class="px-4 py-1.5 bg-teal-600 hover:bg-teal-700 text-white text-xs font-bold rounded" @click="currentStep++">
다음 단계
</button>
<button v-else class="px-4 py-1.5 bg-teal-700 hover:bg-teal-800 text-white text-xs font-bold rounded shadow-sm">
최종 반품 신청
</button>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import StringField from '../../components/fields/StringField.vue';
import QuantityField from '../../components/domain-fields/QuantityField.vue';
import MoneyField from '../../components/domain-fields/MoneyField.vue';
const currentStep = ref(1);
const originalOrderNo = ref('SO-2026-000128');
const returnReason = ref('상품 파손 및 규격 불일치');
</script>
@@ -0,0 +1,49 @@
<!-- Enterprise Template Implementation: TPL-DELETE-01 (안전 삭제 템플릿) -->
<template>
<div class="tpl-delete-01-container flex flex-col h-full w-full bg-slate-100 p-4 gap-3 overflow-y-auto">
<!-- Page Header -->
<div class="page-header flex justify-between items-center bg-white p-3.5 rounded-md border border-slate-200 shadow-sm">
<div class="flex items-center gap-2">
<h2 class="text-base font-bold text-slate-800">마스터 데이터 삭제 (`TPL-DELETE-01`)</h2>
<span class="px-2 py-0.5 rounded bg-rose-100 text-rose-800 text-xs font-bold">미사용 임시데이터 한정</span>
</div>
</div>
<!-- Safety Check Section -->
<div class="bg-white p-4 rounded-md border border-slate-200 shadow-sm flex flex-col gap-3">
<div class="p-3 bg-emerald-50 border border-emerald-200 rounded text-xs text-emerald-900">
<strong>안전성 검증 통과:</strong> 거래처(`CUST-00999`) 미완료 주문 결제 내역이 없어 삭제할 있습니다.
</div>
<div class="flex flex-col gap-1 text-xs text-slate-700">
<div>삭제 대상: <strong>CUST-00999 / () 테스트유통</strong></div>
<div class="text-slate-500">삭제 복구할 없으며 감사 로그에 이력이 기록됩니다.</div>
</div>
<div class="border-t pt-3 mt-1">
<label class="text-xs font-bold text-slate-700 block mb-1">실수 방지를 위한 확인 입력 (거래처 코드를 입력하세요)</label>
<StringField v-model="confirmationCode" placeholder="CUST-00999" required />
</div>
</div>
<!-- Action Bar -->
<div class="bg-white p-3 rounded-md border border-slate-200 shadow-md flex justify-between items-center">
<button class="px-3.5 py-1.5 bg-slate-100 hover:bg-slate-200 text-slate-700 text-xs font-bold rounded">
취소
</button>
<button
:disabled="confirmationCode !== 'CUST-00999'"
:class="['px-4 py-1.5 text-xs font-bold rounded shadow-sm transition', confirmationCode === 'CUST-00999' ? 'bg-rose-600 hover:bg-rose-700 text-white' : 'bg-slate-200 text-slate-400 cursor-not-allowed']"
>
영구 삭제
</button>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
import StringField from '../../components/fields/StringField.vue';
const confirmationCode = ref('CUST-00999');
</script>
@@ -0,0 +1,52 @@
<!-- Enterprise Template Implementation: TPL-HISTORY-01 (변경 이력 템플릿) -->
<template>
<div class="tpl-history-01-container flex flex-col h-full w-full bg-slate-100 p-4 gap-3 overflow-y-auto">
<!-- Page Header -->
<div class="page-header flex justify-between items-center bg-white p-3.5 rounded-md border border-slate-200 shadow-sm">
<div class="flex items-center gap-2">
<h2 class="text-base font-bold text-slate-800">주문 변경 이력 감사 로그 (`TPL-HISTORY-01`)</h2>
<span class="px-2 py-0.5 rounded bg-slate-100 text-slate-700 text-xs font-mono font-bold">AuditEvent Log</span>
</div>
<span class="text-xs text-slate-500">Correlation ID: <code class="font-mono text-slate-700">CORR-20260726-001829</code></span>
</div>
<!-- History Timeline Table -->
<div class="bg-white p-4 rounded-md border border-slate-200 shadow-sm flex flex-col gap-3">
<h3 class="font-bold text-xs text-slate-700 border-b pb-1">필드 단위 변경 추적 이력</h3>
<div class="overflow-x-auto">
<table class="w-full text-xs text-left border border-slate-200">
<thead class="bg-slate-50 text-slate-700 font-bold border-b">
<tr>
<th class="p-2 border-r">발생 시각</th>
<th class="p-2 border-r">작업자 (Actor)</th>
<th class="p-2 border-r">변경 필드</th>
<th class="p-2 border-r text-slate-500">변경 (Before)</th>
<th class="p-2 border-r text-blue-700">변경 (After)</th>
<th class="p-2">사유 (Reason)</th>
</tr>
</thead>
<tbody>
<tr v-for="log in auditLogs" :key="log.id" class="border-b hover:bg-slate-50">
<td class="p-2 border-r font-mono text-slate-600">{{ log.occurredAt }}</td>
<td class="p-2 border-r font-bold text-slate-800">{{ log.actor }}</td>
<td class="p-2 border-r font-mono text-slate-700">{{ log.field }}</td>
<td class="p-2 border-r font-mono text-slate-400 line-through">{{ log.before }}</td>
<td class="p-2 border-r font-mono font-bold text-blue-700">{{ log.after }}</td>
<td class="p-2 text-slate-600">{{ log.reason }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</template>
<script setup lang="ts">
import { ref } from 'vue';
const auditLogs = ref([
{ id: 1, occurredAt: '2026-07-26 15:01:11', actor: '김담당 (User)', field: 'requestedDeliveryDate', before: '2026-07-28', after: '2026-07-29', reason: '고객 배송지 변경 요청' },
{ id: 2, occurredAt: '2026-07-26 14:35:11', actor: '김담당 (User)', field: 'status', before: 'DRAFT', after: 'READY_TO_SHIP', reason: '최초 주문 확정' }
]);
</script>