You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
autoflow-web-console/src/components/templates/workflow/ViewComponent.vue

263 lines
7.2 KiB

<script setup lang="ts">
import { onMounted, ref, watch, onBeforeUnmount } from "vue";
import * as monaco from "monaco-editor";
import "monaco-editor/min/vs/editor/editor.main.css";
import { WorkflowService } from "@/components/service/management/workflowService";
type TabKey = "details" | "yaml";
const props = defineProps<{ id: number | string }>();
const emit = defineEmits<{ (e: "close"): void }>();
const activeTab = ref<TabKey>("details");
const editorRef = ref<HTMLDivElement | null>(null);
let editorInstance: monaco.editor.IStandaloneCodeEditor | null = null;
const detail = ref({
workflowName: "",
version: "",
workflowDescription: "",
createdDate: "",
createdId: "",
});
const stepHeaders = [
{ title: "Order", key: "order", width: "10%", align: "center" },
{ title: "Step Name", key: "name", width: "40%", align: "center" },
{
title: "Component Type",
key: "componentType",
width: "30%",
align: "center",
},
{ title: "Status", key: "status", width: "20%", align: "center" },
];
const steps = ref<
Array<{ order: number; name: string; componentType: string; status: string }>
>([]);
const defaultYaml = `# YAML not provided by server
apiVersion: argoproj.io/v1alpha1
kind: Workflow
metadata:
generateName: dummy-
spec:
entrypoint: main
templates:
- name: main
container:
image: alpine:latest
command: [sh, -c]
args: ["echo hello"]
`;
/** ===== 상세 조회 ===== */
async function fetchDetail(id: number | string) {
try {
const res = await WorkflowService.view(Number(id));
const d = res.data;
detail.value.workflowName = d.workflowName || "";
detail.value.version = String(d.version || 1);
detail.value.workflowDescription = d.workflowDescription || "";
detail.value.createdDate = d.regDt || d.regDate || "-";
detail.value.createdId = d.regUserId || "-";
if (Array.isArray(d.steps)) {
steps.value = d.steps.map((s: any, idx: number) => ({
order: idx + 1,
name: s.stepName || s.name || `Step ${idx + 1}`,
componentType: s.componentType || s.type || "-",
status: s.status || "Not Configured",
}));
} else {
steps.value = [];
}
// YAML 표시 (서버 필드 이름에 맞춰 하나라도 있으면 사용)
const yamlFromServer =
d.workflowYaml ||
d.yaml ||
d.pipelineYaml ||
d.specYaml ||
d.yamlStr ||
"";
if (editorInstance) {
editorInstance.setValue(yamlFromServer || defaultYaml);
}
} catch (e) {
console.error("[Child] view API failed:", e);
}
}
/** ===== 마운트 & 변경 감지 ===== */
onMounted(() => {
if (editorRef.value) {
editorInstance = monaco.editor.create(editorRef.value, {
value: defaultYaml,
language: "yaml",
theme: "vs-dark",
readOnly: true,
automaticLayout: true,
minimap: { enabled: false },
lineNumbers: "on",
});
}
});
// props.id가 바뀌면 재조회
watch(
() => props.id,
(val) => {
if (val !== null && val !== undefined && val !== "") {
fetchDetail(val);
}
},
{ immediate: true },
);
onBeforeUnmount(() => {
if (editorInstance) {
editorInstance.dispose();
editorInstance = null;
}
});
</script>
<template>
<v-container class="h-100 w-100 pa-5 d-flex flex-column align-center">
<v-card
flat
class="bg-shades-transparent d-flex flex-column justify-center w-100"
>
<!-- 헤더 -->
<v-card flat class="bg-shades-transparent w-100">
<v-card-item class="text-h5 font-weight-bold pt-0 pa-5 pl-0">
<div class="d-flex flex-row justify-start align-center">
<div class="text-primary">View Details</div>
</div>
</v-card-item>
</v-card>
<!-- -->
<v-tabs
v-model="activeTab"
background-color="grey lighten-4"
style="max-width: 360px"
grow
>
<v-tab value="details">Details</v-tab>
<v-tab value="yaml">YAML</v-tab>
</v-tabs>
<!-- Details -->
<template v-if="activeTab === 'details'">
<v-card class="bordered-box mb-6 w-100 rounded-lg pa-8 step-card">
<v-card-title class="grey lighten-4 py-2 px-4">
<span class="font-weight-bold">Workflow Information</span>
</v-card-title>
<v-card-text class="px-6 pb-6 pt-4">
<v-row align="center" class="py-2">
<v-col cols="3" class="text-h6 font-weight-bold"
>Workflow Name</v-col
>
<v-col cols="3">{{ detail.workflowName }}</v-col>
<v-col cols="3" class="text-h6 font-weight-bold">Version</v-col>
<v-col cols="3">{{ detail.version }}</v-col>
</v-row>
<v-divider class="my-2" />
<v-row align="center" class="py-2">
<v-col cols="3" class="text-h6 font-weight-bold"
>Workflow Description</v-col
>
<v-col cols="9">{{ detail.workflowDescription }}</v-col>
</v-row>
<v-divider class="my-2" />
<v-row align="center" class="py-2">
<v-col cols="3" class="text-h6 font-weight-bold"
>Created Date</v-col
>
<v-col cols="3">{{ detail.createdDate }}</v-col>
<v-col cols="3" class="text-h6 font-weight-bold"
>Created ID</v-col
>
<v-col cols="3">{{ detail.createdId }}</v-col>
</v-row>
</v-card-text>
</v-card>
<!-- Steps -->
<v-card
flat
class="bordered-box mb-6 w-100 rounded-lg pa-8"
style="min-height: 500px"
>
<v-card-title class="grey lighten-4 py-2 px-4">
<span class="font-weight-bold">Step Overview</span>
</v-card-title>
<v-data-table
:headers="stepHeaders"
:items="steps"
dense
class="text-center"
hide-default-footer
:items-per-page="5"
header-color="primary"
disable-sort
>
<template #item.order="{ index }">{{ index + 1 }}</template>
<template #item.status="{ item }">
<v-chip
:color="
{ Configured: 'success', 'Not Configured': 'warning' }[
item.status
] || 'default'
"
small
dark
>
{{ item.status }}
</v-chip>
</template>
</v-data-table>
<v-sheet class="d-flex justify-end mt-4">
<v-btn class="back-to-list" color="primary" @click="emit('close')"
>Back to List</v-btn
>
</v-sheet>
</v-card>
</template>
<!-- YAML -->
<div
v-show="activeTab === 'yaml'"
ref="editorRef"
class="editor-container"
/>
</v-card>
</v-container>
</template>
<style scoped>
.editor-container {
width: 100%;
height: 900px;
max-height: 900px;
border: 1px solid #444;
border-radius: 4px;
}
.step-card {
position: relative;
min-height: 500px;
padding-bottom: 84px;
}
.back-to-list {
position: absolute;
right: 24px;
bottom: 24px;
}
</style>