Compare commits

...

2 Commits

Author SHA1 Message Date
zio
4cee6c1f66 feat(frontend): 신규 5화면 SCR-13~17 — 경영분석·현장운영·전시일정·관리자·스타일가이드
- design.md v1.3: SCR-13~17 매핑·상세 스펙 순증 (기존 SCR-01~12 무수정)
- React 이식 5화면 + Recharts 도입 + chartColors(§1 토큰)·shared.css 공통 프리미티브
- App.tsx 라우트 5종 + AppShell NavLink 배선 (신규 4항목, /_styleguide 네비 비노출)
- QA: tsc 위양성 교정(tsconfig.app 기준)·미사용 import 제거 → tsc/vite build EXIT 0
- reviewer: 배포 가능(Critical/Major 0) — Minor 티켓 BACKLOG 등재, R15-01 홀 필터 배선 즉시 수정
- docs: UNDEVELOPED_BACKLOG.md 신설(세션 미개발 통합) + WORK_STATUS 갱신
- stitch: 재취득 4화면 + component_guide 2종 + _icons 7종

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-11 21:56:28 +09:00
zio
58556ee1da docs: WORK_STATUS.md — 작업 인수인계·요구사항 로그·현황·남은 큐·함정 (다른 PC 이어작업용)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 21:02:18 +09:00
44 changed files with 4698 additions and 106 deletions

View File

