Owner directives 2026-07-14: select floorplan via tree (center > hall > zone),
generate booths inside it, images only on button click, zoom popup + save.
- V59: align hall width/depth to official floorplan orientation (all 10 halls
were transposed vs drawings, e.g. H1 63x171 -> 171x63); re-derive assumed
exits (V4 formula) and trench grid (V5 formula)
- V60: hall_zone tree table + drawing-measured zones (1A/1B, 6A/6B/6C, 7A/7B,
8A/8B, 9B/9A, 10B/10A) - data as tree: center > hall > zone
- GET /api/halls/tree (HallTreeController) - selection tree for the wizard
- AutoLayoutRequest.zoneId: pack booths only inside the selected zone bounds
- S7 aerial preview is NO LONGER auto-published per option (Gemini quota);
new POST .../auto-generate/preview publishes only on user button click,
persisted in render_job for gallery re-view and auction reference reuse
- AutoLayoutDialog reworked as 3-step wizard (place > conditions > pick):
per-option generate-aerial-image button, click-to-zoom lightbox with
download/save, and REAL apply via POST /apply-option (was refetch-only -
selected options were never persisted)
- FloorplanCanvas calibration: H8/H10 floor regions re-measured with
union-of-blobs (center aisle split the color blob, bottom half only before)
- Editor toolbar hall selector (grouped by exhibition center)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner directive 2026-07-14: booths must be auto-placed inside the floorplan.
- hallFloorplan.ts: per-hall floor-region calibration [x0,y0,x1,y1] (auto-measured
largest color blob of the exhibition floor in each crawled JPG, halls 1-10)
- FloorplanCanvas: align calibrated floor region to hall rect (0,0-W,H m) so booth
coordinates land inside the drawn floor; raise underlay visibility (0.3 -> 0.55)
- BoothLayoutEditorPage: use real hall dims from LayoutDto.hall (was hardcoded 126x90),
pass floorRegion; types.ts HallInfoDto added in previous commit
- FloorplanServiceImpl.packBooths: honor request conditions as reserved zones -
central cross aisles (>=40m spans), entrance clear zones, stage/lounge blocks
(previously mainEntranceCount/stageCount/loungeCount were ignored)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- non-passive wheel listener zooms 0.3x~4x, synced to page zoom state via
new onZoomChange prop (wired in BoothLayoutEditorPage)
- pointer-capture left-drag pans the canvas; 3px threshold and click-capture
suppression so a drag never mis-fires booth select/deselect
- grab/grabbing cursor, transition off while panning, touch-action none
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner: 'admin은 모든 권한'. requireRole(ORGANIZER)-style endpoints 403'd for
admin because roleFor() returned HALL_MANAGER (hm/ADMIN flag) even when the
user held an explicit ORGANIZER membership.
- EventAccessGuard.requireRole: isAdmin() bypasses the allowed-role set
(tenant boundary still enforced)
- KintexPrincipal.roleFor: explicit event membership role wins; HALL_MANAGER
only as fallback
- HallAssignController: ADMIN allowed alongside hall manager
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner report: '도면이 안나온다 / 도면을 불러오지 못했습니다'.
- Canvas now renders crawled hall drawings (hall1-10.jpg, build-bundled per
V46 poster pattern) as blueprint-tone underlay beneath booth polygons;
hides gracefully on load failure.
- GET layout 404 (no saved layout for the hall) no longer shows a hard error
screen — enters empty canvas with 'AI 자동배치로 시작' CTA instead.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Owner directive: grids must render fully without internal scrollbars
(pagination handles row counts). .kx-aq was the only data-grid shell with
height:100% + overflow:hidden/auto clipping; now page-flow with sticky side.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Worker published only Redis pub/sub events; backend status stayed QUEUED
forever (callback endpoint never called — gap masked by earlier Gemini 429).
- worker.py: POST /api/internal/render/callback (X-Worker-Token) after each
job with imageUrl=/renders/{key}, schemaHash, modelVersion, errorMessage
- backend: serve /renders/** from RENDER_OUTPUT_DIR + security permit
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
All @kintex.test seed addresses bounce now that live send is open.
Replace them with ythong86+<registrationId>@gmail.com so live-send E2E
actually delivers — every address routes to the owner's own inbox; no
third-party real addresses are introduced. Idempotent (post-update the
LIKE predicate matches nothing).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Server EDM_TEST_MODE flipped to false (env, owner request), so the UI
gains an explicit live-send button: existing button stays as safe test
send, live send requires an audience-count warning confirm plus typing
LIVE. sendMut now carries {id, live}; i18n keys x4 locales.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
sentCount tallies async queue submissions, so SMTP failures (e.g. the
2026-07-14 MailAuthenticationException) were invisible in the response.
Meta now says 'N queued, see send history' for both test and live paths.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
History endpoint over render_job (DONE only) with covering index V54;
design studio shows a per-booth history gallery with watermark intact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
openhtmltopdf renderer with bundled NanumGothic for Korean text;
report authoring page gains PDF download wired to the new endpoint.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
CmsService translate path returns per-locale drafts with degraded
fallback; multilingual CMS page gets an AI translate action. Service
test updated.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Consent-filtered recipient resolution, SMTP send with test-mode ON by
default (EDM_TEST_MODE, campaign cap, per-minute rate), opaque
unsubscribe tokens with a public unsubscribe endpoint (no PII in
URLs/logs), and per-campaign send history via V53.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
SearchAiService interprets the query via AiTextRouter and grounds
results on existing search providers; degraded fallback keeps plain
search working when AI is unavailable.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Root cause: flex/grid children default min-width:auto let wide tables
blow out their track instead of scrolling, and .kx-dash__table-wrap
clipped with overflow:hidden. Converge the fix on the canonical
wrappers (.kx-table-scroll, .kx-dt) plus four screens lacking a scroll
wrapper (dashboard, admin matrix, settlement modal/report, hallassign
quote).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Adds a menu search box above the nav (match-highlight, Escape/clear
restores the accordion, track/admin gates preserved). Single-open
accordion with route-driven active group confirmed per owner request.
i18n keys shell.menu.search.* added across ko/en/ja/zh.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Token lives in sessionStorage but user/workspaces existed only in the
zustand store, so any full reload left workspaces=[] and every
event-scoped screen fell to 'select an event' with an empty selector.
RequireAuth now restores /api/auth/me + /api/auth/workspaces once when
authenticated with an empty store, and logs out on 401.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
PostgreSQL cannot infer the type of a bare bind in '(? IS NULL OR col = ?)',
failing GET /api/admin/messages with BadSqlGrammarException. Cast the
null-check occurrence to ::text (findAll category/locale, delete locale).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Unescaped <= at findProducts/findProduct broke SqlSessionFactory XML
parsing, crashing every new jar at startup; deploy rolled back to the
pre-V50 jar, so V50/V51 never reached prod and the admin demo account
lost event membership again (floorplan menu fell back to /home).
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Floorplan/dashboard scoped menu resolves the target event from the
user workspaces; admin had zero event_member rows so clicks silently
landed on /home (perceived broken link). Idempotent V51 enrolls admin
as ORGANIZER of e-2026-live and e-2026-smf.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
POST .../auto-generate/interpret parses NL conditions into a clamped
AutoLayoutRequest via AiTextRouter (Claude, injection-guarded JSON-only
prompt; degraded:true fallback keeps manual form authoritative).
auto-generate?ai=true attaches metric-grounded pros/cons per option
(generation never blocked by AI failure). AutoLayoutDialog gains an NL
input bar + interpreted-note + AI summary cards with WISE AI label.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
auction/visitor/marketing sample data + cms.css headers now state the
real wiring status (live API primary, samples are degraded fallback
only; G-04 SMTP follow-up noted). Mark hygiene item resolved in
GAP_AUDIT.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace hand-rolled zebra tables with KxDataTable (sort/filter/CSV,
per-column control) on HolidayAdminPage, ProgramAdminPage and
RulesetVersionsPage. Manage columns excluded from CSV/sort/search;
empty states delegated to the table.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Replace hardcoded TENANTS array and disabled add button with live
GET/POST /api/admin/tenants via react-query: list states, create modal
(server-authoritative validation mirrored client-side), KxDataTable
search/sort/CSV. tenant.* i18n 17 keys x 4 locales (parity kept).
Mark G-01 resolved in GAP_AUDIT.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Normalize 13 custom page roots to the .kx-page canon (flex:1, min-width:0,
overflow-y:auto) fixing right-side dead space and bottom clipping inside the
shell content flexbox; add missing narrow-width breakpoints to settlement KPIs
and hall-assignment forms. No visual redesign - WISE shell and kx tokens intact.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- PublicShell: track tabs get icons + title tooltips (right-aligned), login link restored at >=768px
- VisitorTrackPage: wayfinding/checkin CTAs point to #guide/#transport//tickets/lookup instead of /login
- accent buttons: enforce --color-on-accent (white text) across approval/docs/home/movein/schedule/work/public
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
KintexPrincipal.hasAccess/roleFor ignored global role_code=ADMIN, so
admin@ users got 403 on all event-scoped endpoints. Add isAdmin() and
treat ADMIN as hall-manager equivalent (all-event read + approve).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- HomePage: wire fetchHolidaySet, is-holiday cell class + holiday name label
- home.css: is-holiday red daynum, kx-home__cal-holiday-name, neutral-100 grid lines
- PublicShell: absorb 3-track switcher into single top header (COEX 1-frame), drop dark topbar
- public.css: remove .kxp-topbar styles, tracktabs bar/menu variants
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Root cause: dark theme remaps --color-white to --dk-surface (a dark color),
so every 'color: var(--color-white)' on a colored background (primary/error/
success buttons, badges, chips, hero CTAs) rendered dark/invisible text in
dark mode (e.g. '등록 신청' blue button showed navy text).
Add always-white --color-on-accent token and repoint 77 text-color usages
across 28 files to it. Background/border usages of --color-white untouched
(they legitimately flip to dark surface). Full-CSS audit applied.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Root cause of persistent Tomcat cat favicon: public/favicon.ico was the
Tomcat default (21630 bytes), shipped by every build; browsers prefer
/favicon.ico over the correct favicon.png. Regenerate favicon.ico from the
KINTEX wordmark PNG (multi-size 16/32/48/64) and bump cache-bust to v3.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- icons.tsx: new IconShieldCheck (stroke/line, 24 viewBox standard)
- OtpChallengePanel: shield badge above 2차 인증 title (was icon-less/bare)
- login.css: .kx-otp__badge circular primary-tinted badge
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>