This commit is contained in:
2026-07-10 16:41:22 +09:00
parent bbda38a013
commit 50d75fcfcd
47 changed files with 6309 additions and 350 deletions
+2 -1
View File
@@ -436,7 +436,8 @@ async def list_all_users() -> list[dict[str, Any]]:
pool = get_db_pool()
async with pool.acquire() as connection, connection.cursor(aiomysql.DictCursor) as cursor:
await cursor.execute(
"""SELECT u.id, u.email, u.name, u.role, u.status, u.company_id,
"""SELECT u.id, u.email, u.name, u.position, u.department, u.phone,
u.role, u.status, u.company_id,
c.name AS company_name, u.last_login
FROM users u LEFT JOIN companies c ON c.id = u.company_id
WHERE u.deleted_at IS NULL ORDER BY u.created_at DESC LIMIT 200"""
+10 -19
View File
@@ -2,6 +2,7 @@ import { ui_locales, currentLanguageIndex } from "@ui/ui_template_locale";
import {
createButton,
createInputField,
createSelectField,
showToast,
showLoadingOverlay,
hideLoadingOverlay,
@@ -181,27 +182,17 @@ export function openEditUserModal(user: DashboardUser, target: Member | Dashboar
}
export function openChangeRoleModal(target: Member | DashboardUser): void {
const select = document.createElement("select");
select.className = "ui-input-field__input";
const roles = ["USER", "ADMIN"];
roles.forEach((r) => {
const opt = document.createElement("option");
opt.value = r;
opt.textContent = r;
opt.selected = target.role === r;
select.append(opt);
const roleField = createSelectField({
label: L("B01_Dashboard_Table_Role"),
options: [
{ value: "USER", text: "USER" },
{ value: "ADMIN", text: "ADMIN" },
],
value: target.role,
});
const wrap = document.createElement("div");
wrap.className = "ui-input-field";
const label = document.createElement("label");
label.className = "ui-input-field__label";
label.textContent = L("B01_Dashboard_Table_Role");
wrap.append(label, select);
openModal(L("B01_Dashboard_ChangeRole"), [wrap], async () => {
await changeUserRole(target.id, select.value);
openModal(L("B01_Dashboard_ChangeRole"), [roleField.root], async () => {
await changeUserRole(target.id, roleField.select.value);
showToast(L("B01_Dashboard_Saved"), "success");
});
}
+13 -1
View File
@@ -527,6 +527,9 @@ function userTable(users: DashboardUser[], currentUser: DashboardUser): HTMLElem
[
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"),
@@ -553,7 +556,16 @@ function userTable(users: DashboardUser[], currentUser: DashboardUser): HTMLElem
);
}
return [text(user.email), text(user.name), text(user.role), text(user.status), actionsEl];
return [
text(user.email),
text(user.name),
text(user.position),
text(user.department),
text(user.phone),
text(user.role),
text(user.status),
actionsEl,
];
}),
);
}
+6 -3
View File
@@ -70,8 +70,11 @@ function buildResourceChart(resources: ResourceData | null): HTMLElement {
0,
);
for (let i = 0; i <= 42; i++) {
const time = new Date(baseDate.getTime() - (42 - i) * 4 * 60 * 60 * 1000);
// 7일을 1시간 버킷으로 분할. 버킷이 넓으면 짧은 가동 구간이 한 칸에 뭉쳐
// 점 하나(선 없음)가 되므로, 가동 시간대가 연속 버킷으로 이어지도록 1시간 단위 사용.
const HOURS = 7 * 24;
for (let i = 0; i <= HOURS; i++) {
const time = new Date(baseDate.getTime() - (HOURS - i) * 60 * 60 * 1000);
points.push({ timestamp: time, cpu: null, mem: null, disk: null });
}
@@ -84,7 +87,7 @@ function buildResourceChart(resources: ResourceData | null): HTMLElement {
points.forEach((p, idx) => {
const diff = Math.abs(p.timestamp.getTime() - hTime.getTime());
if (diff < minDiff && diff <= 2 * 60 * 60 * 1000) {
if (diff < minDiff && diff <= 30 * 60 * 1000) {
minDiff = diff;
closestIdx = idx;
}
+4
View File
@@ -195,6 +195,10 @@
padding: var(--spacing-24);
}
.b01-dashboard__modal-panel .b01-dashboard__actions {
margin-top: var(--spacing-24);
}
.b01-dashboard__modal-title {
margin-bottom: var(--spacing-16);
}