Compare commits

..

No commits in common. "8719d110f355d2abace32c10460822c590a410c3" and "7b861a4af2692620aba0b5e3f1f47d3a74fa4347" have entirely different histories.

20 changed files with 8 additions and 83 deletions

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 87 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 93 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 90 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 92 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 165 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 97 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 121 KiB

View File

@ -3,9 +3,8 @@
min-width: 0; min-width: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
/* 페이지-영역 스크롤(.kx-page 모델). 고정 100% 높이 셸을 제거해 표가 잘리지 않고 height: 100%;
* 내용 높이만큼 늘어나며, 넘칠 때만 루트가 스크롤(내부 그리드 스크롤 없음). */ min-height: 0;
overflow-y: auto;
} }
.kx-aq__head { .kx-aq__head {
display: flex; display: flex;
@ -100,16 +99,16 @@
.kx-aq__body { .kx-aq__body {
display: flex; display: flex;
flex: 1; flex: 1;
/* overflow:hidden 제거 — 본문이 내용 높이만큼 늘어나고 루트(.kx-aq)가 스크롤. */ min-height: 0;
overflow: hidden;
} }
.kx-aq__main { .kx-aq__main {
flex: 1; flex: 1;
min-width: 0;
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--space-3); gap: var(--space-3);
padding: var(--space-4) var(--space-5); padding: var(--space-4) var(--space-5);
/* overflow:auto 제거 — 표 내부 세로 스크롤 대신 페이지 흐름으로 전체 노출. */ overflow: auto;
} }
.kx-aq__main-head { .kx-aq__main-head {
display: flex; display: flex;
@ -181,17 +180,13 @@
/* side */ /* side */
.kx-aq__side { .kx-aq__side {
width: 320px; width: 320px;
flex: none;
border-left: var(--border-card); border-left: var(--border-card);
background: var(--color-neutral-050); background: var(--color-neutral-050);
padding: var(--space-5); padding: var(--space-5);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
gap: var(--space-4); gap: var(--space-4);
/* 표 높이에 스트레치되지 않고, 페이지 스크롤 시 상단에 고정 유지. */ overflow-y: auto;
align-self: flex-start;
position: sticky;
top: 0;
} }
.kx-aq__card { .kx-aq__card {
background: var(--color-white); background: var(--color-white);

View File

@ -8,8 +8,7 @@ import { Skeleton } from '../../components/ui/States';
import { FloorplanCanvas, type CanvasLayer } from './FloorplanCanvas'; import { FloorplanCanvas, type CanvasLayer } from './FloorplanCanvas';
import { AutoLayoutDialog } from './AutoLayoutDialog'; import { AutoLayoutDialog } from './AutoLayoutDialog';
import { ValidationPanel } from './ValidationPanel'; import { ValidationPanel } from './ValidationPanel';
import { sampleLayout, emptyLayout } from './sampleLayout'; import { sampleLayout } from './sampleLayout';
import { hallFloorplanUrl } from './hallFloorplan';
import type { AutoLayoutOption, ComplianceReport, LayoutDto } from '../../api/types'; import type { AutoLayoutOption, ComplianceReport, LayoutDto } from '../../api/types';
import './editor.css'; import './editor.css';
@ -41,10 +40,6 @@ export function BoothLayoutEditorPage() {
if (err instanceof ApiRequestError && err.code === 'NOT_IMPLEMENTED') { if (err instanceof ApiRequestError && err.code === 'NOT_IMPLEMENTED') {
return { layout: sampleLayout(eventId, hallId), degraded: true }; return { layout: sampleLayout(eventId, hallId), degraded: true };
} }
// 아직 저장된 배치안이 없는 홀(404) — 에러가 아니라 "AI 자동배치로 시작" 빈 캔버스 상태.
if (err instanceof ApiRequestError && err.code === 'NOT_FOUND') {
return { layout: emptyLayout(eventId, hallId), degraded: false };
}
throw err; throw err;
} }
}, },
@ -179,7 +174,6 @@ export function BoothLayoutEditorPage() {
selectedBoothId={selectedBoothId} selectedBoothId={selectedBoothId}
onSelectBooth={setSelectedBoothId} onSelectBooth={setSelectedBoothId}
zoom={zoom} zoom={zoom}
floorplanUrl={hallFloorplanUrl(hallId)}
/> />
) )
)} )}

View File

