- 프로젝트 표의 진행도(%) 열 삭제 — 워크플로 배지가 같은 것을 보여 줌. 상태 문자열로 따로 세던 서버 계산도 제거(배지는 `project_workflow_stages` 표가 근거라 둘이 어긋났음) - 시스템 로그 표 = 이메일 · 동작 · 대상 · 일시. 「관리」 문구를 돌려 쓰던 것을 가르고, 대상(resource_type·id)을 새로 보이며, 일시는 날짜/시각 두 줄(아랫줄 작은 글씨라 행 높이 불변) - 표 안 관리 버튼을 한 줄로 — 작은 버튼 + 줄바꿈 금지, 폭이 모자라면 표가 가로 스크롤 (프로젝트 행 높이 105px → 61px, 사용자 행 55px) - 기본정보는 로그인 본인 화면 — 이메일 라벨을 「팀원 이메일」에서 「이메일」로 바꾸고 본인 서명 칸을 사용자 수정 모달과 같은 부품으로 추가 - 제목·여백을 공용 템플릿(`createGeneralLayout`)으로 통일, 역할 배지는 제목 줄 오른쪽 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
111 lines
4.1 KiB
TypeScript
111 lines
4.1 KiB
TypeScript
import { isBlank } from "@util/common_util_validate";
|
|
import { createButton, createInputField } from "@ui/ui_template_elements";
|
|
import {
|
|
changePassword,
|
|
fetchCompanyAssets,
|
|
updateCompanyAsset,
|
|
updateUserProfile,
|
|
type DashboardUser,
|
|
} from "./B01_Dashboard_Api_Fetch";
|
|
import { createAssetField } from "./B01_Dashboard_UI_AssetPicker";
|
|
import { buildUserFields, L, runRequest } from "./B01_Dashboard_UI_Common";
|
|
|
|
const PASSWORD_MIN_LENGTH = 8;
|
|
|
|
export function buildProfileForm(user: DashboardUser): HTMLElement {
|
|
// 로그인한 본인의 정보다 — 이메일 라벨도 「이메일」로 나간다 (2026-09-06 사용자 지시).
|
|
const fields = buildUserFields(user, { self: true });
|
|
const grid = fields.grid;
|
|
// 본인 서명 — 사용자 수정 모달과 같은 칸이다. 도면 표제란이 이 사람 자리를 채울 때
|
|
// 그대로 실리므로 본인이 여기서 바로 걸 수 있게 둔다 (2026-09-06 사용자 지시).
|
|
const signatureSlot = document.createElement("div");
|
|
if (user.company_id) {
|
|
void fetchCompanyAssets(user.company_id).then((assets) => {
|
|
const owned = assets.find((asset) => asset.kind === "SIGNATURE" && asset.user_id === user.id);
|
|
signatureSlot.append(
|
|
createAssetField("서명", "SIGNATURE", assets, owned?.id ?? null, user.company_id!, user, {
|
|
owner: { id: user.id, name: user.name },
|
|
onChange: async (assetId) => {
|
|
if (assetId === null) return;
|
|
const picked = assets.find((asset) => asset.id === assetId);
|
|
if (picked)
|
|
await updateCompanyAsset(assetId, { label: picked.label, user_id: user.id });
|
|
},
|
|
}).root,
|
|
);
|
|
});
|
|
}
|
|
const save = createButton({
|
|
label: L("B01_Dashboard_SaveProfile"),
|
|
onClick: async function onB01_Profile_Save_Click() {
|
|
if (!fields.validate()) return;
|
|
await runRequest(() => updateUserProfile(fields.values()));
|
|
},
|
|
});
|
|
const wrap = document.createElement("div");
|
|
wrap.append(grid, signatureSlot, save);
|
|
return wrap;
|
|
}
|
|
|
|
export function buildSecurityForm(): HTMLElement {
|
|
const currentPassword = createInputField({
|
|
label: L("B01_Account_Field_CurrentPw"),
|
|
type: "password",
|
|
required: true,
|
|
});
|
|
const newPassword = createInputField({
|
|
label: L("B01_Account_Field_NewPw"),
|
|
type: "password",
|
|
required: true,
|
|
});
|
|
const confirmPassword = createInputField({
|
|
label: L("B01_Account_Field_ConfirmPw"),
|
|
type: "password",
|
|
required: true,
|
|
});
|
|
const grid = document.createElement("div");
|
|
grid.className = "b01-dashboard__form-grid";
|
|
grid.append(currentPassword.root, newPassword.root, confirmPassword.root);
|
|
const save = createButton({
|
|
label: L("B01_Account_Save_Password"),
|
|
variant: "ghost",
|
|
onClick: async function onB01_Password_Save_Click() {
|
|
currentPassword.setError();
|
|
newPassword.setError();
|
|
confirmPassword.setError();
|
|
|
|
const currentValue = currentPassword.input.value;
|
|
const nextValue = newPassword.input.value;
|
|
const confirmValue = confirmPassword.input.value;
|
|
if (isBlank(currentValue) || isBlank(nextValue) || isBlank(confirmValue)) {
|
|
currentPassword.setError(isBlank(currentValue) ? L("Common_Msg_RequiredField") : undefined);
|
|
newPassword.setError(isBlank(nextValue) ? L("Common_Msg_RequiredField") : undefined);
|
|
confirmPassword.setError(isBlank(confirmValue) ? L("Common_Msg_RequiredField") : undefined);
|
|
return;
|
|
}
|
|
if (nextValue.length < PASSWORD_MIN_LENGTH) {
|
|
newPassword.setError(L("B01_Account_Error_PwLength"));
|
|
return;
|
|
}
|
|
if (nextValue !== confirmValue) {
|
|
confirmPassword.setError(L("B01_Account_Error_PwMismatch"));
|
|
return;
|
|
}
|
|
await runRequest(() =>
|
|
changePassword({
|
|
current_password: currentValue,
|
|
new_password: nextValue,
|
|
new_password_confirm: confirmValue,
|
|
logout_all: false,
|
|
}),
|
|
);
|
|
currentPassword.input.value = "";
|
|
newPassword.input.value = "";
|
|
confirmPassword.input.value = "";
|
|
},
|
|
});
|
|
const wrap = document.createElement("div");
|
|
wrap.append(grid, save);
|
|
return wrap;
|
|
}
|