@ -16,3 +16,14 @@
| B-10 | Minor | 홀매니저(SCR-10/11)·현장 모바일(SCR-M1/M2) 화면에 Phase 2/3 라벨 부착, 조명 배치안 제안(M4a) 전용 UI 검토 | designer | open | | B-10 | Minor | 홀매니저(SCR-10/11)·현장 모바일(SCR-M1/M2) 화면에 Phase 2/3 라벨 부착, 조명 배치안 제안(M4a) 전용 UI 검토 | designer | open |
| B-11 | Major | ReRoomAI 소스 분석(docs/analysis/reroomai-source.md) — 소스 폴더 연결 대기 중. 완료 후 PLANNING §6 ReRoomAI 연계 절 보강 | planner/visualizer | **done** (v1.1) — 소스 분석 완료·PLANNING §6-4/6-5 확정(모델·SDK·구조화 사전·보존/교체 프롬프트 템플릿·RenderJob 방어·UX), §10 R11 해소·R12 추가 | | B-11 | Major | ReRoomAI 소스 분석(docs/analysis/reroomai-source.md) — 소스 폴더 연결 대기 중. 완료 후 PLANNING §6 ReRoomAI 연계 절 보강 | planner/visualizer | **done** (v1.1) — 소스 분석 완료·PLANNING §6-4/6-5 확정(모델·SDK·구조화 사전·보존/교체 프롬프트 템플릿·RenderJob 방어·UX), §10 R11 해소·R12 추가 |
| B-12 | Minor | client.py에 동일 시드/일관성 파라미터(PLANNING §6-3) 지원 검토 (Gemini API 시드 지원 범위 확인 필요) | visualizer | **done** (v1.2) — `render_shot(..., seed=)` 지원. `GenerateContentConfig(seed=)`로 전달 시도하되 SDK/모델 미지원(TypeError)이면 자동으로 참조체인 기반 일관성(첫 컷을 다음 샷 reference_image로 재사용)으로 폴백. SKILL.md에 명시. ★실호출 검증은 소유자 승인 후 | | B-12 | Minor | client.py에 동일 시드/일관성 파라미터(PLANNING §6-3) 지원 검토 (Gemini API 시드 지원 범위 확인 필요) | visualizer | **done** (v1.2) — `render_shot(..., seed=)` 지원. `GenerateContentConfig(seed=)`로 전달 시도하되 SDK/모델 미지원(TypeError)이면 자동으로 참조체인 기반 일관성(첫 컷을 다음 샷 reference_image로 재사용)으로 폴백. SKILL.md에 명시. ★실호출 검증은 소유자 승인 후 |
## 신규 5화면(SCR-13~17) reviewer 검증 티켓 (2026-07-11 · 배포 가능 판정, 전부 Minor)
| ID | 심각도 | 내용 | 담당 | 상태 |
|---|---|---|---|---|
| R13-01 | Minor | SCR-13 추세 차트 "AI 예측 점선 구간"이 스펙·힌트 문구엔 있으나 AreaChart가 점선 예측 구간을 미렌더 — 힌트가 없는 요소를 지칭(오도 소지). 점선 렌더 구현 또는 힌트 정정 | developer(designer 확인) | open |
| R15-01 | Minor | SCR-15 홀 셀렉트가 필터 로직에 미배선(선택 무효과) | developer | **done** (2026-07-11) — `matchesHall()` 배선("홀" 뒤 번호만 파싱, "제N전시장" N 오매칭 방지), tsc 재통과 |
| R15-02 | Minor | SCR-15 실 워크스페이스 모드에서 category를 'exhibition' 하드코딩 — 샘플/기본값 시각 표기 없음(estVisitors는 "집계 대기" 표기됨) | developer | open |
| R17-01 | Minor | SCR-17 스타일가이드에 "시스템 아이콘 라이브러리" 섹션 누락(스펙 9항목 중 1) — 아이콘 세트 교체(UNDEVELOPED_BACKLOG §1)와 함께 처리 권장 | developer(designer) | open |
| R17-02 | Minor | `chartColors.ts``CHART.warning` 키가 §1-2 warning(#B45309)이 아닌 §1-4 violation-warn(#F79009) 값 — 값은 토큰 정합, 키 이름만 오용 위험 → `violationWarn` 등으로 개명 권장 | developer | open |
| R00-01 | 정보 | AppShell 단일 셸에 전 역할 네비 무차별 노출 — 역할별 포털+MDI 분리는 미착수 대형 트랙으로 기추적(UNDEVELOPED_BACKLOG §1·§4) | FE·DES | tracked |

View File

@ -0,0 +1,79 @@
# 미개발 백로그 — 세션 스캔 통합 (UNDEVELOPED_BACKLOG)
> 2026-07-11 세션 대화 전체 스캔 결과. "무엇이 아직 개발 안 됐는가"를 단일 문서로 통합.
> 상위 문서: `WORK_STATUS.md`(인수인계) · `IMPLEMENTATION_BACKLOG.md`(Phase 구조) · `FEATURE_BACKLOG_100.md`(100대 기능) · `BACKLOG.md`(reviewer 티켓).
> 상태: ⬜ 미착수 · 🔶 부분(샘플/스텁) · 🔷 진행 중 · ⏸ 게이트 대기
---
## 1. 신규 5화면(SCR-13~17) 트랙 — 이번 세션 파생
구현 자체는 완료(프론트 16파일 생성·tsc 통과, QA 진행 중). 아래는 **화면은 있으나 데이터/백엔드가 미개발**인 잔여.
| 상태 | 항목 | 내용 | 담당 | 편입 Phase |
|---|---|---|---|---|
| 🔶 | SCR-13 BI 실데이터 전환 | BI 데이터마트·KpiSnapshot 파이프라인 미구축 → 화면 전체 샘플. 집계 API(매출·RevPAD·P&L·리텐션·예측) 신설 필요 | DA·BI·BE | D-M16 |
| 🔶 | SCR-14 현장운영 실전환 | 혼잡=M10 체크인 파생, 전력=M4 부하 집계로 실전환 경로 확보. CCTV·HVAC/전력계·iparking·조명 IoT는 **하드웨어 계약 필요 — 연동 코드 착수 금지** | BE·FE | E-M14 |
| 🔶 | SCR-15 일정 실전환 | 행사 목록만 실(인증 workspaces). WorkspaceDto에 category·estVisitors 필드 추가 + 홀 가동률 집계 API + 캘린더 점유 실데이터 | BE·FE | D-M1/6 |
| 🔶 | SCR-16 관리자 집계 실전환 | 사용자수·활성행사수 등 실 가능 지표 API 연결. 실시간 방문객·주차·라이브는 M10/M14 이후 | ADM·BE | D-M18 |
| ⬜ | M18 관리자 본체 화면 | `/admin/{users,roles,codes,menus,audit,settings,masterdata/*}` 7종 — 랜딩(SCR-16)만 존재, 본체 CRUD 화면 미구현 (백엔드 B-2 시스템관리 API는 라이브) | ADM·FE | D-M18 |
| ⬜ | SCR-16 플로팅 AI 봇 | design.md상 P2·비노출 기본 — 미구현(주석만) | AI·FE | P2 |
| ⬜ | AppShell 기존 네비 배선 | 대시보드·플로어플랜·참가업체·서류·정산 5항목 여전히 `href="#"` — 행사 스코프 라우트라 워크스페이스 컨텍스트 선택 UX 필요(신규 4항목만 배선됨) | FE·DES | C-C |
| ⬜ | `/_styleguide` 노출 방침 | 내부 전용 라우트로만 구분 — 프로덕션 빌드 제외(env 분기 등) 방침 미확정 | FE·TA | E-DEP |
| ⬜ | Stitch 아이콘 세트 교체 | 이모지/글리프 → `stitch_kintex_ai_system_architect/_icons/` 정식 아이콘 전면 교체 | FE·DES | C-C |
| ⬜ | Stitch 구버전 폴더 정리 | `component_guide_kintex_ai_system/`(구버전, updated로 대체됨) 정리 — **삭제는 사용자 확인 후** | — | — |
## 2. 백그라운드 산출물 — 커밋 대기·미완 검증 필요
| 상태 | 항목 | 내용 | 비고 |
|---|---|---|---|
| 🔷 | 모바일 앱(Expo) `mobile/` | 스캐폴드 생성됨 — 미커밋. 빌드/실행 검증·기능 범위 확정 미완 | node_modules gitignore 확인 |
| 🔶 | 산출물 `docs/deliverables/` | 사업수행계획서.docx + `_gen/` 스크립트만 확인됨. **지침서 3종(사용자·운영자·개발자) PPT·프로그램 사양서+순서도 PPT 폴더 부재 → 재실행 필요**. 자동갱신 스크립트 검증 미완 | `gen_xlsx.py.tmp.*` 잔재 정리 |
| ⬜ | 잡파일 정리 | `hs_err_pid*.log`·`replay_pid*.log`(JVM 크래시 잔재)·`_gen/*.tmp.*` — 삭제는 사용자 확인 후 | 루트 오염 |
| ⬜ | `ci/`(CI 로고 자산) 커밋 여부 | 미추적 상태 — 번들 참조 여부 확인 후 커밋/ignore 결정 | |
## 3. 전 화면 공통 NFR — 미적용 (WORK_STATUS §6 이월)
| 상태 | 항목 | 내용 |
|---|---|---|
| 🔶 | 라이트/다크 테마 | 토큰 변수는 준비 — 전 화면 다크 검증·토글 UI 미구현 |
| ⬜ | 다국어(i18n) | 한/영/중/일 — i18n 프레임워크 자체 미도입(문구 하드코딩) |
| 🔶 | 웹접근성 | 신규 화면은 aria/role 반영 — 기존 화면 전면 감사·WCAG AA 준수 검증 미완 |
| 🔶 | 반응형 풀스크린 | 데스크톱 1440 기준 — 태블릿/모바일 브레이크포인트 전면 적용 미완 |
| ⬜ | 시큐어코딩 점검 | 전면 감사(입력 검증·XSS·CSRF) 미실행 |
## 4. 아키텍처·플랫폼 대형 트랙 — 미착수
| 상태 | 항목 | 내용 | 편입 |
|---|---|---|---|
| ⬜ | 멀티테넌시 구현 | PLANNING v3.0 기획만 완료 — DA 데이터모델 → tenant_id 스키마 → 백엔드 테넌트 컨텍스트 → 프론트 테넌트 스위치 | DA·DB·BE |
| ⬜ | MDI 셸 전환 | design.md §2.7 다중문서 인터페이스 — 현 AppShell은 단일 문서 | FE·DES |
| ⬜ | 100대 기능 갭 | FEATURE_BACKLOG_100 기준 신규 19 + 부분 18 — 관람객·네트워킹·참가업체 서비스 집중, orchestrator Phase 편입 필요 | 전체 |
## 5. 도메인 모듈(Phase D/E) — 미착수
| 상태 | 모듈 | 내용 |
|---|---|---|
| ⬜ | M15 공사/장치 옥션 | AI자료 열람→견적서 제출→역경매→낙찰·계약. 등록업체만 응찰 |
| ⬜ | M10 관람객·현장 | 등록·티켓(PG)·배지/QR·체크인·리드캡처 — SCR-14/16 실데이터의 선행 |
| ⬜ | M12/M17 CMS·공개사이트 | 헤드리스 CMS·마이크로사이트·일반 대중 공개 홍보 사이트(SEO·다국어) |
| ⬜ | M16 BI 백엔드 | 데이터마트·집계·수요예측 — SCR-13 프론트는 완료(샘플) |
| ⬜ | M18 관리자 본체 | §1 참조 — 마스터데이터(홀·요율·룰셋·등록업체) CRUD 포함 |
| ⬜ | M1/6/7/9 | 홀 배정·자동견적(요율 룰)·마일스톤/서식·매칭·정산/결제 |
| ⬜ | M11 비즈매칭 · M13 wayfinding | P2 |
## 6. 잔여 reviewer 티켓 (BACKLOG.md open)
B-01(조립부스 옵션 UI)·B-04(화면 수 표기)·B-05(정산 메뉴 IA)·B-06(이메일 인증 프롬프트)·B-07(SCR-03 공유·핀 불일치)·B-08(SCR-04 산술 오류)·B-10(Phase 라벨·조명 UI) — 전부 designer 담당.
## 7. 게이트·인프라 후속
| 상태 | 항목 | 내용 |
|---|---|---|
| ⏸ | 운영 도메인 | `kintex.wise.ai.kr` 전환(현 개발 kintex.zioinfo.co.kr) — 소유자 확정 후 |
| 🔷 | 이번 트랙 마감 | QA(진행 중) → reviewer 정합 검증 → 커밋·push(자동배포) → WORK_STATUS 갱신 |
## 변경 이력
| 일자 | 갱신 |
|---|---|
| 2026-07-11 | 최초 작성 — 세션 대화 전체 스캔(신규 5화면 파생 잔여·커밋 대기·NFR·대형 트랙·도메인·티켓·게이트) |

87
docs/WORK_STATUS.md Normal file
View File

@ -0,0 +1,87 @@
# 킨텍스 자동전시시스템 — 작업 현황·인수인계 (WORK_STATUS)
> **목적**: 다른 PC/세션에서 작업을 이어가기 위한 단일 인수인계 문서. **수시 업데이트**(기능 추가·변경·트랙 완료 시 갱신 후 커밋).
> 최종 갱신: 2026-07-11 · 리포 `git.zioinfo.co.kr/zio/kintex`(main) · 시크릿(비밀번호·키·서버IP)은 본 문서에 **미기재**(env·GUARDiA 공용 참조).
---
## 0. 빠른 시작 (다른 PC에서 이어받기)
1. `git clone` `zio/kintex` → main.
2. 핵심 문서: `docs/PLANNING.md`(v3.0 기획)·`docs/design.md`(v1.2 화면·MDI)·`docs/IMPLEMENTATION_BACKLOG.md`·`docs/FEATURE_BACKLOG_100.md`·`docs/architecture/*`·`docs/GUARDIA_ALIGNMENT.md`·`docs/SECURITY.md`·`docs/ENV_SETUP.md`·`docs/OPS_RUNBOOK.md`·`CLAUDE.md`.
3. 하네스: `.claude/`(에이전트 ~20 + `kintex-impl-orchestrator` 스킬). 구현은 오케스트레이터가 조율.
4. 이 문서(WORK_STATUS.md)로 "무엇이 됐고/도는 중/남았는지" 파악.
## 1. 프로젝트 개요
킨텍스 **자동전시시스템**(다중 전시관 멀티테넌트 SaaS, KINTEX=테넌트#1, COEX 등 온보딩). 전시 생애주기(판매→AI 설계·시각화→공사 옥션→시공→운영→경영분석) 폐루프.
## 2. 스택·인프라 (시크릿 제외)
- 프론트 React 18/19(Vite·TS) / 백엔드 Spring Boot 3.2.5(Java17)+MyBatis / DB PostgreSQL+PostGIS(Flyway) / Redis / 나노바나나 Python 워커(google-genai `gemini-3.1-flash-image-preview`).
- AI 지능 = **Claude 기본**(AiTextRouter/AiConfig) + Ollama 폴백. 이미지 = 나노바나나(Gemini, 소유자 승인=G1 라이브).
- 개발 배포: **https://kintex.zioinfo.co.kr**(TLS·백엔드 8021·PostGIS·Redis·워커 systemd). 운영 도메인 kintex.wise.ai.kr(후속). 서버 접속 자격증명은 **GUARDiA 공용**(env·리포 미기재).
- CI/CD: Gitea webhook → `deploy_kintex.sh`(백엔드 `sh gradlew bootJar` + 프론트 vite 빌드 → 배포 → health → 롤백). push하면 자동배포.
## 3. 요구사항 로그 (이번 세션 누적 — 스코프 진화)
1. Gitea `kintex` 리포 생성 + 로컬 소스 push.
2. kintex.com 전 페이지 분석 + ReRoomAI 소스 분석 → AI 전시시스템 하네스 생성(부스 배치·인테리어·전기·조명·네트워크 배선 자동화 → 나노바나나 시공 후 사진).
3. 웹/모바일 UI는 디자인 에이전트로 design.md 생성 후 **Google Stitch** 전달.
4. 스택 확정: React + Spring Boot + MyBatis + PostgreSQL.
5. **구현용 하네스 전체 생성**(전문 에이전트 + 오케스트레이터 + 백로그).
6. **자동전시시스템**으로 확장: 전시 관련 모든 기능·**경영분석(BI)**·웹/모바일 역할별 분리·**별도 관리자 시스템**·**CMS**.
7. 사용자 역할: 전시하려는자/공사 입찰사/킨텍스 직원 등 + **일반 대중 공개 홍보 페이지**.
8. 공사업체가 **AI 생성 자료 열람 → 견적서 제출 → 옥션(역경매) → 전시업체가 업체 확정**.
9. **부스 구성 AI 1·2·3안 생성 → 선택 또는 병합**.
10. **UIWS(WISE) 시스템관리·공통기능 전체 이식**(2FA/OTP 포함).
11. AI·QA·AA·SA·NA·TA·DA + **PM·개발PM·PMO** 에이전트 고용. AI는 **Claude(클로드코드)로 구현 + 설정에서 모델 변경**.
12. 전시장 평면도 크롤링 확보(CAD+JPG) + **CAD/JPG 포맷 지원**.
13. **개발서버 + CI/CD 구축**(WISE env 참조·kintex.zioinfo.co.kr).
14. **멀티테넌시(tenant_id)** — 코엑스 등 다중 전시관.
15. **MDI(다중문서 인터페이스)** 셸.
16. **DB 별도 구성**(전용 kintex_db).
17. GUARDiA 솔루션 재사용 패턴(py/md) kintex 흡수.
18. 로그인 좌측 히어로 이미지 + **CI 로고**(ci/ 폴더).
19. **Stitch 전 화면 디자인 반영**(이미지 포함) + 신규 화면.
20. **회원가입·비밀번호 찾기/초기화·아이디 기억**.
21. **반응형 풀스크린 + 모바일 앱**.
22. **전시관리 100대 기능** 크롤링·기획(AI 자동화로 수작업 최소화) + 백로그.
23. **웹접근성·시큐어코딩·다국어·라이트/다크 테마**(공통 NFR).
24. 행안부 산출물 표준 → **사업수행계획서 + 전 산출물 Excel/PPT/Doc** + **기능 변경 시 자동 업데이트**.
25. **사용자·운영자·개발자 지침서 PPT**.
26. **프로그램 사양서(기능별 상세) + 순서도 PPT**.
27. 본 WORK_STATUS.md로 인수인계·수시 업데이트.
## 4. 완료·라이브 (커밋 기준)
- **문서**: PLANNING v3.0(멀티테넌시)·design.md v1.2(MDI)·아키텍처 5종(app/system/tech/data/network)·WISE 개발문서 6종·FEATURE_BACKLOG_100 + GAP_ANALYSIS·GUARDIA_ALIGNMENT·SECURITY·OPS_RUNBOOK.
- **백엔드(라이브)**: 스캐폴드 + 인증(JWT/RBAC) + M2~M5 매퍼 배선(501 해소·PostGIS) + 룰엔진 + Phase B 공통레이어(2FA/OTP·시스템관리·공통 업무기능 V7/V8) + 공개 인증(register·forgot·reset V9). Flyway V1~V9(테이블 39).
- **프론트(라이브)**: 로그인(히어로+CI로고+인증 UI: 회원가입·비번찾기·아이디기억) + Stitch 반영(부스 설계 스튜디오·대시보드·갤러리·신규 8화면 SCR-04/05/08/09/10/11 + 모바일 M1/M2) + 번들 이미지 25.
- **인프라(라이브)**: 개발서버 8021·PostGIS·Redis·나노바나나 워커(Gemini 라이브)·TLS·CI/CD 자동배포·배포 파이프라인 근본수정.
- **계정**: `admin@kintex.zioinfo.co.kr`(env 시더, role ADMIN) — 로그인 200 확인.
- **자산**: 평면도 JPG 15 + CAD(트렌치) `docs/assets/floorplans/`(CAD는 gitignore). Stitch 프로젝트 9385904003821333054 화면 `stitch_kintex_ai_system_architect/`.
## 5. 진행 중(백그라운드 에이전트) — 커밋 대기
- 모바일 앱(Expo, `mobile/`) 스캐폴드.
- 산출물: 사업수행계획서 + 행안부 표준양식 크롤링 + 전 산출물 Excel/PPT/Doc(`docs/deliverables/`) + 자동갱신 스크립트.
- 지침서 3종(사용자·운영자·개발자) PPT(`docs/deliverables/지침서/`).
- 프로그램 사양서(기능별 상세) + 순서도 PPT(`docs/deliverables/프로그램사양서/`).
## 6. 남은 큐 (후속)
> **상세 미개발 통합 목록: `docs/UNDEVELOPED_BACKLOG.md`** (2026-07-11 세션 스캔 — 화면별 샘플→실데이터 전환·커밋 대기·NFR·대형 트랙 전체)
- **신규 5화면**: ~~반영 대기~~**구현 완료(2026-07-11)** — SCR-13~17(경영분석 `/analytics`·현장운영 `/ops/operations`·전시일정 `/schedule`·관리자 랜딩 `/admin`·스타일가이드 `/_styleguide`) design.md v1.3 매핑 + React 이식(tsc 통과·Recharts 도입). QA·reviewer·커밋 마감 진행 중. 데이터는 대부분 샘플(집계 API 미구축 — UNDEVELOPED_BACKLOG §1).
- **아이콘 세트 교체**(이모지 → Stitch 정식 아이콘, `_icons/`).
- **반응형 풀스크린 + 라이트/다크 테마 + 다국어(i18n) + 접근성** 전면 적용.
- **100대 기능 갭**(신규19/부분18) → orchestrator Phase 편입 구현(관람객·네트워킹·참가업체 서비스 집중).
- **멀티테넌시 구현**(DA 데이터모델 → db-engineer tenant_id 스키마 → backend 테넌트 컨텍스트).
- **MDI 프론트 전환**(design.md §2.7 기준).
- 도메인 모듈 구현: 옥션(M15)·관람객(M10)·CMS/공개사이트(M12/M17)·BI(M16)·관리자(M18).
## 7. 배포·운영 함정 (반드시 숙지)
- `deploy_kintex.sh`는 백엔드도 빌드해야 함(과거 미빌드 버그·수정됨). `gradlew``sh ./gradlew`(실행권한 이슈). `grep -q` SIGPIPE 회피.
- git 대용량(CAD·PNG·build)로 커밋/푸시 느림 → 특정 경로 add + push 재시도. CAD/build/node_modules gitignore.
- 로컬 `vite build`는 win32 rollup 크래시 가능 → **서버 빌드 신뢰**(tsc 통과로 검증).
- 서버 `/opt/zioinfo/deploy_server.py`(kintex 블록)·`/opt/kintex/` 수정 시 반영+재시작.
- 보안 불변: 외부API 금지(Claude·Gemini 예외)·자격증명 미노출·AI 이미지 워터마크·admin env 시더.
## 8. 변경 이력 (이 문서)
| 일자 | 갱신 |
|---|---|
| 2026-07-11 | 최초 작성 — 요구사항 27건·현황·남은 큐·함정 |
| 2026-07-11 | 신규 5화면(SCR-13~17) 구현 완료 반영 + 미개발 통합 백로그 `UNDEVELOPED_BACKLOG.md` 신설·연결 |

View File

@ -1,6 +1,6 @@
# 킨텍스 AI 전시관리 시스템 — UI 디자인 스펙 (design.md) # 킨텍스 AI 전시관리 시스템 — UI 디자인 스펙 (design.md)
> 작성: 디자인 에이전트(designer) · 작성일: 2026-07-11 · 버전: v1.2 > 작성: 디자인 에이전트(designer) · 작성일: 2026-07-11 · 버전: v1.3
> 근거 문서: `docs/PLANNING.md` v1.0 (모듈 맵 M1~M9, P0 기능, 나노바나나 표준 샷 세트 S1~S7) · Stitch 산출물 `stitch_kintex_ai_system_architect/`(18화면 + DESIGN.md 3변형) 학습·정합 > 근거 문서: `docs/PLANNING.md` v1.0 (모듈 맵 M1~M9, P0 기능, 나노바나나 표준 샷 세트 S1~S7) · Stitch 산출물 `stitch_kintex_ai_system_architect/`(18화면 + DESIGN.md 3변형) 학습·정합
> **단일 출처(Single Source of Truth)**: 프론트 구현 토큰·컴포넌트 계약은 본 문서가 권위다. Stitch 화면(code.html/screen.png)은 시각 참조로만 사용하며, 상충 시 본 문서를 따른다. > **단일 출처(Single Source of Truth)**: 프론트 구현 토큰·컴포넌트 계약은 본 문서가 권위다. Stitch 화면(code.html/screen.png)은 시각 참조로만 사용하며, 상충 시 본 문서를 따른다.
> 용도: **각 화면의 "Stitch 프롬프트" 코드블록을 Google Stitch(stitch.withgoogle.com)에 그대로 붙여넣어 화면을 생성**한다. 문서는 한국어, Stitch 프롬프트만 영어. > 용도: **각 화면의 "Stitch 프롬프트" 코드블록을 Google Stitch(stitch.withgoogle.com)에 그대로 붙여넣어 화면을 생성**한다. 문서는 한국어, Stitch 프롬프트만 영어.
@ -146,6 +146,22 @@
각 SCR의 상세 레이아웃·상태·Stitch 프롬프트는 아래 §3(웹)·§4(모바일)에 정의돼 있다. Stitch 화면은 시각 참조(screen.png)로만 사용하고, 프론트 토큰·컴포넌트 계약은 본 문서를 따른다. 각 SCR의 상세 레이아웃·상태·Stitch 프롬프트는 아래 §3(웹)·§4(모바일)에 정의돼 있다. Stitch 화면은 시각 참조(screen.png)로만 사용하고, 프론트 토큰·컴포넌트 계약은 본 문서를 따른다.
### 2.5-1. v2.0 확장 화면 매핑 (SCR-13~17 — 도메인·경영·시스템·스타일가이드)
PLANNING v2.0/v3.0에서 도메인 모듈(M10~M18)이 정식 편입되며, Stitch가 재취득한 아래 5개 화면을 SCR-13~17로 정합화한다. §2.6에서 "P2 후보(기획 미반영)"로 보류됐던 4개 화면 중 **M14·M16·M18은 planner가 PLANNING §5/§5A에 정식 반영**(M16 P1 승격·M18 P1·M14 P2)하여 승격 요건을 충족했고, exhibition_schedule은 방문객용이 아닌 **운영사·주최자용 행사 일정/홀 가동 관리 화면**으로 재해석되어 M1(홀 배정·행사일정)·M6(마일스톤)에 접합된다. SCR-17은 §1 디자인 시스템의 실증 스타일가이드(제품 화면 아님·내부 참조)다.
| Stitch 화면 디렉터리 | SCR ID | 화면명 | 모듈 | 우선순위 | 대상 역할 | 플랫폼 |
|---|---|---|---|---|---|---|
| `business_intelligence_kintex_ai_system` | SCR-13 | 경영분석 대시보드 | **M16** | P1 | 주최자·운영사(참가업체 ROI 관점 병행) | 웹 |
| `hall_operations_kintex_ai_system` | SCR-14 | 홀 현장운영 대시보드 | **M14** | P2 | 홀매니저·운영 | 웹 |
| `exhibition_schedule_kintex_ai_system` | SCR-15 | 전시 일정 관리 | **M1·M6** | P1 | 주최자·운영사 | 웹 |
| `admin_dashboard_kintex_ai_system` | SCR-16 | 관리자 대시보드 | **M18** | P1 | 테넌트 관리자·플랫폼 슈퍼관리자 | 웹(백오피스) |
| `component_guide_updated_kintex_ai_system` | SCR-17 | 컴포넌트 가이드(스타일가이드) | §1 디자인 시스템 | — (내부 참조) | 디자이너·프론트 개발 | 웹(내부) |
> **`component_guide_updated`가 최신본**이며 `component_guide`(구버전)은 참조하지 않는다. SCR-17은 배포 제품 라우트가 아닌 **개발/디자인 내부 참조(스토리북 성격)**로만 노출한다.
> **공통 NFR(SCR-13~17)**: ① **테마** — 라이트 모드 단일이 기본(§1-5), Recharts·게이지·히트맵 등 데이터 시각화는 §1 컬러 토큰을 CSS 변수로 주입해 향후 다크모드(Phase 3) 전환에 대비(하드코딩 hex 금지). ② **웹접근성** — 명도 대비 WCAG AA 이상, 차트는 색 외 패턴/라벨 병기, KPI·표는 `aria-label`·스크린리더 읽기 순서 준수, 키보드 포커스 링 `primary-600` 2px. ③ **다국어** — 화면 문자열은 i18n 키로 분리(M17 CMS 다국어 한/영/중/일 정책 정합), 숫자·통화·날짜는 로케일 포맷(tabular numerals) 사용. ④ **AI 산출물 구분** — 예측·매칭·이상탐지 등 AI 지표는 AiLabel(보라 `ai-accent`)로 사람 산출물과 항상 구분(§1-4).
## 2.6 범위 게이트 — P2 후보 (기획 반영 전 이식 금지) ## 2.6 범위 게이트 — P2 후보 (기획 반영 전 이식 금지)
`docs/PLANNING.md`가 **범위 권위**다(부스 신청→설계→유틸리티→규정검증→승인→시공→검수 파이프라인 중심). Stitch `kintex_ai_system_proposal.md`와 아래 4개 Stitch 화면은 PLANNING 범위 밖 비전(AI 컨시어지·CCTV 혼잡도·에너지 최적화·비즈니스 매칭·AR 내비 등)이므로 **P2 후보로만 분류**한다. **P0/P1으로 끌어오지 않으며, 승격하려면 먼저 planner에 기획 반영을 요청해야 한다(designer 임의 승격 금지).** `docs/PLANNING.md`가 **범위 권위**다(부스 신청→설계→유틸리티→규정검증→승인→시공→검수 파이프라인 중심). Stitch `kintex_ai_system_proposal.md`와 아래 4개 Stitch 화면은 PLANNING 범위 밖 비전(AI 컨시어지·CCTV 혼잡도·에너지 최적화·비즈니스 매칭·AR 내비 등)이므로 **P2 후보로만 분류**한다. **P0/P1으로 끌어오지 않으며, 승격하려면 먼저 planner에 기획 반영을 요청해야 한다(designer 임의 승격 금지).**
@ -159,6 +175,8 @@
> 위 4개 Stitch 화면의 시각 자산은 참고만 하고 프론트 구현에 착수하지 않는다. `kintex_ai_system_proposal.md`는 Stitch가 자동 생성한 확장 비전 문서로, 우리 PLANNING과 별개다. > 위 4개 Stitch 화면의 시각 자산은 참고만 하고 프론트 구현에 착수하지 않는다. `kintex_ai_system_proposal.md`는 Stitch가 자동 생성한 확장 비전 문서로, 우리 PLANNING과 별개다.
> **갱신(v1.3 · 2026-07-11)**: 위 표의 3개 화면은 **planner의 정식 기획 반영으로 P2 후보 게이트를 통과**했다 — `business_intelligence`→**M16 경영분석 BI(P1 승격, PLANNING §5A M16·M16-1)**, `hall_operations`→**M14 현장운영(P2, PLANNING §5A M14)**, `admin_dashboard`→**M18 관리자 시스템(P1, PLANNING §5A M18·§5B)**. 따라서 designer 임의 승격이 아니라 기획 근거를 갖춰 각각 SCR-13·SCR-14·SCR-16으로 정합화한다(§2.5-1·§3). `exhibition_schedule`은 방문객 서비스가 아닌 **운영사·주최자용 행사일정/홀 가동 관리 화면**(M1 배정 가용성 캘린더 + M6 마일스톤)으로 재분류되어 SCR-15로 편입한다. 단, Stitch 화면이 담은 **비전성 요소(CCTV/센서 실시간 혼잡·에너지 IoT·AI 컨시어지 플로팅 봇·매칭 성과 분석 등)는 여전히 해당 모듈의 P2/후속 범위**이며, PLANNING이 명시한 초기 데이터 원천(M4/M10 파생·수기 입력) 밖의 하드웨어 연동은 착수하지 않는다(§3 각 SCR "데이터 원천" 참조).
--- ---
## 2.7 MDI 셸 (다중 문서 인터페이스) — AppShell 표준 (v1.2 신설) ## 2.7 MDI 셸 (다중 문서 인터페이스) — AppShell 표준 (v1.2 신설)
@ -269,7 +287,7 @@ An enterprise MDI (multiple document interface) application shell for "KINTEX AI
## 3. 화면별 스펙 ## 3. 화면별 스펙
> P0 모듈(M2·M3·M4·M5) 중심 총 14개 화면(웹 12 + 모바일 2). 각 Stitch 프롬프트는 화면 1개를 1회 생성하는 기준으로 작성했다. > P0 모듈(M2·M3·M4·M5) 중심 총 14개 화면(웹 12 + 모바일 2) + **v2.0 확장 SCR-13~17(경영분석 M16·홀운영 M14·전시일정 M1·M6·관리자 M18·컴포넌트 가이드)**. 각 Stitch 프롬프트는 화면 1개를 1회 생성하는 기준으로 작성했다.
--- ---
@ -477,6 +495,118 @@ An AI-render gallery screen for exhibition booth visualizations in a Korean web
--- ---
### SCR-13 경영분석 대시보드 (히어로: 매출·가동률 지표 + 추세 차트) [M16 BI]
- **대상**: 주최자·킨텍스 운영사(관점 토글로 참가업체 ROI 병행) · **진입 경로**: 주최자 콘솔/운영 대시보드 사이드바 → "경영분석" (탭 열기)
- **모듈·우선순위**: M16 경영분석 BI · **P1**(PLANNING §5A M16·M16-1). BI는 두 관점을 격리 제공 — **① 운영사(킨텍스) 관점 수익성/ROI**(기본)와 **② 참가업체 관점 ROI**(리드·매칭·부스 비용 대비 성과). 상단 관점 토글로 전환하며 권한으로 분리(참가업체는 자기 부스 범위만).
- **레이아웃 (위→아래)**:
1. 헤더: 타이틀 "경영분석" + 부제 "킨텍스 운영 수익성 · 참가업체 인사이트", 우측 관점 토글([운영사] / [참가업체]), 기간 셀렉트("이번 행사 / 최근 90일 / 연간"), 우측 상단 "PDF 내보내기" · "Excel 내보내기" 버튼
2. **KPI 카드 3~4개(상단 밴드)**: [운영사 관점] 홀 가동률 78% · 매출(임대+유틸+옥션 수수료) ₩1.24B · ㎡당 수익(RevPAD) · 참가사 리텐션(재참가율) — 각 카드에 전기 대비 델타(▲/▼)와 §1-4 상태색. [참가업체 관점] 총 방문(부스) · 평균 체류시간 · 매칭 성공률(AiLabel "AI 매칭") 등
3. **히어로 — 추세 차트(2/3 폭)**: "매출 구성 추이"(운영사) 또는 "참가업체 vs 바이어 매칭 추이"(참가업체) — **Recharts `LineChart`/`AreaChart`**(다계열), 기간 셀렉트 연동. AI 예측 구간은 점선 + AiLabel "AI 수요예측"
4. 우측(1/3) — **부문 분석**: "홀·업종별 구성" 가로 막대 리스트(Recharts `BarChart` 또는 progress 바) — 기술/IT 45% · 제조 28% · 헬스케어 15% · 유통 12%
5. 하단(전폭) — **성과 테이블**: [운영사] 홀·행사별 P&L(행사, 홀, 매출, 공헌이익, 마진율, 상태 §1-4 StatusBadge) / [참가업체] 상위 참가사(업체명, 부스 위치, 방문 수, 매칭 수, 성과 배지 "우수/양호/보통/검토필요"). 지브라 스트라이프·sticky 헤더(§1-4)
- **핵심 컴포넌트/상태**: KPI 카드(전기 대비 델타 칩) · Recharts 차트(라이트/다크 테마 토큰 연동, tabular numerals) · StatusBadge/성과 배지 재사용 · AiLabel(수요예측·이상탐지·매칭). 로딩 — KPI·차트 스켈레톤 · 빈 상태 — "집계할 데이터가 없습니다 — 행사 종료 후 지표가 채워집니다" · 에러 — 카드별 재시도.
- **데이터 원천 (실/샘플 경계면)**: **대부분 샘플 시연.** M16 BI 데이터마트(스타 스키마 FactBooking·FactSettlement·FactUtility·FactAuction·FactVisitor + Dim*)·`KpiSnapshot` 집계 파이프라인은 **DA 후속 트랙 미구축**이므로(PLANNING §5A M16-1) 집계·예측·RevPAD·리텐션·P&L·매칭 지표는 샘플로 시연한다. 원천 목록(행사·부스·정산 레코드)은 실 API 부분 가능하나 **BI 지표는 집계 API 부재 → 샘플**(SCR-02 주최자 대시보드 KPI와 동일 원칙).
- **Stitch 프롬프트**:
```
An enterprise Business Intelligence analytics dashboard for a venue operator, part of "KINTEX AI 전시관리" B2B web app. Desktop 1440px, light theme, white cards on #F9FAFB, primary blue #0066B3, purple #6D4AFF for AI elements, Korean sans-serif (Pretendard style). All UI text in Korean. Left sidebar 240px (Dashboard, 전시 일정, 홀 운영, 경영분석 highlighted). Main top header: title "경영분석" with subtitle "킨텍스 운영 수익성 · 참가업체 인사이트", a perspective toggle "[운영사] / [참가업체]" (운영사 selected), a period select "이번 행사 ▾", and two outlined buttons "PDF 내보내기" and "Excel 내보내기". Below, a bento grid. Top band: 3 KPI stat cards — "홀 가동률 78%" with a green ▲ delta chip "+4%p", "매출 ₩1.24B" with green ▲ "+12.5%", "㎡당 수익(RevPAD) ₩58,200" — each card with a small icon and tabular-number figure. Middle row: a large chart card (2/3 width) titled "매출 구성 추이" showing a multi-series line/area chart in blue and slate with a dashed forecast segment on the right end labeled with a small purple chip "AI 수요예측", plus a period dropdown "최근 90일"; a right card (1/3) titled "업종별 구성" with horizontal bar rows "기술·IT 45%", "제조 28%", "헬스케어 15%", "유통 12%" using blue bars of varying widths and mono-number percentages. Bottom (full width): a dense data table "홀·행사별 손익(P&L)" with columns 행사, 홀, 매출, 공헌이익, 마진율, 상태 — status pill badges (녹색 "우수", 파랑 "양호", 주황 "보통", 빨강 "검토필요"), zebra striped rows, sticky header. Data-rich executive analytics aesthetic, clear number typography.
```
- **라우트**: 운영사(테넌트 전역 크로스-행사) `/analytics` · 행사 P&L 드릴다운 `/events/:eventId/analytics` · 참가업체 관점 `/events/:eventId/booths/:boothId/analytics`. MDI 적용(주최자·운영 — §2.7-6).
---
### SCR-14 홀 현장운영 대시보드 (혼잡·에너지·주차) [M14]
- **대상**: 홀매니저·킨텍스 운영 · **진입 경로**: 운영 대시보드(`ops.`) 사이드바 → "홀 운영" (행사 진행 중 상시 모니터)
- **모듈·우선순위**: M14 현장운영 · **P2**(PLANNING §5A M14). 초기 범위는 **M4(전력 부하)·M10(체크인 혼잡) 파생 데이터 + 수기 입력** 결합이며, CCTV/센서/IoT 실시간은 킨텍스 시설 인프라 의존(후속).
- **레이아웃 (위→아래)**:
1. 헤더: 타이틀 "홀 현장운영" + 부제 "실시간 시설 모니터링 및 자원 최적화" + 우측 [실시간] / [이력 조회] 토글
2. **12컬럼 벤토 그리드 — 히어로: 혼잡도 맵(span 8, 높이 500)**: 홀 도면 위 혼잡 히트(홀별 셀 Hall1~5, 임계 홀은 error 하이라이트 "홀3(혼잡)"), 하단 범례(원활 녹 / 보통 주황 / 혼잡 빨강)와 총 추정 인원. 전시장1/전시장2 홀 그룹 셀렉트. **데이터 미연동 시 상단 배지 "센서 미연동 · 체크인(M10) 파생/샘플"**
3. 우측(span 4) — **에너지·환경**: HVAC 최적화 카드(평균 온도 22.5°C, 전력 부하 4.2MW, 상태 배지 "최적"), 조명존 카드(전시홀 85% / 공용부 100%, 자동 디밍 토글) — 게이지·진행 바
4. 하단(span 12) — **주차 점유**: 4개 존 카드(존A 지하 98% error / 존B 옥외남측 75% warning / 존C 옥외북측 42% success / 하역장 20%), 각 카드 하단 점유율 바 + "N / 총면수" — iparking 연계 표기
- **핵심 컴포넌트/상태**: 히트맵(도면 오버레이·범례) · 게이지/진행 바(Recharts `RadialBar` 또는 progress) · 존 점유 카드(임계 상태색 §1-4) · 이상 시 알림 배지. 로딩 — 위젯 스켈레톤 · 빈 상태 — "진행 중인 행사가 없습니다 — 운영 데이터는 개장 시 활성화됩니다" · 에러 — 위젯별 "데이터 원본 연결 안 됨" 안내.
- **데이터 원천 (실/샘플 경계면)**: **샘플 시연 중심.** CCTV·혼잡 센서·HVAC/전력계·iparking·조명 IoT는 **미연동**(PLANNING §5A M14 "센서·IoT 연동은 킨텍스 시설 인프라 의존"). 혼잡은 M10 체크인 파생, 전력은 M4 신청 부하 집계로 **해당 모듈 구현 후 실 데이터 가능**하나 현 시점 위젯은 샘플. 하드웨어 연동 착수 금지(§2.6 갱신).
- **Stitch 프롬프트**:
```
A real-time hall operations dashboard for a KINTEX exhibition-center facility manager, Korean B2B internal tool, desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Left sidebar 240px (홀 운영 highlighted). Header: title "홀 현장운영" with subtitle "실시간 시설 모니터링 및 자원 최적화" and a segmented toggle "[실시간] / [이력 조회]". A 12-column bento grid. Hero card (span 8, tall ~500px): "실시간 혼잡도 히트맵" — a top-down hall layout with a 5-cell grid labeled Hall 15, soft colored heat blobs (green/orange/red) blurred over it, Hall 3 highlighted red with label "홀3 (혼잡)", a hall-group select "홀 1-5 (제1전시장) ▾", a small top-left badge "센서 미연동 · 체크인 파생/샘플", and a bottom legend "원활 / 보통 / 혼잡" plus "총 추정 인원 14,250". Right column (span 4): a "HVAC 최적화" card showing "22.5°C 평균온도", a progress bar, "전력 부하 4.2MW", and a green status pill "최적"; below it a "조명 존" card with rows "전시홀 85%", "공용부 100%" with bars and an "자동 디밍" toggle switch. Bottom (span 12): "주차 점유 현황" with 4 zone cards — "존A(지하) 98%" red, "존B(옥외남) 75%" amber, "존C(옥외북) 42%" green, "하역장 20%", each with an occupancy bar and "1,176 / 1,200" style counts. Dense real-time operations monitoring aesthetic.
```
- **라우트**: `/ops/operations` (테넌트·운영 전역, 홀매니저) · 행사 진행 중 스코프 `/events/:eventId/operations`. MDI 적용(홀매니저 — §2.7-6).
---
### SCR-15 전시 일정 관리 (행사 캘린더 + 홀 가동) [M1·M6]
- **대상**: 주최자·킨텍스 운영사 · **진입 경로**: 콘솔/운영 대시보드 사이드바 → "전시 일정"
- **모듈·우선순위**: M1 홀 배정·행사일정(가용성 캘린더) + M6 마일스톤 · **P1**. 방문객용 세션 편성이 아니라 **운영사·주최자 관점의 전 행사 일정·홀 점유 관리 화면**(§2.6 갱신 재분류).
- **레이아웃 (위→아래)**:
1. 헤더: 타이틀 "전시 일정" + 부제 "킨텍스 전 홀 행사 일정 관리·모니터링"
2. 필터/컨트롤 바: 홀 셀렉트(전체 홀 1-10 / 홀별) · 카테고리(전시/문화·예술/무역·산업/기술) · 상태(예정/진행중/종료) + 우측 [목록] / [캘린더] 뷰 토글
3. **좌측 2/3 — 행사 리스트**: 행사 카드(포스터 썸네일, 행사명, 상태 배지 §1-4 "진행중/예정/종료", 카테고리, 기간, 배정 홀, 추정 인원, [상세 편집]·[반입/물류 보기] 버튼)
4. 우측 1/3 — **미니 캘린더**(월 이동, 오늘·행사일 하이라이트) + **홀 가동률 요약**(홀1-3 85% · 홀4-5 40% 진행 바)
- **핵심 컴포넌트/상태**: 행사 카드 리스트·미니 캘린더(월 그리드)·가동률 진행 바·StatusBadge 재사용·DdayChip(개장 임박). 로딩 — 카드 스켈레톤 · 빈 상태 — "등록된 행사가 없습니다 — 새 행사를 생성하세요" CTA · 에러 — 리스트 로드 실패 배너.
- **데이터 원천 (실/샘플 경계면)**: **부분 실 API + 부분 샘플.** 행사(Event) 목록·기간·배정 홀은 **실 API 가능**(Event 엔티티 존재). 반면 **홀 가동률 집계·외부 행사일정 시스템 연동은 미구현 → 샘플**. 캘린더 점유 매트릭스는 배정(M1) 데이터 정합 후 실 전환.
- **Stitch 프롬프트**:
```
An exhibition schedule management screen for a venue operator, Korean B2B web app "KINTEX AI 전시관리", desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Left sidebar 240px (전시 일정 highlighted). Header: title "전시 일정" with subtitle "킨텍스 전 홀 행사 일정 관리·모니터링". A filter bar: three selects "전체 홀 (1-10) ▾", "전체 카테고리 ▾", "전체 상태 ▾", and on the right a view toggle "[목록] / [캘린더]". Main grid: left 2/3 a vertical list of event cards, each card with a poster thumbnail on the left and details on the right — event title "스마트팩토리 코리아", a status pill ("진행중" green or "예정" amber), category "무역·산업 전시", a row of meta with icons "2026.09.0509.08", "홀 4, 5", "추정 25,000명", and two small buttons "상세 편집" and "반입/물류 보기". Right 1/3 column: a mini month calendar "2026년 8월" with prev/next chevrons and a highlighted date "15", and below it a "홀 가동률 (8월)" card with progress bars "홀 1-3 85%" and "홀 4-5 40%". Clean scheduling/console aesthetic, dense but organized.
```
- **라우트**: `/schedule` (테넌트 전역, 운영·주최자) · 캘린더 딥링크 `/schedule/calendar`. MDI 적용(주최자·운영 — §2.7-6).
---
### SCR-16 관리자 대시보드 (백오피스 진입 · 시스템 개요) [M18]
- **대상**: 테넌트(전시관) 관리자·플랫폼 슈퍼관리자 · **진입 경로**: 백오피스(`admin.`) 로그인 후 기본 홈
- **모듈·우선순위**: M18 관리자 시스템(백오피스) · **P1**(PLANNING §5A M18·§5B). 본 화면은 **admin 랜딩 대시보드(시스템 개요)**이며, M18 본체(사용자관리·역할/권한 RBAC·공통코드·메뉴·감사로그·시스템설정·킨텍스 마스터데이터·룰셋 버전)는 **UIWS 표준 `system` 모듈 화면**으로 별도(§5B-1) — 후속 화면 세트로 추가. 플랫폼 슈퍼관리자는 상단 **테넌트 스위처** 보유, 테넌트 관리자는 자기 전시관 고정(§1A-4).
- **레이아웃 (위→아래)**:
1. 헤더: 타이틀 "시스템 개요" + 부제 "실시간 지표 및 운영 상태" + 우측 상태 인디케이터("시스템 정상" 녹색 점) + (플랫폼 슈퍼관리자) 테넌트 스위처 드롭다운
2. **KPI 카드 3개**: 진행 행사(활성) 14 (▲+2) · 실시간 방문객 24,592 (▲+12%) · 주차 가용 18% (혼잡)
3. **차트(2/3 폭)**: "방문객 추이(오늘)" — **Recharts `BarChart`**(시간대 09:00~19:00, [오늘]/[주간] 토글), 호버 툴팁
4. 우측(1/3) — **진행 중 이벤트 리스트**: 카드(썸네일, "LIVE" 상태 점, 행사명, 홀·시간) + "준비중" 카드(진행 바)
5. (선택) 우하단 플로팅 AI 어시스턴트 버튼 — **범위 표기: AI 컨시어지는 P2/후속 트랙**(PLANNING M18 미포함), 기본은 숨김/비활성
- **핵심 컴포넌트/상태**: KPI 카드(델타 칩)·Recharts 막대 차트·라이브 이벤트 카드(상태 점)·StatusBadge. 로딩 — 스켈레톤 · 빈 상태 — "진행 중인 행사가 없습니다" · 에러 — 위젯별 재시도. RBAC: 테넌트 관리자 vs 플랫폼 슈퍼관리자 위젯 차등(크로스-테넌트 집계는 슈퍼관리자만).
- **데이터 원천 (실/샘플 경계면)**: **혼합.** 사용자 수·활성 행사 수 등 **일부 실 API 가능**(M18/시스템관리 엔티티). **실시간 방문객·주차·라이브 이벤트·시간대 추이 위젯은 집계/센서 미구축 → 샘플 시연.** 플로팅 AI 봇은 미구현(비노출 기본).
- **Stitch 프롬프트**:
```
An admin back-office overview dashboard for "KINTEX AI 전시관리" platform, Korean B2B internal tool, desktop 1440px, light theme, blue #0066B3, Korean sans-serif. All UI text in Korean. Left sidebar 240px with menu (Dashboard highlighted, 전시 일정, 홀 운영, 경영분석) and a "Create New Event" button near the bottom with Settings/Support and an "Admin Profile" row. Header: title "시스템 개요" with subtitle "실시간 지표 및 운영 상태" and a small right-aligned status indicator "● 시스템 정상" in green (plus, for super admin, a tenant switcher dropdown "KINTEX ▾"). Top band: 3 KPI cards — "진행 행사(활성) 14" with a green chip "+2 this week", "실시간 방문객 24,592" with green "+12% vs 어제", "주차 가용 18%" with an amber chip "혼잡" and a thin progress bar. Below: a 2/3-width chart card "방문객 추이 (오늘)" with a bar chart across hours 09:0019:00 (blue bars of varying heights, hover tooltip showing values) and a "[오늘] / [주간]" toggle; a 1/3-width column "진행 중 이벤트" with event cards each showing a thumbnail, a green "LIVE NOW" dot, event title, hall + time, and one "준비중" card with an amber progress bar. Optional small floating circular assistant button bottom-right (kept subtle). Clean operational admin aesthetic.
```
- **라우트**: `/admin` (백오피스 랜딩, 테넌트 스코프 고정) · 슈퍼관리자 테넌트 스위처는 컨텍스트 전환. M18 본체 화면(별도): `/admin/users`·`/admin/roles`·`/admin/codes`·`/admin/menus`·`/admin/audit`·`/admin/settings`·`/admin/masterdata/*`(홀·요율·룰셋). MDI 적용(관리자 — §2.7-6).
---
### SCR-17 컴포넌트 가이드 (디자인 시스템 스타일가이드) [§1 실증]
- **대상**: 디자이너·프론트 개발(kintex-frontend-dev) · **진입 경로**: 내부 스타일가이드 URL(제품 내비 미노출)
- **성격**: **§1 디자인 시스템의 실증 스타일가이드(스토리북 성격).** 제품 화면이 아니라 §1 토큰·컴포넌트가 실제로 어떻게 렌더되는지 보여주는 **단일 참조**다. Stitch `component_guide_updated`(최신본)를 근거로 하되, **색·치수의 권위는 §1**(본 문서)이다.
- **레이아웃 (벤토 섹션)**:
1. 헤더: "System Reference / 컴포넌트 가이드", 설명("KINTEX AI 디자인 시스템 — 고신뢰·정밀 정보 처리 B2B 전시 환경"), 버전 배지
2. **타이포그래피 & 컬러**: Pretendard 스케일(Display 28-36 / Headline 24 / Title/H3 16-18 / Body 14) 한국어 샘플 + 팔레트 스와치(주 브랜드 블루·AI 액센트 `#6D4AFF`·Success·Error·Neutral)
3. **AI 전용 요소**: "AI 초안 컴포넌트"(좌측 4px 보라 액센트·워터마크 샘플 영역), 위반 플래그 핀(피난 통로 차단·바닥하중 초과)
4. **버튼 & 액션**: Primary / Secondary Outlined / Ghost / **AI 제안(보라)** / Disabled / 아이콘 버튼(편집·삭제·공유)
5. **상태 배지 & 칩**: 생애주기 `작성중→제출됨→AI검토(보라)→승인→반려→시공중→검수완료`(§1-4와 1:1) + DdayChip(D-3 warning)·Hall 칩·High Traffic 칩
6. **시스템 아이콘 라이브러리(커스텀 SVG)**: Dashboard·Calendar·Operations·Analytics·Search·Menu·AI Bot (stroke 스타일)
7. **데이터 시각화**: 게이지(점유율 78% 임계치 85%)·혼잡도 히트맵 범례(매우혼잡·다소혼잡·원활)·주간 추이 미니 막대 — 실제 구현은 **Recharts**로 대체
8. **폼 요소**: Input(기본/포커스/에러)·Dropdown·Checkbox·Radio
9. **피드백 & 오버레이**: Toast·Alert·마이크로 모달
- **§1 정합 주의(권위=본 문서)**: 스타일가이드가 표기한 일부 값은 §1과 미세 상이 — **KINTEX Blue `#004E8B`**(가이드) ≈ 본 문서 `primary-700 #004C86`(헤더/hover 짙은 톤)이며 **주 브랜드 블루는 `primary-600 #0066B3` 유지**(§1-2). **Success `#00734E`/Error `#BA1A1A`/히트맵 `#D92D20`·`#F79009`·`#12B76A`**는 Stitch Material 팔레트값이므로, 구현은 **§1 토큰(`success #0E8A5F`·`error #D92D20`)을 권위로 사용**하고 가이드값은 시각 참조로만 둔다. **AI 액센트 `#6D4AFF`·상태 배지 진행·라운드 4/8px·pill·좌측 액센트 바는 §1과 일치**(실증 확인).
- **데이터 원천 (실/샘플 경계면)**: **해당 없음(N/A) — 정적 참조.** 실 데이터·API 없음. 내부 문서/스토리북으로만 유지하며 배포 제품 라우트로 노출하지 않는다.
- **Stitch 프롬프트**:
```
A design-system style guide / component reference page for "KINTEX AI 전시관리", a Korean B2B exhibition platform. Desktop 1440px, light theme, primary blue #0066B3, AI accent purple #6D4AFF, backgrounds #F9FAFB, 1px borders #E4E7EC, Korean sans-serif (Pretendard). All UI text in Korean where shown. A bento grid of white bordered section cards, each with a left 4px colored accent bar and a small title. Sections: (1) "타이포그래피 & 컬러" — a Pretendard type scale with Korean samples "전시공간 최적화 엔진" (Display), "도면 분석 보고서" (Headline), "부스 배치 시뮬레이션" (Title), body text; and a color swatch grid "KINTEX Blue", "AI Accent #6D4AFF", "Success", "Error". (2) "AI 전용 요소" — an "AI 초안 컴포넌트" card with a faint diagonal "AI GENERATED" watermark area and violation flag map pins "피난 통로 차단", "바닥하중 초과" in red. (3) "버튼 & 액션" — Primary, Secondary Outlined, Ghost, a purple "AI 제안 레이아웃" button, a disabled button, and edit/delete/share icon buttons. (4) "상태 배지 & 칩" — lifecycle status chips in a row with left accent bars: "작성중"(gray), "제출됨"(blue), "AI검토"(purple), "승인"(green), "반려"(red), plus "시공중", "검수완료", and small chips "D-3", "Hall 1A", "High Traffic". (5) "데이터 시각화" — an occupancy gauge "전시장 점유율 78% / 임계치 85%", a heatmap legend "매우 혼잡 / 다소 혼잡 / 원활", and a small weekly bar chart. (6) "폼 요소" — text input (default/focus/error states), dropdown, checkbox, radio. (7) "피드백 & 오버레이" — a dark toast "데이터가 저장되었습니다", a red alert "소방법 위반 구역 3곳", and a small confirm modal. Clean, systematic, storybook-style reference layout, high density.
```
- **라우트**: `/_styleguide` (내부 전용, 프로덕션 비노출) — 제품 내비게이션·사이드바에 포함하지 않는다.
---
## 4. 모바일 대응 (390px) ## 4. 모바일 대응 (390px)
모바일은 **조회·승인·현장** 시나리오 전용(PLANNING.md §8). 캔버스 편집(SCR-03, 07)과 스펙 편집(SCR-06 좌패널)은 모바일 미제공 — 뷰어+승인 액션만. 아래 두 화면은 **모바일 전용 신규 화면**, 나머지는 반응형 축소. 모바일은 **조회·승인·현장** 시나리오 전용(PLANNING.md §8). 캔버스 편집(SCR-03, 07)과 스펙 편집(SCR-06 좌패널)은 모바일 미제공 — 뷰어+승인 액션만. 아래 두 화면은 **모바일 전용 신규 화면**, 나머지는 반응형 축소.
@ -540,4 +670,5 @@ A mobile field inspection screen for a KINTEX hall manager verifying booth const
| v1.0 | 2026-07-11 | designer | 최초 작성 — PLANNING.md v1.0 기반. 디자인 시스템·IA·P0 중심 14화면(웹 12 + 모바일 2) Stitch 프롬프트 포함. 다크모드는 Phase 1 미지원(캔버스 다크 서피스만) 결정 | | v1.0 | 2026-07-11 | designer | 최초 작성 — PLANNING.md v1.0 기반. 디자인 시스템·IA·P0 중심 14화면(웹 12 + 모바일 2) Stitch 프롬프트 포함. 다크모드는 Phase 1 미지원(캔버스 다크 서피스만) 결정 |
| v1.0.1 | 2026-07-11 | reviewer 반영 | 화면 수 표기 정정(13→14). 잔여 지적사항은 docs/BACKLOG.md 참조 | | v1.0.1 | 2026-07-11 | reviewer 반영 | 화면 수 표기 정정(13→14). 잔여 지적사항은 docs/BACKLOG.md 참조 |
| v1.1 | 2026-07-11 | designer | **Stitch 산출물 학습·정합화.** `stitch_kintex_ai_system_architect/` 18화면 code.html + DESIGN.md 3변형(Intelligence·Nexus·Precision) 학습. ①§1-2 컬러: Precision Enterprise AI 변형이 우리 토큰(#0066B3·#1C2536·#6D4AFF·#D92D20·#F9FAFB·#101828·StatusBadge 진행) 실증 확인 — 우리 팔레트를 단일 출처로 유지, #0059A6·#00427D는 짙은 대체 톤으로만 허용, ai-surface #F5F3FF AI 카드 배경 채택. ②§1-3 타이포: Pretendard 권위 유지, Inter를 라틴/tabular 보조로 허용(Stitch가 Hanken Grotesk/Inter 채택), 행 높이 44/48px 권위값 유지. ③§1-4 라운드(4/8px·pill)·엘리베이션(톤레이어+1px 테두리+AI 좌측 보라 액센트)·그리드(12컬럼·1440px·4px 베이스)·지브라 테이블 토큰 신설(이전 누락분). ④§2.5 Stitch 화면↔SCR 매핑표 신설(14화면). ⑤§2.6 범위 게이트 — proposal.md 및 범위 밖 4화면(business_intelligence·hall_operations·exhibition_schedule·admin_dashboard)을 P2 후보로 분류(PLANNING이 범위 권위, designer 임의 승격 금지). PLANNING.md·타 문서 무수정 | | v1.1 | 2026-07-11 | designer | **Stitch 산출물 학습·정합화.** `stitch_kintex_ai_system_architect/` 18화면 code.html + DESIGN.md 3변형(Intelligence·Nexus·Precision) 학습. ①§1-2 컬러: Precision Enterprise AI 변형이 우리 토큰(#0066B3·#1C2536·#6D4AFF·#D92D20·#F9FAFB·#101828·StatusBadge 진행) 실증 확인 — 우리 팔레트를 단일 출처로 유지, #0059A6·#00427D는 짙은 대체 톤으로만 허용, ai-surface #F5F3FF AI 카드 배경 채택. ②§1-3 타이포: Pretendard 권위 유지, Inter를 라틴/tabular 보조로 허용(Stitch가 Hanken Grotesk/Inter 채택), 행 높이 44/48px 권위값 유지. ③§1-4 라운드(4/8px·pill)·엘리베이션(톤레이어+1px 테두리+AI 좌측 보라 액센트)·그리드(12컬럼·1440px·4px 베이스)·지브라 테이블 토큰 신설(이전 누락분). ④§2.5 Stitch 화면↔SCR 매핑표 신설(14화면). ⑤§2.6 범위 게이트 — proposal.md 및 범위 밖 4화면(business_intelligence·hall_operations·exhibition_schedule·admin_dashboard)을 P2 후보로 분류(PLANNING이 범위 권위, designer 임의 승격 금지). PLANNING.md·타 문서 무수정 |
| v1.3 | 2026-07-11 | designer | **v2.0 확장 화면 SCR-13~17 정합화.** Stitch 재취득 5화면(`business_intelligence`·`hall_operations`·`exhibition_schedule`·`admin_dashboard`·`component_guide_updated`)을 기존 SCR 형식대로 순증. ①§2.5-1 v2.0 확장 화면 매핑표 신설(SCR-13 M16 P1·SCR-14 M14 P2·SCR-15 M1·M6 P1·SCR-16 M18 P1·SCR-17 §1 스타일가이드) + 공통 NFR(테마·웹접근성·다국어·AI 라벨). ②§2.6 갱신 노트 — business_intelligence/hall_operations/admin_dashboard는 planner의 PLANNING §5A 정식 반영(M16/M14/M18)으로 P2 후보 게이트 통과, exhibition_schedule은 운영사·주최자용 행사일정으로 재분류(M1·M6). 비전성 요소(센서·CCTV·AI 컨시어지)는 P2/후속으로 범위 표기. ③§3에 SCR-13~17 상세(레이아웃·상태·데이터 원천 실/샘플 경계면·라우트·Stitch 영어 프롬프트) 추가 — BI 차트 Recharts, StatusBadge/AiLabel 등 공통 자산 재사용 명시, 집계/센서 API 부재분은 샘플 시연 표기(SCR-02 원칙 준수). 라우트는 App.tsx 컨벤션 정합(`/analytics`·`/ops/operations`·`/schedule`·`/admin`·`/_styleguide` + event 스코프 드릴다운). §1 토큰 권위 유지(신규 토큰 0), PLANNING.md·src/ 무수정 |
| v1.2 | 2026-07-11 | designer | **MDI(다중 문서 인터페이스) 셸 표준 신설.** 업무 포털 AppShell을 단일 라우트 SPA → 탭 호스트로 전환하는 표준을 §2.7로 정의(kintex-frontend-dev AppShell 전환 기준). ①§2-2 내비게이션을 MDI 기준으로 갱신(사이드바+상단 문서 탭바+중앙 DocumentHost, 메뉴 클릭=탭 열기, 공개/관람객 사이트·모바일은 MDI 비적용). ②§2.7 신설 — 2.7-1 셸 레이아웃(3영역 그리드) · 2.7-2 탭 규격(열기/활성/닫기·미들클릭·드래그 재정렬·중복방지·최대 12탭·오버플로우 스크롤+드롭다운·dirty ● 및 닫기 확인·핀 고정) · 2.7-3 탭별 상태 보존(비활성 언마운트 금지 display:none·캔버스 freeze·WebGL 메모리 가드·beforeunload) · 2.7-4 세션 복원 & 딥링크↔탭 URL 동기화(라우트 예시·localStorage 세션·행사 전환 분리) · 2.7-5 단축키(Ctrl+W/Tab/1~9/Shift+T·Ctrl+클릭 새 탭)+접근성(ARIA tablist/tab/tabpanel·로빙 탭인덱스·WCAG AA) · 2.7-6 역할별 적용(관리자·주최자·참가·장치·홀매니저=적용, 관람객/공개=비적용, 모바일 전역 비적용) · 2.7-7 컴포넌트·스토어 표준(AppShell·TabBar·Tab·DocumentHost·useMdiStore/DocumentInstance/docKey 중복방지·직렬화 규칙) + MDI 셸 Stitch 프롬프트(영어). §1 토큰 재사용, 신규 토큰 0. PLANNING.md·src/·타 문서 무수정 | | v1.2 | 2026-07-11 | designer | **MDI(다중 문서 인터페이스) 셸 표준 신설.** 업무 포털 AppShell을 단일 라우트 SPA → 탭 호스트로 전환하는 표준을 §2.7로 정의(kintex-frontend-dev AppShell 전환 기준). ①§2-2 내비게이션을 MDI 기준으로 갱신(사이드바+상단 문서 탭바+중앙 DocumentHost, 메뉴 클릭=탭 열기, 공개/관람객 사이트·모바일은 MDI 비적용). ②§2.7 신설 — 2.7-1 셸 레이아웃(3영역 그리드) · 2.7-2 탭 규격(열기/활성/닫기·미들클릭·드래그 재정렬·중복방지·최대 12탭·오버플로우 스크롤+드롭다운·dirty ● 및 닫기 확인·핀 고정) · 2.7-3 탭별 상태 보존(비활성 언마운트 금지 display:none·캔버스 freeze·WebGL 메모리 가드·beforeunload) · 2.7-4 세션 복원 & 딥링크↔탭 URL 동기화(라우트 예시·localStorage 세션·행사 전환 분리) · 2.7-5 단축키(Ctrl+W/Tab/1~9/Shift+T·Ctrl+클릭 새 탭)+접근성(ARIA tablist/tab/tabpanel·로빙 탭인덱스·WCAG AA) · 2.7-6 역할별 적용(관리자·주최자·참가·장치·홀매니저=적용, 관람객/공개=비적용, 모바일 전역 비적용) · 2.7-7 컴포넌트·스토어 표준(AppShell·TabBar·Tab·DocumentHost·useMdiStore/DocumentInstance/docKey 중복방지·직렬화 규칙) + MDI 셸 Stitch 프롬프트(영어). §1 토큰 재사용, 신규 토큰 0. PLANNING.md·src/·타 문서 무수정 |

View File

@ -13,6 +13,7 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^6.26.2", "react-router-dom": "^6.26.2",
"recharts": "^3.9.2",
"sockjs-client": "^1.6.1", "sockjs-client": "^1.6.1",
"zustand": "^4.5.5" "zustand": "^4.5.5"
}, },
@ -750,6 +751,32 @@
"@jridgewell/sourcemap-codec": "^1.4.14" "@jridgewell/sourcemap-codec": "^1.4.14"
} }
}, },
"node_modules/@reduxjs/toolkit": {
"version": "2.12.0",
"resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.12.0.tgz",
"integrity": "sha512-KiT+RzZbp6mQET+Mg+h2c97+9j1sNflUxQkIHI7Yuzf6Peu+OYpmkn6nbHWmLLWj+1ZODUJFwGZ7gx3L9R9EOw==",
"license": "MIT",
"dependencies": {
"@standard-schema/spec": "^1.0.0",
"@standard-schema/utils": "^0.3.0",
"immer": "^11.0.0",
"redux": "^5.0.1",
"redux-thunk": "^3.1.0",
"reselect": "^5.1.0"
},
"peerDependencies": {
"react": "^16.9.0 || ^17.0.0 || ^18 || ^19",
"react-redux": "^7.2.1 || ^8.1.3 || ^9.0.0"
},
"peerDependenciesMeta": {
"react": {
"optional": true
},
"react-redux": {
"optional": true
}
}
},
"node_modules/@remix-run/router": { "node_modules/@remix-run/router": {
"version": "1.23.3", "version": "1.23.3",
"resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.3.tgz", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.23.3.tgz",
@ -1116,6 +1143,18 @@
"win32" "win32"
] ]
}, },
"node_modules/@standard-schema/spec": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/@standard-schema/spec/-/spec-1.1.0.tgz",
"integrity": "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==",
"license": "MIT"
},
"node_modules/@standard-schema/utils": {
"version": "0.3.0",
"resolved": "https://registry.npmjs.org/@standard-schema/utils/-/utils-0.3.0.tgz",
"integrity": "sha512-e7Mew686owMaPJVNNLs55PUvgz371nKgwsc4vxE49zsODpJEnxgxRo2y/OKrqueavXgZNMDVj3DdHFlaSAeU8g==",
"license": "MIT"
},
"node_modules/@stomp/stompjs": { "node_modules/@stomp/stompjs": {
"version": "7.3.0", "version": "7.3.0",
"resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.3.0.tgz", "resolved": "https://registry.npmjs.org/@stomp/stompjs/-/stompjs-7.3.0.tgz",
@ -1193,6 +1232,69 @@
"@babel/types": "^7.28.2" "@babel/types": "^7.28.2"
} }
}, },
"node_modules/@types/d3-array": {
"version": "3.2.2",
"resolved": "https://registry.npmjs.org/@types/d3-array/-/d3-array-3.2.2.tgz",
"integrity": "sha512-hOLWVbm7uRza0BYXpIIW5pxfrKe0W+D5lrFiAEYR+pb6w3N2SwSMaJbXdUfSEv+dT4MfHBLtn5js0LAWaO6otw==",
"license": "MIT"
},
"node_modules/@types/d3-color": {
"version": "3.1.3",
"resolved": "https://registry.npmjs.org/@types/d3-color/-/d3-color-3.1.3.tgz",
"integrity": "sha512-iO90scth9WAbmgv7ogoq57O9YpKmFBbmoEoCHDB2xMBY0+/KVrqAaCDyCE16dUspeOvIxFFRI+0sEtqDqy2b4A==",
"license": "MIT"
},
"node_modules/@types/d3-ease": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-ease/-/d3-ease-3.0.2.tgz",
"integrity": "sha512-NcV1JjO5oDzoK26oMzbILE6HW7uVXOHLQvHshBUW4UMdZGfiY6v5BeQwh9a9tCzv+CeefZQHJt5SRgK154RtiA==",
"license": "MIT"
},
"node_modules/@types/d3-interpolate": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-interpolate/-/d3-interpolate-3.0.4.tgz",
"integrity": "sha512-mgLPETlrpVV1YRJIglr4Ez47g7Yxjl1lj7YKsiMCb27VJH9W8NVM6Bb9d8kkpG/uAQS5AmbA48q2IAolKKo1MA==",
"license": "MIT",
"dependencies": {
"@types/d3-color": "*"
}
},
"node_modules/@types/d3-path": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/@types/d3-path/-/d3-path-3.1.1.tgz",
"integrity": "sha512-VMZBYyQvbGmWyWVea0EHs/BwLgxc+MKi1zLDCONksozI4YJMcTt8ZEuIR4Sb1MMTE8MMW49v0IwI5+b7RmfWlg==",
"license": "MIT"
},
"node_modules/@types/d3-scale": {
"version": "4.0.9",
"resolved": "https://registry.npmjs.org/@types/d3-scale/-/d3-scale-4.0.9.tgz",
"integrity": "sha512-dLmtwB8zkAeO/juAMfnV+sItKjlsw2lKdZVVy6LRr0cBmegxSABiLEpGVmSJJ8O08i4+sGR6qQtb6WtuwJdvVw==",
"license": "MIT",
"dependencies": {
"@types/d3-time": "*"
}
},
"node_modules/@types/d3-shape": {
"version": "3.1.8",
"resolved": "https://registry.npmjs.org/@types/d3-shape/-/d3-shape-3.1.8.tgz",
"integrity": "sha512-lae0iWfcDeR7qt7rA88BNiqdvPS5pFVPpo5OfjElwNaT2yyekbM0C9vK+yqBqEmHr6lDkRnYNoTBYlAgJa7a4w==",
"license": "MIT",
"dependencies": {
"@types/d3-path": "*"
}
},
"node_modules/@types/d3-time": {
"version": "3.0.4",
"resolved": "https://registry.npmjs.org/@types/d3-time/-/d3-time-3.0.4.tgz",
"integrity": "sha512-yuzZug1nkAAaBlBBikKZTgzCeA+k1uy4ZFwWANOfKw5z5LRhV0gNA7gNkKm7HoK+HRN0wX3EkxGk0fpbWhmB7g==",
"license": "MIT"
},
"node_modules/@types/d3-timer": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/@types/d3-timer/-/d3-timer-3.0.2.tgz",
"integrity": "sha512-Ps3T8E8dZDam6fUyNiMkekK3XUsaUEik+idO9/YjPtfj2qruF8tFBXS7XhtE4iIXBLxhmLjP3SXpLhVf21I9Lw==",
"license": "MIT"
},
"node_modules/@types/estree": { "node_modules/@types/estree": {
"version": "1.0.9", "version": "1.0.9",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz", "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.9.tgz",
@ -1247,6 +1349,12 @@
"dev": true, "dev": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/@types/use-sync-external-store": {
"version": "0.0.6",
"resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz",
"integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==",
"license": "MIT"
},
"node_modules/@vitejs/plugin-react": { "node_modules/@vitejs/plugin-react": {
"version": "4.7.0", "version": "4.7.0",
"resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz",
@ -1337,6 +1445,15 @@
], ],
"license": "CC-BY-4.0" "license": "CC-BY-4.0"
}, },
"node_modules/clsx": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz",
"integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/convert-source-map": { "node_modules/convert-source-map": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
@ -1351,6 +1468,127 @@
"devOptional": true, "devOptional": true,
"license": "MIT" "license": "MIT"
}, },
"node_modules/d3-array": {
"version": "3.2.4",
"resolved": "https://registry.npmjs.org/d3-array/-/d3-array-3.2.4.tgz",
"integrity": "sha512-tdQAmyA18i4J7wprpYq8ClcxZy3SC31QMeByyCFyRt7BVHdREQZ5lpzoe5mFEYZUWe+oq8HBvk9JjpibyEV4Jg==",
"license": "ISC",
"dependencies": {
"internmap": "1 - 2"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-color": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-color/-/d3-color-3.1.0.tgz",
"integrity": "sha512-zg/chbXyeBtMQ1LbD/WSoW2DpC3I0mpmPdW+ynRTj/x2DAWYrIY7qeZIHidozwV24m4iavr15lNwIwLxRmOxhA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-ease": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-ease/-/d3-ease-3.0.1.tgz",
"integrity": "sha512-wR/XK3D3XcLIZwpbvQwQ5fK+8Ykds1ip7A2Txe0yxncXSdq1L9skcG7blcedkOX+ZcgxGAmLX1FrRGbADwzi0w==",
"license": "BSD-3-Clause",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-format": {
"version": "3.1.2",
"resolved": "https://registry.npmjs.org/d3-format/-/d3-format-3.1.2.tgz",
"integrity": "sha512-AJDdYOdnyRDV5b6ArilzCPPwc1ejkHcoyFarqlPqT7zRYjhavcT3uSrqcMvsgh2CgoPbK3RCwyHaVyxYcP2Arg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-interpolate": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-interpolate/-/d3-interpolate-3.0.1.tgz",
"integrity": "sha512-3bYs1rOD33uo8aqJfKP3JWPAibgw8Zm2+L9vBKEHJ2Rg+viTR7o5Mmv5mZcieN+FRYaAOWX5SJATX6k1PWz72g==",
"license": "ISC",
"dependencies": {
"d3-color": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-path": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-path/-/d3-path-3.1.0.tgz",
"integrity": "sha512-p3KP5HCf/bvjBSSKuXid6Zqijx7wIfNW+J/maPs+iwR35at5JCbLUT0LzF1cnjbCHWhqzQTIN2Jpe8pRebIEFQ==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/d3-scale": {
"version": "4.0.2",
"resolved": "https://registry.npmjs.org/d3-scale/-/d3-scale-4.0.2.tgz",
"integrity": "sha512-GZW464g1SH7ag3Y7hXjf8RoUuAFIqklOAq3MRl4OaWabTFJY9PN/E1YklhXLh+OQ3fM9yS2nOkCoS+WLZ6kvxQ==",
"license": "ISC",
"dependencies": {
"d3-array": "2.10.0 - 3",
"d3-format": "1 - 3",
"d3-interpolate": "1.2.0 - 3",
"d3-time": "2.1.1 - 3",
"d3-time-format": "2 - 4"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-shape": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/d3-shape/-/d3-shape-3.2.0.tgz",
"integrity": "sha512-SaLBuwGm3MOViRq2ABk3eLoxwZELpH6zhl3FbAoJ7Vm1gofKx6El1Ib5z23NUEhF9AsGl7y+dzLe5Cw2AArGTA==",
"license": "ISC",
"dependencies": {
"d3-path": "^3.1.0"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/d3-time/-/d3-time-3.1.0.tgz",
"integrity": "sha512-VqKjzBLejbSMT4IgbmVgDjpkYrNWUYJnbCGo874u7MMKIWsILRX+OpX/gTk8MqjpT1A/c6HY2dCA77ZN0lkQ2Q==",
"license": "ISC",
"dependencies": {
"d3-array": "2 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-time-format": {
"version": "4.1.0",
"resolved": "https://registry.npmjs.org/d3-time-format/-/d3-time-format-4.1.0.tgz",
"integrity": "sha512-dJxPBlzC7NugB2PDLwo9Q8JiTR3M3e4/XANkreKSUxF8vvXKqm1Yfq4Q5dl8budlunRVlUUaDUgFt7eA8D6NLg==",
"license": "ISC",
"dependencies": {
"d3-time": "1 - 3"
},
"engines": {
"node": ">=12"
}
},
"node_modules/d3-timer": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/d3-timer/-/d3-timer-3.0.1.tgz",
"integrity": "sha512-ndfJ/JxxMd3nw31uyKoY2naivF+r29V+Lc0svZxe1JvvIRmi8hUsrMvdOwgS1o6uBHmiz91geQ0ylPP0aj1VUA==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/debug": { "node_modules/debug": {
"version": "4.4.3", "version": "4.4.3",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz",
@ -1369,6 +1607,12 @@
} }
} }
}, },
"node_modules/decimal.js-light": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/decimal.js-light/-/decimal.js-light-2.5.1.tgz",
"integrity": "sha512-qIMFpTMZmny+MMIitAB6D7iVPEorVw6YQRWkvarTkT4tBeSLLiHzcwj6q0MmYSFCiVpiqPJTJEYIrpcPzVEIvg==",
"license": "MIT"
},
"node_modules/electron-to-chromium": { "node_modules/electron-to-chromium": {
"version": "1.5.389", "version": "1.5.389",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.389.tgz",
@ -1376,6 +1620,16 @@
"dev": true, "dev": true,
"license": "ISC" "license": "ISC"
}, },
"node_modules/es-toolkit": {
"version": "1.49.0",
"resolved": "https://registry.npmjs.org/es-toolkit/-/es-toolkit-1.49.0.tgz",
"integrity": "sha512-G5iZ6Pc/FNRY/soKZHC+TxGDD83rHUDXxzaWhGCX44vAv/tMs56WMusnm/KMNK+luUPsgA9U28cGr4RDlSzL2g==",
"license": "MIT",
"workspaces": [
"docs",
"benchmarks"
]
},
"node_modules/esbuild": { "node_modules/esbuild": {
"version": "0.21.5", "version": "0.21.5",
"resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.21.5.tgz",
@ -1425,6 +1679,12 @@
"node": ">=6" "node": ">=6"
} }
}, },
"node_modules/eventemitter3": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz",
"integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==",
"license": "MIT"
},
"node_modules/eventsource": { "node_modules/eventsource": {
"version": "2.0.2", "version": "2.0.2",
"resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz", "resolved": "https://registry.npmjs.org/eventsource/-/eventsource-2.0.2.tgz",
@ -1477,12 +1737,32 @@
"integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==", "integrity": "sha512-Pysuw9XpUq5dVc/2SMHpuTY01RFl8fttgcyunjL7eEMhGM3cI4eOmiCycJDVCo/7O7ClfQD3SaI6ftDzqOXYMA==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/immer": {
"version": "11.1.11",
"resolved": "https://registry.npmjs.org/immer/-/immer-11.1.11.tgz",
"integrity": "sha512-qzXuyXAkPySAGYkfsAwodDPWT8Zm7/Uo5BNt4BjhMhG5WlWyZZ4wQqnWwdS8kjlQ1Cwu6gjw3A6+0gTQwlyYtw==",
"license": "MIT",
"peer": true,
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/immer"
}
},
"node_modules/inherits": { "node_modules/inherits": {
"version": "2.0.4", "version": "2.0.4",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
"integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
"license": "ISC" "license": "ISC"
}, },
"node_modules/internmap": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/internmap/-/internmap-2.0.3.tgz",
"integrity": "sha512-5Hh7Y1wQbvY5ooGgPbDaL5iYLAPzMTUrjMulskHLH6wnv/A+1q5rgEaiuqEjB+oxGXIVZs1FF+R/KPN3ZSQYYg==",
"license": "ISC",
"engines": {
"node": ">=12"
}
},
"node_modules/js-tokens": { "node_modules/js-tokens": {
"version": "4.0.0", "version": "4.0.0",
"resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",
@ -1641,6 +1921,37 @@
"react": "^18.3.1" "react": "^18.3.1"
} }
}, },
"node_modules/react-is": {
"version": "19.2.7",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.7.tgz",
"integrity": "sha512-kZFnouyVv7eP/Phmrlo9FK+zcAdriZJvzxXHF1Sl1P377WSGe2G/JxVolhTrB/jeV47lKImhNUsijjHAAbcl/A==",
"license": "MIT",
"peer": true
},
"node_modules/react-redux": {
"version": "9.3.0",
"resolved": "https://registry.npmjs.org/react-redux/-/react-redux-9.3.0.tgz",
"integrity": "sha512-KQopgqFo/p/fgmAs5qz6p5RWaNAzq40WAu7fJIXnQpYxFPbJYtsJPWvGeF2rOBaY/kEuV77AVsX8TsQzKm+A/g==",
"license": "MIT",
"peer": true,
"dependencies": {
"@types/use-sync-external-store": "^0.0.6",
"use-sync-external-store": "^1.4.0"
},
"peerDependencies": {
"@types/react": "^18.2.25 || ^19",
"react": "^18.0 || ^19",
"redux": "^5.0.0"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
},
"redux": {
"optional": true
}
}
},
"node_modules/react-refresh": { "node_modules/react-refresh": {
"version": "0.17.0", "version": "0.17.0",
"resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz",
@ -1683,12 +1994,64 @@
"react-dom": ">=16.8" "react-dom": ">=16.8"
} }
}, },
"node_modules/recharts": {
"version": "3.9.2",
"resolved": "https://registry.npmjs.org/recharts/-/recharts-3.9.2.tgz",
"integrity": "sha512-G4fy+Pk46RaXgwWMh+Nzhyo/lbFAVqXo9gtetlyehe6Ehge9CsgDuOTwQDD+i1+llaLktNBiNq4bhnGlDRXFtw==",
"license": "MIT",
"workspaces": [
"www"
],
"dependencies": {
"@reduxjs/toolkit": "^1.9.0 || 2.x.x",
"clsx": "^2.1.1",
"decimal.js-light": "^2.5.1",
"es-toolkit": "^1.39.3",
"eventemitter3": "^5.0.1",
"immer": "^11.1.8",
"react-redux": "8.x.x || 9.x.x",
"reselect": "5.2.0",
"tiny-invariant": "^1.3.3",
"use-sync-external-store": "^1.2.2",
"victory-vendor": "^37.0.2"
},
"engines": {
"node": ">=18"
},
"peerDependencies": {
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-dom": "^16.0.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
"react-is": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
}
},
"node_modules/redux": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz",
"integrity": "sha512-M9/ELqF6fy8FwmkpnF0S3YKOqMyoWJ4+CS5Efg2ct3oY9daQvd/Pc71FpGZsVsbl3Cpb+IIcjBDUnnyBdQbq4w==",
"license": "MIT",
"peer": true
},
"node_modules/redux-thunk": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/redux-thunk/-/redux-thunk-3.1.0.tgz",
"integrity": "sha512-NW2r5T6ksUKXCabzhL9z+h206HQw/NJkcLm1GPImRQ8IzfXwRGqjVhKJGauHirT0DAuyy6hjdnMZaRoAcy0Klw==",
"license": "MIT",
"peerDependencies": {
"redux": "^5.0.0"
}
},
"node_modules/requires-port": { "node_modules/requires-port": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz",
"integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==",
"license": "MIT" "license": "MIT"
}, },
"node_modules/reselect": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/reselect/-/reselect-5.2.0.tgz",
"integrity": "sha512-AgZ3UOZm3YndfrJ4OYjgrT7bmCm/1iqkjvEfH/oYjzh6PD2qw4QuT3jjnXIrpdt4MTpMXclMT3lXbmRY+XRakw==",
"license": "MIT"
},
"node_modules/rollup": { "node_modules/rollup": {
"version": "4.62.2", "version": "4.62.2",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz", "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.62.2.tgz",
@ -1811,6 +2174,12 @@
"node": ">=0.10.0" "node": ">=0.10.0"
} }
}, },
"node_modules/tiny-invariant": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.3.3.tgz",
"integrity": "sha512-+FbBPE1o9QAYvviau/qC5SE3caw21q3xkvWKBtja5vgqOWIHHJ3ioaq1VPfn/Szqctz2bU/oYeKd9/z5BL+PVg==",
"license": "MIT"
},
"node_modules/typescript": { "node_modules/typescript": {
"version": "5.9.3", "version": "5.9.3",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
@ -1882,6 +2251,28 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
} }
}, },
"node_modules/victory-vendor": {
"version": "37.3.6",
"resolved": "https://registry.npmjs.org/victory-vendor/-/victory-vendor-37.3.6.tgz",
"integrity": "sha512-SbPDPdDBYp+5MJHhBCAyI7wKM3d5ivekigc2Dk2s7pgbZ9wIgIBYGVw4zGHBml/qTFbexrofXW6Gu4noGxrOwQ==",
"license": "MIT AND ISC",
"dependencies": {
"@types/d3-array": "^3.0.3",
"@types/d3-ease": "^3.0.0",
"@types/d3-interpolate": "^3.0.1",
"@types/d3-scale": "^4.0.2",
"@types/d3-shape": "^3.1.0",
"@types/d3-time": "^3.0.0",
"@types/d3-timer": "^3.0.0",
"d3-array": "^3.1.6",
"d3-ease": "^3.0.1",
"d3-interpolate": "^3.0.1",
"d3-scale": "^4.0.2",
"d3-shape": "^3.1.0",
"d3-time": "^3.0.0",
"d3-timer": "^3.0.1"
}
},
"node_modules/vite": { "node_modules/vite": {
"version": "5.4.21", "version": "5.4.21",
"resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz", "resolved": "https://registry.npmjs.org/vite/-/vite-5.4.21.tgz",

View File

@ -17,6 +17,7 @@
"react": "^18.3.1", "react": "^18.3.1",
"react-dom": "^18.3.1", "react-dom": "^18.3.1",
"react-router-dom": "^6.26.2", "react-router-dom": "^6.26.2",
"recharts": "^3.9.2",
"sockjs-client": "^1.6.1", "sockjs-client": "^1.6.1",
"zustand": "^4.5.5" "zustand": "^4.5.5"
}, },

