Merge remote-tracking branch 'origin/sub_laptop_1' into main_laptop_1
This commit is contained in:
@@ -26,6 +26,8 @@ export interface StructureOptionField {
|
||||
default: string | number | null;
|
||||
/** 미확정 항목(기본값 없음) — 사용자가 값을 넣어야 저장된다. */
|
||||
required?: boolean;
|
||||
/** 거짓이면 폼에 **회색으로** 그려지고 못 고른다 — 칸은 남기되 잠그는 자리. */
|
||||
enabled?: boolean;
|
||||
/** 입력 시점 — B05는 유무·종류·위치만 받고 상세 치수(detail)는 B06/B07에서 받는다
|
||||
* (2026-08-17 사용자 확정). detail이면 required여도 B05 폼에 그리지 않는다. */
|
||||
phase?: "b05" | "detail";
|
||||
@@ -129,18 +131,28 @@ async function requestJson<T>(path: string, init: RequestInit = {}): Promise<T>
|
||||
/** 타입 레지스트리는 서버 배포 중에 바뀌지 않으므로 탭 수명 동안 한 번만 받는다. */
|
||||
let typesCache: Promise<StructureType[]> | null = null;
|
||||
|
||||
export function fetchStructureTypes(): Promise<StructureType[]> {
|
||||
/**
|
||||
* 구조물 타입 목록.
|
||||
*
|
||||
* `includeDisabled` 를 주면 `enabled:false` 타입(B군 종단배수·F군 생태/녹화·G군 일부)도
|
||||
* 함께 준다. 레지스트리 주석(2026-08-17)이 「B05 선택지에서 빼고 **B06 개별 횡단도
|
||||
* 옵션으로 재사용**」이라 적어 둔 그 자리다 — 2026-09-07 사용자 지시 「A군뿐 아니라
|
||||
* 구조물 전체를 넣을 수 있어야 함」으로 B06 이 그 목록을 쓴다. B05 는 종전대로 켜진 것만.
|
||||
*/
|
||||
export function fetchStructureTypes(includeDisabled = false): Promise<StructureType[]> {
|
||||
if (!typesCache) {
|
||||
typesCache = requestJson<StructureTypesResponse>("/projects/structure-types", {
|
||||
method: "GET",
|
||||
})
|
||||
.then((payload) => payload.types.filter((type) => type.enabled))
|
||||
.then((payload) => payload.types)
|
||||
.catch((error) => {
|
||||
typesCache = null; // 실패한 약속을 남겨 두면 다시 시도할 수 없다.
|
||||
throw error;
|
||||
});
|
||||
}
|
||||
return typesCache;
|
||||
return typesCache.then((types) =>
|
||||
includeDisabled ? types : types.filter((type) => type.enabled),
|
||||
);
|
||||
}
|
||||
|
||||
export async function fetchStructures(projectId: string): Promise<StructureListResponse> {
|
||||
@@ -174,6 +186,33 @@ export async function migrateLegacyStations(
|
||||
|
||||
/** 종단도 마크 위치 = 기준점. 구간형도 chainage_m이 기준점이다(2026-08-17 사용자
|
||||
* 확정: 기준점에 마킹 + 시작·종료 측점). 기준점이 없는 기존 저장분은 시점으로 본다. */
|
||||
/** 관 지점을 **알약 레인용 가상 구조물**로 만든다 — 정본은 `pipe_points.json` 이라
|
||||
* 저장하지 않는다. B05 종단과 B06 종단이 같은 표기를 쓰도록 한 벌만 둔다
|
||||
* (2026-09-07 사용자 지시 4 「구조물 표시 통일」). */
|
||||
export function pipesToStructureMarks(
|
||||
pipes: ReadonlyArray<{
|
||||
chainage_m: number;
|
||||
facility: string;
|
||||
options?: Record<string, unknown> | null;
|
||||
}>,
|
||||
): StructureInstance[] {
|
||||
return pipes.map((pipe) => ({
|
||||
structure_id: `pipe-${pipe.chainage_m.toFixed(2)}`,
|
||||
type_id: pipe.facility,
|
||||
placement: "point",
|
||||
chainage_m: pipe.chainage_m,
|
||||
start_m: null,
|
||||
end_m: null,
|
||||
options: (pipe.options ?? {}) as Record<string, string | number>,
|
||||
memo: "",
|
||||
placement_source: "automatic",
|
||||
status: "draft",
|
||||
revision: 0,
|
||||
geometry: null,
|
||||
})) as StructureInstance[];
|
||||
}
|
||||
|
||||
|
||||
export function structureAnchorM(structure: StructureInstance): number {
|
||||
return structure.chainage_m ?? structure.start_m ?? 0;
|
||||
}
|
||||
|
||||
@@ -639,6 +639,13 @@
|
||||
"default": null,
|
||||
"required": true,
|
||||
"phase": "detail"
|
||||
},
|
||||
{
|
||||
"key": "side",
|
||||
"label": "설치 측",
|
||||
"input": "select",
|
||||
"choices": ["자동(성토 쪽)", "좌", "우"],
|
||||
"default": "자동(성토 쪽)"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -697,6 +704,13 @@
|
||||
"default": null,
|
||||
"required": true,
|
||||
"phase": "detail"
|
||||
},
|
||||
{
|
||||
"key": "side",
|
||||
"label": "설치 측",
|
||||
"input": "select",
|
||||
"choices": ["자동(성토 쪽)", "좌", "우"],
|
||||
"default": "자동(성토 쪽)"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -755,6 +769,13 @@
|
||||
"default": null,
|
||||
"required": true,
|
||||
"phase": "detail"
|
||||
},
|
||||
{
|
||||
"key": "side",
|
||||
"label": "설치 측",
|
||||
"input": "select",
|
||||
"choices": ["자동(성토 쪽)", "좌", "우"],
|
||||
"default": "자동(성토 쪽)"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -813,6 +834,13 @@
|
||||
"default": null,
|
||||
"required": true,
|
||||
"phase": "detail"
|
||||
},
|
||||
{
|
||||
"key": "side",
|
||||
"label": "설치 측",
|
||||
"input": "select",
|
||||
"choices": ["자동(성토 쪽)", "좌", "우"],
|
||||
"default": "자동(성토 쪽)"
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -871,6 +899,13 @@
|
||||
"default": null,
|
||||
"required": true,
|
||||
"phase": "detail"
|
||||
},
|
||||
{
|
||||
"key": "side",
|
||||
"label": "설치 측",
|
||||
"input": "select",
|
||||
"choices": ["자동(성토 쪽)", "좌", "우"],
|
||||
"default": "자동(성토 쪽)"
|
||||
}
|
||||
]
|
||||
},
|
||||
|
||||
@@ -44,6 +44,10 @@ class StructureOptionField(BaseModel):
|
||||
# (2026-08-17 사용자 확정). `detail`이면 required여도 B05 저장에서 강제하지 않는다
|
||||
# — 필수 원칙은 유지되고 강제 시점만 B06/B07로 미뤄진다.
|
||||
phase: Literal["b05", "detail"] = "b05"
|
||||
# 폼에 칸은 두되 **지금은 못 고르게** 할 때 거짓으로 둔다 — 회색으로 그려지고 값은
|
||||
# 기본값이 그대로 저장된다(2026-09-07 사용자: 「폼 선택은 가능하게 반영하고 나중에
|
||||
# 선택 비활성화로 하자」). 칸 자체를 없애면 나중에 켤 자리를 다시 찾아야 한다.
|
||||
enabled: bool = True
|
||||
|
||||
|
||||
class StructureType(BaseModel):
|
||||
|
||||
@@ -322,11 +322,18 @@ export function createStructuresSection(callbacks: StructuresCallbacks): Structu
|
||||
input.value = String(preset ?? "");
|
||||
}
|
||||
const label = option.unit ? `${option.label} (${option.unit})` : option.label;
|
||||
// `enabled:false` 는 칸을 남기고 잠그기만 한다 — 값은 기본값이 그대로 저장된다.
|
||||
const locked = option.enabled === false;
|
||||
if (locked) {
|
||||
input.disabled = true;
|
||||
input.classList.add("is-locked");
|
||||
input.title = "지금은 고를 수 없는 항목입니다.";
|
||||
}
|
||||
optionRow.append(field(label, input));
|
||||
input.addEventListener("change", () => liveCommit());
|
||||
optionInputs.push({
|
||||
key: option.key,
|
||||
required: !!option.required,
|
||||
required: !locked && !!option.required,
|
||||
input,
|
||||
read: () => (option.input === "number" ? Number(input.value) || 0 : input.value),
|
||||
isEmpty: () => input.value.trim() === "",
|
||||
|
||||
Reference in New Issue
Block a user