Files
Aislo/B01_Dashboard/B01_Dashboard_UI_Legacy.ts
T
eomsangdonandClaude Opus 5.5 e505f4014d feat(B01): 대시보드 새 틀 — 셸 · 왼쪽 메뉴 · 세부 페이지 등록부 (546-1)
- 셸 = 위 머리 48(AISLO · 회사 이름 — 시스템 관리자 회사 전환 · 찾기 자리 · [?] · 알림 자리 · 사용자 메뉴) + 왼쪽 메뉴 210 + 챗봇 단추
- 등록부 B01_Dashboard_UI_Views.ts — { key, title, group, roles, icon, mount(host, ctx) } · 주소 ?view= · 같은 주소 안 이동 = 페이지만 갈아 끼움(MountedPage)
- 역할 넷(일반 · 회사 관리자 · 산림청 · 시스템 관리자) · 권한 없는 묶음 · 항목 숨김 · 권한 없는 열쇠 = 홈
- 열쇠마다 옛 구역 부품 그대로 얹음(B01_Dashboard_UI_Legacy.ts) — 기능 빠짐 0
- 옛 ☰ 서랍 · 구역 접기 · 회사 알약 걷음 — 테마 · 언어 · 터치 · 설정 · 로그아웃은 사용자 메뉴로
- 아이콘 = Tabler(MIT) 선 아이콘 몇 개(ui_template_icons.ts · 라이센스 글 함께)
- 시험 — test_b01_settings_cards 옛 한 페이지 단정(buildPage · 접힘 · 알약) 지움 · 셸 회사 고르기 단정으로 바꿈 · test_546_b01_shell 새로

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 18:05:56 +09:00

287 lines
9.9 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 { auditLogTable, 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, openCreateCompanyModal } from "./B01_Dashboard_UI_Modals";
import { openNewProjectPicker } from "./B01_Dashboard_UI_NewProject";
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";
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,
),
],
sysOverview: (s) => [
section(L("B01_Dashboard_Resources"), buildResourcePanel(s.resources), true),
],
sysCompanies: (s) => [
section(
L("B01_Dashboard_Companies"),
companyTable(s.allCompanies, s.user),
true,
addButton(() => openCreateCompanyModal()),
),
],
sysUsers: (s) => [
section(
L("B01_Dashboard_Users"),
userTable(s.allUsers, s.user),
true,
addButton(() => openAddMemberModal(undefined, s.adminCompanyId)),
),
],
sysDefaults: (s) => [
section(
L("B01_Dashboard_SystemSettings"),
buildSystemSettingsPanel(s.user, s.adminCompanyId),
true,
),
],
sysLogs: (s) => [section(L("B01_Dashboard_AuditLogs"), auditLogTable(s.auditLogs), true)],
me: (s) => [
section(L("B01_Dashboard_Profile"), buildProfileForm(s.user), true),
section(L("B01_Account_Section_Security"), buildSecurityForm(), 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);
}