Files
Aislo/B01_Dashboard/B01_Dashboard_UI_Admin.ts
T
eomsangdonandClaude Sonnet 5.5 2a2c4d0f62 feat(B01): 대시보드 항목별 탭 — 해시 탭 줄 · 개요 · 프로젝트 · 회사 · 내 계정 · 시스템 (92-1·92-2)
- 해시 ?tab= 탭 줄(B08 단추 꼴) · 고른 탭 자료만 받음 · 설정 카드는 탭 줄 위 · 회사 고르기 알약은 탭 줄 오른쪽 하나
- 회사 없음 진입 두 곳(router · M02 Layer)은 ?tab=company · 회사 관리자 프로젝트 목록 한 번만 받음
- 개요 숫자 카드(단계별 · 실패 · 재확인 · 가입 대기 · 보관함 · 리소스) · 프로젝트 표 칸 보강 · 찾기 · 거르기 · 쪽 넘김 · 진행 현황
- 회사 정보 칸(사업자번호 · 대표 · 상태) · 팀원 읽기 · 자산 표 · 내 계정 마지막 로그인 · 시스템 리소스 7·30·90일 · 활성 추이 · 전체 회사 · 사용자 · 로그 쪽 · 고객 문의
- 시험 test_b01_dashboard_tabs 신규 · test_b01_settings_cards 고침

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MgChuAcyBqUkhHMTXomQC7
2026-10-08 13:58:34 +09:00

136 lines
4.5 KiB
TypeScript

import { createButton } from "@ui/ui_template_elements";
import type { AuditLog, DashboardUser } from "./B01_Dashboard_Api_Fetch";
import { canChangeRole, canDeleteUser } from "./B01_Dashboard_UI_Helper";
import {
openChangeRoleModal,
openDeleteUserModal,
openEditUserModal,
} from "./B01_Dashboard_UI_Modals";
import { table, text } from "@ui/ui_template_general_blocks";
import { formatDate, formatTime, L, stackedCell } from "./B01_Dashboard_UI_Common";
import { createPagedTable } from "./B01_Dashboard_UI_Paging";
export function userTable(users: DashboardUser[], currentUser: DashboardUser): HTMLElement {
return createPagedTable(
[
L("B01_Dashboard_Table_Email"),
L("B01_Dashboard_Table_Name"),
L("B01_Dashboard_Table_Position"),
L("B01_Dashboard_Table_Department"),
L("B01_Account_Field_Phone"),
L("B01_Dashboard_Table_Role"),
L("B01_Dashboard_Table_Status"),
L("B01_Dashboard_Table_Action"),
],
(user: DashboardUser) => {
const actionsEl = document.createElement("div");
actionsEl.className = "b01-dashboard__actions";
actionsEl.append(
createButton({
label: L("Common_Btn_Edit"),
variant: "ghost",
onClick: () => openEditUserModal(currentUser, user),
}),
);
if (canChangeRole(currentUser, user)) {
actionsEl.append(
createButton({
label: L("B01_Dashboard_ChangeRole"),
variant: "ghost",
onClick: () => openChangeRoleModal(user),
}),
);
}
// 회사에서 빼기·계정 삭제 (2026-09-06 사용자 지시) — 범위는 백엔드가 다시 본다.
if (canDeleteUser(currentUser, user) && user.id !== currentUser.id) {
actionsEl.append(
createButton({
label: L("B01_Dashboard_DeleteUser"),
variant: "ghost",
onClick: () => openDeleteUserModal(user),
}),
);
}
return [
text(user.email),
text(user.name),
text(user.position),
text(user.department),
text(user.phone),
text(user.role),
text(user.status),
actionsEl,
];
},
users,
).root;
}
/** 전체 사용자(시스템 탭) — 소속 · 마지막 로그인까지 읽기만 · 관리는 회사 탭 */
export function allUserTable(users: DashboardUser[]): HTMLElement {
return createPagedTable(
[
L("B01_Dashboard_Table_Email"),
L("B01_Dashboard_Table_Name"),
L("B01_Sy_Affiliation"),
L("B01_Dashboard_Table_Role"),
L("B01_Dashboard_Table_Status"),
L("B01_Sy_LastLogin"),
],
(user: DashboardUser) => [
text(user.email),
text(user.name),
text(user.company_name),
text(user.role),
text(user.status),
stackedCell(formatDate(user.last_login), formatTime(user.last_login)),
],
users,
).root;
}
/** 무엇에 한 일인지 — 표에 저장된 대상 종류·번호를 사람이 읽는 말로. */
function auditTarget(log: AuditLog): string {
// 저장값은 소문자(`project`)로 들어온다 — 대문자로 맞춰 찾는다.
const key = (log.resource_type ?? "").toUpperCase();
const kind = TARGET_LABELS[key] ?? log.resource_type ?? "";
const reference = log.resource_ref ?? (log.resource_id ? String(log.resource_id) : "");
if (!kind) return reference || "-";
// 프로젝트 UUID 는 길어 앞 8자만 — 어느 프로젝트인지 가리기에는 충분하다.
const shortened = reference.length > 12 ? `${reference.slice(0, 8)}…` : reference;
return shortened ? `${kind} ${shortened}` : kind;
}
const TARGET_LABELS: Record<string, string> = {
PROJECT: "프로젝트",
COMPANY: "회사",
USER: "사용자",
ASSET: "자산",
};
export function auditLogTable(logs: AuditLog[]): HTMLElement {
return table(
[
L("B01_Dashboard_Table_Email"),
// 관리 버튼 열과 같은 말(「관리」)을 돌려 쓰던 것을 갈랐다 (2026-09-06 사용자 지적).
L("B01_Dashboard_Table_Event"),
L("B01_Dashboard_Table_Target"),
L("B01_Dashboard_Table_Origin"),
L("B01_Dashboard_Table_When"),
],
logs.map((log) => [
text(log.email),
text(log.action),
text(auditTarget(log)),
// 접속 주소 — 기록이 없는 옛 줄은 빈칸으로 남는다 (2026-09-06부터 기록).
text(log.ip_address ?? "-"),
// 날짜와 시각을 두 줄로 — 아랫줄이 작은 글씨라 행 높이는 그대로다.
stackedCell(formatDate(log.timestamp), formatTime(log.timestamp)),
]),
);
}