View File

@ -12,6 +12,11 @@ import { ComplianceReportPage } from './screens/compliance/ComplianceReportPage'
import { ManagerApprovalQueuePage } from './screens/approval/ManagerApprovalQueuePage'; import { ManagerApprovalQueuePage } from './screens/approval/ManagerApprovalQueuePage';
import { ReviewDetailsPage } from './screens/approval/ReviewDetailsPage'; import { ReviewDetailsPage } from './screens/approval/ReviewDetailsPage';
import { VisualizationGalleryPage } from './screens/gallery/VisualizationGalleryPage'; import { VisualizationGalleryPage } from './screens/gallery/VisualizationGalleryPage';
import { AnalyticsDashboardPage } from './screens/analytics/AnalyticsDashboardPage';
import { HallOperationsPage } from './screens/ops/HallOperationsPage';
import { ExhibitionSchedulePage } from './screens/schedule/ExhibitionSchedulePage';
import { AdminDashboardPage } from './screens/admin/AdminDashboardPage';
import { ComponentGuidePage } from './screens/styleguide/ComponentGuidePage';
import { ContractorOnsiteChecklistPage } from './screens/mobile/ContractorOnsiteChecklistPage'; import { ContractorOnsiteChecklistPage } from './screens/mobile/ContractorOnsiteChecklistPage';
import { ManagerFieldInspectionPage } from './screens/mobile/ManagerFieldInspectionPage'; import { ManagerFieldInspectionPage } from './screens/mobile/ManagerFieldInspectionPage';
import { AppShell } from './components/layout/AppShell'; import { AppShell } from './components/layout/AppShell';
@ -79,6 +84,16 @@ export function App() {
path="/events/:eventId/booths/:boothId/gallery" path="/events/:eventId/booths/:boothId/gallery"
element={<VisualizationGalleryPage />} element={<VisualizationGalleryPage />}
/> />
{/* SCR-13 경영분석 대시보드 (M16 BI · 테넌트 전역) */}
<Route path="/analytics" element={<AnalyticsDashboardPage />} />
{/* SCR-14 홀 현장운영 대시보드 (M14) */}
<Route path="/ops/operations" element={<HallOperationsPage />} />
{/* SCR-15 전시 일정 관리 (M1·M6) */}
<Route path="/schedule" element={<ExhibitionSchedulePage />} />
{/* SCR-16 관리자 대시보드 (M18 랜딩) */}
<Route path="/admin" element={<AdminDashboardPage />} />
{/* SCR-17 컴포넌트 가이드 (내부 전용 · 네비 비노출) */}
<Route path="/_styleguide" element={<ComponentGuidePage />} />
</Route> </Route>
{/* 모바일 전용 (390) — 셸 없이 전폭, 조회·현장 */} {/* 모바일 전용 (390) — 셸 없이 전폭, 조회·현장 */}

View File

@ -1,14 +1,23 @@
import { Outlet } from 'react-router-dom'; import { NavLink, Outlet } from 'react-router-dom';
import { useAuthStore } from '../../store/authStore'; import { useAuthStore } from '../../store/authStore';
import { DdayChip } from '../ui/Badge'; import { DdayChip } from '../ui/Badge';
import './app-shell.css'; import './app-shell.css';
const NAV_ITEMS = [ /**
* . `to` react-router NavLink( ), (#).
* (· ) (:eventId) .
* 4(·· ·) .
*/
const NAV_ITEMS: { key: string; label: string; icon: string; to?: string }[] = [
{ key: 'dashboard', label: '대시보드', icon: '▦' }, { key: 'dashboard', label: '대시보드', icon: '▦' },
{ key: 'floorplan', label: '플로어플랜 스튜디오', icon: '◱' }, { key: 'floorplan', label: '플로어플랜 스튜디오', icon: '◱' },
{ key: 'exhibitors', label: '참가업체 관리', icon: '☷' }, { key: 'exhibitors', label: '참가업체 관리', icon: '☷' },
{ key: 'schedule', label: '전시 일정', icon: '▤', to: '/schedule' },
{ key: 'operations', label: '현장운영', icon: '◐', to: '/ops/operations' },
{ key: 'analytics', label: '경영분석', icon: '▨', to: '/analytics' },
{ key: 'documents', label: '서류·마일스톤', icon: '❒' }, { key: 'documents', label: '서류·마일스톤', icon: '❒' },
{ key: 'settlement', label: '정산', icon: '₩' }, { key: 'settlement', label: '정산', icon: '₩' },
{ key: 'admin', label: '관리자', icon: '⚙', to: '/admin' },
]; ];
/** /**
@ -39,12 +48,24 @@ export function AppShell() {
)} )}
<ul className="kx-shell__nav"> <ul className="kx-shell__nav">
{NAV_ITEMS.map((item, i) => ( {NAV_ITEMS.map((item) => (
<li key={item.key}> <li key={item.key}>
<a className={`kx-shell__nav-link ${i === 1 ? 'is-active' : ''}`} href="#"> {item.to ? (
<span className="kx-shell__nav-icon" aria-hidden="true">{item.icon}</span> <NavLink
{item.label} to={item.to}
</a> className={({ isActive }) =>
`kx-shell__nav-link ${isActive ? 'is-active' : ''}`
}
>
<span className="kx-shell__nav-icon" aria-hidden="true">{item.icon}</span>
{item.label}
</NavLink>
) : (
<a className="kx-shell__nav-link" href="#" aria-disabled="true">
<span className="kx-shell__nav-icon" aria-hidden="true">{item.icon}</span>
{item.label}
</a>
)}
</li> </li>
))} ))}
</ul> </ul>

View File

@ -0,0 +1,140 @@
import { useState } from 'react';
import {
Bar,
BarChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import { CHART } from '../chartColors';
import {
ADMIN_KPIS,
LIVE_EVENTS,
TENANTS,
VISITOR_TREND,
} from './sampleAdmin';
import './admin.css';
const MOCK = `${import.meta.env.BASE_URL}mock/organizer_dashboard`;
const THUMBS = [`${MOCK}/img1.jpg`, `${MOCK}/img2.jpg`, `${MOCK}/img3.jpg`];
/*
* SCR-16 (M18 ). Stitch admin_dashboard .
* - 개요: KPI 3 + (/) + .
* (): (/ ). AI P2 ().
* M18 (···) (/admin/*).
*/
export function AdminDashboardPage() {
const [range, setRange] = useState<'today' | 'weekly'>('today');
const [tenant, setTenant] = useState(TENANTS[0]);
return (
<div className="kx-admin">
<header className="kx-admin__head">
<div>
<h1 className="kx-admin__title"> </h1>
<p className="kx-admin__subtitle"> </p>
</div>
<div className="kx-admin__head-actions">
{/* 플랫폼 슈퍼관리자 전용 테넌트 스위처(샘플) */}
<select
className="kx-select"
aria-label="테넌트 전환"
value={tenant}
onChange={(e) => setTenant(e.target.value)}
>
{TENANTS.map((t) => (
<option key={t} value={t}>{t}</option>
))}
</select>
<span className="kx-admin__status" role="status">
<span className="kx-admin__status-dot" aria-hidden="true" />
</span>
</div>
</header>
<div className="kx-admin__degraded-bar">
<span className="kx-bi__degraded"> API ()</span>
·· . (M18) .
</div>
<section className="kx-admin__kpis" aria-label="핵심 지표">
{ADMIN_KPIS.map((k) => (
<div key={k.key} className={`kx-kpi kx-kpi--${k.tone ?? 'normal'}`}>
<span className="kx-kpi__label">{k.label}</span>
<strong className="kx-kpi__value tnum">{k.value}</strong>
{k.delta && (
<span className={`kx-delta kx-delta--${k.deltaDir ?? 'up'}`}>
{k.tone === 'warn' ? '⚠' : k.deltaDir === 'down' ? '▼' : '▲'} {k.delta}
</span>
)}
{k.bar != null && (
<div className="kx-barlist__track" style={{ marginTop: 8 }}>
<span className="kx-barlist__fill is-error" style={{ width: `${k.bar}%` }} />
</div>
)}
</div>
))}
</section>
<div className="kx-admin__split">
<section className="kx-card kx-admin__chart-card" aria-label="방문객 추이">
<div className="kx-card__head">
<h2> ({range === 'today' ? '오늘' : '주간 평균'})</h2>
<div className="kx-seg" role="tablist" aria-label="추이 기간">
<button role="tab" aria-selected={range === 'today'} className={`kx-seg__btn ${range === 'today' ? 'is-active' : ''}`} onClick={() => setRange('today')}></button>
<button role="tab" aria-selected={range === 'weekly'} className={`kx-seg__btn ${range === 'weekly' ? 'is-active' : ''}`} onClick={() => setRange('weekly')}></button>
</div>
</div>
<div className="kx-admin__chart">
<ResponsiveContainer width="100%" height={300}>
<BarChart data={VISITOR_TREND} margin={{ top: 8, right: 8, bottom: 0, left: -8 }}>
<CartesianGrid stroke={CHART.neutral200} strokeDasharray="3 3" vertical={false} />
<XAxis dataKey="hour" tick={{ fontSize: 12, fill: CHART.slate }} tickLine={false} axisLine={{ stroke: CHART.neutral200 }} />
<YAxis tick={{ fontSize: 12, fill: CHART.slate }} tickLine={false} axisLine={false} width={44} />
<Tooltip cursor={{ fill: CHART.primary050 }} contentStyle={TOOLTIP_STYLE} />
<Bar dataKey={range} name="방문객" fill={CHART.primary600} radius={[4, 4, 0, 0]} maxBarSize={34} />
</BarChart>
</ResponsiveContainer>
</div>
</section>
<aside className="kx-admin__live" aria-label="진행 중 이벤트">
<div className="kx-card__head">
<h2> </h2>
</div>
<div className="kx-admin__live-list">
{LIVE_EVENTS.map((e) => (
<article key={e.id} className="kx-livecard">
<div className="kx-livecard__thumb" style={{ backgroundImage: `url(${THUMBS[e.thumbIdx]})` }} aria-hidden="true" />
<div className="kx-livecard__body">
<div className="kx-livecard__top">
<span className={`kx-livecard__dot ${e.live ? 'is-live' : 'is-prep'}`} aria-hidden="true" />
<span className="kx-livecard__state">{e.live ? 'LIVE NOW' : '준비중'}</span>
</div>
<h3 className="kx-livecard__title">{e.title}</h3>
<p className="kx-livecard__meta">{e.hallTime}</p>
{e.progress != null && (
<div className="kx-barlist__track">
<span className="kx-barlist__fill is-warning" style={{ width: `${e.progress}%` }} />
</div>
)}
</div>
</article>
))}
</div>
</aside>
</div>
{/* 플로팅 AI 컨시어지 버튼: PLANNING M18 미포함 → P2/후속. 현재 비노출. */}
</div>
);
}
const TOOLTIP_STYLE = {
fontSize: 12,
borderRadius: 8,
border: '1px solid #E4E7EC',
boxShadow: '0 4px 12px rgba(16,24,40,0.1)',
} as const;

View File

@ -0,0 +1,158 @@
@import '../shared.css';
.kx-admin {
flex: 1;
min-width: 0;
overflow-y: auto;
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.kx-admin__head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--space-4);
flex-wrap: wrap;
}
.kx-admin__title {
font-size: var(--fs-h1);
line-height: var(--lh-h1);
color: var(--color-neutral-900);
}
.kx-admin__subtitle {
font-size: var(--fs-body);
color: var(--color-neutral-500);
margin-top: 2px;
}
.kx-admin__head-actions {
display: flex;
align-items: center;
gap: var(--space-3);
}
.kx-admin__status {
display: inline-flex;
align-items: center;
gap: 6px;
font-size: var(--fs-caption);
font-weight: 600;
color: var(--color-success);
}
.kx-admin__status-dot {
width: 8px;
height: 8px;
border-radius: 50%;
background: var(--color-success);
box-shadow: 0 0 0 3px rgba(14, 138, 95, 0.15);
}
.kx-admin__degraded-bar {
display: flex;
align-items: center;
gap: var(--space-3);
font-size: var(--fs-caption);
color: var(--color-neutral-500);
background: var(--color-neutral-050);
border: var(--border-card);
border-radius: var(--radius-sm);
padding: var(--space-2) var(--space-3);
}
.kx-admin__kpis {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-4);
}
.kx-admin__split {
display: grid;
grid-template-columns: 2fr 1fr;
gap: var(--space-4);
align-items: start;
}
.kx-admin__chart {
width: 100%;
}
.kx-admin__live {
border: var(--border-card);
border-radius: var(--radius-lg);
background: var(--color-white);
padding: var(--space-4);
}
.kx-admin__live-list {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.kx-livecard {
display: flex;
gap: var(--space-3);
padding: var(--space-3);
border: var(--border-card);
border-radius: var(--radius-lg);
background: var(--color-neutral-050);
}
.kx-livecard__thumb {
width: 56px;
height: 56px;
flex-shrink: 0;
border-radius: var(--radius-sm);
background-size: cover;
background-position: center;
background-color: var(--color-neutral-100);
}
.kx-livecard__body {
flex: 1;
min-width: 0;
display: flex;
flex-direction: column;
gap: 4px;
}
.kx-livecard__top {
display: flex;
align-items: center;
gap: 6px;
}
.kx-livecard__dot {
width: 8px;
height: 8px;
border-radius: 50%;
}
.kx-livecard__dot.is-live {
background: var(--color-error);
animation: kx-live 1.6s ease-in-out infinite;
}
.kx-livecard__dot.is-prep {
background: var(--color-violation-warn);
}
@keyframes kx-live {
0%, 100% { box-shadow: 0 0 0 0 rgba(217, 45, 32, 0.5); }
50% { box-shadow: 0 0 0 5px rgba(217, 45, 32, 0); }
}
.kx-livecard__state {
font-size: 11px;
font-weight: 700;
letter-spacing: 0.02em;
color: var(--color-neutral-500);
}
.kx-livecard__title {
font-size: var(--fs-body);
font-weight: 600;
color: var(--color-neutral-900);
}
.kx-livecard__meta {
font-size: var(--fs-caption);
color: var(--color-neutral-500);
}
@media (max-width: 1100px) {
.kx-admin__kpis {
grid-template-columns: 1fr;
}
.kx-admin__split {
grid-template-columns: 1fr;
}
}

View File

@ -0,0 +1,58 @@
/*
* (SCR-16 · M18 ).
* (): · M18/ API
* . ·· ·
* / . AI () P2/ .
*/
export interface AdminKpi {
key: string;
label: string;
value: string;
delta?: string;
deltaDir?: 'up' | 'down';
tone?: 'normal' | 'warn';
bar?: number; // 진행 바(%) 선택
}
export const ADMIN_KPIS: AdminKpi[] = [
{ key: 'events', label: '진행 행사 (활성)', value: '14', delta: '+2 (금주)', deltaDir: 'up' },
{ key: 'visitors', label: '실시간 방문객', value: '24,592', delta: '+12% (어제 대비)', deltaDir: 'up' },
{ key: 'parking', label: '주차 가용', value: '18%', delta: '혼잡', tone: 'warn', bar: 82 },
];
// 방문객 추이(오늘) — 시간대별.
export interface HourPoint {
hour: string;
today: number;
weekly: number;
}
export const VISITOR_TREND: HourPoint[] = [
{ hour: '09시', today: 1200, weekly: 980 },
{ hour: '10시', today: 2600, weekly: 2100 },
{ hour: '11시', today: 3800, weekly: 3200 },
{ hour: '12시', today: 3100, weekly: 2900 },
{ hour: '13시', today: 3400, weekly: 3000 },
{ hour: '14시', today: 4200, weekly: 3600 },
{ hour: '15시', today: 4600, weekly: 3900 },
{ hour: '16시', today: 4100, weekly: 3500 },
{ hour: '17시', today: 3200, weekly: 2800 },
{ hour: '18시', today: 1900, weekly: 1600 },
{ hour: '19시', today: 900, weekly: 720 },
];
export interface LiveEvent {
id: string;
title: string;
hallTime: string;
live: boolean;
progress?: number; // 준비중 카드
thumbIdx: number;
}
export const LIVE_EVENTS: LiveEvent[] = [
{ id: 'smf', title: '스마트팩토리 코리아', hallTime: '홀 4·5 · 10:0018:00', live: true, thumbIdx: 0 },
{ id: 'health', title: '국제 헬스케어 위크', hallTime: '홀 7 · 10:0017:00', live: true, thumbIdx: 1 },
{ id: 'foodtech', title: '푸드테크 엑스포 (준비중)', hallTime: '홀 3 · D-2 설치', live: false, progress: 64, thumbIdx: 2 },
];
// 슈퍼관리자 테넌트 스위처(샘플)
export const TENANTS = ['KINTEX', 'BEXCO', 'COEX'];

View File

@ -0,0 +1,246 @@
import { useState } from 'react';
import {
Area,
AreaChart,
CartesianGrid,
ResponsiveContainer,
Tooltip,
XAxis,
YAxis,
} from 'recharts';
import { AiLabel, StatusBadge } from '../../components/ui/Badge';
import { Button } from '../../components/ui/Button';
import { CHART } from '../chartColors';
import {
EXHIBITOR_KPIS,
EXHIBITOR_PERF,
EXHIBITOR_TREND,
GRADE_LABEL,
OPERATOR_KPIS,
OPERATOR_PNL,
OPERATOR_TREND,
SECTORS,
type Kpi,
type Perspective,
type Period,
} from './sampleAnalytics';
import './analytics.css';
/*
* SCR-13 (M16 BI). Stitch business_intelligence .
* - (/) KPI·· .
* 경계면: BI · (PLANNING §5A M16-1) .
* ( /analytics . P&L .)
*/
export function AnalyticsDashboardPage() {
const [perspective, setPerspective] = useState<Perspective>('operator');
const [period, setPeriod] = useState<Period>('event');
const isOperator = perspective === 'operator';
const kpis = isOperator ? OPERATOR_KPIS : EXHIBITOR_KPIS;
return (
<div className="kx-bi">
{/* 헤더 */}
<header className="kx-bi__head">
<div>
<h1 className="kx-bi__title"></h1>
<p className="kx-bi__subtitle"> · </p>
</div>
<div className="kx-bi__head-actions">
<div className="kx-seg" role="tablist" aria-label="분석 관점">
<button
role="tab"
aria-selected={isOperator}
className={`kx-seg__btn ${isOperator ? 'is-active' : ''}`}
onClick={() => setPerspective('operator')}
>
</button>
<button
role="tab"
aria-selected={!isOperator}
className={`kx-seg__btn ${!isOperator ? 'is-active' : ''}`}
onClick={() => setPerspective('exhibitor')}
>
</button>
</div>
<select
className="kx-select"
aria-label="분석 기간"
value={period}
onChange={(e) => setPeriod(e.target.value as Period)}
>
<option value="event"> </option>
<option value="d90"> 90</option>
<option value="annual"></option>
</select>
<Button variant="secondary">PDF </Button>
<Button variant="secondary">Excel </Button>
</div>
</header>
<div className="kx-bi__degraded-bar">
<span className="kx-bi__degraded"> API ()</span>
BI · .
</div>
{/* KPI 밴드 */}
<section className="kx-bi__kpis" aria-label="핵심 지표">
{kpis.map((k) => (
<KpiCard key={k.key} kpi={k} />
))}
</section>
{/* 히어로 추세(2/3) + 업종 구성(1/3) */}
<div className="kx-bi__split">
<section className="kx-card kx-bi__trend" aria-label="추세 차트">
<div className="kx-card__head">
<h2>{isOperator ? '매출 구성 추이' : '참가업체 vs 바이어 매칭 추이'}</h2>
<AiLabel>AI </AiLabel>
</div>
<p className="kx-card__hint"> AI (단위: 백만원 / ).</p>
<div className="kx-bi__chart">
<ResponsiveContainer width="100%" height={320}>
{isOperator ? (
<AreaChart data={OPERATOR_TREND} margin={{ top: 8, right: 12, bottom: 0, left: -8 }}>
<defs>
<linearGradient id="gRental" x1="0" y1="0" x2="0" y2="1">
<stop offset="0%" stopColor={CHART.primary600} stopOpacity={0.35} />
<stop offset="100%" stopColor={CHART.primary600} stopOpacity={0.02} />
</linearGradient>
</defs>
<CartesianGrid stroke={CHART.neutral200} strokeDasharray="3 3" vertical={false} />
<XAxis dataKey="label" tick={{ fontSize: 12, fill: CHART.slate }} tickLine={false} axisLine={{ stroke: CHART.neutral200 }} />
<YAxis tick={{ fontSize: 12, fill: CHART.slate }} tickLine={false} axisLine={false} width={44} />
<Tooltip contentStyle={TOOLTIP_STYLE} />
<Area type="monotone" dataKey="auction" name="옥션 수수료" stackId="1" stroke={CHART.aiAccent} fill={CHART.aiAccent} fillOpacity={0.12} strokeWidth={2} />
<Area type="monotone" dataKey="utility" name="유틸리티" stackId="1" stroke={CHART.slate} fill={CHART.slate} fillOpacity={0.12} strokeWidth={2} />
<Area type="monotone" dataKey="rental" name="임대" stackId="1" stroke={CHART.primary600} fill="url(#gRental)" strokeWidth={2} />
</AreaChart>
) : (
<AreaChart data={EXHIBITOR_TREND} margin={{ top: 8, right: 12, bottom: 0, left: -8 }}>
<CartesianGrid stroke={CHART.neutral200} strokeDasharray="3 3" vertical={false} />
<XAxis dataKey="label" tick={{ fontSize: 12, fill: CHART.slate }} tickLine={false} axisLine={{ stroke: CHART.neutral200 }} />
<YAxis tick={{ fontSize: 12, fill: CHART.slate }} tickLine={false} axisLine={false} width={44} />
<Tooltip contentStyle={TOOLTIP_STYLE} />
<Area type="monotone" dataKey="exhibitors" name="참가업체" stroke={CHART.primary600} fill={CHART.primary600} fillOpacity={0.12} strokeWidth={2} />
<Area type="monotone" dataKey="buyers" name="바이어" stroke={CHART.aiAccent} fill={CHART.aiAccent} fillOpacity={0.1} strokeWidth={2} />
</AreaChart>
)}
</ResponsiveContainer>
</div>
</section>
<section className="kx-card kx-bi__sectors" aria-label="업종별 구성">
<div className="kx-card__head">
<h2> </h2>
</div>
<ul className="kx-barlist">
{SECTORS.map((s) => (
<li key={s.label} className="kx-barlist__item">
<div className="kx-barlist__top">
<span>{s.label}</span>
<span className="tnum">{s.pct}%</span>
</div>
<div className="kx-barlist__track">
<span className="kx-barlist__fill" style={{ width: `${s.pct}%` }} />
</div>
</li>
))}
</ul>
</section>
</div>
{/* 성과 테이블(전폭) */}
<section className="kx-card kx-bi__table-card" aria-label="성과 테이블">
<div className="kx-card__head">
<h2>{isOperator ? '홀·행사별 손익(P&L)' : '상위 참가사 성과'}</h2>
<span className="kx-bi__degraded"> API ()</span>
</div>
<div className="kx-table-scroll">
{isOperator ? (
<table className="kx-table kx-table--zebra">
<thead>
<tr>
<th></th>
<th></th>
<th className="kx-num"></th>
<th className="kx-num"></th>
<th className="kx-num"></th>
<th></th>
</tr>
</thead>
<tbody>
{OPERATOR_PNL.map((r) => (
<tr key={r.event}>
<td>{r.event}</td>
<td>{r.hall}</td>
<td className="kx-num tnum">{r.revenue}</td>
<td className="kx-num tnum">{r.contribution}</td>
<td className="kx-num tnum">{r.margin}</td>
<td><GradePill grade={r.grade} /></td>
</tr>
))}
</tbody>
</table>
) : (
<table className="kx-table kx-table--zebra">
<thead>
<tr>
<th></th>
<th></th>
<th className="kx-num"> </th>
<th className="kx-num"> </th>
<th> </th>
<th></th>
</tr>
</thead>
<tbody>
{EXHIBITOR_PERF.map((r) => (
<tr key={r.company}>
<td>{r.company}</td>
<td className="tnum">{r.booth}</td>
<td className="kx-num tnum">{r.visits.toLocaleString()}</td>
<td className="kx-num tnum">{r.matches}</td>
<td><StatusBadge status={r.status} /></td>
<td><GradePill grade={r.grade} /></td>
</tr>
))}
</tbody>
</table>
)}
</div>
</section>
</div>
);
}
const TOOLTIP_STYLE = {
fontSize: 12,
borderRadius: 8,
border: '1px solid #E4E7EC',
boxShadow: '0 4px 12px rgba(16,24,40,0.1)',
} as const;
function KpiCard({ kpi }: { kpi: Kpi }) {
return (
<div className={`kx-kpi kx-kpi--${kpi.tone ?? 'normal'}`}>
<span className="kx-kpi__label">
{kpi.label}
{kpi.ai && <span className="kx-kpi__ai"> AI</span>}
</span>
<strong className="kx-kpi__value tnum">{kpi.value}</strong>
{kpi.delta && (
<span className={`kx-delta kx-delta--${kpi.deltaDir ?? 'up'}`}>
{kpi.deltaDir === 'down' ? '▼' : '▲'} {kpi.delta}
</span>
)}
</div>
);
}
function GradePill({ grade }: { grade: 'excellent' | 'good' | 'fair' | 'review' }) {
return <span className={`kx-grade kx-grade--${grade}`}>{GRADE_LABEL[grade]}</span>;
}

View File

@ -0,0 +1,75 @@
@import '../shared.css';
.kx-bi {
flex: 1;
min-width: 0;
overflow-y: auto;
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.kx-bi__head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--space-4);
flex-wrap: wrap;
}
.kx-bi__title {
font-size: var(--fs-h1);
line-height: var(--lh-h1);
color: var(--color-neutral-900);
}
.kx-bi__subtitle {
font-size: var(--fs-body);
color: var(--color-neutral-500);
margin-top: 2px;
}
.kx-bi__head-actions {
display: flex;
align-items: center;
gap: var(--space-2);
flex-wrap: wrap;
}
.kx-bi__degraded-bar {
display: flex;
align-items: center;
gap: var(--space-3);
font-size: var(--fs-caption);
color: var(--color-neutral-500);
background: var(--color-neutral-050);
border: var(--border-card);
border-radius: var(--radius-sm);
padding: var(--space-2) var(--space-3);
}
.kx-bi__kpis {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-4);
}
.kx-bi__split {
display: grid;
grid-template-columns: 2fr 1fr;
gap: var(--space-4);
align-items: start;
}
.kx-bi__chart {
width: 100%;
}
.kx-bi__sectors {
align-self: stretch;
}
@media (max-width: 1100px) {
.kx-bi__kpis {
grid-template-columns: repeat(2, 1fr);
}
.kx-bi__split {
grid-template-columns: 1fr;
}
}

View File

