211 lines
7.1 KiB
TypeScript
211 lines
7.1 KiB
TypeScript
/**
|
|
* 화면명: 하수관로 편집 세션
|
|
* 현재 편집 대상과 변경 상태를 표시하고 저장·취소 동작을 제공한다.
|
|
*/
|
|
"use client";
|
|
|
|
import { Button } from "@common/components";
|
|
|
|
import type { GisEditSessionSnapshot, GisEditTool } from "./gisEditTypes";
|
|
import { useGisEditOperationLabels } from "./useGisEditCommonCodes";
|
|
|
|
type Props = GisEditSessionSnapshot & {
|
|
onRemoveFocusedChange: () => void;
|
|
onRemoveLastChange: () => void;
|
|
onClearChanges: () => void;
|
|
onFocusChange: (changeId: string) => void;
|
|
onOpenPopup?: () => void;
|
|
variant?: "inline" | "popup";
|
|
className?: string;
|
|
};
|
|
|
|
const buildChangeItemClassName = (isFocused: boolean) =>
|
|
`block w-full rounded-md border px-3 py-2.5 text-left text-[1.1rem] transition ${
|
|
isFocused
|
|
? "border-primary bg-primary-transparent shadow-[inset_0_0_0_1px_var(--primary-transparent)]"
|
|
: "border-border bg-background hover:border-primary hover:bg-background"
|
|
}`;
|
|
|
|
const getChangeLabel = (
|
|
change: GisEditSessionSnapshot["changes"][number],
|
|
operationLabels: Record<GisEditTool, string>,
|
|
) =>
|
|
change.attributeOnly
|
|
? "상세정보수정"
|
|
: operationLabels[change.editType];
|
|
|
|
const getChangeSummary = (
|
|
change: GisEditSessionSnapshot["changes"][number],
|
|
) => {
|
|
if (change.attributeOnly) return "변경한 상세정보를 확인하세요.";
|
|
if (change.editType === "ADD") return "추가한 객체 위치를 확인하세요.";
|
|
if (change.editType === "MOVE") return "객체를 드래그해 위치를 조정하세요.";
|
|
if (change.editType === "VERTEX_EDIT")
|
|
return "점을 옮기거나 선분을 끌어 추가하세요.";
|
|
return "삭제할 객체가 맞는지 확인하세요.";
|
|
};
|
|
|
|
type ChangeListProps = {
|
|
changes: GisEditSessionSnapshot["changes"];
|
|
focusedChangeId: string | null;
|
|
operationLabels: Record<GisEditTool, string>;
|
|
onFocusChange: (changeId: string) => void;
|
|
};
|
|
|
|
const ChangeList = ({
|
|
changes,
|
|
focusedChangeId,
|
|
operationLabels,
|
|
onFocusChange,
|
|
}: ChangeListProps) => (
|
|
<>
|
|
{changes.map((change, index) => (
|
|
<button
|
|
key={change.changeId}
|
|
type="button"
|
|
className={buildChangeItemClassName(
|
|
focusedChangeId === change.changeId,
|
|
)}
|
|
onClick={() => onFocusChange(change.changeId)}
|
|
>
|
|
<div className="flex items-center gap-1">
|
|
<div className="shrink-0 whitespace-nowrap text-[1.1rem] font-semibold leading-4 text-label">
|
|
{index + 1}. {getChangeLabel(change, operationLabels)} /{" "}
|
|
{change.objectLabel}
|
|
</div>
|
|
<div className="ml-auto flex shrink-0 items-center gap-1">
|
|
{change.basicInfoSavedAt && focusedChangeId !== change.changeId ? (
|
|
<span className="whitespace-nowrap rounded-full border border-emerald-200 bg-background px-1.5 py-0.5 text-[0.9rem] font-semibold text-emerald-700">
|
|
정보저장
|
|
</span>
|
|
) : null}
|
|
{focusedChangeId === change.changeId ? (
|
|
<span className="whitespace-nowrap rounded-full border border-primary bg-background px-1.5 py-0.5 text-[0.9rem] font-semibold text-primary">
|
|
선택됨
|
|
</span>
|
|
) : null}
|
|
</div>
|
|
</div>
|
|
<div className="mt-1 truncate text-[1rem] text-muted-foreground">
|
|
{getChangeSummary(change)}
|
|
</div>
|
|
</button>
|
|
))}
|
|
</>
|
|
);
|
|
|
|
export default function GisEditSessionPanel({
|
|
isEditMode,
|
|
selectedLayerName,
|
|
activeTool,
|
|
changes,
|
|
canUndo,
|
|
focusedChangeId,
|
|
onRemoveFocusedChange,
|
|
onRemoveLastChange,
|
|
onClearChanges,
|
|
onFocusChange,
|
|
onOpenPopup,
|
|
variant = "inline",
|
|
className = "",
|
|
}: Props) {
|
|
// 인라인·윈도우 팝업 세션 모두 동일한 CMS 공통코드 라벨을 공유한다.
|
|
const operationLabels = useGisEditOperationLabels();
|
|
const isCompactInline = variant === "inline" && changes.length === 0;
|
|
|
|
return (
|
|
<div
|
|
className={`flex min-h-0 flex-col overflow-hidden rounded-lg border border-border bg-[rgba(255,255,255,0.98)] text-label shadow-[0_10px_26px_rgba(23,42,70,0.16)] ${isCompactInline ? "min-h-[68px]" : "p-3"} ${className}`.trim()}
|
|
>
|
|
<div
|
|
className={`flex shrink-0 justify-between gap-2 ${isCompactInline ? "items-center bg-container px-3 py-2.5" : "mb-3 items-start rounded-md bg-container px-3 py-2.5"}`}
|
|
>
|
|
<div className="min-w-0">
|
|
{variant === "inline" ? (
|
|
<div className="mb-1 flex items-center gap-2 text-[1.3rem] font-semibold text-label">
|
|
<span>편집 세션</span>
|
|
<span className="rounded-full bg-primary px-2 py-0.5 text-[0.9rem] font-semibold text-primary-foreground">
|
|
{changes.length}건
|
|
</span>
|
|
</div>
|
|
) : null}
|
|
<div className="truncate text-[1.05rem] text-muted-foreground">
|
|
{selectedLayerName || "레이어 미선택"} ·{" "}
|
|
{isEditMode
|
|
? activeTool
|
|
? operationLabels[activeTool]
|
|
: "대기"
|
|
: "편집 모드 종료"}
|
|
</div>
|
|
</div>
|
|
{onOpenPopup ? (
|
|
<Button
|
|
type="button"
|
|
color="white"
|
|
onClick={onOpenPopup}
|
|
title="편집 세션을 기존 윈도우 팝업으로 열기"
|
|
>
|
|
<span aria-hidden="true" className="text-[1.1rem] leading-none">
|
|
↗
|
|
</span>
|
|
<span>팝업</span>
|
|
</Button>
|
|
) : null}
|
|
</div>
|
|
|
|
<div
|
|
className={`${isCompactInline ? "hidden" : "mb-3 flex"} shrink-0 flex-wrap items-center gap-2 rounded-md border border-border bg-background p-2`}
|
|
>
|
|
<Button
|
|
type="button"
|
|
color="white"
|
|
disabled={!focusedChangeId}
|
|
onClick={onRemoveFocusedChange}
|
|
>
|
|
선택 취소
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
color="white"
|
|
disabled={!canUndo}
|
|
onClick={onRemoveLastChange}
|
|
>
|
|
최근 취소
|
|
</Button>
|
|
<Button
|
|
type="button"
|
|
color="white"
|
|
disabled={!canUndo}
|
|
onClick={onClearChanges}
|
|
>
|
|
전체 초기화
|
|
</Button>
|
|
<div className="ml-auto rounded-full border border-border bg-background px-2.5 py-1 text-[1rem] font-semibold text-muted-foreground">
|
|
총 {changes.length}건
|
|
</div>
|
|
</div>
|
|
|
|
<div
|
|
className={`${isCompactInline ? "hidden" : "min-h-0 flex-1"} overflow-hidden`}
|
|
>
|
|
{changes.length === 0 ? (
|
|
<div className="rounded-md border border-dashed border-border bg-background px-3 py-5 text-center text-[1.1rem] text-muted-foreground">
|
|
저장할 편집 변경분이 없습니다.
|
|
</div>
|
|
) : (
|
|
<div className="h-full overflow-y-auto pr-1">
|
|
<div className="space-y-2">
|
|
<ChangeList
|
|
changes={changes}
|
|
focusedChangeId={focusedChangeId}
|
|
operationLabels={operationLabels}
|
|
onFocusChange={onFocusChange}
|
|
/>
|
|
</div>
|
|
</div>
|
|
)}
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|