From 6b17c1fadf6af3f7b41e206f7b32b9d8b57accf4 Mon Sep 17 00:00:00 2001 From: DDD1542 Date: Thu, 21 May 2026 12:06:55 +0900 Subject: [PATCH] chore: commit remaining workspace updates Include the outstanding numbering-rule admin page changes, TabBar interaction updates, V5 layout theme accent styling, and auto-generation option compatibility fix. Add the local web-prototype skill assets, numbering-rule design variants, control IDE refactor note, and the table canonical cleanup plan/prompts used across phases B through F. This commit captures the remaining workspace files after the canonical table cleanup commit so the branch can be pushed without leaving local dirty work behind. --- .od-skills/web-prototype/SKILL.md | 97 + .od-skills/web-prototype/assets/template.html | 338 ++++ .od-skills/web-prototype/example.html | 81 + .od-skills/web-prototype/open-design.json | 132 ++ .../web-prototype/references/checklist.md | 44 + .../web-prototype/references/layouts.md | 247 +++ .../systemMng/numberingRuleList/page.tsx | 571 +++--- frontend/components/layout/TabBar.tsx | 92 +- frontend/lib/utils/autoGeneration.ts | 13 +- frontend/styles/v5-layout.css | 290 ++- .../gbpark/2026-05-20-control-ide-refactor.md | 372 ++++ .../index.html | 73 + .../numbering-v2.css | 641 +++++++ .../variant-a-cascade.html | 202 +++ .../variant-b-cp-workbench.html | 230 +++ .../variant-c-guard.html | 182 ++ .../index.html | 120 ++ .../numbering-v3.css | 636 +++++++ .../variant-a-quiet-admin.html | 263 +++ .../variant-b-cp-editor.html | 260 +++ .../variant-c-operations-review.html | 232 +++ .../variant-d-table-first.html | 209 +++ .../index.html | 155 ++ .../variant-a-workbench.html | 229 +++ .../variant-b-console.html | 169 ++ .../variant-c-validation.html | 211 +++ .../2026-05-20-numbering-rule-clean-v5.html | 945 ++++++++++ ...2026-05-20-numbering-rule-identity-v6.html | 1164 ++++++++++++ ...2026-05-20-table-canonical-cleanup-plan.md | 1596 +++++++++++++++++ ...le-canonical-input-parity-claude-prompt.md | 149 ++ ...-canonical-phase-b2-b3-d9-claude-prompt.md | 235 +++ ...-table-canonical-phase-c3-claude-prompt.md | 237 +++ ...-table-canonical-phase-c4-claude-prompt.md | 204 +++ ...-table-canonical-phase-d1-claude-prompt.md | 234 +++ ...table-canonical-phase-d10-claude-prompt.md | 135 ++ ...-table-canonical-phase-d2-claude-prompt.md | 234 +++ ...-table-canonical-phase-d3-claude-prompt.md | 196 ++ ...-table-canonical-phase-d4-claude-prompt.md | 236 +++ ...-table-canonical-phase-d5-claude-prompt.md | 281 +++ ...-table-canonical-phase-d6-claude-prompt.md | 305 ++++ ...-table-canonical-phase-d7-claude-prompt.md | 250 +++ ...-table-canonical-phase-d8-claude-prompt.md | 258 +++ ...-table-canonical-phase-e1-claude-prompt.md | 222 +++ ...-table-canonical-phase-e2-claude-prompt.md | 196 ++ ...-table-canonical-phase-e3-claude-prompt.md | 147 ++ ...-table-canonical-phase-f1-claude-prompt.md | 124 ++ ...-table-canonical-phase-f2-claude-prompt.md | 106 ++ ...-table-canonical-phase-f3-claude-prompt.md | 138 ++ ...-table-canonical-phase-f4-claude-prompt.md | 86 + ...-table-canonical-phase-f5-claude-prompt.md | 136 ++ ...-table-canonical-phase-f6-claude-prompt.md | 110 ++ ...-table-canonical-phase-f7-claude-prompt.md | 96 + ...-table-canonical-phase-f8-claude-prompt.md | 197 ++ open-design | 1 + 54 files changed, 13999 insertions(+), 308 deletions(-) create mode 100644 .od-skills/web-prototype/SKILL.md create mode 100644 .od-skills/web-prototype/assets/template.html create mode 100644 .od-skills/web-prototype/example.html create mode 100644 .od-skills/web-prototype/open-design.json create mode 100644 .od-skills/web-prototype/references/checklist.md create mode 100644 .od-skills/web-prototype/references/layouts.md create mode 100644 notes/gbpark/2026-05-20-control-ide-refactor.md create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v2/index.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v2/numbering-v2.css create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v2/variant-a-cascade.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v2/variant-b-cp-workbench.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v2/variant-c-guard.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v3/index.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v3/numbering-v3.css create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v3/variant-a-quiet-admin.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v3/variant-b-cp-editor.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v3/variant-c-operations-review.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants-v3/variant-d-table-first.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants/index.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants/variant-a-workbench.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants/variant-b-console.html create mode 100644 notes/gbpark/2026-05-20-numbering-admin-variants/variant-c-validation.html create mode 100644 notes/gbpark/2026-05-20-numbering-rule-clean-v5.html create mode 100644 notes/gbpark/2026-05-20-numbering-rule-identity-v6.html create mode 100644 notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-input-parity-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-b2-b3-d9-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-c3-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-c4-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-d1-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-d10-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-d2-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-d3-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-d4-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-d5-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-d6-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-d7-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-d8-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-e1-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-e2-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-e3-claude-prompt.md create mode 100644 notes/gbpark/2026-05-20-table-canonical-phase-f1-claude-prompt.md create mode 100644 notes/gbpark/2026-05-21-table-canonical-phase-f2-claude-prompt.md create mode 100644 notes/gbpark/2026-05-21-table-canonical-phase-f3-claude-prompt.md create mode 100644 notes/gbpark/2026-05-21-table-canonical-phase-f4-claude-prompt.md create mode 100644 notes/gbpark/2026-05-21-table-canonical-phase-f5-claude-prompt.md create mode 100644 notes/gbpark/2026-05-21-table-canonical-phase-f6-claude-prompt.md create mode 100644 notes/gbpark/2026-05-21-table-canonical-phase-f7-claude-prompt.md create mode 100644 notes/gbpark/2026-05-21-table-canonical-phase-f8-claude-prompt.md create mode 160000 open-design diff --git a/.od-skills/web-prototype/SKILL.md b/.od-skills/web-prototype/SKILL.md new file mode 100644 index 00000000..d2b54690 --- /dev/null +++ b/.od-skills/web-prototype/SKILL.md @@ -0,0 +1,97 @@ +--- +name: web-prototype +description: | + General-purpose desktop web prototype. Single self-contained HTML file built + by copying the seed `assets/template.html` and pasting section layouts from + `references/layouts.md`. Default for any landing / marketing / docs / SaaS + page when no more specific skill matches. +triggers: + - "prototype" + - "mockup" + - "landing" + - "single page" + - "marketing page" + - "homepage" +od: + mode: prototype + platform: desktop + scenario: design + preview: + type: html + entry: index.html + design_system: + requires: true + sections: [color, typography, layout, components] +--- + +# Web Prototype Skill + +Produce a single, self-contained HTML prototype using the bundled seed and layout library — **not** by writing CSS from scratch. The seed already encodes good defaults (typography, spacing, accent budget). Your job is to compose it. + +## Resource map + +``` +web-prototype/ +├── SKILL.md ← you're reading this +├── assets/ +│ └── template.html ← seed: tokens + class system + chrome (READ FIRST) +└── references/ + ├── layouts.md ← 8 paste-ready section skeletons + └── checklist.md ← P0/P1/P2 self-review +``` + +## Workflow + +### Step 0 — Pre-flight (do this once before writing anything) + +1. **Read `assets/template.html` end-to-end** — at minimum through the ` + + +
+
+ + + +
+
+ +
+ +
+
+

[REPLACE] Eyebrow

+

[REPLACE] One sharp sentence about what this is.

+

[REPLACE] One subhead sentence — concrete value, not a tagline.

+
+ + +
+
+
+
+ + + + diff --git a/.od-skills/web-prototype/example.html b/.od-skills/web-prototype/example.html new file mode 100644 index 00000000..9b7dc490 --- /dev/null +++ b/.od-skills/web-prototype/example.html @@ -0,0 +1,81 @@ + + + + + + Tomato — focused work timer + + + +
+ + +
+
+
+

Twenty-five minutes at a time.

+

The pomodoro timer that actually keeps your hands off Slack. Block notifications, log every cycle, ship more before lunch.

+
+ + +
+
+
+
+
+

Block on, not off

+

Slack and email go quiet for 25 minutes. They come back loud at the break, with a digest.

+
+
+
+

Stats that don't lie

+

Weekly review tells you which days you actually shipped versus which you only seemed busy.

+
+
+
+

Team-friendly silences

+

Your status auto-updates so teammates know when to ask, when to wait, and when you're done.

+
+
+
+

Stop measuring meetings. Start measuring focus.

+

Free for solo. $4/mo per teammate after that.

+ +
+
+ + + diff --git a/.od-skills/web-prototype/open-design.json b/.od-skills/web-prototype/open-design.json new file mode 100644 index 00000000..e44b984d --- /dev/null +++ b/.od-skills/web-prototype/open-design.json @@ -0,0 +1,132 @@ +{ + "$schema": "https://open-design.ai/schemas/plugin.v1.json", + "specVersion": "1.0.0", + "name": "example-web-prototype", + "title": "Web Prototype", + "version": "0.1.1", + "description": "General-purpose desktop web prototype. Single self-contained HTML file built\nby copying the seed `assets/template.html` and pasting section layouts from\n`references/layouts.md`. Default for any landing / marketing / docs / SaaS\npage when no more specific skill matches.", + "license": "MIT", + "author": { + "name": "Open Design", + "url": "https://github.com/nexu-io" + }, + "homepage": "https://github.com/nexu-io/open-design/tree/main/plugins/_official/examples/web-prototype", + "tags": [ + "example", + "first-party", + "prototype", + "design", + "web", + "desktop", + "mockup", + "landing", + "single-page", + "marketing-page", + "homepage" + ], + "compat": { + "agentSkills": [ + { + "path": "./SKILL.md" + } + ] + }, + "od": { + "kind": "scenario", + "taskKind": "new-generation", + "mode": "prototype", + "platform": "desktop", + "scenario": "design", + "surface": "web", + "preview": { + "type": "html", + "entry": "./example.html" + }, + "useCase": { + "query": { + "en": "Build a {{fidelity}} {{artifactKind}} for {{audience}}. Use {{designSystem}} as the design-system direction and start from {{template}}. Single self-contained HTML file built by copying the seed `assets/template.html` and pasting section layouts from `references/layouts.md`.", + "zh-CN": "使用这个插件完成以下任务:为 {{audience}} 构建一个 {{fidelity}} 的 {{artifactKind}}。设计系统方向使用 {{designSystem}},从 {{template}} 开始。使用 `assets/template.html` 种子并从 `references/layouts.md` 粘贴版面,输出单文件 HTML。" + }, + "exampleOutputs": [ + { + "path": "./example.html", + "title": "Web Prototype" + } + ] + }, + "inputs": [ + { + "name": "artifactKind", + "label": "Artifact kind", + "type": "string", + "required": true, + "placeholder": "SaaS landing page", + "default": "web prototype" + }, + { + "name": "fidelity", + "label": "Fidelity", + "type": "select", + "required": true, + "options": [ + "wireframe", + "high-fidelity" + ], + "default": "high-fidelity" + }, + { + "name": "audience", + "label": "Audience", + "type": "string", + "required": true, + "placeholder": "startup founders evaluating an AI CRM", + "default": "product evaluators" + }, + { + "name": "designSystem", + "label": "Design system", + "type": "string", + "placeholder": "OpenAI, Linear, shadcn, or custom brand notes", + "default": "the active project design system" + }, + { + "name": "template", + "label": "Template", + "type": "string", + "placeholder": "marketing homepage, dashboard, docs page", + "default": "the bundled web prototype seed" + } + ], + "context": { + "skills": [ + { + "path": "./SKILL.md" + } + ], + "designSystem": { + "primary": true + }, + "assets": [ + "./example.html", + "./assets/template.html", + "./references/checklist.md", + "./references/layouts.md" + ] + }, + "pipeline": { + "stages": [ + { + "id": "generate", + "atoms": [ + "file-write", + "live-artifact" + ] + } + ] + }, + "capabilities": [ + "prompt:inject", + "fs:write" + ] + } +} diff --git a/.od-skills/web-prototype/references/checklist.md b/.od-skills/web-prototype/references/checklist.md new file mode 100644 index 00000000..0725a9c3 --- /dev/null +++ b/.od-skills/web-prototype/references/checklist.md @@ -0,0 +1,44 @@ +# Web prototype checklist + +Run this before emitting ``. P0 = must pass; P1 = should pass; P2 = nice to have. + +## P0 — must pass + +- [ ] **No raw hex outside `:root` token block.** Every color is `var(--bg)` / `var(--fg)` / `var(--muted)` / `var(--border)` / `var(--accent)` / `var(--surface)` (or a `color-mix()` of those). Grep `#[0-9a-fA-F]{3,8}` outside `:root{}` should return nothing. +- [ ] **All headings use `var(--font-display)`.** No sans-serif `

` / `