@ -0,0 +1,123 @@
/*
* (SCR-13 · M16 BI).
* 경계면: M16 BI (FactBooking·FactSettlement·FactUtility·FactAuction·FactVisitor + Dim*)
* KpiSnapshot DA (PLANNING §5A M16-1).
* KPI··RevPAD··P&L· .
* (·· ) API BI API .
*/
import type { FlowStatus } from '../../components/ui/Badge';
export type Perspective = 'operator' | 'exhibitor';
export type Period = 'event' | 'd90' | 'annual';
export interface Kpi {
key: string;
label: string;
value: string;
delta?: string; // "+4%p"
deltaDir?: 'up' | 'down';
tone?: 'normal' | 'warn' | 'error';
ai?: boolean;
}
/** 매출 구성 추이 — 다계열 + 마지막 2점은 AI 예측(forecast=true). */
export interface TrendPoint {
label: string;
rental: number; // 임대(백만)
utility: number; // 유틸리티(백만)
auction: number; // 옥션 수수료(백만)
forecast?: boolean;
}
export interface ExhibitorTrendPoint {
label: string;
exhibitors: number; // 참가업체 매칭 요청
buyers: number; // 바이어 매칭
forecast?: boolean;
}
export interface SectorRow {
label: string;
pct: number;
}
export interface PnlRow {
event: string;
hall: string;
revenue: string;
contribution: string;
margin: string;
grade: 'excellent' | 'good' | 'fair' | 'review';
}
export interface ExhibitorPerfRow {
company: string;
booth: string;
visits: number;
matches: number;
status: FlowStatus;
grade: 'excellent' | 'good' | 'fair' | 'review';
}
export const GRADE_LABEL: Record<PnlRow['grade'], string> = {
excellent: '우수',
good: '양호',
fair: '보통',
review: '검토필요',
};
// ── 운영사 관점 ──
export const OPERATOR_KPIS: Kpi[] = [
{ key: 'occ', label: '홀 가동률', value: '78%', delta: '+4%p', deltaDir: 'up' },
{ key: 'rev', label: '매출 (임대+유틸+옥션)', value: '₩1.24B', delta: '+12.5%', deltaDir: 'up' },
{ key: 'revpad', label: '㎡당 수익 (RevPAD)', value: '₩58,200', delta: '+3.1%', deltaDir: 'up' },
{ key: 'ret', label: '참가사 리텐션(재참가)', value: '81%', delta: '-2%p', deltaDir: 'down', tone: 'warn' },
];
export const OPERATOR_TREND: TrendPoint[] = [
{ label: '4월', rental: 620, utility: 140, auction: 55 },
{ label: '5월', rental: 680, utility: 155, auction: 62 },
{ label: '6월', rental: 720, utility: 168, auction: 70 },
{ label: '7월', rental: 790, utility: 182, auction: 78 },
{ label: '8월', rental: 860, utility: 205, auction: 88 },
{ label: '9월(예측)', rental: 910, utility: 220, auction: 95, forecast: true },
{ label: '10월(예측)', rental: 965, utility: 236, auction: 102, forecast: true },
];
export const OPERATOR_PNL: PnlRow[] = [
{ event: '스마트팩토리 코리아', hall: '홀 4·5', revenue: '₩412M', contribution: '₩168M', margin: '40.8%', grade: 'excellent' },
{ event: '국제 헬스케어 위크', hall: '홀 7', revenue: '₩286M', contribution: '₩101M', margin: '35.3%', grade: 'good' },
{ event: '푸드테크 엑스포', hall: '홀 3', revenue: '₩198M', contribution: '₩52M', margin: '26.3%', grade: 'fair' },
{ event: '리빙·인테리어 페어', hall: '홀 1·2', revenue: '₩344M', contribution: '₩58M', margin: '16.9%', grade: 'review' },
];
// ── 참가업체 관점 ──
export const EXHIBITOR_KPIS: Kpi[] = [
{ key: 'visit', label: '총 방문 (부스)', value: '3,420', delta: '+18%', deltaDir: 'up' },
{ key: 'stay', label: '평균 체류시간', value: '7분 20초', delta: '+42초', deltaDir: 'up' },
{ key: 'match', label: '매칭 성공률', value: '34%', delta: '+6%p', deltaDir: 'up', ai: true },
{ key: 'roi', label: '부스 비용 대비 리드', value: '₩38k/리드', delta: '-9%', deltaDir: 'down', tone: 'warn' },
];
export const EXHIBITOR_TREND: ExhibitorTrendPoint[] = [
{ label: 'D-4', exhibitors: 80, buyers: 42 },
{ label: 'D-3', exhibitors: 130, buyers: 76 },
{ label: 'D-2', exhibitors: 205, buyers: 128 },
{ label: 'D-1', exhibitors: 260, buyers: 172 },
{ label: '개장일', exhibitors: 340, buyers: 240 },
{ label: 'D+1(예측)', exhibitors: 390, buyers: 285, forecast: true },
{ label: 'D+2(예측)', exhibitors: 430, buyers: 320, forecast: true },
];
export const EXHIBITOR_PERF: ExhibitorPerfRow[] = [
{ company: '(주)한빛로보틱스', booth: 'A-102', visits: 512, matches: 88, status: 'approved', grade: 'excellent' },
{ company: '누리테크놀로지', booth: 'B-201', visits: 388, matches: 61, status: 'approved', grade: 'good' },
{ company: '대성정밀', booth: 'A-104', visits: 246, matches: 32, status: 'ai-review', grade: 'fair' },
{ company: '세종머신', booth: 'B-205', visits: 118, matches: 9, status: 'rejected', grade: 'review' },
];
// 업종별 구성(공통) — 가로 막대.
export const SECTORS: SectorRow[] = [
{ label: '기술·IT', pct: 45 },
{ label: '제조', pct: 28 },
{ label: '헬스케어', pct: 15 },
{ label: '유통', pct: 12 },
];

View File

@ -0,0 +1,26 @@
/*
* Recharts design.md §1-2 JS .
* SVG (fill/stroke) CSS var() 16 .
* styles/tokens.css(§1 ) .
*/
export const CHART = {
primary600: '#0066B3', // 주 브랜드 블루
primary700: '#004C86', // 짙은 톤(hover/강조)
primary100: '#D9EAF7', // 활성 배경
primary050: '#EFF6FC',
aiAccent: '#6D4AFF', // AI 전용(수요예측 등)
aiSurface: '#F5F3FF',
success: '#0E8A5F',
warning: '#F79009', // 오버레이/차트용 앰버(§1 violation-warn)
error: '#D92D20',
slate: '#667085', // 보조 계열
neutral200: '#E4E7EC',
neutral100: '#F2F4F7',
} as const;
/** 임계 상태색 — 점유율/부하 등급별. */
export function occupancyTone(pct: number): 'success' | 'warning' | 'error' {
if (pct >= 90) return 'error';
if (pct >= 70) return 'warning';
return 'success';
}

View File

@ -0,0 +1,193 @@
import { useState } from 'react';
import {
PolarAngleAxis,
RadialBar,
RadialBarChart,
ResponsiveContainer,
} from 'recharts';
import { CHART, occupancyTone } from '../chartColors';
import {
HALL_CELLS,
HALL_GROUPS,
HVAC,
LIGHTING_ZONES,
PARKING_ZONES,
TOTAL_EST_PEOPLE,
} from './sampleOps';
import './ops.css';
/*
* SCR-14 (M14). Stitch hall_operations .
* - 히어로: 혼잡도 ( ) + HVAC· + .
* 경계면: 센서·IoT . (§2.6).
*/
export function HallOperationsPage() {
const [mode, setMode] = useState<'live' | 'history'>('live');
const [group, setGroup] = useState(HALL_GROUPS[0].id);
const powerPct = Math.round((HVAC.powerLoadMw / HVAC.powerCapacityMw) * 100);
return (
<div className="kx-ops">
<header className="kx-ops__head">
<div>
<h1 className="kx-ops__title"> </h1>
<p className="kx-ops__subtitle"> </p>
</div>
<div className="kx-seg" role="tablist" aria-label="조회 모드">
<button
role="tab"
aria-selected={mode === 'live'}
className={`kx-seg__btn ${mode === 'live' ? 'is-active' : ''}`}
onClick={() => setMode('live')}
>
</button>
<button
role="tab"
aria-selected={mode === 'history'}
className={`kx-seg__btn ${mode === 'history' ? 'is-active' : ''}`}
onClick={() => setMode('history')}
>
</button>
</div>
</header>
<div className="kx-ops__grid">
{/* 히어로: 혼잡도 히트맵 (span 8) */}
<section className="kx-card kx-ops__heat" aria-label="실시간 혼잡도 히트맵">
<div className="kx-card__head">
<h2> </h2>
<select
className="kx-select"
aria-label="홀 그룹 선택"
value={group}
onChange={(e) => setGroup(e.target.value)}
>
{HALL_GROUPS.map((g) => (
<option key={g.id} value={g.id}>
{g.label}
</option>
))}
</select>
</div>
<span className="kx-ops__sensor-badge"> · (M10) /</span>
<div className="kx-ops__halls">
{HALL_CELLS.map((c) => (
<div key={c.id} className={`kx-ops__hall is-${c.level}`}>
<span className="kx-ops__hall-label">{c.label}</span>
{c.level === 'busy' && <span className="kx-ops__hall-tag"></span>}
<span className="kx-ops__hall-people tnum">{c.estPeople.toLocaleString()}</span>
</div>
))}
</div>
<div className="kx-ops__heat-foot">
<ul className="kx-ops__legend">
<li><span className="kx-dot is-smooth" /> </li>
<li><span className="kx-dot is-moderate" /> </li>
<li><span className="kx-dot is-busy" /> </li>
</ul>
<span className="kx-ops__total">
<strong className="tnum">{TOTAL_EST_PEOPLE.toLocaleString()}</strong>
</span>
</div>
</section>
{/* 우측: HVAC + 조명존 (span 4) */}
<aside className="kx-ops__aside">
<section className="kx-card" aria-label="HVAC 최적화">
<div className="kx-card__head">
<h2>HVAC </h2>
<span className="kx-ops__pill is-ok">{HVAC.status}</span>
</div>
<div className="kx-ops__hvac">
<div className="kx-ops__gauge">
<ResponsiveContainer width="100%" height={140}>
<RadialBarChart
innerRadius="70%"
outerRadius="100%"
data={[{ name: '전력 부하', value: powerPct }]}
startAngle={90}
endAngle={-270}
>
<PolarAngleAxis type="number" domain={[0, 100]} tick={false} />
<RadialBar dataKey="value" cornerRadius={8} fill={CHART.primary600} background={{ fill: CHART.neutral100 }} />
</RadialBarChart>
</ResponsiveContainer>
<div className="kx-ops__gauge-label">
<strong className="tnum">{HVAC.powerLoadMw}MW</strong>
<span> </span>
</div>
</div>
<div className="kx-ops__hvac-meta">
<div>
<span className="kx-ops__meta-k"> </span>
<strong className="tnum">{HVAC.avgTempC}°C</strong>
</div>
<div>
<span className="kx-ops__meta-k"></span>
<strong className="tnum">{powerPct}%</strong>
</div>
</div>
</div>
</section>
<section className="kx-card" aria-label="조명 존">
<div className="kx-card__head">
<h2> </h2>
<label className="kx-switch">
<input type="checkbox" defaultChecked aria-label="자동 디밍" />
<span className="kx-switch__track" aria-hidden="true" />
<span className="kx-switch__text"> </span>
</label>
</div>
<ul className="kx-barlist">
{LIGHTING_ZONES.map((z) => (
<li key={z.label} className="kx-barlist__item">
<div className="kx-barlist__top">
<span>{z.label}</span>
<span className="tnum">{z.pct}%</span>
</div>
<div className="kx-barlist__track">
<span className="kx-barlist__fill" style={{ width: `${z.pct}%` }} />
</div>
</li>
))}
</ul>
</section>
</aside>
{/* 하단: 주차 점유 (span 12) */}
<section className="kx-card kx-ops__parking" aria-label="주차 점유 현황">
<div className="kx-card__head">
<h2> </h2>
<span className="kx-ops__note">iparking ()</span>
</div>
<div className="kx-ops__zones">
{PARKING_ZONES.map((z) => {
const pct = Math.round((z.occupied / z.total) * 100);
const tone = occupancyTone(pct);
return (
<div key={z.id} className="kx-ops__zone">
<div className="kx-ops__zone-top">
<span className="kx-ops__zone-label">{z.label}</span>
<strong className={`kx-ops__zone-pct tnum is-${tone}`}>{pct}%</strong>
</div>
<div className="kx-barlist__track">
<span className={`kx-barlist__fill is-${tone}`} style={{ width: `${pct}%` }} />
</div>
<span className="kx-ops__zone-count tnum">
{z.occupied.toLocaleString()} / {z.total.toLocaleString()}
</span>
</div>
);
})}
</div>
</section>
</div>
</div>
);
}

View File

@ -0,0 +1,317 @@
@import '../shared.css';
.kx-ops {
flex: 1;
min-width: 0;
overflow-y: auto;
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.kx-ops__head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--space-4);
flex-wrap: wrap;
}
.kx-ops__title {
font-size: var(--fs-h1);
line-height: var(--lh-h1);
color: var(--color-neutral-900);
}
.kx-ops__subtitle {
font-size: var(--fs-body);
color: var(--color-neutral-500);
margin-top: 2px;
}
/* 12컬럼 벤토 */
.kx-ops__grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: var(--space-4);
align-items: start;
}
.kx-ops__heat {
grid-column: span 8;
min-height: 500px;
display: flex;
flex-direction: column;
position: relative;
}
.kx-ops__aside {
grid-column: span 4;
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.kx-ops__parking {
grid-column: span 12;
}
.kx-ops__sensor-badge {
position: absolute;
top: var(--space-4);
left: var(--space-4);
z-index: 2;
font-size: 11px;
color: var(--color-warning);
background: #fff4e5;
padding: 3px 10px;
border-radius: var(--radius-pill);
}
/* 히트맵 홀 셀 */
.kx-ops__halls {
flex: 1;
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: var(--space-3);
padding: var(--space-6) 0 var(--space-4);
}
.kx-ops__hall {
position: relative;
border-radius: var(--radius-lg);
padding: var(--space-4);
display: flex;
flex-direction: column;
justify-content: space-between;
gap: var(--space-3);
min-height: 200px;
border: 1px solid transparent;
color: var(--color-neutral-900);
}
.kx-ops__hall.is-smooth {
background: rgba(18, 183, 106, 0.14);
border-color: rgba(18, 183, 106, 0.3);
}
.kx-ops__hall.is-moderate {
background: rgba(247, 144, 9, 0.16);
border-color: rgba(247, 144, 9, 0.32);
}
.kx-ops__hall.is-busy {
background: rgba(217, 45, 32, 0.18);
border-color: rgba(217, 45, 32, 0.4);
box-shadow: inset 0 0 0 1px rgba(217, 45, 32, 0.25);
}
.kx-ops__hall-label {
font-weight: 700;
font-size: var(--fs-h3);
}
.kx-ops__hall-tag {
position: absolute;
top: var(--space-3);
right: var(--space-3);
font-size: 11px;
font-weight: 700;
color: var(--color-white);
background: var(--color-error);
padding: 2px 8px;
border-radius: var(--radius-pill);
}
.kx-ops__hall-people {
font-size: var(--fs-body);
font-weight: 600;
color: var(--color-neutral-700);
}
.kx-ops__heat-foot {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-4);
border-top: var(--border-card);
padding-top: var(--space-3);
flex-wrap: wrap;
}
.kx-ops__legend {
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: var(--space-4);
font-size: var(--fs-caption);
color: var(--color-neutral-500);
}
.kx-ops__legend li {
display: flex;
align-items: center;
gap: 6px;
}
.kx-dot {
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
}
.kx-dot.is-smooth { background: #12b76a; }
.kx-dot.is-moderate { background: var(--color-violation-warn); }
.kx-dot.is-busy { background: var(--color-error); }
.kx-ops__total {
font-size: var(--fs-body);
color: var(--color-neutral-700);
}
.kx-ops__total strong {
color: var(--color-neutral-900);
}
/* HVAC */
.kx-ops__hvac {
display: flex;
align-items: center;
gap: var(--space-4);
}
.kx-ops__gauge {
position: relative;
width: 140px;
flex-shrink: 0;
}
.kx-ops__gauge-label {
position: absolute;
inset: 0;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
pointer-events: none;
}
.kx-ops__gauge-label strong {
font-size: var(--fs-h2);
color: var(--color-neutral-900);
}
.kx-ops__gauge-label span {
font-size: var(--fs-caption);
color: var(--color-neutral-500);
}
.kx-ops__hvac-meta {
display: flex;
flex-direction: column;
gap: var(--space-3);
}
.kx-ops__hvac-meta > div {
display: flex;
flex-direction: column;
}
.kx-ops__meta-k {
font-size: var(--fs-caption);
color: var(--color-neutral-500);
}
.kx-ops__hvac-meta strong {
font-size: var(--fs-h3);
color: var(--color-neutral-900);
}
.kx-ops__pill {
font-size: var(--fs-caption);
font-weight: 600;
padding: 2px 10px;
border-radius: var(--radius-pill);
}
.kx-ops__pill.is-ok {
background: #e6f4ee;
color: var(--color-success);
}
/* 스위치 */
.kx-switch {
display: inline-flex;
align-items: center;
gap: 8px;
font-size: var(--fs-caption);
color: var(--color-neutral-500);
cursor: pointer;
}
.kx-switch input {
position: absolute;
opacity: 0;
width: 0;
height: 0;
}
.kx-switch__track {
width: 34px;
height: 20px;
border-radius: var(--radius-pill);
background: var(--color-neutral-200);
position: relative;
transition: background 0.15s ease;
}
.kx-switch__track::after {
content: '';
position: absolute;
top: 2px;
left: 2px;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--color-white);
transition: transform 0.15s ease;
}
.kx-switch input:checked + .kx-switch__track {
background: var(--color-primary-600);
}
.kx-switch input:checked + .kx-switch__track::after {
transform: translateX(14px);
}
.kx-switch input:focus-visible + .kx-switch__track {
outline: 2px solid var(--color-primary-600);
outline-offset: 2px;
}
/* 조명존 fill tone */
.kx-barlist__fill.is-success { background: #12b76a; }
.kx-barlist__fill.is-warning { background: var(--color-violation-warn); }
.kx-barlist__fill.is-error { background: var(--color-error); }
/* 주차 */
.kx-ops__note {
font-size: 11px;
color: var(--color-neutral-500);
}
.kx-ops__zones {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: var(--space-4);
}
.kx-ops__zone {
display: flex;
flex-direction: column;
gap: var(--space-2);
padding: var(--space-4);
border: var(--border-card);
border-radius: var(--radius-lg);
background: var(--color-neutral-050);
}
.kx-ops__zone-top {
display: flex;
align-items: baseline;
justify-content: space-between;
}
.kx-ops__zone-label {
font-size: var(--fs-body);
font-weight: 600;
color: var(--color-neutral-900);
}
.kx-ops__zone-pct {
font-size: var(--fs-h2);
font-weight: 700;
}
.kx-ops__zone-pct.is-success { color: var(--color-success); }
.kx-ops__zone-pct.is-warning { color: var(--color-warning); }
.kx-ops__zone-pct.is-error { color: var(--color-error); }
.kx-ops__zone-count {
font-size: var(--fs-caption);
color: var(--color-neutral-500);
}
@media (max-width: 1200px) {
.kx-ops__heat { grid-column: span 12; }
.kx-ops__aside { grid-column: span 12; flex-direction: row; }
.kx-ops__aside > .kx-card { flex: 1; }
}
@media (max-width: 900px) {
.kx-ops__halls { grid-template-columns: repeat(2, 1fr); }
.kx-ops__zones { grid-template-columns: repeat(2, 1fr); }
.kx-ops__aside { flex-direction: column; }
}

View File

@ -0,0 +1,65 @@
/*
* (SCR-14 · M14).
* 경계면: CCTV· ·HVAC/·iparking· IoT는
* (PLANNING §5A M14 "센서·IoT 연동은 킨텍스 시설 인프라 의존").
* M10 , M4 .
* (§2.6). .
*/
export type HeatLevel = 'smooth' | 'moderate' | 'busy';
export interface HallCell {
id: string;
label: string;
level: HeatLevel;
estPeople: number;
}
export interface ParkingZone {
id: string;
label: string;
occupied: number;
total: number;
}
export interface LightingZone {
label: string;
pct: number;
}
export const HALL_GROUPS = [
{ id: 'ex1', label: '홀 1-5 (제1전시장)' },
{ id: 'ex2', label: '홀 6-10 (제2전시장)' },
];
export const HALL_CELLS: HallCell[] = [
{ id: 'h1', label: 'Hall 1', level: 'smooth', estPeople: 1820 },
{ id: 'h2', label: 'Hall 2', level: 'moderate', estPeople: 3240 },
{ id: 'h3', label: 'Hall 3', level: 'busy', estPeople: 4680 },
{ id: 'h4', label: 'Hall 4', level: 'moderate', estPeople: 2760 },
{ id: 'h5', label: 'Hall 5', level: 'smooth', estPeople: 1750 },
];
export const TOTAL_EST_PEOPLE = HALL_CELLS.reduce((s, c) => s + c.estPeople, 0);
export const HVAC = {
avgTempC: 22.5,
powerLoadMw: 4.2,
powerCapacityMw: 6.0,
status: '최적',
};
export const LIGHTING_ZONES: LightingZone[] = [
{ label: '전시홀', pct: 85 },
{ label: '공용부', pct: 100 },
];
export const PARKING_ZONES: ParkingZone[] = [
{ id: 'a', label: '존A (지하)', occupied: 1176, total: 1200 },
{ id: 'b', label: '존B (옥외남측)', occupied: 675, total: 900 },
{ id: 'c', label: '존C (옥외북측)', occupied: 336, total: 800 },
{ id: 'd', label: '하역장', occupied: 24, total: 120 },
];
export const HEAT_LABEL: Record<HeatLevel, string> = {
smooth: '원활',
moderate: '보통',
busy: '혼잡',
};

View File

@ -0,0 +1,213 @@
import { useMemo, useState } from 'react';
import { useAuthStore } from '../../store/authStore';
import { StatusBadge } from '../../components/ui/Badge';
import { Button } from '../../components/ui/Button';
import { EmptyState } from '../../components/ui/States';
import {
CATEGORY_LABEL,
EVENT_DAYS,
HALL_UTILIZATION,
SAMPLE_EVENTS,
STATUS_LABEL,
type EventStatus,
type ScheduleEvent,
} from './sampleSchedule';
import './schedule.css';
const MOCK = `${import.meta.env.BASE_URL}mock/organizer_dashboard`;
const THUMBS = [`${MOCK}/img1.jpg`, `${MOCK}/img2.jpg`, `${MOCK}/img3.jpg`];
const TODAY = new Date(2026, 6, 11); // 2026-07-11 (currentDate)
/** 홀 필터 매칭 — halls 문자열("홀 4, 5"·"제2전시장 홀7")에서 "홀" 뒤 번호만 추출해 전시장 범위("1-5"/"6-10")와 대조. "제N전시장"의 N 오매칭 방지. */
function matchesHall(halls: string, range: string): boolean {
if (range === 'all') return true;
const [lo, hi] = range.split('-').map(Number);
const m = halls.match(/홀\s*([\d,\s~-]+)/);
const nums = m ? (m[1].match(/\d+/g)?.map(Number) ?? []) : [];
return nums.some((n) => n >= lo && n <= hi);
}
/*
* SCR-15 (M1·M6). Stitch exhibition_schedule .
* - 2/3 + 1/3 · .
* 경계면: 행사 workspaces( ) , . · .
*/
export function ExhibitionSchedulePage() {
const workspaces = useAuthStore((s) => s.workspaces);
const [view, setView] = useState<'list' | 'calendar'>('list');
const [hall, setHall] = useState('all');
const [category, setCategory] = useState('all');
const [status, setStatus] = useState('all');
// 실 원천: 인증 workspaces → ScheduleEvent 매핑. 없으면 샘플 폴백.
const usingReal = workspaces.length > 0;
const events: ScheduleEvent[] = useMemo(() => {
if (!usingReal) return SAMPLE_EVENTS;
return workspaces.map((w, i) => ({
id: w.eventId,
title: w.eventName,
// ★ category/estVisitors 는 WorkspaceDto 미포함 → 샘플/기본값
category: 'exhibition',
status: (w.dday > 0 ? 'upcoming' : 'ongoing') as EventStatus,
period: `${w.startDate} ${w.endDate}`,
halls: w.hallLabel,
estVisitors: '집계 대기',
thumbIdx: i % THUMBS.length,
}));
}, [usingReal, workspaces]);
const filtered = events.filter(
(e) =>
matchesHall(e.halls, hall) &&
(category === 'all' || e.category === category) &&
(status === 'all' || e.status === status),
);
return (
<div className="kx-sched">
<header className="kx-sched__head">
<div>
<h1 className="kx-sched__title"> </h1>
<p className="kx-sched__subtitle"> ·</p>
</div>
</header>
<div className="kx-sched__filters">
<select className="kx-select" aria-label="홀 필터" value={hall} onChange={(e) => setHall(e.target.value)}>
<option value="all"> (1-10)</option>
<option value="1-5">1 ( 1-5)</option>
<option value="6-10">2 ( 6-10)</option>
</select>
<select className="kx-select" aria-label="카테고리 필터" value={category} onChange={(e) => setCategory(e.target.value)}>
<option value="all"> </option>
{Object.entries(CATEGORY_LABEL).map(([k, v]) => (
<option key={k} value={k}>{v}</option>
))}
</select>
<select className="kx-select" aria-label="상태 필터" value={status} onChange={(e) => setStatus(e.target.value)}>
<option value="all"> </option>
{Object.entries(STATUS_LABEL).map(([k, v]) => (
<option key={k} value={k}>{v}</option>
))}
</select>
<div className="kx-sched__filter-spacer" />
{!usingReal && <span className="kx-bi__degraded"> API ()</span>}
<div className="kx-seg" role="tablist" aria-label="보기 전환">
<button role="tab" aria-selected={view === 'list'} className={`kx-seg__btn ${view === 'list' ? 'is-active' : ''}`} onClick={() => setView('list')}></button>
<button role="tab" aria-selected={view === 'calendar'} className={`kx-seg__btn ${view === 'calendar' ? 'is-active' : ''}`} onClick={() => setView('calendar')}></button>
</div>
</div>
<div className="kx-sched__split">
<section className="kx-sched__list" aria-label="행사 목록">
{filtered.length === 0 ? (
<div className="kx-card">
<EmptyState
title="등록된 행사가 없습니다"
description="필터를 조정하거나 새 행사를 생성하세요."
action={<Button> </Button>}
/>
</div>
) : (
filtered.map((e) => (
<article key={e.id} className="kx-evcard">
<div className="kx-evcard__poster" style={{ backgroundImage: `url(${THUMBS[e.thumbIdx]})` }} aria-hidden="true" />
<div className="kx-evcard__body">
<div className="kx-evcard__top">
<h2 className="kx-evcard__title">{e.title}</h2>
<span className={`kx-evstatus is-${e.status}`}>{STATUS_LABEL[e.status]}</span>
</div>
<p className="kx-evcard__cat">{CATEGORY_LABEL[e.category]} </p>
<ul className="kx-evcard__meta">
<li aria-label="기간"> <span className="tnum">{e.period}</span></li>
<li aria-label="배정 홀"> {e.halls}</li>
<li aria-label="추정 인원"> <span className="tnum">{e.estVisitors}</span></li>
{e.designStatus && <li><StatusBadge status={e.designStatus} /></li>}
</ul>
<div className="kx-evcard__actions">
<Button variant="secondary"> </Button>
<Button variant="ghost">/ </Button>
</div>
</div>
</article>
))
)}
{view === 'calendar' && (
<p className="kx-sched__calnote">
M1 · .
</p>
)}
</section>
<aside className="kx-sched__aside">
<MiniCalendar />
<section className="kx-card" aria-label="홀 가동률">
<div className="kx-card__head">
<h2> (8)</h2>
<span className="kx-bi__degraded"> ()</span>
</div>
<ul className="kx-barlist">
{HALL_UTILIZATION.map((h) => (
<li key={h.label} className="kx-barlist__item">
<div className="kx-barlist__top">
<span>{h.label}</span>
<span className="tnum">{h.pct}%</span>
</div>
<div className="kx-barlist__track">
<span className="kx-barlist__fill" style={{ width: `${h.pct}%` }} />
</div>
</li>
))}
</ul>
</section>
</aside>
</div>
</div>
);
}
const WD = ['일', '월', '화', '수', '목', '금', '토'];
function MiniCalendar() {
const [cursor, setCursor] = useState(new Date(2026, 7, 1)); // 2026년 8월
const year = cursor.getFullYear();
const month = cursor.getMonth();
const firstDay = new Date(year, month, 1).getDay();
const daysInMonth = new Date(year, month + 1, 0).getDate();
const cells: (number | null)[] = [];
for (let i = 0; i < firstDay; i++) cells.push(null);
for (let d = 1; d <= daysInMonth; d++) cells.push(d);
const isToday = (d: number) =>
TODAY.getFullYear() === year && TODAY.getMonth() === month && TODAY.getDate() === d;
return (
<section className="kx-card kx-cal" aria-label="미니 캘린더">
<div className="kx-cal__head">
<button className="kx-cal__nav" aria-label="이전 달" onClick={() => setCursor(new Date(year, month - 1, 1))}></button>
<strong>{year} {month + 1}</strong>
<button className="kx-cal__nav" aria-label="다음 달" onClick={() => setCursor(new Date(year, month + 1, 1))}></button>
</div>
<div className="kx-cal__grid" role="grid">
{WD.map((w) => (
<span key={w} className="kx-cal__wd" role="columnheader">{w}</span>
))}
{cells.map((d, i) => {
if (d === null) return <span key={`e${i}`} className="kx-cal__cell is-empty" />;
const hasEvent = EVENT_DAYS.has(`${year}-${month + 1}-${d}`);
return (
<span
key={d}
className={`kx-cal__cell tnum ${isToday(d) ? 'is-today' : ''} ${hasEvent ? 'has-event' : ''}`}
role="gridcell"
aria-label={hasEvent ? `${d}일 (행사 있음)` : `${d}`}
>
{d}
</span>
);
})}
</div>
</section>
);
}

View File

@ -0,0 +1,99 @@
/*
* (SCR-15 · M1·M6).
* ( + ):
* - (Event) ·· API (Event ).
* workspaces(WorkspaceDto) , .
* - · . M1 .
*/
import type { FlowStatus } from '../../components/ui/Badge';
export type EventCategory = 'exhibition' | 'culture' | 'trade' | 'tech';
export type EventStatus = 'ongoing' | 'upcoming' | 'ended';
export interface ScheduleEvent {
id: string;
title: string;
category: EventCategory;
status: EventStatus;
period: string; // "2026.09.0509.08"
halls: string; // "홀 4, 5"
estVisitors: string; // "25,000명"
designStatus?: FlowStatus;
thumbIdx: number; // 로컬 mock 썸네일 인덱스
}
export const CATEGORY_LABEL: Record<EventCategory, string> = {
exhibition: '전시',
culture: '문화·예술',
trade: '무역·산업',
tech: '기술',
};
export const STATUS_LABEL: Record<EventStatus, string> = {
ongoing: '진행중',
upcoming: '예정',
ended: '종료',
};
export const SAMPLE_EVENTS: ScheduleEvent[] = [
{
id: 'smf2026',
title: '스마트팩토리 코리아',
category: 'trade',
status: 'ongoing',
period: '2026.09.0509.08',
halls: '홀 4, 5',
estVisitors: '25,000명',
designStatus: 'building',
thumbIdx: 0,
},
{
id: 'health2026',
title: '국제 헬스케어 위크',
category: 'exhibition',
status: 'upcoming',
period: '2026.09.2209.25',
halls: '홀 7',
estVisitors: '18,000명',
designStatus: 'submitted',
thumbIdx: 1,
},
{
id: 'foodtech2026',
title: '푸드테크 엑스포',
category: 'tech',
status: 'upcoming',
period: '2026.10.1010.13',
halls: '홀 3',
estVisitors: '12,000명',
designStatus: 'draft',
thumbIdx: 2,
},
{
id: 'living2026',
title: '리빙·인테리어 페어',
category: 'culture',
status: 'ended',
period: '2026.08.1408.17',
halls: '홀 1, 2',
estVisitors: '30,000명',
designStatus: 'inspected',
thumbIdx: 0,
},
];
export interface HallUtilRow {
label: string;
pct: number;
}
export const HALL_UTILIZATION: HallUtilRow[] = [
{ label: '홀 1-3', pct: 85 },
{ label: '홀 4-5', pct: 40 },
{ label: '홀 6-8', pct: 62 },
{ label: '홀 9-10', pct: 18 },
];
// 미니 캘린더 하이라이트 날짜(샘플) — key: 'YYYY-M-D'
export const EVENT_DAYS = new Set<string>([
'2026-8-14', '2026-8-15', '2026-8-16', '2026-8-17',
'2026-9-5', '2026-9-6', '2026-9-7', '2026-9-8',
]);

View File

@ -0,0 +1,221 @@
@import '../shared.css';
.kx-sched {
flex: 1;
min-width: 0;
overflow-y: auto;
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-5);
}
.kx-sched__title {
font-size: var(--fs-h1);
line-height: var(--lh-h1);
color: var(--color-neutral-900);
}
.kx-sched__subtitle {
font-size: var(--fs-body);
color: var(--color-neutral-500);
margin-top: 2px;
}
.kx-sched__filters {
display: flex;
align-items: center;
gap: var(--space-2);
flex-wrap: wrap;
}
.kx-sched__filter-spacer {
flex: 1;
}
.kx-sched__split {
display: grid;
grid-template-columns: 2fr 1fr;
gap: var(--space-4);
align-items: start;
}
.kx-sched__list {
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.kx-sched__calnote {
font-size: var(--fs-caption);
color: var(--color-neutral-500);
padding: var(--space-3);
background: var(--color-neutral-050);
border: var(--border-card);
border-radius: var(--radius-sm);
}
.kx-sched__aside {
display: flex;
flex-direction: column;
gap: var(--space-4);
}
/* 행사 카드 */
.kx-evcard {
display: flex;
gap: var(--space-4);
border: var(--border-card);
border-radius: var(--radius-lg);
background: var(--color-white);
overflow: hidden;
}
.kx-evcard__poster {
width: 160px;
flex-shrink: 0;
background-size: cover;
background-position: center;
background-color: var(--color-neutral-100);
}
.kx-evcard__body {
flex: 1;
min-width: 0;
padding: var(--space-4);
display: flex;
flex-direction: column;
gap: var(--space-2);
}
.kx-evcard__top {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
}
.kx-evcard__title {
font-size: var(--fs-h3);
color: var(--color-neutral-900);
}
.kx-evcard__cat {
font-size: var(--fs-caption);
color: var(--color-neutral-500);
}
.kx-evcard__meta {
list-style: none;
margin: var(--space-1) 0;
padding: 0;
display: flex;
flex-wrap: wrap;
gap: var(--space-2) var(--space-4);
font-size: var(--fs-body);
color: var(--color-neutral-700);
}
.kx-evcard__meta li {
display: flex;
align-items: center;
gap: 6px;
}
.kx-evcard__actions {
display: flex;
gap: var(--space-2);
margin-top: auto;
}
/* 행사 상태 pill */
.kx-evstatus {
font-size: var(--fs-caption);
font-weight: 600;
padding: 2px 10px;
border-radius: var(--radius-pill);
white-space: nowrap;
}
.kx-evstatus.is-ongoing {
background: #e6f4ee;
color: var(--color-success);
}
.kx-evstatus.is-upcoming {
background: #fff4e5;
color: var(--color-warning);
}
.kx-evstatus.is-ended {
background: var(--color-neutral-100);
color: var(--color-neutral-500);
}
/* 미니 캘린더 */
.kx-cal__head {
display: flex;
align-items: center;
justify-content: space-between;
margin-bottom: var(--space-3);
font-size: var(--fs-body);
color: var(--color-neutral-900);
}
.kx-cal__nav {
width: 28px;
height: 28px;
border: var(--border-card);
border-radius: var(--radius-sm);
background: var(--color-white);
color: var(--color-neutral-700);
font-size: 16px;
cursor: pointer;
}
.kx-cal__nav:hover {
background: var(--color-neutral-050);
}
.kx-cal__grid {
display: grid;
grid-template-columns: repeat(7, 1fr);
gap: 4px;
}
.kx-cal__wd {
text-align: center;
font-size: 11px;
color: var(--color-neutral-500);
padding-bottom: 4px;
}
.kx-cal__cell {
aspect-ratio: 1;
display: flex;
align-items: center;
justify-content: center;
font-size: var(--fs-caption);
color: var(--color-neutral-700);
border-radius: var(--radius-sm);
position: relative;
}
.kx-cal__cell.is-empty {
visibility: hidden;
}
.kx-cal__cell.has-event {
background: var(--color-primary-050);
color: var(--color-primary-700);
font-weight: 600;
}
.kx-cal__cell.has-event::after {
content: '';
position: absolute;
bottom: 3px;
width: 4px;
height: 4px;
border-radius: 50%;
background: var(--color-primary-600);
}
.kx-cal__cell.is-today {
background: var(--color-primary-600);
color: var(--color-white);
font-weight: 700;
}
.kx-cal__cell.is-today.has-event::after {
background: var(--color-white);
}
@media (max-width: 1100px) {
.kx-sched__split {
grid-template-columns: 1fr;
}
}
@media (max-width: 620px) {
.kx-evcard {
flex-direction: column;
}
.kx-evcard__poster {
width: 100%;
height: 120px;
}
}

View File

@ -0,0 +1,205 @@
/*
* v2.0 확장 화면(SCR-13~17) 공통 프리미티브.
* design.md §1 토큰만 참조. 화면 CSS가 @import 선반영한다.
*/
/* 스크롤 가능한 페이지 루트 — kx-shell__content(overflow:hidden, flex) 내부에서 자체 스크롤. */
.kx-page {
flex: 1;
min-width: 0;
overflow-y: auto;
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-5);
}
/* 카드 */
.kx-card {
border: var(--border-card);
border-radius: var(--radius-lg);
background: var(--color-white);
padding: var(--space-4);
}
.kx-card__head {
display: flex;
align-items: center;
justify-content: space-between;
gap: var(--space-3);
margin-bottom: var(--space-3);
}
.kx-card__head h2 {
font-size: var(--fs-h3);
color: var(--color-neutral-900);
}
.kx-card__hint {
font-size: var(--fs-caption);
color: var(--color-neutral-500);
margin: -4px 0 var(--space-3);
}
/* 세그먼트 토글 */
.kx-seg {
display: inline-flex;
padding: 3px;
background: var(--color-neutral-100);
border-radius: var(--radius-sm);
gap: 2px;
}
.kx-seg__btn {
padding: 6px 14px;
border: none;
background: transparent;
border-radius: 3px;
font-size: var(--fs-body);
font-weight: 600;
color: var(--color-neutral-500);
cursor: pointer;
}
.kx-seg__btn.is-active {
background: var(--color-white);
color: var(--color-primary-700);
box-shadow: 0 1px 2px rgba(16, 24, 40, 0.08);
}
/* 셀렉트 */
.kx-select {
height: 36px;
padding: 0 var(--space-3);
border: var(--border-card);
border-radius: var(--radius-sm);
background: var(--color-white);
font-size: var(--fs-body);
color: var(--color-neutral-700);
}
/* KPI 카드(공통) */
.kx-kpi {
display: flex;
flex-direction: column;
gap: var(--space-1);
padding: var(--space-4);
border: var(--border-card);
border-radius: var(--radius-lg);
background: var(--color-white);
}
.kx-kpi__label {
display: flex;
align-items: center;
gap: 6px;
font-size: var(--fs-caption);
color: var(--color-neutral-500);
}
.kx-kpi__ai {
color: var(--color-ai-accent);
font-weight: 700;
font-size: 11px;
}
.kx-kpi__value {
font-size: var(--fs-display);
line-height: var(--lh-display);
font-weight: 700;
color: var(--color-neutral-900);
}
.kx-kpi--warn {
border-color: var(--color-violation-warn);
}
.kx-kpi--error .kx-kpi__value {
color: var(--color-error);
}
/* 델타 칩 */
.kx-delta {
align-self: flex-start;
font-size: var(--fs-caption);
font-weight: 700;
padding: 1px 8px;
border-radius: var(--radius-pill);
}
.kx-delta--up {
color: var(--color-success);
background: #e6f4ee;
}
.kx-delta--down {
color: var(--color-error);
background: #fef3f2;
}
/* degraded 배지 */
.kx-bi__degraded {
font-size: 11px;
color: var(--color-warning);
background: #fff4e5;
padding: 2px 8px;
border-radius: var(--radius-pill);
white-space: nowrap;
}
/* 테이블 확장 */
.kx-table-scroll {
overflow-x: auto;
}
.kx-table--zebra tbody tr:nth-child(even) {
background: var(--zebra);
}
.kx-table thead th {
position: sticky;
top: 0;
}
.kx-num {
text-align: right;
}
/* 가로 막대 리스트 */
.kx-barlist {
list-style: none;
margin: 0;
padding: 0;
display: flex;
flex-direction: column;
gap: var(--space-4);
}
.kx-barlist__top {
display: flex;
justify-content: space-between;
font-size: var(--fs-body);
color: var(--color-neutral-700);
margin-bottom: 6px;
}
.kx-barlist__track {
height: 8px;
border-radius: var(--radius-pill);
background: var(--color-neutral-100);
overflow: hidden;
}
.kx-barlist__fill {
display: block;
height: 100%;
border-radius: var(--radius-pill);
background: var(--color-primary-600);
}
/* 성과 배지 */
.kx-grade {
display: inline-block;
font-size: var(--fs-caption);
font-weight: 600;
padding: 2px 10px;
border-radius: var(--radius-pill);
}
.kx-grade--excellent {
background: #e6f4ee;
color: var(--color-success);
}
.kx-grade--good {
background: var(--color-primary-050);
color: var(--color-primary-700);
}
.kx-grade--fair {
background: #fff4e5;
color: var(--color-warning);
}
.kx-grade--review {
background: #fef3f2;
color: var(--color-error);
}

