Files
KArtSell.Aegis/docs/Design/kbx-foundation-v60-status-canonical-contract-hardening/packages/kbx-ui/src/components/KbxExceptionDetailDrawer.vue
T
kjh2064 3f293d8aa8
deploy / deploy (push) Successful in 1m52s
deploy / notify (push) Successful in 1s
V13-FE-006: consolidate approved UI and contract hardening
2026-08-13 02:41:00 +09:00

57 lines
3.9 KiB
Vue

<script setup lang="ts">
import { inject } from 'vue'
import type { KbxWorkItem, KbxWorkItemAction } from '@kbx/contracts'
import { KbxPermissionHostKey } from '../permission/host'
import KbxButton from './KbxButton.vue'
import KbxDrawer from './KbxDrawer.vue'
const props = defineProps<{ item: KbxWorkItem | null; can?: (permission: string) => boolean }>()
const emit = defineEmits<{ close: []; action: [KbxWorkItemAction] }>()
const permissionHost=inject(KbxPermissionHostKey,null)
function canPermission(permission:string){return props.can?props.can(permission):(permissionHost?.has(permission)??false)}
function allowed(action:KbxWorkItemAction){return !action.permission||canPermission(action.permission)}
function visible(action:KbxWorkItemAction){return allowed(action)||action.permissionMode==='disable'}
function invoke(action:KbxWorkItemAction){if(allowed(action))emit('action',action)}
</script>
<template>
<KbxDrawer :open="Boolean(item)" :title="item?.title ?? '예외 상세'" @update:open="value=>{if(!value)emit('close')}">
<article v-if="item" class="kbx-exception-detail" data-kbx-component="exception-detail-drawer" :data-severity="item.severity">
<header class="kbx-exception-detail__status">
<span>{{item.code}}</span><strong>{{item.status==='resolved'?'해결':item.status==='claimed'?'처리중':item.status==='ignored'?'제외':'확인 필요'}}</strong>
</header>
<section class="meta" aria-label="예외 정보">
<dl>
<div><dt>모듈</dt><dd>{{ item.sourceModule }}</dd></div>
<div><dt>대상</dt><dd>{{ item.sourceType }} · {{ item.referenceNo }}</dd></div>
<div><dt>발생</dt><dd>{{ item.occurredAt }}</dd></div>
<div><dt>담당</dt><dd>{{ item.ownerName || '미지정' }}</dd></div>
<div v-if="item.dueAt"><dt>기한</dt><dd>{{item.dueAt}}</dd></div>
</dl>
</section>
<section class="detail">
<h2>원인/안내</h2>
<p>{{ item.detail || '추가 설명이 없습니다.' }}</p>
<slot name="context" :item="item" />
</section>
</article>
<template #footer>
<div v-if="item" class="kbx-exception-detail__actions" role="toolbar" aria-label="예외 후속 작업">
<KbxButton
v-for="action in (item.actions ?? []).filter(visible)"
:key="action.id"
:label="action.label"
:variant="action.danger?'danger':'secondary'"
:disabled="!allowed(action)"
:title="!allowed(action)?'이 작업을 실행할 권한이 없습니다.':undefined"
@click="invoke(action)"
/>
</div>
</template>
</KbxDrawer>
</template>
<style scoped>
.kbx-exception-detail{display:grid;gap:var(--kbx-space-4)}.kbx-exception-detail__status{display:flex;align-items:center;justify-content:space-between;gap:var(--kbx-space-2);padding:var(--kbx-space-2) var(--kbx-space-3);border:var(--kbx-border-width) solid var(--kbx-color-border);background:var(--kbx-color-surface-muted)}.kbx-exception-detail[data-severity="warning"] .kbx-exception-detail__status{border-color:var(--kbx-color-warning-border);background:var(--kbx-color-warning-surface)}.kbx-exception-detail[data-severity="critical"] .kbx-exception-detail__status{border-color:var(--kbx-color-danger-border);background:var(--kbx-color-danger-surface)}.kbx-exception-detail__status span{color:var(--kbx-color-text-muted);font-size:var(--kbx-font-xs)}.meta,.detail{padding-bottom:var(--kbx-space-4);border-bottom:var(--kbx-border-width) solid var(--kbx-color-border)}dl{margin:0;display:grid;gap:var(--kbx-space-2)}dl div{display:grid;grid-template-columns:var(--kbx-label-width) 1fr;gap:var(--kbx-space-2)}dt{color:var(--kbx-color-text-muted)}dd{margin:0}h2{margin:0 0 var(--kbx-space-2);font-size:var(--kbx-font-md)}p{margin:0;line-height:1.6}.kbx-exception-detail__actions{display:flex;justify-content:flex-end;gap:var(--kbx-space-2);flex-wrap:wrap}
</style>