style(fe): standardize Model Operations & Versioning page UI components and layout
This commit is contained in:
Binary file not shown.
|
Before Width: | Height: | Size: 81 KiB After Width: | Height: | Size: 96 KiB |
@@ -1,4 +1,6 @@
|
||||
<script setup lang="ts">
|
||||
import { KsStatusTag } from '../../../shared/ui/components'
|
||||
|
||||
defineProps<{
|
||||
algorithmStatus: string
|
||||
orderCapability: string
|
||||
@@ -7,25 +9,74 @@ defineProps<{
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section aria-labelledby="automation-boundary-title">
|
||||
<h2 id="automation-boundary-title">자동화 경계</h2>
|
||||
<dl>
|
||||
<div>
|
||||
<dt>알고리즘 상태</dt>
|
||||
<dd>{{ algorithmStatus }}</dd>
|
||||
<div class="boundary-panel">
|
||||
<h3 class="panel-title">🛡️ 모델 운영 및 자동화 통제 경계</h3>
|
||||
<div class="boundary-grid">
|
||||
<div class="boundary-item">
|
||||
<span class="label">알고리즘 상태</span>
|
||||
<KsStatusTag :value="algorithmStatus" severity="info" />
|
||||
</div>
|
||||
<div>
|
||||
<dt>주문 Capability</dt>
|
||||
<dd>{{ orderCapability }}</dd>
|
||||
<div class="boundary-item">
|
||||
<span class="label">자동주문 제출 Capability</span>
|
||||
<KsStatusTag :value="orderCapability" severity="danger" />
|
||||
</div>
|
||||
<div>
|
||||
<dt>모델 변경</dt>
|
||||
<dd>{{ modelMutationBoundary }}</dd>
|
||||
<div class="boundary-item">
|
||||
<span class="label">모델 변이 (Mutation) 경계</span>
|
||||
<KsStatusTag :value="modelMutationBoundary" severity="warning" />
|
||||
</div>
|
||||
</dl>
|
||||
<p>
|
||||
스케줄러는 평가 증거와 개선 제안만 생성합니다. 모델 승격·롤백·임계값 변경은 독립 검증과
|
||||
maker-checker 승인을 거쳐야 합니다.
|
||||
</p>
|
||||
</section>
|
||||
</div>
|
||||
<div class="boundary-notice">
|
||||
ℹ️ <strong>통제 지침:</strong> 스케줄러는 평가 증거 및 개선 제안만 생성합니다. 모델 승격·롤백·임계값 변경은 독립 검증과 Maker-Checker 승인을 거쳐야 합니다.
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.boundary-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ks-space-3);
|
||||
padding: var(--ks-space-3);
|
||||
background: var(--ks-color-surface);
|
||||
border: 1px solid var(--ks-color-border);
|
||||
border-radius: var(--ks-radius-sm);
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
margin: 0;
|
||||
font-size: var(--ks-font-section);
|
||||
font-weight: 700;
|
||||
color: var(--ks-color-neutral-800);
|
||||
}
|
||||
|
||||
.boundary-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
|
||||
gap: var(--ks-space-3);
|
||||
}
|
||||
|
||||
.boundary-item {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ks-space-1);
|
||||
padding: var(--ks-space-2) var(--ks-space-3);
|
||||
background: var(--ks-color-canvas);
|
||||
border: 1px solid var(--ks-color-neutral-200);
|
||||
border-radius: var(--ks-radius-sm);
|
||||
}
|
||||
|
||||
.boundary-item .label {
|
||||
font-size: var(--ks-font-caption);
|
||||
color: var(--ks-color-neutral-600);
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.boundary-notice {
|
||||
font-size: var(--ks-font-caption);
|
||||
color: #1e3a8a;
|
||||
background: #eff6ff;
|
||||
border: 1px solid #bfdbfe;
|
||||
padding: var(--ks-space-2) var(--ks-space-3);
|
||||
border-radius: var(--ks-radius-sm);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,39 +1,150 @@
|
||||
<script setup lang="ts">
|
||||
import type { ModelOperationItem } from '../schema'
|
||||
import { KsStatusTag } from '../../../shared/ui/components'
|
||||
|
||||
defineProps<{ operations: ModelOperationItem[] }>()
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<section aria-labelledby="operation-plan-title">
|
||||
<h2 id="operation-plan-title">지속 평가·개선 작업 계획</h2>
|
||||
<div class="table-wrap">
|
||||
<table>
|
||||
<div class="operation-plan-panel">
|
||||
<h3 class="panel-title">📋 지속 평가·개선 작업 계획</h3>
|
||||
<div class="table-container">
|
||||
<table class="ks-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Job</th>
|
||||
<th>작업</th>
|
||||
<th>주기</th>
|
||||
<th>자동화 모드</th>
|
||||
<th>Queue</th>
|
||||
<th>Gate</th>
|
||||
<th>Owner</th>
|
||||
<th>산출물</th>
|
||||
<th style="width: 50px; text-align: center;">No.</th>
|
||||
<th style="width: 100px;">Job ID</th>
|
||||
<th>작업명</th>
|
||||
<th style="width: 80px; text-align: center;">주기</th>
|
||||
<th style="width: 130px; text-align: center;">자동화 모드</th>
|
||||
<th style="width: 140px;">Queue</th>
|
||||
<th style="width: 140px;">Gate</th>
|
||||
<th>담당자 (Primary / Secondary)</th>
|
||||
<th>필수 증거 및 산출물</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="operation in operations" :key="operation.operationCode">
|
||||
<td>{{ operation.operationCode }}</td>
|
||||
<td>{{ operation.name }}</td>
|
||||
<td>{{ operation.cadence }}</td>
|
||||
<td>{{ operation.automationMode }}</td>
|
||||
<td>{{ operation.queue }}</td>
|
||||
<td>{{ operation.gate }}</td>
|
||||
<td>{{ operation.primaryOwner }} / {{ operation.secondaryOwner }}</td>
|
||||
<td>{{ operation.output }}</td>
|
||||
<tr v-for="(operation, index) in operations" :key="operation.operationCode" :class="index % 2 === 1 ? 'row-odd' : 'row-even'">
|
||||
<td style="text-align: center;" class="ks-financial-number">{{ index + 1 }}</td>
|
||||
<td class="code-text">{{ operation.operationCode }}</td>
|
||||
<td class="name-text">{{ operation.name }}</td>
|
||||
<td style="text-align: center;"><span class="badge cadence">{{ operation.cadence }}</span></td>
|
||||
<td style="text-align: center;">
|
||||
<KsStatusTag :value="operation.automationMode" severity="warning" />
|
||||
</td>
|
||||
<td><code>{{ operation.queue }}</code></td>
|
||||
<td><span class="badge gate">{{ operation.gate }}</span></td>
|
||||
<td class="owner-text">{{ operation.primaryOwner }} / {{ operation.secondaryOwner }}</td>
|
||||
<td class="output-text">{{ operation.output }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.operation-plan-panel {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: var(--ks-space-3);
|
||||
padding: var(--ks-space-3);
|
||||
background: var(--ks-color-surface);
|
||||
border: 1px solid var(--ks-color-border);
|
||||
border-radius: var(--ks-radius-sm);
|
||||
margin-top: var(--ks-space-3);
|
||||
}
|
||||
|
||||
.panel-title {
|
||||
margin: 0;
|
||||
font-size: var(--ks-font-section);
|
||||
font-weight: 700;
|
||||
color: var(--ks-color-neutral-800);
|
||||
}
|
||||
|
||||
.table-container {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
border: 1px solid var(--ks-color-neutral-300);
|
||||
border-radius: var(--ks-radius-sm);
|
||||
}
|
||||
|
||||
.ks-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
font-size: var(--ks-font-body);
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.ks-table th {
|
||||
background: #f1f5f9;
|
||||
color: var(--ks-color-neutral-800);
|
||||
font-weight: 700;
|
||||
padding: var(--ks-space-2) var(--ks-space-3);
|
||||
border-bottom: 1px solid var(--ks-color-neutral-300);
|
||||
font-size: var(--ks-font-caption);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.ks-table td {
|
||||
padding: var(--ks-space-2) var(--ks-space-3);
|
||||
border-bottom: 1px solid var(--ks-color-neutral-200);
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.ks-table tr.row-even {
|
||||
background: #ffffff;
|
||||
}
|
||||
|
||||
.ks-table tr.row-odd {
|
||||
background: #f8fafc;
|
||||
}
|
||||
|
||||
.ks-table tr:hover {
|
||||
background: #e0f2fe;
|
||||
}
|
||||
|
||||
.code-text {
|
||||
font-weight: 700;
|
||||
color: var(--ks-color-action);
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
.name-text {
|
||||
font-weight: 600;
|
||||
color: var(--ks-color-neutral-900);
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 1px 6px;
|
||||
border-radius: var(--ks-radius-sm);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.badge.cadence {
|
||||
background: #e2e8f0;
|
||||
color: #334155;
|
||||
}
|
||||
|
||||
.badge.gate {
|
||||
background: #dcfce7;
|
||||
color: #166534;
|
||||
border: 1px solid #bbf7d0;
|
||||
}
|
||||
|
||||
code {
|
||||
font-family: monospace;
|
||||
font-size: 11px;
|
||||
background: #f1f5f9;
|
||||
padding: 2px 4px;
|
||||
border-radius: 3px;
|
||||
color: #475569;
|
||||
}
|
||||
|
||||
.owner-text, .output-text {
|
||||
color: var(--ks-color-neutral-700);
|
||||
font-size: var(--ks-font-caption);
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<script setup lang="ts">import { computed } from 'vue'; import PageLayout from '../../layouts/PageLayout.vue'; import StandardScreenBoundary from './StandardScreenBoundary.vue'; import type { StandardScreenProps } from '../../contracts/screenContract'; const props=defineProps<StandardScreenProps>(); const governanceBlocked = computed(() => ['LOADING','ERROR','UNAUTHORIZED','FORBIDDEN','CONFLICT','EXPIRED','READONLY','PROCESSING'].includes(props.state ?? 'READY')); defineEmits<{retry:[]}>()</script>
|
||||
<template><PageLayout :title="props.title" :subtitle="props.subtitle" :status="props.state" :as-of="props.evidence?.asOf" :version="props.evidence?.version" aside-width="28rem"><template #actions><slot name="actions"/></template><template #summary><slot name="gateSummary"/></template><StandardScreenBoundary :state="props.state" :warning="props.warning" @retry="$emit('retry')"><section v-if="!governanceBlocked" class="ks-stack"><div class="ks-card ks-section"><slot name="versionComparison"/></div><div class="ks-card ks-section"><slot name="evidenceMatrix"/></div></section></StandardScreenBoundary><template #aside><section v-if="!governanceBlocked" class="ks-stack"><div class="ks-card ks-section"><slot name="approval"/></div><div class="ks-card ks-section"><slot name="rollback"/></div></section></template><template v-if="$slots.footer && !governanceBlocked" #footer><slot name="footer"/></template></PageLayout></template>
|
||||
<template><PageLayout :title="props.title" :subtitle="props.subtitle" :status="props.state" :as-of="props.evidence?.asOf" :version="props.evidence?.version" aside-width="28rem"><template v-if="$slots.actions" #actions><slot name="actions"/></template><template v-if="$slots.gateSummary" #summary><slot name="gateSummary"/></template><StandardScreenBoundary :state="props.state" :warning="props.warning" @retry="$emit('retry')"><section v-if="!governanceBlocked" class="ks-stack"><div v-if="$slots.versionComparison" class="ks-card ks-section"><slot name="versionComparison"/></div><div v-if="$slots.evidenceMatrix" class="ks-card ks-section"><slot name="evidenceMatrix"/></div><slot /></section></StandardScreenBoundary><template v-if="$slots.approval || $slots.rollback" #aside><section v-if="!governanceBlocked" class="ks-stack"><div v-if="$slots.approval" class="ks-card ks-section"><slot name="approval"/></div><div v-if="$slots.rollback" class="ks-card ks-section"><slot name="rollback"/></div></section></template><template v-if="$slots.footer && !governanceBlocked" #footer><slot name="footer"/></template></PageLayout></template>
|
||||
<style scoped>.ks-section{padding:var(--ks-space-4)}</style>
|
||||
|
||||
Reference in New Issue
Block a user