View File

@ -0,0 +1,198 @@
import { AiImage } from '../../components/ui/AiImage';
import { AiLabel, DdayChip, RoleBadge, StatusBadge, type FlowStatus } from '../../components/ui/Badge';
import { Button } from '../../components/ui/Button';
import { EmptyState, ErrorState, Skeleton } from '../../components/ui/States';
import './styleguide.css';
/*
* SCR-17 (§1 / ).
* (/_styleguide) . · design.md §1(tokens.css).
* ·API ( ). .
*/
const STATUS_FLOW: FlowStatus[] = [
'draft', 'submitted', 'ai-review', 'approved', 'rejected', 'building', 'inspected',
];
const SWATCHES = [
{ name: 'KINTEX Blue', varName: '--color-primary-600', hex: '#0066B3' },
{ name: 'Primary 700', varName: '--color-primary-700', hex: '#004C86' },
{ name: 'AI Accent', varName: '--color-ai-accent', hex: '#6D4AFF' },
{ name: 'Success', varName: '--color-success', hex: '#0E8A5F' },
{ name: 'Error', varName: '--color-error', hex: '#D92D20' },
{ name: 'Warning', varName: '--color-warning', hex: '#B45309' },
{ name: 'Neutral 900', varName: '--color-neutral-900', hex: '#101828' },
{ name: 'Neutral 200', varName: '--color-neutral-200', hex: '#E4E7EC' },
];
const MOCK = `${import.meta.env.BASE_URL}mock/organizer_dashboard/img1.jpg`;
export function ComponentGuidePage() {
return (
<div className="kx-sg">
<header className="kx-sg__head">
<div>
<p className="kx-sg__eyebrow">System Reference</p>
<h1 className="kx-sg__title"> </h1>
<p className="kx-sg__desc">
KINTEX AI · B2B . · design.md §1.
</p>
</div>
<span className="kx-sg__ver">design.md v1.3 · </span>
</header>
<div className="kx-sg__grid">
{/* 1. 타이포 & 컬러 */}
<section className="kx-sg-card" data-accent="primary">
<h2 className="kx-sg-card__title"> & </h2>
<div className="kx-sg__type">
<p className="kx-sg__t-display"> </p>
<p className="kx-sg__t-headline"> </p>
<p className="kx-sg__t-title"> </p>
<p className="kx-sg__t-body">
. Pretendard 14px.
</p>
</div>
<div className="kx-sg__swatches">
{SWATCHES.map((s) => (
<div key={s.name} className="kx-sg__swatch">
<span className="kx-sg__swatch-chip" style={{ background: `var(${s.varName})` }} />
<span className="kx-sg__swatch-name">{s.name}</span>
<span className="kx-sg__swatch-hex tnum">{s.hex}</span>
</div>
))}
</div>
</section>
{/* 2. AI 전용 요소 */}
<section className="kx-sg-card" data-accent="ai">
<h2 className="kx-sg-card__title">AI </h2>
<div className="kx-sg__ai-row">
<AiLabel />
<AiLabel>AI </AiLabel>
<AiLabel>AI </AiLabel>
</div>
<div className="kx-sg__aiimg">
<AiImage
imageUrl={MOCK}
status="DONE"
sample
watermarkText="AI 생성 예상 이미지 — 실제 시공 결과와 다를 수 있습니다"
notice="AI 생성 이미지는 계약·심사 서류에 사용할 수 없습니다 — 시공 기준은 도면입니다"
shotLabel="S1 정면"
versionTag="설계 v2"
alt="AI 초안 컴포넌트 예시"
/>
</div>
<div className="kx-sg__violations">
<span className="kx-sg__viol">📍 </span>
<span className="kx-sg__viol">📍 </span>
</div>
</section>
{/* 3. 버튼 & 액션 */}
<section className="kx-sg-card" data-accent="primary">
<h2 className="kx-sg-card__title"> & </h2>
<div className="kx-sg__btn-row">
<Button variant="primary">Primary</Button>
<Button variant="secondary">Secondary</Button>
<Button variant="ghost">Ghost</Button>
<Button variant="ai" leadingIcon="✦">AI </Button>
<Button variant="danger"></Button>
<Button variant="primary" disabled>Disabled</Button>
</div>
<div className="kx-sg__btn-row">
<button className="kx-sg__icon-btn" aria-label="편집"></button>
<button className="kx-sg__icon-btn" aria-label="삭제">🗑</button>
<button className="kx-sg__icon-btn" aria-label="공유"></button>
</div>
</section>
{/* 4. 상태 배지 & 칩 */}
<section className="kx-sg-card" data-accent="success">
<h2 className="kx-sg-card__title"> & </h2>
<div className="kx-sg__chip-row">
{STATUS_FLOW.map((s) => (
<StatusBadge key={s} status={s} />
))}
</div>
<div className="kx-sg__chip-row">
<DdayChip dday={30} />
<DdayChip dday={3} />
<DdayChip dday={-1} />
<RoleBadge role="ORGANIZER" />
<RoleBadge role="HALL_MANAGER" />
<span className="kx-badge kx-badge--role">Hall 1A</span>
<span className="kx-badge kx-badge--dday-warn">High Traffic</span>
</div>
</section>
{/* 5. 데이터 시각화 (범례·게이지 — 실제는 Recharts로 대체) */}
<section className="kx-sg-card" data-accent="primary">
<h2 className="kx-sg-card__title"> </h2>
<p className="kx-sg-card__note"> Recharts (SCR-13·14·16). · .</p>
<div className="kx-sg__gauge">
<div className="kx-barlist__top">
<span> </span>
<span className="tnum">78% / 85%</span>
</div>
<div className="kx-barlist__track">
<span className="kx-barlist__fill is-warning" style={{ width: '78%' }} />
</div>
</div>
<ul className="kx-sg__legend">
<li><span className="kx-dot is-busy" /> </li>
<li><span className="kx-dot is-moderate" /> </li>
<li><span className="kx-dot is-smooth" /> </li>
</ul>
</section>
{/* 6. 폼 요소 */}
<section className="kx-sg-card" data-accent="primary">
<h2 className="kx-sg-card__title"> </h2>
<div className="kx-sg__form">
<label className="kx-sg__field">
<span> Input</span>
<input className="kx-sg__input" placeholder="부스번호 입력" />
</label>
<label className="kx-sg__field">
<span> Input</span>
<input className="kx-sg__input is-error" defaultValue="A-999" aria-invalid />
<em className="kx-sg__err-msg"> .</em>
</label>
<label className="kx-sg__field">
<span>Dropdown</span>
<select className="kx-select">
<option> </option>
<option></option>
</select>
</label>
<div className="kx-sg__check-row">
<label className="kx-sg__check"><input type="checkbox" defaultChecked /> </label>
<label className="kx-sg__check"><input type="radio" name="sg-r" defaultChecked /> </label>
<label className="kx-sg__check"><input type="radio" name="sg-r" /> </label>
</div>
</div>
</section>
{/* 7. 피드백 & 오버레이 */}
<section className="kx-sg-card" data-accent="ai">
<h2 className="kx-sg-card__title"> & </h2>
<div className="kx-sg__toast"> </div>
<div className="kx-sg__alert" role="alert"> 3</div>
<div className="kx-sg__states">
<div className="kx-sg__state-box">
<Skeleton height={12} width="80%" />
<Skeleton height={12} width="60%" />
</div>
<div className="kx-sg__state-box">
<EmptyState title="데이터 없음" description="빈 상태 예시" />
</div>
<div className="kx-sg__state-box">
<ErrorState message="불러오기 실패" onRetry={() => {}} />
</div>
</div>
</section>
</div>
</div>
);
}

View File

@ -0,0 +1,285 @@
@import '../shared.css';
.kx-sg {
flex: 1;
min-width: 0;
overflow-y: auto;
padding: var(--space-5);
display: flex;
flex-direction: column;
gap: var(--space-5);
background: var(--color-neutral-050);
}
.kx-sg__head {
display: flex;
align-items: flex-start;
justify-content: space-between;
gap: var(--space-4);
flex-wrap: wrap;
}
.kx-sg__eyebrow {
font-size: var(--fs-caption);
font-weight: 700;
letter-spacing: 0.06em;
text-transform: uppercase;
color: var(--color-primary-600);
}
.kx-sg__title {
font-size: var(--fs-display);
line-height: var(--lh-display);
color: var(--color-neutral-900);
}
.kx-sg__desc {
font-size: var(--fs-body);
color: var(--color-neutral-500);
margin-top: 4px;
max-width: 640px;
}
.kx-sg__ver {
font-size: var(--fs-caption);
color: var(--color-neutral-500);
background: var(--color-white);
border: var(--border-card);
border-radius: var(--radius-pill);
padding: 4px 12px;
}
.kx-sg__grid {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-4);
align-items: start;
}
.kx-sg-card {
position: relative;
background: var(--color-white);
border: var(--border-card);
border-radius: var(--radius-lg);
padding: var(--space-5);
padding-left: var(--space-5);
overflow: hidden;
}
.kx-sg-card::before {
content: '';
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 4px;
}
.kx-sg-card[data-accent='primary']::before { background: var(--color-primary-600); }
.kx-sg-card[data-accent='ai']::before { background: var(--color-ai-accent); }
.kx-sg-card[data-accent='success']::before { background: var(--color-success); }
.kx-sg-card__title {
font-size: var(--fs-h3);
color: var(--color-neutral-900);
margin-bottom: var(--space-4);
}
.kx-sg-card__note {
font-size: var(--fs-caption);
color: var(--color-neutral-500);
margin: -8px 0 var(--space-4);
}
/* 타이포 */
.kx-sg__type {
display: flex;
flex-direction: column;
gap: var(--space-2);
padding-bottom: var(--space-4);
border-bottom: var(--border-card);
margin-bottom: var(--space-4);
}
.kx-sg__t-display { font-size: var(--fs-display); line-height: var(--lh-display); font-weight: 700; color: var(--color-neutral-900); }
.kx-sg__t-headline { font-size: var(--fs-h1); line-height: var(--lh-h1); font-weight: 700; color: var(--color-neutral-900); }
.kx-sg__t-title { font-size: var(--fs-h3); font-weight: 600; color: var(--color-neutral-700); }
.kx-sg__t-body { font-size: var(--fs-body); color: var(--color-neutral-700); }
.kx-sg__swatches {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-3);
}
.kx-sg__swatch {
display: flex;
align-items: center;
gap: var(--space-2);
}
.kx-sg__swatch-chip {
width: 28px;
height: 28px;
border-radius: var(--radius-sm);
border: 1px solid rgba(16, 24, 40, 0.08);
flex-shrink: 0;
}
.kx-sg__swatch-name {
font-size: var(--fs-caption);
color: var(--color-neutral-700);
flex: 1;
}
.kx-sg__swatch-hex {
font-size: 11px;
color: var(--color-neutral-500);
}
/* AI 요소 */
.kx-sg__ai-row,
.kx-sg__btn-row,
.kx-sg__chip-row {
display: flex;
flex-wrap: wrap;
gap: var(--space-2);
align-items: center;
margin-bottom: var(--space-4);
}
.kx-sg__aiimg {
max-width: 320px;
margin-bottom: var(--space-4);
}
.kx-sg__violations {
display: flex;
gap: var(--space-3);
}
.kx-sg__viol {
font-size: var(--fs-caption);
font-weight: 600;
color: var(--color-error);
background: #fef3f2;
border-radius: var(--radius-sm);
padding: 4px 10px;
}
.kx-sg__icon-btn {
width: 36px;
height: 36px;
border: var(--border-card);
border-radius: var(--radius-sm);
background: var(--color-white);
color: var(--color-neutral-700);
cursor: pointer;
}
.kx-sg__icon-btn:hover {
background: var(--color-neutral-050);
}
/* 게이지·범례 */
.kx-sg__gauge {
margin-bottom: var(--space-4);
}
.kx-sg__legend {
list-style: none;
margin: 0;
padding: 0;
display: flex;
gap: var(--space-4);
font-size: var(--fs-caption);
color: var(--color-neutral-500);
}
.kx-sg__legend li {
display: flex;
align-items: center;
gap: 6px;
}
.kx-dot {
width: 10px;
height: 10px;
border-radius: 50%;
display: inline-block;
}
.kx-dot.is-smooth { background: #12b76a; }
.kx-dot.is-moderate { background: var(--color-violation-warn); }
.kx-dot.is-busy { background: var(--color-error); }
.kx-barlist__fill.is-warning { background: var(--color-violation-warn); }
/* 폼 */
.kx-sg__form {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: var(--space-4);
}
.kx-sg__field {
display: flex;
flex-direction: column;
gap: 6px;
font-size: var(--fs-caption);
color: var(--color-neutral-500);
}
.kx-sg__input {
height: 36px;
padding: 0 var(--space-3);
border: var(--border-card);
border-radius: var(--radius-sm);
font-size: var(--fs-body);
color: var(--color-neutral-900);
}
.kx-sg__input:focus {
outline: none;
border-color: var(--color-primary-600);
box-shadow: 0 0 0 3px rgba(0, 102, 179, 0.15);
}
.kx-sg__input.is-error {
border-color: var(--color-error);
}
.kx-sg__err-msg {
color: var(--color-error);
font-style: normal;
font-size: 11px;
}
.kx-sg__check-row {
grid-column: 1 / -1;
display: flex;
gap: var(--space-4);
}
.kx-sg__check {
display: flex;
align-items: center;
gap: 6px;
font-size: var(--fs-body);
color: var(--color-neutral-700);
}
/* 피드백 */
.kx-sg__toast {
display: inline-block;
background: var(--color-neutral-900);
color: var(--color-white);
font-size: var(--fs-body);
padding: 10px var(--space-4);
border-radius: var(--radius-sm);
margin-bottom: var(--space-3);
}
.kx-sg__alert {
background: #fef3f2;
color: var(--color-error);
font-size: var(--fs-body);
font-weight: 600;
padding: 10px var(--space-4);
border-radius: var(--radius-sm);
border: 1px solid rgba(217, 45, 32, 0.3);
margin-bottom: var(--space-4);
}
.kx-sg__states {
display: grid;
grid-template-columns: repeat(3, 1fr);
gap: var(--space-3);
}
.kx-sg__state-box {
border: var(--border-card);
border-radius: var(--radius-sm);
padding: var(--space-3);
display: flex;
flex-direction: column;
gap: var(--space-2);
min-height: 120px;
justify-content: center;
}
@media (max-width: 1000px) {
.kx-sg__grid,
.kx-sg__form,
.kx-sg__states {
grid-template-columns: 1fr;
}
}

View File

@ -0,0 +1,11 @@
<svg width="48" height="48" viewBox="0 0 48 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="8" y="14" width="32" height="24" rx="4" stroke="#6D4AFF" stroke-width="2.5"/>
<circle cx="18" cy="24" r="2" fill="#6D4AFF"/>
<circle cx="30" cy="24" r="2" fill="#6D4AFF"/>
<path d="M20 32C20 32 22 34 24 34C26 34 28 32 28 32" stroke="#6D4AFF" stroke-width="2" stroke-linecap="round"/>
<path d="M14 14V10C14 8.89543 14.8954 8 16 8H32C33.1046 8 34 8.89543 34 10V14" stroke="#6D4AFF" stroke-width="2"/>
<path d="M8 22H5C4.44772 22 4 22.4477 4 23V29C4 29.5523 4.44772 30 5 30H8" stroke="#6D4AFF" stroke-width="2"/>
<path x="40" y="22" d="M40 22H43C43.5523 22 44 22.4477 44 23V29C44 29.5523 43.5523 30 43 30H40" stroke="#6D4AFF" stroke-width="2"/>
<path d="M24 8V4" stroke="#6D4AFF" stroke-width="2" stroke-linecap="round"/>
<circle cx="24" cy="3" r="1.5" fill="#6D4AFF"/>
</svg>

After

Width:  |  Height:  |  Size: 896 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M18 20V10" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M12 20V4" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M6 20V14" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 425 B

View File

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="4" width="18" height="16" rx="2" stroke="currentColor" stroke-width="2"/>
<path d="M16 2V6" stroke="currentColor" stroke-width="2"/>
<path d="M8 2V6" stroke="currentColor" stroke-width="2"/>
<path d="M3 10H21" stroke="currentColor" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 376 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 580 B

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect x="3" y="3" width="18" height="18" rx="2" stroke="currentColor" stroke-width="2"/>
<path d="M3 9H21" stroke="currentColor" stroke-width="2"/>
<path d="M9 21V9" stroke="currentColor" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 547 B

View File

@ -0,0 +1,5 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M3 12H21" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M3 6H21" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M3 18H21" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 356 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

View File

@ -0,0 +1,6 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M2 12C2 6.47715 6.47715 2 12 2C17.5228 2 22 6.47715 22 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<path d="M5 12C5 8.13401 8.13401 5 12 5C15.866 5 19 8.13401 19 12" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
<circle cx="12" cy="12" r="3" stroke="currentColor" stroke-width="2"/>
<path d="M12 15V22" stroke="currentColor" stroke-width="2"/>
</svg>

After

Width:  |  Height:  |  Size: 505 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

View File

@ -0,0 +1,4 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg">
<circle cx="11" cy="11" r="7" stroke="currentColor" stroke-width="2"/>
<path d="M20 20L16 16" stroke="currentColor" stroke-width="2" stroke-linecap="round"/>
</svg>

After

Width:  |  Height:  |  Size: 264 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 953 B

View File

@ -1,13 +1,11 @@
<!DOCTYPE html> <!DOCTYPE html><html class="light" lang="en"><head></head><body class="bg-background text-on-background font-body-md overflow-x-hidden antialiased">```html
<html class="light" lang="en"><head> <meta charset="utf-8">
<meta charset="utf-8"/> <meta content="width=device-width, initial-scale=1.0" name="viewport">
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>KINTEX AI CMS - Dashboard</title> <title>KINTEX AI CMS - Dashboard</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script> <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;600;700&amp;family=JetBrains+Mono:wght@400&amp;display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;600;700&amp;family=JetBrains+Mono:wght@400&amp;display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script id="tailwind-config"> <script id="tailwind-config">
tailwind.config = { tailwind.config = {
darkMode: "class", darkMode: "class",
@ -179,34 +177,32 @@
font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24; font-variation-settings: 'FILL' 1, 'wght' 400, 'GRAD' 0, 'opsz' 24;
} }
</style> </style>
</head>
<body class="bg-background text-on-background font-body-md overflow-x-hidden antialiased">
<!-- SideNavBar --> <!-- SideNavBar -->
<nav class="hidden md:flex w-[240px] h-screen fixed left-0 top-0 bg-surface border-r border-surface-border flex-col py-stack-lg z-50"> <nav class="hidden md:flex w-[240px] h-screen fixed left-0 top-0 bg-surface border-r border-surface-border flex-col py-stack-lg z-50">
<div class="px-margin-desktop mb-stack-lg"> <div class="px-margin-desktop mb-stack-lg">
<h1 class="font-headline-md text-headline-md font-bold text-primary">KINTEX CMS</h1> <img src="https://lh3.googleusercontent.com/aida-public/AB6AXuDt1Xknrc1Y8U3nIV6ewYeyN8tXpFjPEsXVPB96F8oyD0Ah3VvX2GSupS4YD19zbqoex67mNuCmHKh4XtuF0urGZk3FAwwjZDIu6PDs4YcKuZmMEbRuQrZDXFIcuARq3eA4G5lWgNBvQ12RHvX34GnkFHz5in3XxBq2E7mjsqEc-6UdDr6Wsi_XzE-RDcgGdB9zh5lHTdkxw8DxI3cbqMh-w627ljBUdSFR_XYta_8dCP3-6Xmj4ens-w1ctxsa9RofvA" alt="KINTEX" class="h-8 mb-2">
<p class="font-body-sm text-body-sm text-on-surface-variant mt-1">Exhibition Management</p> <p class="font-body-sm text-body-sm text-on-surface-variant">Exhibition Management</p>
</div> </div>
<div class="flex-1 px-4 space-y-2"> <div class="flex-1 px-4 space-y-2">
<a class="flex items-center gap-3 px-4 py-3 rounded-lg bg-secondary-container text-on-secondary-container font-semibold transition-colors duration-200" href="#"> <a class="flex items-center gap-3 px-4 py-3 rounded-lg bg-secondary-container text-on-secondary-container font-semibold transition-colors duration-200" href="#">
<span class="material-symbols-outlined icon-fill">dashboard</span> <img src="https://lh3.googleusercontent.com/aida/AP1WRLtO2EjzKAcW5wn4YYTv12rmtFAvwxlN4fmvGGktwdLqfBGzTTC8C_nxjBMV0s73Yz_eqq3my6ASAxnQk9MMCNSeoTVuQwK9CU8en_ZU63PoTk6muzScTpKFEU2aFgRqpZAs3IBLHhlZXl4ENmWt1GO_lvpxzlx-FAVMFPeSHBfqi8lMEy8xvdRkiRB0MpVPgcEnBWn0SU0BsxlrRVjlVMlL3gAwpYNlLruSqKP5tDnBgDNwjHSKU9SUV04" class="w-6 h-6" alt="Dashboard">
Dashboard Dashboard
</a> </a>
<a class="flex items-center gap-3 px-4 py-3 rounded-lg text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200 hover:bg-surface-container-high active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-4 py-3 rounded-lg text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200 hover:bg-surface-container-high active:scale-[0.98]" href="#">
<span class="material-symbols-outlined">calendar_month</span> <img src="https://lh3.googleusercontent.com/aida/AP1WRLvpb_qiARCcrTrv6YnOIVjyk5szWwIfCGaaCmyRAnuu9aficeKfPTRrRHDgl4gJCHQUc7LYwtF1Ak04oSwBAezPs20C51ZjzHDaBCkRYJaYqHS-DOPHSGLdeLL_S2tlRlUAG-XF_ib2I1Svtqq26yY0uxmEQA5cWHJabFRv_0WVtBKiSP2QbCssxeccpcVHcWr_6CJ6zXonlepkxJcDXURlfHpvg7P2-O1urNto3A0xsepsB1ucua9Vnw" class="w-6 h-6" alt="Schedule">
Exhibition Schedule Exhibition Schedule
</a> </a>
<a class="flex items-center gap-3 px-4 py-3 rounded-lg text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200 hover:bg-surface-container-high active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-4 py-3 rounded-lg text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200 hover:bg-surface-container-high active:scale-[0.98]" href="#">
<span class="material-symbols-outlined">sensors</span> <img src="https://lh3.googleusercontent.com/aida/AP1WRLvz-lIs0s4a8Cy8eurZwY-dsuBDuvrLeIlIdHrd3y0aSi_u9kpOR3v59PRVX3Y1qP8SJ-459rs6AT8ZgXe7HMwRzZCOTLEpNNY1YqLtB8BBuVmnHOOVESaovK6ib38tM-qoAgpfO5Yxp-0ZCx3EwnULjy98lc_fpS8cQgxMEM-G3oAr71ToD4n-SEH55g1Kyz_ucSfAciJazI6fgyJbqOLgEAOJ-J-Bp1O30aTcbYVLFw3GRp7R06emYmc" class="w-6 h-6" alt="Operations">
Hall Operations Hall Operations
</a> </a>
<a class="flex items-center gap-3 px-4 py-3 rounded-lg text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200 hover:bg-surface-container-high active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-4 py-3 rounded-lg text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200 hover:bg-surface-container-high active:scale-[0.98]" href="#">
<span class="material-symbols-outlined">analytics</span> <img src="https://lh3.googleusercontent.com/aida/AP1WRLujD1FMRHChfM_aVKt2cd12chhcnm31L8Ks2EAzo-_FQ1_RV3zmYa4WUlSJ67wYexda4SpJU88Y5L0HdiYx6fLuKFdsDKjKWm1SmTq4PIP2tx1Nys-Txdlq4Wc1keg1GKsgrxNY68bHBEhurr-0NorgLbpp032gBkj_vE345bYKJwSr5MEnipvo_1P7iE9cXEBybLmdLLYK6vM1nB7dbmVt9_NHG3NQgK7wcPfIaNBrnXrEqtZeckWX3v8" class="w-6 h-6" alt="Analytics">
Business Intelligence Business Intelligence
</a> </a>
</div> </div>
<div class="px-4 mt-auto space-y-4"> <div class="px-4 mt-auto space-y-4">
<button class="w-full bg-primary text-on-primary py-2 rounded-lg font-label-md text-label-md hover:bg-primary-fixed-variant transition-colors"> <button class="w-full bg-primary text-on-primary py-2 rounded-lg font-label-md text-label-md hover:bg-primary-fixed-variant transition-colors flex items-center justify-center gap-2">
Create New Event Create New Event
</button> </button>
<div class="pt-4 border-t border-surface-border space-y-2"> <div class="pt-4 border-t border-surface-border space-y-2">
@ -238,8 +234,8 @@
</div> </div>
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<div class="relative"> <div class="relative">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-outline">search</span> <img alt="Search" class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 opacity-50" src="https://lh3.googleusercontent.com/aida/AP1WRLvR8KDDPFgH9vKk_aFJ_lA5WCSRmrDnPhJH5rSjuWilOAI5PMDe5z2hRnRLQhSn7_712ig52lC61Bb_R8bUkkbqNGZPv3dvykD_Oky9hXWygGnc1ybvaI9TcbT1Yw4Bd2JySNFbit4ho4113ql2BsDvpvJ2ZN64zJqv5GowDs0mdnDyFTwPWmGEKEPr3MigJ4LAaeegtd0fEG_pZWtGpsGSNPVdj4r_TbmkKSxdvmZG_kh-ujSkrXGZzkQ">
<input class="pl-10 pr-4 py-1.5 bg-surface-container-low border border-surface-border rounded-full text-body-sm font-body-sm focus:outline-none focus:border-primary w-64" placeholder="Search..." type="text"/> <input class="pl-10 pr-4 py-1.5 bg-surface-container-low border border-surface-border rounded-full text-body-sm font-body-sm focus:outline-none focus:border-primary w-64" placeholder="Search..." type="text">
</div> </div>
<button class="font-label-md text-label-md text-primary hover:bg-primary-container hover:text-on-primary-container px-4 py-1.5 rounded-full transition-colors border border-primary"> <button class="font-label-md text-label-md text-primary hover:bg-primary-container hover:text-on-primary-container px-4 py-1.5 rounded-full transition-colors border border-primary">
Refresh Data Refresh Data
@ -270,7 +266,7 @@
<div class="bg-surface-container-lowest border border-surface-border rounded-xl p-stack-md flex flex-col justify-between"> <div class="bg-surface-container-lowest border border-surface-border rounded-xl p-stack-md flex flex-col justify-between">
<div class="flex justify-between items-start mb-4"> <div class="flex justify-between items-start mb-4">
<div class="w-10 h-10 rounded-lg bg-primary-container text-on-primary-container flex items-center justify-center"> <div class="w-10 h-10 rounded-lg bg-primary-container text-on-primary-container flex items-center justify-center">
<span class="material-symbols-outlined">event</span> <img src="https://lh3.googleusercontent.com/aida/AP1WRLvpb_qiARCcrTrv6YnOIVjyk5szWwIfCGaaCmyRAnuu9aficeKfPTRrRHDgl4gJCHQUc7LYwtF1Ak04oSwBAezPs20C51ZjzHDaBCkRYJaYqHS-DOPHSGLdeLL_S2tlRlUAG-XF_ib2I1Svtqq26yY0uxmEQA5cWHJabFRv_0WVtBKiSP2QbCssxeccpcVHcWr_6CJ6zXonlepkxJcDXURlfHpvg7P2-O1urNto3A0xsepsB1ucua9Vnw" class="w-6 h-6" alt="Exhibitions">
</div> </div>
<span class="font-label-md text-label-md text-status-active bg-status-active/10 px-2 py-1 rounded-full">+2 this week</span> <span class="font-label-md text-label-md text-status-active bg-status-active/10 px-2 py-1 rounded-full">+2 this week</span>
</div> </div>
@ -298,7 +294,7 @@
<div class="w-10 h-10 rounded-lg bg-tertiary-container text-on-tertiary-container flex items-center justify-center"> <div class="w-10 h-10 rounded-lg bg-tertiary-container text-on-tertiary-container flex items-center justify-center">
<span class="material-symbols-outlined">local_parking</span> <span class="material-symbols-outlined">local_parking</span>
</div> </div>
<span class="font-label-md text-label-md text-status-pending bg-status-pending/10 px-2 py-1 rounded-full text-status-pending">High Capacity</span> <span class="font-label-md text-label-md text-status-pending bg-status-pending/10 px-2 py-1 rounded-full">High Capacity</span>
</div> </div>
<div> <div>
<p class="font-body-sm text-body-sm text-on-surface-variant mb-1">Parking Availability</p> <p class="font-body-sm text-body-sm text-on-surface-variant mb-1">Parking Availability</p>
@ -326,11 +322,11 @@
<div class="flex-1 w-full min-h-[300px] relative border-l border-b border-surface-border flex items-end"> <div class="flex-1 w-full min-h-[300px] relative border-l border-b border-surface-border flex items-end">
<!-- Y Axis Labels --> <!-- Y Axis Labels -->
<div class="absolute -left-10 top-0 bottom-0 flex flex-col justify-between text-[10px] text-outline font-mono-data py-2"> <div class="absolute -left-10 top-0 bottom-0 flex flex-col justify-between text-[10px] text-outline font-mono-data py-2">
<span>10k</span> <span class="">10k</span>
<span>7.5k</span> <span class="">7.5k</span>
<span>5k</span> <span class="">5k</span>
<span>2.5k</span> <span class="">2.5k</span>
<span>0</span> <span class="">0</span>
</div> </div>
<!-- Grid Lines --> <!-- Grid Lines -->
<div class="absolute inset-0 flex flex-col justify-between pointer-events-none"> <div class="absolute inset-0 flex flex-col justify-between pointer-events-none">
@ -351,12 +347,12 @@
</div> </div>
<!-- X Axis Labels --> <!-- X Axis Labels -->
<div class="absolute -bottom-6 left-0 right-0 flex justify-around text-[10px] text-outline font-mono-data"> <div class="absolute -bottom-6 left-0 right-0 flex justify-around text-[10px] text-outline font-mono-data">
<span>09:00</span> <span class="">09:00</span>
<span>11:00</span> <span class="">11:00</span>
<span>13:00</span> <span class="">13:00</span>
<span>15:00</span> <span class="">15:00</span>
<span>17:00</span> <span class="">17:00</span>
<span>19:00</span> <span class="">19:00</span>
</div> </div>
</div> </div>
</div> </div>
@ -369,7 +365,7 @@
<!-- Event Card 1 --> <!-- Event Card 1 -->
<div class="bg-surface-container-lowest border border-surface-border rounded-xl overflow-hidden flex shadow-sm hover:shadow-md transition-shadow"> <div class="bg-surface-container-lowest border border-surface-border rounded-xl overflow-hidden flex shadow-sm hover:shadow-md transition-shadow">
<div class="w-1/3 min-h-full bg-surface-container-high relative"> <div class="w-1/3 min-h-full bg-surface-container-high relative">
<img alt="" class="absolute inset-0 w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida/AP1WRLuTJllS4I1nbHXEBA6rTtfsEgDs7HSWU3ZvN5MWlygnyHWXik8qMDyqNOaYp1fxiUxdEage2cpiVCv0_WlwB6jBEGOm3NTCRWhCwtdjQXAiERgVwKPKww8mfCGKPBv9Cw7UfWBjRO6ztwOtcocqi3cP1Ei96BgMR5whcGWQljKEnJflf1BX4Sa_-aCGHSWGH5s6_NLff4U8Yd9mDkwZ26nHkcRkQQrD66lcPws86w1_YX-L6tg_6ywr7QI"/> <img alt="" class="absolute inset-0 w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida/AP1WRLuTJllS4I1nbHXEBA6rTtfsEgDs7HSWU3ZvN5MWlygnyHWXik8qMDyqNOaYp1fxiUxdEage2cpiVCv0_WlwB6jBEGOm3NTCRWhCwtdjQXAiERgVwKPKww8mfCGKPBv9Cw7UfWBjRO6ztwOtcocqi3cP1Ei96BgMR5whcGWQljKEnJflf1BX4Sa_-aCGHSWGH5s6_NLff4U8Yd9mDkwZ26nHkcRkQQrD66lcPws86w1_YX-L6tg_6ywr7QI">
</div> </div>
<div class="w-2/3 p-4 flex flex-col justify-center"> <div class="w-2/3 p-4 flex flex-col justify-center">
<div class="flex items-center gap-2 mb-1"> <div class="flex items-center gap-2 mb-1">
@ -387,7 +383,7 @@
<!-- Event Card 2 --> <!-- Event Card 2 -->
<div class="bg-surface-container-lowest border border-surface-border rounded-xl overflow-hidden flex shadow-sm hover:shadow-md transition-shadow"> <div class="bg-surface-container-lowest border border-surface-border rounded-xl overflow-hidden flex shadow-sm hover:shadow-md transition-shadow">
<div class="w-1/3 min-h-full bg-surface-container-high relative"> <div class="w-1/3 min-h-full bg-surface-container-high relative">
<img alt="" class="absolute inset-0 w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida/AP1WRLtsocUuOxtEm-alc6d65dh5o4U3u-CggqrYH7Xbqcimz9JL8VwWAOiQvKy3fhJ4rJsK0RBj89R5A_ooRyY4sij_Dq-GBy3CBBdSFBkuKHx5MFNM1UO7CtakB0nPIPbUIg_8oB-dW6Rq8BRQlMNvN6NY9ZzLHnDTT3elY1G4zqLMH2AuUsUg-iUYj0NQ0lkQuUisEVm5gkgEYZdfb3EaBNMfxQi0S7HSpPzdyzIurlON2jRTQyM7QDoCew0"/> <img alt="" class="absolute inset-0 w-full h-full object-cover" src="https://lh3.googleusercontent.com/aida/AP1WRLtsocUuOxtEm-alc6d65dh5o4U3u-CggqrYH7Xbqcimz9JL8VwWAOiQvKy3fhJ4rJsK0RBj89R5A_ooRyY4sij_Dq-GBy3CBBdSFBkuKHx5MFNM1UO7CtakB0nPIPbUIg_8oB-dW6Rq8BRQlMNvN6NY9ZzLHnDTT3elY1G4zqLMH2AuUsUg-iUYj0NQ0lkQuUisEVm5gkgEYZdfb3EaBNMfxQi0S7HSpPzdyzIurlON2jRTQyM7QDoCew0">
</div> </div>
<div class="w-2/3 p-4 flex flex-col justify-center"> <div class="w-2/3 p-4 flex flex-col justify-center">
<div class="flex items-center gap-2 mb-1"> <div class="flex items-center gap-2 mb-1">
@ -419,4 +415,16 @@
</div> </div>
</div> </div>
</main> </main>
<!-- Floating AI Bot -->
<button class="fixed bottom-8 right-8 w-14 h-14 bg-white border border-surface-border shadow-xl rounded-full flex items-center justify-center hover:scale-110 transition-transform z-50">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLu1SFz3uW8s6iC8BUAS9XvAFoM_7LQZUzR-UgorcHCSIZ_uRCF-ZTLQnQbNUG13WdRaQDX_3m0ewu6jxJ2Ie-gQvwFXyYdy3ZIxhkl0hJ2VGRZF6Iul1GgmSKzO5x92HASTCa0m0XzXjctyxy5aRgxOTOxhzl-Qz9P5jTlRzXngIWh6bCNZdaVEIxd9H6a2fDdToHYg0gowaU93SV2P5YbT0-8ShD0eeNiQEFHXn4HcsQUCrsiacEeGDZY" class="w-8 h-8" alt="AI Bot">
</button>
<!-- Mobile Menu Button -->
<button class="md:hidden fixed top-4 left-4 z-50 p-2 bg-surface rounded-lg border border-surface-border shadow-md">
<img alt="Menu" class="w-6 h-6" src="https://lh3.googleusercontent.com/aida/AP1WRLvsx4tVeyADsrv6nLlY0PM7oxWgkL9gXJ0QAXqJZmzqStfnij6yOt5LsMKGZwBBMOVo8pSaKZA3AcXICD_Wat3ye_0y8Fp-3vWdjl_yjP_RWt1Zo7a6t4YQ2QoPLOPAXZXdNG9hLc6_7NCbU5EWQBmHyra45kfx0UkIyEfS9HlE2b6STJw42QoX3kLCM1GamQm7IHd3pPK0mGmWAAMDUzwoXR7RQSRVCs1kjQUq5dvSD_fF-pPXppK8_g">
</button>
```
</body></html> </body></html>

View File

@ -7,7 +7,6 @@
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script> <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;600;700&amp;family=JetBrains+Mono:wght@400&amp;display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;600;700&amp;family=JetBrains+Mono:wght@400&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script id="tailwind-config"> <script id="tailwind-config">
tailwind.config = { tailwind.config = {
darkMode: "class", darkMode: "class",
@ -165,7 +164,7 @@
"lineHeight": "24px", "lineHeight": "24px",
"fontWeight": "400" "fontWeight": "400"
} }
} ]
} }
}, },
} }
@ -179,47 +178,47 @@
<!-- SideNavBar --> <!-- SideNavBar -->
<nav class="bg-surface dark:bg-surface text-primary dark:text-primary-fixed-dim font-body-md text-body-md w-[240px] h-screen fixed left-0 top-0 border-r border-surface-border dark:border-outline-variant hidden md:flex flex-col py-stack-lg z-50"> <nav class="bg-surface dark:bg-surface text-primary dark:text-primary-fixed-dim font-body-md text-body-md w-[240px] h-screen fixed left-0 top-0 border-r border-surface-border dark:border-outline-variant hidden md:flex flex-col py-stack-lg z-50">
<div class="px-gutter mb-stack-lg"> <div class="px-gutter mb-stack-lg">
<h1 class="font-headline-md text-headline-md font-bold text-primary dark:text-primary-fixed-dim">KINTEX CMS</h1> <img alt="KINTEX CMS" class="h-8 w-auto mb-1" src="https://lh3.googleusercontent.com/aida-public/AB6AXuBhJ6mSmO7gYB5yorAcPYJvt7ueEqWunfucG8e_Jzc46nmEHQEJcqyjcE2wwSqJt-k8X-VZTFokrecioWxwsTSoEv2NseKlClk3NQdHyKgSid4vB-ephP5sTysFaWD1sDIRyJzuiJQvndrbq8K_sNftZWG3WEmhDQJQ-9CMcL6XEE2lSAxyCcBuZqScKfMI_7Cpf1YY7Mz9-96Tv-GmSpIGm4nYsDCST_Xbo7x4QH4Y6ijQVnID3nQFi3DfVKhr_vuJVg"/>
<p class="font-body-sm text-body-sm text-on-surface-variant">Exhibition Management</p> <p class="font-body-sm text-body-sm text-on-surface-variant">Exhibition Management</p>
</div> </div>
<div class="px-gutter mb-stack-md"> <div class="px-gutter mb-stack-md">
<button class="w-full bg-primary-container text-on-primary-container py-2 rounded font-label-md text-label-md hover:opacity-90 transition-opacity">Create New Event</button> <button class="w-full bg-primary-container text-on-primary-container py-2 rounded font-label-md text-label-md hover:opacity-90 transition-opacity" style="background-color: rgb(59, 98, 173);">Create New Event</button>
</div> </div>
<ul class="flex-1 px-stack-sm flex flex-col gap-1"> <ul class="flex-1 px-stack-sm flex flex-col gap-1">
<li> <li class="">
<a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low dark:text-surface-variant transition-colors duration-200 active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low dark:text-surface-variant transition-colors duration-200 active:scale-[0.98]" href="#">
<span class="material-symbols-outlined text-lg">dashboard</span> <img alt="Dashboard" class="w-5 h-5 opacity-70" src="https://lh3.googleusercontent.com/aida/AP1WRLtO2EjzKAcW5wn4YYTv12rmtFAvwxlN4fmvGGktwdLqfBGzTTC8C_nxjBMV0s73Yz_eqq3my6ASAxnQk9MMCNSeoTVuQwK9CU8en_ZU63PoTk6muzScTpKFEU2aFgRqpZAs3IBLHhlZXl4ENmWt1GO_lvpxzlx-FAVMFPeSHBfqi8lMEy8xvdRkiRB0MpVPgcEnBWn0SU0BsxlrRVjlVMlL3gAwpYNlLruSqKP5tDnBgDNwjHSKU9SUV04"/>
Dashboard Dashboard
</a> </a>
</li> </li>
<li> <li class="">
<a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low dark:text-surface-variant transition-colors duration-200 active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low dark:text-surface-variant transition-colors duration-200 active:scale-[0.98]" href="#">
<span class="material-symbols-outlined text-lg">calendar_month</span> <img alt="Exhibition Schedule" class="w-5 h-5 opacity-70" src="https://lh3.googleusercontent.com/aida/AP1WRLvpb_qiARCcrTrv6YnOIVjyk5szWwIfCGaaCmyRAnuu9aficeKfPTRrRHDgl4gJCHQUc7LYwtF1Ak04oSwBAezPs20C51ZjzHDaBCkRYJaYqHS-DOPHSGLdeLL_S2tlRlUAG-XF_ib2I1Svtqq26yY0uxmEQA5cWHJabFRv_0WVtBKiSP2QbCssxeccpcVHcWr_6CJ6zXonlepkxJcDXURlfHpvg7P2-O1urNto3A0xsepsB1ucua9Vnw"/>
Exhibition Schedule Exhibition Schedule
</a> </a>
</li> </li>
<li> <li class="">
<a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low dark:text-surface-variant transition-colors duration-200 active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low dark:text-surface-variant transition-colors duration-200 active:scale-[0.98]" href="#">
<span class="material-symbols-outlined text-lg">sensors</span> <img alt="Hall Operations" class="w-5 h-5 opacity-70" src="https://lh3.googleusercontent.com/aida/AP1WRLvz-lIs0s4a8Cy8eurZwY-dsuBDuvrLeIlIdHrd3y0aSi_u9kpOR3v59PRVX3Y1qP8SJ-459rs6AT8ZgXe7HMwRzZCOTLEpNNY1YqLtB8BBuVmnHOOVESaovK6ib38tM-qoAgpfO5Yxp-0ZCx3EwnULjy98lc_fpS8cQgxMEM-G3oAr71ToD4n-SEH55g1Kyz_ucSfAciJazI6fgyJbqOLgEAOJ-J-Bp1O30aTcbYVLFw3GRp7R06emYmc"/>
Hall Operations Hall Operations
</a> </a>
</li> </li>
<li> <li class="">
<a class="flex items-center gap-3 px-3 py-2 rounded bg-secondary-container text-on-secondary-container dark:bg-primary-container dark:text-on-primary-container font-semibold transition-colors duration-200 active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-3 py-2 rounded bg-secondary-container text-on-secondary-container dark:bg-primary-container dark:text-on-primary-container font-semibold transition-colors duration-200 active:scale-[0.98]" href="#">
<span class="material-symbols-outlined text-lg">analytics</span> <img alt="Business Intelligence" class="w-5 h-5 brightness-0 invert" src="https://lh3.googleusercontent.com/aida/AP1WRLujD1FMRHChfM_aVKt2cd12chhcnm31L8Ks2EAzo-_FQ1_RV3zmYa4WUlSJ67wYexda4SpJU88Y5L0HdiYx6fLuKFdsDKjKWm1SmTq4PIP2tx1Nys-Txdlq4Wc1keg1GKsgrxNY68bHBEhurr-0NorgLbpp032gBkj_vE345bYKJwSr5MEnipvo_1P7iE9cXEBybLmdLLYK6vM1nB7dbmVt9_NHG3NQgK7wcPfIaNBrnXrEqtZeckWX3v8"/>
Business Intelligence Business Intelligence
</a> </a>
</li> </li>
</ul> </ul>
<div class="mt-auto px-stack-sm border-t border-surface-border pt-stack-md"> <div class="mt-auto px-stack-sm border-t border-surface-border pt-stack-md">
<ul class="flex flex-col gap-1"> <ul class="flex flex-col gap-1">
<li> <li class="">
<a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low dark:text-surface-variant transition-colors duration-200" href="#"> <a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low dark:text-surface-variant transition-colors duration-200" href="#">
<span class="material-symbols-outlined text-lg">settings</span> <span class="material-symbols-outlined text-lg">settings</span>
Settings Settings
</a> </a>
</li> </li>
<li> <li class="">
<a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low dark:text-surface-variant transition-colors duration-200" href="#"> <a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low dark:text-surface-variant transition-colors duration-200" href="#">
<span class="material-symbols-outlined text-lg">help</span> <span class="material-symbols-outlined text-lg">help</span>
Support Support
@ -243,7 +242,7 @@
</nav> </nav>
<div class="flex items-center gap-stack-md ml-auto"> <div class="flex items-center gap-stack-md ml-auto">
<div class="relative hidden sm:block"> <div class="relative hidden sm:block">
<span class="material-symbols-outlined absolute left-3 top-1/2 -translate-y-1/2 text-surface-dim">search</span> <img alt="Search" class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 opacity-50" src="https://lh3.googleusercontent.com/aida/AP1WRLvR8KDDPFgH9vKk_aFJ_lA5WCSRmrDnPhJH5rSjuWilOAI5PMDe5z2hRnRLQhSn7_712ig52lC61Bb_R8bUkkbqNGZPv3dvykD_Oky9hXWygGnc1ybvaI9TcbT1Yw4Bd2JySNFbit4ho4113ql2BsDvpvJ2ZN64zJqv5GowDs0mdnDyFTwPWmGEKEPr3MigJ4LAaeegtd0fEG_pZWtGpsGSNPVdj4r_TbmkKSxdvmZG_kh-ujSkrXGZzkQ"/>
<input class="pl-10 pr-4 py-1.5 rounded-full border border-surface-border bg-surface-bright focus:outline-none focus:border-primary-container text-sm w-48 lg:w-64" placeholder="Search reports..." type="text"/> <input class="pl-10 pr-4 py-1.5 rounded-full border border-surface-border bg-surface-bright focus:outline-none focus:border-primary-container text-sm w-48 lg:w-64" placeholder="Search reports..." type="text"/>
</div> </div>
<button class="text-primary hover:text-primary-container transition-colors cursor-pointer opacity-90 hover:opacity-100 flex items-center gap-2 border border-surface-border px-3 py-1.5 rounded"> <button class="text-primary hover:text-primary-container transition-colors cursor-pointer opacity-90 hover:opacity-100 flex items-center gap-2 border border-surface-border px-3 py-1.5 rounded">
@ -292,7 +291,7 @@
<div class="font-headline-lg text-headline-lg text-on-background">45,210</div> <div class="font-headline-lg text-headline-lg text-on-background">45,210</div>
<div class="flex items-center gap-1 mt-2 text-status-active font-body-sm text-body-sm"> <div class="flex items-center gap-1 mt-2 text-status-active font-body-sm text-body-sm">
<span class="material-symbols-outlined text-sm">trending_up</span> <span class="material-symbols-outlined text-sm">trending_up</span>
<span>+12.5% vs last event</span> <span class="">+12.5% vs last event</span>
</div> </div>
</div> </div>
<!-- Metric 2 --> <!-- Metric 2 -->
@ -304,19 +303,19 @@
<div class="font-headline-lg text-headline-lg text-on-background">4h 15m</div> <div class="font-headline-lg text-headline-lg text-on-background">4h 15m</div>
<div class="flex items-center gap-1 mt-2 text-status-active font-body-sm text-body-sm"> <div class="flex items-center gap-1 mt-2 text-status-active font-body-sm text-body-sm">
<span class="material-symbols-outlined text-sm">trending_up</span> <span class="material-symbols-outlined text-sm">trending_up</span>
<span>+45m vs last event</span> <span class="">+45m vs last event</span>
</div> </div>
</div> </div>
<!-- Metric 3 --> <!-- Metric 3 -->
<div class="bg-surface-container-lowest border border-surface-border rounded-lg p-stack-md flex flex-col justify-between"> <div class="bg-surface-container-lowest border border-surface-border rounded-lg p-stack-md flex flex-col justify-between">
<div class="flex justify-between items-start mb-2"> <div class="flex justify-between items-start mb-2">
<span class="font-label-md text-label-md text-on-surface-variant uppercase tracking-wider">Matching Success</span> <span class="font-label-md text-label-md text-on-surface-variant uppercase tracking-wider">Matching Success</span>
<span class="material-symbols-outlined text-primary-container">handshake</span> <img alt="AI Match" class="w-6 h-6" src="https://lh3.googleusercontent.com/aida/AP1WRLu1SFz3uW8s6iC8BUAS9XvAFoM_7LQZUzR-UgorcHCSIZ_uRCF-ZTLQnQbNUG13WdRaQDX_3m0ewu6jxJ2Ie-gQvwFXyYdy3ZIxhkl0hJ2VGRZF6Iul1GgmSKzO5x92HASTCa0m0XzXjctyxy5aRgxOTOxhzl-Qz9P5jTlRzXngIWh6bCNZdaVEIxd9H6a2fDdToHYg0gowaU93SV2P5YbT0-8ShD0eeNiQEFHXn4HcsQUCrsiacEeGDZY"/>
</div> </div>
<div class="font-headline-lg text-headline-lg text-on-background">87.3%</div> <div class="font-headline-lg text-headline-lg text-on-background">87.3%</div>
<div class="flex items-center gap-1 mt-2 text-on-surface-variant font-body-sm text-body-sm"> <div class="flex items-center gap-1 mt-2 text-on-surface-variant font-body-sm text-body-sm">
<span class="material-symbols-outlined text-sm">insights</span> <span class="material-symbols-outlined text-sm">insights</span>
<span>AI-driven matches</span> <span class="">AI-driven matches</span>
</div> </div>
</div> </div>
</div> </div>
@ -335,15 +334,18 @@
<div class="absolute inset-0 bg-[linear-gradient(to_right,#f0f1f2_1px,transparent_1px),linear-gradient(to_bottom,#f0f1f2_1px,transparent_1px)] bg-[size:2rem_2rem] opacity-50"></div> <div class="absolute inset-0 bg-[linear-gradient(to_right,#f0f1f2_1px,transparent_1px),linear-gradient(to_bottom,#f0f1f2_1px,transparent_1px)] bg-[size:2rem_2rem] opacity-50"></div>
<!-- Mock Chart Lines --> <!-- Mock Chart Lines -->
<svg class="absolute inset-0 w-full h-full" preserveaspectratio="none" viewbox="0 0 100 100"> <svg class="absolute inset-0 w-full h-full" preserveaspectratio="none" viewbox="0 0 100 100">
<path class="opacity-80" d="M0,80 Q20,60 40,70 T80,40 T100,20" fill="none" stroke="#0059A6" stroke-width="2"></path> <path class="opacity-80" d="M0,80 Q20,60 40,70 T80,40 T100,20" fill="none" stroke="#3b62ad" stroke-width="2"></path>
<path class="opacity-80" d="M0,90 Q30,70 50,80 T90,50 T100,60" fill="none" stroke="#6389B8" stroke-width="2"></path> <path class="opacity-80" d="M0,90 Q30,70 50,80 T90,50 T100,60" fill="none" stroke="#6389b8" stroke-width="2"></path>
</svg> </svg>
<div class="relative z-10 font-mono-data text-mono-data text-outline">Chart Visualization Area</div> <div class="relative z-10 font-mono-data text-mono-data text-outline">Chart Visualization Area</div>
</div> </div>
</div> </div>
<!-- Secondary Analysis Area --> <!-- Secondary Analysis Area -->
<div class="md:col-span-1 bg-surface-container-lowest border border-surface-border rounded-lg p-stack-md flex flex-col"> <div class="md:col-span-1 bg-surface-container-lowest border border-surface-border rounded-lg p-stack-md flex flex-col">
<h3 class="font-headline-md text-headline-md text-on-background mb-stack-md">Industry Trend Analysis</h3> <div class="flex items-center gap-2 mb-stack-md">
<h3 class="font-headline-md text-headline-md text-on-background">Industry Trend Analysis</h3>
<img alt="Analytics" class="w-5 h-5 opacity-50" src="https://lh3.googleusercontent.com/aida/AP1WRLujD1FMRHChfM_aVKt2cd12chhcnm31L8Ks2EAzo-_FQ1_RV3zmYa4WUlSJ67wYexda4SpJU88Y5L0HdiYx6fLuKFdsDKjKWm1SmTq4PIP2tx1Nys-Txdlq4Wc1keg1GKsgrxNY68bHBEhurr-0NorgLbpp032gBkj_vE345bYKJwSr5MEnipvo_1P7iE9cXEBybLmdLLYK6vM1nB7dbmVt9_NHG3NQgK7wcPfIaNBrnXrEqtZeckWX3v8"/>
</div>
<div class="flex-1 flex flex-col gap-4"> <div class="flex-1 flex flex-col gap-4">
<!-- Trend Item --> <!-- Trend Item -->
<div> <div>
@ -352,7 +354,7 @@
<span class="font-mono-data text-mono-data">45%</span> <span class="font-mono-data text-mono-data">45%</span>
</div> </div>
<div class="w-full bg-surface-container rounded-full h-2"> <div class="w-full bg-surface-container rounded-full h-2">
<div class="bg-primary-container h-2 rounded-full" style="width: 45%"></div> <div class="bg-primary-container h-2 rounded-full" style="width: 45%; background-color: rgb(59, 98, 173);"></div>
</div> </div>
</div> </div>
<!-- Trend Item --> <!-- Trend Item -->