`. Inter / Roboto / system-sans never serve as a display face. +- [ ] **Accent appears at most twice per screen.** Count: eyebrow color, primary CTA fill, anything else? If three or more, demote one to `var(--fg)` or `var(--muted)`. +- [ ] **No purple/violet gradient backgrounds.** No `linear-gradient(... #a855f7 / #8b5cf6 / purple ...)`. The seed template has no gradients on backgrounds — keep it that way. +- [ ] **No emoji used as feature icons.** Use the inline SVG monoline marks shipped in Layout 3, or a tasteful single-character glyph in `--font-mono`. ✨ 🚀 🎯 are out. +- [ ] **No invented metrics.** Every number on the page came from the user, the brief, or is clearly labelled as a placeholder (e.g. `[REPLACE] · 38×`). "10× faster", "99.9% uptime" without source = remove. +- [ ] **No filler copy.** Zero "Feature One / Feature Two", lorem ipsum, "Lorem ipsum dolor". If a section feels empty, delete it; do not pad. +- [ ] **`data-od-id` on every top-level `
`.** Used by comment mode to target sections. +- [ ] **Mobile reflow works.** All `grid-2`, `grid-3`, `grid-4`, `grid-2-1`, `grid-1-2` collapse to one column at ≤920px (the default media query in `template.html` does this). Verify by mentally narrowing — no horizontal scroll. +- [ ] **No `scrollIntoView()` calls.** Breaks the OD preview iframe. Use `scrollTo({...})` if you need scroll behaviour. + +## P1 — should pass + +- [ ] **One decisive flourish.** A pull quote, a striking stat, a real-feeling photograph, one micro-animation on the hero. *One.* Not three. +- [ ] **Section rhythm alternates.** No two stat rows in a row. No two feature triplets in a row. No two quote blocks in a row. +- [ ] **Headlines under 14 words.** If longer, the writing is doing the design's job. +- [ ] **Lead text under 56 ch / two sentences.** `max-width: 60ch` on `.lead` enforces this; don't override. +- [ ] **CTA buttons say what happens.** "Start free" beats "Get Started". "Read the story" beats "Learn More". +- [ ] **Hover states present** for all `` and `.btn`. Seed template covers this. +- [ ] **Numerics use `.num` (mono, tabular).** Prices, stats, version numbers, dates. +- [ ] **One image style per page.** Don't mix square portrait headshots with widescreen product hero with vertical phone mock — pick a lane. + +## P2 — nice to have + +- [ ] **`text-wrap: pretty` / `balance`** on long paragraphs / headings (already on `

` and `h*` in seed). +- [ ] **`color-mix()` for derived tones.** No additional `--accent-50` / `--accent-300` Bootstrap-style tokens — derive on the spot. +- [ ] **Sticky topnav has frosted glass** (already in seed via `backdrop-filter: blur()`). +- [ ] **Loaded fonts are system-first.** Iowan Old Style / Charter for serif, system stack for sans. Only pull a Google Font if DESIGN.md specifies one. + +## Anti-slop spot-check + +Look at the page for two seconds. If your gut says any of: + +- "looks like every Cursor / Linear / Vercel ripoff I've seen this month" +- "this could be any AI startup's homepage" +- "the feature row has an icon, a heading, and three lines of vague benefit copy" + +…go back, replace one feature cell with something more specific to *this* product (a screenshot, a concrete example, a sample of the actual output), and remove one accent. diff --git a/.od-skills/web-prototype/references/layouts.md b/.od-skills/web-prototype/references/layouts.md new file mode 100644 index 00000000..b8a0b4e1 --- /dev/null +++ b/.od-skills/web-prototype/references/layouts.md @@ -0,0 +1,247 @@ +# Web prototype layouts + +**8 paste-ready section skeletons.** Drop into `

` of `assets/template.html`. Don't write sections from scratch — pick the closest layout, paste, swap copy. + +## Pre-flight (do this once before pasting anything) + +1. **Read `assets/template.html`** through the end of the ` + + +
+
Numbering Rule Admin - HTML drafts
+

실제 운영 파일에 넣기 전, 채번 admin을 세 방향으로 비교합니다.

+

각 시안은 독립 HTML입니다. A는 조립감, B는 관리 밀도, C는 충돌 검증을 우선순위로 둔 방향입니다.

+ +
+ +
+
A
+

조립 작업대형

+

다음 발번 프리뷰와 파트 파이프라인을 화면의 중심에 둔 방향. 규칙을 직접 만드는 느낌이 가장 강합니다.

+
preview firstpipelineinspector
+
+ 시안 A 열기 +
+ + +
+
B
+

운영 콘솔형

+

규칙 목록, 사용처, 현재 시퀀스, 리셋 정책을 한 화면에서 훑는 방향. 실무 admin 안정감이 큽니다.

+
dense tableright inspectorgovernance
+
+ 시안 B 열기 +
+ + +
+
C
+

검증 보드형

+

채번 변경이 운영 데이터와 충돌하지 않는지 먼저 보는 방향. 승인/검수 플로우가 필요한 팀에 맞습니다.

+
validationimpactapproval
+
+ 시안 C 열기 +
+
+ +

운영 React/TSX 파일은 이 갤러리에서 건드리지 않습니다. 실제 적용은 선택한 방향을 기준으로 별도 반영하면 됩니다.

+
+ + diff --git a/notes/gbpark/2026-05-20-numbering-admin-variants/variant-a-workbench.html b/notes/gbpark/2026-05-20-numbering-admin-variants/variant-a-workbench.html new file mode 100644 index 00000000..b7bc1270 --- /dev/null +++ b/notes/gbpark/2026-05-20-numbering-admin-variants/variant-a-workbench.html @@ -0,0 +1,229 @@ + + + + + + 채번 Admin 시안 A - 조립 작업대형 + + + +
+
+

#채번 관리 규칙을 조립하고 다음 코드를 확인

+
18 규칙14 연결4 미사용
+
Ctrl K
+
+ +
+ + +
+
+
+

수주번호 자동채번

사용 중sales_order.order_no
+
테이블 sales_order컬럼 order_no리셋 월별구분자 -
+
+
+
+ +
+
+
NEXT NUMBER PREVIEW
+
SO-202605-0048
+
+ 현재 순번47 + 다음 순번48 + 초기화 기준매월 1일 + 마지막 발번2026-05-20 09:42 +
+
+
+ +
+
+

파트 조립 클릭하면 아래 속성이 바뀝니다

+
+ - + - + + +
+
+ +
+
+

선택 파트 속성

sequence
+
+ + + + + + +
+
+
+

시퀀스 운영

+
+
CURRENT47
NEXT48
+ + +
+
+
+
+
저장하지 않은 변경 있음
+
+
+
+ + + + diff --git a/notes/gbpark/2026-05-20-numbering-admin-variants/variant-b-console.html b/notes/gbpark/2026-05-20-numbering-admin-variants/variant-b-console.html new file mode 100644 index 00000000..b956a7e0 --- /dev/null +++ b/notes/gbpark/2026-05-20-numbering-admin-variants/variant-b-console.html @@ -0,0 +1,169 @@ + + + + + + 채번 Admin 시안 B - 운영 콘솔형 + + + +
+
+
NR

채번 운영 콘솔

규칙, 연결 컬럼, 현재 시퀀스, 리셋 정책을 한 화면에서 관리

+
+
+
+
Total rules18
+
Linked14
+
Need review2
+
Monthly reset9
+
Generated today126
+
+
+
+
+ +
+
+
+ + + + + + + + + +
RulePatternTargetStatusResetCurrentNextUpdated
수주번호 자동채번
영업 수주 등록
SO-{yyyyMM}-{0000}sales_order.order_noACTIVE월별474805-20
구매발주 번호
구매 발주서
PO-{yyyy}-{00000}purchase_order.po_noACTIVE연도별13113205-18
품목마스터 코드
분류 접두어 확인 필요
ITEM-{category}-{000}item.item_codeREVIEW없음7805-16
작업지시 번호
아직 연결된 컬럼 없음
WO-{yyyyMMdd}-{000}-UNUSED일별111205-12
입고검사 LOT
품질 입고 검사
IQC-{date}-{seq}incoming_inspection.lot_noACTIVE일별5605-20
+
+
+ +
+
+ + + diff --git a/notes/gbpark/2026-05-20-numbering-admin-variants/variant-c-validation.html b/notes/gbpark/2026-05-20-numbering-admin-variants/variant-c-validation.html new file mode 100644 index 00000000..7e73aa5a --- /dev/null +++ b/notes/gbpark/2026-05-20-numbering-admin-variants/variant-c-validation.html @@ -0,0 +1,211 @@ + + + + + + 채번 Admin 시안 C - 검증 보드형 + + + +
+
+
+
QA

채번 변경 검증 보드

규칙 저장 전에 중복 가능성, 연결 컬럼, 시퀀스 리셋 영향을 확인합니다.

+
+
+
+
Collision risk2
+
Need mapping3
+
Ready to save9
+
Approved4
+
+
+
+
+
+ +
+
+
+
+

충돌 위험

2
+
+

품목마스터 코드

collision
ITEM-{category}-{000}

기존 수동 코드와 신규 자동 순번 범위가 겹칠 수 있습니다.

item.item_code현재 7다음 8
+

거래처 코드

collision
CUST-{0000}

과거 마이그레이션 데이터의 접두어가 동일합니다.

customer.customer_codelegacy
+
+
+
+

검토 필요

3
+
+

작업지시 번호

mapping
WO-{yyyyMMdd}-{000}

규칙은 정상이나 아직 연결된 컬럼이 없습니다.

unlinkeddaily reset
+

입고검사 LOT

reset
IQC-{date}-{seq}

일별 리셋 변경 시 오늘 발번된 LOT와의 영향 확인이 필요합니다.

incoming_inspection5 today
+
+
+
+

저장 가능

9
+
+

수주번호 자동채번

ready
SO-{yyyyMM}-{0000}

연결 컬럼과 다음 순번이 정상입니다.

sales_ordermonthly
+

구매발주 번호

ready
PO-{yyyy}-{00000}

연도별 리셋 정책과 기존 발번 이력이 일치합니다.

purchase_orderyearly
+
+
+
+
+ +
+
+ + + diff --git a/notes/gbpark/2026-05-20-numbering-rule-clean-v5.html b/notes/gbpark/2026-05-20-numbering-rule-clean-v5.html new file mode 100644 index 00000000..768cfbf1 --- /dev/null +++ b/notes/gbpark/2026-05-20-numbering-rule-clean-v5.html @@ -0,0 +1,945 @@ + + + + +채번 관리 — v5 (차분 정돈본) + + + + + + +
+ + +
+
+

채번 관리

+ + 자동 번호 규칙을 만들고 컬럼에 연결합니다 · 14 규칙 · 9 사용 중 + +
+
+ ⌘ K + + +
+
+ +
+ + + + + +
+ + +
+
+

+ 수주번호 + 사용 중 + RULE-1778487089084 +

+
+ 생성 2026-03-12 by gbpark + 마지막 수정 2026-05-14 16:22 + 지금까지 142건 발번 +
+
+
+ + + + +
+
+ + +
+ + 현재 + SO-2026-05-0142 + + + + 다음 발번 + SO-2026-05-0143 + + + 매월 1일 초기화 + · + 시퀀스 142 → 143 + +
+ + +
+
+

코드를 이루는 조각

+ 4개 + 조각 클릭해서 편집 · 사이 hover 로 추가 +
+ +
+
+ +
+
+
+
1번고정
+ SO + +
+ + - + +
+
2번년도
+ 2026 + +
+ + - + +
+
3번
+ 05 + +
+ + - + +
+
4번순번
+ 0143 + +
+ +
+
+ + +
+
+
+ 2번 조각 + 년도 설정 +
+ +
+
+
+ +
+ + + + + + +
+
+
+ +
+ + + + +
+ 매월 1일 00:00 에 순번이 1 부터 다시 시작 +
+
+
+ + +
+ 조각 추가: + + + + + + +
+
+ + +
+
+
+ 연결된 컬럼 1 + +
+
+ user_info·order_no +
단일 연결 · 향후 N:M 확장 예정
+
+
+
+
+ 시퀀스 현황 + +
+
+ 142143 +
매월 1일 초기화 · 다음 리셋 2026-06-01 00:00
+
+
+
+ + +
+
+ 저장하지 않은 변경 1건 +
+
+ + +
+
+ +
+
+
+ + + + diff --git a/notes/gbpark/2026-05-20-numbering-rule-identity-v6.html b/notes/gbpark/2026-05-20-numbering-rule-identity-v6.html new file mode 100644 index 00000000..fd5977c9 --- /dev/null +++ b/notes/gbpark/2026-05-20-numbering-rule-identity-v6.html @@ -0,0 +1,1164 @@ + + + + +채번 관리 — v6 (정체성 강화본) + + + + + + +
+ + +
+
+

+ + 채번 관리 +

+ + 새 데이터가 등록될 때 자동으로 부여되는 코드 규칙 · 14 규칙 · 9 사용 중 + +
+
+ ⌘ K + + +
+
+ +
+ + + + + +
+ + +
+
+

+ 수주번호 + 사용 중 + RULE-1778487089084 +

+
+ 생성 2026-03-12 by gbpark + 마지막 수정 2026-05-14 16:22 + 적용 컬럼 user_info.order_no +
+
+
+ + + + +
+
+ + +
+
+ + 다음에 발번될 코드 + + PREVIEW + +
+
+ SO + 고정 +
+ - +
+ 2026 + 년도 +
+ - +
+ 05 + +
+ - +
+ 0143 + 순번 (자동 증가) +
+
+ +
+ 새 수주가 등록되면 이 번호가 자동으로 매겨집니다 +
+ +
+ + 현재 시퀀스 + 142 143 + + + 지금까지 + 142건 발번 + + + 초기화 주기 + 매월 1일 + + + 다음 리셋 + 2026-06-01 00:00 + +
+
+
+ + +
+
+

코드를 이루는 조각

+ 4개 + 조각 클릭해서 편집 · 사이 hover 로 추가 +
+ +
+
+ +
+
+
+ FIXED + 1번 + 고정 글자 + SO + +
+ + - + +
+ YYYY + 2번 + 년도 + 2026 + +
+ + - + +
+ MM + 3번 + + 05 + +
+ + - + +
+ SEQ · 4자리 + 4번 + 순번 + 0143 + +
+ +
+
+ + +
+
+
+ 2번 조각 + 년도 설정 +
+ +
+
+
+ +
+ + + + + + +
+
+
+ +
+ + + + +
+ 매월 1일 00:00 에 순번이 1 부터 다시 시작 +
+
+
+ + +
+ 조각 추가: + + + + + + +
+
+ + +
+
+
+ 연결된 컬럼 1 + +
+
+ user_info·order_no +
단일 연결 · 향후 N:M 확장 예정
+
+
+
+
+ 시퀀스 수동 조정 + +
+
+ 142143 +
시퀀스 직접 수정은 발번 코드와 충돌 가능
+
+
+
+ + +
+
+ 저장하지 않은 변경 1건 +
+
+ + +
+
+ +
+
+
+ + + + diff --git a/notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md b/notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md new file mode 100644 index 00000000..96268918 --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md @@ -0,0 +1,1596 @@ +# 2026-05-20 — Table Canonical Cleanup Plan + +이 문서는 `notes/gbpark/2026-05-20-table-canonical-input-parity-claude-prompt.md` 가 요구한 +"table cleanup 을 canonical input cleanup 동일 완성도로 끝내는 작업" 을 **다음 세션에서 바로 +실행 가능한 phase 단위** 로 쪼갠 plan 이다. + +이번 세션에서 직접 진행한 것은 본 plan 문서 하나뿐 (사용자 지시로 코드 변경 금지). + +연관 문서: +- 원본 프롬프트: `notes/gbpark/2026-05-20-table-canonical-input-parity-claude-prompt.md` +- 직전 cleanup 보고서: `notes/gbpark/2026-05-19-canonical-data-view-cleanup-followup.md` +- input canonical 완료 기준: `notes/gbpark/2026-05-08-input-canonical-migration.md`, + `notes/gbpark/2026-05-12-codex-handoff-input-canonical.md` + +--- + +## 1. 한 줄 요약 + +**옛 본체 14,060줄 + ConfigPanel 1,361줄 + 외부 의존성 30+ 파일** 을 canonical +`table` 컴포넌트 (현재 680줄 prototype) 에 흡수하고, 옛 alias/schema/runtime 을 +0건으로 만드는 작업. 단일 세션 token/time 한계로 불가능 → **15 sub-phase +(B.1 ~ G.1) 로 분할**. 권장 세션 수 12 ~ 15. + +--- + +## 2. 현 상태 베이스라인 (Phase 0 완료) + +### 2.1 작업 트리 +- branch: `gbpark-node` +- untracked: `notes/gbpark/2026-05-20-table-canonical-input-parity-claude-prompt.md` (원본) + + 본 plan 문서 +- 금지 토큰 (`v2-input` / `v2-select` / `V2InputRenderer` / `V2SelectRenderer`): **0건** ✅ +- `EntityPicker` / `EntitySearchModal` (canonical input): **0건** ✅ + +### 2.2 파일 크기 + +| 파일 | 줄 수 | 크기 | +|---|---|---| +| `frontend/lib/registry/components/table/TableComponent.tsx` (canonical) | **680** | 26 KB | +| `frontend/lib/registry/components/table/InvTableConfigPanel.tsx` (canonical) | (전체) | 30 KB | +| `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` (legacy 본체) | **6,838** | 274 KB | +| `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` (V2 본체) | **7,222** | 285 KB | +| `frontend/lib/registry/components/table/_shared/V2TableListContainerWrapper.tsx` | 92 | 3 KB | +| `frontend/lib/registry/components/table/_shared/TableListConfigPanel.tsx` | **1,361** | 57 KB | +| `frontend/lib/registry/components/table/_shared/SingleTableWithSticky.tsx` (shared) | 591 | 30 KB | +| `frontend/lib/registry/components/table/_shared/CardModeRenderer.tsx` (shared) | 259 | 10 KB | +| `frontend/lib/registry/components/table/_shared/tableListConfigTypes.ts` | 357 | 11 KB | +| `frontend/components/v2/config-panels/V2TableListConfigPanel.tsx` | **1,518** | (대형) | +| `frontend/components/v2/V2List.tsx` | 176 | (small wrapper) | +| `frontend/components/v2/config-panels/V2ListConfigPanel.tsx` | 333 | | +| `frontend/components/v2/config-panels/InvDataConfigPanel.tsx` | 172 | | + +**흡수 대상 총량: 약 16,940 줄.** + +### 2.3 잔존 매칭 카운트 + +`rg "v2-table-list|table-list|V2TableList|TableListComponent|TableListConfigPanel|V2TableListContainerWrapper" frontend/lib frontend/components frontend/app frontend/types` +→ **176 매칭 / 43 파일** + +--- + +## 3. 잔존 매칭 전체 분류 (43 파일) + +각 파일을 (A) **active runtime/config** (B) **alias/schema/helper** (C) **stale comment/doc** +(D) **domain enum (table-list != component type)** 로 분류. + +### 3.1 (A) 옛 본체 자체 — Phase F 에서 삭제 대상 + +| 파일 | 매칭 | 비고 | +|---|---|---| +| `_shared/TableListComponent.tsx` (6838줄) | 7건 | legacy 본체. `component_type: "table-list"` self-id 2회 + `tableId = \`table-list-${id}\`` | +| `_shared/V2TableListComponent.tsx` (7222줄) | 13건 | V2 본체. `componentType="v2-table-list"` self-id (line 7216) + 다수 | +| `_shared/V2TableListContainerWrapper.tsx` (92줄) | 5건 | ResizeObserver wrapper | +| `_shared/TableListConfigPanel.tsx` (1361줄) | 6건 | 옛 ConfigPanel 본체 (디버그 console.log 4건 포함) | +| `_shared/SingleTableWithSticky.tsx` (591줄) | 7건 | shared. `variant="v2"` 분기는 v2 흡수 후 제거 가능. **FlowWidget 도 사용** — 통째 삭제 X | +| `_shared/CardModeRenderer.tsx` (259줄) | 3건 | shared. `variant="v2"` 분기는 흡수 후 제거 가능 | + +**소계: 41 매칭 (옛 본체 self-reference)** + +### 3.2 (A) Runtime delegation — Phase F.1 제거 대상 + +| 파일 | 매칭 | 위치 | +|---|---|---| +| `frontend/lib/registry/components/table/TableComponent.tsx` | 11건 | early delegation 분기 (line 113, 133) + MEANINGFUL_TABLE_TYPES Set + docstring | +| `frontend/lib/registry/components/table/index.ts` | 2건 | docstring "흡수 대상" 목록 | +| `frontend/lib/registry/components/table/types.ts` | 2건 | docstring | +| `frontend/lib/registry/components/index.ts` | 4건 | 주석 (alias 라우팅 설명) | +| `frontend/lib/registry/DynamicComponentRenderer.tsx` | 2건 | `LEGACY_TO_UNIFIED` (line 388) + `isTableLikeComponentType` 사용 주석 (line 936) | +| `frontend/lib/utils/templateMigrate.ts` | 2건 | `LEGACY_TO_UNIFIED` (line 44-45) | + +**소계: 23 매칭** + +### 3.3 (A) Config Panel layer — Phase E 제거 대상 + +| 파일 | 매칭 | 비고 | +|---|---|---| +| `frontend/components/v2/V2List.tsx` | 7건 | `TableListComponent` 직접 import (line 11) + `type: "table-list"` 객체 생성 (line 53) + 콜백 wrapping (line 155) | +| `frontend/components/v2/config-panels/InvDataConfigPanel.tsx` | 7건 | brumb 의 `v2-table-list` type (line 22, 25, 68, 88, 149, 150) + `V2TableListConfigPanel` import | +| `frontend/components/v2/config-panels/V2TableListConfigPanel.tsx` | 6건 | 1518줄 ConfigPanel 본체 self-id + import 흔적 | +| `frontend/components/v2/config-panels/V2ListConfigPanel.tsx` | 3건 | `TableListConfigPanel` import (line 25) + 위임 (line 319) | +| `frontend/lib/utils/getComponentConfigPanel.tsx` | 3건 | `CONFIG_PANEL_ALIAS["v2-table-list"] = "table"`, `CONFIG_PANEL_ALIAS["table-list"] = "table"` (line 146) | + +**소계: 26 매칭** + +### 3.4 (A) Studio panels — 외부 active branch (이미 helper 화 끝남 — Phase D 이후 grep 정리만) + +이 파일들은 `isTableLikeComponentType` helper 를 이미 호출하지만 **주석 또는 fallback 으로 옛 ID 가 명시**되어 있음. 본체 흡수 + helper 의 Set 에서 table-list/v2-table-list 제거 후 자연 소멸. + +| 파일 | 매칭 | 분류 | +|---|---|---| +| `frontend/lib/utils/buttonActions.ts` | 6건 | helper 사용. line 3268/3464/5283 주석 + line 5284 `findTableListComponent` 함수명 (rename 후보) | +| `frontend/lib/utils/componentTypeUtils.ts` | 6건 | `TABLE_LIKE_COMPONENT_TYPES` Set 본체. Phase F.7 에서 v2-table-list/table-list 제거 | +| `frontend/components/screen/modals/MultilangSettingsModal.tsx` | 6건 | `case "table-list" / "v2-table-list"` (line 156-157) + Set (line 201-202) | +| `frontend/components/screen/panels/ComponentsPanel.tsx` | 8건 | hiddenComponents (line 145 "table-list", line 179 "v2-table-list") + 아이콘 map (line 238) + 주석 | +| `frontend/components/screen/panels/V2PropertiesPanel.tsx` | 1건 | 주석 | +| `frontend/components/screen/widgets/TabsWidget.tsx` | 1건 | 주석 | +| `frontend/components/screen/ScreenNode.tsx` | 3건 | 주석 | +| `frontend/components/screen/RealtimePreviewDynamic.tsx` | 3건 | 주석 (helper 호출) | +| `frontend/components/screen/InvyoneStudio.tsx` | 2건 | drag/drop 분기의 fullWidthComponents weight (line 4106-4107) | +| `frontend/components/screen-embedding/ScreenSplitPanel.tsx` | 1건 | 주석 | +| `frontend/components/screen/config-panels/button/DataTab.tsx` | 1건 | 주석 (helper 호출) | +| `frontend/components/screen/config-panels/button-config/ActionTab.tsx` | 2건 | 주석 (helper 호출) | +| `frontend/components/v2/config-panels/InvLegacyButtonConfigPanel.tsx` | 1건 | 주석 (helper 호출) | +| `frontend/lib/registry/components/button-primary/ButtonPrimaryComponent.tsx` | 1건 | 주석 | +| `frontend/lib/registry/components/v2-button-primary/ButtonPrimaryComponent.tsx` | 1건 | 주석 | +| `frontend/lib/registry/components/universal-form-modal/UniversalFormModalConfigPanel.tsx` | 1건 | 주석 | +| `frontend/lib/registry/components/table-search-widget/TableSearchWidget.tsx` | 1건 | 주석 | +| `frontend/lib/registry/components/v2-table-search-widget/TableSearchWidget.tsx` | 1건 | 주석 | +| `frontend/lib/registry/components/domain/v2-approval-step/ApprovalStepConfigPanel.tsx` | 1건 | 주석 (TableListConfigPanel 참조 — 옛 본체 폐기 후 적용) | +| `frontend/lib/utils/responsiveDefaults.ts` | 2건 | `fullWidthComponents` 배열 (line 27 "table-list") | +| `frontend/app/(main)/screens/[screenId]/page.tsx` | 1건 | 주석 | + +**소계: 48 매칭 (Phase F.7 이후 옛 ID 제거)** + +### 3.5 (B) Schema / Type 정의 + +| 파일 | 매칭 | 위치 | +|---|---|---| +| `frontend/lib/schemas/componentConfig.ts` | 4건 | `v2TableListOverridesSchema` (line 226) + `componentOverridesRegistry["v2-table-list"]` (line 554) + `componentDefaultsRegistry["v2-table-list"]` (line 589) + line 128 주석 | +| `frontend/types/v2-components.ts` | 1건 | `LEGACY_TO_V2_MAP` table-list 매핑 제거 흔적 주석 (line 357) | +| `frontend/types/invyone-component.ts` | 1건 | `componentId` 예시 주석 (line 829) | +| `frontend/types/screen-management.ts` | 1건 | `component_type` 예시 주석 (line 223) | +| `frontend/types/component-events.ts` | 3건 | publisher/subscriber 주석 (line 14, 72, 116) | +| `frontend/types/table-options.ts` | 1건 | `table_id` 예시 주석 (line 53) — table-options Context 용 | +| `frontend/lib/api/screenGroup.ts` | 1건 | `componentKind` 예시 주석 (line 369) | +| `frontend/lib/fieldConfig/adapters.ts` | 3건 | `fieldsToColumns` docstring | + +**소계: 15 매칭 (Phase F.5 + 정리)** + +### 3.6 (C) Stale comment / docs + +| 파일 | 매칭 | 분류 | +|---|---|---| +| `frontend/lib/registry/components/selected-items-detail-input/README.md` | 3건 | docs only | +| `frontend/lib/registry/components/domain/v2-timeline-scheduler/README.md` | 2건 | docs only | +| `frontend/app/test-card-responsive/page.tsx` | 8건 | 테스트 페이지 (ResizeObserver demo). Phase F 이후 정리 | + +**소계: 13 매칭** + +### 3.7 (D) Domain enum — table-list 가 컴포넌트 type 이 아님 + +`repeat-container` 의 `dataSourceType` enum 값이 "table-list" — **컴포넌트 ID 가 아니라 데이터 소스 모드**. **건드리지 말 것.** + +| 파일 | 매칭 | 주의 | +|---|---|---| +| `frontend/lib/registry/components/repeat-container/types.ts` | 1건 | `DataSourceType = "table-list" \| ...` | +| `frontend/lib/registry/components/v2-repeat-container/types.ts` | 1건 | 동일 | +| `frontend/lib/registry/components/repeat-container/RepeatContainerConfigPanel.tsx` | 2건 | SelectItem value="table-list" UI | +| `frontend/lib/registry/components/v2-repeat-container/RepeatContainerConfigPanel.tsx` | 2건 | 동일 | +| `frontend/components/v2/config-panels/V2RepeatContainerConfigPanel.tsx` | 2건 | 동일 | + +**소계: 8 매칭. Phase F 범위 외. 별도 rename 트랙 (예: "legacyTableList") 필요하나 본 plan 의 scope 아님.** + +### 3.8 매칭 합산 검증 + +| 그룹 | 매칭 | +|---|---| +| 3.1 (옛 본체 self-ref) | 41 | +| 3.2 (Runtime delegation) | 23 | +| 3.3 (Config Panel layer) | 26 | +| 3.4 (Studio external) | 48 | +| 3.5 (Schema / Type) | 15 | +| 3.6 (Stale comment) | 13 | +| 3.7 (Domain enum) | 8 | +| **합계** | **174** | + +(±2 = 동일 파일 중복 매칭 카운트 차이. grep 카운트 176 과 일치) + +--- + +## 4. 외부 의존성 그래프 + +옛 본체 (`_shared/TableListComponent` + `_shared/V2TableListComponent`) 가 의존하는 외부 시스템: + +### 4.1 Context (provider 가 외부에 wiring 됨) + +| Context | 위치 | 누가 wiring | 사용 | +|---|---|---|---| +| `useSplitPanelContext` | `@/contexts/SplitPanelContext` | `lib/registry/components/v2-split-panel-layout/` 등 split-panel 컴포넌트의 provider | master-detail UX. canonical table.displayMode='split' 이 미구현이라 보존 hard blocker (§ 2026-05-19 후속 §4) | +| `useTableOptions` | `@/contexts/TableOptionsContext` | `TableSettingsModal`, `TableOptionsToolbar`, `ColumnVisibilityPanel`, `FilterPanel`, `GroupingPanel` (5 파일) | 사용자 컬럼 visibility / filter / grouping 영구 저장 | +| `useScreenContextOptional` | `@/contexts/ScreenContext` | `(main)/screens/[screenId]/page.tsx` 등 | 화면 메타 | +| `useTabId` | `@/contexts/TabIdContext` | V2 전용 (multi-tab) | V2 본체만 | + +### 4.2 Store (전역 state) + +| Store | 위치 | 사용 | +|---|---|---| +| `tableDisplayStore` | `@/stores/tableDisplayStore` | 컬럼 visibility 저장 / 사용자 옵션 | +| `v2EventBus` / `V2_EVENTS` | `@/lib/v2-core` | V2 row select / refresh event broadcast | + +### 4.3 Modal / Widget (별도 UI) + +| 컴포넌트 | 위치 | 사용 | +|---|---|---| +| `TableOptionsModal` | `@/components/common/TableOptionsModal` | 옵션 모달 (filter/sort/grouping/columns 통합) | +| `AdvancedSearchFilters` | `@/components/screen/filters/AdvancedSearchFilters` | 고급 검색 위젯 | +| `TableSettingsModal` / `TableOptionsToolbar` / `ColumnVisibilityPanel` / `GroupingPanel` / `FilterPanel` | `@/components/screen/table-options/*` | TableOptionsContext 의 UI | + +### 4.4 API client + +| 모듈 | 위치 | 사용 | +|---|---|---| +| `tableTypeApi` | `@/lib/api/screen` | 테이블 컬럼 메타 | +| `entityJoinApi` | `@/lib/api/entityJoin` | 엔티티 조인 정보 | +| `codeCache` | `@/lib/caching/codeCache` | 공통 코드 캐시 | +| `useEntityJoinOptimization` | `@/lib/hooks/useEntityJoinOptimization` | 엔티티 조인 최적화 | +| `getFullImageUrl`, `getFilePreviewUrl` | `@/lib/api/client` + `@/lib/api/file` | 이미지/파일 URL | + +### 4.5 Type 계약 + +| 타입 | 위치 | 사용 | +|---|---|---| +| `DataProvidable`, `DataReceivable`, `DataReceiverConfig` | `@/types/data-transfer` | master-detail data transfer | +| `DataReceivableComponentType`, `EntityJoinColumnMeta` | 동일 | V2 본체만 | +| `TableFilter`, `ColumnVisibility`, `GroupSumConfig` | `@/types/table-options` | TableOptions Context 의 contracts | + +### 4.6 외부 의존 사용처 사용자 그래프 (역방향) + +`useSplitPanelContext` 사용: +- `lib/registry/components/v2-split-panel-layout/SplitPanelLayoutComponent.tsx` +- `lib/registry/components/split-panel-layout/SplitPanelLayoutComponent.tsx` +- `_shared/TableListComponent.tsx` (line 316) ★ 흡수 대상 +- `_shared/V2TableListComponent.tsx` (line 648) ★ 흡수 대상 + +`useTableOptions` 사용 (생산자 + 소비자 7+ 파일): +- `_shared/TableListComponent.tsx` (line 339) +- `_shared/V2TableListComponent.tsx` (line 671) +- `components/screen/table-options/*` (5 파일) + +`DataProvidable / DataReceivable` 의 다른 사용자: +- `lib/registry/components/v2-button-primary/ButtonPrimaryComponent.tsx` +- `lib/registry/components/button-primary/ButtonPrimaryComponent.tsx` +- `lib/registry/components/conditional-container/ConditionalContainerComponent.tsx` +- `lib/registry/components/related-data-buttons/RelatedDataButtonsComponent.tsx` +- `components/screen-embedding/EmbeddedScreen.tsx` +- `components/screen/InteractiveScreenViewer.tsx` +- `components/screen/InteractiveScreenViewerDynamic.tsx` +- `components/screen/InteractiveDataTable.tsx` + +--- + +## 5. Parity gap 체크리스트 — canonical `TableComponent` vs `_shared/{Table,V2}TableListComponent` + +prompt 의 "Phase 1 parity inventory" 24개 기능 + 추가 발견 항목. +각 항목의 (1) 흡수 위치 (2) 외부 의존성 (3) 권장 phase 를 명시. + +### 5.1 완전히 미구현 — canonical 에서 새로 작성 필요 + +| # | 기능 | 외부 의존 | 권장 phase | 흡수 후 옛본체 line | +|---|---|---|---|---| +| 1 | 인라인 편집 (category/code) | `codeCache`, `entityJoinApi` | D.3 | TableList ~3700-4200 / V2 ~4500-5000 | +| 2 | 인라인 편집 (date / datetime) | DatePicker | D.3 | TableList ~4200-4400 / V2 ~5000-5200 | +| 3 | 인라인 편집 (number) | format util | D.3 | TableList ~4400-4500 / V2 ~5200-5300 | +| 4 | entity 조인 컬럼 다중 표시 (`entityDisplayConfig`) | `useEntityJoinOptimization` | D.5 | V2 ~3100-3500 | +| 5 | 컬럼 고정 left/right (`fixed`, `fixedOrder`) | (없음, CSS sticky) | D.1 | TableList ~5000-5300 / V2 ~5400-5800 | +| 6 | 사용자 컬럼 visibility 영구 저장 | `tableDisplayStore` + `useTableOptions` | D.1 + B.1 | 다수 분기 | +| 7 | linked filter | (없음, props 콜백) | D.2 | TableList ~2200-2400 / V2 ~2800-3000 | +| 8 | exclude filter | `tableTypeApi` | D.2 | TableList ~2400-2600 / V2 ~3000-3200 | +| 9 | dataFilter (정적 컬럼 값) | (없음) | D.2 | TableList ~2000-2200 | +| 10 | GroupSum (그룹 소계) | `GroupSumConfig` | D.8 | V2 ~6000-6300 | +| 11 | DataProvidable adapter | `DataProvidable` type | B.3 + D.9 | TableList line 805 / V2 line 1143 | +| 12 | DataReceivable adapter | `DataReceivable` type | B.3 + D.9 | TableList line 831 / V2 line 1183 | +| 13 | Excel 내보내기 (XLSX) | `xlsx` package | D.6 | TableList ~5500-5700 / V2 ~6300-6500 | +| 14 | TableCellImage (인증된 이미지) | `getFullImageUrl` + `getFilePreviewUrl` | D.5 | V2 line 21-124 | +| 15 | TableCellFile (파일명 + 모달) | `getFileInfoByObjid` | D.5 | V2 line 126-300 | +| 16 | row action 컬럼 (view/edit/delete/custom) | (없음, props 콜백) | D.4 | TableList ~5800-6000 / V2 ~6600-6900 | +| 17 | bulk actions | (없음, props 콜백) | D.4 | TableList ~6000-6100 / V2 ~6900-7000 | +| 18 | TableOptionsModal 통합 | `TableOptionsModal` + `useTableOptions` | B.1 + D.8 | 다수 분기 | +| 19 | AdvancedSearchFilters 통합 | `AdvancedSearchFilters` | D.2 | TableList ~3000-3300 | +| 20 | 검색 highlight | (없음) | D.2 | TableList ~3300-3500 / V2 ~3700-3900 | +| 21 | horizontalScroll + min/maxColumnWidth | (없음, CSS) | D.1 | TableList ~5300-5500 / V2 ~5800-6000 | +| 22 | autoLoad / refreshInterval | (없음, useEffect) | D.6 | TableList ~700-900 | +| 23 | SplitPanelContext 통합 (master-detail) | `useSplitPanelContext` | B.2 + D.9 | TableList line 316 / V2 line 648 | +| 24 | ResizeObserver 카드 fallback | (없음) | D.7 | V2TableListContainerWrapper.tsx | +| 25 | defaultSort | (없음) | D.6 | TableList ~1800-1900 | +| 26 | 다국어 langKey (`useScreenMultiLang`) | `ScreenMultiLangContext` | D.5 (보조) | SingleTableWithSticky 의 langKey 처리 | +| 27 | 자동생성 (autoGeneration) — uuid/current_user/numbering_rule 등 | `lib/registry/components/input/autoGeneration.ts` | D.10 | TableList ~1300-1500 | +| 28 | Excel paste / 클립보드 (V2 only) | (DOM event) | D.6 | V2 ~6500-6700 | +| 29 | `customTableName` / `useCustomTable` / `isReadOnly` | (없음) | C.1 | config 필드만 | +| 30 | 8 toolbar buttons (`showEditMode/Excel/Pdf/Copy/Search/Filter/Refresh/PaginationRefresh`) | (없음, button render) | C.5 + D.6 | TableList ~2900-3000 | + +### 5.2 부분 구현 — canonical 에 기본만, 옛 본체에 풀 옵션 + +| # | 기능 | 현 canonical 상태 | 옛 본체 추가 | +|---|---|---|---| +| 31 | 카드 모드 | `views/CardView` 가 cardConfig minimal 만 처리 | `cardConfig.showActions`, `cardHeight`, `idColumn` 등 풀 옵션 / **CardModeRenderer 의 variant="v2"** 흡수 | +| 32 | 페이지네이션 | `pagination.pageSize` 만 | `showSizeSelector` / `showPageInfo` / `pageSizeOptions` / `currentPage` / position | +| 33 | 정렬 | 헤더 클릭 toggle | `defaultSort`, multi-column sort | +| 34 | checkbox | single/multiple toggle | `position: "left"/"right"`, `selectAll`, `multiple` 토글 (CheckboxConfig) | +| 35 | tableStyle | striped/hoverable/bordered | `theme: striped/bordered/minimal`, `headerStyle: default/dark/light`, `borderStyle: none/light/heavy`, `alternateRows`, `hoverEffect` | +| 36 | 검색 | 외부 `searchParams` 만 받음 | 자체 검색바 / Excel 셀 복사 / clipboard / 검색 highlight | +| 37 | error 표시 | "데이터가 없습니다" 한 줄 | toast + showErrorToast + 디테일 | +| 38 | 행 클릭 / 더블클릭 | single click 만 | `onRowDoubleClick` + 디테일 | + +### 5.3 Canonical 에만 있고 옛 본체에 없음 (보존할 것) + +| # | 기능 | +|---|---| +| 39 | `displayMode: "pivot"` (`views/PivotView`) — 옛 본체엔 pivot 없음 | +| 40 | `displayMode: "grouped"` (`views/GroupedView`) — 옛 본체는 카드/테이블만 | +| 41 | Field-based 컬럼 (`fields: FieldConfig[]`) — INVYONE 의 새 표준 | +| 42 | DataPort 표준 (`searchParams`, `refreshTrigger` input / `selectedRow`, `selectedRows` output) | + +### 5.4 흡수 후 외부 소멸 가능 파일 (옛 본체 폐기 후) + +- `_shared/TableListComponent.tsx` (6838줄) +- `_shared/V2TableListComponent.tsx` (7222줄) +- `_shared/V2TableListContainerWrapper.tsx` (92줄) +- `_shared/TableListConfigPanel.tsx` (1361줄) +- `_shared/tableListConfigTypes.ts` (357줄) — 또는 canonical types.ts 로 흡수 +- `components/v2/V2List.tsx` (176줄) +- `components/v2/config-panels/V2TableListConfigPanel.tsx` (1518줄) +- `components/v2/config-panels/V2ListConfigPanel.tsx` (333줄) — InvDataConfigPanel 단순화 후 + +**보존 (다른 곳에서 사용):** +- `_shared/SingleTableWithSticky.tsx` — FlowWidget 도 사용. `variant="v2"` 분기만 제거 +- `_shared/CardModeRenderer.tsx` — canonical CardView 와 통합 또는 보존 (CSS-only) + +--- + +## 6. 옛 ConfigPanel vs canonical InvTableConfigPanel parity gap + +### 6.1 canonical `InvTableConfigPanel` 의 cp 섹션 (확인됨) + +- ② 표시 모드 (table/split/grouped/pivot/card) +- ③ 행 / 높이 (행 선택, 행 높이) +- ④ 모드별 설정 (분할 비율 / 그룹화 컬럼 / 피벗 설정 / 카드 설정) +- ⑤ 컬럼 (개수 표시 + 컬럼별 너비/정렬/sortable) +- "동작 옵션" (CPGroup default closed) + +### 6.2 옛 `V2TableListConfigPanel.tsx` (1518줄) 의 추가 섹션 (확인됨) + +- 데이터 소스 (테이블 선택 + customTableName) +- 툴바 버튼 (8개) +- 데이터 필터링 (dataFilter) +- (1518줄에 더 많은 섹션 — 다음 phase 에서 정밀 분석) + +### 6.3 옛 `TableListConfigPanel.tsx` (1361줄) 의 추가 섹션 (TableListConfig 타입 기반 추정) + +- selectedTable / customTableName / useCustomTable / isReadOnly +- checkbox (CheckboxConfig 풀 옵션) +- height (auto/fixed/viewport) + fixedHeight +- columns (ColumnConfig 풀 옵션 — visible/sortable/searchable/format/order/fixed/hidden/autoGeneration/editable/inputType/additionalJoinInfo/entityDisplayConfig 등) +- autoWidth / stickyHeader / horizontalScroll (min/maxColumnWidth, maxVisibleColumns) +- pagination 풀 옵션 +- filter (FilterConfig — filters[].columnName/widgetType/label/gridColumns/codeInfo/referenceTable/referenceColumn) +- actions (ActionConfig — view/edit/delete/custom + bulkActions[]) +- tableStyle (TableStyleConfig 풀 옵션) +- autoLoad / refreshInterval +- defaultSort +- toolbar (8 buttons) +- dataFilter (DataFilterConfig) +- linkedFilters (LinkedFilterConfig[]) +- excludeFilter (ExcludeFilterConfig) +- cardConfig 풀 옵션 + +### 6.4 흡수 우선순위 + +| 우선순위 | 섹션 | 흡수 phase | 위험도 | +|---|---|---|---| +| 1 (즉시 필요) | 데이터 소스 (selectedTable / customTableName / useCustomTable) | C.1 | LOW | +| 2 (자주 사용) | 컬럼 풀 옵션 (visible/fixed/hidden/inputType/editable) | C.2 | MED | +| 3 | 필터 (linkedFilters / excludeFilter / dataFilter) | C.3 | MED | +| 4 | 액션 (row/bulk actions) | C.4 | LOW | +| 5 | 스타일/툴바/페이지네이션 확장 | C.5 | LOW | + +--- + +## 7. 삭제 의존성 순서 (역방향 그래프) + +``` +[F.1 ~ F.8] 폐기 / 삭제 / alias 제거 + ↑ +[E.1 ~ E.3] 외부 사용처 마이그 (V2List → canonical, InvDataConfigPanel 분기 제거) + ↑ +[D.1 ~ D.10] 본체 parity 흡수 (canonical TableComponent) + ↑ +[C.1 ~ C.5] ConfigPanel parity 흡수 (InvTableConfigPanel) + ↑ +[B.1 ~ B.4] 외부 의존 시스템 캐노니컬 인식 (선행 조건) + ↑ +[A.0] Inventory + plan (현 문서) +``` + +각 phase 의 **선행 조건** 명시 (다음 세션에서 phase 시작 전 검증): + +| Phase | 선행 | +|---|---| +| B.1 | A.0 완료 (이 문서 검토 OK) | +| B.2 | A.0 | +| B.3 | A.0 | +| B.4 | A.0 | +| C.1 | A.0 | +| C.2 | C.1 + 옛 ConfigPanel 정밀 분석 | +| C.3 | C.2 | +| C.4 | C.3 | +| C.5 | C.4 | +| D.1 | C.2 + B.1 | +| D.2 | C.3 + D.1 | +| D.3 | D.1 | +| D.4 | C.4 | +| D.5 | D.1 | +| D.6 | C.5 + D.1 | +| D.7 | D.1 | +| D.8 | B.1 + D.6 | +| D.9 | B.2 + B.3 + D.1 | +| D.10 | D.1 | +| E.1 | D.1 ~ D.10 모두 | +| E.2 | E.1 | +| E.3 | E.1 | +| F.1 | E.1 ~ E.3 모두 | +| F.2 | F.1 | +| F.3 | F.2 | +| F.4 | F.2 | +| F.5 | F.4 | +| F.6 | F.5 | +| F.7 | F.6 | +| F.8 | F.7 | +| G.1 | F.8 | + +병렬 실행 가능 그룹: +- B.1 / B.2 / B.3 / B.4 → **병렬** +- C.1 → C.2 → C.3 → C.4 → C.5 (순차) +- D.1 / D.4 / D.5 / D.6 / D.7 / D.10 → 일부 병렬 +- D.2 (D.1 의존), D.3 (D.1 의존), D.8 (D.6 의존), D.9 (B.2/B.3 의존) +- E.1 / E.2 / E.3 → 순차 +- F.1 → F.2 → F.3, F.4 / F.5 / F.6 / F.7 / F.8 일부 순차 + +--- + +## 8. Phase 단위 plan + +각 phase = **1 세션 분량**. 세션은 다음 항목을 한 번에 처리: +- file scope (≤ 7 파일 권장, 큰 본체 1~2개는 single-file 세션) +- 검증 (git diff --check + grep + tsc 변경 파일) +- 예상 작업 시간 30~90분 (Claude 세션 기준) +- 위험도 LOW / MED / HIGH + +### Phase Group B — 외부 의존 시스템 canonical 인식 + +#### Phase B.1 — TableOptionsContext / tableDisplayStore canonical 인식 + +**Goal**: `useTableOptions` 의 `registerTable` 가 canonical `table` 컴포넌트 ID 도 받음. `tableDisplayStore.setTableData(tableId, data)` 의 tableId 가 canonical 화면에서도 유효. + +**File scope**: +- `frontend/contexts/TableOptionsContext.tsx` (확인 필요) +- `frontend/stores/tableDisplayStore.ts` (확인 필요) +- `frontend/lib/registry/components/table/TableComponent.tsx` — `useTableOptions` hook 사용 추가 + +**위험도**: MED — Context 와 store key 컨벤션 변경. 옛 본체와 동시에 같은 키를 다루지 않도록. + +**검증**: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "(TableOptionsContext|tableDisplayStore|TableComponent)" | head +``` + +**프롬프트 초안**: §9 B.1. + +--- + +#### Phase B.2 — SplitPanelContext canonical 인식 + +**Goal**: `useSplitPanelContext` 가 canonical `table.displayMode='split'` 도 master-detail 으로 인식. 또는 옛 `split-panel-layout` 의 SplitPanelContext provider 가 canonical table 도 leftPanel/rightPanel 로 받음. + +**선행 결정**: canonical table.displayMode='split' 의 leftPanel/rightPanel UX (drag/drop, selection sync, resize) 를 어디에 구현할 것인가? +- 옵션 A: canonical TableComponent 가 SplitPanelContext provider 도 됨 +- 옵션 B: 옛 `split-panel-layout` provider 가 canonical 도 받게 확장 +- 옵션 C: 새 `SplitPanelProvider` neutral wrapper + +**File scope**: +- `frontend/contexts/SplitPanelContext.tsx` +- `frontend/lib/registry/components/v2-split-panel-layout/SplitPanelContext.tsx` +- `frontend/lib/registry/components/split-panel-layout/SplitPanelContext.tsx` +- canonical TableComponent.tsx + +**위험도**: HIGH — provider 2개 + 의존 사용자 다수. 옛 split-panel-layout 폐기 hard blocker 와 묶임. + +**검증**: +```bash +git diff --check +rg -l "useSplitPanelContext" frontend/ +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "SplitPanel" | head +``` + +**프롬프트 초안**: §9 B.2. + +--- + +#### Phase B.3 — DataProvidable / DataReceivable adapter canonical 노출 + +**Goal**: canonical TableComponent 가 `DataProvidable` (selectedRows 제공) / `DataReceivable` (search/sort params 받음) 어댑터 expose. 옛 본체와 호환되는 contract. + +**File scope**: +- `frontend/types/data-transfer.ts` +- `frontend/lib/registry/components/table/TableComponent.tsx` +- `frontend/lib/registry/components/table/types.ts` + +**위험도**: MED — type 계약. 변경 시 사용자 (`ButtonPrimaryComponent` 등 8 파일) 영향. + +**검증**: +```bash +git diff --check +rg -l "DataProvidable|DataReceivable" frontend/ +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "data-transfer" | head +``` + +**프롬프트 초안**: §9 B.3. + +--- + +#### Phase B.4 — tableDisplayStore key 정규화 + +**Goal**: `tableId = \`table-list-${id}\`` 컨벤션 (옛 본체 line 931 / V2 line 1276) 을 canonical 의 `id` 기반으로 정규화. 마이그 가능한 경우 옛 키도 backward read. + +**File scope**: +- `frontend/stores/tableDisplayStore.ts` +- 사용자 (옛 본체 + canonical) + +**위험도**: LOW — store key prefix 만. + +**검증**: +```bash +git diff --check +rg -l "tableDisplayStore" frontend/ +``` + +**프롬프트 초안**: §9 B.4. + +--- + +### Phase Group C — ConfigPanel parity 흡수 + +#### Phase C.1 — 데이터 소스 (selectedTable / customTableName / useCustomTable / isReadOnly) + +**Goal**: `InvTableConfigPanel` 에 데이터 소스 섹션 (① 추가). cp 컨벤션 (CPSection / CPRow / CPSelect / CPSwitch / CPText) 적용. + +**File scope**: +- `frontend/lib/registry/components/table/InvTableConfigPanel.tsx` +- `frontend/lib/registry/components/table/types.ts` — `TableConfig.selectedTable`, `customTableName`, `useCustomTable`, `isReadOnly` 추가 + +**위험도**: LOW — 옵션 추가만. + +**검증**: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "InvTableConfigPanel|TableConfig" | head +``` + +**프롬프트 초안**: §9 C.1. + +--- + +#### Phase C.2 — 컬럼 풀 옵션 (visible / fixed / hidden / inputType / editable / 너비 / 정렬) + +**Goal**: `InvTableConfigPanel` 의 ⑤ 컬럼 섹션을 옛 ConfigPanel 풀 옵션으로 확장. `ColumnConfig` 의 `visible`, `fixed`, `fixedOrder`, `hidden`, `inputType`, `editable`, `thousandSeparator`, `additionalJoinInfo`, `entityDisplayConfig` 모두 편집 가능. + +**File scope**: +- `frontend/lib/registry/components/table/InvTableConfigPanel.tsx` +- `frontend/lib/registry/components/table/types.ts` — `TableColumn` 확장 +- `frontend/lib/fieldConfig/adapters.ts` — `fieldsToColumns` 가 확장된 옵션도 전달 + +**위험도**: MED — 컬럼 타입 확장이 다른 호출자 (`SingleTableWithSticky`, `FlowWidget`) 와 호환되어야 함. + +**검증**: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "ColumnConfig|TableColumn|fieldsToColumns" | head +``` + +**프롬프트 초안**: §9 C.2. + +--- + +#### Phase C.3 — 필터 (linkedFilters / excludeFilter / dataFilter) + +**Goal**: `InvTableConfigPanel` 에 필터 섹션 추가. 옛 `FilterConfig`, `LinkedFilterConfig`, `ExcludeFilterConfig`, `DataFilterConfig` 모두 흡수. + +**File scope**: +- `frontend/lib/registry/components/table/InvTableConfigPanel.tsx` +- `frontend/lib/registry/components/table/types.ts` +- `frontend/types/screen-management.ts` — `DataFilterConfig` 사용 확인 + +**위험도**: MED — `DataFilterConfig` 는 다른 곳에서 사용 가능. + +**검증**: +```bash +git diff --check +rg -l "LinkedFilterConfig|ExcludeFilterConfig|DataFilterConfig" frontend/ +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "Filter" | head +``` + +**프롬프트 초안**: §9 C.3. + +--- + +#### Phase C.4 — 액션 (row actions / bulk actions / 액션 컬럼) + +**Goal**: `InvTableConfigPanel` 에 액션 섹션 추가. `ActionConfig` 의 `view/edit/delete/custom` + `bulkActions` 흡수. + +**File scope**: +- `frontend/lib/registry/components/table/InvTableConfigPanel.tsx` +- `frontend/lib/registry/components/table/types.ts` + +**위험도**: LOW. + +**검증**: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "ActionConfig" | head +``` + +**프롬프트 초안**: §9 C.4. + +--- + +#### Phase C.5 — 스타일 / 툴바 / 페이지네이션 확장 + +**Goal**: `tableStyle` (theme/headerStyle/borderStyle/alternateRows/hoverEffect), `toolbar` (8 buttons), `pagination` (showSizeSelector/showPageInfo/pageSizeOptions), `autoLoad`/`refreshInterval`/`defaultSort` 흡수. + +**File scope**: +- `frontend/lib/registry/components/table/InvTableConfigPanel.tsx` +- `frontend/lib/registry/components/table/types.ts` + +**위험도**: LOW. + +**검증**: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "InvTableConfigPanel|TableConfig" | head +``` + +**프롬프트 초안**: §9 C.5. + +--- + +### Phase Group D — Component parity 흡수 (canonical TableComponent 본체) + +#### Phase D.1 — 컬럼 시스템 + +**Goal**: canonical TableComponent 가 `visibility` (사용자 영구 저장 + 동적 hide), `fixed` (sticky left/right), `horizontalScroll` (min/maxColumnWidth, maxVisibleColumns), `autoWidth`, `stickyHeader` 모두 처리. + +**File scope**: +- `frontend/lib/registry/components/table/TableComponent.tsx` +- `frontend/lib/registry/components/table/types.ts` +- 가능하면 `frontend/lib/registry/components/table/_shared/SingleTableWithSticky.tsx` 의 `variant="default"` 분기를 canonical 에 흡수 또는 재사용 + +**위험도**: MED — sticky 와 horizontalScroll CSS 가 화면별로 다름. + +**검증**: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "TableComponent" | head +``` + +**프롬프트 초안**: §9 D.1. + +--- + +#### Phase D.2 — 필터 시스템 + +**Goal**: canonical TableComponent 가 `linkedFilters` (props 또는 DataPort), `excludeFilter` (참조 테이블 SQL 적용), `dataFilter` (정적 컬럼 값) 처리. `AdvancedSearchFilters` 통합. + +**File scope**: +- `frontend/lib/registry/components/table/TableComponent.tsx` +- `frontend/lib/registry/components/table/useTableData.ts` — search params 확장 +- `frontend/components/screen/filters/AdvancedSearchFilters.tsx` — props 확인 (변경 X) + +**위험도**: MED — backend API 의 filter 쿼리 호환 확인 필요 (`tableTypeApi`, `entityJoinApi`). + +**검증**: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "TableComponent|useTableData" | head +``` + +**프롬프트 초안**: §9 D.2. + +--- + +#### Phase D.3 — 인라인 편집 + +**Goal**: canonical TableComponent 의 셀이 더블클릭 시 inline edit. category/code (select picker), date/datetime (date picker), number (number input), text (text input) 분기. + +**File scope**: +- `frontend/lib/registry/components/table/TableComponent.tsx` +- 가능하면 `_shared/SingleTableWithSticky.tsx` 의 `variant="v2"` 인라인 편집 분기를 canonical 로 흡수 + +**위험도**: MED — `codeCache`, date picker 컴포넌트 의존. + +**검증**: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | head 20 +``` + +**프롬프트 초안**: §9 D.3. + +--- + +#### Phase D.4 — 액션 컬럼 (row + bulk) + +**Goal**: ActionConfig 의 `view/edit/delete/custom` row 액션 + `bulkActions` 처리. 사용자 콜백 props 또는 buttonActions wiring. + +**File scope**: +- `frontend/lib/registry/components/table/TableComponent.tsx` + +**위험도**: LOW. + +**검증**: +```bash +git diff --check +``` + +**프롬프트 초안**: §9 D.4. + +--- + +#### Phase D.5 — 특수 셀 (이미지 / 파일 / entity 조인) + +**Goal**: `TableCellImage`, `TableCellFile`, `entityDisplayConfig` 다중 컬럼 표시 canonical 흡수. multilang langKey 도 함께. + +**File scope**: +- `frontend/lib/registry/components/table/TableComponent.tsx` 또는 `cell-renderers.tsx` 새 파일 +- `frontend/lib/registry/components/table/types.ts` + +**위험도**: MED — `getFullImageUrl`, `getFilePreviewUrl`, `getFileInfoByObjid` API client 의존. + +**검증**: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "TableComponent|TableCell" | head +``` + +**프롬프트 초안**: §9 D.5. + +--- + +#### Phase D.6 — 툴바 / Excel / autoLoad / defaultSort / paste + +**Goal**: 8 toolbar buttons (`showEditMode/Excel/Pdf/Copy/Search/Filter/Refresh/PaginationRefresh`), XLSX export, autoLoad/refreshInterval, defaultSort, Excel 셀 복사 / clipboard / paste. + +**File scope**: +- `frontend/lib/registry/components/table/TableComponent.tsx` + +**위험도**: MED — XLSX bundle size 이슈 + paste 의 DOM event. + +**검증**: +```bash +git diff --check +``` + +**프롬프트 초안**: §9 D.6. + +--- + +#### Phase D.7 — 카드 모드 풀 옵션 + ResizeObserver + +**Goal**: canonical CardView 가 cardConfig 풀 옵션 + ResizeObserver 기반 narrow/wide 자동 전환 (옛 V2TableListContainerWrapper 흡수). + +**File scope**: +- `frontend/lib/registry/components/table/views/CardView.tsx` +- `frontend/lib/registry/components/table/TableComponent.tsx` + +**위험도**: LOW — `_shared/CardModeRenderer.tsx` 의 variant="v2" 분기를 흡수. + +**검증**: +```bash +git diff --check +``` + +**프롬프트 초안**: §9 D.7. + +--- + +#### Phase D.8 — GroupSum + TableOptionsModal 통합 + +**Goal**: GroupSum (그룹 소계 표시), TableOptionsModal 통합 (옵션 모달에서 filter/sort/grouping/columns 통합 편집). + +**File scope**: +- `frontend/lib/registry/components/table/TableComponent.tsx` +- `frontend/lib/registry/components/table/views/GroupedView.tsx` +- `frontend/components/common/TableOptionsModal.tsx` — props 확인 (변경 X) + +**위험도**: MED — Phase B.1 의 TableOptionsContext canonical 인식 필수. + +**검증**: +```bash +git diff --check +``` + +**프롬프트 초안**: §9 D.8. + +--- + +#### Phase D.9 — SplitPanel / DataProvidable / DataReceivable wiring + +**Goal**: canonical TableComponent 가 SplitPanelContext (Phase B.2 결과) 와 DataProvidable/DataReceivable (Phase B.3 결과) 모두 wiring. master-detail UX 완성. + +**File scope**: +- `frontend/lib/registry/components/table/TableComponent.tsx` + +**위험도**: HIGH — Phase B.2 + B.3 의존. 외부 사용자 8 파일 영향. + +**검증**: +```bash +git diff --check +rg -l "DataProvidable|DataReceivable|useSplitPanelContext" frontend/ +cd frontend && npx tsc --noEmit --pretty false 2>&1 | head 30 +``` + +**프롬프트 초안**: §9 D.9. + +--- + +#### Phase D.10 — 자동생성 (autoGeneration) + +**Goal**: canonical TableComponent 의 인라인 편집 또는 새 행 추가 시 autoGeneration (uuid/current_user/numbering_rule 등) 처리. canonical input 의 `lib/registry/components/input/autoGeneration.ts` 재사용. + +**File scope**: +- `frontend/lib/registry/components/table/TableComponent.tsx` + +**위험도**: LOW. + +**검증**: +```bash +git diff --check +``` + +**프롬프트 초안**: §9 D.10. + +--- + +### Phase Group E — 외부 사용처 마이그 + +#### Phase E.1 — V2List.tsx 가 canonical TableComponent 직접 사용 + +**Goal**: V2List.tsx 가 `_shared/TableListComponent` import 제거하고 canonical `TableComponent` 사용. `type: "table-list"` 객체 생성 제거. config 형식 변환은 그대로 유지. + +**File scope**: +- `frontend/components/v2/V2List.tsx` +- `frontend/types/v2-components.ts` — V2List 관련 타입 정리 (`LEGACY_TO_V2_MAP` 의 `table-search-widget/modal-repeater-table/repeater-field-group/card-display` 매핑은 보존) + +**위험도**: MED — V2List 가 ComponentRegistry 의 `v2-list` 로 등록되어 있음. 옛 layout JSON 호환. + +**검증**: +```bash +git diff --check +rg -n "table-list" frontend/components/v2/V2List.tsx # 0건 +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "V2List" | head +``` + +**프롬프트 초안**: §9 E.1. + +--- + +#### Phase E.2 — InvDataConfigPanel v2-table-list 분기 제거 + +**Goal**: `InvDataConfigPanel` 의 brumb 에서 `v2-table-list` 옵션 제거. `V2TableListConfigPanel` import 제거. `read` kind 는 `v2-list` 하나만. `V2ListConfigPanel` 도 옛 `TableListConfigPanel` 위임 (line 25 import / line 319) 을 canonical `InvTableConfigPanel` 직접 호출로 변경. + +**File scope**: +- `frontend/components/v2/config-panels/InvDataConfigPanel.tsx` +- `frontend/components/v2/config-panels/V2ListConfigPanel.tsx` + +**위험도**: MED — V2ListConfigPanel 이 `TableListConfigPanel` (옛 본체) 에 위임. 풀 옵션 흡수 후 canonical 패널로 전환. + +**검증**: +```bash +git diff --check +rg -n "v2-table-list|V2TableListConfigPanel|TableListConfigPanel" frontend/components/v2/config-panels/ # 0건 +``` + +**프롬프트 초안**: §9 E.2. + +--- + +#### Phase E.3 — 외부 active branch 의 hard reference 제거 + +**Goal**: §3.4 의 19 파일 (Studio panels external) 의 주석 / fallback 에 박혀있는 옛 ID 를 정리. helper 호출은 유지하되 `case "table-list":`, `case "v2-table-list":` 같은 hard fallback 분기를 제거. fullWidthComponents 배열 (`responsiveDefaults.ts`) 에서 옛 ID 제거. + +**File scope**: +- `frontend/components/screen/modals/MultilangSettingsModal.tsx` (case + Set) +- `frontend/components/screen/panels/ComponentsPanel.tsx` (hidden list) +- `frontend/components/screen/InvyoneStudio.tsx` (fullWidthComponents weight) +- `frontend/lib/utils/responsiveDefaults.ts` (fullWidthComponents 배열) +- 나머지 19 파일의 stale comment 정리 + +**위험도**: LOW — 모두 helper 호출 뒤의 stale fallback. 동작 변경 없음. + +**검증**: +```bash +git diff --check +rg -n 'componentType === "table-list"|componentType === "v2-table-list"' frontend/ +cd frontend && npx tsc --noEmit --pretty false 2>&1 | head 20 +``` + +**프롬프트 초안**: §9 E.3. + +--- + +### Phase Group F — Runtime / Schema / Type / Alias 폐기 + +#### Phase F.1 — TableComponent.tsx early delegation 제거 + +**Goal**: `TableComponent.tsx` 의 line 27-28 `LegacyTableListWrapper` / `V2TableListContainerWrapper` import 제거. line 109-150 early delegation 분기 제거. line 44-83 `_MEANINGFUL_TABLE_TYPES` / `_GENERIC_COMPONENT_TYPES` / `_resolveRawComponentType` 제거. + +**선행 검증**: `_rawType` 후보가 canonical 분기에서 모두 처리되는지 (D.1~D.10 흡수 검증). + +**File scope**: +- `frontend/lib/registry/components/table/TableComponent.tsx` +- `frontend/lib/registry/components/table/index.ts` — docstring 정리 + +**위험도**: HIGH — 옛 layout JSON 의 동작 영향. + +**검증**: +```bash +git diff --check +rg -n "table-list|v2-table-list" frontend/lib/registry/components/table/TableComponent.tsx # 0건 +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep TableComponent | head +# 운영 모드 검증 권장 (옛 layout 화면 한두 개 띄워 확인) +``` + +**프롬프트 초안**: §9 F.1. + +--- + +#### Phase F.2 — `_shared/{TableListComponent, V2TableListComponent, V2TableListContainerWrapper, TableListConfigPanel}` 4 파일 삭제 + +**Goal**: 옛 본체 4 파일 통째 삭제. import 사용처 0건 확인. + +**선행 검증**: +```bash +rg -l "from.*_shared/(TableListComponent|V2TableListComponent|V2TableListContainerWrapper|TableListConfigPanel)" frontend/ +# → 0건이어야 함 +``` + +**File scope**: +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` (D) +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` (D) +- `frontend/lib/registry/components/table/_shared/V2TableListContainerWrapper.tsx` (D) +- `frontend/lib/registry/components/table/_shared/TableListConfigPanel.tsx` (D) + +**위험도**: LOW (선행 검증 통과 시). + +**검증**: +```bash +git diff --check +git status --short +rg -l "TableListComponent|V2TableListComponent|V2TableListContainerWrapper|TableListConfigPanel" frontend/ +``` + +**프롬프트 초안**: §9 F.2. + +--- + +#### Phase F.3 — `_shared/{SingleTableWithSticky, CardModeRenderer}` variant="v2" 분기 제거 + tableListConfigTypes.ts 정리 + +**Goal**: `SingleTableWithSticky.tsx` 의 `variant="v2"` 분기 제거 (variant prop 자체 제거 또는 default 만 유지). `CardModeRenderer.tsx` 동일. `tableListConfigTypes.ts` 는 canonical types.ts 로 흡수 또는 그대로 보존. + +**File scope**: +- `frontend/lib/registry/components/table/_shared/SingleTableWithSticky.tsx` +- `frontend/lib/registry/components/table/_shared/CardModeRenderer.tsx` +- `frontend/lib/registry/components/table/_shared/tableListConfigTypes.ts` +- `frontend/components/screen/widgets/FlowWidget.tsx` — `SingleTableWithSticky` 호출 변경 확인 + +**위험도**: MED — FlowWidget 의존. + +**검증**: +```bash +git diff --check +rg -n "variant=\"v2\"|variant: \"v2\"" frontend/ # 0건 +``` + +**프롬프트 초안**: §9 F.3. + +--- + +#### Phase F.4 — componentConfig.ts v2-table-list schema/default 제거 + +**Goal**: `componentConfig.ts` line 226 (`v2TableListOverridesSchema`), line 554 (`componentOverridesRegistry`), line 589 (`componentDefaultsRegistry`) 의 `v2-table-list` 항목 제거. + +**File scope**: +- `frontend/lib/schemas/componentConfig.ts` + +**위험도**: MED — 옛 layout JSON 의 schema 검증 미통과 위험. 선행: 모든 v2-table-list layout 이 canonical TableConfig 로 마이그 완료 또는 schema fallback OK. + +**검증**: +```bash +git diff --check +rg -n "v2-table-list" frontend/lib/schemas/componentConfig.ts # 0건 +cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep componentConfig | head +``` + +**프롬프트 초안**: §9 F.4. + +--- + +#### Phase F.5 — types/*.ts 의 table-list / v2-table-list 잔재 제거 + +**Goal**: §3.5 의 type/schema 파일 8개에서 stale 주석 + 매핑 제거. + +**File scope**: +- `frontend/types/v2-components.ts` (LEGACY_TO_V2_MAP 의 table-search-widget/modal-repeater-table 매핑 보존, 잔여 주석 정리) +- `frontend/types/invyone-component.ts` (line 829 예시 주석 갱신) +- `frontend/types/screen-management.ts` (line 223 예시 주석 갱신) +- `frontend/types/component-events.ts` (line 14/72/116 publisher/subscriber 주석 갱신) +- `frontend/types/table-options.ts` (line 53 예시 주석 갱신) +- `frontend/lib/api/screenGroup.ts` (line 369 예시 주석 갱신) +- `frontend/lib/fieldConfig/adapters.ts` (docstring 갱신 — `fieldsToColumns` 가 canonical 만 받음으로) + +**위험도**: LOW. + +**검증**: +```bash +git diff --check +rg -n "v2-table-list|table-list" frontend/types/ frontend/lib/api/screenGroup.ts frontend/lib/fieldConfig/adapters.ts # docs only / 0건 +``` + +**프롬프트 초안**: §9 F.5. + +--- + +#### Phase F.6 — alias 라우팅 LEGACY_TO_UNIFIED 의 v2-table-list / table-list 제거 + +**Goal**: `DynamicComponentRenderer.tsx` 의 `LEGACY_TO_UNIFIED` (line 388) 와 `templateMigrate.ts` 의 동일 매핑 (line 44-45) 제거. `getComponentConfigPanel.tsx` 의 `CONFIG_PANEL_ALIAS` (line 146) 도 제거. + +**File scope**: +- `frontend/lib/registry/DynamicComponentRenderer.tsx` +- `frontend/lib/utils/templateMigrate.ts` +- `frontend/lib/utils/getComponentConfigPanel.tsx` + +**위험도**: HIGH — 옛 layout JSON 이 마이그 안 되면 canonical 라우팅 실패 시 unknown component 렌더. + +**선행**: F.4 완료 + 옛 layout 마이그 완료 또는 templateMigrate 에서 강제 변환 확인. + +**검증**: +```bash +git diff --check +rg -n '"v2-table-list".*"table"|"table-list".*"table"' frontend/lib/registry/ frontend/lib/utils/ # 0건 +``` + +**프롬프트 초안**: §9 F.6. + +--- + +#### Phase F.7 — TABLE_LIKE_COMPONENT_TYPES 에서 table-list / v2-table-list 제거 + +**Goal**: `componentTypeUtils.ts` line 218 `TABLE_LIKE_COMPONENT_TYPES` Set 에서 `"table-list"`, `"v2-table-list"` 제거. helper 는 canonical `"table"` 만 인식. + +**File scope**: +- `frontend/lib/utils/componentTypeUtils.ts` + +**위험도**: MED — §3.4 의 19 파일이 이 helper 호출. F.6 이후라 ID 가 이미 canonical 이라 영향 없어야 함. + +**검증**: +```bash +git diff --check +rg -n "TABLE_LIKE_COMPONENT_TYPES" frontend/lib/utils/componentTypeUtils.ts +# Set 에 'table' / 'data-table' / 'datatable' 만 있어야 함 +cd frontend && npx tsc --noEmit --pretty false 2>&1 | head 20 +``` + +**프롬프트 초안**: §9 F.7. + +--- + +#### Phase F.8 — V2List.tsx + V2ListConfigPanel.tsx + V2TableListConfigPanel.tsx 삭제 (또는 통합) + +**Goal**: V2List.tsx 가 canonical TableComponent 로 정리 (E.1) 된 후, ComponentRegistry 의 `v2-list` 등록을 canonical `table` 직접 등록 또는 V2List shim 제거. V2TableListConfigPanel 과 V2ListConfigPanel 은 통째 삭제. + +**선행 검증**: +```bash +rg -l "V2TableListConfigPanel|V2ListConfigPanel" frontend/ # 0건 (InvDataConfigPanel 제거 후) +rg -l "from.*V2List" frontend/ # 정리 후 0건 +``` + +**File scope**: +- `frontend/components/v2/V2List.tsx` (D) +- `frontend/components/v2/config-panels/V2TableListConfigPanel.tsx` (D) +- `frontend/components/v2/config-panels/V2ListConfigPanel.tsx` (D) +- `frontend/components/v2/registerV2Components.ts` — `v2-list` 등록 부분 정리 +- `frontend/components/v2/index.ts` — V2List export 제거 +- `frontend/components/v2/V2ComponentRenderer.tsx` — isV2List 분기 제거 +- `frontend/components/v2/V2ComponentsDemo.tsx` — V2List 데모 제거 + +**위험도**: MED — v2-list 등록 변경이 옛 layout JSON 영향. + +**검증**: +```bash +git diff --check +rg -l "V2List" frontend/ +git status --short +``` + +**프롬프트 초안**: §9 F.8. + +--- + +### Phase G — 최종 검증 + +#### Phase G.1 — Full verification + final report + +**Goal**: prompt 의 final report requirements 모두 충족. 변경/삭제 파일 목록 + 잔존 매칭 0건 검증 + parity 체크리스트 최종 + 운영 모드 검증. + +**검증 명령**: +```bash +git diff --check +git diff --cached --check + +# 1. 금지 토큰 (input canonical 유지) +rg "v2-input|v2-select|V2InputRenderer|V2SelectRenderer" \ + frontend/lib frontend/components frontend/app frontend/types frontend/styles +# → 0건 + +# 2. EntityPicker 잔재 +rg "EntityPicker|entity-picker|EntitySearchModal" \ + frontend/lib/registry/components/input \ + frontend/components/v2/config-panels/InvFieldConfigPanel.tsx +# → 0건 + +# 3. table cleanup 본 목표 +rg "v2-table-list|table-list|V2TableList|TableListComponent|TableListConfigPanel|V2TableListContainerWrapper" \ + frontend/lib frontend/components frontend/app frontend/types +# → 0건 (또는 docs-only/domain-enum 만) + +# 4. import 잔재 +rg "components/v2-table-list|components/table-list|./v2-table-list|./table-list" frontend +# → 0건 + +# 5. tsc +cd frontend && npx tsc --noEmit --pretty false +# → 변경 파일의 new error 0건 + +# 6. backend 컴파일 (backend 변경 시) +cd backend-spring && ./gradlew compileJava +# → BUILD SUCCESSFUL + +# 7. 운영 모드 검증 (옛 layout 화면 5개 + 새 화면 5개 띄워 확인) +``` + +**산출물**: +- `notes/gbpark/2026-05-?-table-canonical-cleanup-final-report.md` — 본 plan 의 phase 별 완료 상태 + 잔존 매칭 0건 증명 + +--- + +## 9. Phase 별 Claude 프롬프트 초안 + +각 phase 의 첫 메시지로 사용. **선행 phase 완료 후** 실행. file scope 와 검증 명령 그대로 인용. + +### 9.B.1 — TableOptionsContext / tableDisplayStore canonical 인식 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase B.1 + §4 + +Goal: Make TableOptionsContext / tableDisplayStore recognize canonical "table" component +id so that future canonical TableComponent absorbs columnVisibility / GroupSum / filter UX +without requiring _shared/TableListComponent or V2TableListComponent. + +Do not absorb the legacy bodies yet. Do not delete legacy bodies. Do not touch +v2-input / v2-select. + +Scope: +- frontend/contexts/TableOptionsContext.tsx +- frontend/stores/tableDisplayStore.ts +- frontend/lib/registry/components/table/TableComponent.tsx — useTableOptions hook + hookup placeholder (register/unregister/updateTableDataCount) + +Verification: +- git diff --check +- cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "(TableOptionsContext|tableDisplayStore|TableComponent)" | head +- rg -n "useTableOptions|registerTable|unregisterTable" frontend/contexts frontend/lib/registry/components/table + +Forbidden: +- Do not reintroduce v2-input, v2-select, V2InputRenderer, V2SelectRenderer, + EntityPicker, EntitySearchModal, FileUploadComponent, V2Media. +- Do not shrink FieldConfig / DataPort / DataProvidable / DataReceivable contracts. +``` + +### 9.B.2 — SplitPanelContext canonical 인식 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase B.2 + §4 + +Goal: Make canonical TableComponent.displayMode === "split" recognizable by +SplitPanelContext provider so that master-detail UX (selection sync, resize, +nested child) is available without legacy split-panel-layout components. + +Before coding: read the three SplitPanelContext files and decide between: +- Option A: canonical TableComponent provides its own SplitPanelProvider +- Option B: existing split-panel-layout provider also accepts canonical table +- Option C: neutral SplitPanelProvider wrapper at /contexts/SplitPanelContext.tsx + +Recommend a single option in the report with rationale. + +Scope: +- frontend/contexts/SplitPanelContext.tsx +- frontend/lib/registry/components/v2-split-panel-layout/SplitPanelContext.tsx (legacy) +- frontend/lib/registry/components/split-panel-layout/SplitPanelContext.tsx (legacy) +- frontend/lib/registry/components/table/TableComponent.tsx + +Verification: +- git diff --check +- rg -l "useSplitPanelContext|SplitPanelProvider" frontend/ +- cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "SplitPanel" | head + +Forbidden: same as B.1. +``` + +### 9.B.3 — DataProvidable / DataReceivable adapter canonical 노출 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase B.3 + §4 + +Goal: canonical TableComponent must expose DataProvidable and DataReceivable adapters +identical in contract to _shared/TableListComponent (line 805 / line 831) and +_shared/V2TableListComponent (line 1143 / line 1183). External users +(ButtonPrimaryComponent, ConditionalContainerComponent, RelatedDataButtons, +EmbeddedScreen, InteractiveScreenViewer, InteractiveDataTable) must not require code +changes after this phase. + +Scope: +- frontend/types/data-transfer.ts (no shrink) +- frontend/lib/registry/components/table/TableComponent.tsx +- frontend/lib/registry/components/table/types.ts + +Verification: +- git diff --check +- rg -l "DataProvidable|DataReceivable" frontend/ +- cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "data-transfer" | head + +Forbidden: same as B.1. +``` + +### 9.B.4 — tableDisplayStore key 정규화 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase B.4 + +Goal: normalize tableDisplayStore key prefix so canonical TableComponent and legacy +_shared/TableListComponent both write/read the same store entries during the transition. + +Scope: +- frontend/stores/tableDisplayStore.ts +- frontend/lib/registry/components/table/TableComponent.tsx + +Verification: +- git diff --check +- rg -n "tableDisplayStore" frontend/ | head 20 + +Forbidden: same as B.1. +``` + +### 9.C.1 — 데이터 소스 섹션 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase C.1 + §6 + +Goal: Add a "Data Source" section to canonical InvTableConfigPanel.tsx supporting +selectedTable / customTableName / useCustomTable / isReadOnly. Follow cp standard from +notes/gbpark/2026-04-28-cp-panel-standard.md. Do not absorb other legacy sections yet. + +Scope: +- frontend/lib/registry/components/table/InvTableConfigPanel.tsx +- frontend/lib/registry/components/table/types.ts (TableConfig adds selectedTable, + customTableName, useCustomTable, isReadOnly) + +Verification: +- git diff --check +- cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "InvTableConfigPanel|TableConfig" | head + +Forbidden: same as B.1. Also: do not introduce shadcn Select/Input/Switch/Label directly +— use CPSelect / CPText / CPSwitch / CPSection / CPRow. +``` + +### 9.C.2 — 컬럼 풀 옵션 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase C.2 + §6.3 + +Goal: Expand the ⑤ Column section in InvTableConfigPanel.tsx with the legacy ColumnConfig +options: visible, fixed, fixedOrder, hidden, inputType, editable, thousandSeparator, +additionalJoinInfo, entityDisplayConfig. Use cp components only. + +Scope: +- frontend/lib/registry/components/table/InvTableConfigPanel.tsx +- frontend/lib/registry/components/table/types.ts (TableColumn extends with new fields) +- frontend/lib/fieldConfig/adapters.ts (fieldsToColumns also propagates the new fields) + +Verification: +- git diff --check +- cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "ColumnConfig|TableColumn|fieldsToColumns" | head + +Forbidden: same as C.1. +``` + +### 9.C.3 / 9.C.4 / 9.C.5 — 필터 / 액션 / 스타일·툴바·페이지네이션 + +(C.1 / C.2 와 동일 패턴. Goal 만 다름. file scope / 검증 명령은 §8 Phase C.3 ~ C.5 인용.) + +### 9.D.1 — 컬럼 시스템 흡수 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase D.1 + §5.1#5,6,21 + +Goal: canonical TableComponent.tsx now supports column visibility (persistent via +tableDisplayStore / useTableOptions from Phase B.1), fixed left/right (CSS sticky), +horizontalScroll (min/maxColumnWidth, maxVisibleColumns), autoWidth, stickyHeader. +Do not delete _shared/TableListComponent yet. Do not touch v2-input/v2-select. + +Scope: +- frontend/lib/registry/components/table/TableComponent.tsx +- frontend/lib/registry/components/table/types.ts +- frontend/lib/registry/components/table/_shared/SingleTableWithSticky.tsx (default + variant 의 sticky logic 을 canonical 에서 재사용 가능하면 활용) + +Verification: +- git diff --check +- cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "TableComponent" | head + +Forbidden: same as B.1. +``` + +### 9.D.2 ~ 9.D.10 — 필터 / 인라인 편집 / 액션 / 셀 / 툴바 / 카드 / GroupSum / SplitPanel / 자동생성 + +(각 phase 의 Goal 만 다름. file scope / 검증 명령은 §8 Phase D.2 ~ D.10 인용. forbidden 동일.) + +### 9.E.1 — V2List canonical 사용 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase E.1 + §3.3 + +Goal: V2List.tsx no longer imports TableListComponent from +_shared/TableListComponent. Instead it uses canonical TableComponent via ComponentRegistry +or direct import. Do not delete TableListComponent yet (Phase F.2). Do not create +`type: "table-list"` object — pass canonical "table" id or call canonical component +directly. + +Scope: +- frontend/components/v2/V2List.tsx +- frontend/types/v2-components.ts (LEGACY_TO_V2_MAP table-search-widget / + modal-repeater-table / repeater-field-group / card-display 매핑 보존) + +Verification: +- git diff --check +- rg -n "table-list|TableListComponent" frontend/components/v2/V2List.tsx + # → 0건 +- cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "V2List" | head + +Forbidden: same as B.1. +``` + +### 9.E.2 — InvDataConfigPanel v2-table-list 분기 제거 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase E.2 + §3.3 + +Goal: InvDataConfigPanel.tsx removes the "v2-table-list" brumb option and the +V2TableListConfigPanel import. V2ListConfigPanel.tsx stops delegating to legacy +TableListConfigPanel and calls canonical InvTableConfigPanel instead. Do not delete +V2TableListConfigPanel.tsx or V2ListConfigPanel.tsx yet — only stop importing them. + +Scope: +- frontend/components/v2/config-panels/InvDataConfigPanel.tsx +- frontend/components/v2/config-panels/V2ListConfigPanel.tsx + +Verification: +- git diff --check +- rg -n "v2-table-list|V2TableListConfigPanel|TableListConfigPanel" frontend/components/v2/config-panels/ + # → 0건 (V2TableListConfigPanel.tsx 본체 self-id 제외) +- cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep -E "InvDataConfigPanel|V2ListConfigPanel" | head + +Forbidden: same as B.1. +``` + +### 9.E.3 — 외부 active branch stale fallback 정리 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase E.3 + §3.4 + +Goal: Remove stale `case "table-list":`, `case "v2-table-list":` fallback branches +and stale ID literals from the 19 external active-branch files (Studio panels). Keep +the helper calls (isTableLikeComponentType / isTableLikeComponent / +getTableNameFromTableLikeComponent) — only the stale ID literals must go. + +Scope: +- frontend/components/screen/modals/MultilangSettingsModal.tsx +- frontend/components/screen/panels/ComponentsPanel.tsx +- frontend/components/screen/InvyoneStudio.tsx +- frontend/lib/utils/responsiveDefaults.ts +- 나머지 §3.4 의 stale comment 정리 후보 + +Verification: +- git diff --check +- rg -n 'componentType === "table-list"|componentType === "v2-table-list"|case "table-list"|case "v2-table-list"' frontend/ + # → 0건 +- cd frontend && npx tsc --noEmit --pretty false 2>&1 | head 20 + +Forbidden: same as B.1. +``` + +### 9.F.1 — TableComponent.tsx early delegation 제거 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase F.1 + §3.2 + +Goal: TableComponent.tsx no longer delegates to LegacyTableListWrapper or +V2TableListContainerWrapper. The early delegation block (line 109-150 of current +TableComponent.tsx) and the _resolveRawComponentType helper plus _MEANINGFUL_TABLE_TYPES / +_GENERIC_COMPONENT_TYPES sets are removed. The remaining canonical body must +handle any layout JSON componentType in [table, table-list, v2-table-list, data-table, +datatable] via alias routing (DynamicComponentRenderer.LEGACY_TO_UNIFIED) without +delegation. + +Scope: +- frontend/lib/registry/components/table/TableComponent.tsx +- frontend/lib/registry/components/table/index.ts (docstring 정리) + +Verification: +- git diff --check +- rg -n "table-list|v2-table-list|LegacyTableListWrapper|V2TableListContainerWrapper" \ + frontend/lib/registry/components/table/TableComponent.tsx + # → 0건 +- cd frontend && npx tsc --noEmit --pretty false 2>&1 | grep TableComponent | head +- 운영 모드 검증 권장 (옛 layout 화면 한두 개 띄워 확인) + +Forbidden: same as B.1. +``` + +### 9.F.2 — _shared 본체 4 파일 삭제 + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase F.2 + +Precondition (must verify first): +rg -l "from.*_shared/(TableListComponent|V2TableListComponent|V2TableListContainerWrapper|TableListConfigPanel)" frontend/ +# → 0건 + +Goal: Delete the four legacy bodies: +- frontend/lib/registry/components/table/_shared/TableListComponent.tsx +- frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx +- frontend/lib/registry/components/table/_shared/V2TableListContainerWrapper.tsx +- frontend/lib/registry/components/table/_shared/TableListConfigPanel.tsx + +If precondition fails, stop and report missing import sources. Do not delete. + +Verification: +- git diff --check +- git status --short +- rg -l "TableListComponent|V2TableListComponent|V2TableListContainerWrapper|TableListConfigPanel" frontend/ + # → 0건 + +Forbidden: same as B.1. +``` + +### 9.F.3 ~ 9.F.8 + +(각 phase 의 Goal / Scope 만 다름. forbidden 동일. 위 패턴 인용.) + +### 9.G.1 — Final verification + report + +```text +You are working in /Users/gbpark/invyone. + +Reference: notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md §8 Phase G.1 ++ notes/gbpark/2026-05-20-table-canonical-input-parity-claude-prompt.md (Final report +requirements). + +Goal: Produce notes/gbpark/2026-05-?-table-canonical-cleanup-final-report.md with: +- list of changed and deleted files (B.1 ~ F.8 누적) +- whether final code still has any table-list / v2-table-list runtime/config/schema/alias +- inclusion of rg results below (모두 0건이어야) +- parity checklist (§5 of plan) marked done / N/A / unresolved +- exact file/line for any blocker + +Verification commands (all must pass): +```bash +git diff --check +git diff --cached --check + +rg "v2-input|v2-select|V2InputRenderer|V2SelectRenderer" \ + frontend/lib frontend/components frontend/app frontend/types frontend/styles + +rg "EntityPicker|entity-picker|EntitySearchModal" \ + frontend/lib/registry/components/input \ + frontend/components/v2/config-panels/InvFieldConfigPanel.tsx + +rg "v2-table-list|table-list|V2TableList|TableListComponent|TableListConfigPanel|V2TableListContainerWrapper" \ + frontend/lib frontend/components frontend/app frontend/types + +rg "components/v2-table-list|components/table-list|./v2-table-list|./table-list" frontend + +cd frontend && npx tsc --noEmit --pretty false + +cd backend-spring && ./gradlew compileJava # backend 변경 시 +``` + +Forbidden: same as B.1. +``` + +--- + +## 10. 권장 진행 순서 + 세션 분배 + +| 세션 | Phase | 누적 진행 % | 예상 시간 | +|---|---|---|---| +| 1 | B.1 + B.4 (병렬 가능) | 5% | 60 min | +| 2 | B.2 | 12% | 90 min | +| 3 | B.3 | 18% | 60 min | +| 4 | C.1 + C.5 (소규모, 묶음 가능) | 25% | 60 min | +| 5 | C.2 | 32% | 90 min | +| 6 | C.3 + C.4 | 40% | 90 min | +| 7 | D.1 | 50% | 90 min | +| 8 | D.2 | 58% | 90 min | +| 9 | D.3 + D.4 | 65% | 90 min | +| 10 | D.5 + D.7 | 72% | 90 min | +| 11 | D.6 | 78% | 90 min | +| 12 | D.8 + D.10 | 83% | 90 min | +| 13 | D.9 | 88% | 90 min | +| 14 | E.1 + E.2 + E.3 | 93% | 90 min | +| 15 | F.1 + F.2 + F.3 | 96% | 60 min | +| 16 | F.4 ~ F.8 | 99% | 60 min | +| 17 | G.1 | 100% | 60 min | + +**전체 누적: 17 세션 × 평균 80분 ≈ 23시간.** + +병렬화 / 작업자 분배 옵션: +- B 그룹 (B.1 ~ B.4) 은 서로 독립 → 4 세션 병렬 가능 (작업자 2명이면 2일) +- D.1 / D.4 / D.5 / D.7 / D.10 은 일부 병렬 → 작업자 2명이면 5 세션 / 2 = 3 세션 + +--- + +## 11. Risk register (Phase 별 통합) + +| Risk | 영향 받는 phase | 완화 방안 | +|---|---|---| +| 옛 layout JSON 의 schema 변경 호환 | F.4 / F.6 | templateMigrate 가 모든 옛 ID 를 canonical 로 변환 + canonical schema 가 옛 옵션 superset | +| SplitPanelContext provider 2종 + 옛 split-panel-layout 폐기 hard blocker | B.2 / D.9 | 본 plan 의 범위 외 (split-panel-layout 별도 트랙). canonical table.displayMode=split 만 우선 | +| 외부 사용자 8 파일 (DataProvidable/DataReceivable) 의 contract 호환 | B.3 / D.9 | type 변경 없이 expose 만. backwards-compatible adapter | +| useTableOptions / TableOptionsModal 의 컬럼 visibility 영구 저장 key | B.1 / B.4 | tableId prefix 양쪽 호환 phase (transitional) 두기 | +| AdvancedSearchFilters 의 backend filter spec 호환 | D.2 | 별도 API endpoint 검증 | +| 옛 layout 의 `componentType: "table-list"` JSON 이 운영 DB 에 존재 | F.6 / F.1 | 통계 grep / DB rows count 후 일괄 마이그레이션 SQL (단 prompt 가 SQL 작성 금지하므로 templateMigrate runtime 변환 우선) | +| XLSX 패키지 bundle size 증가 | D.6 | dynamic import (`import()`) lazy load | +| FlowWidget 의 SingleTableWithSticky 의존 | F.3 | variant prop 제거 후 FlowWidget 단순 호출만 유지 | + +--- + +## 12. 비고 + +- 본 plan 은 사용자 지시 (이번 세션에서 코드 변경 금지) 에 따라 **문서만** 작성. 다음 세션이 B.1 부터 시작 가능. +- 각 phase 의 Verification 명령은 모두 prompt 의 final report grep 패턴과 호환 (`v2-input/v2-select/EntityPicker/EntitySearchModal/v2-file-upload` 등 0건 유지). +- 본 plan 의 범위는 **table cleanup 한정**. split-panel-layout / repeat-container / accordion-basic / conditional-container 등 §2026-05-19 §4 의 다른 hard blocker 는 별도 트랙. +- Codex (GPT-5.5) 와의 교차검증은 각 phase 의 verification 단계에서 옵션 (메모리 `feedback_codex_collaboration` 따라 권장). diff --git a/notes/gbpark/2026-05-20-table-canonical-input-parity-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-input-parity-claude-prompt.md new file mode 100644 index 00000000..edf35f10 --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-input-parity-claude-prompt.md @@ -0,0 +1,149 @@ +# 2026-05-20 Claude Prompt — Table Canonical Input-Parity Cleanup + +이 문서는 `table` 계열만 대상으로 하는 대형 작업용 Claude 프롬프트다. + +운영 원칙: +- 큰 refactor 는 Claude 프롬프트로 진행한다. +- Codex 는 프롬프트 작성, 작은 follow-up, 검증/리뷰, 좁은 패치만 직접 처리한다. + +--- + +## 바로 실행할 명령 + +```text +/goal Read notes/gbpark/2026-05-20-table-canonical-input-parity-claude-prompt.md, notes/gbpark/2026-05-08-input-canonical-migration.md, notes/gbpark/2026-05-12-codex-handoff-input-canonical.md, and notes/gbpark/2026-05-19-canonical-data-view-cleanup-followup.md. Then finish the table canonical cleanup to the same completion standard as canonical input: required v2-table-list/table-list behavior must be absorbed into canonical table, legacy/v2 table runtime aliases/fallback/schema/config-panel paths must be removed, FieldConfig/DataPort/sourceProvider/dataReceiver behavior must be preserved or reimplemented in canonical table, v2-input/v2-select must not be reintroduced, git diff --check must pass, backend compileJava must pass if backend is touched, and the final report must prove all remaining table-list/v2-table-list matches are either zero in code paths or explicitly non-runtime docs only. +``` + +--- + +## 보조 프롬프트 + +```text +You are working in /Users/gbpark/invyone. + +This is a large refactor. The goal is not another "preserve and classify" pass. The goal is to bring table cleanup to the same completion standard as the canonical input cleanup. + +Read these files first: +- notes/gbpark/2026-05-20-table-canonical-input-parity-claude-prompt.md +- notes/gbpark/2026-05-08-input-canonical-migration.md +- notes/gbpark/2026-05-12-codex-handoff-input-canonical.md +- notes/gbpark/2026-05-19-canonical-data-view-cleanup-followup.md + +Key precedent from canonical input: +- v2-input / v2-select were not kept as runtime compatibility aliases. +- v2-input / v2-select renderer folders, component bodies, config panels, alias/fallback/schema/default paths were removed. +- Required behavior was absorbed into canonical input. +- DB layout JSON migration SQL was not written. +- New-solution code paths use canonical input. + +Apply that same policy to table: +- canonical id is "table". +- v2-table-list / table-list are removal targets, not long-term runtime aliases. +- Do not keep weak compatibility shims just to keep old type names alive. +- If an old behavior is needed, implement or move it into canonical table or a neutral helper with no v2-table-list/table-list component identity. +- Do not write DB layout JSON migration SQL unless the user explicitly asks later. + +Non-negotiable rules: +1. Do not reintroduce v2-input, v2-select, V2InputRenderer, V2SelectRenderer, V2Input.tsx, V2Select.tsx, EntityPicker, or EntitySearchModal. +2. Do not shrink FieldConfig, DataPort, sourceProvider, dataReceiver, selectedRow/selectedRows, searchParams, or refreshTrigger contracts. +3. Do not use broad git reset/checkout/clean. Preserve unrelated user changes. +4. Do not delete domain/special components such as modal-repeater-table, simple-repeater-table, repeat-screen-modal, tax-invoice-list, universal-form-modal table sections unless equivalent canonical behavior is implemented and active references are updated. +5. Do not leave v2-table-list/table-list as a runtime alias/fallback/schema/config-panel path in final code unless you stop and report a concrete blocker. The intended final state is removal. +6. Keep the work scoped to table canonical cleanup. Do not revive the broader stats/container cleanup. + +Current known table state: +- New creation path is canonical table: + frontend/lib/registry/components/table/index.ts +- table-list/ and v2-table-list/ shell folders are already deleted. +- The old runtime still survives under: + frontend/lib/registry/components/table/_shared/TableListComponent.tsx + frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx + frontend/lib/registry/components/table/_shared/V2TableListContainerWrapper.tsx + frontend/lib/registry/components/table/_shared/TableListConfigPanel.tsx + frontend/lib/registry/components/table/_shared/tableListConfigTypes.ts +- TableComponent currently early-delegates old raw types: + rawType === "table-list" -> LegacyTableListWrapper + rawType === "v2-table-list" -> V2TableListContainerWrapper +- Aliases/schema/default/config routing still exist in: + frontend/lib/registry/DynamicComponentRenderer.tsx + frontend/lib/utils/templateMigrate.ts + frontend/lib/utils/getComponentConfigPanel.tsx + frontend/lib/schemas/componentConfig.ts + frontend/lib/utils/componentTypeUtils.ts +- InvDataConfigPanel still has a v2-table-list branch and imports V2TableListConfigPanel. +- V2List.tsx builds a component object with type: "table-list". +- repeat-container / v2-repeat-container use dataSourceType = "table-list"; if this is not a component id, rename it to a clearer non-component enum such as "legacyTableSelection" or "tableSelectionSource" and update references. + +Required implementation phases: + +Phase 0 - Inventory and baseline +- Run: + git status --short + rg -n "v2-table-list|table-list|V2TableList|TableListComponent|TableListConfigPanel|V2TableListContainerWrapper" frontend/lib frontend/components frontend/app frontend/types + rg -n "v2-input|v2-select|V2InputRenderer|V2SelectRenderer" frontend/lib frontend/components frontend/app frontend/types frontend/styles +- Summarize each match into: runtime path, config path, schema/default path, type/helper path, docs/comment only, domain enum. + +Phase 1 - Canonical Table parity inventory +- Compare canonical TableComponent / InvTableConfigPanel against _shared/TableListComponent and _shared/V2TableListComponent. +- Build a short checklist in the final report for these old behaviors: + selection, multi-select, checkbox, sorting, pagination, column visibility/order/width/align, searchable/filterable per column, linked filter, exclude filter, entity label display, category/code select, date picker fallback, image url/fallback, inline edit, row actions, context menu, export Excel, card mode, GroupSum, ResizeObserver responsive card fallback, DataProvider/DataReceiver, FieldConfig adapter, selectedTable/tableName/dbTable compatibility. +- Implement missing behavior in canonical table, or move reusable internals to neutral files under frontend/lib/registry/components/table/ with neutral names. Do not preserve old component identity. + +Phase 2 - Canonical runtime absorption +- Remove early delegation from TableComponent after parity is implemented. +- Delete or empty old runtime files only when imports are gone: + _shared/TableListComponent.tsx + _shared/V2TableListComponent.tsx + _shared/V2TableListContainerWrapper.tsx + _shared/TableListConfigPanel.tsx +- Keep neutral helpers only if they do not encode old component identity: + SingleTableWithSticky.tsx may survive if it is a neutral internal table view, but rename/comments/types must not imply table-list component ownership. + CardModeRenderer.tsx may survive as a neutral card table helper if needed. + tableListConfigTypes.ts should be renamed or absorbed if it remains a runtime type dependency. + +Phase 3 - Config and Studio paths +- InvTableConfigPanel must become the only table config panel. +- Remove InvDataConfigPanel v2-table-list branch and V2TableListConfigPanel import. +- Remove V2TableListConfigPanel.tsx if no active imports remain. +- Migrate V2List.tsx away from TableListComponent/table-list object identity or delete/deprecate it if no active canonical path needs it. +- Update ComponentsPanel, V2PropertiesPanel, DetailSettingsPanel, ActionTab, DataTab, InvLegacyButtonConfigPanel, ButtonPrimaryComponent, TabsWidget, RealtimePreviewDynamic, ScreenNode, screen pages, and buttonActions so they use canonical table helpers without old component ids. + +Phase 4 - Alias/schema/type cleanup +- Remove table-list/v2-table-list aliases from: + DynamicComponentRenderer.LEGACY_TO_UNIFIED + templateMigrate.LEGACY_TO_UNIFIED + getComponentConfigPanel.CONFIG_PANEL_ALIAS + componentTypeUtils.TABLE_LIKE_COMPONENT_TYPES +- Remove v2-table-list schema/default from: + frontend/lib/schemas/componentConfig.ts +- Remove old table component type examples from: + frontend/types/invyone-component.ts + frontend/types/screen-management.ts + frontend/types/v2-components.ts + frontend/types/component-events.ts +- If a value is a domain enum rather than component id, rename it away from table-list so grep is not ambiguous. + +Phase 5 - Verification +- Required commands: + git diff --check + rg -n "v2-input|v2-select|V2InputRenderer|V2SelectRenderer" frontend/lib frontend/components frontend/app frontend/types frontend/styles + rg -n "EntityPicker|entity-picker|EntitySearchModal" frontend/lib/registry/components/input frontend/components/v2/config-panels/InvFieldConfigPanel.tsx + rg -n "v2-table-list|table-list|V2TableList|TableListComponent|TableListConfigPanel|V2TableListContainerWrapper" frontend/lib frontend/components frontend/app frontend/types + rg -n "components/v2-table-list|components/table-list|./v2-table-list|./table-list" frontend +- Expected: + input forbidden tokens stay 0. + table old runtime/config/schema/import tokens should be 0 in code paths. + Any remaining table-list/v2-table-list matches must be docs-only or explicitly justified non-runtime comments. Prefer removing/rewriting stale comments. +- Run targeted typecheck if practical: + cd frontend && npx tsc --noEmit --pretty false + If global existing errors remain, report only whether modified files introduced new errors. +- Run backend compile only if backend touched: + cd backend-spring && ./gradlew compileJava + +Final report requirements: +- List files changed and deleted. +- State whether final code still has any table-list/v2-table-list runtime/config/schema aliases. +- Include the rg results above. +- Include the old behavior parity checklist and mark implemented / not applicable / unresolved. +- If any blocker prevents full input-parity completion, stop with exact file/line blockers and do not pretend the cleanup is complete. +``` diff --git a/notes/gbpark/2026-05-20-table-canonical-phase-b2-b3-d9-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-phase-b2-b3-d9-claude-prompt.md new file mode 100644 index 00000000..845666eb --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-phase-b2-b3-d9-claude-prompt.md @@ -0,0 +1,235 @@ +# Table Canonical Cleanup — Phase B.2 + B.3 + D.9 Claude Prompt + +You are working in `/Users/gbpark/invyone`. + +Reference: +- `notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md` + - §8 Phase B.2 + - §8 Phase B.3 + - §8 Phase D.9 + - §4 dependency table + +Current status: +- B.1 / B.4 complete. +- C.1 ~ C.5 complete. +- D.1 ~ D.8 complete. +- D.9 is blocked by B.2 + B.3, so this session should finish the prerequisite wiring and then complete the canonical TableComponent D.9 wiring. + +Important workspace rule: +- Do not revert or touch unrelated dirty files. +- Known unrelated dirty files include: + - `frontend/app/(main)/admin/systemMng/numberingRuleList/page.tsx` + - `frontend/components/layout/TabBar.tsx` + - `frontend/styles/v5-layout.css` + - `notes/gbpark/2026-05-20-control-ide-refactor.md` + - `notes/gbpark/2026-05-20-numbering-rule-*.html` + - `open-design/` +- Do not edit `_shared/TableListComponent.tsx` or `_shared/V2TableListComponent.tsx`; read them only as parity references. +- Do not reintroduce `v2-input`, `v2-select`, `EntityPicker`, or `EntitySearchModal`. +- Preserve the canonical TableComponent early delegation branch for legacy table-list / v2-table-list. + +Goal: + +Canonical `frontend/lib/registry/components/table/TableComponent.tsx` must expose the same data-transfer and split-panel runtime contracts that legacy `_shared/TableListComponent.tsx` and `_shared/V2TableListComponent.tsx` expose: + +1. ScreenContext data provider registration. +2. ScreenContext data receiver registration. +3. SplitPanelContext receiver registration when mounted inside a split panel. +4. Left-panel row selection should set selected left data for right-panel consumers. +5. Right-panel linked filters should merge split-panel linked filter values into table search where compatible. +6. Provider absence must be safe. Canonical TableComponent must still mount outside ScreenContext / SplitPanelContext. + +Primary file scope: +- `frontend/lib/registry/components/table/TableComponent.tsx` + +Allowed only if needed: +- `frontend/lib/registry/components/table/useTableData.ts` + - only to expose a minimal local-data setter needed by `DataReceivable.receiveData`. +- `frontend/types/data-transfer.ts` + - additive type-only changes only. Do not shrink existing contracts. +- `frontend/contexts/SplitPanelContext.tsx` + - only if you prove canonical cannot be supported by consuming the existing context. + +Prefer not to edit: +- `frontend/lib/registry/components/split-panel-layout/SplitPanelContext.tsx` +- `frontend/lib/registry/components/v2-split-panel-layout/SplitPanelContext.tsx` + +Read first: +- `frontend/types/data-transfer.ts` +- `frontend/contexts/ScreenContext.tsx` +- `frontend/contexts/SplitPanelContext.tsx` +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` + - around `useScreenContextOptional` + - around `useSplitPanelContext` + - around `DataProvidable` + - around `DataReceivable` + - around split-panel receiver registration + - around row click / `setSelectedLeftData` +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` + - same areas as above +- current canonical: + - `frontend/lib/registry/components/table/TableComponent.tsx` + - `frontend/lib/registry/components/table/useTableData.ts` + +Implementation requirements: + +## 1. Import / context wiring + +In canonical `TableComponent.tsx`, wire: +- `useSplitPanelContext` and `SplitPanelPosition` from `@/contexts/SplitPanelContext`. +- `DataProvidable`, `DataReceivable`, `DataReceiverConfig`, `DataReceivableComponentType`, and `EntityJoinColumnMeta` from `@/types/data-transfer` as needed. + +There is already `screenContext = useScreenContextOptional()` from D.2. Reuse it. + +Derive the current split position the same way legacy does: +- `screenContext?.split_panel_position` +- else `splitPanelContext?.getPositionByScreenId(Number(props.screenId))` +- else `null` + +Do not require `props.screenId` to exist. Guard it. + +## 2. DataProvidable parity + +Create a stable provider object with `useMemo`. + +Contract: +- `component_id`: canonical component id (`component.id` / `_componentId`). +- `component_type`: prefer `"table"` for canonical. If you choose `"table-list"` for legacy compatibility, explain why in report. +- `table_name`: effective canonical `tableName`. +- `getSelectedData()`: + - use canonical selected row state (`selectedRows` index set) and current runtime `rows` / `tableData.data`. + - preserve the same semantics as current selection callbacks. +- `getAllData()`: + - return current runtime rows. +- `clearSelection()`: + - clear `selectedRows` and `selectedRowIdx`. + - call existing selection emission logic if needed so external selected row callbacks stay consistent. +- `getEntityJoinColumns()`: + - return snake_case `EntityJoinColumnMeta[]`. + - use `sourceColumns` metadata: + - `additionalJoinInfo` maps to `{ source_table, source_column, join_alias, reference_table }`. + - also preserve useful `entityJoinInfo` if it has enough fields. + - no crashes on missing metadata. + +## 3. DataReceivable parity + +Create a stable receiver object with `useMemo`. + +Contract: +- `component_id`: same as provider. +- `component_type`: `"table"` as `DataReceivableComponentType`. +- `getData()` returns current runtime rows. +- `receiveData(data, config)` supports at least: + - `append` + - `replace` + - `merge` + +Legacy behavior mutates local table data. Canonical `useTableData` currently exposes data but not a setter. Choose the smallest safe implementation: +- Preferred: extend `useTableData.ts` with a minimal setter API (`setLocalData` or similar) and update `UseTableDataResult`. +- Alternative: keep a local override state inside `TableComponent` only if it does not break pagination/refresh semantics. + +Rules: +- Do not refetch just to apply received data. +- `merge` should merge by `id` when present, then by `${tableName}_id` when present, then append with a generated stable-ish fallback key. +- If config has mapping rules, apply them conservatively: + - source field -> target field. + - default value if source missing. + - required missing values should skip that field, not crash the whole receive. +- Keep mode unknown cases safe. + +## 4. ScreenContext registration + +Register provider and receiver when all are true: +- not design mode +- `screenContext` exists +- component id exists + +Cleanup: +- unregister both on unmount / dependency change. + +Use stable `useMemo` / `useEffect` deps. Avoid infinite re-register loops caused by non-memoized objects. + +## 5. SplitPanelContext receiver registration + +If `splitPanelContext`, component id, and current split position exist: +- register receiver using `splitPanelContext.registerReceiver(position, componentId, receiver)`. +- receiver should forward to canonical `DataReceivable.receiveData(...)` with `target_component_type: "table"` and the incoming mode. +- unregister on cleanup. + +Do not edit legacy split-panel provider unless the existing context cannot support canonical registration. If you do edit it, make the change additive and explain why. + +## 6. Left-panel selected data + +When a row is selected in canonical table and current split position is `"left"`: +- if `splitPanelContext.disable_auto_data_transfer` is false, call `splitPanelContext.setSelectedLeftData(row)`. +- if selection is cleared, call `setSelectedLeftData(null)`. + +Integrate this into the existing `handleRowClick`, `handleCheckboxToggle`, or central `emitSelection` path so both single and multi-selection stay coherent. + +Do not break existing `onRowSelect` / `onSelectedRowsChange` callbacks. + +## 7. Right-panel linked filters + +When current split position is `"right"` and SplitPanelContext has linked filters: +- merge `splitPanelContext.getLinkedFilterValues()` into the canonical active search object. +- preserve D.2 search merge priority: + - props.searchParams + - TableOptions filters + - linkedFilters from ScreenContext providers + - searchApplied +- Add split-panel linked filter values at the same layer as linkedFilters, but do not let empty values override meaningful searchApplied. +- Sanitize values using the existing `_sanitizeSearchValues` path if available. + +Also support `splitPanelContext.selected_left_data` fallback for configured right-panel filters if current D.2 `excludeFilter` / linked filter logic can reuse it without large changes. + +## 8. Left-panel added item filtering + +Legacy table filters out items whose ids are in `splitPanelContext.added_item_ids` for left panel display. + +Implement only if it is straightforward in canonical without corrupting pagination/total: +- safe option: filter only `rows` render output for current page when current split position is `"left"`. +- do not mutate `tableData.data`. +- do not change server total. + +If not implemented, explicitly report it as remaining parity gap. + +## 9. Forbidden work in this session + +- Do not implement D.10 autoGeneration. +- Do not migrate V2List / E phases. +- Do not delete `v2-table-list`, `table-list`, aliases, schemas, or registry entries. +- Do not touch config panel parity unless a type compile error forces a tiny additive fix. +- Do not add broad new abstractions. +- Do not add network dependencies. +- Do not add toast dependencies. + +Validation: + +Run: +```bash +git diff --check +rg -n "DataProvidable|DataReceivable|useSplitPanelContext|registerDataProvider|registerDataReceiver|registerReceiver|setSelectedLeftData|getLinkedFilterValues" frontend/lib/registry/components/table frontend/contexts frontend/types +cd frontend && npx tsc --noEmit --pretty false 2>&1 | rg "^(lib/registry/components/table/(TableComponent|useTableData|types)|contexts/(ScreenContext|SplitPanelContext)|types/data-transfer)" +``` + +Expected: +- `git diff --check` passes. +- Targeted tsc grep has no output. +- If full `tsc` has unrelated baseline errors, list only errors that touch the scoped files above. + +Report format: + +1. Files changed with line-count scale. +2. Which B.2 option was used for SplitPanel support: + - existing context consumed only + - context additive change + - other +3. DataProvidable contract summary. +4. DataReceivable contract summary. +5. SplitPanel behavior summary: + - receiver registration + - left selected data + - right linked filters + - added item filtering implemented or deferred +6. Verification results. +7. Remaining parity gaps, if any. diff --git a/notes/gbpark/2026-05-20-table-canonical-phase-c3-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-phase-c3-claude-prompt.md new file mode 100644 index 00000000..98e5b32d --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-phase-c3-claude-prompt.md @@ -0,0 +1,237 @@ +# Phase C.3 Claude Prompt — Table Canonical Filter Config Parity + +You are working in `/Users/gbpark/invyone`. + +Reference: +- `notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md` §8 Phase C.3 +- Already completed in the current worktree: B.1, B.4, C.1, C.2, C.5 +- Codex follow-up patches already applied: + - `useTableData.ts` keeps `defaultSort` initial state instead of resetting it on mount. + - `InvTableConfigPanel.tsx` keeps `striped/hoverable` aliases synchronized with `tableStyle.alternateRows/hoverEffect`. + +Goal: +Complete **Phase C.3 only**: canonical table config parity for filters. + +Absorb the old table filter config surface into canonical `TableConfig` and expose it in canonical `InvTableConfigPanel`, while leaving actual runtime filter execution to Phase D.2. + +## Scope + +Primary files: +- `frontend/lib/registry/components/table/types.ts` +- `frontend/lib/registry/components/table/InvTableConfigPanel.tsx` + +Allowed narrow touch: +- `frontend/lib/registry/components/table/TableComponent.tsx` + - only for `fromProps` merge and DOM prop filtering of new config fields + - no runtime filter execution in this phase + +Reference-only files: +- `frontend/lib/registry/components/table/_shared/tableListConfigTypes.ts` +- `frontend/types/screen-management.ts` +- `frontend/components/screen/config-panels/DataFilterConfigPanel.tsx` + +Do not modify old runtime bodies: +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/TableListConfigPanel.tsx` + +## Required Type Work + +In `frontend/lib/registry/components/table/types.ts`: + +1. Import `DataFilterConfig` from `@/types/screen-management` as a type-only import. + +2. Add canonical filter interfaces matching the old config shape, but name them canonically to avoid collisions: + - `TableFilterConfig` + - `TableLinkedFilterConfig` + - `TableExcludeFilterConfig` + +Use the old definitions in `_shared/tableListConfigTypes.ts` as the source of truth: + +```ts +FilterConfig { + enabled: boolean; + filters: Array<{ + columnName: string; + widgetType: string; + label: string; + gridColumns: number; + numberFilterMode?: "exact" | "range"; + codeInfo?: string; + referenceTable?: string; + referenceColumn?: string; + displayColumn?: string; + }>; + bottomSpacing?: number; +} + +LinkedFilterConfig { + sourceComponentId: string; + sourceField?: string; + targetColumn: string; + operator?: "equals" | "contains" | "in"; + enabled?: boolean; +} + +ExcludeFilterConfig { + enabled: boolean; + referenceTable: string; + referenceColumn: string; + sourceColumn: string; + filterColumn?: string; + filterValueSource?: "url" | "formData" | "parentData"; + filterValueField?: string; +} +``` + +3. Add these fields to `TableConfig` with clear docstrings: + - `filter?: TableFilterConfig` + - `linkedFilters?: TableLinkedFilterConfig[]` + - `excludeFilter?: TableExcludeFilterConfig` + - `dataFilter?: DataFilterConfig` + +Docstrings must clearly say: +- C.3 is config parity + ConfigPanel editing. +- Runtime application is Phase D.2. +- `dataFilter` reuses the shared `DataFilterConfig` contract from `screen-management.ts`. + +## Required ConfigPanel Work + +In `frontend/lib/registry/components/table/InvTableConfigPanel.tsx`: + +Add a new collapsed CP group for filters. Since C.5 already added groups `⑥` to `⑨`, do not churn existing numbering. Add this group after `⑨ 툴바 버튼` and before existing `동작 옵션`: + +```tsx +... +``` + +Use the existing CP primitives and local compact row style: +- `CPGroup`, `CPRow`, `CPText`, `CPSelect`, `CPSegment`, `CPNumber`, `CPSwitch`, `Hint` +- Native `` is acceptable where `CPText` is too constrained. +- Do not import `DataFilterConfigPanel`; it is shadcn/Tailwind styled and does not match this CP panel. + +### UI Sections Inside `⑩ 필터` + +Keep the UI compact. Avoid nested `CPGroup` inside this `CPGroup`. + +1. **검색 필터 위젯 (`filter`)** + - switch: `filter.enabled` + - number: `filter.bottomSpacing` + - list editor for `filter.filters` + - each row edits: + - `columnName` via `CPSelect` from available columns + - `widgetType` via `CPSelect` (`text`, `number`, `date`, `select`, `entity`, `code`, `checkbox`) + - `label` via `CPText` or input + - `gridColumns` via `CPNumber` + - optional `numberFilterMode` via `CPSegment` when `widgetType === "number"` + - optional `codeInfo`, `referenceTable`, `referenceColumn`, `displayColumn` as compact text inputs + - add/remove buttons can follow the existing column editor button style. + +2. **연결 필터 (`linkedFilters`)** + - list editor for `linkedFilters` + - each row edits: + - `enabled` + - `sourceComponentId` + - `sourceField` + - `targetColumn` via available columns + - `operator` via `CPSegment` (`equals`, `contains`, `in`) + - add/remove controls. + +3. **제외 필터 (`excludeFilter`)** + - switch: `excludeFilter.enabled` + - text/select controls: + - `referenceTable` + - `referenceColumn` + - `sourceColumn` via available columns + - `filterColumn` + - `filterValueSource` via `CPSegment` (`url`, `formData`, `parentData`) + - `filterValueField` + - Preserve optional empty strings as `undefined` where reasonable. + +4. **데이터 필터 (`dataFilter`)** + - switch: `dataFilter.enabled` + - segment: `dataFilter.match_type` (`all`, `any`) + - compact list editor for `dataFilter.filters` + - each row edits: + - `column_name` via available columns + - `operator` via `CPSelect` using the `ColumnFilter.operator` values + - `value_type` via `CPSegment` (`static`, `category`, `code`, `dynamic`) + - `value` as text input. For `in` / `not_in`, comma text is acceptable and should become `string[]`; otherwise string. + - for `date_range_contains`, expose `range_config.start_column` and `range_config.end_column`. + - New filter default: + - `id: filter-${Date.now()}` + - first available column key or `""` + - `operator: "equals"` + - `value: ""` + - `value_type: "static"` + +### Available Column Options + +Build a small local helper or `useMemo` to normalize available columns from: +- canonical `columns` (`TableColumn[]`) +- `effectiveTableColumns` from connected DB metadata, if canonical columns are empty + +Option shape: +- `value`: canonical key / DB column name +- `label`: display label / column name + +Avoid unstable assumptions about backend metadata. Support common keys already used in this file: +- `key` +- `columnName` +- `column_name` +- `name` +- `label` +- `displayName` + +## Optional TableComponent Narrow Work + +In `frontend/lib/registry/components/table/TableComponent.tsx`, only if needed: + +1. Add `fromProps` merge for: + - `filter` + - `linkedFilters` + - `excludeFilter` + - `dataFilter` + +2. Add these fields to DOM prop filtering so they never leak onto DOM nodes. + +3. Add comments that runtime application is Phase D.2. + +Do **not** pass these fields to `useTableData` in C.3. +Do **not** implement linked filter polling, SplitPanelContext reading, excludeFilter API params, or AdvancedSearchFilters here. + +## Forbidden + +- Do not modify `_shared/TableListComponent.tsx` or `_shared/V2TableListComponent.tsx`. +- Do not delete old table-list/v2-table-list code. +- Do not remove early delegation in canonical `TableComponent.tsx`. +- Do not touch schema, alias routing, template migration, component registry, or input/select/entity components. +- Do not import or revive `v2-input`, `v2-select`, `EntityPicker`, `EntitySearchModal`. +- Do not do Phase D.2 runtime filtering in this phase. +- Do not do Phase C.4 actions. +- Do not change unrelated untracked notes, especially `notes/gbpark/2026-05-20-control-ide-refactor.md`. + +## Verification + +Run: + +```bash +git diff --check +rg -n "TableFilterConfig|TableLinkedFilterConfig|TableExcludeFilterConfig|filter\\?:|linkedFilters|excludeFilter|dataFilter" frontend/lib/registry/components/table +cd frontend && npx tsc --noEmit --pretty false 2>&1 | rg "lib/registry/components/table/(InvTableConfigPanel|TableComponent|types)|types/screen-management|DataFilterConfig" +``` + +Expected: +- `git diff --check` has no output. +- Targeted `tsc` grep shows no new errors for changed table files. +- Existing unrelated errors elsewhere may still appear if running full `tsc`; report them separately. + +## Report Format + +After finishing, report: +- changed files and line-count summary +- exact fields added to `TableConfig` +- which filter editors were added in `InvTableConfigPanel` +- confirmation that runtime filtering was intentionally deferred to D.2 +- validation command results +- any existing unrelated `tsc` errors, clearly marked unrelated diff --git a/notes/gbpark/2026-05-20-table-canonical-phase-c4-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-phase-c4-claude-prompt.md new file mode 100644 index 00000000..484c8c02 --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-phase-c4-claude-prompt.md @@ -0,0 +1,204 @@ +# Phase C.4 Claude Prompt — Table Canonical Action Config Parity + +You are working in `/Users/gbpark/invyone`. + +Reference: +- `notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md` §8 Phase C.4 +- Already completed in the current worktree: B.1, B.4, C.1, C.2, C.3, C.5 +- C.3 review follow-up already applied by Codex: + - filter column options now remove empty/duplicate values + - dataFilter operator changes normalize `value` between string and string[] + - C.3 is still config-only; runtime filter application remains D.2 + +Goal: +Complete **Phase C.4 only**: canonical table config parity for row actions / bulk actions. + +Absorb old `ActionConfig` into canonical `TableConfig` and expose it in canonical `InvTableConfigPanel`, while leaving actual runtime action execution to Phase D.4. + +## Scope + +Primary files: +- `frontend/lib/registry/components/table/types.ts` +- `frontend/lib/registry/components/table/InvTableConfigPanel.tsx` + +Allowed narrow touch: +- `frontend/lib/registry/components/table/TableComponent.tsx` + - only for `fromProps` merge and DOM prop filtering of new action config fields + - no runtime action execution in this phase + +Reference-only files: +- `frontend/lib/registry/components/table/_shared/tableListConfigTypes.ts` +- `frontend/lib/registry/components/table/_shared/TableListConfigPanel.tsx` +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` + +Do not modify old runtime bodies: +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/TableListConfigPanel.tsx` + +Also do not touch unrelated current dirty files: +- `frontend/components/layout/TabBar.tsx` +- `frontend/styles/v5-layout.css` +- `notes/gbpark/2026-05-20-control-ide-refactor.md` + +## Required Type Work + +In `frontend/lib/registry/components/table/types.ts`: + +1. Add canonical action interfaces matching the old config shape, but use canonical names: + - `TableActionType` + - `TableActionItemConfig` + - `TableActionConfig` + +Source of truth from `_shared/tableListConfigTypes.ts`: + +```ts +ActionConfig { + showActions: boolean; + actions: Array<{ + type: "view" | "edit" | "delete" | "custom"; + label: string; + icon?: string; + color?: string; + confirmMessage?: string; + targetScreen?: string; + }>; + bulkActions: boolean; + bulkActionList: string[]; +} +``` + +2. Add to `TableConfig`: + +```ts +actions?: TableActionConfig; +``` + +Docstring must say: +- C.4 is config parity + ConfigPanel editing. +- Runtime rendering/execution is Phase D.4. +- This is table-level row/bulk actions, separate from `cardStyle.showActions` and `cardColumnMapping.actionColumns`. + +## Required ConfigPanel Work + +In `frontend/lib/registry/components/table/InvTableConfigPanel.tsx`: + +Add a new collapsed CP group after `⑩ 필터` and before existing `동작 옵션`: + +```tsx +... +``` + +Use existing CP primitives and local compact helpers: +- `CPGroup`, `CPRow`, `CPText`, `CPSelect`, `CPSegment`, `CPNumber`, `CPSwitch`, `Hint` +- Native `` is acceptable where compact text fields are easier. +- Reuse C.3 helpers if appropriate (`SubSectionHeading`, `AddRowButton`, `RemoveRowButton`), but keep write scope in this file. + +### UI Sections Inside `⑪ 액션` + +1. **Row actions** + - switch: `actions.showActions` + - list editor for `actions.actions` + - each row edits: + - `type` via `CPSegment` or `CPSelect`: `view`, `edit`, `delete`, `custom` + - `label` + - `icon` + - `color` + - `confirmMessage` + - `targetScreen` + - add/remove controls. + - New row default: + - `type: "view"` + - `label: "보기"` + +2. **Bulk actions** + - switch: `actions.bulkActions` + - compact editor for `actions.bulkActionList` + - simple comma-separated text input is acceptable, but trim empty entries and store `string[]`. + - Help text should clarify runtime execution is D.4. + +### Defaults / Patch Helper + +Add a local `patchActions` helper near the C.3 helpers: + +```ts +const patchActions = (next: Partial>) => + patch({ + actions: { + showActions: false, + actions: [], + bulkActions: false, + bulkActionList: [], + ...current.actions, + ...next, + } as any, + }); +``` + +Do not overwrite existing `actions.actions` while toggling `showActions`. +Do not overwrite existing `bulkActionList` while toggling `bulkActions`. + +## Optional TableComponent Narrow Work + +In `frontend/lib/registry/components/table/TableComponent.tsx`, only if needed: + +1. Add `fromProps` merge for: + - `actions` + +2. Add `actions` to DOM prop filtering so it never leaks onto DOM nodes. + +3. Add a short comment that runtime rendering/execution is Phase D.4. + +Do **not** render action columns or buttons in C.4. +Do **not** wire navigation, modal opening, delete API, custom handlers, or bulk selection execution here. + +## Relationship With Existing Card Actions + +Current canonical types already contain: +- `cardStyle.showActions` +- `cardStyle.showViewButton` +- `cardStyle.showEditButton` +- `cardStyle.showDeleteButton` +- `cardColumnMapping.actionColumns` + +Do not remove or rewrite those. They are card-mode presentation hints. The new `actions?: TableActionConfig` is table-level row/bulk action config parity with old `ActionConfig`. + +If you add UI text, make this distinction clear in help text. + +## Forbidden + +- Do not modify `_shared/TableListComponent.tsx` or `_shared/V2TableListComponent.tsx`. +- Do not delete old table-list/v2-table-list code. +- Do not remove early delegation in canonical `TableComponent.tsx`. +- Do not touch schema, alias routing, template migration, component registry, or input/select/entity components. +- Do not import or revive `v2-input`, `v2-select`, `EntityPicker`, `EntitySearchModal`. +- Do not do Phase D.4 runtime action rendering/execution in this phase. +- Do not do Phase D.2 filtering or Phase D.1 column runtime. +- Do not change unrelated TabBar / v5 layout CSS dirty files. + +## Verification + +Run: + +```bash +git diff --check +rg -n "TableActionConfig|TableActionItemConfig|TableActionType|actions\\?:|showActions|bulkActions|bulkActionList" frontend/lib/registry/components/table/{types.ts,InvTableConfigPanel.tsx,TableComponent.tsx} +cd frontend && npx tsc --noEmit --pretty false 2>&1 | rg "lib/registry/components/table/(InvTableConfigPanel|TableComponent|types)" +``` + +Expected: +- `git diff --check` has no output. +- Targeted `tsc` grep shows no new errors for changed table files. +- Existing unrelated errors elsewhere may still appear if running full `tsc`; report them separately. + +## Report Format + +After finishing, report: +- changed files and line-count summary +- exact action types/interfaces added +- `TableConfig` fields added +- which editors were added in `InvTableConfigPanel` +- confirmation that runtime action rendering/execution was intentionally deferred to D.4 +- validation command results +- unrelated dirty files left untouched diff --git a/notes/gbpark/2026-05-20-table-canonical-phase-d1-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-phase-d1-claude-prompt.md new file mode 100644 index 00000000..07cea2d7 --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-phase-d1-claude-prompt.md @@ -0,0 +1,234 @@ +# Phase D.1 Claude Prompt — Table Canonical Column Runtime + +You are working in `/Users/gbpark/invyone`. + +Reference: +- `notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md` §8 Phase D.1 + §9.D.1 +- Already completed in the current worktree: B.1, B.4, C.1, C.2, C.3, C.4, C.5 +- C.3/C.4 are config-only. Do not start D.2 filters or D.4 actions. + +Goal: +Complete **Phase D.1 only**: canonical `TableComponent.tsx` runtime support for the column system. + +Implement runtime support for: +- column visibility from config + TableOptions UI callback +- `hidden` behavior: design mode shows dimmed column, runtime hides it +- `fixed: "left" | "right"` sticky columns +- `fixedOrder` ordering inside fixed groups +- `horizontalScroll` +- `autoWidth` +- `stickyHeader` + +Keep the canonical table lightweight. Do not replace the whole renderer unless absolutely necessary. + +## Scope + +Primary files: +- `frontend/lib/registry/components/table/TableComponent.tsx` +- `frontend/lib/registry/components/table/types.ts` + +Reference-only files: +- `frontend/lib/registry/components/table/_shared/SingleTableWithSticky.tsx` +- `frontend/lib/registry/components/table/_shared/tableListConfigTypes.ts` +- `frontend/types/table-options.ts` +- `frontend/components/screen/table-options/ColumnVisibilityPanel.tsx` +- `frontend/components/screen/table-options/TableSettingsModal.tsx` + +Do not modify old runtime bodies: +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/SingleTableWithSticky.tsx` + +Also do not touch unrelated current dirty files: +- `frontend/components/layout/TabBar.tsx` +- `frontend/styles/v5-layout.css` +- `notes/gbpark/2026-05-20-control-ide-refactor.md` + +## Required Type Work + +In `frontend/lib/registry/components/table/types.ts`, add these canonical config fields to `TableConfig`: + +```ts +autoWidth?: boolean; +stickyHeader?: boolean; +horizontalScroll?: { + enabled?: boolean; + maxVisibleColumns?: number; + minColumnWidth?: number; + maxColumnWidth?: number; +}; +``` + +Docstrings must say: +- These are legacy `TableListConfig` column/runtime options absorbed into canonical config. +- D.1 wires runtime behavior. +- `horizontalScroll.maxVisibleColumns` is a layout threshold, not a hard column visibility cap. + +Do not add ConfigPanel UI in D.1 unless unavoidable. Old layout JSON compatibility and runtime support are the goal. + +## Required TableComponent Work + +### 1. Separate Source Columns From Runtime Columns + +Currently `columns` is used as both config source and render list. For D.1, separate: + +- source/config columns: all configured columns after field adapter overlay, without dropping `visible === false` +- runtime/render columns: derived from source columns + user option overrides + +Behavior: +- `visible === false`: hide in both design and runtime. +- `hidden === true`: in design mode keep visible but dimmed; in runtime hide. +- TableOptions visibility callback can hide/show/reorder/resize/freeze columns without mutating props. + +Avoid losing ConfigPanel-only metadata (`fixed`, `hidden`, `fixedOrder`, `inputType`, `editable`, `thousandSeparator`, entity join metadata). + +### 2. TableOptions Column Visibility Callback + +Use `ColumnVisibility` from `@/types/table-options`. + +Add local state similar to: + +```ts +const [columnOptionsState, setColumnOptionsState] = useState([]); +const [frozenColumnCount, setFrozenColumnCount] = useState(0); +``` + +Wire registration callbacks: + +```ts +onColumnVisibilityChange: setColumnOptionsState +onFrozenColumnCountChange: (count, updatedColumns) => { ... } +``` + +If `updatedColumns` is passed, use it to update visible state too. + +Important: +- Existing TableOptions UI does not persist column visibility in localStorage; do not claim DB persistence. +- This phase only needs mounted runtime state + `tableDisplayStore` metadata sync. +- Do not change `TableOptionsContext` unless a type mismatch forces a narrow fix. + +Registration columns should include all configurable source columns, with `visible` reflecting current runtime state. + +### 3. Runtime Column Derivation + +Create helpers inside `TableComponent.tsx` or near top-level constants: + +- normalize option overrides by `column_name` +- apply visible/order/width/fixed +- derive `renderColumns` +- derive `tableOptionsColumns` + +Ordering policy: +1. TableOptions callback order, if present. +2. `column.order`, if present. +3. original source array order. +4. fixed groups: + - fixed left first + - non-fixed middle + - fixed right last + - within left/right, `fixedOrder` wins if present; otherwise current order. + +`ColumnVisibility.fixed` is boolean only. Treat `true` as `fixed: "left"` because the existing options UI represents frozen left columns only. + +`onFrozenColumnCountChange(count)` should mark the first `count` visible columns as `fixed: "left"` for runtime state. Do not convert right-fixed columns to left unless the callback explicitly affects them through visibility state. + +### 4. Sticky Left/Right Runtime CSS + +Do not import `SingleTableWithSticky` wholesale. Inspect it as a reference for offset math and port only the small sticky offset logic into canonical table if needed. + +Implement sticky styles for `` and ``: +- `position: "sticky"` +- `left` offset for left fixed columns +- `right` offset for right fixed columns +- `zIndex` higher for header, lower for cells +- stable background color so sticky cells do not become transparent over scrolled content +- light border/shadow to distinguish frozen edge + +Offset is the sum of widths of preceding left fixed columns or following right fixed columns. Use the same effective column width helper that rendering uses. + +### 5. Width / Auto Width / Horizontal Scroll + +Add helper: + +```ts +const getColumnWidth = (col: TableColumn): number => ... +``` + +Rules: +- explicit `col.width` wins +- `horizontalScroll.minColumnWidth` default around 120 +- `horizontalScroll.maxColumnWidth` clamps explicit/default width if present +- checkbox column stays stable at 32px +- if `autoWidth === true`, allow browser natural width by not forcing `width`, but still use numeric fallback for sticky offset math + +For table/container: +- existing scroll container remains the wrapper. +- `horizontalScroll.enabled` should produce a `minWidth` for the `` so horizontal scrolling actually appears. +- If `maxVisibleColumns` is set and render column count is greater than it, use at least `maxVisibleColumns * minColumnWidth` or sum of effective widths, whichever is appropriate to keep columns readable. +- `horizontalScroll.maxVisibleColumns` must not drop columns. + +### 6. Sticky Header + +Current canonical header is always sticky. Make it configurable: +- default preserve current behavior: `stickyHeader !== false` +- if `stickyHeader === false`, header should not be sticky. + +### 7. Store Sync + +Update `tableDisplayStore.setTableDataForComponent` metadata to use runtime visible columns: +- `column_labels` should include source columns or render columns consistently. +- `visible_columns` should be runtime render column keys. +- `page_size` remains existing logic. + +### 8. Rendering Places To Update + +Use runtime render columns consistently in: +- header rendering +- row cell rendering +- empty-state colspan +- split detail panel +- `GroupedView` columns prop +- TableOptions registration +- tableDisplayStore metadata + +`CardView` and `PivotView` may continue to use their existing mapping/config fields unless the current basic table columns are directly passed. + +## Forbidden + +- Do not modify `_shared/TableListComponent.tsx`, `_shared/V2TableListComponent.tsx`, or `_shared/SingleTableWithSticky.tsx`. +- Do not delete old table-list/v2-table-list code. +- Do not remove early delegation in canonical `TableComponent.tsx`. +- Do not touch schema, alias routing, template migration, component registry, or input/select/entity components. +- Do not import or revive `v2-input`, `v2-select`, `EntityPicker`, `EntitySearchModal`. +- Do not implement Phase D.2 filters. +- Do not implement Phase D.3 inline edit. +- Do not implement Phase D.4 action rendering/execution. +- Do not implement Phase D.6 toolbar/export/pagination UI. +- Do not change unrelated `TabBar.tsx` or `v5-layout.css` dirty files. + +## Verification + +Run: + +```bash +git diff --check +rg -n "autoWidth|stickyHeader|horizontalScroll|onColumnVisibilityChange|onFrozenColumnCountChange|fixedOrder|hidden" frontend/lib/registry/components/table/{types.ts,TableComponent.tsx} +cd frontend && npx tsc --noEmit --pretty false 2>&1 | rg "lib/registry/components/table/(TableComponent|types)|types/table-options|TableOptionsContext" +``` + +Expected: +- `git diff --check` has no output. +- Targeted `tsc` grep shows no new errors for changed table files. +- Existing unrelated errors elsewhere may still appear if running full `tsc`; report them separately. + +## Report Format + +After finishing, report: +- changed files and line-count summary +- fields added to `TableConfig` +- how source columns vs render columns are derived +- how hidden/visible/fixed/fixedOrder are applied +- how TableOptions callbacks are wired +- what was intentionally deferred to later phases +- validation command results +- unrelated dirty files left untouched diff --git a/notes/gbpark/2026-05-20-table-canonical-phase-d10-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-phase-d10-claude-prompt.md new file mode 100644 index 00000000..15ba9f2e --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-phase-d10-claude-prompt.md @@ -0,0 +1,135 @@ +# Table Canonical Cleanup — Phase D.10 Claude Prompt + +You are working in `/Users/gbpark/invyone`. + +Reference: +- `notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md` §8 Phase D.10 +- Completed phases: B.1, B.2, B.3, B.4, C.1~C.5, D.1~D.9. + +Important workspace rule: +- Do not revert or touch unrelated dirty files. +- Known unrelated dirty files include: + - `frontend/app/(main)/admin/systemMng/numberingRuleList/page.tsx` + - `frontend/components/layout/TabBar.tsx` + - `frontend/styles/v5-layout.css` + - `notes/gbpark/2026-05-20-control-ide-refactor.md` + - `notes/gbpark/2026-05-20-numbering-rule-*.html` + - `open-design/` +- Do not edit `_shared/TableListComponent.tsx` or `_shared/V2TableListComponent.tsx`; read them only as parity references. +- Preserve canonical TableComponent early delegation for legacy `table-list` / `v2-table-list`. +- Do not start E-phase migration in this prompt. + +Goal: + +Finish Phase D.10 conservatively: canonical table should preserve and safely use table column `autoGeneration` metadata where there is a real row-creation path. If no authoritative create/new-row path exists in canonical `TableComponent`, do **not** invent one. In that case, implement only safe type/adapter preservation and report the runtime gap clearly. + +Why this caution matters: +- `AutoGenerationUtils.generateValue()` supports `numbering_rule`, which can allocate real codes. +- Canonical `TableComponent` currently has inline cell update and DataReceivable local data override, but no clear “create new persisted row” UI/API path. +- Do not consume numbering rules on render, search, receive-only preview, or unrelated local state updates. + +Read first: +- `frontend/lib/registry/components/table/TableComponent.tsx` +- `frontend/lib/registry/components/table/types.ts` +- `frontend/lib/fieldConfig/adapters.ts` +- `frontend/lib/utils/autoGeneration.ts` +- `frontend/types/screen.ts` (`AutoGenerationConfig`) +- `frontend/lib/registry/components/input/InputComponent.tsx` autoGeneration behavior +- legacy references only: + - `frontend/lib/registry/components/table/_shared/tableListConfigTypes.ts` + - `_shared/TableListComponent.tsx` + - `_shared/V2TableListComponent.tsx` + +Implementation rules: + +## 1. Audit first + +Before coding, find whether canonical `TableComponent` has any persisted new-row/create path: +- add row button +- insert API +- create API +- paste-to-backend write +- batch edit save +- DataReceivable receive path that is explicitly persisted + +If there is no persisted create path, say so in the report and keep runtime work minimal. + +## 2. Type preservation + +Add canonical type support if missing: +- `TableColumn.autoGeneration?: AutoGenerationConfig` + +Use the existing `AutoGenerationConfig` from `@/types/screen` unless that causes import cycles. If import risk exists, use a structurally compatible type-only alias and explain. + +Do not remove or rename legacy config keys. + +## 3. Adapter preservation + +Update `fieldsToCanonicalColumns()` in `frontend/lib/fieldConfig/adapters.ts` to preserve auto-generation metadata if it exists on field objects: +- `(f as any).autoGeneration` +- `(f as any).auto_generation` + +Do not change `FieldConfig` itself unless absolutely necessary. It currently does not expose `autoGeneration`; this phase should tolerate metadata from legacy layouts without forcing a global schema change. + +Optionally preserve the same metadata in `fieldsToColumns()` for legacy snake output if the mapping is obviously missing and low-risk: +- `autoGeneration` +- `auto_generation` + +But do not touch legacy table runtime files. + +## 4. Runtime behavior + +Only implement runtime auto-generation if there is a real and narrow row creation path. + +Allowed safe runtime surfaces: +- If canonical has a real create/new-row handler, fill empty generated columns immediately before create API call. +- If receiveData is explicitly used as a new-row input and persists rows, apply generation only to newly appended rows, not to existing merge targets. + +Forbidden runtime surfaces: +- Do not generate values during render. +- Do not generate values during normal fetch. +- Do not generate values during inline edit commit unless the user is explicitly editing an auto-generated column and the value is empty. +- Do not allocate `numbering_rule` values for local-only `receiveData` overrides. +- Do not add a new “Add row” UI in this phase. +- Do not make paste write to backend. + +If no safe persisted create path exists, runtime change should be **zero** except helper code that is unused is also discouraged. Prefer no unused helper. + +## 5. Numbering rule policy + +For `numbering_rule`: +- Only call `AutoGenerationUtils.generateValue()` from an explicit persisted create/save path. +- Pass useful form/row context if available. +- If path is not persisted, skip and report deferred. + +For non-allocating types (`uuid`, `current_user`, `current_time`, `sequence`, `random_string`, `random_number`, `company_code`, `department`): +- They may be generated in a create path when target value is empty. +- Still do not generate on render/fetch. + +## 6. Validation + +Run: +```bash +git diff --check +rg -n "autoGeneration|auto_generation|AutoGenerationConfig|AutoGenerationUtils" frontend/lib/registry/components/table frontend/lib/fieldConfig frontend/types/screen.ts frontend/lib/utils/autoGeneration.ts +cd frontend && npx tsc --noEmit --pretty false 2>&1 | rg "^(lib/registry/components/table/(TableComponent|types)|lib/fieldConfig/adapters|lib/utils/autoGeneration|types/screen)" +``` + +Expected: +- `git diff --check` passes. +- Targeted tsc grep has no output. +- If full tsc has unrelated baseline errors, list only scoped-file errors. + +Report format: + +1. Files changed. +2. Audit result: + - persisted create path exists: yes/no + - if yes, where + - if no, runtime generation deferred +3. Metadata preservation summary. +4. Runtime behavior summary: + - implemented, or intentionally zero runtime changes +5. Numbering rule policy result. +6. Verification results. +7. Remaining gap before E.1, if any. diff --git a/notes/gbpark/2026-05-20-table-canonical-phase-d2-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-phase-d2-claude-prompt.md new file mode 100644 index 00000000..f6ab08b9 --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-phase-d2-claude-prompt.md @@ -0,0 +1,234 @@ +# Table Canonical Cleanup — Phase D.2 Claude Prompt + +You are working in `/Users/gbpark/invyone`. + +Reference: +- `notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md` §8 Phase D.2 +- Prior completed phases: B.1/B.4, C.1/C.2/C.3/C.4/C.5, D.1 +- D.1 includes Codex follow-up fixes already present in the worktree: + - `fieldsToCanonicalColumns()` preserves `visible === false` source columns. + - canonical `sourceColumns` overlays FieldConfig columns with `componentConfig.columns` and appends config-only extra columns. + - fixed group ordering treats missing `fixedOrder` as last and stable. + - TableOptions registration uses stable callback deps, runtime column state, and `frozen_column_count`. + - `TableOptionsContext.updateTableDataCount()` writes `data_count`. + +## Goal + +Implement Phase D.2 filter runtime parity for canonical +`frontend/lib/registry/components/table/TableComponent.tsx`. + +Canonical `TableComponent` must now handle: +- `filter` / `filter.filters[]` via `AdvancedSearchFilters` +- `linkedFilters[]` from `ScreenContext` data providers and/or explicit props/search params +- `excludeFilter` by passing the legacy-compatible API payload to `entityJoinApi.getTableDataWithJoins` +- `dataFilter` by passing the configured `DataFilterConfig` to `entityJoinApi.getTableDataWithJoins` +- TableOptions `onFilterChange` callback, replacing the current D.2 placeholder + +This phase is runtime wiring only. Do not expand the ConfigPanel and do not implement D.3/D.4/D.6/D.8 behavior. + +## File Scope + +Allowed to edit: +- `frontend/lib/registry/components/table/TableComponent.tsx` +- `frontend/lib/registry/components/table/useTableData.ts` + +Read-only references: +- `frontend/components/screen/filters/AdvancedSearchFilters.tsx` +- `frontend/lib/api/entityJoin.ts` +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` +- `frontend/contexts/ScreenContext.tsx` +- `frontend/types/table-options.ts` + +Avoid changing `AdvancedSearchFilters.tsx` unless TypeScript makes it unavoidable. If you must touch it, keep the edit tiny and explain why. + +## Forbidden + +- Do not modify old bodies: + - `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` + - `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` + - `frontend/lib/registry/components/table/_shared/V2TableListContainerWrapper.tsx` +- Do not remove early legacy/v2 delegation in canonical `TableComponent`. +- Do not touch schema, registry aliases, template migration, component lists, `V2List`, or split-panel files in this phase. +- Do not reintroduce `v2-input`, `v2-select`, EntityPicker, EntitySearchModal, or any deleted input/select path. +- Do not touch unrelated dirty files: + - `frontend/components/layout/TabBar.tsx` + - `frontend/styles/v5-layout.css` + - `frontend/app/(main)/admin/systemMng/numberingRuleList/page.tsx` + - `notes/gbpark/2026-05-20-control-ide-refactor.md` + - `notes/gbpark/2026-05-20-numbering-rule-clean-v5.html` + +## Current Relevant State + +`TableComponent.tsx` already merges these C.3 fields into `componentConfig` and filters them from DOM props: +- `filter` +- `linkedFilters` +- `excludeFilter` +- `dataFilter` + +`useTableData.ts` currently accepts: +- `tableName` +- `page/pageSize` +- `sortBy/sortOrder` +- `search` +- `enabled` + +`entityJoinApi.getTableDataWithJoins()` already supports: +- `search` +- `dataFilter` +- `excludeFilter` + +See `frontend/lib/api/entityJoin.ts` lines around `getTableDataWithJoins`. + +## Implementation Requirements + +### 1. Extend `useTableData` + +Add params: +- `dataFilter?: DataFilterConfig` +- `excludeFilter?: { enabled: boolean; referenceTable: string; referenceColumn: string; sourceColumn: string; filterColumn?: string; filterValue?: any }` + +Use type-only import for `DataFilterConfig`. + +Pass both through to `entityJoinApi.getTableDataWithJoins()` exactly as supported by `frontend/lib/api/entityJoin.ts`. + +Dependency correctness matters: +- `fetchData` must refetch when effective `search`, `dataFilter`, or `excludeFilter` changes. +- Avoid object identity refetch loops. In `TableComponent`, memoize effective payloads before passing to `useTableData`. +- Preserve C.5 behavior: `defaultSort` must still seed initial sorting and user header sort must still override. +- Preserve C.1 behavior: `autoLoad === false` still disables initial fetch. + +### 2. Advanced Search Filter UI + +Import and render `AdvancedSearchFilters` in canonical `TableComponent` when: +- `componentConfig.filter?.enabled === true` +- and there is at least one configured filter, or table columns can be supplied for auto-generation. + +Place the filter area above the table body and below the toolbar. Respect: +- `componentConfig.filter.bottomSpacing` as margin-bottom, default small spacing. + +Convert canonical `TableFilterConfig.filters[]` and `renderColumns/sourceColumns` to the shape expected by `AdvancedSearchFilters`: +- `columnName` +- `widgetType` +- `label` +- `gridColumns` +- `numberFilterMode` +- `codeInfo` +- `referenceTable` +- `referenceColumn` +- `displayColumn` + +For `tableColumns`, pass normalized column metadata from `sourceColumns`, not only visible columns, so auto-generation can see searchable hidden columns when needed. Include web type fields that `AdvancedSearchFilters` checks (`webType` / `web_type`, `columnName` / `column_name`, labels, visibility). + +Search value handling: +- Keep draft/applied search state so text filters do not rely on stale React state. +- `AdvancedSearchFilters` calls `onSearchValueChange()` then `onSearch()` in the same tick for several widget types. Use a ref or functional next-value helper so `onSearch()` applies the latest value, not the previous render's value. +- `onClearFilters()` clears both draft and applied values. +- Strip empty values and `"__ALL__"` before passing to `useTableData`. + +### 3. Effective Search Merge + +Create one memoized `effectiveSearch` object that merges, in this order: +1. external `props.searchParams` (existing behavior) +2. TableOptions modal filters from `onFilterChange` +3. linked filter values +4. applied AdvancedSearchFilters values + +Later sources win if keys collide. + +Sanitize empty values: +- empty string +- `null` / `undefined` +- `"__ALL__"` +- empty arrays +- range objects with all empty members + +For number/date range objects, preserve the existing `AdvancedSearchFilters` shape; do not invent a new backend contract unless an existing one is clearly used elsewhere. + +### 4. Linked Filters + +Use `useScreenContextOptional()` from `frontend/contexts/ScreenContext.tsx`. + +For each enabled `componentConfig.linkedFilters[]`: +- read source provider via `screenContext.getDataProvider(sourceComponentId)` +- use `getSelectedData()` +- use `sourceField || "value"` +- write the value to `targetColumn` + +Match legacy behavior from `_shared/TableListComponent.tsx` / `_shared/V2TableListComponent.tsx`, but keep implementation smaller: +- initial check on mount/config change +- lightweight polling is acceptable if there is no event bus; use the same 500ms interval style as legacy +- cleanup interval +- do not throw if `ScreenContext` or provider is missing + +If an operator other than equality is configured and no backend `search` contract exists for it, preserve legacy-compatible plain search value and leave a short comment. Do not create a speculative query DSL. + +### 5. Exclude Filter + +Build `excludeFilterParam` in `TableComponent` and pass it to `useTableData`. + +Legacy-compatible payload: +```ts +{ + enabled: true, + referenceTable, + referenceColumn, + sourceColumn, + filterColumn, + filterValue, +} +``` + +Resolve `filterValue` when `filterColumn` and `filterValueField` are present. Priority: +1. explicit props form data (`props.formData`) +2. `screenContext.form_data` +3. URL query param (`window.location.search`) +4. parent/split data if present on props (`splitPanelParentData`, `parentData`, or `selectedParentData`) + +If `filterColumn` is absent, pass the exclude filter without a `filterValue`, same as legacy. + +Memoize the resulting object to avoid unnecessary fetch loops. + +### 6. Data Filter + +Pass `componentConfig.dataFilter` to `useTableData` when enabled. + +Do not implement client-side filtering in this phase unless the API contract is demonstrably unavailable. In this codebase it is available in `entityJoinApi`, so prefer server-side pass-through to keep total/page counts correct. + +### 7. TableOptions Filter Callback + +Replace the D.2 placeholder: +```ts +onFilterChange: () => undefined +``` + +with state wiring: +- keep `tableOptionsFilters` state +- convert `TableFilter[]` to an object merged into `effectiveSearch` +- support at least equality-style values +- keep unsupported operators as simple values unless an existing backend shape is confirmed + +Do not modify TableOptions UI files. + +## Verification + +Run: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | rg "lib/registry/components/table/(TableComponent|useTableData|types)|components/screen/filters/AdvancedSearchFilters|types/table-options|contexts/ScreenContext" +``` + +Expected: +- `git diff --check` has no output. +- targeted `tsc` grep has no new errors for changed files. + +Also run: +```bash +rg -n "onFilterChange|AdvancedSearchFilters|linkedFilters|excludeFilter|dataFilter" frontend/lib/registry/components/table/{TableComponent.tsx,useTableData.ts} +``` + +Report: +- changed files and line ranges +- which filter features are runtime-wired now +- which items are intentionally deferred +- verification output diff --git a/notes/gbpark/2026-05-20-table-canonical-phase-d3-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-phase-d3-claude-prompt.md new file mode 100644 index 00000000..98d1cf6a --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-phase-d3-claude-prompt.md @@ -0,0 +1,196 @@ +# Table Canonical Cleanup — Phase D.3 Claude Prompt + +You are working in `/Users/gbpark/invyone`. + +Reference: +- `notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md` §8 Phase D.3 +- Prior completed phases: B.1/B.4, C.1/C.2/C.3/C.4/C.5, D.1, D.2 +- D.2 includes Codex follow-up fixes already present in the worktree: + - `useTableData` clears stale internal search when filters become empty. + - canonical linked filters handle `getSelectedData()` array results. + - `excludeFilter` is skipped when `filterColumn` exists but no filter value can be resolved. + - `AdvancedSearchFilters` table column metadata now includes `isVisible`. + +## Goal + +Implement Phase D.3 inline cell editing in canonical +`frontend/lib/registry/components/table/TableComponent.tsx`. + +Canonical table cells should enter edit mode on double-click and support input behavior by column type: +- `number` / `decimal` → number input +- `date` / `datetime` → `InlineCellDatePicker` if practical, otherwise safe text/date fallback +- `category` / `code` / `select` → select when options are already available on the column, otherwise text fallback +- default → text input + +This phase should implement immediate-save inline editing only. Keep batch editing, edit-mode toolbar UX, paste behavior, and richer validation for later phases unless a tiny helper is required. + +## File Scope + +Allowed to edit: +- `frontend/lib/registry/components/table/TableComponent.tsx` + +Allowed only if a tiny type or helper is necessary: +- `frontend/lib/registry/components/table/types.ts` + +Read-only references: +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/SingleTableWithSticky.tsx` +- `frontend/components/screen/filters/InlineCellDatePicker.tsx` +- `frontend/lib/api/screen.ts` + +Do not edit old body files. They are references only. + +## Forbidden + +- Do not modify: + - `_shared/TableListComponent.tsx` + - `_shared/V2TableListComponent.tsx` + - `_shared/V2TableListContainerWrapper.tsx` + - `_shared/SingleTableWithSticky.tsx` +- Do not remove early legacy/v2 delegation in canonical `TableComponent`. +- Do not implement D.4 row/bulk actions. +- Do not implement D.5 special cell rendering except what is strictly needed for edit input choice. +- Do not implement D.6 toolbar/export/pagination/paste. +- Do not touch schema, registry aliases, template migration, V2List, split-panel files, or unrelated dirty files. +- Do not reintroduce `v2-input`, `v2-select`, EntityPicker, EntitySearchModal, or deleted input/select paths. + +Unrelated dirty files to leave untouched: +- `frontend/components/layout/TabBar.tsx` +- `frontend/styles/v5-layout.css` +- `frontend/app/(main)/admin/systemMng/numberingRuleList/page.tsx` +- `notes/gbpark/2026-05-20-control-ide-refactor.md` +- `notes/gbpark/2026-05-20-numbering-rule-*.html` + +## Current Relevant State + +Canonical `TableComponent.tsx` already has: +- `sourceColumns` / `renderColumns` +- `TableColumn.editable` +- `TableColumn.inputType` +- `TableConfig.isReadOnly` +- `componentConfig.toolbar?.showEditMode` config field, but D.6 owns toolbar button behavior +- `tableData.refresh()` +- `rows` derived from `tableData.data` +- `renderRows()` with `
` rendering in table mode + +Old reference behavior: +- `_shared/TableListComponent.tsx` around lines 625-648: edit state shape +- `_shared/TableListComponent.tsx` around lines 2213-2231: double-click start edit +- `_shared/TableListComponent.tsx` around lines 2366-2458: immediate save to `/dynamic-form/update-field` +- `_shared/SingleTableWithSticky.tsx` around lines 463-553: input type branching for select/date/number/text + +## Implementation Requirements + +### 1. Edit State + +Add minimal state to canonical `TableComponent`: +- `editingCell: { rowIndex, columnKey, originalValue } | null` +- `editingValue: string` +- `editInputRef` +- optional `savingCellKey` / `editError` + +Focus/select the input when edit starts. + +### 2. Edit Entry Rules + +Start editing on double-click of a data cell only when all are true: +- not design mode +- not `componentConfig.isReadOnly` +- column `editable !== false` +- column key is not checkbox/internal +- `tableName` is known +- row exists + +Do not start edit for grouped/card/pivot/split detail panels unless table-mode cells naturally reuse the same render path. Keep the scope to cells rendered by canonical basic table. + +### 3. Save Rules + +Save on: +- Enter +- blur + +Cancel on: +- Escape + +If value did not change, close without API call. + +Persist using the existing endpoint pattern from old body: +```ts +const { apiClient } = await import("@/lib/api/client"); +await apiClient.put("/dynamic-form/update-field", { + tableName, + keyField, + keyValue, + updateField: columnKey, + updateValue, +}); +``` + +Primary key: +- use `(componentConfig as any).primaryKey` if present +- else use `"id"` +- else try `${tableName}_id` +- if no key value exists, close edit and surface a non-crashing console warning + +After successful save: +- call `tableData.refresh()` +- clear edit state + +On failure: +- keep edit state open +- store a short error string or console.error +- do not corrupt local table data + +### 4. Value Normalization + +Normalize before save: +- empty string → `null` +- number/decimal → `Number(value)` if value is non-empty and numeric +- checkbox/boolean may be deferred unless trivial +- date/datetime should pass string value from picker/input + +### 5. Input Rendering + +When cell is editing, render an input inside the cell: +- text input for default +- number input for number/decimal +- date input or `InlineCellDatePicker` for date/datetime +- select for category/code/select only when options are already available in `column.options` or `column` has an obvious array option field; otherwise text fallback + +Use inline styles consistent with existing canonical table (not Tailwind-only styling unless already in this file). + +Stop propagation for input click/key events so row selection does not fire unexpectedly. + +### 6. Avoid Scope Creep + +Do not implement: +- batch edit mode +- pending changes map +- validation framework +- category/code option fetching or code cache +- cascading lookup loading +- toolbar edit mode button +- toast dependency + +Leave comments for deferred parts only where they help future phases. + +## Verification + +Run: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | rg "^lib/registry/components/table/(TableComponent|types)" +rg -n "editingCell|editingValue|update-field|InlineCellDatePicker|onDoubleClick" frontend/lib/registry/components/table/TableComponent.tsx +``` + +Expected: +- `git diff --check` has no output. +- targeted `tsc` grep has no new errors for changed files. + +Report: +- changed files +- edit entry/save/cancel behavior +- input type branches implemented +- intentionally deferred items +- verification output diff --git a/notes/gbpark/2026-05-20-table-canonical-phase-d4-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-phase-d4-claude-prompt.md new file mode 100644 index 00000000..443ee948 --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-phase-d4-claude-prompt.md @@ -0,0 +1,236 @@ +# Table Canonical Cleanup — Phase D.4 Claude Prompt + +You are working in `/Users/gbpark/invyone`. + +Reference: +- `notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md` §8 Phase D.4 +- Prior completed phases: B.1/B.4, C.1/C.2/C.3/C.4/C.5, D.1, D.2, D.3 +- C.4 already added `TableActionType`, `TableActionItemConfig`, `TableActionConfig`, and `TableConfig.actions`. +- D.3 includes Codex follow-up fixes already present in the worktree: + - checkbox/boolean columns are not editable in canonical inline edit. + - inline edit uses a draft ref so date picker save sees the latest value. + - Enter/blur duplicate save is guarded by a commit ref. + +## Goal + +Implement Phase D.4 row and bulk action runtime wiring for canonical +`frontend/lib/registry/components/table/TableComponent.tsx`. + +This phase should make `componentConfig.actions` usable at runtime: +- render a row action column for basic table mode +- execute `view` / `edit` / `delete` / `custom` row actions +- render a small bulk action bar when row selection + bulk actions are enabled +- execute configured bulk actions conservatively + +Keep this as runtime wiring. Do not expand ConfigPanel. + +## File Scope + +Allowed to edit: +- `frontend/lib/registry/components/table/TableComponent.tsx` + +Allowed only if a tiny type fix is necessary: +- `frontend/lib/registry/components/table/types.ts` + +Read-only references: +- `frontend/lib/registry/components/table/types.ts` +- `frontend/lib/registry/components/table/views/CardView.tsx` +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` +- `frontend/lib/api/screen.ts` +- `frontend/types/component.ts` + +Do not edit old body files or `CardView.tsx` unless TypeScript makes it unavoidable. Prefer wiring existing `CardView` props from `TableComponent` if you support card mode. + +## Forbidden + +- Do not modify: + - `_shared/TableListComponent.tsx` + - `_shared/V2TableListComponent.tsx` + - `_shared/V2TableListContainerWrapper.tsx` + - `_shared/SingleTableWithSticky.tsx` +- Do not remove early legacy/v2 delegation in canonical `TableComponent`. +- Do not implement D.5 special cell rendering. +- Do not implement D.6 toolbar/export/pagination/paste. +- Do not implement schema/alias/V2List/split-panel cleanup. +- Do not reintroduce deleted v2 input/select/entity modal paths. +- Do not touch unrelated dirty files: + - `frontend/components/layout/TabBar.tsx` + - `frontend/styles/v5-layout.css` + - `frontend/app/(main)/admin/systemMng/numberingRuleList/page.tsx` + - `notes/gbpark/2026-05-20-control-ide-refactor.md` + - `notes/gbpark/2026-05-20-numbering-rule-*.html` + +## Current Relevant State + +`TableComponent.tsx` already has: +- `componentConfig.actions` merged from props/config +- `selectedRows: Set` where selected values are row indexes +- `rows` for current visible data +- `tableName` +- `tableData.refresh()` +- D.3 helper `resolveKeyField(row)` for primary key resolution +- checkbox column / selection handlers +- `renderHeader()` and `renderRows()` for basic table mode + +Action config shape: +```ts +interface TableActionConfig { + showActions: boolean; + actions: TableActionItemConfig[]; + bulkActions: boolean; + bulkActionList: string[]; +} + +interface TableActionItemConfig { + type: "view" | "edit" | "delete" | "custom"; + label: string; + icon?: string; + color?: string; + confirmMessage?: string; + targetScreen?: string; +} +``` + +## Implementation Requirements + +### 1. Normalize Active Actions + +Create memoized helpers: +- `rowActions`: enabled when `componentConfig.actions?.showActions === true` and actions array non-empty +- `bulkActionNames`: enabled when `componentConfig.actions?.bulkActions === true` and `bulkActionList` non-empty + +Filter out malformed row actions with no `type`. + +### 2. Row Action Column + +In basic table mode only: +- add an "actions" `` at the right side when row actions are enabled +- add matching `` per row +- update empty-row `colSpan` +- make action cell compact and stable width +- stop event propagation so action clicks do not select rows or start inline edit + +Use lucide icons for known action types if already importing is acceptable: +- `view` → `Eye` +- `edit` → `Pencil` +- `delete` → `Trash2` +- `custom` → `MoreHorizontal` or `Play` + +If the configured `icon` string is unknown, ignore it or show the label; do not build a risky dynamic import. + +### 3. Row Action Execution + +Create a single handler: +```ts +handleRowAction(action, row, rowIndex) +``` + +Order: +1. stop propagation at button level +2. if `action.confirmMessage` exists, `window.confirm()` before proceeding +3. call optional external callbacks if present: + - `props.onRowAction?.(action, row, context)` + - `props.onTableAction?.(action, row, context)` + - `props.onAction?.(action.type, row, context)` +4. perform built-in fallback by `action.type` + +Context should include: +- `tableName` +- `rowIndex` +- `keyField` +- `keyValue` +- `componentId` + +Built-in fallback: +- `view` / `edit`: + - if `action.targetScreen` is a numeric string, dispatch: + ```ts + window.dispatchEvent(new CustomEvent("openScreenModal", { + detail: { + screenId: Number(action.targetScreen), + urlParams: { + mode: action.type, + editId: keyValue, + tableName, + primaryKeyColumn: keyField, + }, + }, + })); + ``` + - if `targetScreen` starts with `/`, use `window.location.assign(targetScreen)`. + - otherwise do not crash; console.info a short message. +- `delete`: + - resolve row id using D.3 `resolveKeyField(row)` / `keyValue` + - confirm with `action.confirmMessage || "이 행을 삭제하시겠습니까?"` + - call `apiClient.delete(`/table-management/tables/${tableName}/delete`, { data: { ids: [String(keyValue)] } })` + - on success: clear selection for deleted index if needed and `tableData.refresh()` +- `custom`: + - dispatch `window.dispatchEvent(new CustomEvent("tableRowAction", { detail }))` after callbacks + - no built-in mutation + +Do not add toast dependency. + +### 4. Bulk Action Bar + +When all are true: +- not design mode +- `bulkActionNames.length > 0` +- `selectedRows.size > 0` + +Render a compact bar between filters and body, or just above the table body: +- selected count +- one button per bulk action name + +Button click: +```ts +handleBulkAction(actionName) +``` + +Rows: +- selected row indexes are in `selectedRows` +- selected data is `Array.from(selectedRows).map(i => rows[i]).filter(Boolean)` + +Built-in bulk behavior: +- `delete`: confirm, collect ids via `resolveKeyField(row)`, call the same delete endpoint with all ids, clear selection, refresh +- `export` / `copy` / unknown: call optional `props.onBulkAction?.(actionName, selectedRowsData, context)` and dispatch `tableBulkAction`; no built-in mutation + +Do not implement Excel export here; D.6 owns export/copy toolbar behavior. + +### 5. Card View Optional Wiring + +If simple and safe, wire existing `CardView` callbacks: +- map first configured `view` action to `onView` +- first `edit` to `onEdit` +- first `delete` to `onDelete` + +Do not edit `CardView.tsx`. If card mode conflicts with `cardStyle.showActions`, leave it untouched and report that row action column is table-mode only for this phase. + +### 6. Styling + +Use the existing inline style pattern in `TableComponent.tsx`. +- no nested cards +- no layout shifts +- compact icon buttons +- action header/cell fixed width + +## Verification + +Run: +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | rg "^lib/registry/components/table/(TableComponent|types)" +rg -n "handleRowAction|handleBulkAction|tableRowAction|tableBulkAction|actions\\?|bulkAction" frontend/lib/registry/components/table/TableComponent.tsx +``` + +Expected: +- `git diff --check` has no output. +- targeted `tsc` grep has no new errors for changed files. + +Report: +- changed files +- row action rendering and handlers +- bulk action rendering and handlers +- built-in fallback behavior +- intentionally deferred items +- verification output diff --git a/notes/gbpark/2026-05-20-table-canonical-phase-d5-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-phase-d5-claude-prompt.md new file mode 100644 index 00000000..1cec4a91 --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-phase-d5-claude-prompt.md @@ -0,0 +1,281 @@ +# Table Canonical Cleanup — Phase D.5 Claude Prompt + +You are working in `/Users/gbpark/invyone`. + +Reference: +- `notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md` §8 Phase D.5 +- Prior completed phases: B.1/B.4, C.1/C.2/C.3/C.4/C.5, D.1, D.2, D.3, D.4 +- D.3 already implemented basic-table inline editing. +- D.4 already implemented row/bulk action runtime wiring. + +## Goal + +Implement Phase D.5 special cell rendering for canonical table: +- image cells +- file / attachment cells +- entity display cells using `entityDisplayConfig` +- number/date/boolean display formatting +- column `langKey` label translation support + +This phase should replace the canonical table's current plain +`String(row[col.key])` display path with a safe canonical cell renderer. Keep the +scope to display rendering. Do not change ConfigPanel. + +## File Scope + +Allowed to edit: +- `frontend/lib/registry/components/table/TableComponent.tsx` +- `frontend/lib/registry/components/table/types.ts` + +Preferred if the implementation grows beyond a small helper: +- create `frontend/lib/registry/components/table/cell-renderers.tsx` + +Allowed only if absolutely required for a type-only compatibility fix: +- `frontend/lib/fieldConfig/adapters.ts` + +Read-only references: +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` + - `TableCellImage` lines near 21-124 + - `TableCellFile` lines near 128-314 + - `formatCellValue` lines near 4489-4700 +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` + - `formatCellValue` lines near 4171-4445 +- `frontend/contexts/ScreenMultiLangContext.tsx` +- `frontend/lib/api/client.ts` +- `frontend/lib/api/file.ts` +- `frontend/lib/formatting.ts` + +## Forbidden + +- Do not modify: + - `_shared/TableListComponent.tsx` + - `_shared/V2TableListComponent.tsx` + - `_shared/V2TableListContainerWrapper.tsx` + - `_shared/SingleTableWithSticky.tsx` +- Do not remove early legacy/v2 delegation in canonical `TableComponent`. +- Do not implement D.6 toolbar/export/pagination/paste. +- Do not implement D.7 card-mode full parity. +- Do not implement schema/alias/V2List/split-panel cleanup. +- Do not reintroduce deleted v2 input/select/entity modal paths. +- Do not add toast dependencies. +- Do not use `require()` inside React render paths. Use static imports or dynamic + imports inside effects/handlers. +- Do not touch unrelated dirty files: + - `frontend/components/layout/TabBar.tsx` + - `frontend/styles/v5-layout.css` + - `frontend/app/(main)/admin/systemMng/numberingRuleList/page.tsx` + - `notes/gbpark/2026-05-20-control-ide-refactor.md` + - `notes/gbpark/2026-05-20-numbering-rule-*.html` + +## Current Relevant State + +`TableComponent.tsx` currently renders basic-table cells as plain strings: + +```tsx +{isEditingThisCell ? ( + renderEditInput(col) +) : isDesignMode ? ( + + {row[col.key] != null ? String(row[col.key]) : "..."} + +) : ( + {row[col.key] != null ? String(row[col.key]) : ""} +)} +``` + +`TableColumn` already has these Phase C.2 fields: +- `inputType` +- `dataType` +- `format` +- `thousandSeparator` +- `isEntityJoin` +- `entityJoinInfo` +- `entityDisplayConfig` +- `additionalJoinInfo` + +It does not yet explicitly type `langKey` / `langKeyId`; add them if needed. + +## Implementation Requirements + +### 1. Add Canonical Cell Renderer + +Create a helper that can be used from `TableComponent`: + +```ts +renderCellValue(value, column, row, options) +``` + +or, if placed in a new file: + +```ts +export function renderTableCellValue(args): React.ReactNode +``` + +Inputs should include: +- raw `value` +- `column: TableColumn` +- `row: Record` +- `isDesignMode` +- optional `getTranslatedText` + +Use this helper in the basic table body display path. Keep D.3 inline edit +unchanged: if a cell is editing, `renderEditInput(col)` still wins. + +### 2. Entity Display Config + +Apply `column.entityDisplayConfig` before null/empty fallback, because joined +display values can live elsewhere in the row even when `row[col.key]` is empty. + +Support both forms: +- `displayColumns` +- legacy `selectedColumns` + +For each display column, try these row keys in order: +- `${column.key}_${displayColumn}` +- `${column.entityJoinInfo?.sourceColumn}_${displayColumn}` +- `${column.entityDisplayConfig?.joinTable}_${displayColumn}` +- `${column.entityJoinInfo?.joinAlias}_${displayColumn}` +- direct `displayColumn` +- if `displayColumn` contains `.`, also try the last segment + +Join non-empty values using `separator || " - "`. If the result is non-empty, +return it. If not, fall through to normal value formatting. + +Do not add new entity fetches in this phase. Use data already returned in the row. + +### 3. Image Cell + +Render image cells when: +- `column.inputType === "image"` +- or `column.format === "image"` + +Value formats to support: +- numeric objid string +- comma-separated objid/path string +- JSON array of objects/ids if trivial to support +- direct URL or `/uploads/...` path + +Use existing APIs: +- `getFullImageUrl` from `@/lib/api/client` +- `getFilePreviewUrl` from `@/lib/api/file` +- optionally dynamic `getFileInfoByObjid` only to choose representative image for + comma-separated numeric objids. Keep this conservative. + +Rendering: +- compact thumbnail, stable 32-40px size +- click opens preview/new tab and stops row click propagation +- safe loading/error fallback +- clean up blob/object URLs if you create any +- do not start remote image/file lookup in design mode; show a muted placeholder + or direct path preview only. + +Prefer a small `TableCellImage` component in `cell-renderers.tsx` if the logic is +not trivial. + +### 4. File / Attachment Cell + +Render file cells when: +- `column.inputType === "file"` or `"attachment"` +- or `column.format === "file"` or `"attachment"` +- or column key contains `attachment` / `file` case-insensitively + +Value formats to support: +- objid string +- comma-separated objids +- JSON array objects with `objid` / `id` / `realFileName` / `real_file_name` / + `name` / `fileExt` / `file_ext` / `fileSize` / `file_size` + +Use existing APIs: +- dynamic `getFileInfoByObjid` from `@/lib/api/file` +- `getFilePreviewUrl` from `@/lib/api/file` +- dynamic `apiClient` only inside a download handler if you implement download + +Rendering: +- compact icon + filename text +- if multiple files, show count or joined names without forcing row height growth +- click stops row selection and opens preview/list modal or preview URL +- no upload/edit/delete behavior; D.5 is display-only +- no shadcn modal dependency required. If you need a modal, use a minimal inline + overlay like old V2 did, but keep it small. + +Prefer a small `TableCellFile` component in `cell-renderers.tsx` if the logic is +not trivial. + +### 5. Number / Date / Boolean Formatting + +Implement display formatting for basic cells: +- `inputType === "number" | "decimal"` or `format === "number"`: + - if `column.thousandSeparator !== false`, use central formatting from + `@/lib/formatting` if available, otherwise `toLocaleString("ko-KR")`. + - if disabled, show numeric string without separators. +- `format === "currency"`: + - use central currency formatting if available, otherwise `₩` + localized number. +- `inputType === "date" | "datetime"` or `format === "date"`: + - render `YYYY-MM-DD` for valid values. +- `format === "boolean"` or `inputType === "checkbox"`: + - render `예` / `아니오` for boolean-like values. + +Do not change sort/filter semantics. This is display only. + +### 6. Column Label Translation + +Add optional `langKey?: string` and `langKeyId?: number` to `TableColumn` if not +present. + +Use `useScreenMultiLang()` in `TableComponent` to translate header labels: + +```ts +const { getTranslatedText } = useScreenMultiLang(); +const label = getTranslatedText(col.langKey, col.label); +``` + +Apply the translated label in: +- basic table header +- `tableDisplayStore.setTableDataForComponent` `column_labels` if that data is + already being synced + +Do not edit `ScreenMultiLangContext.tsx`; it already collects `componentConfig.columns[].langKey`. + +### 7. Keep Existing Phase Behavior + +Do not regress: +- D.1 visible/hidden/fixed/sticky column rendering +- D.2 filter runtime +- D.3 inline edit entry/save/cancel +- D.4 row action column and bulk action bar + +Action cells and checkbox cells should not use the special cell renderer. + +## Validation + +Run: + +```bash +git diff --check +cd frontend && npx tsc --noEmit --pretty false 2>&1 | rg "^lib/registry/components/table/(TableComponent|types|cell-renderers)|^lib/fieldConfig/adapters" +rg -n "TableCellImage|TableCellFile|renderTableCellValue|entityDisplayConfig|thousandSeparator|useScreenMultiLang|langKey" frontend/lib/registry/components/table +``` + +Expected: +- `git diff --check` prints nothing. +- targeted `tsc | rg ...` prints nothing. +- grep shows canonical table files only, plus old `_shared` references if your + grep includes `_shared`. + +Known unrelated baseline errors may still appear if you run full `tsc` without a +targeted grep: +- `components/screen/filters/AdvancedSearchFilters.tsx` import of + `@/types/screen-legacy-backup` +- `lib/registry/components/repeater-field-group/RepeaterFieldGroupRenderer.tsx` + `DataReceivable` import from `ScreenContext` + +## Final Report + +Report: +- changed files +- whether a new `cell-renderers.tsx` was created +- exact cell types now handled +- entity display fallback key order +- whether `langKey` translation was wired +- validation command results +- any intentionally deferred items diff --git a/notes/gbpark/2026-05-20-table-canonical-phase-d6-claude-prompt.md b/notes/gbpark/2026-05-20-table-canonical-phase-d6-claude-prompt.md new file mode 100644 index 00000000..538e8d63 --- /dev/null +++ b/notes/gbpark/2026-05-20-table-canonical-phase-d6-claude-prompt.md @@ -0,0 +1,305 @@ +# Table Canonical Cleanup — Phase D.6 Claude Prompt + +You are working in `/Users/gbpark/invyone`. + +Reference: +- `notes/gbpark/2026-05-20-table-canonical-cleanup-plan.md` §8 Phase D.6 +- Prior completed phases: B.1/B.4, C.1/C.2/C.3/C.4/C.5, D.1, D.2, D.3, D.4, D.5 +- C.5 already added `tableStyle`, `toolbar`, `defaultSort`, `refreshInterval`, and + expanded `pagination`. +- D.5 added `cell-renderers.tsx` and Codex follow-up fixes already present: + - `profile`-like keys are no longer treated as file columns just because they contain `file`. + - file cells reset loading state when value changes. + - translated column labels are memoized and synced into `tableDisplayStore`. + +## Goal + +Implement Phase D.6 runtime wiring for canonical `TableComponent.tsx`: +- toolbar buttons (`showEditMode`, `showExcel`, `showPdf`, `showCopy`, + `showSearch`, `showFilter`, `showRefresh`) +- footer pagination controls (`showSizeSelector`, `showPageInfo`, + `pageSizeOptions`, `showPaginationRefresh`, `position`) +- lazy XLSX export +- clipboard copy +- conservative paste handling +- basic runtime styling for `tableStyle.theme`, `headerStyle`, `borderStyle` + +Keep this as canonical runtime wiring. Do not expand ConfigPanel. + +## File Scope + +Allowed to edit: +- `frontend/lib/registry/components/table/TableComponent.tsx` + +Allowed only if absolutely required for a tiny type fix: +- `frontend/lib/registry/components/table/types.ts` +- `frontend/lib/registry/components/table/cell-renderers.tsx` + +Read-only references: +- `frontend/lib/registry/components/table/_shared/TableListComponent.tsx` + - `exportToExcel` near 2618-2715 + - copy helpers near 3161-3184 and 6554-6568 + - toolbar/footer rendering near 5087-5520 +- `frontend/lib/registry/components/table/_shared/V2TableListComponent.tsx` + - `exportToExcel` near 2972-3042 + - copy helpers near 3466-3489 and 6930-6944 + - toolbar/footer rendering near 5503-5865 +- `frontend/lib/registry/components/table/TableComponent.tsx` +- `frontend/stores/tableDisplayStore.ts` + +## Forbidden + +- Do not modify: + - `_shared/TableListComponent.tsx` + - `_shared/V2TableListComponent.tsx` + - `_shared/V2TableListContainerWrapper.tsx` + - `_shared/SingleTableWithSticky.tsx` +- Do not remove early legacy/v2 delegation in canonical `TableComponent`. +- Do not implement D.7 card-mode full parity. +- Do not implement D.8 GroupSum / table options advanced summaries. +- Do not implement schema/alias/V2List/split-panel cleanup. +- Do not add static `import * as XLSX from "xlsx"` at module top. Use dynamic + `await import("xlsx")` inside the export handler. +- Do not add PDF libraries. PDF button should use a conservative fallback + (`window.print()` or `tablePdfExport` CustomEvent) unless a local existing helper + is already available without adding dependencies. +- Do not add toast dependencies. +- Do not touch unrelated dirty files: + - `frontend/components/layout/TabBar.tsx` + - `frontend/styles/v5-layout.css` + - `frontend/app/(main)/admin/systemMng/numberingRuleList/page.tsx` + - `notes/gbpark/2026-05-20-control-ide-refactor.md` + - `notes/gbpark/2026-05-20-numbering-rule-*.html` + +## Current Relevant State + +`TableComponent.tsx` currently has: +- `renderToolbar()` with refresh and disabled Excel only. +- `renderFooter()` with simple prev/page/next only. +- `renderColumns` from D.1 for visible ordered columns. +- `rows` for current page/display data. +- `selectedRows: Set` where values are current `rows` indexes. +- `tableData.refresh()`, `tableData.setPage()`, `tableData.totalPages`. +- D.2 `AdvancedSearchFilters` always renders when `filter.enabled`. +- D.3 inline editing. +- D.4 row/bulk actions. +- D.5 `renderTableCellValue()` for display cells. + +Important: preserve all previous behavior. Toolbar controls should not break +filtering, inline edit, row actions, or sticky columns. + +## Implementation Requirements + +### 1. Toolbar Visibility Aliases + +Normalize toolbar flags near the existing `_showRefreshBtn` / `_showExcelBtn`: + +```ts +const toolbar = componentConfig.toolbar ?? {}; +const _showRefreshBtn = toolbar.showRefresh ?? componentConfig.showRefresh; +const _showExcelBtn = toolbar.showExcel ?? componentConfig.showExcel; +const _showEditModeBtn = toolbar.showEditMode === true; +const _showPdfBtn = toolbar.showPdf === true; +const _showCopyBtn = toolbar.showCopy === true; +const _showSearchBtn = toolbar.showSearch === true; +const _showFilterBtn = toolbar.showFilter === true; +const _showPaginationRefreshBtn = toolbar.showPaginationRefresh === true; +``` + +Use existing inline style patterns and lucide icons if already imported. Prefer +icon buttons with `title`. + +### 2. Edit Mode Toggle + +Add local state: + +```ts +const [editModeEnabled, setEditModeEnabled] = useState(false); +``` + +When `toolbar.showEditMode` is true: +- render an edit-mode toggle button. +- `canEditCell()` should require `editModeEnabled`. + +When `toolbar.showEditMode` is false or undefined: +- preserve D.3 behavior: editable cells can still enter edit mode by double-click. + +Do not implement batch-edit pending changes in D.6. + +### 3. XLSX Export + +Implement `handleExportExcel()` with dynamic import: + +```ts +const XLSX = await import("xlsx"); +``` + +Export rows: +- if `selectedRows.size > 0`, export selected visible rows. +- otherwise export current visible `rows`. + +Columns: +- use `renderColumns` only. +- header labels should use translated `getColumnLabel(col)`. +- values should be raw values from `row[col.key]`, but if + `entityDisplayConfig` would display a joined value and you can reuse a pure + helper safely, use display value. Do not mount React cell renderers into XLSX. + +File name: +- `${tableName || "table"}_YYYY-MM-DD.xlsx` + +Bundle: +- no static XLSX import. +- handler should fail gracefully with `console.error`. + +### 4. Clipboard Copy + +Implement `handleCopyTable()`: +- if selected rows exist, copy selected visible rows. +- otherwise copy current visible `rows`. +- format as TSV: header row + data rows. +- use translated column labels for headers. +- use raw text-safe values, not React nodes. +- call `navigator.clipboard.writeText(tsv)` if available. +- fallback to a temporary `