260717_0
This commit is contained in:
@@ -115,18 +115,6 @@ export interface ResourceData {
|
||||
stats: ResourceSnapshot;
|
||||
}
|
||||
|
||||
export interface AutomationItem {
|
||||
id: number;
|
||||
project_id: string;
|
||||
name: string;
|
||||
logic_type: string;
|
||||
config_json: Record<string, unknown>;
|
||||
status: "DRAFT" | "ACTIVE" | "INACTIVE";
|
||||
last_executed_at?: string | null;
|
||||
created_at?: string | null;
|
||||
updated_at?: string | null;
|
||||
}
|
||||
|
||||
export interface UpdateUserRequest {
|
||||
name: string;
|
||||
position?: string | null;
|
||||
@@ -148,13 +136,6 @@ export interface AdminUpdateUserRequest extends UpdateUserRequest {
|
||||
status?: string | null;
|
||||
}
|
||||
|
||||
export interface AutomationRequest {
|
||||
name: string;
|
||||
logic_type: string;
|
||||
config_json: Record<string, unknown>;
|
||||
status: "DRAFT" | "ACTIVE" | "INACTIVE";
|
||||
}
|
||||
|
||||
export interface CreateCompanyRequest {
|
||||
name: string;
|
||||
business_registration_number: string;
|
||||
@@ -335,38 +316,6 @@ export async function fetchSystemResources(days = 30): Promise<ResourceData> {
|
||||
return request<ResourceData>(`/dashboard/admin/resources?days=${encodeURIComponent(days)}`);
|
||||
}
|
||||
|
||||
export async function fetchProjectAutomations(projectId: string): Promise<AutomationItem[]> {
|
||||
const data = await request<{ automations: AutomationItem[] }>(
|
||||
`/dashboard/projects/${encodeURIComponent(projectId)}/automations`,
|
||||
);
|
||||
return data.automations;
|
||||
}
|
||||
|
||||
export function createProjectAutomation(
|
||||
projectId: string,
|
||||
payload: AutomationRequest,
|
||||
): Promise<unknown> {
|
||||
return request(`/dashboard/projects/${encodeURIComponent(projectId)}/automations`, {
|
||||
method: "POST",
|
||||
body: body(payload),
|
||||
});
|
||||
}
|
||||
|
||||
export function updateProjectAutomation(
|
||||
automationId: number,
|
||||
payload: AutomationRequest,
|
||||
): Promise<unknown> {
|
||||
return request(`/dashboard/automations/${automationId}`, { method: "PUT", body: body(payload) });
|
||||
}
|
||||
|
||||
export function deleteProjectAutomation(automationId: number): Promise<unknown> {
|
||||
return request(`/dashboard/automations/${automationId}`, { method: "DELETE" });
|
||||
}
|
||||
|
||||
export function executeProjectAutomation(automationId: number): Promise<unknown> {
|
||||
return request(`/dashboard/automations/${automationId}/execute`, { method: "POST" });
|
||||
}
|
||||
|
||||
export function fetchProjectWorkflowState(projectId: string): Promise<WorkflowState> {
|
||||
return request(`/projects/${projectId}/workflow-state`, {
|
||||
method: "GET",
|
||||
|
||||
@@ -678,114 +678,3 @@ async def get_system_resources(days: int = 30) -> dict[str, Any]:
|
||||
(bucket_seconds, bucket_seconds, since, bucket_seconds),
|
||||
)
|
||||
return {"current": current, "history": list(await cursor.fetchall()), "stats": current}
|
||||
|
||||
|
||||
async def list_project_automations(project_id: str) -> list[dict[str, Any]]:
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor:
|
||||
await cursor.execute(
|
||||
"""SELECT id, project_id, name, logic_type, config_json, status,
|
||||
last_executed_at, created_at, updated_at
|
||||
FROM project_automations
|
||||
WHERE project_id = %s AND deleted_at IS NULL
|
||||
ORDER BY updated_at DESC, created_at DESC""",
|
||||
(project_id,),
|
||||
)
|
||||
return list(await cursor.fetchall())
|
||||
|
||||
|
||||
async def create_project_automation(
|
||||
project_id: str, actor_id: int, data: dict[str, Any]
|
||||
) -> dict[str, Any]:
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor:
|
||||
await connection.begin()
|
||||
await cursor.execute(
|
||||
"""INSERT INTO project_automations
|
||||
(project_id, name, logic_type, config_json, status, created_by, updated_by)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s)""",
|
||||
(
|
||||
project_id,
|
||||
data["name"],
|
||||
data["logic_type"],
|
||||
json.dumps(data["config_json"], ensure_ascii=False),
|
||||
data["status"],
|
||||
actor_id,
|
||||
actor_id,
|
||||
),
|
||||
)
|
||||
automation_id = cursor.lastrowid
|
||||
await connection.commit()
|
||||
await cursor.execute("SELECT * FROM project_automations WHERE id = %s", (automation_id,))
|
||||
return await cursor.fetchone()
|
||||
|
||||
|
||||
async def get_project_automation(automation_id: int) -> dict[str, Any] | None:
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor:
|
||||
await cursor.execute(
|
||||
"""SELECT a.*, p.company_id
|
||||
FROM project_automations a
|
||||
JOIN projects p ON p.id = a.project_id
|
||||
WHERE a.id = %s AND a.deleted_at IS NULL AND p.deleted_at IS NULL""",
|
||||
(automation_id,),
|
||||
)
|
||||
return await cursor.fetchone()
|
||||
|
||||
|
||||
async def update_project_automation(
|
||||
automation_id: int, actor_id: int, data: dict[str, Any]
|
||||
) -> bool:
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor() as cursor:
|
||||
await cursor.execute(
|
||||
"""UPDATE project_automations
|
||||
SET name = %s, logic_type = %s, config_json = %s, status = %s, updated_by = %s
|
||||
WHERE id = %s AND deleted_at IS NULL""",
|
||||
(
|
||||
data["name"],
|
||||
data["logic_type"],
|
||||
json.dumps(data["config_json"], ensure_ascii=False),
|
||||
data["status"],
|
||||
actor_id,
|
||||
automation_id,
|
||||
),
|
||||
)
|
||||
changed = cursor.rowcount > 0
|
||||
await connection.commit()
|
||||
return changed
|
||||
|
||||
|
||||
async def delete_project_automation(automation_id: int, actor_id: int) -> bool:
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor() as cursor:
|
||||
await connection.begin()
|
||||
await cursor.execute(
|
||||
"""UPDATE project_automations
|
||||
SET deleted_at = CURRENT_TIMESTAMP, updated_by = %s
|
||||
WHERE id = %s AND deleted_at IS NULL""",
|
||||
(actor_id, automation_id),
|
||||
)
|
||||
changed = cursor.rowcount > 0
|
||||
if changed:
|
||||
await cursor.execute(
|
||||
"""INSERT INTO system_audit_logs (user_id, action, resource_type, resource_id)
|
||||
VALUES (%s, 'AUTOMATION_DELETE', 'automation', %s)""",
|
||||
(actor_id, automation_id),
|
||||
)
|
||||
await connection.commit()
|
||||
return changed
|
||||
|
||||
|
||||
async def mark_project_automation_executed(automation_id: int, actor_id: int) -> bool:
|
||||
pool = get_db_pool()
|
||||
async with pool.acquire() as connection, connection.cursor() as cursor:
|
||||
await cursor.execute(
|
||||
"""UPDATE project_automations
|
||||
SET last_executed_at = CURRENT_TIMESTAMP, updated_by = %s
|
||||
WHERE id = %s AND deleted_at IS NULL""",
|
||||
(actor_id, automation_id),
|
||||
)
|
||||
changed = cursor.rowcount > 0
|
||||
await connection.commit()
|
||||
return changed
|
||||
|
||||
@@ -10,14 +10,9 @@ from .B01_Dashboard_Repository import (
|
||||
add_company_member,
|
||||
assign_user_company,
|
||||
change_user_role,
|
||||
count_company_admins,
|
||||
create_company,
|
||||
create_project_automation,
|
||||
create_system_company,
|
||||
delete_project_automation,
|
||||
get_dashboard_me,
|
||||
get_project,
|
||||
get_project_automation,
|
||||
get_system_resources,
|
||||
get_user_admin_target,
|
||||
get_user_company,
|
||||
@@ -29,23 +24,19 @@ from .B01_Dashboard_Repository import (
|
||||
list_company_members,
|
||||
list_company_projects,
|
||||
list_join_requests,
|
||||
list_project_automations,
|
||||
list_user_projects,
|
||||
mark_project_automation_executed,
|
||||
process_join_request,
|
||||
remove_company_member,
|
||||
search_companies,
|
||||
soft_delete_project,
|
||||
update_admin_user,
|
||||
update_project,
|
||||
update_project_automation,
|
||||
update_user_profile,
|
||||
)
|
||||
from .B01_Dashboard_Schema import (
|
||||
AddMemberRequest,
|
||||
AdminUpdateUserRequest,
|
||||
AssignCompanyRequest,
|
||||
AutomationRequest,
|
||||
ChangeUserRoleRequest,
|
||||
CreateCompanyRequest,
|
||||
JoinCompanyRequest,
|
||||
@@ -84,12 +75,6 @@ def _can_edit_project(session: dict[str, Any], project: dict[str, Any]) -> bool:
|
||||
return False
|
||||
|
||||
|
||||
def _can_manage_automation(session: dict[str, Any], project: dict[str, Any]) -> bool:
|
||||
if session["role"] == "SYSTEM_ADMIN":
|
||||
return True
|
||||
return session["role"] == "ADMIN" and _same_company(session, project.get("company_id"))
|
||||
|
||||
|
||||
def _can_edit_user(session: dict[str, Any], target: dict[str, Any]) -> bool:
|
||||
if session["role"] == "SYSTEM_ADMIN":
|
||||
return True
|
||||
@@ -361,81 +346,3 @@ async def system_resources(
|
||||
async def system_projects(session: dict[str, Any] = Depends(require_system_admin)):
|
||||
_ = session
|
||||
return {"status": "success", "projects": await list_all_projects()}
|
||||
|
||||
|
||||
@router.get("/projects/{project_id}/automations")
|
||||
async def dashboard_project_automations(
|
||||
project_id: str,
|
||||
session: dict[str, Any] = Depends(verify_session),
|
||||
):
|
||||
project = await get_project(project_id)
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="프로젝트를 찾을 수 없습니다.")
|
||||
if not _can_edit_project(session, project):
|
||||
raise HTTPException(status_code=403, detail="자동화 로직 조회 권한이 없습니다.")
|
||||
return {"status": "success", "automations": await list_project_automations(project_id)}
|
||||
|
||||
|
||||
@router.post("/projects/{project_id}/automations")
|
||||
async def dashboard_create_automation(
|
||||
project_id: str,
|
||||
payload: AutomationRequest,
|
||||
session: dict[str, Any] = Depends(verify_session),
|
||||
):
|
||||
project = await get_project(project_id)
|
||||
if not project:
|
||||
raise HTTPException(status_code=404, detail="프로젝트를 찾을 수 없습니다.")
|
||||
if not _can_manage_automation(session, project):
|
||||
raise HTTPException(status_code=403, detail="자동화 로직 생성 권한이 없습니다.")
|
||||
automation = await create_project_automation(
|
||||
project_id, int(session["user_id"]), payload.model_dump()
|
||||
)
|
||||
return {"status": "success", "automation": automation}
|
||||
|
||||
|
||||
@router.put("/automations/{automation_id}")
|
||||
async def dashboard_update_automation(
|
||||
automation_id: int,
|
||||
payload: AutomationRequest,
|
||||
session: dict[str, Any] = Depends(verify_session),
|
||||
):
|
||||
automation = await get_project_automation(automation_id)
|
||||
if not automation:
|
||||
raise HTTPException(status_code=404, detail="자동화 로직을 찾을 수 없습니다.")
|
||||
if not _can_manage_automation(session, automation):
|
||||
raise HTTPException(status_code=403, detail="자동화 로직 수정 권한이 없습니다.")
|
||||
if not await update_project_automation(
|
||||
automation_id, int(session["user_id"]), payload.model_dump()
|
||||
):
|
||||
raise HTTPException(status_code=404, detail="자동화 로직을 찾을 수 없습니다.")
|
||||
return {"status": "success"}
|
||||
|
||||
|
||||
@router.delete("/automations/{automation_id}")
|
||||
async def dashboard_delete_automation(
|
||||
automation_id: int,
|
||||
session: dict[str, Any] = Depends(verify_session),
|
||||
):
|
||||
automation = await get_project_automation(automation_id)
|
||||
if not automation:
|
||||
raise HTTPException(status_code=404, detail="자동화 로직을 찾을 수 없습니다.")
|
||||
if not _can_manage_automation(session, automation):
|
||||
raise HTTPException(status_code=403, detail="자동화 로직 삭제 권한이 없습니다.")
|
||||
if not await delete_project_automation(automation_id, int(session["user_id"])):
|
||||
raise HTTPException(status_code=404, detail="자동화 로직을 찾을 수 없습니다.")
|
||||
return {"status": "success"}
|
||||
|
||||
|
||||
@router.post("/automations/{automation_id}/execute")
|
||||
async def dashboard_execute_automation(
|
||||
automation_id: int,
|
||||
session: dict[str, Any] = Depends(verify_session),
|
||||
):
|
||||
automation = await get_project_automation(automation_id)
|
||||
if not automation:
|
||||
raise HTTPException(status_code=404, detail="자동화 로직을 찾을 수 없습니다.")
|
||||
if not _can_manage_automation(session, automation):
|
||||
raise HTTPException(status_code=403, detail="자동화 로직 실행 권한이 없습니다.")
|
||||
if not await mark_project_automation_executed(automation_id, int(session["user_id"])):
|
||||
raise HTTPException(status_code=404, detail="자동화 로직을 찾을 수 없습니다.")
|
||||
return {"status": "success"}
|
||||
|
||||
@@ -52,10 +52,3 @@ class AdminUpdateUserRequest(UpdateUserRequest):
|
||||
status: str | None = Field(
|
||||
default=None, pattern="^(NO_COMPANY|PENDING|ACTIVE|INACTIVE|REJECTED)$"
|
||||
)
|
||||
|
||||
|
||||
class AutomationRequest(BaseModel):
|
||||
name: str = Field(min_length=1, max_length=100)
|
||||
logic_type: str = Field(min_length=1, max_length=50)
|
||||
config_json: dict = Field(default_factory=dict)
|
||||
status: str = Field(default="DRAFT", pattern="^(DRAFT|ACTIVE|INACTIVE)$")
|
||||
|
||||
@@ -2,7 +2,8 @@ import { createButton } from "@ui/ui_template_elements";
|
||||
import type { AuditLog, DashboardUser } from "./B01_Dashboard_Api_Fetch";
|
||||
import { canChangeRole } from "./B01_Dashboard_UI_Helper";
|
||||
import { openChangeRoleModal, openEditUserModal } from "./B01_Dashboard_UI_Modals";
|
||||
import { formatDate, L, table, text } from "./B01_Dashboard_UI_Common";
|
||||
import { table, text } from "@ui/ui_template_general_blocks";
|
||||
import { formatDate, L } from "./B01_Dashboard_UI_Common";
|
||||
|
||||
export function userTable(users: DashboardUser[], currentUser: DashboardUser): HTMLElement {
|
||||
return table(
|
||||
|
||||
@@ -1,110 +1,17 @@
|
||||
import { currentLanguageIndex, ui_locales } from "@ui/ui_template_locale";
|
||||
import {
|
||||
createButton,
|
||||
createCard,
|
||||
hideLoadingOverlay,
|
||||
showLoadingOverlay,
|
||||
showToast,
|
||||
} from "@ui/ui_template_elements";
|
||||
import { hideLoadingOverlay, showLoadingOverlay, showToast } from "@ui/ui_template_elements";
|
||||
import type { DashboardUser } from "./B01_Dashboard_Api_Fetch";
|
||||
|
||||
export function L(key: keyof typeof ui_locales): string {
|
||||
return ui_locales[key][currentLanguageIndex];
|
||||
}
|
||||
|
||||
export function buildSectionHeader(
|
||||
titleText: string,
|
||||
actionButtons: HTMLElement[] = [],
|
||||
): HTMLElement {
|
||||
const header = document.createElement("div");
|
||||
header.className = "b01-dashboard__section-header";
|
||||
header.style.display = "flex";
|
||||
header.style.justifyContent = "space-between";
|
||||
header.style.alignItems = "center";
|
||||
header.style.marginBottom = "var(--spacing-16)";
|
||||
|
||||
const title = document.createElement("h3");
|
||||
title.className = "ui-card__title";
|
||||
title.style.margin = "0";
|
||||
title.textContent = titleText;
|
||||
|
||||
const actions = document.createElement("div");
|
||||
actions.className = "b01-dashboard__actions";
|
||||
actions.append(...actionButtons);
|
||||
|
||||
header.append(title, actions);
|
||||
return header;
|
||||
}
|
||||
|
||||
export function section(
|
||||
title: string,
|
||||
body: HTMLElement,
|
||||
wide = false,
|
||||
actions: HTMLElement[] = [],
|
||||
): HTMLElement {
|
||||
const header = buildSectionHeader(title, actions);
|
||||
const card = createCard({ body: [header, body], raised: true });
|
||||
card.classList.add("b01-dashboard__section");
|
||||
if (wide) card.classList.add("b01-dashboard__section--wide");
|
||||
return card;
|
||||
}
|
||||
|
||||
export function roleLabel(role: DashboardUser["role"]): string {
|
||||
if (role === "SYSTEM_ADMIN") return L("B01_Dashboard_Role_SystemAdmin");
|
||||
if (role === "ADMIN") return L("B01_Dashboard_Role_Admin");
|
||||
return L("B01_Dashboard_Role_User");
|
||||
}
|
||||
|
||||
export function table(headers: string[], rows: HTMLElement[][]): HTMLElement {
|
||||
if (!rows.length) {
|
||||
const empty = document.createElement("p");
|
||||
empty.className = "b01-dashboard__empty";
|
||||
empty.textContent = L("Common_Status_Empty");
|
||||
return empty;
|
||||
}
|
||||
const wrap = document.createElement("div");
|
||||
wrap.className = "b01-dashboard__table-wrap";
|
||||
const tableEl = document.createElement("table");
|
||||
tableEl.className = "b01-dashboard__table";
|
||||
const thead = document.createElement("thead");
|
||||
const headRow = document.createElement("tr");
|
||||
for (const header of headers) {
|
||||
const th = document.createElement("th");
|
||||
th.textContent = header;
|
||||
headRow.append(th);
|
||||
}
|
||||
thead.append(headRow);
|
||||
const tbody = document.createElement("tbody");
|
||||
for (const row of rows) {
|
||||
const tr = document.createElement("tr");
|
||||
for (const cell of row) {
|
||||
const td = document.createElement("td");
|
||||
td.append(cell);
|
||||
tr.append(td);
|
||||
}
|
||||
tbody.append(tr);
|
||||
}
|
||||
tableEl.append(thead, tbody);
|
||||
wrap.append(tableEl);
|
||||
return wrap;
|
||||
}
|
||||
|
||||
export function text(value: unknown): HTMLElement {
|
||||
const span = document.createElement("span");
|
||||
span.textContent = value == null || value === "" ? "-" : String(value);
|
||||
return span;
|
||||
}
|
||||
|
||||
export function actionPair(approve: () => void, reject: () => void): HTMLElement {
|
||||
const row = document.createElement("div");
|
||||
row.className = "b01-dashboard__actions";
|
||||
row.append(
|
||||
createButton({ label: L("B01_Dashboard_Approve"), variant: "ghost", onClick: approve }),
|
||||
createButton({ label: L("B01_Dashboard_Reject"), variant: "danger", onClick: reject }),
|
||||
);
|
||||
return row;
|
||||
}
|
||||
|
||||
export async function runRequest(action: () => Promise<unknown>): Promise<void> {
|
||||
showLoadingOverlay();
|
||||
try {
|
||||
|
||||
@@ -15,7 +15,8 @@ import {
|
||||
openFindCompanyModal,
|
||||
} from "./B01_Dashboard_UI_Modals";
|
||||
import type { DashboardState } from "./B01_Dashboard_UI_Page";
|
||||
import { actionPair, formatDate, L, runRequest, table, text } from "./B01_Dashboard_UI_Common";
|
||||
import { actionPair, table, text } from "@ui/ui_template_general_blocks";
|
||||
import { formatDate, L, runRequest } from "./B01_Dashboard_UI_Common";
|
||||
|
||||
export function buildCompanyPanel(state: DashboardState): HTMLElement {
|
||||
const wrap = document.createElement("div");
|
||||
|
||||
@@ -26,10 +26,3 @@ export function canDeleteUser(user: DashboardUser, _targetUser: Member | Dashboa
|
||||
// ADMIN은 본인 회사의 멤버만 삭제 가능
|
||||
return user.role === "ADMIN" && user.company_id !== null;
|
||||
}
|
||||
|
||||
export function canManageAutomation(
|
||||
user: DashboardUser,
|
||||
_projectOrAutomation: ProjectItem | { company_id?: number | null },
|
||||
): boolean {
|
||||
return user.role === "SYSTEM_ADMIN" || (user.role === "ADMIN" && user.company_id !== null);
|
||||
}
|
||||
|
||||
@@ -13,11 +13,6 @@ import {
|
||||
changeUserRole,
|
||||
updateDashboardUser,
|
||||
removeCompanyMember,
|
||||
fetchProjectAutomations,
|
||||
createProjectAutomation,
|
||||
updateProjectAutomation,
|
||||
deleteProjectAutomation,
|
||||
executeProjectAutomation,
|
||||
createCompany,
|
||||
joinCompany,
|
||||
searchCompanies,
|
||||
@@ -25,7 +20,6 @@ import {
|
||||
type DashboardUser,
|
||||
type ProjectItem,
|
||||
type Member,
|
||||
type AutomationItem,
|
||||
} from "./B01_Dashboard_Api_Fetch";
|
||||
|
||||
function L(key: keyof typeof ui_locales): string {
|
||||
@@ -282,136 +276,3 @@ export function openAddMemberModal(): void {
|
||||
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);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
showLoadingOverlay,
|
||||
showToast,
|
||||
} from "@ui/ui_template_elements";
|
||||
import { section } from "@ui/ui_template_general_blocks";
|
||||
import { navigateTo } from "../A00_Common/router";
|
||||
import {
|
||||
fetchAllCompanies,
|
||||
@@ -29,7 +30,7 @@ import {
|
||||
type ResourceData,
|
||||
} from "./B01_Dashboard_Api_Fetch";
|
||||
import { auditLogTable, userTable } from "./B01_Dashboard_UI_Admin";
|
||||
import { L, roleLabel, section } from "./B01_Dashboard_UI_Common";
|
||||
import { L, roleLabel } from "./B01_Dashboard_UI_Common";
|
||||
import {
|
||||
buildCompanyPanel,
|
||||
companyTable,
|
||||
|
||||
@@ -1,15 +1,12 @@
|
||||
import { createButton } from "@ui/ui_template_elements";
|
||||
import { table, text } from "@ui/ui_template_general_blocks";
|
||||
import { createStepBar } from "@ui/ui_template_workflow_layout";
|
||||
import { workflowSteps } from "../A00_Common/b_page_scaffold";
|
||||
import { goToWorkflowStage, WORKFLOW_STEP_ROUTES } from "../A00_Common/b_workflow_nav";
|
||||
import type { DashboardUser, ProjectItem } from "./B01_Dashboard_Api_Fetch";
|
||||
import { canDeleteProject, canEditProject, canManageAutomation } from "./B01_Dashboard_UI_Helper";
|
||||
import {
|
||||
openAutomationModal,
|
||||
openDeleteProjectModal,
|
||||
openEditProjectModal,
|
||||
} from "./B01_Dashboard_UI_Modals";
|
||||
import { formatDate, L, table, text } from "./B01_Dashboard_UI_Common";
|
||||
import { canDeleteProject, canEditProject } from "./B01_Dashboard_UI_Helper";
|
||||
import { openDeleteProjectModal, openEditProjectModal } from "./B01_Dashboard_UI_Modals";
|
||||
import { formatDate, L } from "./B01_Dashboard_UI_Common";
|
||||
|
||||
export function projectTable(projects: ProjectItem[], currentUser: DashboardUser): HTMLElement {
|
||||
return table(
|
||||
@@ -43,15 +40,6 @@ export function projectTable(projects: ProjectItem[], currentUser: DashboardUser
|
||||
}),
|
||||
);
|
||||
}
|
||||
if (canManageAutomation(currentUser, project)) {
|
||||
actCell.append(
|
||||
createButton({
|
||||
label: L("B01_Dashboard_AutomationLogic"),
|
||||
variant: "ghost",
|
||||
onClick: () => openAutomationModal(project),
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
return [
|
||||
text(project.name),
|
||||
|
||||
Reference in New Issue
Block a user