View File

@ -0,0 +1,566 @@
<!DOCTYPE html>
<html class="light" lang="ko"><head>
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>KINTEX AI - UI Library</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Geist:wght@400;500;600&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script id="tailwind-config">
tailwind.config = {
darkMode: "class",
theme: {
extend: {
"colors": {
"tertiary": "#00583b",
"on-secondary-fixed-variant": "#4500d8",
"surface-bright": "#f9f9ff",
"secondary-container": "#6d4aff",
"on-secondary-container": "#f3edff",
"tertiary-fixed": "#8ef7c4",
"surface-container-high": "#e0e8ff",
"on-tertiary": "#ffffff",
"on-primary-fixed": "#001c38",
"on-tertiary-fixed": "#002113",
"primary-container": "#0066b3",
"surface": "#f9f9ff",
"error": "#ba1a1a",
"on-secondary": "#ffffff",
"on-primary-container": "#d2e3ff",
"inverse-surface": "#293041",
"on-error": "#ffffff",
"background": "#f9f9ff",
"surface-container-highest": "#dbe2f9",
"on-background": "#141b2c",
"surface-dim": "#d2daf0",
"on-primary-fixed-variant": "#004881",
"secondary-fixed-dim": "#c9bfff",
"secondary-fixed": "#e5deff",
"on-secondary-fixed": "#1b0063",
"secondary": "#5427e6",
"on-error-container": "#93000a",
"tertiary-fixed-dim": "#72daa9",
"surface-variant": "#dbe2f9",
"surface-container-lowest": "#ffffff",
"on-tertiary-container": "#8ef7c3",
"error-container": "#ffdad6",
"primary": "#004e8b",
"surface-tint": "#0060a9",
"on-surface-variant": "#414751",
"outline": "#717782",
"primary-fixed": "#d3e4ff",
"inverse-primary": "#a2c9ff",
"tertiary-container": "#00734e",
"on-surface": "#141b2c",
"on-primary": "#ffffff",
"primary-fixed-dim": "#a2c9ff",
"inverse-on-surface": "#edf0ff",
"surface-container-low": "#f1f3ff",
"surface-container": "#e9edff",
"outline-variant": "#c1c7d3",
"on-tertiary-fixed-variant": "#005236"
},
"borderRadius": {
"DEFAULT": "0.125rem",
"lg": "0.25rem",
"xl": "0.5rem",
"full": "0.75rem"
},
"spacing": {
"base": "4px",
"stack-sm": "8px",
"container-padding": "24px",
"stack-md": "16px",
"stack-lg": "24px",
"gutter": "16px"
},
"fontFamily": {
"label-xs": ["Geist"],
"body-base": ["Inter"],
"label-md": ["Geist"],
"headline-md": ["Inter"],
"display-lg": ["Inter"],
"body-sm": ["Inter"],
"title-sm": ["Inter"]
},
"fontSize": {
"label-xs": ["11px", {"lineHeight": "14px", "fontWeight": "500"}],
"body-base": ["14px", {"lineHeight": "20px", "fontWeight": "400"}],
"label-md": ["12px", {"lineHeight": "16px", "letterSpacing": "0.02em", "fontWeight": "600"}],
"headline-md": ["24px", {"lineHeight": "32px", "fontWeight": "600"}],
"display-lg": ["36px", {"lineHeight": "44px", "letterSpacing": "-0.02em", "fontWeight": "700"}],
"body-sm": ["13px", {"lineHeight": "18px", "fontWeight": "400"}],
"title-sm": ["18px", {"lineHeight": "28px", "fontWeight": "600"}]
}
},
},
}
</script>
<style>
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
.ai-glow-border {
border: 1px solid #6d4aff;
box-shadow: 0 0 8px rgba(109, 74, 255, 0.2);
}
.glass-panel {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(228, 231, 236, 1);
}
.bento-grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 16px;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f3ff;
}
::-webkit-scrollbar-thumb {
background: #c1c7d3;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #717782;
}
</style>
</head>
<body class="bg-background text-on-background font-body-base">
<!-- Sidebar Navigation Shell -->
<aside class="fixed left-0 top-0 h-screen w-60 bg-surface-container-low border-r border-outline-variant flex flex-col py-container-padding z-50">
<div class="px-6 mb-8">
<h1 class="text-headline-md font-headline-md font-bold text-primary">KINTEX AI</h1>
<p class="text-body-sm font-body-sm text-on-surface-variant">Exhibition Manager</p>
</div>
<nav class="flex-1 space-y-1">
<a class="flex items-center px-6 py-3 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">dashboard</span>
<span class="text-label-md font-label-md">Dashboard</span>
</a>
<a class="flex items-center px-6 py-3 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">architecture</span>
<span class="text-label-md font-label-md">Floor Plan Studio</span>
</a>
<a class="flex items-center px-6 py-3 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">groups</span>
<span class="text-label-md font-label-md">Exhibitor Management</span>
</a>
<!-- Active State -->
<a class="flex items-center px-6 py-3 space-x-3 text-primary font-bold border-r-4 border-primary bg-primary-container/10 transition-all duration-200" href="#">
<span class="material-symbols-outlined">library_books</span>
<span class="text-label-md font-label-md">Component Guide</span>
</a>
<a class="flex items-center px-6 py-3 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">payments</span>
<span class="text-label-md font-label-md">Settlement</span>
</a>
</nav>
<div class="mt-auto px-6 space-y-1">
<a class="flex items-center py-2 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">settings</span>
<span class="text-label-md font-label-md">Settings</span>
</a>
<a class="flex items-center py-2 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">help</span>
<span class="text-label-md font-label-md">Support</span>
</a>
</div>
</aside>
<!-- Top AppBar -->
<header class="fixed top-0 right-0 left-60 h-16 bg-surface border-b border-outline-variant flex justify-between items-center px-container-padding z-40">
<div class="flex items-center space-x-8">
<span class="text-title-sm font-title-sm font-bold text-on-surface">Exhibition Studio</span>
<nav class="flex space-x-6 h-full items-center">
<a class="text-on-surface-variant text-label-md font-label-md hover:text-primary transition-colors" href="#">Projects</a>
<a class="text-on-surface-variant text-label-md font-label-md hover:text-primary transition-colors" href="#">Analytics</a>
</nav>
</div>
<div class="flex items-center space-x-4">
<span class="bg-error text-white px-3 py-1 rounded-full text-label-md font-label-md">D-31</span>
<button class="material-symbols-outlined text-on-surface-variant hover:text-primary transition-transform hover:scale-105">notifications</button>
<div class="w-8 h-8 rounded-full bg-surface-container-highest flex items-center justify-center overflow-hidden border border-outline-variant">
<img class="w-full h-full object-cover" data-alt="A clean, professional headshot of a Korean business manager for an exhibition center. The lighting is corporate and bright, with a soft-focus office background. High-quality digital photography style with professional skin tones." src="https://lh3.googleusercontent.com/aida-public/AB6AXuDm1aqLEx_Q2mj7aBGn8DA4oloT45mA_qSrkmAAGaX62wMuKBSv2eMrcf20AMZ7p7dDRTGiKXpPQoxw26VmFhs45FhUD1OXJtxDwyOU81zKX-9m5AtZ3w9rre-ihdJiXLmZiVsc4n3hd-lzJ9ebzRmTjReE8wn1GH_tt3-XsqV-um2JjJFpPS-yNWeGo_e_RObu1a21sA5GKGXLg2FUSktNOcstFb1uYcRK0QfrgWmhXdpAgpiadHKe"/>
</div>
<button class="bg-primary text-on-primary px-4 py-2 rounded-lg text-label-md font-label-md hover:opacity-90 transition-all">Publish Layout</button>
</div>
</header>
<!-- Main Content Canvas -->
<main class="ml-60 mt-16 p-stack-lg min-h-screen">
<!-- Page Header -->
<div class="mb-stack-lg flex justify-between items-end">
<div>
<span class="text-label-md font-label-md text-primary tracking-widest uppercase">System Reference</span>
<h2 class="text-display-lg font-display-lg text-on-surface mt-1">Component Guide</h2>
<p class="text-body-sm font-body-sm text-on-surface-variant mt-2 max-w-2xl">
KINTEX AI 디자인 시스템 v1.0. 본 가이드는 고도의 신뢰성과 정밀한 정보 처리가 필요한 B2B 전시 환경을 위해 설계되었습니다.
</p>
</div>
<div class="text-right">
<span class="bg-secondary-container text-on-secondary-container px-3 py-1 rounded-lg text-label-xs font-label-xs">LATEST STABLE v1.0.4</span>
</div>
</div>
<div class="bento-grid">
<!-- SECTION: Typography & Colors -->
<section class="col-span-12 lg:col-span-8 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Typography &amp; Colors</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Typography -->
<div class="space-y-4">
<div class="border-b border-outline-variant pb-2 mb-4">
<span class="text-label-xs font-label-xs text-outline uppercase">Pretendard Type Scale</span>
</div>
<div class="space-y-4">
<div>
<p class="text-label-xs text-outline mb-1">Display Large (36px)</p>
<p class="text-display-lg font-display-lg">전시공간 최적화 엔진</p>
</div>
<div>
<p class="text-label-xs text-outline mb-1">Headline Medium (24px)</p>
<p class="text-headline-md font-headline-md">도면 분석 보고서</p>
</div>
<div>
<p class="text-label-xs text-outline mb-1">Title Small (18px)</p>
<p class="text-title-sm font-title-sm">신규 부스 배치 시뮬레이션</p>
</div>
<div>
<p class="text-label-xs text-outline mb-1">Body Base (14px)</p>
<p class="text-body-base font-body-base">전시 관리자는 실시간으로 변화하는 도면 데이터를 분석하여 최적의 동선을 설계합니다.</p>
</div>
</div>
</div>
<!-- Colors -->
<div class="space-y-4">
<div class="border-b border-outline-variant pb-2 mb-4">
<span class="text-label-xs font-label-xs text-outline uppercase">Primary Color Palette</span>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="p-3 bg-primary rounded-lg text-white">
<p class="text-label-md font-label-md">KINTEX Blue</p>
<p class="text-label-xs opacity-80">#004E8B</p>
</div>
<div class="p-3 bg-secondary-container rounded-lg text-on-secondary-container border border-secondary">
<p class="text-label-md font-label-md">AI Accent</p>
<p class="text-label-xs opacity-80">#6D4AFF</p>
</div>
<div class="p-3 bg-tertiary-container rounded-lg text-white">
<p class="text-label-md font-label-md">Success</p>
<p class="text-label-xs opacity-80">#00734E</p>
</div>
<div class="p-3 bg-error rounded-lg text-white">
<p class="text-label-md font-label-md">Error/Alert</p>
<p class="text-label-xs opacity-80">#BA1A1A</p>
</div>
<div class="p-3 bg-surface-container-low border border-outline-variant rounded-lg col-span-2">
<p class="text-label-md font-label-md">Neutral Low</p>
<p class="text-label-xs text-outline">#F1F3FF</p>
</div>
</div>
</div>
</div>
</section>
<!-- SECTION: AI Specific Elements -->
<section class="col-span-12 lg:col-span-4 bg-white border border-outline-variant p-stack-md rounded-lg overflow-hidden relative">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-secondary pl-2">
<h3 class="text-title-sm font-title-sm">AI Specific Elements</h3>
</div>
<div class="space-y-6">
<div class="ai-glow-border p-4 rounded-xl bg-surface-container-low flex flex-col gap-3">
<div class="flex items-center space-x-2">
<span class="material-symbols-outlined text-secondary" style="font-variation-settings: 'FILL' 1;">auto_awesome</span>
<span class="text-label-md font-label-md text-secondary">AI Drafted Component</span>
</div>
<div class="h-16 w-full bg-white/50 rounded border border-secondary/20 flex items-center justify-center relative overflow-hidden">
<!-- Simple repeating pattern for watermark demo -->
<div class="absolute inset-0 opacity-10 flex flex-wrap gap-4 text-[10px] font-bold rotate-12 pointer-events-none">
<span>AI GENERATED</span><span>AI GENERATED</span><span>AI GENERATED</span>
<span>AI GENERATED</span><span>AI GENERATED</span><span>AI GENERATED</span>
</div>
<p class="text-label-xs text-on-surface-variant z-10">워터마크 샘플 영역</p>
</div>
</div>
<div class="space-y-2">
<p class="text-label-xs text-outline">Violation Flag Pins (Safety)</p>
<div class="flex items-center space-x-4">
<div class="flex flex-col items-center">
<span class="material-symbols-outlined text-error text-[32px]" style="font-variation-settings: 'FILL' 1;">location_on</span>
<span class="text-label-xs text-error font-bold">Fire Exit Block</span>
</div>
<div class="flex flex-col items-center">
<span class="material-symbols-outlined text-error text-[32px]" style="font-variation-settings: 'FILL' 1;">warning</span>
<span class="text-label-xs text-error font-bold">Overload</span>
</div>
</div>
</div>
</div>
<!-- Background Subtle AI Decoration -->
<div class="absolute -right-4 -bottom-4 opacity-5">
<span class="material-symbols-outlined text-[120px]">smart_toy</span>
</div>
</section>
<!-- SECTION: Buttons & Actions -->
<section class="col-span-12 lg:col-span-6 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Buttons &amp; Actions</h3>
</div>
<div class="grid grid-cols-2 gap-6">
<div class="space-y-4">
<button class="w-full bg-primary text-white py-3 rounded-lg text-label-md font-bold transition-all hover:shadow-lg active:scale-95">Primary Button</button>
<button class="w-full border border-primary text-primary py-3 rounded-lg text-label-md font-bold transition-all hover:bg-primary/5 active:scale-95">Secondary Outlined</button>
<button class="w-full text-on-surface-variant py-3 rounded-lg text-label-md font-bold hover:bg-surface-container transition-colors">Ghost Action</button>
</div>
<div class="space-y-4">
<button class="w-full bg-secondary text-white py-3 rounded-lg text-label-md font-bold flex items-center justify-center space-x-2 shadow-md hover:opacity-90 active:scale-95">
<span class="material-symbols-outlined text-[18px]" style="font-variation-settings: 'FILL' 1;">auto_awesome</span>
<span>AI 제안 레이아웃</span>
</button>
<div class="relative group">
<button class="w-full bg-surface-container-highest border border-outline-variant text-on-surface py-3 rounded-lg text-label-md font-bold opacity-50 cursor-not-allowed">Disabled State</button>
</div>
<div class="flex space-x-2">
<button class="flex-1 bg-surface-container-high p-3 rounded-lg flex items-center justify-center transition-transform hover:-translate-y-1">
<span class="material-symbols-outlined">edit</span>
</button>
<button class="flex-1 bg-surface-container-high p-3 rounded-lg flex items-center justify-center transition-transform hover:-translate-y-1">
<span class="material-symbols-outlined">delete</span>
</button>
<button class="flex-1 bg-surface-container-high p-3 rounded-lg flex items-center justify-center transition-transform hover:-translate-y-1">
<span class="material-symbols-outlined">share</span>
</button>
</div>
</div>
</div>
</section>
<!-- SECTION: Status Badges & Chips -->
<section class="col-span-12 lg:col-span-6 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Status Badges &amp; Chips</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="space-y-4">
<p class="text-label-xs text-outline uppercase border-b border-outline-variant pb-1">Lifecycle Status</p>
<div class="flex flex-wrap gap-2">
<span class="px-3 py-1 bg-surface-container-highest text-on-surface-variant text-label-xs font-bold rounded border-l-4 border-outline">작성중</span>
<span class="px-3 py-1 bg-primary-container/20 text-primary text-label-xs font-bold rounded border-l-4 border-primary">제출됨</span>
<span class="px-3 py-1 bg-secondary-container/20 text-secondary text-label-xs font-bold rounded border-l-4 border-secondary">AI검토</span>
<span class="px-3 py-1 bg-tertiary-container/20 text-tertiary text-label-xs font-bold rounded border-l-4 border-tertiary">승인</span>
<span class="px-3 py-1 bg-error-container text-error text-label-xs font-bold rounded border-l-4 border-error">반려</span>
</div>
<div class="flex flex-wrap gap-2">
<span class="px-3 py-1 bg-secondary text-white text-label-xs font-bold rounded">시공중</span>
<span class="px-3 py-1 bg-tertiary text-white text-label-xs font-bold rounded">검수완료</span>
</div>
</div>
<div class="space-y-4">
<p class="text-label-xs text-outline uppercase border-b border-outline-variant pb-1">Contextual Chips</p>
<div class="flex flex-wrap gap-2">
<span class="bg-error text-white px-2 py-1 rounded text-label-xs font-bold flex items-center">
<span class="material-symbols-outlined text-[14px] mr-1">alarm</span> D-3
</span>
<span class="bg-primary text-white px-2 py-1 rounded text-label-xs font-bold flex items-center">
<span class="material-symbols-outlined text-[14px] mr-1">event</span> 10/24
</span>
<span class="bg-surface-container text-on-surface-variant px-2 py-1 rounded text-label-xs border border-outline-variant">Hall 1A</span>
<span class="bg-secondary-container text-on-secondary-container px-2 py-1 rounded text-label-xs font-bold ring-1 ring-secondary/20">High Traffic</span>
</div>
</div>
</div>
</section>
<!-- SECTION: Data Visualization -->
<section class="col-span-12 lg:col-span-12 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Data Visualization Components</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- Gauge Example -->
<div class="bg-surface-container-low p-4 rounded-lg">
<div class="flex justify-between items-center mb-4">
<span class="text-label-md font-bold">전시장 점유율</span>
<span class="text-primary font-bold">78%</span>
</div>
<div class="w-full h-3 bg-surface-container-highest rounded-full overflow-hidden">
<div class="h-full bg-primary rounded-full transition-all duration-1000" style="width: 78%;"></div>
</div>
<div class="flex justify-between mt-2 text-label-xs text-on-surface-variant">
<span>0%</span>
<span>임계치 85%</span>
<span>100%</span>
</div>
</div>
<!-- Heatmap Legend -->
<div class="bg-surface-container-low p-4 rounded-lg">
<p class="text-label-md font-bold mb-4">혼잡도 히트맵 범례</p>
<div class="flex items-center space-x-2 mb-2">
<div class="w-4 h-4 bg-[#D92D20] rounded-sm"></div>
<span class="text-label-xs">매우 혼잡 (Bottle Neck)</span>
</div>
<div class="flex items-center space-x-2 mb-2">
<div class="w-4 h-4 bg-[#F79009] rounded-sm"></div>
<span class="text-label-xs">다소 혼잡</span>
</div>
<div class="flex items-center space-x-2">
<div class="w-4 h-4 bg-[#12B76A] rounded-sm"></div>
<span class="text-label-xs">원활 (Optimized)</span>
</div>
</div>
<!-- Trend Small Chart Placeholder -->
<div class="bg-surface-container-low p-4 rounded-lg flex flex-col justify-between">
<p class="text-label-md font-bold mb-2">방문객 추이 (Weekly)</p>
<div class="flex items-end space-x-1 h-12">
<div class="flex-1 bg-primary/20 rounded-t h-[40%]"></div>
<div class="flex-1 bg-primary/20 rounded-t h-[60%]"></div>
<div class="flex-1 bg-primary/20 rounded-t h-[30%]"></div>
<div class="flex-1 bg-primary rounded-t h-[80%]"></div>
<div class="flex-1 bg-primary/20 rounded-t h-[50%]"></div>
<div class="flex-1 bg-primary/20 rounded-t h-[90%]"></div>
<div class="flex-1 bg-primary/20 rounded-t h-[45%]"></div>
</div>
<div class="flex justify-between text-[10px] text-on-surface-variant mt-2">
<span>Mon</span><span>Wed</span><span>Sun</span>
</div>
</div>
</div>
</section>
<!-- SECTION: Form Elements -->
<section class="col-span-12 lg:col-span-7 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Form Elements</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-4">
<div>
<label class="block text-label-xs font-bold text-on-surface-variant mb-1">Standard Input</label>
<input class="w-full border border-outline-variant rounded p-3 text-body-sm focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none transition-all" placeholder="전시회 명칭 입력" type="text"/>
</div>
<div>
<label class="block text-label-xs font-bold text-on-surface-variant mb-1">Active / Focus</label>
<input class="w-full border border-primary ring-2 ring-primary/20 rounded p-3 text-body-sm outline-none" type="text" value="KINTEX Exhibition Studio"/>
</div>
<div>
<label class="block text-label-xs font-bold text-error mb-1">Error State</label>
<input class="w-full border border-error rounded p-3 text-body-sm focus:ring-2 focus:ring-error/20 outline-none" type="text" value="Invalid Location"/>
<p class="text-label-xs text-error mt-1">올바른 위치 정보를 입력하세요.</p>
</div>
</div>
<div class="space-y-6">
<div>
<label class="block text-label-xs font-bold text-on-surface-variant mb-2">Dropdown Selector</label>
<select class="w-full border border-outline-variant rounded p-3 text-body-sm focus:border-primary outline-none appearance-none bg-no-repeat bg-[right_1rem_center] bg-[url('https://api.iconify.design/material-symbols:expand-more.svg')]">
<option>전시홀 선택 (Hall 1~5)</option>
<option>Hall 1A</option>
<option>Hall 2B</option>
</select>
</div>
<div class="flex items-center space-x-8">
<div class="space-y-2">
<label class="flex items-center space-x-2 cursor-pointer group">
<input checked="" class="w-4 h-4 rounded text-primary focus:ring-primary border-outline-variant" type="checkbox"/>
<span class="text-body-sm">AI 최적화 자동 적용</span>
</label>
<label class="flex items-center space-x-2 cursor-pointer group">
<input class="w-4 h-4 rounded text-primary focus:ring-primary border-outline-variant" type="checkbox"/>
<span class="text-body-sm">비공개 전환</span>
</label>
</div>
<div class="space-y-2">
<label class="flex items-center space-x-2 cursor-pointer group">
<input checked="" class="w-4 h-4 text-primary focus:ring-primary border-outline-variant" name="perm" type="radio"/>
<span class="text-body-sm">전체 공개</span>
</label>
<label class="flex items-center space-x-2 cursor-pointer group">
<input class="w-4 h-4 text-primary focus:ring-primary border-outline-variant" name="perm" type="radio"/>
<span class="text-body-sm">관리자만</span>
</label>
</div>
</div>
</div>
</div>
</section>
<!-- SECTION: Feedback & Overlays -->
<section class="col-span-12 lg:col-span-5 bg-white border border-outline-variant p-stack-md rounded-lg flex flex-col">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Feedback &amp; Overlays</h3>
</div>
<div class="space-y-6 flex-1">
<!-- Toast Notification -->
<div class="flex items-center justify-between p-4 bg-inverse-surface text-inverse-on-surface rounded-lg shadow-lg">
<div class="flex items-center space-x-3">
<span class="material-symbols-outlined text-[#12B76A]">check_circle</span>
<span class="text-body-sm font-medium">데이터가 성공적으로 저장되었습니다.</span>
</div>
<button class="material-symbols-outlined text-[20px] opacity-60">close</button>
</div>
<!-- Alert Message -->
<div class="flex items-start space-x-3 p-4 bg-error-container text-on-error-container rounded-lg border border-error/20">
<span class="material-symbols-outlined">error</span>
<div>
<p class="text-label-md font-bold">중요 알림</p>
<p class="text-body-sm">현재 설계된 도면에 소방법 위반 구역이 3곳 발견되었습니다. 수정을 권장합니다.</p>
</div>
</div>
<!-- Micro Modal Preview -->
<div class="border border-outline-variant rounded-lg p-6 bg-surface relative overflow-hidden">
<div class="absolute top-0 left-0 w-full h-1 bg-primary"></div>
<h4 class="text-title-sm font-bold mb-2">변경사항 적용</h4>
<p class="text-body-sm text-on-surface-variant mb-6">수정된 레이아웃을 서버에 동기화하시겠습니까? 이 작업은 되돌릴 수 없습니다.</p>
<div class="flex justify-end space-x-3">
<button class="px-4 py-2 text-label-md font-bold text-on-surface-variant">취소</button>
<button class="px-4 py-2 bg-primary text-white rounded text-label-md font-bold">확인</button>
</div>
</div>
</div>
</section>
</div>
<!-- Footer Info -->
<footer class="mt-stack-lg border-t border-outline-variant pt-8 pb-12 flex flex-col md:flex-row justify-between items-center text-on-surface-variant">
<div class="flex items-center space-x-4 mb-4 md:mb-0">
<h2 class="text-headline-md font-bold text-primary opacity-50">KINTEX AI</h2>
<div class="h-4 w-[1px] bg-outline-variant"></div>
<p class="text-label-md">© 2024 KINTEX AI System. All Rights Reserved.</p>
</div>
<div class="flex space-x-6">
<a class="text-label-md hover:text-primary transition-colors" href="#">Documentation</a>
<a class="text-label-md hover:text-primary transition-colors" href="#">Changelog</a>
<a class="text-label-md hover:text-primary transition-colors" href="#">Style Guide PDF</a>
</div>
</footer>
</main>
<!-- Floating Action Button Contextual Mock -->
<div class="fixed bottom-8 right-8 flex flex-col space-y-4">
<button class="w-14 h-14 bg-secondary text-white rounded-full flex items-center justify-center shadow-xl hover:scale-110 transition-transform group relative">
<span class="material-symbols-outlined text-[28px]" style="font-variation-settings: 'FILL' 1;">smart_toy</span>
<div class="absolute right-16 bg-white border border-outline-variant px-3 py-1 rounded shadow-md opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap text-on-surface text-label-md pointer-events-none">
AI 어시스턴트 호출
</div>
</button>
</div>
<script>
// Simple micro-interactions for the demo
document.querySelectorAll('button').forEach(button => {
button.addEventListener('mousedown', () => {
button.classList.add('scale-95');
});
button.addEventListener('mouseup', () => {
button.classList.remove('scale-95');
});
button.addEventListener('mouseleave', () => {
button.classList.remove('scale-95');
});
});
// Simulating some loading state on the gauge for visual interest
const gauge = document.querySelector('.bg-primary.rounded-full');
if (gauge) {
setTimeout(() => {
gauge.style.width = '78%';
}, 300);
}
</script>
</body></html>

