From 96b3718493b6c225d3bcc007d5e140b54eaea2e9 Mon Sep 17 00:00:00 2001 From: jschoi Date: Wed, 29 Oct 2025 11:11:14 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20=ED=94=84=EB=A1=9C=EC=A0=9D=ED=8A=B8=20S?= =?UTF-8?q?elect=20Users=EC=97=90=20=ED=8F=AC=ED=95=A8=EB=90=9C=20?= =?UTF-8?q?=EC=82=AC=EC=9A=A9=EC=9E=90=EB=A7=8C=20=ED=94=84=EB=A1=9C?= =?UTF-8?q?=EC=A0=9D=ED=8A=B8=20=EB=AA=A9=EB=A1=9D=20=ED=91=9C=EC=8B=9C=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B2=80=EC=83=89=ED=95=84=ED=84=B0=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../templates/projects/ListComponent.vue | 27 ++++--------- .../run/executions/ViewComponent.vue | 27 +++++++++---- .../templates/users/ListComponent.vue | 10 ++--- src/views/Select.vue | 39 +++++++++++-------- 4 files changed, 51 insertions(+), 52 deletions(-) diff --git a/src/components/templates/projects/ListComponent.vue b/src/components/templates/projects/ListComponent.vue index 3a433ad..204628e 100644 --- a/src/components/templates/projects/ListComponent.vue +++ b/src/components/templates/projects/ListComponent.vue @@ -10,19 +10,17 @@ import IconModifyBtn from "@/components/atoms/button/IconModifyBtn.vue"; const store = commonStore(); -type SearchType = "전체" | "제목" | "작성자"; +type SearchType = "전체" | "제목"; const searchOptions = [ { label: "전체", value: "전체" as SearchType }, { label: "제목", value: "제목" as SearchType }, - { label: "작성자", value: "작성자" as SearchType }, ]; -const SEARCH_TYPE_MAP: Record = { +const SEARCH_TYPE_MAP: Record = { "": "ALL", 전체: "ALL", 제목: "TITLE", - 작성자: "AUTHOR", }; const DEFAULT_PERMISSIONS: Permission[] = [ @@ -157,22 +155,11 @@ async function getData() { // 로컬 필터 if (needLocalFilter) { const kw = keyword.toLowerCase(); - if (mapped === "TITLE") { - list = list.filter((x: any) => - String(x?.prjNm ?? "") - .toLowerCase() - .includes(kw), - ); - } else if (mapped === "AUTHOR") { - list = list.filter((x: any) => { - let authorStr = ""; - if (typeof x.modUserNm === "string" && x.modUserNm.length > 0) - authorStr = x.modUserNm; - else if (typeof x.regUserNm === "string") authorStr = x.regUserNm; - return authorStr.toLowerCase().includes(kw); - }); - } - + list = list.filter((x: any) => + String(x?.prjNm ?? "") + .toLowerCase() + .includes(kw), + ); const uiSize = data.value.params.pageSize; const totalElements = list.length; const totalPages = Math.max(1, Math.ceil(totalElements / uiSize)); diff --git a/src/components/templates/run/executions/ViewComponent.vue b/src/components/templates/run/executions/ViewComponent.vue index 612540d..534def7 100644 --- a/src/components/templates/run/executions/ViewComponent.vue +++ b/src/components/templates/run/executions/ViewComponent.vue @@ -59,7 +59,7 @@ const pendingArtifactPath = ref(null); const packageOptions = ref([]); const packagesLoading = ref(false); const packagesError = ref(""); - +const shouldOpenDeploymentAfterLogin = ref(false); /* ========= MLflow State ========= */ const runs = ref([]); const loadingRuns = ref(false); @@ -587,6 +587,12 @@ function restoreAuthFromStorage() { /* no-op */ } } +function openLoginManually() { + shouldOpenDeploymentAfterLogin.value = false; + pendingArtifactPath.value = null; + loginDialog.value = true; +} + const handleLogout = () => { localStorage.removeItem(AUTH_KEY); isAuthenticated.value = false; @@ -602,6 +608,7 @@ const handleLogin = async () => { loginError.value = "ID와 비밀번호를 입력하세요."; return; } + const res = await ExternalAuthControllerService.signIn(id, password); const raw = res?.data ?? res; if (!raw) { @@ -615,6 +622,7 @@ const handleLogin = async () => { loginError.value = "로그인에 실패했습니다. 아이디/비밀번호를 확인하세요."; return; } + const toSave: ExternalAuth = { id: payload.id ?? id, name: payload.name ?? id, @@ -626,8 +634,12 @@ const handleLogin = async () => { loginDialog.value = false; loginForm.value = { id: "", password: "" }; - await nextTick(); - openDeploymentModal(); // pendingArtifactPath 활용 + + if (shouldOpenDeploymentAfterLogin.value) { + shouldOpenDeploymentAfterLogin.value = false; + await nextTick(); + openDeploymentModal(); + } } catch (e: any) { loginError.value = e?.response?.data?.message || e?.message || "로그인에 실패했습니다."; @@ -644,6 +656,7 @@ const openDeploymentModal = async (fullPath?: string) => { pendingArtifactPath.value = uri; } if (!isAuthenticated.value) { + shouldOpenDeploymentAfterLogin.value = true; loginDialog.value = true; return; } @@ -1260,7 +1273,7 @@ const artifactsLoading = ref(false); size="small" color="primary" variant="text" - @click="loginDialog = true" + @click="openLoginManually" > mdi-login Login @@ -1330,12 +1343,10 @@ const artifactsLoading = ref(false); - -
- {{ it.path }} + {{ it.path }}
@@ -1488,6 +1499,6 @@ const artifactsLoading = ref(false); background: rgba(255, 255, 255, 0.04); } .child-path { - padding-left: 18px; /* 들여쓰기로 디렉터리 소속임을 표시 */ + padding-left: 18px; } diff --git a/src/components/templates/users/ListComponent.vue b/src/components/templates/users/ListComponent.vue index 0ea673d..4eef9e3 100644 --- a/src/components/templates/users/ListComponent.vue +++ b/src/components/templates/users/ListComponent.vue @@ -12,17 +12,15 @@ const store = commonStore(); const roleOptions = ["ROLE_USER", "ROLE_MODERATOR", "ROLE_ADMIN"] as const; -type SearchType = "전체" | "제목" | "작성자"; +type SearchType = "전체" | "제목"; const searchOptions = [ { label: "전체", value: "전체" as SearchType }, - { label: "제목", value: "제목" as SearchType }, - { label: "작성자", value: "작성자" as SearchType }, + { label: "이름", value: "제목" as SearchType }, ]; -const SEARCH_TYPE_MAP: Record = { +const SEARCH_TYPE_MAP: Record = { "": "ALL", 전체: "ALL", 제목: "TITLE", - 작성자: "AUTHOR", }; const fmtDate = (v?: string) => (v ? v.replace("T", " ").slice(0, 19) : "-"); @@ -147,8 +145,6 @@ async function getData() { : String(u?.roles ?? "").toLowerCase(); if (mapped === "TITLE") return username.includes(keyword); - if (mapped === "AUTHOR") - return email.includes(keyword) || rolesStr.includes(keyword); return ( username.includes(keyword) || email.includes(keyword) || diff --git a/src/views/Select.vue b/src/views/Select.vue index 705be7a..6f1cc72 100644 --- a/src/views/Select.vue +++ b/src/views/Select.vue @@ -82,23 +82,30 @@ const splitCSV = (v?: string) => .map((s) => s.trim()) .filter(Boolean); +const pickSelected = (p: ProjectSearchResponseItem) => { + const modIds = splitCSV(p.modUserId); + const modNms = splitCSV(p.modUserNm); + const useMod = modIds.length > 0 || modNms.length > 0; // mod가 존재하면 mod 우선 + return { + ids: useMod ? modIds : splitCSV(p.regUserId), + nms: useMod ? modNms : splitCSV(p.regUserNm), + }; +}; // 열람 가능 여부 (등록/수정 사용자에 포함되면 OK) const canViewProjectRaw = (p: ProjectSearchResponseItem) => { - if (isAdmin.value) return true; const idStr = - currentUser.value.id != null ? String(currentUser.value.id) : ""; - const uname = currentUser.value.username ?? ""; - - const allowIds = new Set([ - ...splitCSV(p.regUserId), - ...splitCSV(p.modUserId), - ]); - const allowNms = new Set([ - ...splitCSV(p.regUserNm), - ...splitCSV(p.modUserNm), - ]); - - return (idStr && allowIds.has(idStr)) || (uname && allowNms.has(uname)); + currentUser.value.id != null ? String(currentUser.value.id).trim() : ""; + const uname = (currentUser.value.username ?? "").trim(); + + const norm = (s: string) => s.trim().toLowerCase(); + const { ids, nms } = pickSelected(p); + + const allowIds = new Set(ids.map(String).map(norm)); + const allowNms = new Set(nms.map(norm)); + + return ( + (idStr && allowIds.has(norm(idStr))) || (uname && allowNms.has(norm(uname))) + ); }; /** ===== 페이지네이션 상태 ===== */ const pager = ref({ pageNum: 1, pageSize: 8, total: 0, pageLength: 1 }); @@ -261,9 +268,7 @@ const loadProjects = async () => { projectRegById.value[p.id] = { regId: p.regUserId, regNm: p.regUserNm }; // 카드 표시용 사용자명: reg/mod 합쳐서 중복 제거 - const usersDisplay = Array.from( - new Set([...splitCSV(p.regUserNm), ...splitCSV(p.modUserNm)]), - ).join(","); + const usersDisplay = pickSelected(p).nms.join(","); return { id: p.id,