- [+] 세 자리(시스템관리자 · 회사관리자 · 사용자) = 고르기 모달 - 그림 = SVG 직접(선 = 글자 색 · 밝음/어두움) · 산림청 아니면 예정노선 흐림 + 까닭 툴팁 · 누름 = 같은 알림 - 사방 = 준비 중 · 임도 = B02 · 예정노선 = D01 · 안정성평가 = E01 - 세션 is_forest_service 칸(SessionUser) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
90 lines
4.1 KiB
TypeScript
90 lines
4.1 KiB
TypeScript
/* =============================================================================
|
|
* B01_Dashboard_UI_NewProject.ts
|
|
* 대시보드 [+] 고르기 모달(요구 345 · PLAN 528-2) — 카드 넷(그림 + 제목) = 예정노선 · 임도설계 ·
|
|
* 사방설계 · 안정성평가. 그림 = SVG 직접(선 색 = 글자 색 → 밝음 · 어두움 그대로).
|
|
* 예정노선 = 산림청(세션 is_forest_service)만 · 아니면 흐림 + 까닭 툴팁(누름 = 같은 까닭 알림)
|
|
* 사방설계 = 「준비 중」 · 임도설계 = B02 · 예정노선 = D01 · 안정성평가 = E01
|
|
* ========================================================================== */
|
|
|
|
import "./B01_Dashboard_UI_NewProject.css";
|
|
import { ROUTES, type RoutePath } from "@config/config_frontend";
|
|
import { el, showToast } from "@ui/ui_template_elements";
|
|
import { t, type LocaleKey } from "@ui/ui_template_locale";
|
|
import { openModal } from "@ui/ui_template_modal";
|
|
import { fetchSessionUser } from "../A06_Login/A06_Login_Api_Fetch";
|
|
import { navigateTo } from "../A00_Common/router";
|
|
|
|
/** 48 칸 그림 — 선 · 면은 currentColor */
|
|
const ICONS = {
|
|
plan: `<path d="M6 12l12-5 12 5 12-5v29l-12 5-12-5-12 5z"/><path d="M18 7v29M30 12v29"/><path d="M11 33c5-3 8-12 14-12s7 6 12-4" stroke-dasharray="3 3"/><circle cx="11" cy="33" r="2.5" fill="currentColor"/><circle cx="37" cy="17" r="2.5" fill="currentColor"/>`,
|
|
road: `<path d="M8 42c6-10 18-6 20-16S20 12 26 6"/><path d="M20 42c6-10 18-6 20-16S32 12 38 6"/><path d="M14 42c6-10 18-6 20-16S26 12 32 6" stroke-dasharray="3 4"/>`,
|
|
erosion: `<path d="M4 14c6 6 10 12 10 20M44 14c-6 6-10 12-10 20"/><path d="M12 26h24v8H12z"/><path d="M16 26v8M22 26v8M28 26v8M32 26v8"/><path d="M14 40c4-2 6 2 10 0s6 2 10 0" />`,
|
|
stability: `<path d="M4 40h40"/><path d="M4 40V30l12-2 14-16h14v28"/><path d="M10 29a16 16 0 0 0 28-17" stroke-dasharray="3 3"/><path d="M36 14l3-3 1 4" />`,
|
|
} as const;
|
|
|
|
interface CardSpec {
|
|
icon: keyof typeof ICONS;
|
|
title: LocaleKey;
|
|
route?: RoutePath;
|
|
/** 못 고르는 까닭(툴팁 · 누름 알림) — 없으면 고를 수 있음 */
|
|
reason?: LocaleKey;
|
|
soon?: boolean;
|
|
}
|
|
|
|
function svgIcon(kind: keyof typeof ICONS): HTMLElement {
|
|
const box = el("span", { className: "b01-new-project__icon", attrs: { "aria-hidden": "true" } });
|
|
box.innerHTML = `<svg viewBox="0 0 48 48" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">${ICONS[kind]}</svg>`;
|
|
return box;
|
|
}
|
|
|
|
function card(spec: CardSpec, close: () => void): HTMLButtonElement {
|
|
const button = el("button", {
|
|
className: "b01-new-project__card",
|
|
attrs: { type: "button" },
|
|
children: [svgIcon(spec.icon), el("span", { text: t(spec.title) })],
|
|
});
|
|
button.dataset.newProject = spec.icon;
|
|
if (spec.soon) {
|
|
button.classList.add("is-soon");
|
|
button.append(el("span", { className: "b01-new-project__badge", text: t("DE_New_Soon") }));
|
|
}
|
|
if (spec.reason) {
|
|
button.classList.add("is-dim");
|
|
button.title = t(spec.reason);
|
|
}
|
|
button.addEventListener("click", () => {
|
|
if (spec.reason || spec.soon) return void showToast(t(spec.reason ?? "DE_New_Soon"));
|
|
close();
|
|
navigateTo(spec.route!);
|
|
});
|
|
return button;
|
|
}
|
|
|
|
/** [+] — 세션의 산림청 판정으로 예정노선 카드 켬 · 흐림 */
|
|
export async function openNewProjectPicker(): Promise<void> {
|
|
const forest = Boolean((await fetchSessionUser())?.is_forest_service);
|
|
const specs: CardSpec[] = [
|
|
{
|
|
icon: "plan",
|
|
title: "DE_New_Plan",
|
|
route: ROUTES.D01_PLAN_REGISTER,
|
|
reason: forest ? undefined : "DE_New_PlanForestOnly",
|
|
},
|
|
{ icon: "road", title: "DE_New_Road", route: ROUTES.B02_PROJ_REGISTER },
|
|
{ icon: "erosion", title: "DE_New_Erosion", soon: true },
|
|
{ icon: "stability", title: "DE_New_Stability", route: ROUTES.E01_STAB_REGISTER },
|
|
];
|
|
openModal({
|
|
title: t("DE_New_Title"),
|
|
closeLabel: t("Common_Btn_Close"),
|
|
dialogClass: "b01-new-project",
|
|
mount: (body, close) =>
|
|
body.append(
|
|
el("div", {
|
|
className: "b01-new-project__grid",
|
|
children: specs.map((spec) => card(spec, close)),
|
|
}),
|
|
),
|
|
});
|
|
}
|