diff --git a/docs/CURRENT/SLICE_SPECS/VS-00-UI-ROUTE-MENU-PARITY.md b/docs/CURRENT/SLICE_SPECS/VS-00-UI-ROUTE-MENU-PARITY.md new file mode 100644 index 00000000..f9a4b515 --- /dev/null +++ b/docs/CURRENT/SLICE_SPECS/VS-00-UI-ROUTE-MENU-PARITY.md @@ -0,0 +1,18 @@ +# VS-00 UI Route/Menu Parity + +- Requirement ID: REQ-PLAT-001 +- Policy/Data/Screen ID: UI-PLAT-01 / existing screen implementations +- WBS IDs: AEG-VS-00-06, V13-FE-011..020, AEG-V14-013..022 +- API/DB/Job IDs: None (behavior-preserving route/menu wiring) +- Test IDs: T-ARCH-001 / frontend typecheck and build +- 사용자 결과: 구현되어 있으나 접근할 수 없던 화면을 WBS 기능 영역과 일치하는 메뉴·라우트로 제공한다. +- 비목표: 새 업무 정책, 주문/KIS 제출, API·DB·migration, 내부 UI catalogue의 일반 사용자 노출 +- 권한/Capability: 기존 화면의 권한 경계를 변경하지 않음. `/internal/*`은 메뉴에서 숨김. +- Source: `docs/CURRENT/CATALOGS/WBS_MASTER.csv`, `docs/CURRENT/CATALOGS/TRACEABILITY_MATRIX.csv`, `frontend/src/features/**/pages/*.vue`, current router/app shell +- Assumption: 현재 저장소에 구현된 화면은 해당 Slice의 승인된 UI 후보이며, 실제 endpoint readiness는 각 화면의 기존 상태 처리로 판단한다. +- Unknown/Decision Required: WBS에 정의되었으나 저장소에 화면 구현이 없는 VS-01~VS-25 화면의 API·권한·Read Model 계약은 별도 Slice로 확정해야 한다. +- Decision: 이번 변경은 기존 화면을 route/menu에 연결하는 단일 동작보존 Slice로 제한한다. +- Rollback: route/menu 변경 revert; 데이터 변경 없음. +- 구현: `frontend/src/app/router.ts`, `frontend/src/App.vue` +- 검증 증거 (2026-08-06): `pnpm typecheck` PASS; `pnpm test -- --run` PASS (18 files / 40 tests); `pnpm build` PASS (Vite production build). Build emitted a non-blocking chunk-size warning (>500 kB). +- 미실행: Playwright E2E, .NET build/test, DB migration rehearsal. 이 Slice는 FE route/menu만 변경하므로 별도 실행하지 않았으며 통과로 주장하지 않는다. diff --git a/frontend/src/App.vue b/frontend/src/App.vue index 687af15f..e6d36159 100644 --- a/frontend/src/App.vue +++ b/frontend/src/App.vue @@ -4,7 +4,11 @@ import { AppShellLayout } from './shared/ui/layouts' diff --git a/frontend/src/App.vue.js b/frontend/src/App.vue.js index f2945625..888dce2c 100644 --- a/frontend/src/App.vue.js +++ b/frontend/src/App.vue.js @@ -21,8 +21,11 @@ const { default: __VLS_6 } = __VLS_3.slots; const { navigation: __VLS_7 } = __VLS_3.slots; __VLS_asFunctionalElement1(__VLS_intrinsics.nav, __VLS_intrinsics.nav)({ ...{ class: "app-nav" }, + 'aria-label': "주요 메뉴", }); /** @type {__VLS_StyleScopedClasses['app-nav']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({}); + __VLS_asFunctionalElement1(__VLS_intrinsics.h2, __VLS_intrinsics.h2)({}); let __VLS_8; /** @ts-ignore @type { | typeof __VLS_components.RouterLink | typeof __VLS_components.RouterLink} */ RouterLink; @@ -35,15 +38,17 @@ const { default: __VLS_6 } = __VLS_3.slots; }, ...__VLS_functionalComponentArgsRest(__VLS_9)); const { default: __VLS_13 } = __VLS_11.slots; var __VLS_11; + __VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({}); + __VLS_asFunctionalElement1(__VLS_intrinsics.h2, __VLS_intrinsics.h2)({}); let __VLS_14; /** @ts-ignore @type { | typeof __VLS_components.RouterLink | typeof __VLS_components.RouterLink} */ RouterLink; // @ts-ignore const __VLS_15 = __VLS_asFunctionalComponent1(__VLS_14, new __VLS_14({ - to: "/ops/data-quality", + to: "/portfolio/risk", })); const __VLS_16 = __VLS_15({ - to: "/ops/data-quality", + to: "/portfolio/risk", }, ...__VLS_functionalComponentArgsRest(__VLS_15)); const { default: __VLS_19 } = __VLS_17.slots; var __VLS_17; @@ -52,32 +57,70 @@ const { default: __VLS_6 } = __VLS_3.slots; RouterLink; // @ts-ignore const __VLS_21 = __VLS_asFunctionalComponent1(__VLS_20, new __VLS_20({ - to: "/ops/model-operations", + to: "/portfolio/rebalance", })); const __VLS_22 = __VLS_21({ - to: "/ops/model-operations", + to: "/portfolio/rebalance", }, ...__VLS_functionalComponentArgsRest(__VLS_21)); const { default: __VLS_25 } = __VLS_23.slots; var __VLS_23; + __VLS_asFunctionalElement1(__VLS_intrinsics.section, __VLS_intrinsics.section)({}); + __VLS_asFunctionalElement1(__VLS_intrinsics.h2, __VLS_intrinsics.h2)({}); let __VLS_26; /** @ts-ignore @type { | typeof __VLS_components.RouterLink | typeof __VLS_components.RouterLink} */ RouterLink; // @ts-ignore const __VLS_27 = __VLS_asFunctionalComponent1(__VLS_26, new __VLS_26({ - to: "/internal/ui-standard", + to: "/ops/data-quality", })); const __VLS_28 = __VLS_27({ - to: "/internal/ui-standard", + to: "/ops/data-quality", }, ...__VLS_functionalComponentArgsRest(__VLS_27)); const { default: __VLS_31 } = __VLS_29.slots; var __VLS_29; + let __VLS_32; + /** @ts-ignore @type { | typeof __VLS_components.RouterLink | typeof __VLS_components.RouterLink} */ + RouterLink; + // @ts-ignore + const __VLS_33 = __VLS_asFunctionalComponent1(__VLS_32, new __VLS_32({ + to: "/ops/market-data-ingestion", + })); + const __VLS_34 = __VLS_33({ + to: "/ops/market-data-ingestion", + }, ...__VLS_functionalComponentArgsRest(__VLS_33)); + const { default: __VLS_37 } = __VLS_35.slots; + var __VLS_35; + let __VLS_38; + /** @ts-ignore @type { | typeof __VLS_components.RouterLink | typeof __VLS_components.RouterLink} */ + RouterLink; + // @ts-ignore + const __VLS_39 = __VLS_asFunctionalComponent1(__VLS_38, new __VLS_38({ + to: "/ops/market-data-history", + })); + const __VLS_40 = __VLS_39({ + to: "/ops/market-data-history", + }, ...__VLS_functionalComponentArgsRest(__VLS_39)); + const { default: __VLS_43 } = __VLS_41.slots; + var __VLS_41; + let __VLS_44; + /** @ts-ignore @type { | typeof __VLS_components.RouterLink | typeof __VLS_components.RouterLink} */ + RouterLink; + // @ts-ignore + const __VLS_45 = __VLS_asFunctionalComponent1(__VLS_44, new __VLS_44({ + to: "/ops/model-operations", + })); + const __VLS_46 = __VLS_45({ + to: "/ops/model-operations", + }, ...__VLS_functionalComponentArgsRest(__VLS_45)); + const { default: __VLS_49 } = __VLS_47.slots; + var __VLS_47; } -let __VLS_32; +let __VLS_50; /** @ts-ignore @type { | typeof __VLS_components.RouterView} */ RouterView; // @ts-ignore -const __VLS_33 = __VLS_asFunctionalComponent1(__VLS_32, new __VLS_32({})); -const __VLS_34 = __VLS_33({}, ...__VLS_functionalComponentArgsRest(__VLS_33)); +const __VLS_51 = __VLS_asFunctionalComponent1(__VLS_50, new __VLS_50({})); +const __VLS_52 = __VLS_51({}, ...__VLS_functionalComponentArgsRest(__VLS_51)); var __VLS_3; const __VLS_export = (await import('vue')).defineComponent({}); export default {}; diff --git a/frontend/src/app/router.js b/frontend/src/app/router.js index 646c10c5..20039246 100644 --- a/frontend/src/app/router.js +++ b/frontend/src/app/router.js @@ -3,6 +3,10 @@ import SellDecisionPage from '../features/sell-decision/pages/SellDecisionPage.v import DataQualityPage from '../features/data-quality/pages/DataQualityPage.vue'; import ModelOperationsPage from '../features/model-operations/pages/ModelOperationsPage.vue'; import UiStandardPage from '../features/ui-standard/pages/UiStandardPage.vue'; +import RiskDashboard from '../features/portfolio/pages/RiskDashboard.vue'; +import RebalanceForm from '../features/portfolio/pages/RebalanceForm.vue'; +import MarketDataIngestion from '../features/marketData/pages/MarketDataIngestion.vue'; +import IngestionStatus from '../features/marketData/pages/IngestionStatus.vue'; export const router = createRouter({ history: createWebHistory(), routes: [ @@ -10,6 +14,10 @@ export const router = createRouter({ { path: '/research/sell-decision', component: SellDecisionPage, meta: { screenId: 'SCR-002', templateId: 'T02' } }, { path: '/ops/data-quality', component: DataQualityPage, meta: { screenId: 'SCR-013', templateId: 'T08' } }, { path: '/ops/model-operations', component: ModelOperationsPage, meta: { screenId: 'SCR-015', templateId: 'T10' } }, + { path: '/ops/market-data-ingestion', component: MarketDataIngestion, meta: { screenId: 'SCR-016', templateId: 'T08' } }, + { path: '/ops/market-data-history', component: IngestionStatus, meta: { screenId: 'SCR-017', templateId: 'T08' } }, + { path: '/portfolio/risk', component: RiskDashboard, meta: { screenId: 'SCR-018', templateId: 'T07' } }, + { path: '/portfolio/rebalance', component: RebalanceForm, meta: { screenId: 'SCR-019', templateId: 'T03' } }, { path: '/internal/ui-standard', component: UiStandardPage, meta: { screenId: 'SCR-DEV-001', templateId: 'T01', internalOnly: true } } ] }); diff --git a/frontend/src/app/router.ts b/frontend/src/app/router.ts index d4ff48e7..f4193730 100644 --- a/frontend/src/app/router.ts +++ b/frontend/src/app/router.ts @@ -3,6 +3,10 @@ import SellDecisionPage from '../features/sell-decision/pages/SellDecisionPage.v import DataQualityPage from '../features/data-quality/pages/DataQualityPage.vue' import ModelOperationsPage from '../features/model-operations/pages/ModelOperationsPage.vue' import UiStandardPage from '../features/ui-standard/pages/UiStandardPage.vue' +import RiskDashboard from '../features/portfolio/pages/RiskDashboard.vue' +import RebalanceForm from '../features/portfolio/pages/RebalanceForm.vue' +import MarketDataIngestion from '../features/marketData/pages/MarketDataIngestion.vue' +import IngestionStatus from '../features/marketData/pages/IngestionStatus.vue' export const router = createRouter({ history: createWebHistory(), @@ -11,6 +15,10 @@ export const router = createRouter({ { path: '/research/sell-decision', component: SellDecisionPage, meta: { screenId: 'SCR-002', templateId: 'T02' } }, { path: '/ops/data-quality', component: DataQualityPage, meta: { screenId: 'SCR-013', templateId: 'T08' } }, { path: '/ops/model-operations', component: ModelOperationsPage, meta: { screenId: 'SCR-015', templateId: 'T10' } }, + { path: '/ops/market-data-ingestion', component: MarketDataIngestion, meta: { screenId: 'SCR-016', templateId: 'T08' } }, + { path: '/ops/market-data-history', component: IngestionStatus, meta: { screenId: 'SCR-017', templateId: 'T08' } }, + { path: '/portfolio/risk', component: RiskDashboard, meta: { screenId: 'SCR-018', templateId: 'T07' } }, + { path: '/portfolio/rebalance', component: RebalanceForm, meta: { screenId: 'SCR-019', templateId: 'T03' } }, { path: '/internal/ui-standard', component: UiStandardPage, meta: { screenId: 'SCR-DEV-001', templateId: 'T01', internalOnly: true } } ] }) diff --git a/frontend/src/features/marketData/pages/IngestionStatus.vue b/frontend/src/features/marketData/pages/IngestionStatus.vue index 69f95187..0e38b8dc 100644 --- a/frontend/src/features/marketData/pages/IngestionStatus.vue +++ b/frontend/src/features/marketData/pages/IngestionStatus.vue @@ -76,49 +76,88 @@ + + diff --git a/frontend/src/features/marketData/pages/MarketDataIngestion.vue.js b/frontend/src/features/marketData/pages/MarketDataIngestion.vue.js new file mode 100644 index 00000000..7f8399aa --- /dev/null +++ b/frontend/src/features/marketData/pages/MarketDataIngestion.vue.js @@ -0,0 +1,407 @@ +import { ref, computed } from 'vue'; +import { useRouter } from 'vue-router'; +const router = useRouter(); +const isLoading = ref(false); +const jobId = ref(null); +const form = ref({ + dataSource: 'KRX', + fromDate: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], + toDate: new Date().toISOString().split('T')[0], +}); +const minDate = '2015-01-01'; // KRX historical data starts here +const maxDate = new Date().toISOString().split('T')[0]; // Today +const validationErrors = computed(() => { + const errors = []; + if (!form.value.fromDate) + errors.push('From Date is required'); + if (!form.value.toDate) + errors.push('To Date is required'); + if (form.value.fromDate && form.value.toDate) { + if (form.value.fromDate > form.value.toDate) { + errors.push('From Date must be before To Date'); + } + if (form.value.toDate > maxDate) { + errors.push('To Date cannot be in the future'); + } + } + return errors; +}); +const daysCount = computed(() => { + if (!form.value.fromDate || !form.value.toDate) + return 0; + const from = new Date(form.value.fromDate); + const to = new Date(form.value.toDate); + return Math.ceil((to.getTime() - from.getTime()) / (1000 * 60 * 60 * 24)); +}); +const estimatedRows = computed(() => { + // KRX: ~2000 stocks × days + // OpenDart: ~200 quarterly filings + if (form.value.dataSource === 'KRX') { + return (daysCount.value * 2000).toLocaleString(); + } + else if (form.value.dataSource === 'OpenDart') { + return (Math.ceil(daysCount.value / 90) * 200).toLocaleString(); + } + return '0'; +}); +const setPreset = (preset) => { + const today = new Date(); + const from = new Date(); + if (preset === '1y') + from.setFullYear(from.getFullYear() - 1); + else if (preset === '2y') + from.setFullYear(from.getFullYear() - 2); + else if (preset === '5y') + from.setFullYear(from.getFullYear() - 5); + else if (preset === 'all') + from.setFullYear(2015); + form.value.fromDate = from.toISOString().split('T')[0]; + form.value.toDate = today.toISOString().split('T')[0]; +}; +const triggerIngestion = async () => { + if (validationErrors.value.length > 0) + return; + isLoading.value = true; + try { + const response = await fetch('/api/market/ingest', { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'X-KArtSell-User': 'ingestion-user', + 'X-KArtSell-Role': 'DataAdmin', + }, + body: JSON.stringify({ + dataSource: form.value.dataSource, + fromDate: form.value.fromDate, + toDate: form.value.toDate, + }), + }); + if (!response.ok) { + throw new Error(`HTTP ${response.status}`); + } + const data = await response.json(); + jobId.value = data.jobId; + // Reset form after success + setTimeout(() => { + form.value.fromDate = new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0]; + form.value.toDate = new Date().toISOString().split('T')[0]; + jobId.value = null; + }, 5000); + } + catch (error) { + console.error('Ingestion error:', error); + alert(`Failed to trigger ingestion: ${error instanceof Error ? error.message : 'Unknown error'}`); + } + finally { + isLoading.value = false; + } +}; +const resetForm = () => { + form.value = { + dataSource: 'KRX', + fromDate: new Date(Date.now() - 365 * 24 * 60 * 60 * 1000).toISOString().split('T')[0], + toDate: new Date().toISOString().split('T')[0], + }; + jobId.value = null; +}; +const __VLS_ctx = { + ...{}, + ...{}, +}; +let __VLS_components; +let __VLS_intrinsics; +let __VLS_directives; +/** @type {__VLS_StyleScopedClasses['header']} */ ; +/** @type {__VLS_StyleScopedClasses['config-card']} */ ; +/** @type {__VLS_StyleScopedClasses['summary-card']} */ ; +/** @type {__VLS_StyleScopedClasses['error-card']} */ ; +/** @type {__VLS_StyleScopedClasses['success-card']} */ ; +/** @type {__VLS_StyleScopedClasses['form-group']} */ ; +/** @type {__VLS_StyleScopedClasses['form-group']} */ ; +/** @type {__VLS_StyleScopedClasses['form-group']} */ ; +/** @type {__VLS_StyleScopedClasses['preset-btn']} */ ; +/** @type {__VLS_StyleScopedClasses['summary-item']} */ ; +/** @type {__VLS_StyleScopedClasses['summary-item']} */ ; +/** @type {__VLS_StyleScopedClasses['error-card']} */ ; +/** @type {__VLS_StyleScopedClasses['error-card']} */ ; +/** @type {__VLS_StyleScopedClasses['error-card']} */ ; +/** @type {__VLS_StyleScopedClasses['error-card']} */ ; +/** @type {__VLS_StyleScopedClasses['success-card']} */ ; +/** @type {__VLS_StyleScopedClasses['success-card']} */ ; +/** @type {__VLS_StyleScopedClasses['job-info']} */ ; +/** @type {__VLS_StyleScopedClasses['job-info']} */ ; +/** @type {__VLS_StyleScopedClasses['btn-primary']} */ ; +/** @type {__VLS_StyleScopedClasses['btn-primary']} */ ; +/** @type {__VLS_StyleScopedClasses['btn-primary']} */ ; +/** @type {__VLS_StyleScopedClasses['btn-secondary']} */ ; +/** @type {__VLS_StyleScopedClasses['btn-secondary']} */ ; +/** @type {__VLS_StyleScopedClasses['btn-link']} */ ; +/** @type {__VLS_StyleScopedClasses['btn-link']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "market-data-ingestion" }, +}); +/** @type {__VLS_StyleScopedClasses['market-data-ingestion']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "header" }, +}); +/** @type {__VLS_StyleScopedClasses['header']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.h1, __VLS_intrinsics.h1)({}); +__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({ + ...{ class: "subtitle" }, +}); +/** @type {__VLS_StyleScopedClasses['subtitle']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "content" }, +}); +/** @type {__VLS_StyleScopedClasses['content']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "config-card" }, +}); +/** @type {__VLS_StyleScopedClasses['config-card']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.h2, __VLS_intrinsics.h2)({}); +__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "form-group" }, +}); +/** @type {__VLS_StyleScopedClasses['form-group']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.label, __VLS_intrinsics.label)({}); +__VLS_asFunctionalElement1(__VLS_intrinsics.select, __VLS_intrinsics.select)({ + value: (__VLS_ctx.form.dataSource), +}); +__VLS_asFunctionalElement1(__VLS_intrinsics.option, __VLS_intrinsics.option)({ + value: "KRX", +}); +__VLS_asFunctionalElement1(__VLS_intrinsics.option, __VLS_intrinsics.option)({ + value: "OpenDart", +}); +__VLS_asFunctionalElement1(__VLS_intrinsics.option, __VLS_intrinsics.option)({ + value: "Stub", +}); +__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({ + ...{ class: "hint" }, +}); +/** @type {__VLS_StyleScopedClasses['hint']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({}); +__VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({}); +__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "form-row" }, +}); +/** @type {__VLS_StyleScopedClasses['form-row']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "form-group" }, +}); +/** @type {__VLS_StyleScopedClasses['form-group']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.label, __VLS_intrinsics.label)({}); +__VLS_asFunctionalElement1(__VLS_intrinsics.input)({ + type: "date", + min: (__VLS_ctx.minDate), + max: (__VLS_ctx.maxDate), + placeholder: "YYYY-MM-DD", +}); +(__VLS_ctx.form.fromDate); +__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({ + ...{ class: "hint" }, +}); +/** @type {__VLS_StyleScopedClasses['hint']} */ ; +(__VLS_ctx.minDate); +__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "form-group" }, +}); +/** @type {__VLS_StyleScopedClasses['form-group']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.label, __VLS_intrinsics.label)({}); +__VLS_asFunctionalElement1(__VLS_intrinsics.input)({ + type: "date", + min: (__VLS_ctx.form.fromDate || __VLS_ctx.minDate), + max: (__VLS_ctx.maxDate), + placeholder: "YYYY-MM-DD", +}); +(__VLS_ctx.form.toDate); +__VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({ + ...{ class: "hint" }, +}); +/** @type {__VLS_StyleScopedClasses['hint']} */ ; +(__VLS_ctx.maxDate); +__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "presets" }, +}); +/** @type {__VLS_StyleScopedClasses['presets']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({ + ...{ onClick: (...[$event]) => { + return (__VLS_ctx.setPreset('1y')); + // @ts-ignore + [form, form, form, form, minDate, minDate, minDate, maxDate, maxDate, maxDate, setPreset,]; + } }, + ...{ class: "preset-btn" }, +}); +/** @type {__VLS_StyleScopedClasses['preset-btn']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({ + ...{ onClick: (...[$event]) => { + return (__VLS_ctx.setPreset('2y')); + // @ts-ignore + [setPreset,]; + } }, + ...{ class: "preset-btn" }, +}); +/** @type {__VLS_StyleScopedClasses['preset-btn']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({ + ...{ onClick: (...[$event]) => { + return (__VLS_ctx.setPreset('5y')); + // @ts-ignore + [setPreset,]; + } }, + ...{ class: "preset-btn" }, +}); +/** @type {__VLS_StyleScopedClasses['preset-btn']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({ + ...{ onClick: (...[$event]) => { + return (__VLS_ctx.setPreset('all')); + // @ts-ignore + [setPreset,]; + } }, + ...{ class: "preset-btn" }, +}); +/** @type {__VLS_StyleScopedClasses['preset-btn']} */ ; +if (__VLS_ctx.validationErrors.length) { + __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "error-card" }, + }); + /** @type {__VLS_StyleScopedClasses['error-card']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.h3, __VLS_intrinsics.h3)({}); + __VLS_asFunctionalElement1(__VLS_intrinsics.ul, __VLS_intrinsics.ul)({}); + for (const [err, idx] of __VLS_vFor((__VLS_ctx.validationErrors))) { + __VLS_asFunctionalElement1(__VLS_intrinsics.li, __VLS_intrinsics.li)({ + key: (idx), + }); + (err); + // @ts-ignore + [validationErrors, validationErrors,]; + } +} +if (!__VLS_ctx.validationErrors.length) { + __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "summary-card" }, + }); + /** @type {__VLS_StyleScopedClasses['summary-card']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.h3, __VLS_intrinsics.h3)({}); + __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: "summary-item" }, + }); + /** @type {__VLS_StyleScopedClasses['summary-item']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({ + ...{ class: "label" }, + }); + /** @type {__VLS_StyleScopedClasses['label']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({ + ...{ class: "value" }, + }); + /** @type {__VLS_StyleScopedClasses['value']} */ ; + (__VLS_ctx.form.dataSource); + __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "summary-item" }, + }); + /** @type {__VLS_StyleScopedClasses['summary-item']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({ + ...{ class: "label" }, + }); + /** @type {__VLS_StyleScopedClasses['label']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({ + ...{ class: "value" }, + }); + /** @type {__VLS_StyleScopedClasses['value']} */ ; + (__VLS_ctx.form.fromDate); + (__VLS_ctx.form.toDate); + __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "summary-item" }, + }); + /** @type {__VLS_StyleScopedClasses['summary-item']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({ + ...{ class: "label" }, + }); + /** @type {__VLS_StyleScopedClasses['label']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({ + ...{ class: "value" }, + }); + /** @type {__VLS_StyleScopedClasses['value']} */ ; + (__VLS_ctx.daysCount); + __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "summary-item" }, + }); + /** @type {__VLS_StyleScopedClasses['summary-item']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({ + ...{ class: "label" }, + }); + /** @type {__VLS_StyleScopedClasses['label']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({ + ...{ class: "value" }, + }); + /** @type {__VLS_StyleScopedClasses['value']} */ ; + (__VLS_ctx.estimatedRows); +} +__VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "actions" }, +}); +/** @type {__VLS_StyleScopedClasses['actions']} */ ; +__VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({ + ...{ onClick: (__VLS_ctx.triggerIngestion) }, + disabled: (__VLS_ctx.isLoading || __VLS_ctx.validationErrors.length > 0), + ...{ class: "btn-primary" }, +}); +/** @type {__VLS_StyleScopedClasses['btn-primary']} */ ; +if (!__VLS_ctx.isLoading) { + __VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({}); +} +else { + __VLS_asFunctionalElement1(__VLS_intrinsics.span, __VLS_intrinsics.span)({}); +} +__VLS_asFunctionalElement1(__VLS_intrinsics.button, __VLS_intrinsics.button)({ + ...{ onClick: (__VLS_ctx.resetForm) }, + ...{ class: "btn-secondary" }, +}); +/** @type {__VLS_StyleScopedClasses['btn-secondary']} */ ; +if (__VLS_ctx.jobId) { + __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "success-card" }, + }); + /** @type {__VLS_StyleScopedClasses['success-card']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.h3, __VLS_intrinsics.h3)({}); + __VLS_asFunctionalElement1(__VLS_intrinsics.div, __VLS_intrinsics.div)({ + ...{ class: "job-info" }, + }); + /** @type {__VLS_StyleScopedClasses['job-info']} */ ; + __VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({}); + __VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({}); + (__VLS_ctx.jobId); + __VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({}); + __VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({}); + __VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({}); + __VLS_asFunctionalElement1(__VLS_intrinsics.strong, __VLS_intrinsics.strong)({}); + (new Date().toLocaleString()); + __VLS_asFunctionalElement1(__VLS_intrinsics.p, __VLS_intrinsics.p)({ + ...{ class: "hint" }, + }); + /** @type {__VLS_StyleScopedClasses['hint']} */ ; + let __VLS_0; + /** @ts-ignore @type { | typeof __VLS_components.routerLink | typeof __VLS_components.RouterLink | typeof __VLS_components['router-link'] | typeof __VLS_components.routerLink | typeof __VLS_components.RouterLink | typeof __VLS_components['router-link']} */ + routerLink; + // @ts-ignore + const __VLS_1 = __VLS_asFunctionalComponent1(__VLS_0, new __VLS_0({ + to: "/ops/market-data-history", + ...{ class: "btn-link" }, + })); + const __VLS_2 = __VLS_1({ + to: "/ops/market-data-history", + ...{ class: "btn-link" }, + }, ...__VLS_functionalComponentArgsRest(__VLS_1)); + /** @type {__VLS_StyleScopedClasses['btn-link']} */ ; + const { default: __VLS_5 } = __VLS_3.slots; + // @ts-ignore + [form, form, form, validationErrors, validationErrors, daysCount, estimatedRows, triggerIngestion, isLoading, isLoading, resetForm, jobId, jobId,]; + var __VLS_3; +} +// @ts-ignore +[]; +const __VLS_export = (await import('vue')).defineComponent({}); +export default {}; diff --git a/frontend/tsconfig.tsbuildinfo b/frontend/tsconfig.tsbuildinfo index 52fc2124..c8aa1702 100644 --- a/frontend/tsconfig.tsbuildinfo +++ b/frontend/tsconfig.tsbuildinfo @@ -1 +1 @@ -{"root":["./src/env.d.ts","./src/main.ts","./src/app/queryclient.ts","./src/app/router.ts","./src/features/data-quality/schema.ts","./src/features/data-quality/tests/schema.spec.ts","./src/features/model-operations/api.ts","./src/features/model-operations/queries.ts","./src/features/model-operations/schema.ts","./src/features/model-operations/tests/schema.spec.ts","./src/features/sell-decision/api.ts","./src/features/sell-decision/queries.ts","./src/features/sell-decision/schema.ts","./src/features/sell-decision/tests/schema.spec.ts","./src/shared/api/client.ts","./src/shared/api/problem.ts","./src/shared/commands/idempotency.ts","./src/shared/contracts/versionset.ts","./src/shared/crud/contracts.ts","./src/shared/crud/querycodec.ts","./src/shared/crud/resourcedefinition.ts","./src/shared/crud/usecrudliststate.ts","./src/shared/crud/useoptimisticcommand.ts","./src/shared/crud/tests/querycodec.spec.ts","./src/shared/formatters/financial.ts","./src/shared/formatters/tests/financial.spec.ts","./src/shared/ui/index.ts","./src/shared/ui/adapter/compatibility.ts","./src/shared/ui/adapter/contracts.ts","./src/shared/ui/adapter/useuiadapter.ts","./src/shared/ui/adapter/native/index.ts","./src/shared/ui/adapter/native/installnativeadapter.ts","./src/shared/ui/adapter/primevue/index.ts","./src/shared/ui/adapter/primevue/installprimevueadapter.ts","./src/shared/ui/adapter/tests/uiadapter.contract.spec.ts","./src/shared/ui/components/index.ts","./src/shared/ui/contracts/screencontract.ts","./src/shared/ui/layouts/index.ts","./src/shared/ui/provider/uiprovider.ts","./src/shared/ui/provider/index.ts","./src/shared/ui/provider/resolveuiprovider.ts","./src/shared/ui/screen-types/catalogue.ts","./src/shared/ui/screen-types/index.ts","./src/shared/ui/screen-types/tests/catalogue.spec.ts","./src/shared/ui/screen-types/v2/index.ts","./src/shared/ui/tests/adaptercompatibility.spec.ts","./src/shared/ui/tests/idempotency.spec.ts","./src/app.vue","./src/features/data-quality/pages/dataqualitypage.vue","./src/features/marketdata/pages/ingestionstatus.vue","./src/features/model-operations/components/automationboundarypanel.vue","./src/features/model-operations/components/modeloperationtable.vue","./src/features/model-operations/pages/modeloperationspage.vue","./src/features/portfolio/pages/rebalanceform.vue","./src/features/portfolio/pages/riskdashboard.vue","./src/features/sell-decision/components/policytracepanel.vue","./src/features/sell-decision/pages/selldecisionpage.vue","./src/features/ui-standard/pages/uistandardpage.vue","./src/shared/auth/permissionguard.vue","./src/shared/crud/standardcrudformpage.vue","./src/shared/crud/standardcrudlistpage.vue","./src/shared/status/datafreshnessbadge.vue","./src/shared/ui/datagridshell.vue","./src/shared/ui/evidenceversionset.vue","./src/shared/ui/querystateboundary.vue","./src/shared/ui/versionconflictdialog.vue","./src/shared/ui/adapter/native/nativebuttonadapter.vue","./src/shared/ui/adapter/native/nativecheckboxadapter.vue","./src/shared/ui/adapter/native/nativedatagridadapter.vue","./src/shared/ui/adapter/native/nativedatefieldadapter.vue","./src/shared/ui/adapter/native/nativedialogadapter.vue","./src/shared/ui/adapter/native/nativeinlinemessageadapter.vue","./src/shared/ui/adapter/native/nativemultiselectadapter.vue","./src/shared/ui/adapter/native/nativenumberfieldadapter.vue","./src/shared/ui/adapter/native/nativepaginatoradapter.vue","./src/shared/ui/adapter/native/nativeselectadapter.vue","./src/shared/ui/adapter/native/nativestatustagadapter.vue","./src/shared/ui/adapter/native/nativetabsadapter.vue","./src/shared/ui/adapter/native/nativetextareaadapter.vue","./src/shared/ui/adapter/native/nativetextfieldadapter.vue","./src/shared/ui/adapter/primevue/aggridadapter.vue","./src/shared/ui/adapter/primevue/primebuttonadapter.vue","./src/shared/ui/adapter/primevue/primecheckboxadapter.vue","./src/shared/ui/adapter/primevue/primedatefieldadapter.vue","./src/shared/ui/adapter/primevue/primedialogadapter.vue","./src/shared/ui/adapter/primevue/primeinlinemessageadapter.vue","./src/shared/ui/adapter/primevue/primemultiselectadapter.vue","./src/shared/ui/adapter/primevue/primenumberfieldadapter.vue","./src/shared/ui/adapter/primevue/primepaginatoradapter.vue","./src/shared/ui/adapter/primevue/primeselectadapter.vue","./src/shared/ui/adapter/primevue/primestatustagadapter.vue","./src/shared/ui/adapter/primevue/primetabsadapter.vue","./src/shared/ui/adapter/primevue/primetextareaadapter.vue","./src/shared/ui/adapter/primevue/primetextfieldadapter.vue","./src/shared/ui/components/fieldshell.vue","./src/shared/ui/components/ksbutton.vue","./src/shared/ui/components/kscheckbox.vue","./src/shared/ui/components/kscommandbar.vue","./src/shared/ui/components/ksdatacontextheader.vue","./src/shared/ui/components/ksdatagrid.vue","./src/shared/ui/components/ksdatefield.vue","./src/shared/ui/components/ksdialog.vue","./src/shared/ui/components/ksinlinemessage.vue","./src/shared/ui/components/ksmultiselect.vue","./src/shared/ui/components/ksnumberfield.vue","./src/shared/ui/components/kspaginator.vue","./src/shared/ui/components/ksselect.vue","./src/shared/ui/components/ksstatustag.vue","./src/shared/ui/components/kstabs.vue","./src/shared/ui/components/kstextarea.vue","./src/shared/ui/components/kstextfield.vue","./src/shared/ui/feedback/standardstatepanel.vue","./src/shared/ui/layouts/appshelllayout.vue","./src/shared/ui/layouts/crudworkspacelayout.vue","./src/shared/ui/layouts/dashboardlayout.vue","./src/shared/ui/layouts/formpagelayout.vue","./src/shared/ui/layouts/operationsconsolelayout.vue","./src/shared/ui/layouts/pagelayout.vue","./src/shared/ui/layouts/reviewworkbenchlayout.vue","./src/shared/ui/screen-types/batchoperationspage.vue","./src/shared/ui/screen-types/cruddetailpage.vue","./src/shared/ui/screen-types/crudformpage.vue","./src/shared/ui/screen-types/crudlistpage.vue","./src/shared/ui/screen-types/crudreviewpage.vue","./src/shared/ui/screen-types/v2/approvalworkbenchpage.vue","./src/shared/ui/screen-types/v2/batchoperationspagev2.vue","./src/shared/ui/screen-types/v2/detailreadpage.vue","./src/shared/ui/screen-types/v2/editformpage.vue","./src/shared/ui/screen-types/v2/masterdetailcrudpage.vue","./src/shared/ui/screen-types/v2/reconciliationexceptionpage.vue","./src/shared/ui/screen-types/v2/scorecarddashboardpage.vue","./src/shared/ui/screen-types/v2/searchlistcrudpage.vue","./src/shared/ui/screen-types/v2/standardscreenboundary.vue","./src/shared/ui/screen-types/v2/stepwizardpage.vue","./src/shared/ui/screen-types/v2/versiongovernancepage.vue","./vite.config.ts"],"version":"5.9.3"} \ No newline at end of file +{"root":["./src/env.d.ts","./src/main.ts","./src/app/queryclient.ts","./src/app/router.ts","./src/features/data-quality/schema.ts","./src/features/data-quality/tests/schema.spec.ts","./src/features/model-operations/api.ts","./src/features/model-operations/queries.ts","./src/features/model-operations/schema.ts","./src/features/model-operations/tests/schema.spec.ts","./src/features/sell-decision/api.ts","./src/features/sell-decision/queries.ts","./src/features/sell-decision/schema.ts","./src/features/sell-decision/tests/schema.spec.ts","./src/shared/api/client.ts","./src/shared/api/problem.ts","./src/shared/commands/idempotency.ts","./src/shared/contracts/versionset.ts","./src/shared/crud/contracts.ts","./src/shared/crud/querycodec.ts","./src/shared/crud/resourcedefinition.ts","./src/shared/crud/usecrudliststate.ts","./src/shared/crud/useoptimisticcommand.ts","./src/shared/crud/tests/querycodec.spec.ts","./src/shared/formatters/financial.ts","./src/shared/formatters/tests/financial.spec.ts","./src/shared/ui/index.ts","./src/shared/ui/adapter/compatibility.ts","./src/shared/ui/adapter/contracts.ts","./src/shared/ui/adapter/useuiadapter.ts","./src/shared/ui/adapter/native/index.ts","./src/shared/ui/adapter/native/installnativeadapter.ts","./src/shared/ui/adapter/primevue/index.ts","./src/shared/ui/adapter/primevue/installprimevueadapter.ts","./src/shared/ui/adapter/tests/uiadapter.contract.spec.ts","./src/shared/ui/components/index.ts","./src/shared/ui/contracts/screencontract.ts","./src/shared/ui/layouts/index.ts","./src/shared/ui/provider/uiprovider.ts","./src/shared/ui/provider/index.ts","./src/shared/ui/provider/resolveuiprovider.ts","./src/shared/ui/screen-types/catalogue.ts","./src/shared/ui/screen-types/index.ts","./src/shared/ui/screen-types/tests/catalogue.spec.ts","./src/shared/ui/screen-types/v2/index.ts","./src/shared/ui/tests/adaptercompatibility.spec.ts","./src/shared/ui/tests/idempotency.spec.ts","./src/app.vue","./src/features/data-quality/pages/dataqualitypage.vue","./src/features/marketdata/pages/ingestionstatus.vue","./src/features/marketdata/pages/marketdataingestion.vue","./src/features/model-operations/components/automationboundarypanel.vue","./src/features/model-operations/components/modeloperationtable.vue","./src/features/model-operations/pages/modeloperationspage.vue","./src/features/portfolio/pages/rebalanceform.vue","./src/features/portfolio/pages/riskdashboard.vue","./src/features/sell-decision/components/policytracepanel.vue","./src/features/sell-decision/pages/selldecisionpage.vue","./src/features/ui-standard/pages/uistandardpage.vue","./src/shared/auth/permissionguard.vue","./src/shared/crud/standardcrudformpage.vue","./src/shared/crud/standardcrudlistpage.vue","./src/shared/status/datafreshnessbadge.vue","./src/shared/ui/datagridshell.vue","./src/shared/ui/evidenceversionset.vue","./src/shared/ui/querystateboundary.vue","./src/shared/ui/versionconflictdialog.vue","./src/shared/ui/adapter/native/nativebuttonadapter.vue","./src/shared/ui/adapter/native/nativecheckboxadapter.vue","./src/shared/ui/adapter/native/nativedatagridadapter.vue","./src/shared/ui/adapter/native/nativedatefieldadapter.vue","./src/shared/ui/adapter/native/nativedialogadapter.vue","./src/shared/ui/adapter/native/nativeinlinemessageadapter.vue","./src/shared/ui/adapter/native/nativemultiselectadapter.vue","./src/shared/ui/adapter/native/nativenumberfieldadapter.vue","./src/shared/ui/adapter/native/nativepaginatoradapter.vue","./src/shared/ui/adapter/native/nativeselectadapter.vue","./src/shared/ui/adapter/native/nativestatustagadapter.vue","./src/shared/ui/adapter/native/nativetabsadapter.vue","./src/shared/ui/adapter/native/nativetextareaadapter.vue","./src/shared/ui/adapter/native/nativetextfieldadapter.vue","./src/shared/ui/adapter/primevue/aggridadapter.vue","./src/shared/ui/adapter/primevue/primebuttonadapter.vue","./src/shared/ui/adapter/primevue/primecheckboxadapter.vue","./src/shared/ui/adapter/primevue/primedatefieldadapter.vue","./src/shared/ui/adapter/primevue/primedialogadapter.vue","./src/shared/ui/adapter/primevue/primeinlinemessageadapter.vue","./src/shared/ui/adapter/primevue/primemultiselectadapter.vue","./src/shared/ui/adapter/primevue/primenumberfieldadapter.vue","./src/shared/ui/adapter/primevue/primepaginatoradapter.vue","./src/shared/ui/adapter/primevue/primeselectadapter.vue","./src/shared/ui/adapter/primevue/primestatustagadapter.vue","./src/shared/ui/adapter/primevue/primetabsadapter.vue","./src/shared/ui/adapter/primevue/primetextareaadapter.vue","./src/shared/ui/adapter/primevue/primetextfieldadapter.vue","./src/shared/ui/components/fieldshell.vue","./src/shared/ui/components/ksbutton.vue","./src/shared/ui/components/kscheckbox.vue","./src/shared/ui/components/kscommandbar.vue","./src/shared/ui/components/ksdatacontextheader.vue","./src/shared/ui/components/ksdatagrid.vue","./src/shared/ui/components/ksdatefield.vue","./src/shared/ui/components/ksdialog.vue","./src/shared/ui/components/ksinlinemessage.vue","./src/shared/ui/components/ksmultiselect.vue","./src/shared/ui/components/ksnumberfield.vue","./src/shared/ui/components/kspaginator.vue","./src/shared/ui/components/ksselect.vue","./src/shared/ui/components/ksstatustag.vue","./src/shared/ui/components/kstabs.vue","./src/shared/ui/components/kstextarea.vue","./src/shared/ui/components/kstextfield.vue","./src/shared/ui/feedback/standardstatepanel.vue","./src/shared/ui/layouts/appshelllayout.vue","./src/shared/ui/layouts/crudworkspacelayout.vue","./src/shared/ui/layouts/dashboardlayout.vue","./src/shared/ui/layouts/formpagelayout.vue","./src/shared/ui/layouts/operationsconsolelayout.vue","./src/shared/ui/layouts/pagelayout.vue","./src/shared/ui/layouts/reviewworkbenchlayout.vue","./src/shared/ui/screen-types/batchoperationspage.vue","./src/shared/ui/screen-types/cruddetailpage.vue","./src/shared/ui/screen-types/crudformpage.vue","./src/shared/ui/screen-types/crudlistpage.vue","./src/shared/ui/screen-types/crudreviewpage.vue","./src/shared/ui/screen-types/v2/approvalworkbenchpage.vue","./src/shared/ui/screen-types/v2/batchoperationspagev2.vue","./src/shared/ui/screen-types/v2/detailreadpage.vue","./src/shared/ui/screen-types/v2/editformpage.vue","./src/shared/ui/screen-types/v2/masterdetailcrudpage.vue","./src/shared/ui/screen-types/v2/reconciliationexceptionpage.vue","./src/shared/ui/screen-types/v2/scorecarddashboardpage.vue","./src/shared/ui/screen-types/v2/searchlistcrudpage.vue","./src/shared/ui/screen-types/v2/standardscreenboundary.vue","./src/shared/ui/screen-types/v2/stepwizardpage.vue","./src/shared/ui/screen-types/v2/versiongovernancepage.vue","./vite.config.ts"],"version":"5.9.3"} \ No newline at end of file