fix : 프로젝트 선택 버그 수정 및 Execution 데이터 추가

main
jschoi 9 months ago
parent f9800c23d1
commit ac1f47a72a

@ -55,52 +55,47 @@ function fmtDuration(start?: string, end?: string) {
const pad = (n: number) => String(n).padStart(2, "0");
return `${h}:${pad(m)}:${pad(sec)}`;
}
// Runs
const displayNo = (i: number) => {
const start = (data.value.params.pageNum - 1) * data.value.params.pageSize;
return data.value.totalDataLength - (start + i);
};
async function loadRunsAll() {
runsLoading.value = true;
try {
const all: any[] = [];
const seen = new Set<string>();
// page_size (SDK )
let first = await ExecutionsService.search({ pageSize: 79 });
// 1
let resp = await ExecutionsService.search({
page_size: 500,
} as any);
all.push(...(resp?.data?.runs ?? []));
all.push(...(first?.data?.runs ?? []));
// (/ )
let token: string | undefined =
first?.data?.next_page_token ?? first?.data?.nextPageToken;
resp?.data?.next_page_token ?? resp?.data?.nextPageToken;
//
let guard = 0;
while (token && !seen.has(token) && guard < 1000) {
const seen = new Set<string>();
while (token && !seen.has(token)) {
seen.add(token);
// snake_case
let page = await ExecutionsService.search({ pageSize: 79 });
// camelCase SDK
if (
(!Array.isArray(page?.data?.runs) || page?.data?.runs.length === 0) &&
(page?.data?.next_page_token === token ||
page?.data?.next_page_token === undefined)
) {
page = await ExecutionsService.search({
pageToken: token,
pageSize: 500,
} as any);
}
all.push(...(page?.data?.runs ?? []));
token = page?.data?.next_page_token ?? page?.data?.nextPageToken;
guard += 1;
// token (snake/camel )
resp = await ExecutionsService.search({
page_token: token,
pageToken: token,
page_size: 500,
} as any);
all.push(...(resp?.data?.runs ?? []));
token = resp?.data?.next_page_token ?? resp?.data?.nextPageToken;
}
// ()
//
const dedup = Array.from(
new Map(all.map((r: any) => [r?.run_id ?? r?.id ?? r?.name, r])).values(),
);
// 👉
//
data.value.results = dedup.map((r: any, idx: number) => ({
no: idx + 1,
name: r?.display_name ?? r?.name ?? r?.run_id ?? "(no name)",
@ -112,38 +107,27 @@ async function loadRunsAll() {
r?.pipeline_version_reference?.pipeline_version_id ??
"-",
startTime: fmtStart(r?.created_at),
registryStatus: r?.storage_state ?? "-", // AVAILABLE
//
registryStatus: r?.storage_state ?? "-",
run_id: r?.run_id,
raw: r,
}));
// /
experimentOptions.value = Array.from(
new Set(data.value.results.map((r: any) => String(r.experiment || "-"))),
).filter((v) => v && v !== "-");
workflowOptions.value = Array.from(
new Set(data.value.results.map((r: any) => String(r.workflow || "-"))),
).filter((v) => v && v !== "-");
data.value.totalDataLength = data.value.results.length;
setPaginationLength();
console.log(
"[Runs] total_size(from API):",
first?.data?.total_size ?? first?.data?.totalSize,
"fetched:",
data.value.results.length,
);
console.table(
data.value.results.slice(0, 50).map((r: any) => ({
no: r.no,
run_id: r.run_id,
name: r.name,
status: r.status,
startTime: r.startTime,
duration: r.duration,
})),
);
} catch (e: any) {
console.error("[Runs] 호출 실패:", e?.response?.data ?? e);
} finally {
runsLoading.value = false;
}
}
const pagedResults = computed(() => {
const { pageNum, pageSize } = data.value.params;
const start = (pageNum - 1) * pageSize;
@ -171,6 +155,8 @@ const searchOptions = [
{ searchType: "Registry Status", searchText: "registryStatus" },
];
const experimentOptions = ref<string[]>([]);
const workflowOptions = ref<string[]>([]);
const execDialogOpen = ref(false);
const execMode = ref<"create" | "edit" | "clone">("create");
const execSelected = ref<any>(null);
@ -192,6 +178,8 @@ const data = ref({
pageSize: 10,
searchType: "",
searchText: "",
experimentFilter: "",
workflowFilter: "",
},
results: [],
totalDataLength: 0,
@ -205,96 +193,52 @@ const data = ref({
userOption: [],
});
const getCodeList = () => {
// UserService.search(data.value.params).then((d) => {
// if (d.status === 200) {
// data.value.userOption = d.data.userList;
// }
// });
};
const filteredResults = computed(() => {
const { searchType, searchText, experimentFilter, workflowFilter } =
data.value.params;
let list = data.value.results;
const getData = () => {
const params = { ...data.value.params };
if (params.searchType === "" || params.searchText === "") {
delete params.searchType;
delete params.searchText;
//
if (experimentFilter) {
list = list.filter((r) => String(r.experiment).includes(experimentFilter));
}
if (workflowFilter) {
list = list.filter((r) => String(r.workflow).includes(workflowFilter));
}
data.value.results = [
{
no: 11,
name: "run-batch32-lr0.001",
status: "Succeeded",
duration: "0:00:21",
experiment: "Baseline Model Training",
workflow: "baseline_train_pipeline",
startTime: "2025-05-20 10:12",
registryStatus: "Registered",
},
{
no: 10,
name: "run-batch64-lr0.001",
status: "Failed",
duration: "0:00:20",
experiment: "Baseline Model Training",
workflow: "baseline_train_pipeline",
startTime: "2025-05-20 09:10",
registryStatus: "Not Registered",
},
{
no: 9,
name: "run-batch32-lr0.0005",
status: "Succeeded",
duration: "0:00:21",
experiment: "Baseline Model Training",
workflow: "baseline_train_pipeline",
startTime: "2025-05-19 10:12",
registryStatus: "Registered",
},
{
no: 8,
name: "run-batch64-lr0.0005",
status: "Running",
duration: "0:00:20",
experiment: "Baseline Model Training",
workflow: "baseline_train_pipeline",
startTime: "2025-05-18 11:50",
registryStatus: "Not Registered",
},
{
no: 7,
name: "run-augmented-data",
status: "Succeeded",
duration: "0:00:20",
experiment: "Baseline Model Training",
workflow: "baseline_train_pipeline",
startTime: "2025-05-17 09:12",
registryStatus: "Registered",
},
{
no: 6,
name: "run-augmented-data",
status: "Succeeded",
duration: "0:00:20",
experiment: "Baseline Model Training",
workflow: "baseline_train_pipeline",
startTime: "2025-05-17 09:12",
registryStatus: "Registered",
},
{
no: 5,
name: "run-augmented-data",
status: "Succeeded",
duration: "0:00:20",
experiment: "Baseline Model Training",
workflow: "baseline_train_pipeline",
startTime: "2025-05-17 09:12",
registryStatus: "Registered",
},
];
data.value.totalDataLength = data.value.results.length;
setPaginationLength();
};
//
const q = (searchText || "").trim().toLowerCase();
if (q) {
if (!searchType) {
// All: OR
list = list.filter((r) => {
const pool = [
r.name,
r.status,
r.duration,
r.experiment,
r.workflow,
r.registryStatus,
r.startTime,
];
return pool.some((v) =>
String(v ?? "")
.toLowerCase()
.includes(q),
);
});
} else {
list = list.filter((r) =>
String(r[searchType] ?? "")
.toLowerCase()
.includes(q),
);
}
}
return list;
});
const setPaginationLength = () => {
if (data.value.totalDataLength % data.value.params.pageSize === 0) {
data.value.pageLength =
@ -318,7 +262,6 @@ const handleClone = () => {
const changePageNum = (page) => {
data.value.params.pageNum = page;
getData();
};
const openCreateExecution = () => {
execMode.value = "create";
@ -364,7 +307,6 @@ const getSelectedAllData = () => {
onMounted(() => {
loadRunsAll();
getCodeList();
});
</script>
@ -542,8 +484,8 @@ onMounted(() => {
</thead>
<tbody class="text-body-2">
<tr
v-for="item in data.results"
:key="item.no"
v-for="(item, i) in pagedResults"
:key="item.run_id || item.no || i"
class="text-center"
>
<td>
@ -554,7 +496,8 @@ onMounted(() => {
style="min-width: 36px"
/>
</td>
<td>{{ item.no }}</td>
<td>{{ displayNo(i) }}</td>
<td>{{ item.name }}</td>
<td>
<v-icon v-if="item.status === 'Succeeded'" color="green"
@ -586,7 +529,7 @@ onMounted(() => {
:total-visible="10"
color="primary"
rounded="circle"
@update:model-value="getData"
@update:model-value="changePageNum"
></v-pagination>
</v-card-actions>
</v-col>

@ -122,16 +122,13 @@ const router = createRouter({
routes,
});
router.beforeEach((to) => {
router.beforeEach((to, from) => {
const authed = !!(
typeof storage.getToken === "function" && storage.getToken()
);
const isLogin = to.name === "login" || to.path === "/login";
const isSignup = to.name === "signup" || to.path === "/signup";
const isSelect = to.name === "select" || to.path === "/select";
const bootDone = sessionStorage.getItem("initialRedirectDone") === "1";
const isLogin = to.name === "login";
const isSignup = to.name === "signup";
const isSelect = to.name === "select";
if (!authed) {
if (!isLogin && !isSignup) {
@ -140,32 +137,26 @@ router.beforeEach((to) => {
return true;
}
if (!bootDone && !isSelect && !isLogin && !isSignup) {
sessionStorage.setItem("initialRedirectDone", "1");
return { name: "select", replace: true, query: { redirect: to.fullPath } };
}
if (to.matched.some((r) => r.meta?.requiresAdmin)) {
try {
const raw =
typeof storage?.getAuth === "function"
? storage.getAuth()
: JSON.parse(localStorage.getItem("autoflow-auth") || "null");
const roles = raw?.userInfo?.roles ?? raw?.roles ?? [];
const authCd = raw?.userInfo?.authCd ?? raw?.authCd ?? raw?.auth;
const hasProject = !!localStorage.getItem("projectId"); // ✅ 프로젝트 선택 여부
const bootDone = sessionStorage.getItem("initialRedirectDone") === "1";
const isAdmin =
(Array.isArray(roles)
? roles.includes("ROLE_ADMIN")
: roles === "ROLE_ADMIN") || authCd === "ADMIN";
// 이미 프로젝트 선택됨 → 어떤 화면이든 통과
if (hasProject) return true;
if (!isAdmin) {
return { name: "home", replace: true };
}
} catch {
return { name: "home", replace: true };
// 아직 프로젝트 미선택
if (!bootDone) {
// ✅ 선택 화면에 "들어온 순간"을 부트 완료로 간주 (여기서 한 번만 세팅)
if (isSelect) {
sessionStorage.setItem("initialRedirectDone", "1");
return true;
}
// ✅ select 로 1회만 보냄
return { name: "select", replace: true, query: { redirect: to.fullPath } };
}
// 부트 완료인데 여전히 프로젝트 미선택이면, select만 허용
if (!isSelect) {
return { name: "select", replace: true, query: { redirect: to.fullPath } };
}
return true;

@ -1,6 +1,6 @@
<script setup lang="ts">
import { onMounted, onBeforeUnmount, ref, computed } from "vue";
import { useRouter } from "vue-router";
import { useRoute, useRouter } from "vue-router";
import { useAutoflowStore } from "@/stores/autoflowStore";
import type {
@ -21,6 +21,7 @@ const DEFAULT_PERMISSIONS: Permission[] = [
/** ===== 라우터 & 스토어 ===== */
const router = useRouter();
const route = useRoute();
const autoflowStore = useAutoflowStore();
/** ===== 상태 ===== */
@ -265,11 +266,20 @@ const closeDialog = () => {
const selectProject = (index: number) => {
const selected = pagedProjects.value[index];
if (!selected) return;
// 1)
localStorage.setItem("projectId", String(selected.id));
localStorage.setItem("projectName", selected.title);
// ( )
autoflowStore.setProjectId(selected.id);
autoflowStore.setProjectName(selected.title);
router.push("/home");
};
// 2) redirect , /home
const dest = (route.query.redirect as string) || "/home";
router.replace(dest);
};
const grantDefaultPermissions = async (
projectId: number,
usernames: string[],

Loading…
Cancel
Save