Files
Aislo/B01_Dashboard/B01_Dashboard_UI_Legacy.ts
T
eomsangdonandClaude Sonnet 5.5 70786cef9e feat(B01): 시스템 관리 · 내 정보 세부 페이지 여섯 — 개요 · 회사 · 사용자 · 설계 기본값 · 로그 · 내 정보 (PLAN 549-2)
- 개요 = 숫자 카드 넷 + CPU · 메모리 · 디스크 게이지(70 % 주황 · 90 % 빨강) + 7 일 SVG 그래프
- 회사 = 목록(찾기 · ⋯) → 상세(정보 · 사람 · 프로젝트 탭) · 사용자 = 회사 · 역할 · 찾기 거르기 · ⋯
- 설계 기본값 = 설정 카드를 묶음 제목 + 줄로 · 시스템 로그 = 동작 · 기간 · 찾기 거르기 + 이전 기록 더 보기
- 내 정보 = Linear 꼴 설정 줄(기본정보 · 비밀번호 · 테마 · 내 설계 기본값)
- 옛 구역(리소스 패널 · 옛 내 정보 폼) 지움 · 지킴 시험 test_549 추가 · test_546 stub 맞춤

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-26 18:19:47 +09:00

254 lines
8.8 KiB
TypeScript

/* =============================================================================
* B01_Dashboard_UI_Legacy.ts
* 옛 한 페이지 대시보드의 구역 부품을 세부 페이지에 그대로 얹음(546-1) — 기능 빠짐 0.
* 새 모양 페이지가 등록부(`B01_Dashboard_UI_Views.ts`) 줄을 갈아 끼우면 여기 그 구역은 지움.
* 자료 = 열 때마다 역할별로 다시 읽음(옛 화면이 주소 바뀔 때마다 다시 읽던 것과 같음).
* ========================================================================== */
import { navigateTo } from "../A00_Common/router";
import { settingLinks } from "../A00_Common/m_settings_nav";
import { createButton, hideLoadingOverlay, showLoadingOverlay } from "@ui/ui_template_elements";
import { section } from "@ui/ui_template_general_blocks";
import {
fetchAllCompanies,
fetchAllUsers,
fetchAuditLogs,
fetchCompanyJoinRequests,
fetchCompanyMembers,
fetchCompanyProjects,
fetchSystemResources,
fetchUserCompany,
fetchUserProjects,
type AuditLog,
type CompanyInfo,
type DashboardUser,
type JoinRequest,
type Member,
type ProjectItem,
type ResourceData,
} from "./B01_Dashboard_Api_Fetch";
import { userTable } from "./B01_Dashboard_UI_Admin";
import {
assignedPlanTable,
fetchAssignedPlans,
type AssignedPlan,
} from "./B01_Dashboard_UI_AssignedPlans";
import { openCalcSitesDialog } from "./B01_Dashboard_UI_CalcSites";
import { L } from "./B01_Dashboard_UI_Common";
import {
buildCompanyPanel,
companyTable,
joinRequestTable,
memberTable,
} from "./B01_Dashboard_UI_Company";
import { openAddMemberModal } from "./B01_Dashboard_UI_Modals";
import { openNewProjectPicker } from "./B01_Dashboard_UI_NewProject";
import { projectTable } from "./B01_Dashboard_UI_Projects";
import { buildTempUploadSection } from "./B01_Dashboard_UI_TempUpload";
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;
/** 우리 회사에 지정된 예정노선(528-2) — 회사관리자 · 사용자만 */
assignedPlans: AssignedPlan[];
/** 시스템 관리자가 고른 회사(29-1) — 프로젝트 · 사용자 · 가입 요청 · 회사 관리가 이 회사 것 */
adminCompanyId: number | null;
}
/** 고른 회사 id 자리 — M02 도 같은 키를 읽음(이름 바꾸지 않음) */
export const ADMIN_COMPANY_KEY = "aislo.adminCompany";
/** 고른 회사 — 저장값이 없거나 없는 회사면 자기 회사 */
export function pickAdminCompany(user: DashboardUser, companies: CompanyInfo[]): number | null {
const saved = Number(sessionStorage.getItem(ADMIN_COMPANY_KEY));
if (companies.some((c) => c.id === saved)) return saved;
return user.company_id ?? companies[0]?.id ?? null;
}
async function loadRoleData(state: DashboardState): Promise<void> {
if (state.user.role === "SYSTEM_ADMIN") {
// 회사 목록을 먼저 — 고른 회사를 가린 뒤 그 회사 것만 읽음(29-1)
state.allCompanies = await fetchAllCompanies().catch(() => []);
const companyId = pickAdminCompany(state.user, state.allCompanies);
state.adminCompanyId = companyId;
const results = await Promise.allSettled([
fetchAllUsers(companyId),
fetchCompanyJoinRequests(companyId),
fetchAuditLogs(),
fetchSystemResources(7),
fetchCompanyProjects(companyId),
]);
state.allUsers = results[0].status === "fulfilled" ? results[0].value : [];
state.allJoinRequests = results[1].status === "fulfilled" ? results[1].value : [];
state.auditLogs = results[2].status === "fulfilled" ? results[2].value : [];
state.resources = results[3].status === "fulfilled" ? results[3].value : null;
state.allProjects = results[4].status === "fulfilled" ? results[4].value : [];
results.forEach((res, idx) => {
if (res.status === "rejected") {
console.error(`System Admin API failed at index ${idx}:`, res.reason);
}
});
return;
}
const [projects, company, assigned] = await Promise.all([
fetchUserProjects().catch(() => []),
fetchUserCompany().catch(() => null),
fetchAssignedPlans().catch(() => []),
]);
state.userProjects = projects;
state.company = company;
state.assignedPlans = assigned;
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 settingCard(title: string, desc: string, go: () => void): HTMLElement {
const card = document.createElement("button");
card.type = "button";
card.className = "b01-dashboard__setting-card";
const head = document.createElement("strong");
head.className = "b01-dashboard__setting-title";
head.textContent = title;
const sub = document.createElement("span");
sub.className = "b01-dashboard__setting-desc";
sub.textContent = desc;
card.append(head, sub);
card.addEventListener("click", go);
return card;
}
/** 설계 기본값 카드 — 카드 규칙은 `settingLinks` 한 벌(334) · 계산 자리 = 창(504-2) */
function buildSystemSettingsPanel(user: DashboardUser, companyId: number | null): HTMLElement {
const wrap = document.createElement("div");
wrap.className = "b01-dashboard__setting-grid";
wrap.append(
...settingLinks(user, companyId).map((link) =>
settingCard(link.title, link.desc, () =>
link.key === "calcSites" ? void openCalcSitesDialog() : navigateTo(link.route, link.query),
),
),
);
return wrap;
}
const addButton = (onClick: () => void): HTMLElement[] => [createButton({ label: "+", onClick })];
/** 열쇠별 옛 구역 — 역할 갈래는 옛 `buildPage` 그대로 */
const SECTIONS: Record<string, (s: DashboardState) => HTMLElement[]> = {
projects: (s) => {
const list =
s.user.role === "SYSTEM_ADMIN"
? s.allProjects
: s.user.role === "ADMIN"
? s.companyProjects
: s.userProjects;
return [
section(
L("B01_Dashboard_Projects"),
projectTable(list, s.user),
true,
addButton(() => void openNewProjectPicker()),
),
];
},
assigned: (s) => [section(L("DE_Assigned_Title"), assignedPlanTable(s.assignedPlans), true)],
temp: () => [buildTempUploadSection()],
people: (s) =>
s.user.role === "SYSTEM_ADMIN"
? [
section(
L("B01_Dashboard_Users"),
userTable(s.allUsers, s.user),
true,
addButton(() => openAddMemberModal(undefined, s.adminCompanyId)),
),
section(L("B01_Dashboard_JoinRequests"), joinRequestTable(s.allJoinRequests, true), true),
]
: [
section(
L("B01_Dashboard_Members"),
memberTable(s.members, s.user),
true,
addButton(() => openAddMemberModal()),
),
section(L("B01_Dashboard_JoinRequests"), joinRequestTable(s.joinRequests, false), true),
],
company: (s) =>
s.user.role === "SYSTEM_ADMIN"
? [
section(
L("B01_Dashboard_Companies"),
companyTable(
s.allCompanies.filter((c) => c.id === s.adminCompanyId),
s.user,
),
true,
),
]
: [section(L("B01_Dashboard_Company"), buildCompanyPanel(s), true)],
companyDefaults: (s) => [
section(
L("B01_Dashboard_SystemSettings"),
buildSystemSettingsPanel(s.user, s.adminCompanyId),
true,
),
],
};
export type LegacyKey = keyof typeof SECTIONS;
/** 옛 구역을 host 에 얹음 — 자료를 다 읽은 뒤 그림 · 그 사이 떠났으면(host 떨어짐) 안 그림 */
export async function mountLegacy(
key: LegacyKey,
host: HTMLElement,
me: DashboardUser,
): Promise<void> {
const state: DashboardState = {
user: me,
userProjects: [],
companyProjects: [],
allProjects: [],
company: null,
members: [],
joinRequests: [],
allCompanies: [],
allUsers: [],
allJoinRequests: [],
auditLogs: [],
resources: null,
assignedPlans: [],
adminCompanyId: null,
};
showLoadingOverlay();
try {
await loadRoleData(state);
} finally {
hideLoadingOverlay();
}
if (!host.isConnected) return;
const grid = document.createElement("div");
grid.className = "b01-dashboard__grid";
grid.append(...SECTIONS[key](state));
host.append(grid);
}