사용자 확정(2026-09-02) — 이름이 들어가는 자리는 서명도 받고, 담당자 선택에 신규 등록 항목을 두며, 회사 로고는 회사 등록 단계에서 받아 이후 변경. - 014_company_logo.sql: companies.logo_asset_id 추가 (서명은 company_assets.user_id 로 이미 표현되어 컬럼 없음). 공유 DB 적용 완료 - 사용자 수정 모달에 「서명 (도면 표제란)」 칸 — createAssetField 에 owner(주인 못박기)· onChange(즉시 반영) 추가로 재사용, 주인 고정 시 물리기 체크 잠금 - 담당자 select 3개에 「+ 신규 등록…」 — 계정 생성 모달 뒤 세 select 에 항목 삽입·자동 선택 - POST /admin/members 가 이름·직위·부서 수신, 계정 없으면 status=PENDING 으로 생성 (로그인 불가 비밀번호). 700줄 제한으로 B01_Dashboard_Repository_Members.py 분리 - 회사 등록 모달에 로고 파일 칸, PUT /company/logo 신설, 회사 패널·회사 목록에서 변경 - 프로젝트 수정 모달의 「설계자 서명」 칸 제거 — signature_asset_id 는 null 로 고정 저장 - 검증(5174 실조작): 신규 등록 구성원 2→3(id 6 PENDING)·select 자동 선택, 서명 자산 [4,'검증신규 서명',user_id 6] 생성, 회사 logo_asset_id None→1, 검증 자산·계정 정리. pytest 143 passed, tsc·ruff·prettier 통과 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
204 lines
7.2 KiB
TypeScript
204 lines
7.2 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 { 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 buildPage(state: DashboardState): HTMLElement {
|
|
const page = document.createElement("div");
|
|
page.className = "b01-dashboard";
|
|
page.append(buildHeader(state.user));
|
|
|
|
const grid = document.createElement("div");
|
|
grid.className = "b01-dashboard__grid";
|
|
|
|
if (state.user.role === "SYSTEM_ADMIN") {
|
|
grid.append(
|
|
section(L("B01_Dashboard_Resources"), buildResourcePanel(state.resources), true),
|
|
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() }),
|
|
]),
|
|
section(L("B01_Dashboard_AuditLogs"), auditLogTable(state.auditLogs), true),
|
|
);
|
|
} 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()),
|
|
);
|
|
page.append(grid);
|
|
return page;
|
|
}
|
|
|
|
function buildHeader(user: DashboardUser): HTMLElement {
|
|
const header = document.createElement("header");
|
|
header.className = "b01-dashboard__header";
|
|
const text = document.createElement("div");
|
|
const title = document.createElement("h1");
|
|
title.className = "b01-dashboard__title";
|
|
title.textContent = L("B01_Dashboard_Title");
|
|
const subtitle = document.createElement("p");
|
|
subtitle.className = "b01-dashboard__subtitle";
|
|
subtitle.textContent = L("B01_Dashboard_Subtitle");
|
|
text.append(title, subtitle);
|
|
const tag = createTag(roleLabel(user.role), user.role === "SYSTEM_ADMIN" ? "accent" : "neutral");
|
|
tag.classList.add("b01-dashboard__role");
|
|
header.append(text, tag);
|
|
return header;
|
|
}
|