diff --git a/frontend/src/app/installKbx.ts b/frontend/src/app/installKbx.ts index 3ef08fdf..5bc43e0a 100644 --- a/frontend/src/app/installKbx.ts +++ b/frontend/src/app/installKbx.ts @@ -1,146 +1,20 @@ /** - * KBX Foundation v4 App Initialization - * Bootstraps screen registry, permissions, and UI adapter + * App Initialization (minimal) */ import type { App } from 'vue' -import type { KbxScreenDefinition, KbxPermissionDefinition, KbxDensity } from '@shared/contracts/kbx-types' -// Global state -let screenRegistry: Map = new Map() -let permissionRegistry: Map = new Map() let userPermissions: Set = new Set() -let currentDensity: KbxDensity = 'compact' -/** - * Register screen definitions from all modules - */ -export function registerScreens(screens: KbxScreenDefinition[]) { - screens.forEach(screen => { - screenRegistry.set(screen.screenId, screen) - }) -} - -/** - * Register permission definitions - */ -export function registerPermissions(permissions: KbxPermissionDefinition[]) { - permissions.forEach(perm => { - permissionRegistry.set(perm.permissionId, perm) - }) -} - -/** - * Set user permissions (called after auth) - */ export function setUserPermissions(permissions: string[]) { userPermissions.clear() permissions.forEach(p => userPermissions.add(p)) } -/** - * Check if user has permission - */ export function hasPermission(permissionId: string): boolean { return userPermissions.has(permissionId) } -/** - * Check if user has all permissions - */ -export function hasAllPermissions(permissionIds: string[]): boolean { - return permissionIds.every(id => userPermissions.has(id)) -} - -/** - * Get screen by ID - */ -export function getScreen(screenId: string): KbxScreenDefinition | undefined { - return screenRegistry.get(screenId) -} - -/** - * Get all screens - */ -export function getAllScreens(): KbxScreenDefinition[] { - return Array.from(screenRegistry.values()) -} - -/** - * Set density (compact, comfortable, touch) - */ -export function setDensity(density: KbxDensity) { - currentDensity = density - // Apply to DOM - document.documentElement.style.setProperty('--kbx-density', density) - - // Update tokens based on density - const tokens = { - compact: { - inputHeight: '34px', - gridRowHeight: '34px', - touchTarget: '44px', - fontSize: '12px', - }, - comfortable: { - inputHeight: '36px', - gridRowHeight: '36px', - touchTarget: '48px', - fontSize: '14px', - }, - touch: { - inputHeight: '48px', - gridRowHeight: '48px', - touchTarget: '52px', - fontSize: '16px', - }, - } - - Object.entries(tokens[density]).forEach(([key, value]) => { - document.documentElement.style.setProperty(`--kbx-${key}`, value) - }) -} - -/** - * Vue plugin install - */ export function installKbx(app: App) { - // Provide global registry access - app.provide('kbx-screens', screenRegistry) - app.provide('kbx-permissions', permissionRegistry) - - // Global methods - app.config.globalProperties.$kbx = { - hasPermission, - hasAllPermissions, - getScreen, - getAllScreens, - setDensity, - } - - // Initialize default density - setDensity('compact') - - // Apply theme colors - document.documentElement.style.setProperty('--kbx-color-primary', '#3b82f6') - document.documentElement.style.setProperty('--kbx-color-danger', '#ef4444') - document.documentElement.style.setProperty('--kbx-color-success', '#10b981') - document.documentElement.style.setProperty('--kbx-color-border', '#e5e7eb') - document.documentElement.style.setProperty('--kbx-color-text', '#000000') - document.documentElement.style.setProperty('--kbx-color-text-muted', '#6b7280') - document.documentElement.style.setProperty('--kbx-color-background', '#ffffff') - document.documentElement.style.setProperty('--kbx-color-surface', '#ffffff') - document.documentElement.style.setProperty('--kbx-color-shell-chrome', '#f9fafb') -} - -// Composable for component usage -export function useKbx() { - return { - hasPermission, - hasAllPermissions, - getScreen, - getAllScreens, - setDensity, - screenRegistry: () => getAllScreens(), - } + app.config.globalProperties.$permissions = userPermissions } diff --git a/frontend/src/features/approval/registry.ts b/frontend/src/features/approval/registry.ts index f50edc72..2793688d 100644 --- a/frontend/src/features/approval/registry.ts +++ b/frontend/src/features/approval/registry.ts @@ -2,47 +2,13 @@ * Approval Feature Screen Registry */ -import type { ScreenDefinition } from '@kbx/contracts' - -export const approvalQueueScreen: ScreenDefinition = { +export const approvalQueueScreen = { screenId: 'governance.approval.queue', title: 'Approval Queue', module: 'ERP', path: '/governance/approvals', component: () => import('./pages/ApprovalQueue.vue'), permissions: ['approval.review'], - template: 'T03', - - help: { - title: 'Approval Workflow', - sections: [ - { - title: 'What is Maker-Checker?', - content: - 'Maker-Checker enforces that critical model decisions require two parties: the requester and an independent reviewer.', - }, - { - title: 'How to Approve', - content: 'Select a pending request, review the metrics and comments, then approve or reject with your decision.', - }, - { - title: 'Decision Criteria', - content: 'Activation requires: PBO ≤ 20%, DSR ≥ 95%, OOS ≤ 2.5%, plus 252+ trading-day shadow run.', - }, - ], - relatedScreens: ['model-ops.models.list', 'model-ops.shadow-run.queue'], - }, - - grid: { - columnDefs: [ - { field: 'requestId', headerName: 'Request ID', width: 120 }, - { field: 'modelName', headerName: 'Model', width: 150 }, - { field: 'action', headerName: 'Action', width: 100 }, - { field: 'status', headerName: 'Status', width: 100 }, - { field: 'requesterName', headerName: 'Requester', width: 120 }, - { field: 'requestedAt', headerName: 'Date', width: 150 }, - ], - }, } export default [approvalQueueScreen] diff --git a/frontend/src/features/home/pages/HomePage.vue b/frontend/src/features/home/pages/HomePage.vue index c026ee45..caf99817 100644 --- a/frontend/src/features/home/pages/HomePage.vue +++ b/frontend/src/features/home/pages/HomePage.vue @@ -1,377 +1,101 @@ diff --git a/frontend/src/features/home/registry.ts b/frontend/src/features/home/registry.ts index 3c165d9f..e2008442 100644 --- a/frontend/src/features/home/registry.ts +++ b/frontend/src/features/home/registry.ts @@ -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] diff --git a/frontend/src/features/models/pages/ModelDetail.vue b/frontend/src/features/models/pages/ModelDetail.vue index b7dbd9ed..43ae2cff 100644 --- a/frontend/src/features/models/pages/ModelDetail.vue +++ b/frontend/src/features/models/pages/ModelDetail.vue @@ -1,607 +1,110 @@ diff --git a/frontend/src/features/models/pages/ModelsList.vue b/frontend/src/features/models/pages/ModelsList.vue index db481547..5e23e297 100644 --- a/frontend/src/features/models/pages/ModelsList.vue +++ b/frontend/src/features/models/pages/ModelsList.vue @@ -1,237 +1,130 @@ diff --git a/frontend/src/features/models/registry.ts b/frontend/src/features/models/registry.ts index 2fc1c9f0..c7df0f55 100644 --- a/frontend/src/features/models/registry.ts +++ b/frontend/src/features/models/registry.ts @@ -1,96 +1,23 @@ /** * Models Feature Screen Registry - * Define all screens in the models feature module */ -import type { ScreenDefinition } from '@kbx/contracts' - -export const modelsListScreen: ScreenDefinition = { +export const modelsListScreen = { screenId: 'model-ops.models.list', title: 'Model Management', module: 'ModelOps', - type: 'list', path: '/model-ops/models', - component: () => import('./pages/ModelsList.vue'), + component: () => import('./pages/ModelList.vue'), permissions: ['model.read'], - description: 'Manage trading models across their complete lifecycle', - - help: { - title: 'Model Lifecycle', - sections: [ - { - title: 'Phases', - content: - 'Models progress: Freeze → Mature → Score → Diagnose → Hypothesis → Challenger → Validate → Review → Manual Activation', - }, - { - title: 'Getting Started', - content: 'Click "New" to create a model, or select an existing one to view details and manage transitions.', - }, - ], - relatedScreens: ['model-ops.shadow-run.list'], - }, - - grid: { - columnDefs: [ - { field: 'modelId', header: 'Model ID', type: 'link', width: 150, pinned: 'left' }, - { field: 'name', header: 'Name', width: 200 }, - { field: 'phase', header: 'Phase', type: 'status', width: 120 }, - { field: 'active', header: 'Active', type: 'text', width: 80 }, - { field: 'lastValidation', header: 'Last Validation', type: 'datetime', width: 150 }, - { field: 'pbo', header: 'PBO', type: 'percentage', width: 80 }, - { field: 'dsr', header: 'DSR', type: 'percentage', width: 80 }, - { field: 'returnMtd', header: 'Return (YTD)', type: 'money', width: 120 }, - { field: 'createdAt', header: 'Created', type: 'datetime', width: 150 }, - ], - pageSize: 50, - serverSideDatasource: true, - }, - - shortcuts: [ - { key: 'F3', label: 'Search', action: 'search' }, - { key: 'Ctrl+N', label: 'New Model', action: 'new' }, - ], - - telemetry: { enabled: true }, } -export const modelsDetailScreen: ScreenDefinition = { +export const modelsDetailScreen = { screenId: 'model-ops.models.detail', title: 'Model Details', module: 'ModelOps', - type: 'detail', path: '/model-ops/models/:modelId', component: () => import('./pages/ModelDetail.vue'), permissions: ['model.read'], - description: 'View and manage model configuration, validation history, and phase transitions', - - help: { - title: 'Model Management', - sections: [ - { - title: 'Activation Requirements', - content: - 'Before activating a model: 252+ trading-day shadow run, PBO < 20%, DSR > 0.5, OOS < 2.5%, plus maker-checker approval.', - }, - { - title: 'Phase Transitions', - content: - 'Models cannot auto-promote. Each phase requires explicit review and approval. Check phase breakdown for regime-specific performance.', - }, - ], - relatedScreens: ['model-ops.models.list', 'model-ops.shadow-run.list'], - }, - - shortcuts: [ - { key: 'Escape', label: 'Back to List', action: 'back' }, - { key: 'Ctrl+E', label: 'Export Report', action: 'export' }, - ], - - telemetry: { enabled: true }, } -/** - * All screens in models module - */ -export const modelScreens: ScreenDefinition[] = [modelsListScreen, modelsDetailScreen] +export const modelScreens = [modelsListScreen, modelsDetailScreen] diff --git a/frontend/src/features/shadow-run/pages/ShadowRunDetail.vue b/frontend/src/features/shadow-run/pages/ShadowRunDetail.vue index 2a6371c2..25540bbb 100644 --- a/frontend/src/features/shadow-run/pages/ShadowRunDetail.vue +++ b/frontend/src/features/shadow-run/pages/ShadowRunDetail.vue @@ -1,420 +1,118 @@ diff --git a/frontend/src/features/shadow-run/pages/ShadowRunList.vue b/frontend/src/features/shadow-run/pages/ShadowRunList.vue index 4e91abd3..868c179a 100644 --- a/frontend/src/features/shadow-run/pages/ShadowRunList.vue +++ b/frontend/src/features/shadow-run/pages/ShadowRunList.vue @@ -1,252 +1,134 @@ diff --git a/frontend/src/features/shadow-run/registry.ts b/frontend/src/features/shadow-run/registry.ts index 0dc8bcab..eb6ccad6 100644 --- a/frontend/src/features/shadow-run/registry.ts +++ b/frontend/src/features/shadow-run/registry.ts @@ -1,103 +1,14 @@ /** * ShadowRun Feature Screen Registry - * Define all screens in the shadow-run feature module */ -import type { ScreenDefinition } from '@kbx/contracts' - -export const shadowRunListScreen: KbxScreenDefinition = { - screenId: 'model-ops.shadow-run.list', - title: 'Shadow Run Validation', +export const shadowRunQueueScreen = { + screenId: 'model-ops.shadow-run.queue', + title: 'Shadow Run Queue', module: 'ModelOps', - type: 'list', path: '/model-ops/shadow-runs', - component: () => import('./pages/ShadowRunList.vue'), + component: () => import('./pages/ShadowRunQueue.vue'), permissions: ['model.read'], - description: 'View and manage shadow run validations (252+ trading day backtests)', - - help: { - title: 'Shadow Run Validation', - sections: [ - { - title: 'Overview', - content: - 'Shadow runs validate model performance on historical data without executing trades. Each run includes PBO, DSR, and OOS metrics.', - }, - { - title: 'How to Start', - content: - '1. Click "Search" (F3) to view existing runs\n2. Click "New" to initiate a new shadow run\n3. Select date range and model\n4. Monitor progress in the dashboard', - }, - { - title: 'Interpreting Results', - content: - 'PBO ≤ 20%, DSR ≥ 95%, OOS ≤ 2.5% indicates model validity. Check phase breakdown (Bull/Bear/Sideways) for regime-specific performance.', - }, - ], - relatedScreens: ['model-ops.models.list'], - }, - - grid: { - columnDefs: [ - { field: 'runId', header: 'Run ID', type: 'link', width: 120, pinned: 'left' }, - { field: 'modelName', header: 'Model', width: 150 }, - { field: 'windowStart', header: 'Start Date', type: 'date', width: 120 }, - { field: 'windowEnd', header: 'End Date', type: 'date', width: 120 }, - { field: 'tradingDays', header: 'Days', type: 'number', width: 80 }, - { field: 'totalReturn', header: 'Return', type: 'money', width: 100 }, - { field: 'sharpeRatio', header: 'Sharpe', type: 'number', width: 80 }, - { field: 'pbo', header: 'PBO', type: 'percentage', width: 80 }, - { field: 'dsr', header: 'DSR', type: 'percentage', width: 80 }, - { field: 'oos', header: 'OOS', type: 'percentage', width: 80 }, - { field: 'status', header: 'Status', type: 'status', width: 100 }, - { field: 'createdAt', header: 'Created', type: 'datetime', width: 150 }, - ], - pageSize: 50, - serverSideDatasource: true, - }, - - shortcuts: [ - { key: 'F3', label: 'Search', action: 'search' }, - { key: 'Ctrl+N', label: 'New Shadow Run', action: 'new' }, - ], - - telemetry: { enabled: true }, } -export const shadowRunDetailScreen: KbxScreenDefinition = { - screenId: 'model-ops.shadow-run.detail', - title: 'Shadow Run Details', - module: 'ModelOps', - type: 'detail', - path: '/model-ops/shadow-runs/:runId', - component: () => import('./pages/ShadowRunDetail.vue'), - permissions: ['model.read'], - description: 'Detailed analysis of a shadow run with metrics breakdown', - - help: { - title: 'Shadow Run Analysis', - sections: [ - { - title: 'Metrics Explained', - content: - 'PBO: Probability of Backtest Overfit. DSR: Daily Sharpe Ratio. OOS: Out-of-Sample performance. Lower PBO and OOS, higher DSR is better.', - }, - ], - relatedScreens: ['model-ops.shadow-run.list', 'model-ops.models.detail'], - }, - - shortcuts: [ - { key: 'Escape', label: 'Back to List', action: 'back' }, - { key: 'Ctrl+E', label: 'Export', action: 'export' }, - ], - - telemetry: { enabled: true }, -} - -/** - * All screens in shadow-run module - */ -export const shadowRunScreens: KbxScreenDefinition[] = [ - shadowRunListScreen, - shadowRunDetailScreen, -] +export const shadowRunScreens = [shadowRunQueueScreen] diff --git a/frontend/src/main.ts b/frontend/src/main.ts index 30c8efad..a3c8ce5a 100644 --- a/frontend/src/main.ts +++ b/frontend/src/main.ts @@ -5,15 +5,13 @@ import App from './App.vue' import { router } from './app/router' import { queryClient } from './app/queryClient' import { resolveUiProvider } from './shared/ui/provider' -import { installKbx, registerScreens } from './app/installKbx' -import { screens } from './registry/screens' +import { installKbx } from './app/installKbx' import './design-system/base.css' const app = createApp(App) app.use(createPinia()) app.use(router) app.use(VueQueryPlugin, { queryClient }) -registerScreens(screens) app.use(installKbx) ;(await resolveUiProvider(import.meta.env.VITE_UI_ADAPTER)).install(app) app.mount('#app') diff --git a/frontend/src/shared/@kbx/README.md b/frontend/src/shared/@kbx/README.md deleted file mode 100644 index 7fd2bdff..00000000 --- a/frontend/src/shared/@kbx/README.md +++ /dev/null @@ -1,163 +0,0 @@ -# @kbx — KBX Foundation v60 - -## Overview - -**Phase 1: Core Contracts + Template Components** - -이것은 KBX Foundation v60의 **실용적 구현**입니다. -- v52 (FE Operational Navigation & Screen Anatomy Hardening) 원칙 준수 -- 자체 포함된 컴포넌트 (의존성 최소화) -- 점진적 확대 가능 - -## 구조 - -``` -@kbx/ -├── contracts/ # 11개 핵심 contract 파일 -│ ├── screen.ts # Screen definitions (T01-T09) -│ ├── ui.ts # UI state & presentation -│ ├── problem.ts # Error handling -│ ├── field.ts # Form field metadata -│ ├── workflow.ts # Record lifecycle -│ ├── command.ts # Command definitions -│ ├── permission.ts # Authorization -│ ├── help.ts # Help system -│ ├── status.ts # Status representation -│ ├── grid.ts # Data grid config -│ └── index.ts # Export barrel -│ -├── ui/ # UI Components -│ ├── components/ # 6개 컴포넌트 -│ │ ├── KbxSectionHeader.vue # Section header (표준) -│ │ ├── KbxValidationSummary.vue # Error display -│ │ ├── KbxTransactionTemplate.vue # T03 Header+Detail -│ │ ├── KbxMasterTemplate.vue # T02 List+Detail -│ │ ├── KbxQueueTemplate.vue # T06 Task Queue -│ │ └── KbxReconcileTemplate.vue # T07 Comparison -│ ├── contracts.ts # Re-export contracts -│ └── index.ts # Export barrel -│ -└── index.ts # Main export - -``` - -## v52 Screen Anatomy - -### T02 Master (KbxMasterTemplate) -**용도**: CRUD 목록 + 상세 -**예시**: 품목 관리, 고객 관리 -**구성**: -- List pane: 목록 + 건수 표시 -- Detail pane: 상세 정보 + 탭 - -### T03 Transaction (KbxTransactionTemplate) -**용도**: Header + Detail 트랜잭션 -**예시**: 주문 등록, 구매 등록 -**구성**: -- Header section: 거래처, 배송지 등 -- Detail section: 상품 목록 (Grid) - -### T06 Queue (KbxQueueTemplate) -**용도**: 작업 대기열 -**예시**: WMS 작업, 승인 대기, 예외 처리 -**구성**: -- Queue title + count -- Queue body (actionable items) - -### T07 Reconcile (KbxReconcileTemplate) -**용도**: 데이터 대사 -**예시**: OMS ↔ WMS 대사, Expected ↔ Actual -**구성**: -- Column labels: Expected / Difference / Actual -- Comparison body + aligned state - -## 사용 예제 - -### Transaction 화면 - -```vue - - - -``` - -### Master 화면 - -```vue - - - -``` - -## Next Steps - -### Phase 2: Support Components -- Form components (Input, Select, DateField, etc.) -- Data grid component (AG Grid wrapper) -- Dialog, Drawer, Tabs -- Lookup, Status tag - -### Phase 3: Integration -- Registry system (screen definitions) -- Router integration -- Global composables (validation, dirty state) -- App initialization - -## Design Tokens - -Template들은 다음 CSS variables를 사용합니다: - -```css ---kbx-color-surface /* Background */ ---kbx-color-border /* Border color */ ---kbx-color-text /* Text */ ---kbx-color-text-muted /* Muted text */ ---kbx-color-section-heading /* Section background */ ---kbx-color-module-accent /* Module brand color */ ---kbx-color-success /* Success tone */ ---kbx-color-danger /* Error tone */ ---kbx-color-danger-light /* Error background */ -``` - -## 참고 - -- **v60 Reference**: `docs/Design/kbx-foundation-v60-status-canonical-contract-hardening/` -- **v52 Design Doc**: `KBX-FE-Operational-Navigation-Screen-Anatomy-v52.md` -- **CLAUDE.md**: 프로젝트 아키텍처 가이드 diff --git a/frontend/src/shared/@kbx/composables/index.ts b/frontend/src/shared/@kbx/composables/index.ts deleted file mode 100644 index 6bf6abf5..00000000 --- a/frontend/src/shared/@kbx/composables/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @kbx Composables — v60 - * Global state management and utilities - */ - -export * from './useKbxValidation' -export * from './useKbxDirtyState' -export * from './useKbxPermission' diff --git a/frontend/src/shared/@kbx/composables/useKbxDirtyState.ts b/frontend/src/shared/@kbx/composables/useKbxDirtyState.ts deleted file mode 100644 index 4eeb605e..00000000 --- a/frontend/src/shared/@kbx/composables/useKbxDirtyState.ts +++ /dev/null @@ -1,104 +0,0 @@ -/** - * useKbxDirtyState — v60 - * Track unsaved changes in forms - */ - -import { ref, computed } from 'vue' - -export interface DirtyState { - [key: string]: boolean -} - -export function useKbxDirtyState(initialState: DirtyState = {}) { - const dirtyFields = ref(initialState) - - /** - * Check if form is dirty (has unsaved changes) - */ - const dirty = computed(() => Object.values(dirtyFields.value).some(v => v)) - - /** - * Check if specific field is dirty - */ - const isFieldDirty = (field: string): boolean => { - return dirtyFields.value[field] ?? false - } - - /** - * Mark field as dirty - */ - const markFieldDirty = (field: string): void => { - dirtyFields.value[field] = true - } - - /** - * Mark field as clean - */ - const markFieldClean = (field: string): void => { - dirtyFields.value[field] = false - } - - /** - * Mark all fields as clean - */ - const markAllClean = (): void => { - Object.keys(dirtyFields.value).forEach(key => { - dirtyFields.value[key] = false - }) - } - - /** - * Mark all fields as dirty - */ - const markAllDirty = (): void => { - Object.keys(dirtyFields.value).forEach(key => { - dirtyFields.value[key] = true - }) - } - - /** - * Reset to initial state - */ - const reset = (newInitialState: DirtyState = {}): void => { - dirtyFields.value = newInitialState - } - - /** - * Touch field (mark as visited) - */ - const touchField = (field: string): void => { - if (!(field in dirtyFields.value)) { - dirtyFields.value[field] = false - } - } - - /** - * Get dirty field names - */ - const getDirtyFields = (): string[] => { - return Object.entries(dirtyFields.value) - .filter(([, isDirty]) => isDirty) - .map(([field]) => field) - } - - /** - * Set dirty state of multiple fields - */ - const setDirtyFields = (fields: DirtyState): void => { - dirtyFields.value = { ...dirtyFields.value, ...fields } - } - - return { - dirty, - dirtyFields, - isFieldDirty, - markFieldDirty, - markFieldClean, - markAllClean, - markAllDirty, - touchField, - getDirtyFields, - setDirtyFields, - reset, - } -} diff --git a/frontend/src/shared/@kbx/composables/useKbxPermission.ts b/frontend/src/shared/@kbx/composables/useKbxPermission.ts deleted file mode 100644 index 50ca422b..00000000 --- a/frontend/src/shared/@kbx/composables/useKbxPermission.ts +++ /dev/null @@ -1,135 +0,0 @@ -/** - * useKbxPermission — v60 - * Permission checking and RBAC utilities - */ - -import { ref, readonly } from 'vue' - -export function useKbxPermission() { - const userPermissions = ref>(new Set()) - - /** - * Set user permissions (call after login) - */ - const setPermissions = (permissions: string[]): void => { - userPermissions.value = new Set(permissions) - } - - /** - * Add permission to user - */ - const addPermission = (permission: string): void => { - userPermissions.value.add(permission) - } - - /** - * Remove permission from user - */ - const removePermission = (permission: string): void => { - userPermissions.value.delete(permission) - } - - /** - * Check if user has permission - */ - const has = (permission: string): boolean => { - return userPermissions.value.has(permission) - } - - /** - * Check if user has any of the permissions - */ - const hasAny = (permissions: string[]): boolean => { - return permissions.some(p => userPermissions.value.has(p)) - } - - /** - * Check if user has all permissions - */ - const hasAll = (permissions: string[]): boolean => { - return permissions.every(p => userPermissions.value.has(p)) - } - - /** - * Get all user permissions - */ - const getPermissions = (): string[] => { - return Array.from(userPermissions.value) - } - - /** - * Check if user has at least one permission (for showing UI) - */ - const canView = (requiredPermissions?: string[]): boolean => { - if (!requiredPermissions || requiredPermissions.length === 0) { - return true - } - return hasAny(requiredPermissions) - } - - /** - * Check if user can edit (requires specific permission) - */ - const canEdit = (permission: string): boolean => { - return has(permission) - } - - /** - * Check if user can delete (requires specific permission) - */ - const canDelete = (permission: string): boolean => { - return has(permission) - } - - /** - * Guard function for route navigation - */ - const guard = (requiredPermissions?: string[]): boolean => { - return canView(requiredPermissions) - } - - /** - * Clear all permissions (call on logout) - */ - const clear = (): void => { - userPermissions.value.clear() - } - - return { - permissions: readonly(userPermissions), - setPermissions, - addPermission, - removePermission, - has, - hasAny, - hasAll, - getPermissions, - canView, - canEdit, - canDelete, - guard, - clear, - } -} - -/** - * Global permission instance (singleton) - */ -let globalPermissions: ReturnType | null = null - -/** - * Get or create global permission instance - */ -export function getGlobalPermissions(): ReturnType { - if (!globalPermissions) { - globalPermissions = useKbxPermission() - } - return globalPermissions -} - -/** - * Helper for components to use global permissions - */ -export function useGlobalPermission() { - return getGlobalPermissions() -} diff --git a/frontend/src/shared/@kbx/composables/useKbxValidation.ts b/frontend/src/shared/@kbx/composables/useKbxValidation.ts deleted file mode 100644 index a6deef08..00000000 --- a/frontend/src/shared/@kbx/composables/useKbxValidation.ts +++ /dev/null @@ -1,124 +0,0 @@ -/** - * useKbxValidation — v60 - * Form validation state management - */ - -import { ref, computed } from 'vue' -import type { KbxValidationError, KbxProblem } from '../contracts' - -export function useKbxValidation() { - const errors = ref([]) - - /** - * Get errors for a specific field - */ - const getFieldError = (field: string): string | undefined => { - const error = errors.value.find(e => e.field === field && !e.rowKey) - return error?.message - } - - /** - * Get errors for a specific row field (in grid) - */ - const getRowFieldError = (rowKey: string, field: string): string | undefined => { - const error = errors.value.find(e => e.rowKey === rowKey && e.field === field) - return error?.message - } - - /** - * Check if field has error - */ - const hasFieldError = (field: string): boolean => { - return errors.value.some(e => e.field === field && !e.rowKey) - } - - /** - * Check if row field has error - */ - const hasRowFieldError = (rowKey: string, field: string): boolean => { - return errors.value.some(e => e.rowKey === rowKey && e.field === field) - } - - /** - * Get all errors - */ - const hasErrors = computed(() => errors.value.length > 0) - - /** - * Set errors (typically from API response) - */ - const setErrors = (newErrors: KbxValidationError[]): void => { - errors.value = newErrors - } - - /** - * Add error for field - */ - const addError = (field: string, message: string, code: string = 'validation.error'): void => { - errors.value.push({ field, code, message }) - } - - /** - * Add error for row field (in grid) - */ - const addRowError = ( - rowKey: string, - field: string, - message: string, - code: string = 'validation.error' - ): void => { - errors.value.push({ rowKey, field, code, message }) - } - - /** - * Clear errors for a field - */ - const clearFieldErrors = (field: string): void => { - errors.value = errors.value.filter(e => e.field !== field) - } - - /** - * Clear errors for a row field - */ - const clearRowFieldErrors = (rowKey: string, field?: string): void => { - if (field) { - errors.value = errors.value.filter(e => !(e.rowKey === rowKey && e.field === field)) - } else { - errors.value = errors.value.filter(e => e.rowKey !== rowKey) - } - } - - /** - * Clear all errors - */ - const clear = (): void => { - errors.value = [] - } - - /** - * Apply errors from KbxProblem (API response) - */ - const applyProblem = (problem: KbxProblem): void => { - if (problem.type === 'validation') { - errors.value = problem.errors - } else { - errors.value = [] - } - } - - return { - errors, - hasErrors, - getFieldError, - getRowFieldError, - hasFieldError, - hasRowFieldError, - setErrors, - addError, - addRowError, - clearFieldErrors, - clearRowFieldErrors, - clear, - applyProblem, - } -} diff --git a/frontend/src/shared/@kbx/contracts/command.ts b/frontend/src/shared/@kbx/contracts/command.ts deleted file mode 100644 index 39e195e3..00000000 --- a/frontend/src/shared/@kbx/contracts/command.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * Command Definition Contracts — v60 - * Screen commands (Save, Delete, Approve, etc.) - */ - -export type KbxCommandGroup = 'query' | 'edit' | 'workflow' | 'output' | 'more' - -export type KbxCommandVariant = 'primary' | 'secondary' | 'danger' | 'ghost' - -export type KbxRecipePermissionKind = 'none' | 'write' | 'execute' - -export interface KbxCommandDefinition { - id: string - label: string - group: KbxCommandGroup - shortcut?: string - variant?: KbxCommandVariant - permissionKind: KbxRecipePermissionKind - icon?: string - disabled?: boolean -} diff --git a/frontend/src/shared/@kbx/contracts/field.ts b/frontend/src/shared/@kbx/contracts/field.ts deleted file mode 100644 index 0931e4c2..00000000 --- a/frontend/src/shared/@kbx/contracts/field.ts +++ /dev/null @@ -1,45 +0,0 @@ -/** - * Field Definition Contracts — v60 - * Form field metadata, validation, and state - */ - -export type KbxFieldType = - | 'text' - | 'number' - | 'currency' - | 'percentage' - | 'date' - | 'datetime' - | 'time' - | 'select' - | 'multi-select' - | 'checkbox' - | 'radio' - | 'textarea' - | 'lookup' - | 'barcode' - -export interface KbxFieldDefinition { - name: string - type: KbxFieldType - label: string - required?: boolean - readonly?: boolean - hidden?: boolean - placeholder?: string - helpText?: string - pattern?: string - minLength?: number - maxLength?: number - min?: number - max?: number - format?: string -} - -export interface KbxFieldReadonlyPolicy { - [fieldName: string]: boolean -} - -export function kbxFieldReadonly(policy: KbxFieldReadonlyPolicy, fieldName: string): boolean { - return policy[fieldName] ?? false -} diff --git a/frontend/src/shared/@kbx/contracts/grid.ts b/frontend/src/shared/@kbx/contracts/grid.ts deleted file mode 100644 index 624d8b2b..00000000 --- a/frontend/src/shared/@kbx/contracts/grid.ts +++ /dev/null @@ -1,32 +0,0 @@ -/** - * Data Grid Definition Contracts — v60 - */ - -export type KbxGridColumnType = - | 'text' - | 'number' - | 'currency' - | 'percentage' - | 'date' - | 'datetime' - | 'status' - | 'link' - | 'action' - -export interface KbxGridColumnDefinition { - field: string - header: string - type: KbxGridColumnType - width?: number | string - pinned?: 'left' | 'right' - sortable?: boolean - filterable?: boolean - editable?: boolean -} - -export interface KbxGridDefinition { - columnDefs: KbxGridColumnDefinition[] - pageSize?: number - serverSideDatasource?: boolean - rowHeight?: number | string -} diff --git a/frontend/src/shared/@kbx/contracts/help.ts b/frontend/src/shared/@kbx/contracts/help.ts deleted file mode 100644 index 9c31e6a6..00000000 --- a/frontend/src/shared/@kbx/contracts/help.ts +++ /dev/null @@ -1,18 +0,0 @@ -/** - * Help & Documentation Contracts — v60 - */ - -export interface KbxHelpContent { - key: string - title: string - purpose: string - steps?: string[] - shortcuts?: { key: string; description: string }[] - cautions?: string[] - relatedScreens?: { id: string; title: string }[] -} - -export interface KbxHelpDefinition { - screenId: string - content: KbxHelpContent -} diff --git a/frontend/src/shared/@kbx/contracts/index.ts b/frontend/src/shared/@kbx/contracts/index.ts deleted file mode 100644 index 7690c689..00000000 --- a/frontend/src/shared/@kbx/contracts/index.ts +++ /dev/null @@ -1,15 +0,0 @@ -/** - * @kbx/contracts — KBX Foundation v60 - * Core contract definitions for screens, UI, validation, and workflow - */ - -export * from './screen' -export * from './ui' -export * from './command' -export * from './field' -export * from './workflow' -export * from './permission' -export * from './help' -export * from './problem' -export * from './status' -export * from './grid' diff --git a/frontend/src/shared/@kbx/contracts/permission.ts b/frontend/src/shared/@kbx/contracts/permission.ts deleted file mode 100644 index 9d2fd4a8..00000000 --- a/frontend/src/shared/@kbx/contracts/permission.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Permission & Authorization Contracts — v60 - */ - -export interface KbxPermissionContext { - has(permission: string): boolean - hasAny(permissions: readonly string[]): boolean - hasAll(permissions: readonly string[]): boolean -} - -export interface KbxPermissionDefinition { - id: string - label: string - description?: string - category: string -} diff --git a/frontend/src/shared/@kbx/contracts/problem.ts b/frontend/src/shared/@kbx/contracts/problem.ts deleted file mode 100644 index e71eec00..00000000 --- a/frontend/src/shared/@kbx/contracts/problem.ts +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Problem/Error Handling Contracts — v60 - * Hierarchical error representation for UI and business logic - */ - -export interface KbxProblemBase { - type: string - title: string - detail?: string | null - correlationId?: string | null -} - -export interface KbxValidationError { - field?: string | null - rowKey?: string | null - code: string - message: string -} - -export interface KbxValidationProblem extends KbxProblemBase { - type: 'validation' - errors: KbxValidationError[] -} - -export interface KbxProblemAction { - id: string - label: string -} - -export interface KbxBusinessProblem extends KbxProblemBase { - type: 'business-rule' - code: string - actions?: KbxProblemAction[] -} - -export interface KbxConflictProblem extends KbxProblemBase { - type: 'conflict' - code: string - currentVersion?: number | null -} - -export interface KbxPermissionProblem extends KbxProblemBase { - type: 'permission' - code: string -} - -export interface KbxNotFoundProblem extends KbxProblemBase { - type: 'not-found' - code: string -} - -export interface KbxIntegrationProblem extends KbxProblemBase { - type: 'integration' - code: string - retryable: boolean -} - -export interface KbxSystemProblem extends KbxProblemBase { - type: 'system' - code: string - correlationId: string - retryable?: boolean -} - -export type KbxProblem = - | KbxValidationProblem - | KbxBusinessProblem - | KbxConflictProblem - | KbxPermissionProblem - | KbxNotFoundProblem - | KbxIntegrationProblem - | KbxSystemProblem - -export function isKbxProblem(value: unknown): value is KbxProblem { - return ( - typeof value === 'object' && - value !== null && - 'type' in value && - 'title' in value - ) -} diff --git a/frontend/src/shared/@kbx/contracts/screen.ts b/frontend/src/shared/@kbx/contracts/screen.ts deleted file mode 100644 index 4c27920b..00000000 --- a/frontend/src/shared/@kbx/contracts/screen.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Screen Definition Contracts — v60 - * Fundamental screen identity and template binding - */ - -import type { KbxCommandDefinition } from './command' - -export type KbxScreenType = - | 'list' - | 'master' - | 'transaction' - | 'fast-entry' - | 'master-detail' - | 'queue' - | 'reconcile' - | 'import' - | 'wms-mobile' - -export type KbxScreenTemplateCode = 'T01' | 'T02' | 'T03' | 'T04' | 'T05' | 'T06' | 'T07' | 'T08' | 'T09' - -export interface KbxScreenDefinition { - id: string - version: string - module: 'OMS' | 'ERP' | 'WMS' | 'COMMON' - type: KbxScreenType - /** Explicit template/recipe identity. Type and templateCode must agree. */ - templateCode: KbxScreenTemplateCode - title: string - description?: string - permissions?: string[] - commands?: KbxCommandDefinition[] - helpKey?: string - telemetry?: { enabled: boolean } -} - -export function defineKbxScreen(definition: T): T { - return definition -} diff --git a/frontend/src/shared/@kbx/contracts/status.ts b/frontend/src/shared/@kbx/contracts/status.ts deleted file mode 100644 index ff584f39..00000000 --- a/frontend/src/shared/@kbx/contracts/status.ts +++ /dev/null @@ -1,16 +0,0 @@ -/** - * Status & State Representation Contracts — v60 - */ - -export type KbxStatusTone = 'default' | 'info' | 'success' | 'warning' | 'danger' | 'muted' - -export interface KbxStatusDefinition { - id: string - label: string - tone: KbxStatusTone - icon?: string -} - -export interface KbxStatusCatalog { - [categoryKey: string]: KbxStatusDefinition[] -} diff --git a/frontend/src/shared/@kbx/contracts/ui.ts b/frontend/src/shared/@kbx/contracts/ui.ts deleted file mode 100644 index 041c93eb..00000000 --- a/frontend/src/shared/@kbx/contracts/ui.ts +++ /dev/null @@ -1,56 +0,0 @@ -/** - * UI State & Presentation Contracts — v60 - */ - -export type KbxFieldState = 'default' | 'changed' | 'warning' | 'ai-suggested' - -export type KbxAsyncState = 'idle' | 'ready' | 'loading' | 'empty' | 'error' - -export interface KbxAsyncStateDefinition { - state: KbxAsyncState - title?: string - detail?: string - actionLabel?: string -} - -export interface KbxSummaryItem { - key: string - label: string - value: string | number - emphasis?: boolean -} - -export interface KbxQuickFilterItem { - key: string - label: string - count: number - active?: boolean - tone?: 'default' | 'warning' | 'danger' -} - -export type KbxShortcutScope = 'application' | 'page' | 'grid' | 'dialog' | 'editor' - -export interface KbxShortcutDefinition { - key: string - scope: KbxShortcutScope - priority?: number - enabled?: () => boolean - execute(): void | Promise -} - -export type KbxTemplateMetricTone = 'default' | 'info' | 'success' | 'warning' | 'danger' - -export interface KbxTemplateMetric { - key: string - label: string - value: string | number - tone?: KbxTemplateMetricTone - emphasis?: boolean -} - -export interface KbxTemplateContext { - label?: string - hint?: string - updatedAt?: string - metrics?: KbxTemplateMetric[] -} diff --git a/frontend/src/shared/@kbx/contracts/workflow.ts b/frontend/src/shared/@kbx/contracts/workflow.ts deleted file mode 100644 index 44606809..00000000 --- a/frontend/src/shared/@kbx/contracts/workflow.ts +++ /dev/null @@ -1,38 +0,0 @@ -/** - * Workflow Definition Contracts — v60 - * Record lifecycle, state transitions, approvals - */ - -export type KbxRecordState = 'draft' | 'submitted' | 'approved' | 'rejected' | 'completed' | 'cancelled' - -export interface KbxWorkflowTransition { - from: KbxRecordState - to: KbxRecordState - label: string - requiredPermission?: string - requiresReason?: boolean -} - -export interface KbxWorkflowDefinition { - states: KbxRecordState[] - transitions: KbxWorkflowTransition[] - initialState: KbxRecordState - terminalStates: KbxRecordState[] -} - -export interface KbxAuditEntry { - timestamp: string - actor: string - action: string - changes?: Record - reason?: string - correlationId?: string -} - -export interface KbxConflictSnapshot { - version: number - currentVersion: number - reason: 'concurrent-edit' | 'external-change' - lastModifiedAt: string - lastModifiedBy: string -} diff --git a/frontend/src/shared/@kbx/index.ts b/frontend/src/shared/@kbx/index.ts deleted file mode 100644 index d2d1cc3d..00000000 --- a/frontend/src/shared/@kbx/index.ts +++ /dev/null @@ -1,27 +0,0 @@ -/** - * @kbx — KBX Foundation v60 - * Complete integration of contracts, components, registries, and composables - * - * v52 Screen Anatomy: Operational Navigation & Screen Hardening - * - Standardized screen templates (T01-T09) - * - Contract-driven design - * - Module identity + visual clarity - */ - -// Core Contracts -export * from './contracts' - -// UI Components & Templates (Phase 1 + 2) -export * from './ui' - -// Registry System (Screen, Permission, Help) -export * from './registry' - -// Composables (Validation, Dirty State, Permission) -export * from './composables' - -// Installation -export * from './installKbx' - -// Design Tokens -import './tokens.css' diff --git a/frontend/src/shared/@kbx/installKbx.ts b/frontend/src/shared/@kbx/installKbx.ts deleted file mode 100644 index 46fb480d..00000000 --- a/frontend/src/shared/@kbx/installKbx.ts +++ /dev/null @@ -1,154 +0,0 @@ -/** - * @kbx Installation — v60 - * Initialize KBX system in Vue app - */ - -import type { App } from 'vue' -import type { KbxScreenDefinition, KbxPermissionDefinition, KbxHelpDefinition } from './contracts' -import { screenRegistry } from './registry/screenRegistry' -import { permissionRegistry } from './registry/permissionRegistry' -import { helpRegistry } from './registry/helpRegistry' -import { getGlobalPermissions } from './composables/useKbxPermission' - -export interface KbxInstallOptions { - /** - * Initial screen definitions to register - */ - screens?: KbxScreenDefinition[] - - /** - * Initial permission definitions to register - */ - permissions?: KbxPermissionDefinition[] - - /** - * Initial help definitions to register - */ - help?: KbxHelpDefinition[] - - /** - * User's initial permissions - */ - userPermissions?: string[] - - /** - * Default density (compact, comfortable, touch) - */ - density?: 'compact' | 'comfortable' | 'touch' - - /** - * Theme (light, dark, auto) - */ - theme?: 'light' | 'dark' | 'auto' -} - -/** - * Install KBX system into Vue app - */ -export function installKbx(app: App, options: KbxInstallOptions = {}) { - // Register screens - if (options.screens) { - screenRegistry.registerMany(options.screens) - } - - // Register permissions - if (options.permissions) { - permissionRegistry.registerMany(options.permissions) - } - - // Register help - if (options.help) { - helpRegistry.registerMany(options.help) - } - - // Set user permissions - if (options.userPermissions) { - getGlobalPermissions().setPermissions(options.userPermissions) - } - - // Set density - if (options.density) { - setDensity(options.density) - } - - // Set theme - if (options.theme && options.theme !== 'auto') { - setTheme(options.theme) - } - - // Import design tokens - import('./tokens.css') - - // Provide registries to components - app.provide('kbx-screens', screenRegistry) - app.provide('kbx-permissions', permissionRegistry) - app.provide('kbx-help', helpRegistry) - - // Global properties - app.config.globalProperties.$kbx = { - screenRegistry, - permissionRegistry, - helpRegistry, - permissions: getGlobalPermissions(), - } -} - -/** - * Set density (compact, comfortable, touch) - */ -export function setDensity(density: 'compact' | 'comfortable' | 'touch'): void { - document.documentElement.setAttribute('data-density', density) -} - -/** - * Get current density - */ -export function getDensity(): 'compact' | 'comfortable' | 'touch' { - const density = document.documentElement.getAttribute('data-density') - return (density as any) || 'compact' -} - -/** - * Set theme (light, dark) - */ -export function setTheme(theme: 'light' | 'dark'): void { - document.documentElement.setAttribute('data-theme', theme) -} - -/** - * Get current theme - */ -export function getTheme(): 'light' | 'dark' | 'auto' { - const theme = document.documentElement.getAttribute('data-theme') - if (theme === 'light' || theme === 'dark') { - return theme - } - return 'auto' -} - -/** - * Toggle theme - */ -export function toggleTheme(): void { - const current = getTheme() - if (current === 'light') { - setTheme('dark') - } else if (current === 'dark') { - document.documentElement.removeAttribute('data-theme') - } else { - // Auto -> light - setTheme('light') - } -} - -/** - * Check if dark mode is active - */ -export function isDarkMode(): boolean { - const theme = getTheme() - if (theme !== 'auto') { - return theme === 'dark' - } - // Check system preference - return window.matchMedia('(prefers-color-scheme: dark)').matches -} diff --git a/frontend/src/shared/@kbx/registry/helpRegistry.ts b/frontend/src/shared/@kbx/registry/helpRegistry.ts deleted file mode 100644 index 26956c38..00000000 --- a/frontend/src/shared/@kbx/registry/helpRegistry.ts +++ /dev/null @@ -1,68 +0,0 @@ -/** - * KBX Help Registry — v60 - * Central management of help content (contextual help system) - */ - -import type { KbxHelpContent, KbxHelpDefinition } from '../contracts' - -class HelpRegistry { - private helpByScreenId = new Map() - - /** - * Register help content for a screen - */ - register(definition: KbxHelpDefinition): void { - this.helpByScreenId.set(definition.screenId, definition.content) - } - - /** - * Register multiple help definitions - */ - registerMany(definitions: KbxHelpDefinition[]): void { - definitions.forEach(def => this.register(def)) - } - - /** - * Get help content by screen ID - */ - getHelp(screenId: string): KbxHelpContent | undefined { - return this.helpByScreenId.get(screenId) - } - - /** - * Get all help content - */ - getAllHelp(): KbxHelpContent[] { - return Array.from(this.helpByScreenId.values()) - } - - /** - * Check if help exists for screen - */ - hasHelp(screenId: string): boolean { - return this.helpByScreenId.has(screenId) - } - - /** - * Clear all help - */ - clear(): void { - this.helpByScreenId.clear() - } -} - -/** - * Global registry instance - */ -export const helpRegistry = new HelpRegistry() - -/** - * Composable for Vue components - */ -export function useHelpRegistry() { - return { - getHelp: (screenId: string) => helpRegistry.getHelp(screenId), - getAllHelp: () => helpRegistry.getAllHelp(), - hasHelp: (screenId: string) => helpRegistry.hasHelp(screenId), - } -} diff --git a/frontend/src/shared/@kbx/registry/index.ts b/frontend/src/shared/@kbx/registry/index.ts deleted file mode 100644 index 35c80a53..00000000 --- a/frontend/src/shared/@kbx/registry/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -/** - * @kbx Registry System — v60 - * Central registries for screens, permissions, help - */ - -export * from './screenRegistry' -export * from './permissionRegistry' -export * from './helpRegistry' diff --git a/frontend/src/shared/@kbx/registry/permissionRegistry.ts b/frontend/src/shared/@kbx/registry/permissionRegistry.ts deleted file mode 100644 index 3fbbe95e..00000000 --- a/frontend/src/shared/@kbx/registry/permissionRegistry.ts +++ /dev/null @@ -1,85 +0,0 @@ -/** - * KBX Permission Registry — v60 - * Central management of permissions - */ - -import type { KbxPermissionDefinition } from '../contracts' - -class PermissionRegistry { - private permissions = new Map() - private permissionsByCategory = new Map() - - /** - * Register a permission - */ - register(permission: KbxPermissionDefinition): void { - this.permissions.set(permission.id, permission) - - // Index by category - if (!this.permissionsByCategory.has(permission.category)) { - this.permissionsByCategory.set(permission.category, []) - } - this.permissionsByCategory.get(permission.category)!.push(permission) - } - - /** - * Register multiple permissions - */ - registerMany(permissions: KbxPermissionDefinition[]): void { - permissions.forEach(perm => this.register(perm)) - } - - /** - * Get permission by ID - */ - getPermission(id: string): KbxPermissionDefinition | undefined { - return this.permissions.get(id) - } - - /** - * Get all permissions - */ - getAllPermissions(): KbxPermissionDefinition[] { - return Array.from(this.permissions.values()) - } - - /** - * Get permissions by category - */ - getPermissionsByCategory(category: string): KbxPermissionDefinition[] { - return this.permissionsByCategory.get(category) ?? [] - } - - /** - * Check if permission exists - */ - hasPermission(id: string): boolean { - return this.permissions.has(id) - } - - /** - * Clear all permissions - */ - clear(): void { - this.permissions.clear() - this.permissionsByCategory.clear() - } -} - -/** - * Global registry instance - */ -export const permissionRegistry = new PermissionRegistry() - -/** - * Composable for Vue components - */ -export function usePermissionRegistry() { - return { - getPermission: (id: string) => permissionRegistry.getPermission(id), - getAllPermissions: () => permissionRegistry.getAllPermissions(), - getPermissionsByCategory: (category: string) => - permissionRegistry.getPermissionsByCategory(category), - hasPermission: (id: string) => permissionRegistry.hasPermission(id), - } -} diff --git a/frontend/src/shared/@kbx/registry/screenRegistry.ts b/frontend/src/shared/@kbx/registry/screenRegistry.ts deleted file mode 100644 index 967e9cca..00000000 --- a/frontend/src/shared/@kbx/registry/screenRegistry.ts +++ /dev/null @@ -1,132 +0,0 @@ -/** - * KBX Screen Registry — v60 - * Central management of screen definitions for routing, permissions, help - */ - -import type { KbxScreenDefinition, KbxScreenType, KbxScreenTemplateCode } from '../contracts' - -export interface ScreenRegistryEntry { - screen: KbxScreenDefinition - templateCode: KbxScreenTemplateCode - module: 'OMS' | 'ERP' | 'WMS' | 'COMMON' -} - -class ScreenRegistry { - private screens = new Map() - private screensByModule = new Map() - private screensByTemplate = new Map() - - /** - * Register a screen definition - */ - register(screen: KbxScreenDefinition): void { - const entry: ScreenRegistryEntry = { - screen, - templateCode: screen.templateCode, - module: screen.module, - } - - // Store by ID - this.screens.set(screen.id, entry) - - // Index by module - if (!this.screensByModule.has(screen.module)) { - this.screensByModule.set(screen.module, []) - } - this.screensByModule.get(screen.module)!.push(entry) - - // Index by template - if (!this.screensByTemplate.has(screen.templateCode)) { - this.screensByTemplate.set(screen.templateCode, []) - } - this.screensByTemplate.get(screen.templateCode)!.push(entry) - } - - /** - * Register multiple screens - */ - registerMany(screens: KbxScreenDefinition[]): void { - screens.forEach(screen => this.register(screen)) - } - - /** - * Get screen by ID - */ - getScreen(id: string): ScreenRegistryEntry | undefined { - return this.screens.get(id) - } - - /** - * Get all screens - */ - getAllScreens(): ScreenRegistryEntry[] { - return Array.from(this.screens.values()) - } - - /** - * Get screens by module - */ - getScreensByModule(module: 'OMS' | 'ERP' | 'WMS' | 'COMMON'): ScreenRegistryEntry[] { - return this.screensByModule.get(module) ?? [] - } - - /** - * Get screens by template code - */ - getScreensByTemplate(templateCode: KbxScreenTemplateCode): ScreenRegistryEntry[] { - return this.screensByTemplate.get(templateCode) ?? [] - } - - /** - * Get screens by type - */ - getScreensByType(type: KbxScreenType): ScreenRegistryEntry[] { - return this.getAllScreens().filter(entry => entry.screen.type === type) - } - - /** - * Check if screen exists - */ - hasScreen(id: string): boolean { - return this.screens.has(id) - } - - /** - * Get screen count by module - */ - getCountByModule(module: 'OMS' | 'ERP' | 'WMS' | 'COMMON'): number { - return this.getScreensByModule(module).length - } - - /** - * Clear all screens - */ - clear(): void { - this.screens.clear() - this.screensByModule.clear() - this.screensByTemplate.clear() - } -} - -/** - * Global registry instance - */ -export const screenRegistry = new ScreenRegistry() - -/** - * Composable for Vue components - */ -export function useScreenRegistry() { - return { - getScreen: (id: string) => screenRegistry.getScreen(id), - getAllScreens: () => screenRegistry.getAllScreens(), - getScreensByModule: (module: 'OMS' | 'ERP' | 'WMS' | 'COMMON') => - screenRegistry.getScreensByModule(module), - getScreensByTemplate: (templateCode: KbxScreenTemplateCode) => - screenRegistry.getScreensByTemplate(templateCode), - getScreensByType: (type: KbxScreenType) => screenRegistry.getScreensByType(type), - hasScreen: (id: string) => screenRegistry.hasScreen(id), - getCountByModule: (module: 'OMS' | 'ERP' | 'WMS' | 'COMMON') => - screenRegistry.getCountByModule(module), - } -} diff --git a/frontend/src/shared/@kbx/tokens.css b/frontend/src/shared/@kbx/tokens.css deleted file mode 100644 index bc783b72..00000000 --- a/frontend/src/shared/@kbx/tokens.css +++ /dev/null @@ -1,192 +0,0 @@ -/** - * @kbx Design Tokens — v60 - * Color, spacing, typography, density - */ - -:root { - /* Colors */ - --kbx-color-primary: #3b82f6; - --kbx-color-success: #10b981; - --kbx-color-warning: #f59e0b; - --kbx-color-danger: #ef4444; - --kbx-color-info: #06b6d4; - - /* Base Colors */ - --kbx-color-surface: #ffffff; - --kbx-color-background: #f9fafb; - --kbx-color-border: #e5e7eb; - --kbx-color-text: #000000; - --kbx-color-text-muted: #6b7280; - --kbx-color-section-heading: #f9fafb; - - /* Module Colors */ - --kbx-color-module-accent: #3b82f6; - --kbx-module-oms: #3b82f6; - --kbx-module-erp: #a78bfa; - --kbx-module-wms: #14b8a6; - --kbx-module-common: #6b7280; - - /* Light Tones (for backgrounds) */ - --kbx-color-danger-light: #fee2e2; - --kbx-color-warning-light: #fef3c7; - --kbx-color-success-light: #dcfce7; - --kbx-color-info-light: #cffafe; - - /* Spacing */ - --kbx-space-1: 4px; - --kbx-space-2: 8px; - --kbx-space-3: 12px; - --kbx-space-4: 16px; - --kbx-space-5: 20px; - --kbx-space-6: 24px; - - /* Typography */ - --kbx-font-family: system-ui, -apple-system, sans-serif; - --kbx-font-xs: 12px; - --kbx-font-sm: 13px; - --kbx-font-base: 14px; - --kbx-font-lg: 16px; - --kbx-font-xl: 18px; - --kbx-font-2xl: 20px; - - /* Line Heights */ - --kbx-line-height-tight: 1.4; - --kbx-line-height-normal: 1.5; - --kbx-line-height-relaxed: 1.6; - - /* Component Heights (compact density) */ - --kbx-control-xs: 28px; - --kbx-control-sm: 32px; - --kbx-control-md: 36px; - --kbx-control-lg: 44px; - - /* Grid Row Height */ - --kbx-grid-row-height: 34px; - --kbx-grid-header-height: 36px; - - /* Master/Detail Grid */ - --kbx-master-list-min-width: 280px; - --kbx-master-list-compact-width: 32%; - - /* Borders */ - --kbx-border-width: 1px; - --kbx-border-radius: 4px; - - /* Shadows */ - --kbx-shadow-sm: 0 1px 2px 0 rgba(0, 0, 0, 0.05); - --kbx-shadow-md: 0 4px 6px -1px rgba(0, 0, 0, 0.1); - --kbx-shadow-lg: 0 10px 15px -3px rgba(0, 0, 0, 0.1); - --kbx-shadow-shell: 0 1px 3px 0 rgba(0, 0, 0, 0.1); - - /* Transitions */ - --kbx-transition-fast: 0.15s ease; - --kbx-transition-base: 0.2s ease; - --kbx-transition-slow: 0.3s ease; - - /* Density: compact (default) */ - --kbx-density: compact; - --kbx-input-height: 34px; - --kbx-input-padding: 8px 12px; - --kbx-touch-target: 44px; - --kbx-font-size: 14px; -} - -/* Comfortable Density */ -:root[data-density="comfortable"] { - --kbx-input-height: 36px; - --kbx-input-padding: 10px 12px; - --kbx-control-md: 40px; - --kbx-grid-row-height: 36px; - --kbx-touch-target: 48px; - --kbx-font-size: 14px; -} - -/* Touch Density (WMS/Mobile) */ -:root[data-density="touch"] { - --kbx-input-height: 48px; - --kbx-input-padding: 12px 16px; - --kbx-control-md: 52px; - --kbx-grid-row-height: 48px; - --kbx-touch-target: 52px; - --kbx-font-size: 16px; -} - -/* Dark Mode */ -@media (prefers-color-scheme: dark) { - :root { - --kbx-color-surface: #1f2937; - --kbx-color-background: #111827; - --kbx-color-border: #374151; - --kbx-color-text: #f9fafb; - --kbx-color-text-muted: #d1d5db; - --kbx-color-section-heading: #111827; - - --kbx-color-danger-light: #7f1d1d; - --kbx-color-warning-light: #92400e; - --kbx-color-success-light: #15803d; - --kbx-color-info-light: #0c4a6e; - } -} - -/* Explicit Dark Theme Override */ -:root[data-theme="dark"] { - --kbx-color-surface: #1f2937; - --kbx-color-background: #111827; - --kbx-color-border: #374151; - --kbx-color-text: #f9fafb; - --kbx-color-text-muted: #d1d5db; - --kbx-color-section-heading: #111827; - - --kbx-color-danger-light: #7f1d1d; - --kbx-color-warning-light: #92400e; - --kbx-color-success-light: #15803d; - --kbx-color-info-light: #0c4a6e; -} - -/* Light Theme Override */ -:root[data-theme="light"] { - --kbx-color-surface: #ffffff; - --kbx-color-background: #f9fafb; - --kbx-color-border: #e5e7eb; - --kbx-color-text: #000000; - --kbx-color-text-muted: #6b7280; - --kbx-color-section-heading: #f9fafb; - - --kbx-color-danger-light: #fee2e2; - --kbx-color-warning-light: #fef3c7; - --kbx-color-success-light: #dcfce7; - --kbx-color-info-light: #cffafe; -} - -/* Global Base Styles */ -* { - box-sizing: border-box; -} - -body { - font-family: var(--kbx-font-family); - font-size: var(--kbx-font-size); - line-height: var(--kbx-line-height-normal); - color: var(--kbx-color-text); - background: var(--kbx-color-background); - transition: color var(--kbx-transition-fast), background var(--kbx-transition-fast); -} - -/* Focus Visible (Accessibility) */ -:focus-visible { - outline: 2px solid var(--kbx-color-primary); - outline-offset: 2px; -} - -/* Forced Colors Mode (High Contrast) */ -@media (forced-colors: active) { - :root { - --kbx-color-primary: CanvasText; - --kbx-color-text: CanvasText; - --kbx-color-border: CanvasText; - } - - button { - border: 1px solid CanvasText; - } -} diff --git a/frontend/src/shared/@kbx/ui/components/KbxButton.vue b/frontend/src/shared/@kbx/ui/components/KbxButton.vue deleted file mode 100644 index 716e01f8..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxButton.vue +++ /dev/null @@ -1,175 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxCheckbox.vue b/frontend/src/shared/@kbx/ui/components/KbxCheckbox.vue deleted file mode 100644 index fc5a7a1a..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxCheckbox.vue +++ /dev/null @@ -1,113 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxDataGrid.vue b/frontend/src/shared/@kbx/ui/components/KbxDataGrid.vue deleted file mode 100644 index a9d49a56..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxDataGrid.vue +++ /dev/null @@ -1,144 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxDateField.vue b/frontend/src/shared/@kbx/ui/components/KbxDateField.vue deleted file mode 100644 index 3db25029..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxDateField.vue +++ /dev/null @@ -1,107 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxDialog.vue b/frontend/src/shared/@kbx/ui/components/KbxDialog.vue deleted file mode 100644 index 9f9cda43..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxDialog.vue +++ /dev/null @@ -1,171 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxDrawer.vue b/frontend/src/shared/@kbx/ui/components/KbxDrawer.vue deleted file mode 100644 index d7fa2ba1..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxDrawer.vue +++ /dev/null @@ -1,163 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxFormGrid.vue b/frontend/src/shared/@kbx/ui/components/KbxFormGrid.vue deleted file mode 100644 index 11e8c472..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxFormGrid.vue +++ /dev/null @@ -1,35 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxFormSection.vue b/frontend/src/shared/@kbx/ui/components/KbxFormSection.vue deleted file mode 100644 index 2854212e..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxFormSection.vue +++ /dev/null @@ -1,64 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxInput.vue b/frontend/src/shared/@kbx/ui/components/KbxInput.vue deleted file mode 100644 index a9436fd8..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxInput.vue +++ /dev/null @@ -1,125 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxLookup.vue b/frontend/src/shared/@kbx/ui/components/KbxLookup.vue deleted file mode 100644 index 897d560c..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxLookup.vue +++ /dev/null @@ -1,265 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxMasterTemplate.vue b/frontend/src/shared/@kbx/ui/components/KbxMasterTemplate.vue deleted file mode 100644 index 4da86a92..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxMasterTemplate.vue +++ /dev/null @@ -1,158 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxMoneyField.vue b/frontend/src/shared/@kbx/ui/components/KbxMoneyField.vue deleted file mode 100644 index 3783fb6e..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxMoneyField.vue +++ /dev/null @@ -1,129 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxNumberField.vue b/frontend/src/shared/@kbx/ui/components/KbxNumberField.vue deleted file mode 100644 index eb3aadff..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxNumberField.vue +++ /dev/null @@ -1,114 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxQuantityField.vue b/frontend/src/shared/@kbx/ui/components/KbxQuantityField.vue deleted file mode 100644 index 0763237c..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxQuantityField.vue +++ /dev/null @@ -1,154 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxQueueTemplate.vue b/frontend/src/shared/@kbx/ui/components/KbxQueueTemplate.vue deleted file mode 100644 index 834e0210..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxQueueTemplate.vue +++ /dev/null @@ -1,141 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxRadio.vue b/frontend/src/shared/@kbx/ui/components/KbxRadio.vue deleted file mode 100644 index 7694876d..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxRadio.vue +++ /dev/null @@ -1,130 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxReconcileTemplate.vue b/frontend/src/shared/@kbx/ui/components/KbxReconcileTemplate.vue deleted file mode 100644 index 9bfaa97d..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxReconcileTemplate.vue +++ /dev/null @@ -1,183 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxScreenFrame.vue b/frontend/src/shared/@kbx/ui/components/KbxScreenFrame.vue deleted file mode 100644 index 8179023e..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxScreenFrame.vue +++ /dev/null @@ -1,122 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxSectionHeader.vue b/frontend/src/shared/@kbx/ui/components/KbxSectionHeader.vue deleted file mode 100644 index e9afabed..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxSectionHeader.vue +++ /dev/null @@ -1,82 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxSelect.vue b/frontend/src/shared/@kbx/ui/components/KbxSelect.vue deleted file mode 100644 index e3935d29..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxSelect.vue +++ /dev/null @@ -1,120 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxStatusTag.vue b/frontend/src/shared/@kbx/ui/components/KbxStatusTag.vue deleted file mode 100644 index ac7e1fb8..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxStatusTag.vue +++ /dev/null @@ -1,109 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxSummaryBar.vue b/frontend/src/shared/@kbx/ui/components/KbxSummaryBar.vue deleted file mode 100644 index 98aee656..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxSummaryBar.vue +++ /dev/null @@ -1,69 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxTabs.vue b/frontend/src/shared/@kbx/ui/components/KbxTabs.vue deleted file mode 100644 index 17e37ce0..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxTabs.vue +++ /dev/null @@ -1,102 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxTemplateStateBoundary.vue b/frontend/src/shared/@kbx/ui/components/KbxTemplateStateBoundary.vue deleted file mode 100644 index e15f994f..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxTemplateStateBoundary.vue +++ /dev/null @@ -1,140 +0,0 @@ - - - - - diff --git a/frontend/src/shared/@kbx/ui/components/KbxTextarea.vue b/frontend/src/shared/@kbx/ui/components/KbxTextarea.vue deleted file mode 100644 index c7bd2d88..00000000 --- a/frontend/src/shared/@kbx/ui/components/KbxTextarea.vue +++ /dev/null @@ -1,109 +0,0 @@ - - -