Files
Aislo/B01_Dashboard/B01_Dashboard_UI_Page.ts

223 lines
7.9 KiB
TypeScript

import { ROUTES } from "@config/config_frontend";
import {
createButton,
createTag,
hideLoadingOverlay,
showLoadingOverlay,
showToast,
} from "@ui/ui_template_elements";
import { section } from "@ui/ui_template_general_blocks";
import { createGeneralLayout } from "@ui/ui_template_general_layout";
import { navigateTo } from "../A00_Common/router";
import {
fetchAllCompanies,
fetchAllJoinRequests,
fetchAllProjects,
fetchAllUsers,
fetchAuditLogs,
fetchCompanyJoinRequests,
fetchCompanyMembers,
fetchCompanyProjects,
fetchDashboardMe,
fetchSystemResources,
fetchUserCompany,
fetchUserProjects,
type AuditLog,
type CompanyInfo,
type DashboardUser,
type JoinRequest,
type Member,
type ProjectItem,
type ResourceData,
} from "./B01_Dashboard_Api_Fetch";
import { auditLogTable, userTable } from "./B01_Dashboard_UI_Admin";
import { L, roleLabel } from "./B01_Dashboard_UI_Common";
import {
buildCompanyPanel,
companyTable,
joinRequestTable,
memberTable,
} from "./B01_Dashboard_UI_Company";
import { openAddMemberModal, openCreateCompanyModal } from "./B01_Dashboard_UI_Modals";
import { buildProfileForm, buildSecurityForm } from "./B01_Dashboard_UI_Profile";
import { projectTable } from "./B01_Dashboard_UI_Projects";
import { buildResourcePanel } from "./B01_Dashboard_UI_Resources";
import { buildTempUploadSection } from "./B01_Dashboard_UI_TempUpload";
import "./B01_Dashboard_UI_Style.css";
export interface DashboardState {
user: DashboardUser;
userProjects: ProjectItem[];
companyProjects: ProjectItem[];
allProjects: ProjectItem[];
company: CompanyInfo | null;
members: Member[];
joinRequests: JoinRequest[];
allCompanies: CompanyInfo[];
allUsers: DashboardUser[];
allJoinRequests: JoinRequest[];
auditLogs: AuditLog[];
resources: ResourceData | null;
}
export async function renderB01Dashboard(root: HTMLElement): Promise<void> {
showLoadingOverlay();
try {
const user = await fetchDashboardMe();
const state: DashboardState = {
user,
userProjects: [],
companyProjects: [],
allProjects: [],
company: null,
members: [],
joinRequests: [],
allCompanies: [],
allUsers: [],
allJoinRequests: [],
auditLogs: [],
resources: null,
};
await loadRoleData(state);
root.innerHTML = "";
root.append(buildPage(state));
} catch (error) {
showToast(error instanceof Error ? error.message : L("B01_Dashboard_LoadFailed"), "error");
} finally {
hideLoadingOverlay();
}
}
async function loadRoleData(state: DashboardState): Promise<void> {
if (state.user.role === "SYSTEM_ADMIN") {
const results = await Promise.allSettled([
fetchAllCompanies(),
fetchAllUsers(),
fetchAllJoinRequests(),
fetchAuditLogs(),
fetchSystemResources(7),
fetchAllProjects(),
]);
state.allCompanies = results[0].status === "fulfilled" ? results[0].value : [];
state.allUsers = results[1].status === "fulfilled" ? results[1].value : [];
state.allJoinRequests = results[2].status === "fulfilled" ? results[2].value : [];
state.auditLogs = results[3].status === "fulfilled" ? results[3].value : [];
state.resources = results[4].status === "fulfilled" ? results[4].value : null;
state.allProjects = results[5].status === "fulfilled" ? results[5].value : [];
results.forEach((res, idx) => {
if (res.status === "rejected") {
console.error(`System Admin API failed at index ${idx}:`, res.reason);
}
});
return;
}
const [projects, company] = await Promise.all([
fetchUserProjects().catch(() => []),
fetchUserCompany().catch(() => null),
]);
state.userProjects = projects;
state.company = company;
if (state.user.role === "ADMIN") {
const [members, requests, companyProjects] = await Promise.all([
fetchCompanyMembers().catch(() => []),
fetchCompanyJoinRequests().catch(() => []),
fetchCompanyProjects().catch(() => []),
]);
state.members = members;
state.joinRequests = requests;
state.companyProjects = companyProjects;
}
}
function buildSystemSettingsPanel(): HTMLElement {
const wrap = document.createElement("div");
wrap.className = "b01-dashboard__actions";
wrap.append(
createButton({
label: L("B01_Dashboard_MasterData"),
variant: "ghost",
onClick: () => navigateTo(ROUTES.Z01_MASTER_DATA),
}),
);
return wrap;
}
function buildPage(state: DashboardState): HTMLElement {
// 제목·여백은 공용 템플릿을 따른다 (2026-09-06 사용자 지시) — B02 등 다른 화면과 같은 모양.
const page = document.createElement("div");
const grid = document.createElement("div");
grid.className = "b01-dashboard__grid";
if (state.user.role === "SYSTEM_ADMIN") {
// 리소스 현황 오른쪽에 시스템 설정 — 앞으로 다른 설정 단추도 이 칸에 듦 (2026-09-15 브레인 Z01).
const topRow = document.createElement("div");
topRow.className = "b01-dashboard__top-row";
topRow.append(
section(L("B01_Dashboard_Resources"), buildResourcePanel(state.resources)),
section(L("B01_Dashboard_SystemSettings"), buildSystemSettingsPanel()),
);
grid.append(
topRow,
section(L("B01_Dashboard_Projects"), projectTable(state.allProjects, state.user), true, [
createButton({ label: "+", onClick: () => navigateTo(ROUTES.B02_PROJ_REGISTER) }),
]),
buildTempUploadSection(),
section(L("B01_Dashboard_Users"), userTable(state.allUsers, state.user), true, [
createButton({ label: "+", onClick: () => openAddMemberModal() }),
]),
section(L("B01_Dashboard_JoinRequests"), joinRequestTable(state.allJoinRequests, true), true),
section(L("B01_Dashboard_Companies"), companyTable(state.allCompanies, state.user), true, [
createButton({ label: "+", onClick: () => openCreateCompanyModal() }),
]),
);
} else if (state.user.role === "ADMIN") {
grid.append(
section(L("B01_Dashboard_Projects"), projectTable(state.companyProjects, state.user), true, [
createButton({ label: "+", onClick: () => navigateTo(ROUTES.B02_PROJ_REGISTER) }),
]),
buildTempUploadSection(),
section(L("B01_Dashboard_Members"), memberTable(state.members, state.user), true, [
createButton({ label: "+", onClick: () => openAddMemberModal() }),
]),
section(L("B01_Dashboard_JoinRequests"), joinRequestTable(state.joinRequests, false), true),
section(L("B01_Dashboard_Company"), buildCompanyPanel(state), true),
);
} else {
grid.append(
section(L("B01_Dashboard_Projects"), projectTable(state.userProjects, state.user), true, [
createButton({ label: "+", onClick: () => navigateTo(ROUTES.B02_PROJ_REGISTER) }),
]),
// 임시 보관함 — 프로젝트를 만들기 전에 자료를 올려 두는 곳이라 프로젝트 목록
// 바로 아래에 둔다(2026-08-08 사용자 지시).
buildTempUploadSection(),
section(L("B01_Dashboard_Company"), buildCompanyPanel(state), true),
);
}
grid.append(
section(L("B01_Dashboard_Profile"), buildProfileForm(state.user)),
section(L("B01_Account_Section_Security"), buildSecurityForm()),
);
// 시스템 로그는 맨 아래 (2026-09-06 사용자 지시).
if (state.user.role === "SYSTEM_ADMIN") {
grid.append(section(L("B01_Dashboard_AuditLogs"), auditLogTable(state.auditLogs), true));
}
page.append(grid);
const layout = createGeneralLayout({
pageClass: "b01-dashboard",
title: L("B01_Dashboard_Title"),
subtitle: L("B01_Dashboard_Subtitle"),
content: page,
});
// 역할 배지는 제목 줄 오른쪽에 둔다(자리는 CSS 격자가 잡는다).
const tag = createTag(
roleLabel(state.user.role),
state.user.role === "SYSTEM_ADMIN" ? "accent" : "neutral",
);
tag.classList.add("b01-dashboard__role");
layout.root.querySelector(".ui-general-layout__header")?.append(tag);
return layout.root;
}