View File

@ -0,0 +1,616 @@
<!DOCTYPE html><html class="light" lang="ko"><head>
<meta charset="utf-8">
<meta content="width=device-width, initial-scale=1.0" name="viewport">
<title>KINTEX AI - UI Library</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&amp;family=Geist:wght@400;500;600&amp;display=swap" rel="stylesheet">
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet">
<script id="tailwind-config">
tailwind.config = {
darkMode: "class",
theme: {
extend: {
"colors": {
"tertiary": "#00583b",
"on-secondary-fixed-variant": "#4500d8",
"surface-bright": "#f9f9ff",
"secondary-container": "#6d4aff",
"on-secondary-container": "#f3edff",
"tertiary-fixed": "#8ef7c4",
"surface-container-high": "#e0e8ff",
"on-tertiary": "#ffffff",
"on-primary-fixed": "#001c38",
"on-tertiary-fixed": "#002113",
"primary-container": "#0066b3",
"surface": "#f9f9ff",
"error": "#ba1a1a",
"on-secondary": "#ffffff",
"on-primary-container": "#d2e3ff",
"inverse-surface": "#293041",
"on-error": "#ffffff",
"background": "#f9f9ff",
"surface-container-highest": "#dbe2f9",
"on-background": "#141b2c",
"surface-dim": "#d2daf0",
"on-primary-fixed-variant": "#004881",
"secondary-fixed-dim": "#c9bfff",
"secondary-fixed": "#e5deff",
"on-secondary-fixed": "#1b0063",
"secondary": "#5427e6",
"on-error-container": "#93000a",
"tertiary-fixed-dim": "#72daa9",
"surface-variant": "#dbe2f9",
"surface-container-lowest": "#ffffff",
"on-tertiary-container": "#8ef7c3",
"error-container": "#ffdad6",
"primary": "#004e8b",
"surface-tint": "#0060a9",
"on-surface-variant": "#414751",
"outline": "#717782",
"primary-fixed": "#d3e4ff",
"inverse-primary": "#a2c9ff",
"tertiary-container": "#00734e",
"on-surface": "#141b2c",
"on-primary": "#ffffff",
"primary-fixed-dim": "#a2c9ff",
"inverse-on-surface": "#edf0ff",
"surface-container-low": "#f1f3ff",
"surface-container": "#e9edff",
"outline-variant": "#c1c7d3",
"on-tertiary-fixed-variant": "#005236"
},
"borderRadius": {
"DEFAULT": "0.125rem",
"lg": "0.25rem",
"xl": "0.5rem",
"full": "0.75rem"
},
"spacing": {
"base": "4px",
"stack-sm": "8px",
"container-padding": "24px",
"stack-md": "16px",
"stack-lg": "24px",
"gutter": "16px"
},
"fontFamily": {
"label-xs": ["Geist"],
"body-base": ["Inter"],
"label-md": ["Geist"],
"headline-md": ["Inter"],
"display-lg": ["Inter"],
"body-sm": ["Inter"],
"title-sm": ["Inter"]
},
"fontSize": {
"label-xs": ["11px", {"lineHeight": "14px", "fontWeight": "500"}],
"body-base": ["14px", {"lineHeight": "20px", "fontWeight": "400"}],
"label-md": ["12px", {"lineHeight": "16px", "letterSpacing": "0.02em", "fontWeight": "600"}],
"headline-md": ["24px", {"lineHeight": "32px", "fontWeight": "600"}],
"display-lg": ["36px", {"lineHeight": "44px", "letterSpacing": "-0.02em", "fontWeight": "700"}],
"body-sm": ["13px", {"lineHeight": "18px", "fontWeight": "400"}],
"title-sm": ["18px", {"lineHeight": "28px", "fontWeight": "600"}]
}
},
},
}
</script>
<style>
.material-symbols-outlined {
font-variation-settings: 'FILL' 0, 'wght' 400, 'GRAD' 0, 'opsz' 24;
}
.ai-glow-border {
border: 1px solid #6d4aff;
box-shadow: 0 0 8px rgba(109, 74, 255, 0.2);
}
.glass-panel {
background: rgba(255, 255, 255, 0.7);
backdrop-filter: blur(10px);
border: 1px solid rgba(228, 231, 236, 1);
}
.bento-grid {
display: grid;
grid-template-columns: repeat(12, 1fr);
gap: 16px;
}
::-webkit-scrollbar {
width: 8px;
}
::-webkit-scrollbar-track {
background: #f1f3ff;
}
::-webkit-scrollbar-thumb {
background: #c1c7d3;
border-radius: 4px;
}
::-webkit-scrollbar-thumb:hover {
background: #717782;
}
.custom-icon {
width: 24px;
height: 24px;
display: flex;
align-items: center;
justify-content: center;
}
.custom-icon svg {
width: 100%;
height: 100%;
}
</style>
</head>
<body class="bg-background text-on-background font-body-base">
<!-- Sidebar Navigation Shell -->
<aside class="fixed left-0 top-0 h-screen w-60 bg-surface-container-low border-r border-outline-variant flex flex-col py-container-padding z-50">
<div class="px-6 mb-8">
<img src="https://lh3.googleusercontent.com/aida-public/AB6AXuABJ1mCYGO3z2dV9mQV-FaWxWJkEPOmozrzGBFt0wLzcQLPA0fbz-d9pe5rSZ2k33_XwOOkH1Kc09XDe9rGEbM22V-18TZFJRlPs2nLV-GxuNJji0R003uiiM-1IS1MP7d-APBUu8lLC0xFRJAgWH8psQ80pgIGH0MZgECicjnoGf77rWkwEacmkt_nB631knQLF5mt06xSoSQHI-kHPPLp1kAXIh5vL5YqEW3fE317ieDuyp5joPTG5Ygp6PKxvs7D1A" class="h-8" alt="KINTEX AI">
<p class="text-body-sm font-body-sm text-on-surface-variant">Exhibition Manager</p>
</div>
<nav class="flex-1 space-y-1">
<a class="flex items-center px-6 py-3 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="custom-icon">https://lh3.googleusercontent.com/aida/AP1WRLtO2EjzKAcW5wn4YYTv12rmtFAvwxlN4fmvGGktwdLqfBGzTTC8C_nxjBMV0s73Yz_eqq3my6ASAxnQk9MMCNSeoTVuQwK9CU8en_ZU63PoTk6muzScTpKFEU2aFgRqpZAs3IBLHhlZXl4ENmWt1GO_lvpxzlx-FAVMFPeSHBfqi8lMEy8xvdRkiRB0MpVPgcEnBWn0SU0BsxlrRVjlVMlL3gAwpYNlLruSqKP5tDnBgDNwjHSKU9SUV04</span>
<span class="text-label-md font-label-md">Dashboard</span>
</a>
<a class="flex items-center px-6 py-3 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">architecture</span>
<span class="text-label-md font-label-md">Floor Plan Studio</span>
</a>
<a class="flex items-center px-6 py-3 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">groups</span>
<span class="text-label-md font-label-md">Exhibitor Management</span>
</a>
<!-- Active State -->
<a class="flex items-center px-6 py-3 space-x-3 text-primary font-bold border-r-4 border-primary bg-primary-container/10 transition-all duration-200" href="#">
<span class="material-symbols-outlined">library_books</span>
<span class="text-label-md font-label-md">Component Guide</span>
</a>
<a class="flex items-center px-6 py-3 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">payments</span>
<span class="text-label-md font-label-md">Settlement</span>
</a>
</nav>
<div class="mt-auto px-6 space-y-1">
<a class="flex items-center py-2 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">settings</span>
<span class="text-label-md font-label-md">Settings</span>
</a>
<a class="flex items-center py-2 space-x-3 text-on-surface-variant hover:bg-surface-container-high transition-colors" href="#">
<span class="material-symbols-outlined">help</span>
<span class="text-label-md font-label-md">Support</span>
</a>
</div>
</aside>
<!-- Top AppBar -->
<header class="fixed top-0 right-0 left-60 h-16 bg-surface border-b border-outline-variant flex justify-between items-center px-container-padding z-40">
<div class="flex items-center space-x-8">
<span class="text-title-sm font-title-sm font-bold text-on-surface">Exhibition Studio</span>
<nav class="flex space-x-6 h-full items-center">
<a class="text-on-surface-variant text-label-md font-label-md hover:text-primary transition-colors" href="#">Projects</a>
<a class="text-on-surface-variant text-label-md font-label-md hover:text-primary transition-colors flex items-center space-x-1" href="#">
<span class="">Analytics</span>
</a>
</nav>
</div>
<div class="flex items-center space-x-4">
<span class="bg-error text-white px-3 py-1 rounded-full text-label-md font-label-md">D-31</span>
<button class="material-symbols-outlined text-on-surface-variant hover:text-primary transition-transform hover:scale-105">notifications</button>
<div class="w-8 h-8 rounded-full bg-surface-container-highest flex items-center justify-center overflow-hidden border border-outline-variant">
<img class="w-full h-full object-cover" data-alt="A clean, professional headshot of a Korean business manager for an exhibition center. The lighting is corporate and bright, with a soft-focus office background. High-quality digital photography style with professional skin tones." src="https://lh3.googleusercontent.com/aida-public/AB6AXuDm1aqLEx_Q2mj7aBGn8DA4oloT45mA_qSrkmAAGaX62wMuKBSv2eMrcf20AMZ7p7dDRTGiKXpPQoxw26VmFhs45FhUD1OXJtxDwyOU81zKX-9m5AtZ3w9rre-ihdJiXLmZiVsc4n3hd-lzJ9ebzRmTjReE8wn1GH_tt3-XsqV-um2JjJFpPS-yNWeGo_e_RObu1a21sA5GKGXLg2FUSktNOcstFb1uYcRK0QfrgWmhXdpAgpiadHKe">
</div>
<button class="bg-primary text-on-primary px-4 py-2 rounded-lg text-label-md font-label-md hover:opacity-90 transition-all">Publish Layout</button>
</div>
</header>
<!-- Main Content Canvas -->
<main class="ml-60 mt-16 p-stack-lg min-h-screen">
<!-- Page Header -->
<div class="mb-stack-lg flex justify-between items-end">
<div>
<span class="text-label-md font-label-md text-primary tracking-widest uppercase">System Reference</span>
<h2 class="text-display-lg font-display-lg text-on-surface mt-1">Component Guide</h2>
<p class="text-body-sm font-body-sm text-on-surface-variant mt-2 max-w-2xl">
KINTEX AI 디자인 시스템 v1.0. 본 가이드는 고도의 신뢰성과 정밀한 정보 처리가 필요한 B2B 전시 환경을 위해 설계되었습니다.
</p>
</div>
<div class="text-right">
<span class="bg-secondary-container text-on-secondary-container px-3 py-1 rounded-lg text-label-xs font-label-xs">LATEST STABLE v1.0.4</span>
</div>
</div>
<div class="bento-grid">
<!-- SECTION: Typography & Colors -->
<section class="col-span-12 lg:col-span-8 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Typography &amp; Colors</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Typography -->
<div class="space-y-4">
<div class="border-b border-outline-variant pb-2 mb-4">
<span class="text-label-xs font-label-xs text-outline uppercase">Pretendard Type Scale</span>
</div>
<div class="space-y-4">
<div>
<p class="text-label-xs text-outline mb-1">Display Large (36px)</p>
<p class="text-display-lg font-display-lg">전시공간 최적화 엔진</p>
</div>
<div>
<p class="text-label-xs text-outline mb-1">Headline Medium (24px)</p>
<p class="text-headline-md font-headline-md">도면 분석 보고서</p>
</div>
<div>
<p class="text-label-xs text-outline mb-1">Title Small (18px)</p>
<p class="text-title-sm font-title-sm">신규 부스 배치 시뮬레이션</p>
</div>
<div>
<p class="text-label-xs text-outline mb-1">Body Base (14px)</p>
<p class="text-body-base font-body-base">전시 관리자는 실시간으로 변화하는 도면 데이터를 분석하여 최적의 동선을 설계합니다.</p>
</div>
</div>
</div>
<!-- Colors -->
<div class="space-y-4">
<div class="border-b border-outline-variant pb-2 mb-4">
<span class="text-label-xs font-label-xs text-outline uppercase">Primary Color Palette</span>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="p-3 bg-primary rounded-lg text-white">
<p class="text-label-md font-label-md">KINTEX Blue</p>
<p class="text-label-xs opacity-80">#004E8B</p>
</div>
<div class="p-3 bg-secondary-container rounded-lg text-on-secondary-container border border-secondary">
<p class="text-label-md font-label-md">AI Accent</p>
<p class="text-label-xs opacity-80">#6D4AFF</p>
</div>
<div class="p-3 bg-tertiary-container rounded-lg text-white">
<p class="text-label-md font-label-md">Success</p>
<p class="text-label-xs opacity-80">#00734E</p>
</div>
<div class="p-3 bg-error rounded-lg text-white">
<p class="text-label-md font-label-md">Error/Alert</p>
<p class="text-label-xs opacity-80">#BA1A1A</p>
</div>
<div class="p-3 bg-surface-container-low border border-outline-variant rounded-lg col-span-2">
<p class="text-label-md font-label-md">Neutral Low</p>
<p class="text-label-xs text-outline">#F1F3FF</p>
</div>
</div>
</div>
</div>
</section>
<!-- SECTION: AI Specific Elements -->
<section class="col-span-12 lg:col-span-4 bg-white border border-outline-variant p-stack-md rounded-lg overflow-hidden relative">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-secondary pl-2">
<h3 class="text-title-sm font-title-sm">AI Specific Elements</h3>
</div>
<div class="space-y-6">
<div class="ai-glow-border p-4 rounded-xl bg-surface-container-low flex flex-col gap-3">
<div class="flex items-center space-x-2">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLu1SFz3uW8s6iC8BUAS9XvAFoM_7LQZUzR-UgorcHCSIZ_uRCF-ZTLQnQbNUG13WdRaQDX_3m0ewu6jxJ2Ie-gQvwFXyYdy3ZIxhkl0hJ2VGRZF6Iul1GgmSKzO5x92HASTCa0m0XzXjctyxy5aRgxOTOxhzl-Qz9P5jTlRzXngIWh6bCNZdaVEIxd9H6a2fDdToHYg0gowaU93SV2P5YbT0-8ShD0eeNiQEFHXn4HcsQUCrsiacEeGDZY" class="w-5 h-5">
<span class="text-label-md font-label-md text-secondary">AI Drafted Component</span>
</div>
<div class="h-16 w-full bg-white/50 rounded border border-secondary/20 flex items-center justify-center relative overflow-hidden">
<div class="absolute inset-0 opacity-10 flex flex-wrap gap-4 text-[10px] font-bold rotate-12 pointer-events-none">
<span class="">AI GENERATED</span><span class="">AI GENERATED</span><span class="">AI GENERATED</span>
<span class="">AI GENERATED</span><span class="">AI GENERATED</span><span class="">AI GENERATED</span>
</div>
<p class="text-label-xs text-on-surface-variant z-10">워터마크 샘플 영역</p>
</div>
</div>
<div class="space-y-2">
<p class="text-label-xs text-outline">Violation Flag Pins (Safety)</p>
<div class="flex items-center space-x-4">
<div class="flex flex-col items-center">
<span class="material-symbols-outlined text-error text-[32px]" style="font-variation-settings: 'FILL' 1;">location_on</span>
<span class="text-label-xs text-error font-bold">Fire Exit Block</span>
</div>
<div class="flex flex-col items-center">
<span class="material-symbols-outlined text-error text-[32px]" style="font-variation-settings: 'FILL' 1;">warning</span>
<span class="text-label-xs text-error font-bold">Overload</span>
</div>
</div>
</div>
</div>
<!-- Background Subtle AI Decoration -->
<div class="absolute -right-4 -bottom-4 opacity-10">
<div class="w-32 h-32">https://lh3.googleusercontent.com/aida/AP1WRLu1SFz3uW8s6iC8BUAS9XvAFoM_7LQZUzR-UgorcHCSIZ_uRCF-ZTLQnQbNUG13WdRaQDX_3m0ewu6jxJ2Ie-gQvwFXyYdy3ZIxhkl0hJ2VGRZF6Iul1GgmSKzO5x92HASTCa0m0XzXjctyxy5aRgxOTOxhzl-Qz9P5jTlRzXngIWh6bCNZdaVEIxd9H6a2fDdToHYg0gowaU93SV2P5YbT0-8ShD0eeNiQEFHXn4HcsQUCrsiacEeGDZY</div>
</div>
</section>
<!-- SECTION: Buttons & Actions -->
<section class="col-span-12 lg:col-span-6 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Buttons &amp; Actions</h3>
</div>
<div class="grid grid-cols-2 gap-6">
<div class="space-y-4">
<button class="w-full bg-primary text-white py-3 rounded-lg text-label-md font-bold transition-all hover:shadow-lg active:scale-95">Primary Button</button>
<button class="w-full border border-primary text-primary py-3 rounded-lg text-label-md font-bold transition-all hover:bg-primary/5 active:scale-95">Secondary Outlined</button>
<button class="w-full text-on-surface-variant py-3 rounded-lg text-label-md font-bold hover:bg-surface-container transition-colors">Ghost Action</button>
</div>
<div class="space-y-4">
<button class="w-full bg-secondary text-white py-3 rounded-lg text-label-md font-bold flex items-center justify-center space-x-2 shadow-md hover:opacity-90 active:scale-95">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLu1SFz3uW8s6iC8BUAS9XvAFoM_7LQZUzR-UgorcHCSIZ_uRCF-ZTLQnQbNUG13WdRaQDX_3m0ewu6jxJ2Ie-gQvwFXyYdy3ZIxhkl0hJ2VGRZF6Iul1GgmSKzO5x92HASTCa0m0XzXjctyxy5aRgxOTOxhzl-Qz9P5jTlRzXngIWh6bCNZdaVEIxd9H6a2fDdToHYg0gowaU93SV2P5YbT0-8ShD0eeNiQEFHXn4HcsQUCrsiacEeGDZY" class="w-5 h-5">
<span class="">AI 제안 레이아웃</span>
</button>
<div class="relative group">
<button class="w-full bg-surface-container-highest border border-outline-variant text-on-surface py-3 rounded-lg text-label-md font-bold opacity-50 cursor-not-allowed">Disabled State</button>
</div>
<div class="flex space-x-2">
<button class="flex-1 bg-surface-container-high p-3 rounded-lg flex items-center justify-center transition-transform hover:-translate-y-1">
<span class="material-symbols-outlined">edit</span>
</button>
<button class="flex-1 bg-surface-container-high p-3 rounded-lg flex items-center justify-center transition-transform hover:-translate-y-1">
<span class="material-symbols-outlined">delete</span>
</button>
<button class="flex-1 bg-surface-container-high p-3 rounded-lg flex items-center justify-center transition-transform hover:-translate-y-1">
<span class="material-symbols-outlined">share</span>
</button>
</div>
</div>
</div>
</section>
<!-- SECTION: Status Badges & Chips -->
<section class="col-span-12 lg:col-span-6 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Status Badges &amp; Chips</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<div class="space-y-4">
<p class="text-label-xs text-outline uppercase border-b border-outline-variant pb-1">Lifecycle Status</p>
<div class="flex flex-wrap gap-2">
<span class="px-3 py-1 bg-surface-container-highest text-on-surface-variant text-label-xs font-bold rounded border-l-4 border-outline">작성중</span>
<span class="px-3 py-1 bg-primary-container/20 text-primary text-label-xs font-bold rounded border-l-4 border-primary">제출됨</span>
<span class="px-3 py-1 bg-secondary-container/20 text-secondary text-label-xs font-bold rounded border-l-4 border-secondary">AI검토</span>
<span class="px-3 py-1 bg-tertiary-container/20 text-tertiary text-label-xs font-bold rounded border-l-4 border-tertiary">승인</span>
<span class="px-3 py-1 bg-error-container text-error text-label-xs font-bold rounded border-l-4 border-error">반려</span>
</div>
<div class="flex flex-wrap gap-2">
<span class="px-3 py-1 bg-secondary text-white text-label-xs font-bold rounded">시공중</span>
<span class="px-3 py-1 bg-tertiary text-white text-label-xs font-bold rounded">검수완료</span>
</div>
</div>
<div class="space-y-4">
<p class="text-label-xs text-outline uppercase border-b border-outline-variant pb-1">Contextual Chips</p>
<div class="flex flex-wrap gap-2">
<span class="bg-error text-white px-2 py-1 rounded text-label-xs font-bold flex items-center">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLvpb_qiARCcrTrv6YnOIVjyk5szWwIfCGaaCmyRAnuu9aficeKfPTRrRHDgl4gJCHQUc7LYwtF1Ak04oSwBAezPs20C51ZjzHDaBCkRYJaYqHS-DOPHSGLdeLL_S2tlRlUAG-XF_ib2I1Svtqq26yY0uxmEQA5cWHJabFRv_0WVtBKiSP2QbCssxeccpcVHcWr_6CJ6zXonlepkxJcDXURlfHpvg7P2-O1urNto3A0xsepsB1ucua9Vnw" class="w-3.5 h-3.5 mr-1"> D-3
</span>
<span class="bg-primary text-white px-2 py-1 rounded text-label-xs font-bold flex items-center">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLvpb_qiARCcrTrv6YnOIVjyk5szWwIfCGaaCmyRAnuu9aficeKfPTRrRHDgl4gJCHQUc7LYwtF1Ak04oSwBAezPs20C51ZjzHDaBCkRYJaYqHS-DOPHSGLdeLL_S2tlRlUAG-XF_ib2I1Svtqq26yY0uxmEQA5cWHJabFRv_0WVtBKiSP2QbCssxeccpcVHcWr_6CJ6zXonlepkxJcDXURlfHpvg7P2-O1urNto3A0xsepsB1ucua9Vnw" class="w-3.5 h-3.5 mr-1"> 10/24
</span>
<span class="bg-surface-container text-on-surface-variant px-2 py-1 rounded text-label-xs border border-outline-variant">Hall 1A</span>
<span class="bg-secondary-container text-on-secondary-container px-2 py-1 rounded text-label-xs font-bold ring-1 ring-secondary/20">High Traffic</span>
</div>
</div>
</div>
</section>
<!-- SECTION: System Assets Library -->
<section class="col-span-12 lg:col-span-12 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">System Assets Library (Custom SVG)</h3>
</div>
<div class="grid grid-cols-2 sm:grid-cols-4 md:grid-cols-7 gap-4">
<div class="flex flex-col items-center p-4 bg-surface-container-low rounded-lg border border-outline-variant/30">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLtO2EjzKAcW5wn4YYTv12rmtFAvwxlN4fmvGGktwdLqfBGzTTC8C_nxjBMV0s73Yz_eqq3my6ASAxnQk9MMCNSeoTVuQwK9CU8en_ZU63PoTk6muzScTpKFEU2aFgRqpZAs3IBLHhlZXl4ENmWt1GO_lvpxzlx-FAVMFPeSHBfqi8lMEy8xvdRkiRB0MpVPgcEnBWn0SU0BsxlrRVjlVMlL3gAwpYNlLruSqKP5tDnBgDNwjHSKU9SUV04" class="w-10 h-10 mb-2">
<span class="text-label-xs text-on-surface-variant">Dashboard</span>
</div>
<div class="flex flex-col items-center p-4 bg-surface-container-low rounded-lg border border-outline-variant/30">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLvpb_qiARCcrTrv6YnOIVjyk5szWwIfCGaaCmyRAnuu9aficeKfPTRrRHDgl4gJCHQUc7LYwtF1Ak04oSwBAezPs20C51ZjzHDaBCkRYJaYqHS-DOPHSGLdeLL_S2tlRlUAG-XF_ib2I1Svtqq26yY0uxmEQA5cWHJabFRv_0WVtBKiSP2QbCssxeccpcVHcWr_6CJ6zXonlepkxJcDXURlfHpvg7P2-O1urNto3A0xsepsB1ucua9Vnw" class="w-10 h-10 mb-2">
<span class="text-label-xs text-on-surface-variant">Calendar</span>
</div>
<div class="flex flex-col items-center p-4 bg-surface-container-low rounded-lg border border-outline-variant/30">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLvz-lIs0s4a8Cy8eurZwY-dsuBDuvrLeIlIdHrd3y0aSi_u9kpOR3v59PRVX3Y1qP8SJ-459rs6AT8ZgXe7HMwRzZCOTLEpNNY1YqLtB8BBuVmnHOOVESaovK6ib38tM-qoAgpfO5Yxp-0ZCx3EwnULjy98lc_fpS8cQgxMEM-G3oAr71ToD4n-SEH55g1Kyz_ucSfAciJazI6fgyJbqOLgEAOJ-J-Bp1O30aTcbYVLFw3GRp7R06emYmc" class="w-10 h-10 mb-2">
<span class="text-label-xs text-on-surface-variant">Operations</span>
</div>
<div class="flex flex-col items-center p-4 bg-surface-container-low rounded-lg border border-outline-variant/30">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLujD1FMRHChfM_aVKt2cd12chhcnm31L8Ks2EAzo-_FQ1_RV3zmYa4WUlSJ67wYexda4SpJU88Y5L0HdiYx6fLuKFdsDKjKWm1SmTq4PIP2tx1Nys-Txdlq4Wc1keg1GKsgrxNY68bHBEhurr-0NorgLbpp032gBkj_vE345bYKJwSr5MEnipvo_1P7iE9cXEBybLmdLLYK6vM1nB7dbmVt9_NHG3NQgK7wcPfIaNBrnXrEqtZeckWX3v8" class="w-10 h-10 mb-2">
<span class="text-label-xs text-on-surface-variant">Analytics</span>
</div>
<div class="flex flex-col items-center p-4 bg-surface-container-low rounded-lg border border-outline-variant/30">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLvR8KDDPFgH9vKk_aFJ_lA5WCSRmrDnPhJH5rSjuWilOAI5PMDe5z2hRnRLQhSn7_712ig52lC61Bb_R8bUkkbqNGZPv3dvykD_Oky9hXWygGnc1ybvaI9TcbT1Yw4Bd2JySNFbit4ho4113ql2BsDvpvJ2ZN64zJqv5GowDs0mdnDyFTwPWmGEKEPr3MigJ4LAaeegtd0fEG_pZWtGpsGSNPVdj4r_TbmkKSxdvmZG_kh-ujSkrXGZzkQ" class="w-10 h-10 mb-2">
<span class="text-label-xs text-on-surface-variant">Search</span>
</div>
<div class="flex flex-col items-center p-4 bg-surface-container-low rounded-lg border border-outline-variant/30">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLvsx4tVeyADsrv6nLlY0PM7oxWgkL9gXJ0QAXqJZmzqStfnij6yOt5LsMKGZwBBMOVo8pSaKZA3AcXICD_Wat3ye_0y8Fp-3vWdjl_yjP_RWt1Zo7a6t4YQ2QoPLOPAXZXdNG9hLc6_7NCbU5EWQBmHyra45kfx0UkIyEfS9HlE2b6STJw42QoX3kLCM1GamQm7IHd3pPK0mGmWAAMDUzwoXR7RQSRVCs1kjQUq5dvSD_fF-pPXppK8_g" class="w-10 h-10 mb-2">
<span class="text-label-xs text-on-surface-variant">Menu</span>
</div>
<div class="flex flex-col items-center p-4 bg-secondary/5 rounded-lg border border-secondary/20"><img src="https://lh3.googleusercontent.com/aida/AP1WRLu1SFz3uW8s6iC8BUAS9XvAFoM_7LQZUzR-UgorcHCSIZ_uRCF-ZTLQnQbNUG13WdRaQDX_3m0ewu6jxJ2Ie-gQvwFXyYdy3ZIxhkl0hJ2VGRZF6Iul1GgmSKzO5x92HASTCa0m0XzXjctyxy5aRgxOTOxhzl-Qz9P5jTlRzXngIWh6bCNZdaVEIxd9H6a2fDdToHYg0gowaU93SV2P5YbT0-8ShD0eeNiQEFHXn4HcsQUCrsiacEeGDZY" class="w-16 h-16 mb-2"><span class="text-label-xs text-secondary font-bold">Friendly AI Bot</span></div>
</div>
</section>
<!-- SECTION: Data Visualization -->
<section class="col-span-12 lg:col-span-12 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Data Visualization Components</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-3 gap-8">
<!-- Gauge Example -->
<div class="bg-surface-container-low p-4 rounded-lg">
<div class="flex justify-between items-center mb-4">
<span class="text-label-md font-bold">전시장 점유율</span>
<span class="text-primary font-bold">78%</span>
</div>
<div class="w-full h-3 bg-surface-container-highest rounded-full overflow-hidden">
<div class="h-full bg-primary rounded-full transition-all duration-1000" style="width: 78%;"></div>
</div>
<div class="flex justify-between mt-2 text-label-xs text-on-surface-variant">
<span class="">0%</span>
<span class="">임계치 85%</span>
<span class="">100%</span>
</div>
</div>
<!-- Heatmap Legend -->
<div class="bg-surface-container-low p-4 rounded-lg">
<p class="text-label-md font-bold mb-4">혼잡도 히트맵 범례</p>
<div class="flex items-center space-x-2 mb-2">
<div class="w-4 h-4 bg-[#D92D20] rounded-sm"></div>
<span class="text-label-xs">매우 혼잡 (Bottle Neck)</span>
</div>
<div class="flex items-center space-x-2 mb-2">
<div class="w-4 h-4 bg-[#F79009] rounded-sm"></div>
<span class="text-label-xs">다소 혼잡</span>
</div>
<div class="flex items-center space-x-2">
<div class="w-4 h-4 bg-[#12B76A] rounded-sm"></div>
<span class="text-label-xs">원활 (Optimized)</span>
</div>
</div>
<!-- Trend Small Chart Placeholder -->
<div class="bg-surface-container-low p-4 rounded-lg flex flex-col justify-between">
<div class="flex justify-between items-center mb-2">
<p class="text-label-md font-bold">방문객 추이 (Weekly)</p>
<span class="w-4 h-4 text-primary">https://lh3.googleusercontent.com/aida/AP1WRLujD1FMRHChfM_aVKt2cd12chhcnm31L8Ks2EAzo-_FQ1_RV3zmYa4WUlSJ67wYexda4SpJU88Y5L0HdiYx6fLuKFdsDKjKWm1SmTq4PIP2tx1Nys-Txdlq4Wc1keg1GKsgrxNY68bHBEhurr-0NorgLbpp032gBkj_vE345bYKJwSr5MEnipvo_1P7iE9cXEBybLmdLLYK6vM1nB7dbmVt9_NHG3NQgK7wcPfIaNBrnXrEqtZeckWX3v8</span>
</div>
<div class="flex items-end space-x-1 h-12">
<div class="flex-1 bg-primary/20 rounded-t h-[40%]"></div>
<div class="flex-1 bg-primary/20 rounded-t h-[60%]"></div>
<div class="flex-1 bg-primary/20 rounded-t h-[30%]"></div>
<div class="flex-1 bg-primary rounded-t h-[80%]"></div>
<div class="flex-1 bg-primary/20 rounded-t h-[50%]"></div>
<div class="flex-1 bg-primary rounded-t h-[90%]"></div>
<div class="flex-1 bg-primary/20 rounded-t h-[45%]"></div>
</div>
<div class="flex justify-between text-[10px] text-on-surface-variant mt-2">
<span class="">Mon</span><span class="">Wed</span><span class="">Sun</span>
</div>
</div>
</div>
</section>
<!-- SECTION: Form Elements -->
<section class="col-span-12 lg:col-span-7 bg-white border border-outline-variant p-stack-md rounded-lg">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Form Elements</h3>
</div>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="space-y-4">
<div class="relative">
<label class="block text-label-xs font-bold text-on-surface-variant mb-1">Standard Input</label>
<div class="relative">
<input class="w-full border border-outline-variant rounded p-3 pl-10 text-body-sm focus:ring-2 focus:ring-primary/20 focus:border-primary outline-none transition-all" placeholder="전시회 명칭 입력" type="text">
<span class="absolute left-3 top-1/2 -translate-y-1/2 w-4 h-4 text-outline">https://lh3.googleusercontent.com/aida/AP1WRLvR8KDDPFgH9vKk_aFJ_lA5WCSRmrDnPhJH5rSjuWilOAI5PMDe5z2hRnRLQhSn7_712ig52lC61Bb_R8bUkkbqNGZPv3dvykD_Oky9hXWygGnc1ybvaI9TcbT1Yw4Bd2JySNFbit4ho4113ql2BsDvpvJ2ZN64zJqv5GowDs0mdnDyFTwPWmGEKEPr3MigJ4LAaeegtd0fEG_pZWtGpsGSNPVdj4r_TbmkKSxdvmZG_kh-ujSkrXGZzkQ</span>
</div>
</div>
<div>
<label class="block text-label-xs font-bold text-on-surface-variant mb-1">Active / Focus</label>
<input class="w-full border border-primary ring-2 ring-primary/20 rounded p-3 text-body-sm outline-none" type="text" value="KINTEX Exhibition Studio">
</div>
<div>
<label class="block text-label-xs font-bold text-error mb-1">Error State</label>
<input class="w-full border border-error rounded p-3 text-body-sm focus:ring-2 focus:ring-error/20 outline-none" type="text" value="Invalid Location">
<p class="text-label-xs text-error mt-1">올바른 위치 정보를 입력하세요.</p>
</div>
</div>
<div class="space-y-6">
<div>
<label class="block text-label-xs font-bold text-on-surface-variant mb-2">Dropdown Selector</label>
<select class="w-full border border-outline-variant rounded p-3 text-body-sm focus:border-primary outline-none appearance-none bg-no-repeat bg-[right_1rem_center] bg-[url('https://api.iconify.design/material-symbols:expand-more.svg')]">
<option>전시홀 선택 (Hall 1~5)</option>
<option>Hall 1A</option>
<option>Hall 2B</option>
</select>
</div>
<div class="flex items-center space-x-8">
<div class="space-y-2">
<label class="flex items-center space-x-2 cursor-pointer group">
<input checked="" class="w-4 h-4 rounded text-primary focus:ring-primary border-outline-variant" type="checkbox">
<span class="text-body-sm">AI 최적화 자동 적용</span>
</label>
<label class="flex items-center space-x-2 cursor-pointer group">
<input class="w-4 h-4 rounded text-primary focus:ring-primary border-outline-variant" type="checkbox">
<span class="text-body-sm">비공개 전환</span>
</label>
</div>
<div class="space-y-2">
<label class="flex items-center space-x-2 cursor-pointer group">
<input checked="" class="w-4 h-4 text-primary focus:ring-primary border-outline-variant" name="perm" type="radio">
<span class="text-body-sm">전체 공개</span>
</label>
<label class="flex items-center space-x-2 cursor-pointer group">
<input class="w-4 h-4 text-primary focus:ring-primary border-outline-variant" name="perm" type="radio">
<span class="text-body-sm">관리자만</span>
</label>
</div>
</div>
</div>
</div>
</section>
<!-- SECTION: Feedback & Overlays -->
<section class="col-span-12 lg:col-span-5 bg-white border border-outline-variant p-stack-md rounded-lg flex flex-col">
<div class="flex items-center space-x-2 mb-6 border-l-4 border-primary pl-2">
<h3 class="text-title-sm font-title-sm">Feedback &amp; Overlays</h3>
</div>
<div class="space-y-6 flex-1">
<!-- Toast Notification -->
<div class="flex items-center justify-between p-4 bg-inverse-surface text-inverse-on-surface rounded-lg shadow-lg">
<div class="flex items-center space-x-3">
<span class="material-symbols-outlined text-[#12B76A]">check_circle</span>
<span class="text-body-sm font-medium">데이터가 성공적으로 저장되었습니다.</span>
</div>
<button class="material-symbols-outlined text-[20px] opacity-60">close</button>
</div>
<!-- Alert Message -->
<div class="flex items-start space-x-3 p-4 bg-error-container text-on-error-container rounded-lg border border-error/20">
<span class="material-symbols-outlined">error</span>
<div>
<p class="text-label-md font-bold">중요 알림</p>
<p class="text-body-sm">현재 설계된 도면에 소방법 위반 구역이 3곳 발견되었습니다. 수정을 권장합니다.</p>
</div>
</div>
<!-- Micro Modal Preview -->
<div class="border border-outline-variant rounded-lg p-6 bg-surface relative overflow-hidden">
<div class="absolute top-0 left-0 w-full h-1 bg-primary"></div>
<h4 class="text-title-sm font-bold mb-2">변경사항 적용</h4>
<p class="text-body-sm text-on-surface-variant mb-6">수정된 레이아웃을 서버에 동기화하시겠습니까? 이 작업은 되돌릴 수 없습니다.</p>
<div class="flex justify-end space-x-3">
<button class="px-4 py-2 text-label-md font-bold text-on-surface-variant">취소</button>
<button class="px-4 py-2 bg-primary text-white rounded text-label-md font-bold">확인</button>
</div>
</div>
</div>
</section>
</div>
<!-- Footer Info -->
<footer class="mt-stack-lg border-t border-outline-variant pt-8 pb-12 flex flex-col md:flex-row justify-between items-center text-on-surface-variant">
<div class="flex items-center space-x-4 mb-4 md:mb-0">
<h2 class="text-headline-md font-bold text-primary opacity-50">KINTEX AI</h2>
<div class="h-4 w-[1px] bg-outline-variant"></div>
<p class="text-label-md">© 2024 KINTEX AI System. All Rights Reserved.</p>
</div>
<div class="flex space-x-6">
<a class="text-label-md hover:text-primary transition-colors" href="#">Documentation</a>
<a class="text-label-md hover:text-primary transition-colors" href="#">Changelog</a>
<a class="text-label-md hover:text-primary transition-colors" href="#">Style Guide PDF</a>
</div>
</footer>
</main>
<!-- Floating Action Button Contextual Mock -->
<div class="fixed bottom-8 right-8 flex flex-col space-y-4">
<button class="w-14 h-14 bg-secondary text-white rounded-full flex items-center justify-center shadow-xl hover:scale-110 transition-transform group relative">
<img src="https://lh3.googleusercontent.com/aida/AP1WRLu1SFz3uW8s6iC8BUAS9XvAFoM_7LQZUzR-UgorcHCSIZ_uRCF-ZTLQnQbNUG13WdRaQDX_3m0ewu6jxJ2Ie-gQvwFXyYdy3ZIxhkl0hJ2VGRZF6Iul1GgmSKzO5x92HASTCa0m0XzXjctyxy5aRgxOTOxhzl-Qz9P5jTlRzXngIWh6bCNZdaVEIxd9H6a2fDdToHYg0gowaU93SV2P5YbT0-8ShD0eeNiQEFHXn4HcsQUCrsiacEeGDZY" class="w-5 h-5">
<div class="absolute right-16 bg-white border border-outline-variant px-3 py-1 rounded shadow-md opacity-0 group-hover:opacity-100 transition-opacity whitespace-nowrap text-on-surface text-label-md pointer-events-none">
AI 어시스턴트 호출
</div>
</button>
</div>
<script>
// Simple micro-interactions for the demo
document.querySelectorAll('button').forEach(button => {
button.addEventListener('mousedown', () => {
button.classList.add('scale-95');
});
button.addEventListener('mouseup', () => {
button.classList.remove('scale-95');
});
button.addEventListener('mouseleave', () => {
button.classList.remove('scale-95');
});
});
// Simulating some loading state on the gauge for visual interest
const gauge = document.querySelector('.bg-primary.rounded-full');
if (gauge) {
setTimeout(() => {
gauge.style.width = '78%';
}, 300);
}
</script>
</body></html>

