113 lines
4.0 KiB
TypeScript
113 lines
4.0 KiB
TypeScript
/* =============================================================================
|
|
* B01_Dashboard_UI_View_Trash.ts
|
|
* 휴지통(592) — 지운 프로젝트 표(이름 · 종류 · 회사 · 지운 사람 · 지운 시각 · 남은 날) + [되살리기] · [영구 삭제].
|
|
* 시스템 관리자 = 전부 · 영구 삭제까지 · 회사 관리자 = 자기 회사 것 되살리기만(서버가 거름).
|
|
* 남은 날이 다 되면 서버 정리 루프가 DB 행 · 폴더째 지움.
|
|
* ========================================================================== */
|
|
|
|
import { askConfirm } from "@ui/ui_template_modal";
|
|
import { showToast } from "@ui/ui_template_toast";
|
|
import { createButton } from "@ui/ui_template_elements";
|
|
import {
|
|
fetchTrashProjects,
|
|
purgeTrashProject,
|
|
restoreTrashProject,
|
|
type TrashProject,
|
|
} from "./B01_Dashboard_Api_Fetch";
|
|
import { formatDate, formatTime } from "./B01_Dashboard_UI_Common";
|
|
import { badge, dataTable, emptyState, nameCell, pageFrame } from "./B01_Dashboard_UI_PageKit";
|
|
import { kindBadge } from "./B01_Dashboard_UI_ProjectList_Parts";
|
|
import { el as h, loadPage } from "./B01_Dashboard_UI_View_Parts";
|
|
import type { ViewCtx } from "./B01_Dashboard_UI_Views";
|
|
|
|
const COLUMNS = [
|
|
{ label: "이름" },
|
|
{ label: "종류" },
|
|
{ label: "회사" },
|
|
{ label: "지운 사람" },
|
|
{ label: "지운 시각" },
|
|
{ label: "남은 날" },
|
|
{ label: "관리", align: "end" as const },
|
|
];
|
|
|
|
export function mountTrash(host: HTMLElement, ctx: ViewCtx): void {
|
|
const admin = ctx.role === "SYSTEM_ADMIN";
|
|
const run = (): void =>
|
|
loadPage(host, async (stage) => {
|
|
const rows = await fetchTrashProjects();
|
|
const frame = pageFrame(
|
|
stage,
|
|
"휴지통",
|
|
"지운 프로젝트는 7 일 동안 여기 있다가 자료째 영구 삭제됩니다. 그 전에 되살릴 수 있습니다.",
|
|
);
|
|
const act = async (
|
|
p: TrashProject,
|
|
ask: string,
|
|
call: (id: string) => Promise<unknown>,
|
|
done: string,
|
|
danger = false,
|
|
): Promise<void> => {
|
|
if (!(await askConfirm({ title: p.name, message: ask, danger }))) return;
|
|
try {
|
|
await call(p.id);
|
|
showToast(done, "success");
|
|
} catch (error) {
|
|
showToast(error instanceof Error ? error.message : "처리하지 못했습니다.", "error");
|
|
}
|
|
run();
|
|
};
|
|
const actions = (p: TrashProject): HTMLElement => {
|
|
const box = h("div", "b01-sys-tool");
|
|
box.style.justifyContent = "flex-end";
|
|
box.append(
|
|
createButton({
|
|
label: "되살리기",
|
|
variant: "ghost",
|
|
size: 28,
|
|
onClick: () =>
|
|
void act(
|
|
p,
|
|
"프로젝트를 되살려 목록에 다시 보이게 합니다.",
|
|
restoreTrashProject,
|
|
"되살렸습니다.",
|
|
),
|
|
}),
|
|
);
|
|
if (admin)
|
|
box.append(
|
|
createButton({
|
|
label: "영구 삭제",
|
|
variant: "danger-ghost",
|
|
size: 28,
|
|
onClick: () =>
|
|
void act(
|
|
p,
|
|
"DB 자료와 프로젝트 폴더를 지금 영구 삭제합니다. 되돌릴 수 없습니다.",
|
|
purgeTrashProject,
|
|
"영구 삭제했습니다.",
|
|
true,
|
|
),
|
|
}),
|
|
);
|
|
return box;
|
|
};
|
|
frame.body.append(
|
|
rows.length
|
|
? dataTable(
|
|
COLUMNS,
|
|
rows.map((p) => [
|
|
p.name,
|
|
kindBadge(p.project_kind),
|
|
p.company_name ?? "-",
|
|
nameCell(p.deleted_by_name ?? "-", p.deleted_by_email),
|
|
`${formatDate(p.deleted_at)} ${formatTime(p.deleted_at)}`.trim(),
|
|
badge(`${p.days_left} 일`, p.days_left <= 1 ? "danger" : "neutral"),
|
|
actions(p),
|
|
]),
|
|
)
|
|
: emptyState({ icon: "trash", text: "휴지통이 비어 있습니다." }),
|
|
);
|
|
});
|
|
run();
|
|
}
|