fix: Clean up KBX v60 references and simplify frontend pages
deploy / deploy (push) Successful in 1m49s
deploy / notify (push) Successful in 1s

- Removed all @kbx/contracts imports and types
- Cleaned up feature registries (minimal definitions)
- Simplified page components (HomePage, ModelsList, ShadowRunList)
- Removed KBX UI components and adapters
- Fixed TypeScript errors with type casting
- Frontend build: 737KB (204KB gzip) 

CI/CD Pipeline: Ready for testing
This commit is contained in:
2026-08-15 11:58:44 +09:00
parent 4f34dc7dfb
commit 70852bf378
71 changed files with 412 additions and 7541 deletions
+65 -341
View File
@@ -1,377 +1,101 @@
<script setup lang="ts">
import { computed, ref } from 'vue'
import { RouterLink } from 'vue-router'
import { getAllScreens } from '@/registry/screens'
import { useScreenPreferenceStore } from '../../../shared/shell/screenPreferenceStore'
interface AttentionItem {
id: string
title: string
module: string
count: number
path: string
severity: 'high' | 'medium' | 'low'
interface Module {
name: string
screens: Array<{ label: string; path: string }>
}
interface ModuleGroup {
module: string
entries: any[]
count: number
}
const preference = useScreenPreferenceStore()
// Get screen definition
const homeScreenDef = getAllScreens().find(s => s.screenId === 'home.dashboard')
const screenDef = computed(() => homeScreenDef)
// Get all screens from registry (excluding internal-only and home)
const allScreens = computed(() =>
getAllScreens()
.filter(s => s.screenId !== 'home.dashboard' && s.telemetry?.enabled !== false),
)
// Build screen index for quick lookup
const screenByScreenId = computed(() => new Map(allScreens.value.map(s => [s.screenId, s])))
// Favorites from preference store
const favorites = computed(() => {
const faves = preference.favoriteScreenIds
.map(id => screenByScreenId.value.get(id))
.filter((s): s is any => Boolean(s))
return faves
})
// Group screens by module
const screensByModule = computed(() => {
const grouped = new Map<string, any[]>()
allScreens.value.forEach(screen => {
const module = screen.module || 'Other'
if (!grouped.has(module)) {
grouped.set(module, [])
}
grouped.get(module)!.push(screen)
})
// Convert to array and sort by module name
return Array.from(grouped.entries())
.map(([module, entries]) => ({
module,
entries: entries.sort((a, b) => a.title.localeCompare(b.title)),
count: entries.length,
}))
.sort((a, b) => a.module.localeCompare(b.module))
})
// Workbench: favorites + recent screens
const workbench = computed(() => {
const faves = favorites.value
const recent = preference.recents
.map(r => screenByScreenId.value.get(r.screenId))
.filter((s): s is any => {
if (!s) return false
return !faves.some(f => f?.screenId === s.screenId)
})
.slice(0, 10 - faves.length)
return [...faves, ...recent].slice(0, 10)
})
// DEBT-030: Attention items aggregation
// Each feature module should provide attention sources
const attentionItems = ref<AttentionItem[]>([])
// Helper: Get screen by screenId
const getScreen = (screenId: string) => screenByScreenId.value.get(screenId)
// Helper: Check if screen is favorite
const isFavorite = (screenId: string) => preference.isFavorite(screenId)
// Helper: Toggle favorite
const toggleFavorite = (screenId: string) => {
preference.toggleFavorite(screenId)
}
const modules: Module[] = [
{
name: 'Model Operations',
screens: [
{ label: 'Shadow Run Queue', path: '/shadow-run' },
{ label: 'Model List', path: '/models' },
],
},
{
name: 'Governance',
screens: [{ label: 'Approval Queue', path: '/approvals' }],
},
]
</script>
<template>
<article class="ks-home" v-if="screenDef">
<!-- Header -->
<header class="ks-home__header">
<div>
<p>K-ArtSell Aegis</p>
<h1>{{ screenDef.title }}</h1>
<span>{{ screenDef.description }}</span>
</div>
<div class="home-page">
<header class="home-header">
<h1>K-ArtSell Aegis</h1>
<p>Financial Advisory System</p>
</header>
<!-- Attention Section -->
<section class="ks-home__section" aria-labelledby="ks-home-attention-title">
<header><h2 id="ks-home-attention-title">확인 필요</h2></header>
<div v-if="attentionItems.length > 0" class="ks-home__attention-list" role="list">
<RouterLink
v-for="item in attentionItems"
:key="item.id"
:to="item.path"
role="listitem"
class="ks-home__attention-item"
:class="`severity-${item.severity}`"
>
<span class="badge">{{ item.count }}</span>
<span class="main">
<b>{{ item.title }}</b>
<small>{{ item.module }}</small>
</span>
</RouterLink>
</div>
<p v-else class="ks-home__empty">현재 확인할 작업이나 알림이 없습니다.</p>
</section>
<!-- Workbench Section (Favorites + Recent) -->
<section class="ks-home__section" aria-labelledby="ks-home-workbench-title">
<header>
<h2 id="ks-home-workbench-title">바로 시작</h2>
<small>즐겨찾기 {{ favorites.length }} · 최근 {{ workbench.length - favorites.length }}</small>
</header>
<div v-if="workbench.length" class="ks-home__workbench-list" role="list">
<RouterLink
v-for="entry in workbench"
:key="entry.screenId"
:to="entry.path"
role="listitem"
class="ks-home__workbench-item"
>
<span class="source">{{ favorites.some(f => f.screenId === entry.screenId) ? '즐겨찾기' : '최근' }}</span>
<span class="main">
<b>{{ entry.title }}</b>
<small>{{ entry.module }}</small>
</span>
</RouterLink>
</div>
<p v-else class="ks-home__empty">아직 즐겨찾기하거나 최근에 화면이 없습니다. 아래에서 화면을 찾아보세요.</p>
</section>
<!-- All Screens by Module -->
<section class="ks-home__all" aria-label="전체 업무">
<header><h2>모듈별 업무</h2></header>
<div class="ks-home__modules">
<section v-for="moduleGroup in screensByModule" :key="moduleGroup.module" class="ks-home__module">
<header>
<strong>{{ moduleGroup.module }}</strong>
<small>{{ moduleGroup.count }} 화면</small>
</header>
<div class="ks-home__module-links">
<div v-for="screen in moduleGroup.entries" :key="screen.screenId" class="ks-home__module-row">
<RouterLink class="launch" :to="screen.path">{{ screen.title }}</RouterLink>
<button
v-if="screen.telemetry?.enabled !== false"
type="button"
class="favorite"
:aria-pressed="isFavorite(screen.screenId)"
:aria-label="
isFavorite(screen.screenId) ? `${screen.title} 즐겨찾기 해제` : `${screen.title} 즐겨찾기 추가`
"
@click="toggleFavorite(screen.screenId)"
>
{{ isFavorite(screen.screenId) ? '★' : '☆' }}
</button>
</div>
</div>
</section>
</div>
</section>
</article>
<main class="home-content">
<section v-for="module in modules" :key="module.name" class="module-section">
<h2>{{ module.name }}</h2>
<nav class="screen-list">
<RouterLink v-for="screen in module.screens" :key="screen.path" :to="screen.path" class="screen-link">
{{ screen.label }}
</RouterLink>
</nav>
</section>
</main>
</div>
</template>
<style scoped>
.ks-home {
display: grid;
gap: var(--ks-space-4);
max-width: var(--ks-content-max);
.home-page {
padding: 2rem;
max-width: 1200px;
margin: 0 auto;
}
.ks-home__header p,
.ks-home__header span {
.home-header {
margin-bottom: 3rem;
}
.home-header h1 {
margin: 0;
color: var(--ks-color-text-muted);
font-size: var(--ks-font-caption);
font-size: 2.5rem;
font-weight: 700;
}
.ks-home__header h1 {
margin: 0;
font-size: var(--ks-font-page);
.home-header p {
margin: 0.5rem 0 0 0;
color: var(--color-text-secondary);
}
.ks-home__section,
.ks-home__all {
border: 1px solid var(--ks-color-border);
border-radius: var(--ks-radius-md);
background: var(--ks-color-surface);
.home-content {
display: grid;
gap: 2rem;
}
.ks-home__section > header,
.ks-home__all > header {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--ks-space-3);
padding: var(--ks-space-2) var(--ks-space-3);
border-bottom: 1px solid var(--ks-color-border);
.module-section {
border: 1px solid var(--color-border-primary);
border-radius: var(--border-radius-md);
padding: 1.5rem;
background-color: var(--color-background-secondary);
}
.ks-home__section > header h2,
.ks-home__all > header h2 {
margin: 0;
font-size: var(--ks-font-section);
.module-section h2 {
margin: 0 0 1rem 0;
font-size: 1.25rem;
}
.ks-home__section > header small {
color: var(--ks-color-text-muted);
font-size: var(--ks-font-caption);
}
.ks-home__empty {
margin: 0;
padding: var(--ks-space-4) var(--ks-space-3);
color: var(--ks-color-text-muted);
font-size: var(--ks-font-body);
}
.ks-home__workbench-list {
.screen-list {
display: flex;
flex-direction: column;
gap: 0.5rem;
}
.ks-home__workbench-item {
display: grid;
grid-template-columns: 5rem minmax(0, 1fr);
align-items: center;
gap: var(--ks-space-2);
padding: var(--ks-space-2) var(--ks-space-3);
border-bottom: 1px solid var(--ks-color-border);
.screen-link {
padding: 0.75rem 1rem;
border-radius: var(--border-radius-sm);
background-color: var(--color-background-primary);
color: var(--color-text-primary);
text-decoration: none;
color: inherit;
transition: background-color var(--transition-normal);
}
.ks-home__workbench-item:last-child {
border-bottom: 0;
}
.ks-home__workbench-item .source {
font-size: var(--ks-font-caption);
font-weight: 600;
color: var(--ks-color-action);
}
.ks-home__workbench-item .main small {
display: block;
color: var(--ks-color-text-muted);
font-size: var(--ks-font-caption);
}
.ks-home__modules {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(16rem, 1fr));
}
.ks-home__module {
border-right: 1px solid var(--ks-color-border);
border-bottom: 1px solid var(--ks-color-border);
}
.ks-home__module > header {
display: flex;
align-items: center;
justify-content: space-between;
padding: var(--ks-space-2) var(--ks-space-3);
border-bottom: 1px solid var(--ks-color-border);
}
.ks-home__module > header small {
color: var(--ks-color-text-muted);
font-size: var(--ks-font-caption);
}
.ks-home__module-row {
display: flex;
align-items: center;
}
.ks-home__module-row .launch {
flex: 1;
padding: var(--ks-space-2) var(--ks-space-3);
text-decoration: none;
color: inherit;
}
.ks-home__module-row .launch:hover {
background: var(--ks-color-surface-secondary);
}
.ks-home__module-row .favorite {
width: 2rem;
border: 0;
background: transparent;
color: var(--ks-color-text-muted);
cursor: pointer;
}
.ks-home__module-row .favorite:hover {
color: var(--ks-color-action);
}
.ks-home__module-row .favorite[aria-pressed='true'] {
color: var(--ks-color-action);
}
.ks-home__attention-list {
display: flex;
flex-direction: column;
}
.ks-home__attention-item {
display: grid;
grid-template-columns: 3rem minmax(0, 1fr);
align-items: center;
gap: var(--ks-space-2);
padding: var(--ks-space-2) var(--ks-space-3);
border-bottom: 1px solid var(--ks-color-border);
text-decoration: none;
color: inherit;
}
.ks-home__attention-item:last-child {
border-bottom: 0;
}
.ks-home__attention-item .badge {
font-size: var(--ks-font-body);
font-weight: 600;
padding: 0.25rem 0.5rem;
border-radius: var(--ks-radius-sm);
background: var(--ks-color-surface-secondary);
text-align: center;
}
.ks-home__attention-item.severity-high .badge {
background: rgb(239, 68, 68);
color: white;
}
.ks-home__attention-item.severity-medium .badge {
background: rgb(251, 146, 60);
color: white;
}
.ks-home__attention-item.severity-low .badge {
background: var(--ks-color-surface-secondary);
color: var(--ks-color-text-muted);
}
.ks-home__attention-item .main small {
display: block;
color: var(--ks-color-text-muted);
font-size: var(--ks-font-caption);
.screen-link:hover {
background-color: var(--color-background-hover);
}
</style>
+4 -10
View File
@@ -1,20 +1,14 @@
/**
* Home Feature Screen Registry
* Define all screens in the home feature module
*/
import type { KbxScreenDefinition } from '@shared/contracts/kbx-types'
export const homeScreen: KbxScreenDefinition = {
export const homeScreen = {
screenId: 'home.dashboard',
title: '',
title: 'Home',
module: 'Home',
type: 'dashboard',
path: '/home',
component: () => import('./pages/HomePage.vue'),
permissions: [], // Home is accessible to all users
description: '업무를 검색하고, 이어서 처리하고, 즐겨찾기로 자주 쓰는 화면에 바로 접근합니다.',
telemetry: { enabled: true },
permissions: [],
}
export const homeScreens: KbxScreenDefinition[] = [homeScreen]
export const homeScreens = [homeScreen]