View File

@ -5,9 +5,9 @@
<meta content="width=device-width, initial-scale=1.0" name="viewport"/> <meta content="width=device-width, initial-scale=1.0" name="viewport"/>
<title>Exhibition Schedule - KINTEX CMS</title> <title>Exhibition Schedule - KINTEX CMS</title>
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script> <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;600;700&family=JetBrains+Mono:wght@400&family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;600;700&amp;family=JetBrains+Mono:wght@400&amp;family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@100..900&display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@100..900&amp;display=swap" rel="stylesheet"/>
<script id="tailwind-config"> <script id="tailwind-config">
tailwind.config = { tailwind.config = {
darkMode: "class", darkMode: "class",
@ -113,37 +113,39 @@
<body class="bg-background text-on-background antialiased flex"> <body class="bg-background text-on-background antialiased flex">
<!-- SideNavBar --> <!-- SideNavBar -->
<nav class="w-[240px] h-screen fixed left-0 top-0 bg-surface border-r border-surface-border flex flex-col py-stack-lg z-50 hidden md:flex"> <nav class="w-[240px] h-screen fixed left-0 top-0 bg-surface border-r border-surface-border flex flex-col py-stack-lg z-50 hidden md:flex">
<div class="px-gutter mb-stack-lg flex items-center gap-stack-md"> <div class="px-gutter mb-stack-lg flex items-center gap-stack-md flex-col !items-start !gap-2">
<img alt="KINTEX Admin Profile" class="w-10 h-10 rounded-full object-cover" data-alt="A clean, minimalist abstract geometric logo representing a modern exhibition center. Corporate blue and gray tones on a white background. Professional administrative dashboard identity." src="https://lh3.googleusercontent.com/aida-public/AB6AXuBVsMPdQO7juKWJmU6p-16B0faYDWSyZMwl7xp7vhMFO-X71CXARl4f0uGfqcIEu0B6KRbqIFrRQOhUjTl5oeVZdxozZBpV5Wzp5SBs5n1HCWkedEunPTGcFWxDO1c-8I_6l90Wq8jPzfmUbX24nlMbiqB3mTiGAUU-HH-GGKyo7v6AAEebnhk1vfGnOLG90v_2sCBXpVB_4gLHNgXKpB3jE-8qz_J8IG670lLTfY6GLr88KyLC8Bgz"/> <img alt="KINTEX Logo" class="h-8 w-auto object-contain" src="https://lh3.googleusercontent.com/aida-public/AB6AXuCLR11UaLwSJShCFFLbDqobx_Y5WpGTGg83m-TotZaoHau7LMFu26QIjyc0NXUv48pZuIsLjReOcoPPUaXqfzp8zNGOgOLjHjIDXLsKHZnhakWSywmqAP2QBnvdAGzhfaNkrp6K3uRIdxzQKx4I_DaxpejAvK_jo19oX44muOW8R00uESszsqVX5UDWxN6vsKhwjATm5AcKznxk0j9IMzorJj2Vg-0mEw8QTRjcePTZGKC3PNZlH193FHaSyuZ7shfjcg"/>
<div> <div><p class="font-label-md text-label-md text-on-surface-variant uppercase tracking-wider">Exhibition Management System</p></div>
<h1 class="font-headline-md text-headline-md font-bold text-primary">KINTEX CMS</h1>
<p class="font-label-md text-label-md text-on-surface-variant">Exhibition Management</p>
</div>
</div> </div>
<div class="px-gutter mb-stack-lg"> <div class="px-gutter mb-stack-lg">
<button class="w-full bg-primary-container text-on-primary text-body-md font-body-md py-2 px-4 rounded hover:opacity-90 transition-opacity flex items-center justify-center gap-2"> <button class="w-full text-on-primary text-body-md font-body-md py-2 px-4 rounded hover:opacity-90 transition-opacity flex items-center justify-center gap-2 bg-primary">
<span class="material-symbols-outlined text-[18px]">add</span> Create New Event <span class="material-symbols-outlined text-[18px]">add</span> Create New Event
</button> </button>
</div> </div>
<ul class="flex-1 overflow-y-auto font-body-md text-body-md"> <ul class="flex-1 overflow-y-auto font-body-md text-body-md">
<li class="mb-1"> <li class="mb-1">
<a class="flex items-center gap-3 px-gutter py-3 text-on-surface-variant hover:bg-surface-container-high transition-colors duration-200 active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-gutter py-3 text-on-surface-variant hover:bg-surface-container-high transition-colors duration-200 active:scale-[0.98]" href="#">
<span class="material-symbols-outlined">dashboard</span> Dashboard <img alt="Dashboard" class="w-6 h-6" src="https://lh3.googleusercontent.com/aida/AP1WRLtO2EjzKAcW5wn4YYTv12rmtFAvwxlN4fmvGGktwdLqfBGzTTC8C_nxjBMV0s73Yz_eqq3my6ASAxnQk9MMCNSeoTVuQwK9CU8en_ZU63PoTk6muzScTpKFEU2aFgRqpZAs3IBLHhlZXl4ENmWt1GO_lvpxzlx-FAVMFPeSHBfqi8lMEy8xvdRkiRB0MpVPgcEnBWn0SU0BsxlrRVjlVMlL3gAwpYNlLruSqKP5tDnBgDNwjHSKU9SUV04"/> Dashboard
</a> </a>
</li> </li>
<li class="mb-1"> <li class="mb-1">
<a class="flex items-center gap-3 px-gutter py-3 bg-secondary-container text-on-secondary-container font-semibold transition-colors duration-200 active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-gutter py-3 font-semibold transition-colors duration-200 active:scale-[0.98] bg-primary/10 !text-primary" href="#">
<span class="material-symbols-outlined">calendar_month</span> Exhibition Schedule <img alt="Exhibition Schedule" class="w-6 h-6" src="https://lh3.googleusercontent.com/aida/AP1WRLvpb_qiARCcrTrv6YnOIVjyk5szWwIfCGaaCmyRAnuu9aficeKfPTRrRHDgl4gJCHQUc7LYwtF1Ak04oSwBAezPs20C51ZjzHDaBCkRYJaYqHS-DOPHSGLdeLL_S2tlRlUAG-XF_ib2I1Svtqq26yY0uxmEQA5cWHJabFRv_0WVtBKiSP2QbCssxeccpcVHcWr_6CJ6zXonlepkxJcDXURlfHpvg7P2-O1urNto3A0xsepsB1ucua9Vnw"/> Exhibition Schedule
</a> </a>
</li> </li>
<li class="mb-1"> <li class="mb-1">
<a class="flex items-center gap-3 px-gutter py-3 text-on-surface-variant hover:bg-surface-container-high transition-colors duration-200 active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-gutter py-3 text-on-surface-variant hover:bg-surface-container-high transition-colors duration-200 active:scale-[0.98]" href="#">
<span class="material-symbols-outlined">sensors</span> Hall Operations <img alt="Hall Operations" class="w-6 h-6" src="https://lh3.googleusercontent.com/aida/AP1WRLvz-lIs0s4a8Cy8eurZwY-dsuBDuvrLeIlIdHrd3y0aSi_u9kpOR3v59PRVX3Y1qP8SJ-459rs6AT8ZgXe7HMwRzZCOTLEpNNY1YqLtB8BBuVmnHOOVESaovK6ib38tM-qoAgpfO5Yxp-0ZCx3EwnULjy98lc_fpS8cQgxMEM-G3oAr71ToD4n-SEH55g1Kyz_ucSfAciJazI6fgyJbqOLgEAOJ-J-Bp1O30aTcbYVLFw3GRp7R06emYmc"/> Hall Operations
</a> </a>
</li> </li>
<li class="mb-1"> <li class="mb-1">
<a class="flex items-center gap-3 px-gutter py-3 text-on-surface-variant hover:bg-surface-container-high transition-colors duration-200 active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 px-gutter py-3 text-on-surface-variant hover:bg-surface-container-high transition-colors duration-200 active:scale-[0.98]" href="#">
<span class="material-symbols-outlined">analytics</span> Business Intelligence <img alt="Business Intelligence" class="w-6 h-6" src="https://lh3.googleusercontent.com/aida/AP1WRLujD1FMRHChfM_aVKt2cd12chhcnm31L8Ks2EAzo-_FQ1_RV3zmYa4WUlSJ67wYexda4SpJU88Y5L0HdiYx6fLuKFdsDKjKWm1SmTq4PIP2tx1Nys-Txdlq4Wc1keg1GKsgrxNY68bHBEhurr-0NorgLbpp032gBkj_vE345bYKJwSr5MEnipvo_1P7iE9cXEBybLmdLLYK6vM1nB7dbmVt9_NHG3NQgK7wcPfIaNBrnXrEqtZeckWX3v8"/> Business Intelligence
</a>
</li>
<li class="mb-1">
<a class="flex items-center gap-3 px-gutter py-3 text-on-surface-variant hover:bg-surface-container-high transition-colors duration-200 active:scale-[0.98]" href="#">
<img alt="AI Bot" class="w-6 h-6" src="https://lh3.googleusercontent.com/aida/AP1WRLu1SFz3uW8s6iC8BUAS9XvAFoM_7LQZUzR-UgorcHCSIZ_uRCF-ZTLQnQbNUG13WdRaQDX_3m0ewu6jxJ2Ie-gQvwFXyYdy3ZIxhkl0hJ2VGRZF6Iul1GgmSKzO5x92HASTCa0m0XzXjctyxy5aRgxOTOxhzl-Qz9P5jTlRzXngIWh6bCNZdaVEIxd9H6a2fDdToHYg0gowaU93SV2P5YbT0-8ShD0eeNiQEFHXn4HcsQUCrsiacEeGDZY"/> AI Assistant
</a> </a>
</li> </li>
</ul> </ul>
@ -154,7 +156,7 @@
<span class="material-symbols-outlined">settings</span> Settings <span class="material-symbols-outlined">settings</span> Settings
</a> </a>
</li> </li>
<li> <li class="">
<a class="flex items-center gap-3 py-2 text-on-surface-variant hover:bg-surface-container-high transition-colors duration-200 active:scale-[0.98]" href="#"> <a class="flex items-center gap-3 py-2 text-on-surface-variant hover:bg-surface-container-high transition-colors duration-200 active:scale-[0.98]" href="#">
<span class="material-symbols-outlined">help</span> Support <span class="material-symbols-outlined">help</span> Support
</a> </a>
@ -204,8 +206,8 @@
</select> </select>
<select class="border-surface-border bg-surface-bright rounded text-body-md font-body-md py-1.5 px-3 focus:ring-primary-container focus:border-primary-container w-full md:w-auto"> <select class="border-surface-border bg-surface-bright rounded text-body-md font-body-md py-1.5 px-3 focus:ring-primary-container focus:border-primary-container w-full md:w-auto">
<option>All Categories</option> <option>All Categories</option>
<option>Culture & Arts</option> <option>Culture &amp; Arts</option>
<option>Trade & Industry</option> <option>Trade &amp; Industry</option>
<option>Technology</option> <option>Technology</option>
</select> </select>
<select class="border-surface-border bg-surface-bright rounded text-body-md font-body-md py-1.5 px-3 focus:ring-primary-container focus:border-primary-container w-full md:w-auto"> <select class="border-surface-border bg-surface-bright rounded text-body-md font-body-md py-1.5 px-3 focus:ring-primary-container focus:border-primary-container w-full md:w-auto">
@ -239,7 +241,7 @@
<h3 class="font-headline-md text-headline-md text-on-surface">Comic World Summer 2026</h3> <h3 class="font-headline-md text-headline-md text-on-surface">Comic World Summer 2026</h3>
<span class="bg-status-active/20 text-status-active px-2 py-0.5 rounded font-label-md text-label-md border border-status-active/30">Ongoing</span> <span class="bg-status-active/20 text-status-active px-2 py-0.5 rounded font-label-md text-label-md border border-status-active/30">Ongoing</span>
</div> </div>
<p class="font-body-sm text-body-sm text-on-surface-variant mb-2">Culture & Arts Festival</p> <p class="font-body-sm text-body-sm text-on-surface-variant mb-2">Culture &amp; Arts Festival</p>
<div class="flex flex-wrap gap-4 text-body-sm font-body-sm text-on-surface-variant"> <div class="flex flex-wrap gap-4 text-body-sm font-body-sm text-on-surface-variant">
<div class="flex items-center gap-1"><span class="material-symbols-outlined text-[16px]">event</span> Aug 15 - Aug 18, 2026</div> <div class="flex items-center gap-1"><span class="material-symbols-outlined text-[16px]">event</span> Aug 15 - Aug 18, 2026</div>
<div class="flex items-center gap-1"><span class="material-symbols-outlined text-[16px]">location_on</span> Hall 1, 2, 3</div> <div class="flex items-center gap-1"><span class="material-symbols-outlined text-[16px]">location_on</span> Hall 1, 2, 3</div>
@ -263,7 +265,7 @@
<h3 class="font-headline-md text-headline-md text-on-surface">Smart Factory Expo</h3> <h3 class="font-headline-md text-headline-md text-on-surface">Smart Factory Expo</h3>
<span class="bg-status-pending/20 text-status-pending px-2 py-0.5 rounded font-label-md text-label-md border border-status-pending/30">Upcoming</span> <span class="bg-status-pending/20 text-status-pending px-2 py-0.5 rounded font-label-md text-label-md border border-status-pending/30">Upcoming</span>
</div> </div>
<p class="font-body-sm text-body-sm text-on-surface-variant mb-2">Trade & Industry Exhibition</p> <p class="font-body-sm text-body-sm text-on-surface-variant mb-2">Trade &amp; Industry Exhibition</p>
<div class="flex flex-wrap gap-4 text-body-sm font-body-sm text-on-surface-variant"> <div class="flex flex-wrap gap-4 text-body-sm font-body-sm text-on-surface-variant">
<div class="flex items-center gap-1"><span class="material-symbols-outlined text-[16px]">event</span> Sep 05 - Sep 08, 2026</div> <div class="flex items-center gap-1"><span class="material-symbols-outlined text-[16px]">event</span> Sep 05 - Sep 08, 2026</div>
<div class="flex items-center gap-1"><span class="material-symbols-outlined text-[16px]">location_on</span> Hall 4, 5</div> <div class="flex items-center gap-1"><span class="material-symbols-outlined text-[16px]">location_on</span> Hall 4, 5</div>
@ -289,11 +291,11 @@
</div> </div>
</div> </div>
<div class="grid grid-cols-7 gap-1 text-center font-label-md text-label-md text-on-surface-variant mb-2"> <div class="grid grid-cols-7 gap-1 text-center font-label-md text-label-md text-on-surface-variant mb-2">
<div>S</div><div>M</div><div>T</div><div>W</div><div>T</div><div>F</div><div>S</div> <div class="">S</div><div class="">M</div><div class="">T</div><div class="">W</div><div class="">T</div><div class="">F</div><div class="">S</div>
</div> </div>
<div class="grid grid-cols-7 gap-1 text-center font-mono-data text-mono-data text-on-surface"> <div class="grid grid-cols-7 gap-1 text-center font-mono-data text-mono-data text-on-surface">
<div class="text-surface-dim p-1">26</div><div class="text-surface-dim p-1">27</div><div class="text-surface-dim p-1">28</div><div class="text-surface-dim p-1">29</div><div class="text-surface-dim p-1">30</div><div class="text-surface-dim p-1">31</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">1</div> <div class="text-surface-dim p-1">26</div><div class="text-surface-dim p-1">27</div><div class="text-surface-dim p-1">28</div><div class="text-surface-dim p-1">29</div><div class="text-surface-dim p-1">30</div><div class="text-surface-dim p-1">31</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">1</div>
<!-- Snipped calendar for brevity in effort level --> <!-- Snipped calendar for brevity -->
<div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">2</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">3</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">4</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">5</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">6</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">7</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">8</div> <div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">2</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">3</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">4</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">5</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">6</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">7</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">8</div>
<div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">9</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">10</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">11</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">12</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">13</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">14</div><div class="bg-primary-container text-on-primary rounded p-1">15</div> <div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">9</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">10</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">11</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">12</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">13</div><div class="p-1 hover:bg-surface-container-high rounded cursor-pointer">14</div><div class="bg-primary-container text-on-primary rounded p-1">15</div>
</div> </div>

View File

@ -7,7 +7,6 @@
<script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script> <script src="https://cdn.tailwindcss.com?plugins=forms,container-queries"></script>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;600;700&amp;family=JetBrains+Mono:wght@400&amp;display=swap" rel="stylesheet"/> <link href="https://fonts.googleapis.com/css2?family=Hanken+Grotesk:wght@400;600;700&amp;family=JetBrains+Mono:wght@400&amp;display=swap" rel="stylesheet"/>
<link href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&amp;display=swap" rel="stylesheet"/>
<script id="tailwind-config"> <script id="tailwind-config">
tailwind.config = { tailwind.config = {
darkMode: "class", darkMode: "class",
@ -179,12 +178,11 @@
<!-- SideNavBar --> <!-- SideNavBar -->
<nav class="w-[240px] h-screen fixed left-0 top-0 bg-surface dark:bg-surface border-r border-surface-border flex flex-col py-stack-lg z-50"> <nav class="w-[240px] h-screen fixed left-0 top-0 bg-surface dark:bg-surface border-r border-surface-border flex flex-col py-stack-lg z-50">
<div class="px-gutter mb-stack-lg flex items-center gap-3"> <div class="px-gutter mb-stack-lg flex items-center gap-3">
<div class="w-10 h-10 rounded-full bg-primary-container flex items-center justify-center text-on-primary-container"> <div class="w-10 h-10 rounded-full bg-primary-container flex items-center justify-center text-on-primary-container overflow-hidden">
<span class="material-symbols-outlined">domain</span> <img alt="KINTEX CMS Logo" class="w-6 h-6 object-contain brightness-0 invert" src="https://lh3.googleusercontent.com/aida/AP1WRLu1SFz3uW8s6iC8BUAS9XvAFoM_7LQZUzR-UgorcHCSIZ_uRCF-ZTLQnQbNUG13WdRaQDX_3m0ewu6jxJ2Ie-gQvwFXyYdy3ZIxhkl0hJ2VGRZF6Iul1GgmSKzO5x92HASTCa0m0XzXjctyxy5aRgxOTOxhzl-Qz9P5jTlRzXngIWh6bCNZdaVEIxd9H6a2fDdToHYg0gowaU93SV2P5YbT0-8ShD0eeNiQEFHXn4HcsQUCrsiacEeGDZY"/>
</div> </div>
<div> <div>
<h1 class="font-headline-md text-headline-md font-bold text-primary dark:text-primary-fixed-dim tracking-tight">KINTEX CMS</h1> <img alt="KINTEX Logo" class="h-8 w-auto object-contain" src="https://lh3.googleusercontent.com/aida-public/AB6AXuAkMBjVM7IWaH47IzpnpoyhFdfwyNQ7Ns7YHqt-z2DDCnToIJcAf848B38CIWVPkiK1TBkk1gKruf_KCA3jVl-gc4NqY6Lc2aiXgQ8yhQNo2eohoFHP7wsdhjzkblMlTCzQzYWiPA7nsncBC0tbYBCJv-xnyq3ErGnpOusWMcFddoXm6ayHIWxRVR9YKG5WOj4PRvhrhVvlSL7xpJpJfCH37ilwRHBzXKVdZgEO28ZQPViNcOp2IpefeQsYd45YoMEEvg"/>
<p class="font-label-md text-label-md text-on-surface-variant">Exhibition Management</p>
</div> </div>
</div> </div>
<div class="px-gutter mb-stack-lg"> <div class="px-gutter mb-stack-lg">
@ -195,19 +193,19 @@
</div> </div>
<div class="flex-1 px-3 space-y-1"> <div class="flex-1 px-3 space-y-1">
<a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200" href="#"> <a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200" href="#">
<span class="material-symbols-outlined">dashboard</span> <img alt="Dashboard" class="w-5 h-5 opacity-70" src="https://lh3.googleusercontent.com/aida/AP1WRLtO2EjzKAcW5wn4YYTv12rmtFAvwxlN4fmvGGktwdLqfBGzTTC8C_nxjBMV0s73Yz_eqq3my6ASAxnQk9MMCNSeoTVuQwK9CU8en_ZU63PoTk6muzScTpKFEU2aFgRqpZAs3IBLHhlZXl4ENmWt1GO_lvpxzlx-FAVMFPeSHBfqi8lMEy8xvdRkiRB0MpVPgcEnBWn0SU0BsxlrRVjlVMlL3gAwpYNlLruSqKP5tDnBgDNwjHSKU9SUV04"/>
<span class="font-label-md text-label-md">Dashboard</span> <span class="font-label-md text-label-md">Dashboard</span>
</a> </a>
<a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200" href="#"> <a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200" href="#">
<span class="material-symbols-outlined">calendar_month</span> <img alt="Schedule" class="w-5 h-5 opacity-70" src="https://lh3.googleusercontent.com/aida/AP1WRLvpb_qiARCcrTrv6YnOIVjyk5szWwIfCGaaCmyRAnuu9aficeKfPTRrRHDgl4gJCHQUc7LYwtF1Ak04oSwBAezPs20C51ZjzHDaBCkRYJaYqHS-DOPHSGLdeLL_S2tlRlUAG-XF_ib2I1Svtqq26yY0uxmEQA5cWHJabFRv_0WVtBKiSP2QbCssxeccpcVHcWr_6CJ6zXonlepkxJcDXURlfHpvg7P2-O1urNto3A0xsepsB1ucua9Vnw"/>
<span class="font-label-md text-label-md">Exhibition Schedule</span> <span class="font-label-md text-label-md">Exhibition Schedule</span>
</a> </a>
<a class="flex items-center gap-3 px-3 py-2 rounded bg-secondary-container text-on-secondary-container font-semibold transition-colors duration-200" href="#"> <a class="flex items-center gap-3 px-3 py-2 rounded bg-secondary-container text-on-secondary-container font-semibold transition-colors duration-200" href="#">
<span class="material-symbols-outlined" style="font-variation-settings: 'FILL' 1;">sensors</span> <img alt="Operations" class="w-5 h-5" src="https://lh3.googleusercontent.com/aida/AP1WRLvz-lIs0s4a8Cy8eurZwY-dsuBDuvrLeIlIdHrd3y0aSi_u9kpOR3v59PRVX3Y1qP8SJ-459rs6AT8ZgXe7HMwRzZCOTLEpNNY1YqLtB8BBuVmnHOOVESaovK6ib38tM-qoAgpfO5Yxp-0ZCx3EwnULjy98lc_fpS8cQgxMEM-G3oAr71ToD4n-SEH55g1Kyz_ucSfAciJazI6fgyJbqOLgEAOJ-J-Bp1O30aTcbYVLFw3GRp7R06emYmc"/>
<span class="font-label-md text-label-md">Hall Operations</span> <span class="font-label-md text-label-md">Hall Operations</span>
</a> </a>
<a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200" href="#"> <a class="flex items-center gap-3 px-3 py-2 rounded text-on-surface-variant hover:bg-surface-container-low transition-colors duration-200" href="#">
<span class="material-symbols-outlined">analytics</span> <img alt="Analytics" class="w-5 h-5 opacity-70" src="https://lh3.googleusercontent.com/aida/AP1WRLujD1FMRHChfM_aVKt2cd12chhcnm31L8Ks2EAzo-_FQ1_RV3zmYa4WUlSJ67wYexda4SpJU88Y5L0HdiYx6fLuKFdsDKjKWm1SmTq4PIP2tx1Nys-Txdlq4Wc1keg1GKsgrxNY68bHBEhurr-0NorgLbpp032gBkj_vE345bYKJwSr5MEnipvo_1P7iE9cXEBybLmdLLYK6vM1nB7dbmVt9_NHG3NQgK7wcPfIaNBrnXrEqtZeckWX3v8"/>
<span class="font-label-md text-label-md">Business Intelligence</span> <span class="font-label-md text-label-md">Business Intelligence</span>
</a> </a>
</div> </div>
@ -267,8 +265,8 @@
<!-- Crowd Density Map (Span 8) --> <!-- Crowd Density Map (Span 8) -->
<div class="col-span-12 lg:col-span-8 bg-surface-container-lowest rounded-xl border border-surface-border overflow-hidden flex flex-col h-[500px]"> <div class="col-span-12 lg:col-span-8 bg-surface-container-lowest rounded-xl border border-surface-border overflow-hidden flex flex-col h-[500px]">
<div class="px-stack-lg py-stack-md border-b border-surface-border flex justify-between items-center bg-surface-bright"> <div class="px-stack-lg py-stack-md border-b border-surface-border flex justify-between items-center bg-surface-bright">
<h3 class="font-headline-md text-headline-md text-on-surface flex items-center gap-2"> <h3 class="font-headline-md text-headline-md text-on-surface flex items-center gap-3">
<span class="material-symbols-outlined text-primary">groups</span> <img alt="Density" class="w-6 h-6 text-primary" src="https://lh3.googleusercontent.com/aida/AP1WRLvz-lIs0s4a8Cy8eurZwY-dsuBDuvrLeIlIdHrd3y0aSi_u9kpOR3v59PRVX3Y1qP8SJ-459rs6AT8ZgXe7HMwRzZCOTLEpNNY1YqLtB8BBuVmnHOOVESaovK6ib38tM-qoAgpfO5Yxp-0ZCx3EwnULjy98lc_fpS8cQgxMEM-G3oAr71ToD4n-SEH55g1Kyz_ucSfAciJazI6fgyJbqOLgEAOJ-J-Bp1O30aTcbYVLFw3GRp7R06emYmc"/>
Live Crowd Density Heatmap Live Crowd Density Heatmap
</h3> </h3>
<div class="flex gap-2"> <div class="flex gap-2">
@ -285,7 +283,7 @@
<div class="relative w-full h-full bg-surface-container-lowest border border-surface-border rounded-lg shadow-sm overflow-hidden flex items-center justify-center" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAzDGCxZryR6cy1r-TqP3RGKxo9Q2SrEo5oqMaZVpLfvhZj7YBSwlVKNq5O4bvG-KFCWIB7ptcbsksw5m6ttfP_KbPFpPAJD2JCufmfiY_7AXdcR6B7yaf88RxuPmB_kviQa1GNFYKVBDYUAPzPdPf_qmV7PTMKcisaMQkM8fIzN4w2ncnDR4TV_EVfiiKmikS7pdvPg2CPN9d7eRsMZIu4qOWqsUVQLtwMIhRygocuOLrAIy81gxLL'); background-size: cover; background-position: center;"> <div class="relative w-full h-full bg-surface-container-lowest border border-surface-border rounded-lg shadow-sm overflow-hidden flex items-center justify-center" style="background-image: url('https://lh3.googleusercontent.com/aida-public/AB6AXuAzDGCxZryR6cy1r-TqP3RGKxo9Q2SrEo5oqMaZVpLfvhZj7YBSwlVKNq5O4bvG-KFCWIB7ptcbsksw5m6ttfP_KbPFpPAJD2JCufmfiY_7AXdcR6B7yaf88RxuPmB_kviQa1GNFYKVBDYUAPzPdPf_qmV7PTMKcisaMQkM8fIzN4w2ncnDR4TV_EVfiiKmikS7pdvPg2CPN9d7eRsMZIu4qOWqsUVQLtwMIhRygocuOLrAIy81gxLL'); background-size: cover; background-position: center;">
<!-- Simulated Heat Spots --> <!-- Simulated Heat Spots -->
<div class="absolute top-1/4 left-1/4 w-32 h-32 bg-status-pending rounded-full blur-2xl opacity-60 mix-blend-multiply"></div> <div class="absolute top-1/4 left-1/4 w-32 h-32 bg-status-pending rounded-full blur-2xl opacity-60 mix-blend-multiply"></div>
<div class="absolute top-1/2 right-1/3 w-48 h-48 bg-error rounded-full blur-3xl opacity-40 mix-blend-multiply"></div> <div class="absolute top-1/2 right-1/3 w-48 h-48 rounded-full blur-3xl opacity-40 mix-blend-multiply bg-tertiary"></div>
<div class="absolute bottom-1/4 left-1/2 w-24 h-24 bg-status-active rounded-full blur-xl opacity-50 mix-blend-multiply"></div> <div class="absolute bottom-1/4 left-1/2 w-24 h-24 bg-status-active rounded-full blur-xl opacity-50 mix-blend-multiply"></div>
<!-- Hall Labels overlaid --> <!-- Hall Labels overlaid -->
<div class="absolute inset-0 grid grid-cols-5 gap-2 p-4"> <div class="absolute inset-0 grid grid-cols-5 gap-2 p-4">
@ -301,7 +299,7 @@
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<span class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-status-active"></span> Low</span> <span class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-status-active"></span> Low</span>
<span class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-status-pending"></span> Moderate</span> <span class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-status-pending"></span> Moderate</span>
<span class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-error"></span> High</span> <span class="flex items-center gap-1"><span class="w-3 h-3 rounded-full bg-tertiary"></span> High</span>
</div> </div>
<span class="font-mono-data text-mono-data text-outline">Total Est. Attendance: 14,250</span> <span class="font-mono-data text-mono-data text-outline">Total Est. Attendance: 14,250</span>
</div> </div>
@ -310,8 +308,8 @@
<div class="col-span-12 lg:col-span-4 flex flex-col gap-gutter h-[500px]"> <div class="col-span-12 lg:col-span-4 flex flex-col gap-gutter h-[500px]">
<!-- HVAC Status --> <!-- HVAC Status -->
<div class="flex-1 bg-surface-container-lowest rounded-xl border border-surface-border p-stack-md flex flex-col"> <div class="flex-1 bg-surface-container-lowest rounded-xl border border-surface-border p-stack-md flex flex-col">
<h3 class="font-label-md text-label-md text-on-surface-variant uppercase tracking-wider mb-4 flex items-center gap-2"> <h3 class="font-label-md text-label-md text-on-surface-variant uppercase tracking-wider mb-4 flex items-center gap-3">
<span class="material-symbols-outlined text-base">air</span> <img alt="HVAC" class="w-5 h-5 opacity-60" src="https://lh3.googleusercontent.com/aida/AP1WRLvz-lIs0s4a8Cy8eurZwY-dsuBDuvrLeIlIdHrd3y0aSi_u9kpOR3v59PRVX3Y1qP8SJ-459rs6AT8ZgXe7HMwRzZCOTLEpNNY1YqLtB8BBuVmnHOOVESaovK6ib38tM-qoAgpfO5Yxp-0ZCx3EwnULjy98lc_fpS8cQgxMEM-G3oAr71ToD4n-SEH55g1Kyz_ucSfAciJazI6fgyJbqOLgEAOJ-J-Bp1O30aTcbYVLFw3GRp7R06emYmc"/>
HVAC Optimization HVAC Optimization
</h3> </h3>
<div class="flex items-end gap-2 mb-2"> <div class="flex items-end gap-2 mb-2">
@ -336,8 +334,8 @@
</div> </div>
<!-- Lighting Status --> <!-- Lighting Status -->
<div class="flex-1 bg-surface-container-lowest rounded-xl border border-surface-border p-stack-md flex flex-col"> <div class="flex-1 bg-surface-container-lowest rounded-xl border border-surface-border p-stack-md flex flex-col">
<h3 class="font-label-md text-label-md text-on-surface-variant uppercase tracking-wider mb-4 flex items-center gap-2"> <h3 class="font-label-md text-label-md text-on-surface-variant uppercase tracking-wider mb-4 flex items-center gap-3">
<span class="material-symbols-outlined text-base">lightbulb</span> <img alt="Lighting" class="w-5 h-5 opacity-60" src="https://lh3.googleusercontent.com/aida/AP1WRLtO2EjzKAcW5wn4YYTv12rmtFAvwxlN4fmvGGktwdLqfBGzTTC8C_nxjBMV0s73Yz_eqq3my6ASAxnQk9MMCNSeoTVuQwK9CU8en_ZU63PoTk6muzScTpKFEU2aFgRqpZAs3IBLHhlZXl4ENmWt1GO_lvpxzlx-FAVMFPeSHBfqi8lMEy8xvdRkiRB0MpVPgcEnBWn0SU0BsxlrRVjlVMlL3gAwpYNlLruSqKP5tDnBgDNwjHSKU9SUV04"/>
Lighting Zones Lighting Zones
</h3> </h3>
<div class="space-y-3"> <div class="space-y-3">
@ -367,8 +365,8 @@
<!-- Parking Logistics (Span 12) --> <!-- Parking Logistics (Span 12) -->
<div class="col-span-12 bg-surface-container-lowest rounded-xl border border-surface-border p-stack-lg"> <div class="col-span-12 bg-surface-container-lowest rounded-xl border border-surface-border p-stack-lg">
<div class="flex justify-between items-center mb-stack-md"> <div class="flex justify-between items-center mb-stack-md">
<h3 class="font-headline-md text-headline-md text-on-surface flex items-center gap-2"> <h3 class="font-headline-md text-headline-md text-on-surface flex items-center gap-3">
<span class="material-symbols-outlined text-primary">local_parking</span> <img alt="Parking" class="w-6 h-6 text-primary" src="https://lh3.googleusercontent.com/aida/AP1WRLujD1FMRHChfM_aVKt2cd12chhcnm31L8Ks2EAzo-_FQ1_RV3zmYa4WUlSJ67wYexda4SpJU88Y5L0HdiYx6fLuKFdsDKjKWm1SmTq4PIP2tx1Nys-Txdlq4Wc1keg1GKsgrxNY68bHBEhurr-0NorgLbpp032gBkj_vE345bYKJwSr5MEnipvo_1P7iE9cXEBybLmdLLYK6vM1nB7dbmVt9_NHG3NQgK7wcPfIaNBrnXrEqtZeckWX3v8"/>
Parking Occupancy Status Parking Occupancy Status
</h3> </h3>
<span class="font-label-md bg-surface-container-high px-3 py-1 rounded text-on-surface-variant">Updated: Just now</span> <span class="font-label-md bg-surface-container-high px-3 py-1 rounded text-on-surface-variant">Updated: Just now</span>