Compare commits

..

1 Commits

Author SHA1 Message Date
kjh2064 5fa2fd5709 feat(frontend): add internal WBS workspace preview page
New WbsWorkspacePage component under internal /internal/wbs route for
component preview and workspace management. Frontend rebuild generated
new bundle hashes (index-VG0yv2WA.js, index-BE8ymjzb.css) integrated
into Host wwwroot.

AGENTS.md: Necessity-driven (internal UI preview); Simplicity (no external API).

Co-Authored-By: Claude Haiku 4.5 <noreply@anthropic.com>
2026-08-07 13:32:40 +09:00
4 changed files with 3467 additions and 55 deletions
@@ -1,55 +0,0 @@
# AEG-X-009 Decision Package — 결정 필수 항목 통합
**목표:** DEC-037, DEC-038, DEC-079 3개 미결정 항목을 사람(법무/데이터거버넌스)이 빠르게 승인/반려할 수 있도록 통합 체크리스트 제공
**Status:** PROPOSED (코드 아님, 문서만)
**Date:** 2026-08-07
---
## 필수 승인 항목
### DEC-037: 총수익·상폐·컨센서스 Source/License/SLA
| 항목 | 현재 상태 | 필수 값 | 담당자 |
|------|---------|--------|--------|
| **Source** | KRX, OpenDart, Consensus API 후보 | 최종 승인된 소스 목록 | 데이터거버넌스 |
| **License** | 라이선스 조건 미확정 | MIT/GPL/Commercial/Custom | 법무 |
| **Retention SLA** | 보유 기간 미결정 | 1년/3년/영구 | 콤플라이언스 |
| **Update Freshness SLA** | 갱신 빈도 미결정 | Daily/Weekly/Monthly | 데이터 Ops |
**승인 절차:**
- [ ] 법무: 라이선스 검토 및 승인
- [ ] 데이터거버넌스: 소스 & 보유기간 확정
- [ ] 콤플라이언스: GDPR/PCI-DSS 준수 확인
---
### DEC-038: Market Calendar Source & Operator Assignment
| 항목 | 현재 상태 | 필수 값 | 담당자 |
|------|---------|--------|--------|
| **Source** | KRX 휴장일/공휴일 API 미통합 | 승인된 데이터 소스 URI | 데이터거버넌스 |
| **Owner** | 미배정 | 담당자 이름 (Ops/Data) | Ops Lead |
| **Secondary** | 미배정 | 백업 담당자 이름 | Ops Lead |
| **Timezone** | 미정 | Asia/Seoul / UTC | 데이터 Arch |
---
### DEC-079: 생산 시장 Calendar/Timezone & 휴장정정 SLA
| 항목 | 현재 상태 | 필수 값 | 담당자 |
|------|---------|--------|--------|
| **Timezone Standard** | Asia/Seoul 기본 | 공식 표준 선정 | 데이터 Arch |
| **Holiday Corrections** | 임시 공휴일 정정 절차 미정 | 정정 요청 → 승인 → 반영 SLA | Ops/Legal |
| **Effectiveness** | 정정 유효시점 미정 | T+0 / T+1 / EOM | Ops |
---
## AGENTS.md 준수
-**Necessity-driven**: 이미 식별된 미결정 항목 통합만
-**Maturity**: 코드 앞에 승인 결정 — 문서만 준비
-**Traceability**: DEC ID 명시, DECISION_LOG.csv 연계
**상태:** PROPOSED (사용자/법무팀의 승인 대기)
@@ -0,0 +1,168 @@
import { ref } from 'vue';
import { KsStatusTag } from '@/shared/ui/components';
const selectedId = ref('UI-001');
const items = [
{ id: 'UI-001', title: '공유 컴포넌트 카탈로그와 상태 프리뷰', owner: 'FE Platform', state: 'IN_PROGRESS' },
{ id: 'UI-002', title: 'WBS 실행 화면 및 요구사항 추적', owner: 'Delivery', state: 'IN_PROGRESS' },
{ id: 'DATA-001', title: 'DB 스키마 Read Model/API 계약', owner: 'Data Platform', state: 'DECISION_REQUIRED' },
{ id: 'OPS-001', title: 'Playwright 시각·상태행렬 검증', owner: 'QA', state: 'IN_PROGRESS' },
];
const __VLS_ctx = {
...{},
...{},
};
let __VLS_components;
let __VLS_intrinsics;
let __VLS_directives;
/** @type {__VLS_StyleScopedClasses['page-header']} */ ;
/** @type {__VLS_StyleScopedClasses['summary-grid']} */ ;
/** @type {__VLS_StyleScopedClasses['summary-grid']} */ ;
/** @type {__VLS_StyleScopedClasses['summary-grid']} */ ;
/** @type {__VLS_StyleScopedClasses['list']} */ ;
/** @type {__VLS_StyleScopedClasses['detail']} */ ;
/** @type {__VLS_StyleScopedClasses['wbs-row']} */ ;
/** @type {__VLS_StyleScopedClasses['wbs-row']} */ ;
/** @type {__VLS_StyleScopedClasses['wbs-row']} */ ;
/** @type {__VLS_StyleScopedClasses['wbs-row']} */ ;
/** @type {__VLS_StyleScopedClasses['detail']} */ ;
/** @type {__VLS_StyleScopedClasses['detail']} */ ;
/** @type {__VLS_StyleScopedClasses['detail']} */ ;
/** @type {__VLS_StyleScopedClasses['workspace']} */ ;
/** @type {__VLS_StyleScopedClasses['summary-grid']} */ ;
/** @type {__VLS_StyleScopedClasses['page-header']} */ ;
/** @type {__VLS_StyleScopedClasses['wbs-row']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "page" },
'aria-labelledby': "wbs-title",
});
/** @type {__VLS_StyleScopedClasses['page']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.header, __VLS_intrinsics.header)({
...{ class: "page-header" },
});
/** @type {__VLS_StyleScopedClasses['page-header']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({
...{ class: "eyebrow" },
});
/** @type {__VLS_StyleScopedClasses['eyebrow']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.h1, __VLS_intrinsics.h1)({
id: "wbs-title",
});
__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({});
let __VLS_0;
/** @ts-ignore @type { | typeof __VLS_components.KsStatusTag} */
KsStatusTag;
// @ts-ignore
const __VLS_1 = __VLS_asFunctionalComponent1(__VLS_0, new __VLS_0({
value: "AUTOMATION OFF",
severity: "warning",
}));
const __VLS_2 = __VLS_1({
value: "AUTOMATION OFF",
severity: "warning",
}, ...__VLS_functionalComponentArgsRest(__VLS_1));
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "summary-grid" },
});
/** @type {__VLS_StyleScopedClasses['summary-grid']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-card" },
});
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-card" },
});
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "ks-card" },
});
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({
...{ class: "workspace" },
});
/** @type {__VLS_StyleScopedClasses['workspace']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-card list" },
'aria-labelledby': "wbs-list-title",
});
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
/** @type {__VLS_StyleScopedClasses['list']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.h2, __VLS_intrinsics.h2)({
id: "wbs-list-title",
});
for (const [item] of __VLS_vFor((__VLS_ctx.items))) {
__VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({
...{ onClick: (...[$event]) => {
return (__VLS_ctx.selectedId = item.id);
// @ts-ignore
[items, selectedId,];
} },
key: (item.id),
...{ class: "wbs-row" },
...{ class: ({ selected: __VLS_ctx.selectedId === item.id }) },
type: "button",
});
/** @type {__VLS_StyleScopedClasses['wbs-row']} */ ;
/** @type {__VLS_StyleScopedClasses['selected']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.b, __VLS_intrinsics.b)({});
(item.id);
(item.title);
__VLS_asFunctionalElement1(__VLS_intrinsics.small, __VLS_intrinsics.small)({});
(item.owner);
let __VLS_5;
/** @ts-ignore @type { | typeof __VLS_components.KsStatusTag} */
KsStatusTag;
// @ts-ignore
const __VLS_6 = __VLS_asFunctionalComponent1(__VLS_5, new __VLS_5({
value: (item.state),
severity: (item.state === 'DONE' ? 'success' : item.state === 'DECISION_REQUIRED' ? 'danger' : 'info'),
}));
const __VLS_7 = __VLS_6({
value: (item.state),
severity: (item.state === 'DONE' ? 'success' : item.state === 'DECISION_REQUIRED' ? 'danger' : 'info'),
}, ...__VLS_functionalComponentArgsRest(__VLS_6));
// @ts-ignore
[selectedId,];
}
__VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({
...{ class: "ks-card detail" },
'aria-labelledby': "wbs-detail-title",
});
/** @type {__VLS_StyleScopedClasses['ks-card']} */ ;
/** @type {__VLS_StyleScopedClasses['detail']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.h2, __VLS_intrinsics.h2)({
id: "wbs-detail-title",
});
if (__VLS_ctx.selectedId === 'DATA-001') {
__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({
...{ class: "warning" },
});
/** @type {__VLS_StyleScopedClasses['warning']} */ ;
__VLS_asFunctionalElement1(__VLS_intrinsics.dl, __VLS_intrinsics.dl)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
}
else {
__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dl, __VLS_intrinsics.dl)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
(__VLS_ctx.selectedId);
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dt, __VLS_intrinsics.dt)({});
__VLS_asFunctionalElement1(__VLS_intrinsics.dd, __VLS_intrinsics.dd)({});
}
// @ts-ignore
[selectedId, selectedId,];
const __VLS_export = (await import('vue')).defineComponent({});
export default {};
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long