Files
Aislo/B01_Dashboard/B01_Dashboard_UI_Modals.ts
T
2026-07-10 16:41:22 +09:00

419 lines
13 KiB
TypeScript

import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
import {
createButton,
createInputField,
createSelectField,
showToast,
showLoadingOverlay,
hideLoadingOverlay,
} from "@ui/ui_template_elements";
import {
updateProject,
deleteProject,
changeUserRole,
updateDashboardUser,
removeCompanyMember,
fetchProjectAutomations,
createProjectAutomation,
updateProjectAutomation,
deleteProjectAutomation,
executeProjectAutomation,
createCompany,
joinCompany,
searchCompanies,
addCompanyMember,
type DashboardUser,
type ProjectItem,
type Member,
type AutomationItem,
type CompanyInfo,
} from "./B01_Dashboard_Api_Fetch";
function L(key: keyof typeof ui_locales): string {
return ui_locales[key][currentLanguageIndex];
}
function openModal(title: string, body: HTMLElement[], onConfirm: () => Promise<void>): void {
const modal = document.createElement("div");
modal.className = "b01-dashboard__modal";
const panel = document.createElement("div");
panel.className = "b01-dashboard__modal-panel";
const heading = document.createElement("h3");
heading.className = "b01-dashboard__modal-title";
heading.textContent = title;
const actions = document.createElement("div");
actions.className = "b01-dashboard__actions";
actions.append(
createButton({
label: L("Common_Btn_Cancel"),
variant: "ghost",
onClick: () => modal.remove(),
}),
createButton({
label: L("Common_Btn_Confirm"),
onClick: async () => {
showLoadingOverlay();
try {
await onConfirm();
modal.remove();
window.dispatchEvent(new HashChangeEvent("hashchange"));
} catch (error) {
showToast(
error instanceof Error ? error.message : L("B01_Dashboard_RequestFailed"),
"error",
);
} finally {
hideLoadingOverlay();
}
},
}),
);
panel.append(heading, ...body, actions);
modal.append(panel);
document.body.append(modal);
}
export function openEditProjectModal(user: DashboardUser, project: ProjectItem): void {
const isUserOnly = user.role === "USER";
const name = createInputField({
label: L("B01_Dashboard_Table_Project"),
value: project.name,
required: true,
});
const region = createInputField({
label: L("B01_Dashboard_Table_Region"),
value: project.region ?? "",
});
const roadType = createInputField({ label: "임도 종류", value: project.road_type ?? "" });
const year = createInputField({
label: "사업 연도",
type: "number",
value: String(project.project_year ?? ""),
});
const length = createInputField({
label: "예상 연장 (m)",
type: "number",
value: String(project.estimated_length_m ?? ""),
});
const memo = createInputField({ label: "비고", value: project.memo ?? "" });
if (isUserOnly) {
name.input.disabled = true;
region.input.disabled = true;
roadType.input.disabled = true;
year.input.disabled = true;
length.input.disabled = true;
memo.input.disabled = true;
}
openModal(
L("B01_Dashboard_EditProject"),
[name.root, region.root, roadType.root, year.root, length.root, memo.root],
async () => {
await updateProject(project.id, {
name: name.input.value.trim(),
region: region.input.value.trim() || null,
road_type: roadType.input.value.trim() || null,
project_year: year.input.value ? Number(year.input.value) : null,
estimated_length_m: length.input.value ? Number(length.input.value) : null,
memo: memo.input.value.trim() || null,
status: project.status,
});
showToast(L("B01_Dashboard_Saved"), "success");
},
);
}
export function openDeleteProjectModal(project: ProjectItem): void {
const warning = document.createElement("p");
warning.className = "b01-dashboard__modal-text";
warning.textContent = L("B01_Dashboard_Confirm_DeleteProject");
openModal(L("B01_Dashboard_DeleteProject"), [warning], async () => {
await deleteProject(project.id);
showToast(L("B01_Dashboard_Saved"), "success");
});
}
export function openEditUserModal(user: DashboardUser, target: Member | DashboardUser): void {
const name = createInputField({
label: L("B01_Dashboard_Table_Name"),
value: target.name,
required: true,
});
const position = createInputField({
label: L("B01_Dashboard_Table_Position"),
value: target.position ?? "",
});
const department = createInputField({
label: L("B01_Dashboard_Table_Department"),
value: target.department ?? "",
});
const phoneVal = (target as DashboardUser).phone || "";
const phone = createInputField({ label: L("B01_Account_Field_Phone"), value: phoneVal });
if (user.role === "ADMIN") {
// ADMIN은 직책(position) / 부서(department)만 수정 정보 가능
name.input.disabled = true;
phone.input.disabled = true;
} else if (user.role === "USER" && user.id !== target.id) {
name.input.disabled = true;
position.input.disabled = true;
department.input.disabled = true;
phone.input.disabled = true;
}
openModal(
L("B01_Dashboard_EditUser"),
[name.root, position.root, department.root, phone.root],
async () => {
await updateDashboardUser(target.id, {
name: name.input.value.trim(),
position: position.input.value.trim() || null,
department: department.input.value.trim() || null,
phone: phone.input.value.trim() || null,
status: target.status,
});
showToast(L("B01_Dashboard_Saved"), "success");
},
);
}
export function openChangeRoleModal(target: Member | DashboardUser): void {
const roleField = createSelectField({
label: L("B01_Dashboard_Table_Role"),
options: [
{ value: "USER", text: "USER" },
{ value: "ADMIN", text: "ADMIN" },
],
value: target.role,
});
openModal(L("B01_Dashboard_ChangeRole"), [roleField.root], async () => {
await changeUserRole(target.id, roleField.select.value);
showToast(L("B01_Dashboard_Saved"), "success");
});
}
export function openDeleteUserModal(target: Member | DashboardUser): void {
const warning = document.createElement("p");
warning.className = "b01-dashboard__modal-text";
warning.textContent = L("B01_Dashboard_Confirm_DeleteUser");
openModal(L("B01_Dashboard_DeleteUser"), [warning], async () => {
await removeCompanyMember(target.id);
showToast(L("B01_Dashboard_Saved"), "success");
});
}
export function openCreateCompanyModal(): void {
const name = createInputField({ label: L("B01_Dashboard_Table_Company"), required: true });
const number = createInputField({
label: L("B01_Dashboard_Field_BusinessNumber"),
required: true,
});
const address = createInputField({ label: L("B01_Dashboard_Field_Address") });
const owner = createInputField({ label: L("B01_Dashboard_Field_Owner") });
openModal(
L("B01_Dashboard_Modal_CreateCompany"),
[name.root, number.root, address.root, owner.root],
async () => {
if (!name.input.value.trim() || !number.input.value.trim()) return;
await createCompany({
name: name.input.value.trim(),
business_registration_number: number.input.value.trim(),
business_address: address.input.value.trim() || null,
business_owner: owner.input.value.trim() || null,
});
showToast(L("B01_Dashboard_Saved"), "success");
},
);
}
export function openFindCompanyModal(): void {
const query = createInputField({ label: L("B01_Dashboard_Field_Search"), required: true });
const results = document.createElement("div");
results.className = "b01-dashboard__actions";
const search = createButton({
label: L("Common_Btn_Search"),
variant: "ghost",
onClick: async function onB01_Company_Search_Click() {
results.innerHTML = "";
const companies = await searchCompanies(query.input.value.trim());
for (const company of companies) {
results.append(
createButton({
label: `${company.name} ${L("B01_Dashboard_JoinCompany")}`,
variant: "ghost",
onClick: async () => {
showLoadingOverlay();
try {
await joinCompany(company.id);
showToast(L("B01_Dashboard_Saved"), "success");
} catch (e) {
showToast("요청 실패", "error");
} finally {
hideLoadingOverlay();
}
},
}),
);
}
},
});
openModal(
L("B01_Dashboard_Modal_FindCompany"),
[query.root, search, results],
async () => undefined,
);
}
export function openAddMemberModal(): void {
const email = createInputField({
label: L("B01_Dashboard_Field_MemberEmail"),
type: "email",
required: true,
});
openModal(L("B01_Dashboard_Modal_AddMember"), [email.root], async () => {
if (!email.input.value.trim()) return;
await addCompanyMember(email.input.value.trim());
showToast(L("B01_Dashboard_Saved"), "success");
});
}
export async function openAutomationModal(project: ProjectItem): Promise<void> {
const modal = document.createElement("div");
modal.className = "b01-dashboard__modal";
const panel = document.createElement("div");
panel.className = "b01-dashboard__modal-panel b01-dashboard__modal-panel--wide";
const heading = document.createElement("h3");
heading.className = "b01-dashboard__modal-title";
heading.textContent = `${project.name} - ${L("B01_Dashboard_AutomationLogic")}`;
const listWrap = document.createElement("div");
listWrap.className = "b01-dashboard__automation-list";
const refreshList = async () => {
listWrap.innerHTML = L("Common_Status_Loading");
try {
const data = await fetchProjectAutomations(project.id);
listWrap.innerHTML = "";
if (data.length === 0) {
listWrap.textContent = L("Common_Status_Empty");
return;
}
data.forEach((item) => {
const itemEl = document.createElement("div");
itemEl.className = "b01-dashboard__automation-item";
const info = document.createElement("div");
info.innerHTML = `<strong>${item.name}</strong> (${item.logic_type}) - status: ${item.status}`;
const actions = document.createElement("div");
actions.className = "b01-dashboard__actions";
const runBtn = createButton({
label: L("B01_Dashboard_ExecuteAutomation"),
variant: "ghost",
onClick: async () => {
await executeProjectAutomation(item.id);
showToast("실행 성공", "success");
refreshList();
},
});
const editBtn = createButton({
label: L("Common_Btn_Edit"),
variant: "ghost",
onClick: () => {
modal.remove();
openEditAutomationModal(project, item);
},
});
const delBtn = createButton({
label: L("Common_Btn_Delete"),
variant: "danger",
onClick: () => {
modal.remove();
openDeleteAutomationModal(project, item);
},
});
actions.append(runBtn, editBtn, delBtn);
itemEl.append(info, actions);
listWrap.append(itemEl);
});
} catch (e) {
listWrap.textContent = "로딩 에러";
}
};
const createBtn = createButton({
label: L("B01_Dashboard_CreateAutomation"),
onClick: () => {
modal.remove();
openCreateAutomationModal(project);
},
});
const closeBtn = createButton({
label: L("Common_Btn_Close"),
variant: "ghost",
onClick: () => modal.remove(),
});
panel.append(heading, createBtn, listWrap, closeBtn);
modal.append(panel);
document.body.append(modal);
await refreshList();
}
function openCreateAutomationModal(project: ProjectItem): void {
const name = createInputField({ label: "자동화 로직명", required: true });
const type = createInputField({ label: "로직 타입", required: true });
openModal(L("B01_Dashboard_CreateAutomation"), [name.root, type.root], async () => {
await createProjectAutomation(project.id, {
name: name.input.value.trim(),
logic_type: type.input.value.trim(),
config_json: {},
status: "DRAFT",
});
showToast(L("B01_Dashboard_Saved"), "success");
setTimeout(() => openAutomationModal(project), 300);
});
}
function openEditAutomationModal(project: ProjectItem, item: AutomationItem): void {
const name = createInputField({ label: "자동화 로직명", value: item.name, required: true });
const type = createInputField({ label: "로직 타입", value: item.logic_type, required: true });
openModal(L("B01_Dashboard_EditAutomation"), [name.root, type.root], async () => {
await updateProjectAutomation(item.id, {
name: name.input.value.trim(),
logic_type: type.input.value.trim(),
config_json: item.config_json,
status: item.status,
});
showToast(L("B01_Dashboard_Saved"), "success");
setTimeout(() => openAutomationModal(project), 300);
});
}
function openDeleteAutomationModal(project: ProjectItem, item: AutomationItem): void {
const warning = document.createElement("p");
warning.textContent = L("B01_Dashboard_DeleteAutomation") + "?";
openModal(L("B01_Dashboard_DeleteAutomation"), [warning], async () => {
await deleteProjectAutomation(item.id);
showToast(L("B01_Dashboard_Saved"), "success");
setTimeout(() => openAutomationModal(project), 300);
});
}