@ -1,4 +1,4 @@
import { useEffect, useMemo, useState } from 'react'; import { useMemo } from 'react';
import type { BoothDto } from '../../api/types'; import type { BoothDto } from '../../api/types';
import './canvas.css'; import './canvas.css';
@ -22,8 +22,6 @@ interface FloorplanCanvasProps {
selectedBoothId: string | null; selectedBoothId: string | null;
onSelectBooth: (boothId: string | null) => void; onSelectBooth: (boothId: string | null) => void;
zoom: number; zoom: number;
/** 실측 홀 도면(크롤 JPG) 언더레이 URL — 없거나 로드 실패 시 기존 다크 서피스만. */
floorplanUrl?: string | null;
} }
const TRENCH_SPACING_M = 9; // 가정 트렌치 그리드 간격(실측 대체 — R4) const TRENCH_SPACING_M = 9; // 가정 트렌치 그리드 간격(실측 대체 — R4)
@ -47,11 +45,8 @@ export function FloorplanCanvas({
selectedBoothId, selectedBoothId,
onSelectBooth, onSelectBooth,
zoom, zoom,
floorplanUrl,
}: FloorplanCanvasProps) { }: FloorplanCanvasProps) {
const [hw, hh] = hallDims; const [hw, hh] = hallDims;
const [drawingOk, setDrawingOk] = useState(true);
useEffect(() => setDrawingOk(true), [floorplanUrl]);
const trenchLines = useMemo(() => { const trenchLines = useMemo(() => {
const lines: { x1: number; y1: number; x2: number; y2: number }[] = []; const lines: { x1: number; y1: number; x2: number; y2: number }[] = [];
@ -83,20 +78,6 @@ export function FloorplanCanvas({
rx={0.5} rx={0.5}
/> />
{/* 실측 홀 도면 언더레이(크롤 JPG) — 부스/트렌치 아래, 클릭 통과 */}
{floorplanUrl && drawingOk && (
<image
href={floorplanUrl}
x={0}
y={0}
width={hw}
height={hh}
preserveAspectRatio="none"
className="kx-canvas__drawing"
onError={() => setDrawingOk(false)}
/>
)}
{/* 트렌치 그리드 */} {/* 트렌치 그리드 */}
{layers.trench && ( {layers.trench && (
<g className="kx-canvas__trench"> <g className="kx-canvas__trench">

View File

@ -17,13 +17,6 @@
stroke-width: 0.6; stroke-width: 0.6;
} }
/* 실측 홀 도면(JPG) 언더레이 — 다크 서피스 위 블루프린트 톤, 상호작용 통과 */
.kx-canvas__drawing {
opacity: 0.3;
filter: saturate(0.35);
pointer-events: none;
}
.kx-canvas__trench line { .kx-canvas__trench line {
stroke: var(--color-canvas-line); stroke: var(--color-canvas-line);
stroke-width: 0.15; stroke-width: 0.15;

View File

@ -1,13 +0,0 @@
/*
* ID ( JPG) URL .
* docs/assets/floorplans public/media/floorplans (V46 ).
* null (FloorplanCanvas onError ).
*/
const AVAILABLE_HALLS = new Set([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
export function hallFloorplanUrl(hallId: string | null | undefined): string | null {
const m = /^H(\d+)$/i.exec((hallId ?? '').trim());
if (!m) return null;
const n = Number(m[1]);
return AVAILABLE_HALLS.has(n) ? `/media/floorplans/hall${n}.jpg` : null;
}

View File

@ -5,31 +5,6 @@ import type { BoothDto, LayoutDto } from '../../api/types';
* . shape과 API . * . shape과 API .
* 126×90m . 3×3m/6×3m . * 126×90m . 3×3m/6×3m .
*/ */
/**
* (GET 404) "AI 자동배치로 시작" .
* (2026-07-14 "도면을 불러오지 못했습니다" 404 .)
*/
export function emptyLayout(eventId: string, hallId: string): LayoutDto {
return {
layoutId: `new-${eventId}-${hallId}`,
eventId,
hallId,
version: 0,
name: '새 배치안',
status: 'draft',
booths: [],
summary: {
boothCount: 0,
targetBoothCount: 0,
salesAreaM2: 0,
minAisleWidthM: 0,
violationBlock: 0,
violationWarn: 0,
},
updatedAt: new Date().toISOString(),
};
}
export function sampleLayout(eventId: string, hallId: string): LayoutDto { export function sampleLayout(eventId: string, hallId: string): LayoutDto {
const booths: BoothDto[] = []; const booths: BoothDto[] = [];
const cols = 12; const cols = 12;