From c62630e22de1561b1be066d9c6cae8aafa53003f Mon Sep 17 00:00:00 2001 From: root Date: Mon, 2 Nov 2026 13:11:47 +0900 Subject: [PATCH] feat(ui): apply inquiry list and modal design Apply the supplied inquiry layout to the list and native write/detail dialogs while preserving existing API requests, author controls, attachments, comments and search. Keep legacy entry links and confirm before discarding drafts. Validation: 98 tests and syntax checks pass; mocked Chromium desktop/mobile and six author/session visibility scenarios pass. --- README.md | 12 +- assets/inquiry/egbim-symbol.svg | 4 + assets/inquiry/egbim.svg | 25 ++ assets/inquiry/entry.js | 8 + assets/inquiry/gaia.svg | 11 + assets/inquiry/inquiry-hero.webp | Bin 0 -> 142176 bytes assets/inquiry/inquiry.css | 234 ++++++++++++++++ assets/inquiry/tova.svg | 24 ++ docs/qa-inquiry-ui-redesign-tasks.md | 257 ++++++++++++++++++ docs/qa-login-to-save-transition.md | 210 +++++---------- egbim/app.js | 381 +++++++++++++++++++++------ egbim/detail.html | 9 +- egbim/index.html | 235 +++++++++++++++-- egbim/write.html | 9 +- package.json | 3 +- scripts/upload-r2.sh | 1 + tests/browser/inquiry-ui.mjs | 185 +++++++++++++ tests/page-entry.test.js | 22 ++ tests/production-deployment.test.js | 9 +- 19 files changed, 1362 insertions(+), 277 deletions(-) create mode 100644 assets/inquiry/egbim-symbol.svg create mode 100644 assets/inquiry/egbim.svg create mode 100644 assets/inquiry/entry.js create mode 100644 assets/inquiry/gaia.svg create mode 100644 assets/inquiry/inquiry-hero.webp create mode 100644 assets/inquiry/inquiry.css create mode 100644 assets/inquiry/tova.svg create mode 100644 docs/qa-inquiry-ui-redesign-tasks.md create mode 100644 tests/browser/inquiry-ui.mjs create mode 100644 tests/page-entry.test.js diff --git a/README.md b/README.md index 2639632..f2beed1 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,8 @@ # EG-BIM Q&A static pages -`egbim/` 아래의 `index.html`, `write.html`, `detail.html` 세 페이지로 구성한 EG-BIM Q&A UI입니다. 원본 `egbim_homepage`의 Q&A 화면 구성과 패키지 S/W 메뉴 방향을 참고해, PHP/그누보드/DB 의존성 없이 정적 호스팅에서 동작하도록 분리했습니다. +`egbim/index.html`의 목록 위에 작성·상세 모달을 여는 EG-BIM Q&A UI입니다. `SW문의_소스패키지`의 히어로·제품 탭·목록·모달 디자인을 적용하고, 기존 첨부·댓글·비밀글·작성자 권한·검색을 유지합니다. 회사 홈페이지의 헤더·메뉴·푸터는 포함하지 않습니다. 기존 `write.html`, `detail.html?id=...` 주소는 같은 목록의 모달로 연결됩니다. + +EG-BIM을 우선 제공하며 TOVA·GAIA는 준비중 탭입니다. 원격지원은 `https://939.co.kr/saman/`으로 열립니다. UI 적용 범위·검증·남은 배포 확인은 [작업 목록](docs/qa-inquiry-ui-redesign-tasks.md)에 정리했습니다. 로그인부터 글 저장까지의 화면·처리 전이맵은 [`docs/qa-login-to-save-transition.md`](docs/qa-login-to-save-transition.md)에 정리했습니다. @@ -12,7 +14,7 @@ python3 -m http.server 4173 ``` -그 다음 `http://localhost:4173/egbim/`에 접속합니다. 브라우저 보안 정책 때문에 `file://` 직접 열기보다 정적 서버를 사용하는 편이 안전합니다. +그 다음 `http://localhost:4173/egbim/`에 접속합니다. 정적 서버는 화면 배치 확인용이며 로그인과 저장 API를 제공하지 않습니다. 브라우저 보안 정책 때문에 `file://` 직접 열기보다 정적 서버를 사용하는 편이 안전합니다. 실제 서버 데이터를 변경하지 않는 모달 회귀 검증은 별도 Playwright 환경에서 `npm run test:ui`로 실행할 수 있습니다. ## feedback.hmac.kr DB 기준 연동 @@ -24,7 +26,7 @@ python3 -m http.server 4173 - 신규 상태: `support_tickets.status_code = RECEIVED`, `feedback_status = NEW` - 첨부 저장소: `storage_bucket = qa_cdn` -작성 시 브라우저가 UUID를 하나 생성합니다. 같은 UUID를 `feedback.id`, `feedback.source_record_id`, `support_tickets.idempotency_key`에 사용하므로 중복 제출을 판별할 수 있습니다. API 서버는 envelope를 받아 다음 DB 레코드를 하나의 트랜잭션으로 생성해야 합니다. +작성 시 브라우저가 기존 UUID v7 방식으로 `feedbackId`를 생성하고 같은 Worker의 `POST /api/feedbacks`에 전달합니다. API 서버의 저장 구조와 작성자·조직 매핑은 이번 UI 변경 범위에 포함하지 않습니다. 기존 저장 대상은 다음과 같습니다. - `feedbacks`: `id`, `channel_id`, `source_namespace`, `source_record_id`, `data` - `support_tickets`: 작성자/테넌트/제목/내용/분류/상태/`idempotency_key` @@ -42,7 +44,7 @@ python3 -m http.server 4173 ## 연동 지점 - SSO: Q&A는 홈페이지 로그인 정보를 전달받지 않고, `https://sso.hmac.kr/oidc`에 등록된 독립적인 PKCE Public Client로 직접 인증합니다. callback은 `https://qa-test.baroncs.co.kr/auth/callback`이며, Worker가 검증한 claim을 버전이 지정된 HttpOnly 세션 쿠키에 저장합니다. 브라우저의 `GET /auth/session`은 Q&A 세션에 저장된 작성자 정보를 반환합니다. feedback 서버는 반드시 SSO 세션 또는 토큰을 서버 측에서 검증해야 합니다. -- 작성자 식별자: `ssoSubject`/`requesterId`, `userUuid`, `tenantId`/`requesterTenantId`, `tenantIds`, `scope`, `roles`, 이메일·이름·부서·전화번호를 payload에 넣습니다. `requester_id`와 `requester_tenant_id`가 없으면 제출을 차단합니다. +- 작성자 식별자: Worker가 검증한 세션의 `requesterId`와 `requesterTenantId`를 사용합니다. 브라우저는 회사·부서·이름·이메일을 읽기 전용으로 표시하고, 필수 UUID가 없으면 제출을 차단합니다. 서버가 요청의 작성자·조직 정보를 결정합니다. - API: 작성페이지는 같은 Worker의 `POST /api/feedbacks`를 호출합니다. Worker가 Q&A 세션 쿠키를 검증하고, SSO requester 정보와 `ABC_API_KEY` Secret을 추가한 뒤 `POST https://feedback.hmac.kr/api/projects/{projectId}/channels/{channelId}/feedbacks`로 전달합니다. API Key와 내부 ABC 주소는 브라우저에 노출하지 않습니다. - 첨부파일: 작성페이지가 `multipart/form-data`의 반복 `images` 필드로 파일을 Worker에 전달하고, Worker가 검증된 requester 정보와 API Key를 추가해 ABC API로 전달합니다. ABC API가 `qa_cdn` 저장소에 파일을 저장하므로 R2 access key/secret은 정적 페이지에 넣지 않습니다. - 텍스트만 등록할 때는 기존 JSON 요청을 사용하고, 파일이 있을 때만 multipart 요청을 사용합니다. @@ -99,7 +101,7 @@ Cloudflare API Token에는 최소한 다음 권한이 필요합니다. `.gitea/workflows/deploy.yml`은 `main` push 또는 수동 실행 시 세션 secret을 Worker에 등록하고 R2 업로드 후 `baron-qa-gateway-test`를 배포합니다. 실제 secret 값은 로그에 출력하지 않습니다. -현재 화면은 API 설정 전에도 QA 흐름을 확인할 수 있도록 샘플 글과 로컬 테스트 저장을 포함합니다. 운영 배포 시에는 Gitea Secret `ABC_API_KEY`가 Worker에 등록되어야 하며, 작성페이지는 Worker 프록시를 통해 ABC API를 호출합니다. +화면에는 디자인 목업의 예시 글·공지를 넣지 않습니다. 브라우저는 실제 Worker API를 호출하며 기존 localStorage는 조회·저장 결과의 캐시로 유지합니다. API 설정 전 로컬 저장 성공으로 대체하지 않습니다. 운영은 [production 파이프라인 안내](docs/qa-production-pipeline.md)의 `PROD_` 등록값과 기존 수동 배포 절차를 사용합니다. ## 회사·부서 조직 연동 (스테이징) diff --git a/assets/inquiry/egbim-symbol.svg b/assets/inquiry/egbim-symbol.svg new file mode 100644 index 0000000..07262a8 --- /dev/null +++ b/assets/inquiry/egbim-symbol.svg @@ -0,0 +1,4 @@ + + + + diff --git a/assets/inquiry/egbim.svg b/assets/inquiry/egbim.svg new file mode 100644 index 0000000..71fded2 --- /dev/null +++ b/assets/inquiry/egbim.svg @@ -0,0 +1,25 @@ + + + + + + + + + + + + + diff --git a/assets/inquiry/entry.js b/assets/inquiry/entry.js new file mode 100644 index 0000000..a3e19e3 --- /dev/null +++ b/assets/inquiry/entry.js @@ -0,0 +1,8 @@ +(function () { + const url = new URL(window.location.href); + const view = url.pathname.endsWith('/write.html') ? 'write' : 'detail'; + url.pathname = url.pathname.replace(/[^/]+$/, ''); + url.searchParams.set('view', view); + if (view === 'write') url.searchParams.delete('id'); + window.location.replace(url.toString()); +})(); diff --git a/assets/inquiry/gaia.svg b/assets/inquiry/gaia.svg new file mode 100644 index 0000000..e1d2412 --- /dev/null +++ b/assets/inquiry/gaia.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/assets/inquiry/inquiry-hero.webp b/assets/inquiry/inquiry-hero.webp new file mode 100644 index 0000000000000000000000000000000000000000..2ffe7c7fe34242a2d227f95c104db647e32ce04c GIT binary patch literal 142176 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%0AV`wVBt^z0t{3~6Ut%ZAk1OjgOE5GM{s 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

F`nG4Yh^e(8&6=H)SSOHV0ZDBdo;YF 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 ZkKZ*EgbP*}>y!bPAL+{20xwno003te(-!~$ literal 0 HcmV?d00001 diff --git a/assets/inquiry/inquiry.css b/assets/inquiry/inquiry.css new file mode 100644 index 0000000..c684ca4 --- /dev/null +++ b/assets/inquiry/inquiry.css @@ -0,0 +1,234 @@ +.inquiry-page { --inquiry-blue: #164b91; --inquiry-dark: #26374f; --inquiry-soft: #edf2f6; padding-top: 0; color: #202a38; } +.inquiry-page *, .inquiry-dialog * { box-sizing: border-box; } +.inquiry-page button, .inquiry-dialog button { font: inherit; } +.inquiry-sr-only { position: absolute !important; width: 1px !important; height: 1px !important; padding: 0 !important; overflow: hidden !important; clip-path: inset(50%); white-space: nowrap; } +.inquiry-page :focus-visible, .inquiry-dialog :focus-visible { outline: 2px solid #0c367f; outline-offset: 3px; } +.inquiry-page input, .inquiry-dialog input, .inquiry-dialog textarea { user-select: text; } +.inquiry-hero { --inquiry-hero-shade: transparent; height: 480px; position: relative; background: #0b2743 linear-gradient(var(--inquiry-hero-shade), var(--inquiry-hero-shade)) center / cover no-repeat; background-image: linear-gradient(var(--inquiry-hero-shade), var(--inquiry-hero-shade)), url(./inquiry-hero.webp); } +.inquiry-hero__copy { height: 100%; display: flex; flex-direction: column; align-items: center; justify-content: center; color: #fff; text-align: center; padding: 40px 24px; } +.inquiry-hero h1 { font-size: 70px; font-weight: 700; line-height: 1.4; letter-spacing: -0.04em; } +.inquiry-hero p { font-size: 22px; line-height: 1.6; } +.inquiry-workspace { position: relative; z-index: 1; width: min(1248px, 100%); padding: 0 24px; margin: -98px auto 120px; } +.inquiry-tabs-shell { --inquiry-tab-curve: 28px; padding: 19px 32px 0; border-radius: 48px 48px 0 0; background: linear-gradient(180deg, #174b64a6, #082f43d9); -webkit-backdrop-filter: blur(10px); backdrop-filter: blur(10px); box-shadow: inset 0 1px 0 #74c7f4b3, inset 0 0 0 1px #5fadd666, inset 0 8px 16px #ffffff12, 0 -18px 12px #0a2f4843; } +.inquiry-tabs { display: flex; gap: 60px; position: relative; isolation: isolate; } +.inquiry-tabs button { position: relative; flex: 1; min-width: 0; min-height: 76px; padding: 8px 12px 10px; display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 4px; border: 0; border-radius: 34px 34px 0 0; color: #b9d4e0a6; background: transparent; cursor: pointer; transition: background-color 0.2s ease, color 0.2s ease; } +.inquiry-tabs button:not([aria-selected="true"]):hover { color: #d6f0ff; background: #ffffff12; } +.inquiry-tabs [aria-selected="true"] { z-index: 1; color: #00a2ef; background: linear-gradient(180deg, #e8f8ff 0%, #fff 58%); box-shadow: inset 0 2px 0 #ffffffb3, 0 -3px 10px #163f5b08; } +/* Concave lower corners join the selected tab to the white board beneath it. */ +.inquiry-tabs [aria-selected="true"]::before, +.inquiry-tabs [aria-selected="true"]::after { content: ""; position: absolute; bottom: 0; width: var(--inquiry-tab-curve); height: var(--inquiry-tab-curve); pointer-events: none; } +.inquiry-tabs [aria-selected="true"]::before { left: calc(-1 * var(--inquiry-tab-curve)); background: radial-gradient(circle at 0 0, transparent calc(var(--inquiry-tab-curve) - 0.5px), #fff var(--inquiry-tab-curve)); } +.inquiry-tabs [aria-selected="true"]::after { right: calc(-1 * var(--inquiry-tab-curve)); background: radial-gradient(circle at 100% 0, transparent calc(var(--inquiry-tab-curve) - 0.5px), #fff var(--inquiry-tab-curve)); } +.inquiry-tab-logo { width: 156px; max-width: 94%; height: 33px; display: flex; align-items: center; justify-content: center; } +.inquiry-tab-logo img { display: block; max-width: 100%; max-height: 33px; object-fit: contain; opacity: 0.48; filter: brightness(0) invert(1); transition: opacity 0.2s ease; } +.inquiry-tabs [aria-selected="true"] .inquiry-tab-logo img { opacity: 1; filter: none; } +.inquiry-tabs button:not([aria-selected="true"]):hover .inquiry-tab-logo img { opacity: 0.8; } +.inquiry-tab-description { font-size: 16px; line-height: 1.5; font-weight: 500; letter-spacing: -0.02em; white-space: nowrap; } +.inquiry-board { padding: 72px 6px 0; background: #fff; } +.inquiry-board__heading { display: flex; justify-content: space-between; align-items: center; gap: 20px; margin-bottom: 28px; } +.inquiry-board h2 { font-size: 24px; line-height: 1.5; font-weight: 600; text-align: left; word-break: keep-all; } +.inquiry-remote { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 44px; padding: 0 18px; border: 1px solid #c8d9ef; border-radius: 999px; color: var(--inquiry-blue); background: #fff; font-size: 14px; font-weight: 600; line-height: 1.4; white-space: nowrap; transition: background-color 0.2s ease; } +.inquiry-remote:hover { background: #f0f5fc; } +.inquiry-remote svg { width: 20px; height: 20px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 1.7; stroke-linecap: round; stroke-linejoin: round; } +.inquiry-controls { display: flex; align-items: center; justify-content: space-between; gap: 24px; padding-bottom: 28px; } +.inquiry-filters { display: flex; align-items: center; gap: 24px; flex-shrink: 0; } +.inquiry-categories { display: flex; gap: 6px; border: 0; } +.inquiry-categories label { position: relative; cursor: pointer; } +.inquiry-categories input { position: absolute; width: 1px; height: 1px; opacity: 0; } +.inquiry-categories span { display: flex; align-items: center; justify-content: center; height: 44px; min-width: 60px; padding: 0 16px; border-radius: 999px; color: #758094; font-size: 15px; } +.inquiry-categories label:hover span { background: var(--inquiry-soft); } +.inquiry-categories input:checked + span { color: var(--inquiry-blue); background: #edf3fd; font-weight: 700; } +.inquiry-categories input:focus-visible + span { outline: 2px solid #0c367f; outline-offset: 3px; } +.inquiry-mine { display: inline-flex; align-items: center; gap: 8px; color: #697386; font-size: 13px; white-space: nowrap; cursor: pointer; } +.inquiry-mine input { appearance: auto; width: 16px; height: 16px; accent-color: var(--inquiry-blue); } +.inquiry-search { width: min(360px, 100%); display: flex; align-items: center; gap: 8px; padding: 0 6px 0 20px; border: 1px solid #e6e9ed; border-radius: 999px; background: #f7f8fa; } +.inquiry-search input { flex: 1; min-width: 0; width: 100%; height: 44px; border: 0; padding: 0; color: #273448; font-size: 14px; background: transparent; } +.inquiry-search input::placeholder { color: #9aa2b0; } +.inquiry-search:focus-within { outline: 2px solid #528bdc; outline-offset: 2px; } +.inquiry-search input:focus { outline: none; } +.inquiry-search-button { display: flex; align-items: center; justify-content: center; flex-shrink: 0; width: 36px; height: 36px; border: 0; border-radius: 50%; color: #35445b; background: transparent; cursor: pointer; } +.inquiry-search-button:hover { background: #e9eef6; } +.inquiry-search-button svg { width: 20px; height: 20px; fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; } +.inquiry-button { display: inline-flex; align-items: center; justify-content: center; gap: 8px; min-height: 45px; padding: 0 20px; border: 1px solid transparent; border-radius: 3px; background: #27241d; color: #fff; font-size: 16px !important; font-weight: 700 !important; white-space: nowrap; cursor: pointer; } +.inquiry-button:hover { background: #454035; } +.inquiry-button svg { width: 18px; height: 18px; flex-shrink: 0; fill: none; stroke: currentColor; stroke-width: 2; stroke-linecap: round; stroke-linejoin: round; } +.inquiry-table-scroll { overflow-x: auto; scrollbar-width: thin; } +.inquiry-scroll-hint { display: none; } +.inquiry-table { table-layout: fixed; min-width: 1024px; width: 100%; font-size: 16px; } +.inquiry-table .col-number { width: 5%; } +.inquiry-table .col-category { width: 8%; } +.inquiry-table .col-company, .inquiry-table .col-team { width: 11%; } +.inquiry-table .col-author { width: 8%; } +.inquiry-table .col-date { width: 10%; } +.inquiry-table .col-status { width: 9%; } +.inquiry-table th { height: 48px; color: #657083; border-top: 1.5px solid var(--inquiry-dark); border-bottom: 1px solid #e5e9ef; font-weight: 500; text-align: center; white-space: nowrap; } +.inquiry-table td { height: 60px; padding: 0 10px; text-align: center; color: #555; border-bottom: 1px solid #eee; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.inquiry-table .inquiry-notice { background: #f6f8fc; font-weight: 500; } +.inquiry-notice .inquiry-subject { color: var(--inquiry-blue); } +.inquiry-table tbody tr:hover { background: #fafafa; } +.inquiry-table .inquiry-subject { text-align: left; } +.inquiry-subject button { display: flex; align-items: center; gap: 6px; width: 100%; color: inherit; text-align: left; } +.inquiry-subject button:hover { color: #08734c; } +.inquiry-subject button > span:first-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.inquiry-comments { display: inline-flex; align-items: center; gap: 2px; color: #008cff; font-size: 14px; font-weight: 700; flex-shrink: 0; } +.inquiry-comments svg { width: 15px; height: 15px; fill: currentColor; } +.inquiry-status { display: inline-block; padding: 6px 10px; border-radius: 4px; font-size: 14px; line-height: 1.5; font-weight: 600; } +.inquiry-status--new { color: #ef4444; background: #ef44441a; } +.inquiry-status--done { color: #38b000; background: #38b00014; } +.inquiry-status--review, .inquiry-status--patch { color: #777; background: #f0f0f0; } +.inquiry-table .inquiry-empty { height: 280px; white-space: normal; } +.inquiry-pagination { display: flex; justify-content: center; align-items: center; gap: 12px; margin: 30px 0; } +.inquiry-pagination button { display: flex; align-items: center; justify-content: center; width: 32px; height: 32px; border-radius: 50%; font-size: 14px; color: #555; } +.inquiry-pagination [aria-current="page"] { background: #edf3fd; color: var(--inquiry-blue); } +.inquiry-pagination button:disabled { opacity: 0.3; cursor: default; } +.inquiry-pagination button:not(:disabled):hover { background: #eee; color: #111; } +.inquiry-board__bottom { display: flex; align-items: center; justify-content: space-between; gap: 16px; } +.inquiry-preview-note { color: #999; font-size: 13px; font-weight: 400; line-height: 1.6; } +.inquiry-write { min-width: 120px; border-radius: 999px; } + +.inquiry-dialog { position: fixed; inset: 0; width: min(780px, calc(100% - 40px)); max-height: 88dvh; overflow: auto; margin: auto; border: 0; border-radius: 12px; padding: 32px; color: #222; background: #fff; box-shadow: 0 16px 60px #0004; } +.inquiry-dialog::backdrop { background: #17212b99; } +.inquiry-dialog__heading { display: flex; justify-content: space-between; align-items: center; gap: 20px; border-bottom: 2px solid #27241d; padding-bottom: 20px; margin-bottom: 20px; } +.inquiry-dialog h2 { font-size: 24px; font-weight: 700; line-height: 1.5; word-break: keep-all; } +.inquiry-close { font-size: 32px !important; line-height: 1; width: 36px; height: 36px; flex-shrink: 0; } +.inquiry-detail__meta { color: #777; font-size: 14px; line-height: 1.8; } +.inquiry-detail__body { min-height: 180px; padding: 28px 0; line-height: 1.8; white-space: pre-line; user-select: text; } +.inquiry-dialog__actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 24px; } +.inquiry-button--light { color: #333; background: #eee; } +.inquiry-button--light:hover { background: #ddd; } +.inquiry-form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 20px; margin: 24px 0; } +.inquiry-form-grid label, .inquiry-form-wide { display: flex; flex-direction: column; gap: 8px; font-size: 14px; font-weight: 600; } +.inquiry-form-wide { margin-top: 20px; } +.inquiry-dialog input, .inquiry-dialog select, .inquiry-dialog textarea { width: 100%; border: 1px solid #ddd; border-radius: 3px; padding: 10px 12px; min-height: 44px; font-size: 14px; background: #fff; font-weight: 400; } +.inquiry-dialog textarea { resize: vertical; } +.inquiry-dialog input:focus, .inquiry-dialog select:focus, .inquiry-dialog textarea:focus { border: 1px solid #1d8467; outline: 2px solid #1d846733; } +.inquiry-form-feedback { font-size: 14px; color: #08734c; margin-top: 16px; } +body:has(.inquiry-dialog[open]) { overflow: hidden; } +/* Lock background scrolling only while a dialog is open; !important is needed to override layout.css html/body { overflow: visible !important }. */ +html.inquiry-modal-open, +html.inquiry-modal-open body { overflow: hidden !important; } +@media (max-width: 1000px) { + .inquiry-scroll-hint { display: block; margin: 0 0 12px; text-align: right; color: #888; font-size: 12px; } + .inquiry-controls { flex-wrap: wrap; gap: 20px; } + .inquiry-search { width: 100%; max-width: none; } +} +@media (max-width: 600px) { + .inquiry-hero { --inquiry-hero-shade: rgba(5, 24, 44, 0.38); height: 340px; background-position: 22% center; } + .inquiry-hero h1 { font-size: 48px; } + .inquiry-hero p { font-size: 17px; } + .inquiry-workspace { margin: -96px auto 72px; padding: 0 16px; } + .inquiry-tabs-shell { --inquiry-tab-curve: 13px; padding: 12px 13px 0; border-radius: 30px 30px 0 0; } + .inquiry-tabs { gap: 5px; } + .inquiry-tabs button { min-height: 84px; padding: 8px 4px 12px; gap: 4px; border-radius: 22px 22px 0 0; } + .inquiry-tab-logo { width: 145px; max-width: 100%; height: 28px; } + .inquiry-tab-logo img { height: auto; max-height: 28px; } + .inquiry-tabs [data-service="egbim"] img { width: 49px; height: 25px; } + .inquiry-tabs [data-service="tova"] img { width: 80px; } + .inquiry-tabs [data-service="gaia"] img { width: 58px; } + .inquiry-tab-description { display: flex; align-items: center; justify-content: center; min-height: 29px; font-size: 11px; line-height: 1.3; white-space: normal; word-break: keep-all; } + .inquiry-board { padding: 24px 4px 0; } + .inquiry-board__heading { flex-wrap: nowrap; gap: 12px; margin-bottom: 22px; } + .inquiry-board h2 { flex: 1 1 auto; font-size: 20px; } + .inquiry-board__heading .inquiry-remote { flex-shrink: 0; margin-left: auto; } + .inquiry-remote { gap: 6px; min-height: 40px; padding: 0 12px; font-size: 12px; } + .inquiry-remote svg { width: 18px; height: 18px; } + .inquiry-filters { width: 100%; flex-wrap: wrap; gap: 16px; } + .inquiry-categories { width: 100%; gap: 6px; } + .inquiry-categories label { flex: 1; } + .inquiry-categories span { min-width: 0; padding: 0 12px; height: 44px; font-size: 14px; } + .inquiry-controls { padding-bottom: 24px; } + .inquiry-search input { font-size: 16px; } + .inquiry-search-button { width: 40px; height: 40px; } + .inquiry-board__bottom { align-items: flex-end; } + .inquiry-preview-note { font-size: 12px; } + .inquiry-dialog { padding: 24px 20px; width: calc(100% - 24px); } + .inquiry-dialog h2 { font-size: 20px; } + .inquiry-form-grid { grid-template-columns: 1fr; gap: 16px; } +} +@media (prefers-reduced-motion: reduce) { + .inquiry-tabs button, .inquiry-tab-logo img, .inquiry-remote { transition: none; } +} + +/* Q&A uses its existing authentication, forms and data inside the reference layout. */ +.inquiry-page { overflow-x: clip; } +.inquiry-page h1, .inquiry-page h2, .inquiry-page p, .inquiry-dialog h2, .inquiry-dialog h3 { margin: 0; } +.inquiry-page fieldset { margin: 0; padding: 0; } +.inquiry-table { border-collapse: collapse; } +.inquiry-table tbody tr { cursor: pointer; } +.inquiry-tabs button:disabled { cursor: default; } +.inquiry-tabs button:disabled:hover { background: transparent; color: #b9d4e0a6; } +.inquiry-tabs button:disabled:hover img { opacity: .48; } +.inquiry-coming-soon { margin-left: 5px; font-size: 11px; font-weight: 400; } +.inquiry-page .help-text { margin: 0 0 12px; } +.inquiry-board__bottom .help-text { margin: 0; } +.inquiry-table .subject-link { display: flex; align-items: center; gap: 5px; min-width: 0; color: inherit; } +.inquiry-table .subject-link > span:last-child { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } +.inquiry-table .subject-link:hover { color: #08734c; } +.inquiry-table .col-date { width: 14%; } +.inquiry-table .col-status { width: 8%; } +.inquiry-table .col-author { width: 7%; } +.inquiry-table .lock { flex-shrink: 0; font-size: 12px; } +.inquiry-table .table-empty { min-width: 1024px; } +.inquiry-status--hold { color: #9b6414; background: #fff5df; } +.inquiry-pagination { flex-wrap: wrap; } +.inquiry-pagination button { border: 0; background: transparent; font-family: inherit; cursor: pointer; } +.inquiry-dialog { overscroll-behavior: contain; } +.inquiry-dialog [aria-busy="true"] .detail-card, +.inquiry-dialog [aria-busy="true"] .comment-box { display: none; } +.inquiry-dialog [hidden], .inquiry-page [hidden] { display: none !important; } +.inquiry-dialog .inquiry-dialog__heading { align-items: flex-start; } +.inquiry-dialog .inquiry-dialog__heading h2 { margin: 0; } +.inquiry-close { border: 0; padding: 0; background: transparent; color: #111; cursor: pointer; } +.inquiry-dialog .help-text { margin: 12px 0 0; line-height: 1.7; } +.inquiry-dialog .inquiry-form-grid { margin: 24px 0; } +.inquiry-dialog .inquiry-form-grid label { min-width: 0; } +.inquiry-dialog .inquiry-form-wide { font-weight: 600; } +.inquiry-dialog input[readonly], .inquiry-dialog select:disabled { color: #687282; background: #f7f8fa; } +.inquiry-dialog input[type="checkbox"] { flex: 0 0 auto; width: 17px; height: 17px; min-height: 17px; margin: 0; accent-color: #164b91; } +.inquiry-secret { display: flex; align-items: center; gap: 8px; margin: 16px 0 0; font-size: 14px; } +.inquiry-dialog textarea { min-height: 136px; line-height: 1.8; } +.inquiry-dialog input[type="file"] { padding: 9px 12px; font-size: 13px; } +.inquiry-dialog .form-error { margin: 16px 0 0; padding: 12px 14px; border-radius: 4px; font-size: 14px; } +.inquiry-dialog .detail-card { border-top: 0; padding-bottom: 0; } +.inquiry-dialog .detail-head { padding: 0; border-bottom: 0; } +.inquiry-detail__summary { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; } +.inquiry-dialog .category { padding: 0; min-width: 0; color: #777; background: none; font-size: 13px; } +.inquiry-dialog .status { min-width: 0; padding: 4px 8px; border: 0; border-radius: 4px; font-size: 12px; color: #777; background: #f0f0f0; } +.inquiry-dialog .status[data-status="신규"] { color: #ef4444; background: #ef44441a; } +.inquiry-dialog .status[data-status="완료"] { color: #38b000; background: #38b00014; } +.inquiry-dialog .status[data-status="보류"] { color: #9b6414; background: #fff5df; } +.inquiry-dialog .detail-meta { padding-top: 10px; gap: 6px; font-size: 13px; line-height: 1.8; } +.inquiry-dialog .detail-meta span + span::before { content: '·'; width: auto; height: auto; margin: 0 6px 0 0; background: none; } +.inquiry-dialog .detail-body { min-height: 160px; padding: 28px 0; line-height: 1.8; } +.inquiry-dialog .attachments { padding: 16px 0; background: none; } +.inquiry-dialog .attachments h3 { margin: 0 0 8px; } +.inquiry-dialog .detail-actions { padding: 0; margin: 20px 0 0; } +.inquiry-dialog .button { min-height: 38px; height: auto; padding: 9px 16px; border-radius: 3px; font-size: 13px; } +.inquiry-dialog .button-primary { border-color: #27241d; background: #27241d; } +.inquiry-dialog .button-primary:hover { background: #454035; } +.inquiry-dialog .button:disabled, .inquiry-button:disabled { opacity: .6; cursor: wait; } +.inquiry-dialog .comment-box { margin-top: 32px; } +.inquiry-dialog .comment-box h3 { padding-bottom: 14px; font-size: 16px; } +.inquiry-dialog .comment { padding: 16px 0; } +.inquiry-dialog .comment-form { padding: 18px 0 0; } +.inquiry-dialog .comment-form textarea { min-height: 120px; } +.inquiry-dialog .comment-form-actions { flex-wrap: wrap; } +.inquiry-dialog .comment-edit-input { min-height: 96px; } +.inquiry-dialog .edit-form { padding-top: 8px; } +.inquiry-dialog .is-editing > .detail-card { display: none; } +.inquiry-dialog .edit-form .select { max-width: none; } +.inquiry-dialog .form-row { display: block; min-height: 0; padding: 0; margin: 20px 0 0; border: 0; } +.inquiry-dialog .form-label { display: block; padding: 0; margin-bottom: 8px; background: none; font-size: 14px; font-weight: 600; } +.inquiry-dialog .form-control { padding: 0; } +.inquiry-dialog .form-footer { justify-content: flex-end; padding-top: 24px; } +.inquiry-dialog .completion-notice { margin: 20px 0; font-size: 13px; } +.inquiry-dialog .completion-notice p { margin: 4px 0 0; } +.inquiry-dialog .inquiry-dialog__actions { clear: both; } +@media (max-width: 600px) { + .inquiry-dialog input, .inquiry-dialog select, .inquiry-dialog textarea { font-size: 16px; } + .inquiry-coming-soon { display: block; margin-left: 0; font-size: 9px; } + .inquiry-tabs button:disabled .inquiry-tab-description { flex-direction: column; gap: 2px; } + .inquiry-dialog .comment-meta { gap: 8px; flex-wrap: wrap; } + .inquiry-dialog .completion-notice { flex-direction: column; align-items: stretch; } + .inquiry-dialog .inquiry-dialog__actions { flex-wrap: wrap; } +} diff --git a/assets/inquiry/tova.svg b/assets/inquiry/tova.svg new file mode 100644 index 0000000..22b3ca2 --- /dev/null +++ b/assets/inquiry/tova.svg @@ -0,0 +1,24 @@ + + + + + + + + + + + + diff --git a/docs/qa-inquiry-ui-redesign-tasks.md b/docs/qa-inquiry-ui-redesign-tasks.md new file mode 100644 index 0000000..c6c2e76 --- /dev/null +++ b/docs/qa-inquiry-ui-redesign-tasks.md @@ -0,0 +1,257 @@ +# S/W 문의 UI 개편 작업 목록 + +- 작성일: 2026-11-02 +- 대상: EG-BIM Q&A 목록·문의 작성·문의 상세 +- 디자인 기준: `SW문의_소스패키지`와 사용자가 제공한 목록·상세 모달·작성 모달 스크린샷 3장 +- 현재 단계: 프론트 UI 구현 및 모의 API 회귀 검증 완료. 원격 push·스테이징 실서버 확인·운영 배포는 대기 중. + +## 1. 사용자 선택으로 확정한 범위 + +| 항목 | 확정 내용 | +| --- | --- | +| 작성·상세 표시 | 목록 위에 뜨는 페이지 내부 모달로 전환. 원본의 `

` 방식 기준 | +| 기존 주소 | 기존 작성·상세 주소 연결 유지 | +| 소프트웨어 | EG-BIM 우선 적용. TOVA·GAIA 탭은 준비중 표시 | +| 디자인 적용 범위 | 문의 화면·작성·상세 영역. 홈페이지 헤더·전체 메뉴·푸터는 제외 | +| 입력 중 닫기 | 입력 변경이 있으면 폐기 여부를 확인한 뒤 닫기 | +| 원격지원 | `https://939.co.kr/saman/` 활성화. 원본처럼 새 창으로 열기 | + +이번 문서는 현행 한국어 Q&A를 기준으로 한다. 소스 패키지의 영문 페이지는 별도 확대 범위다. 위 선택 항목은 확정됐으며 다시 선택받을 필요가 없다. + +## 2. 소스 확인 결과 + +| 확인 파일 | 확인한 내용 | 적용 방향 | +| --- | --- | --- | +| `SW문의_소스패키지/ko/inquiry.html` | 목록과 작성·상세 ``가 한 문서에 있음 | 화면 구조·배치 기준 | +| `SW문의_소스패키지/assets/css/inquiry.css` | 이미지 히어로, 곡선 탭, 검색·표·버튼, 반응형, 모달 스타일 | 문의 영역 스타일을 추려 적용 | +| `SW문의_소스패키지/assets/js/inquiry.js` | 예시 게시물·공지 생성, 모달 열기/닫기, 미리보기 등록 안내 | 모달 UX를 참고하고 실제 데이터 처리는 기존 Q&A 로직으로 연결 | +| `SW문의_소스패키지/assets/js/inquiry-copy.js` | 예시 데이터·상태명·KO/EN 미리보기 문구 | 실제 서비스 문구와 상태명으로 정리 | +| `SW문의_소스패키지/assets/img/inquiry-hero.webp` | 스크린샷의 상단 배경 이미지 | 해당 자산 사용 | +| `SW문의_소스패키지/ko/{egbim,tova,gaia}/img/` | 제품 로고와 모바일용 EG-BIM 심볼 | 활성·준비중 탭에 사용 | +| `SW문의_소스패키지/assets/js/common.js`, `baron-sso-*.js`, `_include/` | 홈페이지 메뉴·헤더·푸터와 별도 SSO 흐름에 의존 | Q&A의 기존 Worker SSO 유지. 필요한 디자인 자산만 반영 | +| `egbim/app.js`, `src/index.js` | 실제 API·SSO·첨부·댓글·검색·작성자 버튼 처리 | 기능 이전의 기준 | + +원본의 등록은 저장 API를 호출하지 않고 미리보기 문구만 표시한다. 상세에도 실제 첨부·댓글·수정·삭제·완료 확인 UI가 없다. 따라서 HTML/CSS 전체 복사만으로 기존 기능이 유지되지 않으며, 현재 기능을 새 화면 구조에 연결해야 한다. + +`docs/관리페이지 md 파일/detail-popup-window-tasks.md`는 관리페이지의 별도 브라우저 창 작업 기록이다. 이번에 확정한 Q&A 모달 전환과는 다른 화면의 기록이다. + +## 3. 목표 화면 + +### 목록 + +- 상단 `S/W 문의` 이미지 배경과 설명 문구를 스크린샷에 맞춘다. +- 반투명 배경의 제품 탭과 선택된 EG-BIM 탭의 곡선·로고·색상을 반영한다. +- 홈페이지 헤더를 제외하므로 원본의 헤더 높이 보정용 상단 여백을 함께 조정한다. +- 문의 내역 제목, 원격지원 버튼, 둥근 분류 필터·검색창, 표·페이지 이동·문의등록 버튼을 맞춘다. +- TOVA·GAIA는 준비중임을 표시하고 실제 목록 조회·등록 대상으로 선택되지 않도록 한다. +- 실제 문의 데이터와 현행 검색·상태 표시를 사용한다. 예시 공지·예시 고객 데이터·미리보기 문구는 배포 화면에 넣지 않는다. + +### 작성 모달 + +- 원본의 중앙 모달, 제목과 구분선, 닫기 버튼, 데스크톱 2열·모바일 1열 폼 구성을 반영한다. +- 서비스는 EG-BIM으로 고정한다. TOVA·GAIA로 잘못 등록할 수 없어야 한다. +- 회사·부서·작성자·이메일 칸은 현재 SSO 정보로 자동 표시하며 읽기 전용으로 구성한다. +- 문의 구분·제목·내용·첨부파일에 기존 비밀글 체크 UI를 더한다. +- 작성자 정보의 저장 원본은 기존 Worker의 세션·조직 조회 결과를 유지한다. +- 등록 중 상태, 필수값 오류, 첨부 오류와 API 오류를 모달 안에서 표시한다. + +### 상세·수정 모달 + +- 원본처럼 제목·닫기 버튼·구분선·작성 정보·본문·하단 버튼 순서로 구성한다. +- 본문 아래에 첨부 목록·다운로드, 완료 안내·확인, 댓글·이미지, 댓글 입력 영역을 추가한다. +- 작성자에게만 게시글 수정·삭제 버튼을 표시한다. 댓글 버튼은 각 댓글 작성자에게만 표시한다. +- 수정은 상세 모달의 편집 모드로 연결하고, 저장·취소 후 상세 보기로 복귀한다. +- 댓글과 첨부로 내용이 길어져도 모달 내부에서 스크롤하고 목록의 위치는 유지한다. + +기본 모달 크기는 원본의 최대 폭 780px·최대 높이 88dvh·둥근 모서리·어두운 배경을 출발점으로 한다. 상세에 추가되는 기능은 이 구조 안에서 배치하고, 작은 화면의 실제 사용성을 검증한다. + +## 4. 반드시 유지할 기능 + +| 기능 | 개편 후 유지 기준 | +| --- | --- | +| SSO 로그인 | 기존 `/auth/*`, Q&A 세션 쿠키, 로그인 후 복귀 흐름 유지 | +| 회사·부서 | Worker의 `org-context` 매핑 유지. 폼의 수동 입력으로 대체하지 않음 | +| 목록 검색 | fuzzy 검색·초성·띄어쓰기·오타 허용, 정확한 제목 우선 정렬 유지 | +| 목록 수집·필터 | 100건 이후 페이지 수집, 분류·내 글 필터, 로딩·부분 실패 표시 유지 | +| 문의 작성 | 기존 구분·제목·내용 검증과 실제 저장 흐름 유지 | +| 문의 첨부 | 다중 첨부·파일당 30MB, 기존 multipart 업로드와 상세 다운로드 유지 | +| 비밀글 | 작성·수정 체크, 목록의 잠금 표시와 기존 조회 정책 유지 | +| 문의 수정·삭제 | 기존 API 연결과 작성자 전용 버튼 표시 유지 | +| 댓글 | 실제 댓글 조회·등록·수정·삭제와 작성자별 버튼 표시 유지 | +| 댓글 이미지 | 다중 이미지·파일당 30MB, 이미지 형식 검증·미리보기·썸네일 유지 | +| 처리 상태 | 현행 신규·접수·진행중·완료·보류 의미 유지 | +| 완료 확인 | 작성자에게 표시하는 처리 완료 안내·완료 확인·확인 후 상태 갱신 유지 | +| 기존 데이터 | 저장된 작성자·회사·부서·첨부·댓글을 그대로 사용. UI 작업으로 과거 데이터 재매핑하지 않음 | + +원본의 상태 예시는 `문의접수/답변완료/검토중/패치예정`이고 현행 상태와 다르다. 배지의 디자인을 적용하면서 현행 상태 의미를 임의로 치환하거나 보류 상태를 누락하지 않는다. + +## 5. 화면 전환·주소 연결안 + +내부 상태 URL은 아래 형태를 구현 기준안으로 사용한다. 기존 주소를 통해 들어오는 사용자도 같은 모달을 볼 수 있도록 연결한다. + +| 진입 | 연결할 화면 | +| --- | --- | +| `/egbim/`, `/egbim/index.html` | 새 목록 | +| 목록의 문의등록 | 목록 위 작성 모달. 예: `/egbim/?view=write` | +| 목록 제목 클릭 | 해당 글의 상세 모달. 예: `/egbim/?view=detail&id=<글 UUID>` | +| `/egbim/write.html` | 목록과 작성 모달로 연결 | +| `/egbim/detail.html?id=<글 UUID>` | 목록과 해당 글의 상세 모달로 연결 | + +```mermaid +flowchart LR + A[문의 목록] -->|문의등록| B[작성 모달] + A -->|글 제목| C[상세 모달] + B -->|저장 성공| C + C -->|작성자 수정| D[상세 편집 모드] + D -->|저장 또는 취소| C + B -->|닫기| A + C -->|닫기 또는 삭제 완료| A +``` + +- 모달 URL로 직접 접속·새로고침해도 해당 화면을 복원한다. +- 브라우저 뒤로·앞으로 이동과 모달 열기·닫기를 일치시킨다. +- 모달을 닫을 때 목록의 검색어·분류·내 글 조건·페이지·스크롤을 보존한다. +- 등록 성공 시 작성 모달에서 해당 글의 상세 모달로 전환하고 목록도 갱신한다. +- 수정·댓글 변경·완료 확인 후 상세와 목록의 관련 상태를 동기화한다. +- 삭제 성공 시 목록으로 복귀하고, 마지막 페이지가 비면 유효한 페이지로 보정한다. +- 세션 만료 시 기존 로그인 흐름을 사용하고 로그인 후 원래 작성·상세 진입 주소를 복원한다. + +## 6. 실제 API 연결 유지 + +아래는 브라우저가 사용하는 현재 Worker 경로다. 원본 목업의 폼 이름을 그대로 전송하지 않고 기존 요청 형식을 연결한다. + +| 동작 | 기존 경로·방식 | 유지할 처리 | +| --- | --- | --- | +| 로그인 사용자 확인 | `GET /auth/session` | 인증된 사용자·조직 정보 표시 | +| 목록 | `GET /api/feedbacks?limit=100&page=...` | 페이지 수집·검색·필터 | +| 작성 | `POST /api/feedbacks` | 텍스트는 JSON, 첨부는 multipart의 `images` 필드 | +| 상세 | `GET /api/feedbacks/{id}` | 실제 본문·작성자·첨부·상태 | +| 수정·삭제 | `PATCH/DELETE /api/feedbacks/{id}` | 기존 수정 가능 필드와 저장 형식 | +| 댓글 조회·등록 | `GET/POST /api/feedbacks/{id}/comments` | 텍스트 또는 multipart의 `attachments` 이미지 필드 | +| 댓글 수정·삭제 | `PATCH/DELETE /api/feedbacks/{id}/comments/{commentId}` | 실제 댓글 ID와 작성자 연결 | +| 완료 확인 | `POST /api/feedbacks/{id}/completion-confirmation` | 기존 완료 확인 처리 | + +이번 범위에서 새 DB나 별도 업로드 API를 도입할 필요는 없다. TOVA·GAIA를 준비중으로 두므로 제품별 API 키·프로젝트·채널 설정 확대도 이번 작업의 선행 조건이 아니다. + +## 7. 단계별 작업 목록 + +### P0. 분석·범위 확정 — 완료 + +- [x] 원본 HTML·CSS·JS·이미지 목록 확인. +- [x] 원본이 실제 저장 기능이 없는 디자인 목업임을 확인. +- [x] 현행 목록·작성·상세 및 Worker API의 기능 대조. +- [x] 사용자 제공 스크린샷 3장을 디자인 기준으로 반영. +- [x] 모달 전환·기존 주소 유지·EG-BIM 우선·헤더/푸터 제외 결정. +- [x] 입력 변경 확인 후 닫기·원본 원격지원 주소 활성화 결정. + +### P1. 스타일·자산 적용 + +- [x] `UI-01` 문의 전용 스타일 범위를 정하고 기존 공통 버튼·폼·레이아웃과의 충돌을 정리한다. +- [x] `UI-02` 히어로 이미지·제품 로고·필요한 폰트만 배포 자산으로 정리하고 상대 경로를 맞춘다. +- [x] `UI-03` 목록의 히어로·곡선 탭·필터·검색창·표·페이지 이동·등록 버튼을 원본 기준으로 구성한다. +- [x] `UI-04` TOVA·GAIA 준비중 표기와 접근 가능한 비활성 탭 처리를 추가한다. +- [x] `UI-05` 원격지원 링크를 확정 주소로 연결하고 새 창 안내와 `noopener noreferrer`를 유지한다. +- [x] `UI-06` 실제 데이터가 없는 예시 공지·예시 댓글 수·미리보기 문구를 제거한다. 댓글 수 배지가 필요하면 실제 API 제공값을 확인하고 연결하며, 행마다 댓글 API를 추가 호출하는 방식은 피한다. + +### P2. 모달·주소·상태 구성 + +- [x] `UI-07` 목록·작성·상세의 데이터 처리와 화면 표시를 분리하고, 모달을 다시 열어도 이벤트가 중복 등록되지 않도록 구성한다. +- [x] `UI-08` 작성·상세 모달의 열기·닫기·전환과 목록 상태 보존을 구현한다. +- [x] `UI-09` URL 직접 진입·새로고침·뒤로/앞으로 이동을 연결한다. +- [x] `UI-10` 기존 `write.html`, `detail.html?id=...` 주소를 새 모달 진입으로 연결하고 로그인 복귀도 확인한다. +- [x] `UI-11` 입력 변경 확인을 닫기 X·취소·ESC·배경 클릭·뒤로 이동에 동일하게 적용한다. 확인을 취소하면 입력과 선택한 파일을 보존한다. +- [x] `UI-12` 상세 안의 미등록 댓글·편집 중 댓글도 닫기 시 입력 변경 확인 대상에 포함한다. +- [x] `UI-13` 모달을 닫거나 다른 글을 열었을 때 이전 글의 늦은 응답이 새 화면을 덮지 않도록 처리한다. +- [x] `UI-14` 포커스 진입·모달 밖 이동 제한·닫은 뒤 포커스 복귀·배경 스크롤 잠금을 구현한다. 삭제로 원래 행이 사라지면 목록의 적절한 위치로 포커스를 돌린다. + +### P3. 실제 작성·상세 기능 이전 + +- [x] `UI-15` 현재 SSO 확인과 회사·부서 매핑을 작성 모달의 읽기 전용 정보에 연결한다. +- [x] `UI-16` 문의 구분·제목·내용·비밀글·첨부파일과 기존 검증·등록 중 상태를 연결한다. +- [x] `UI-17` 등록 성공 시 전체 페이지 이동 대신 상세 모달을 열고 목록을 갱신한다. 실패 시 입력과 파일 선택 상태를 유지한다. +- [x] `UI-18` 상세 본문·작성 정보·한국 시간 등록일·비밀글 표시·첨부 목록 및 다운로드를 연결한다. +- [x] `UI-19` 작성자 UUID가 일치할 때만 게시글 수정·삭제 버튼을 표시하고 수정 폼·저장·취소·삭제 완료 흐름을 연결한다. +- [x] `UI-20` 댓글 조회·등록·수정·삭제, 댓글 이미지 첨부·미리보기·썸네일을 연결한다. +- [x] `UI-21` 게시글 작성자와 댓글 작성자를 각각 판단한다. 다른 사람의 게시글 안에 있는 본인 댓글에는 댓글 버튼을 표시한다. +- [x] `UI-22` 완료 안내·완료 확인 버튼·확인 완료 표시와 목록 상태 갱신을 연결한다. +- [x] `UI-23` 모달 안의 성공·오류 안내를 표시하고, 반복 열기·닫기 때 오류·수정 상태·이전 글의 댓글·이미지가 남지 않도록 초기화한다. +- [x] `UI-24` 실제로 폐기된 첨부·댓글 이미지 미리보기의 임시 URL을 정리한다. + +### P4. 반응형·회귀 검증 + +- [x] `UI-25` 데스크톱 목록·작성·상세 화면을 제공 스크린샷과 비교한다. +- [x] `UI-26` 모바일에서 제품 탭·가로 스크롤 표·1열 작성 폼·모달 내부 스크롤·하단 버튼을 확인한다. +- [x] `UI-27` 작성자·타인·동명이인·작성자 ID 없음·세션 확인 중/만료/실패 조건에서 게시글과 댓글 버튼의 실제 가시성을 검사한다. 새 `.inquiry-button`에도 `hidden` 속성이 적용되도록 한다. +- [x] `UI-28` 오타·초성·띄어쓰기 검색, 100건 이후 데이터, 분류·내 글·페이지 이동 회귀를 검사한다. +- [x] `UI-29` JSON 작성·다중 첨부 작성·비밀글·첨부 다운로드·댓글 이미지·수정/삭제·완료 확인을 모의 API와 브라우저에서 검사한다. +- [x] `UI-30` 반복 모달 열기·등록/저장 중 상태·닫기 확인 취소·오류 후 재시도·다른 글의 늦은 응답을 검사한다. +- [x] `UI-31` 기존 작성자·댓글·검색·Worker 테스트를 새 화면 진입에 맞춰 유지하고 `npm run check`, `npm test`를 통과시킨다. + +### P5. 자산 배포·문서 정리 + +- [x] `UI-32` `scripts/upload-r2.sh`가 새 CSS·JS·이미지·로고를 업로드하도록 경로를 확인하고 WebP의 MIME을 `image/webp`로 지정한다. +- [x] `UI-33` 기존 주소 연결 파일도 배포 목록에 유지하고 CSS·JS 버전을 갱신한다. +- [x] `UI-34` 현행 3개 페이지 전이 설명을 목록·모달 흐름과 실제 multipart API 기준으로 갱신한다. 구형 presign·로컬 저장 전용 설명을 구현 기준으로 사용하지 않는다. +- [x] `UI-35` 변경 커밋과 검증 결과를 정리한다. 원격 push는 기존 요청대로 사용자가 진행한다. +- [ ] `UI-36` 스테이징 자동 배포 성공 후 실제 로그인·조직 정보·작성·첨부·댓글·비밀글·기존 주소를 확인한다. +- [ ] `UI-37` 운영 적용 시 기존 수동 운영 배포 절차를 따른다. UI 개편을 이유로 운영 API 설정이나 SSO client/callback을 임의로 교체하지 않는다. + +## 8. 예상 변경 파일 + +| 파일·영역 | 작업 | +| --- | --- | +| `egbim/index.html` | 새 목록과 작성·상세 모달의 화면 구조 | +| `egbim/write.html`, `egbim/detail.html` | 기존 주소의 진입 연결 | +| `egbim/app.js` 및 필요 시 문의 전용 JS | 기존 기능 연결·모달 수명주기·URL 상태 처리 | +| `assets/styles.css` 및 문의 전용 CSS | 원본 디자인·기능 확장 영역·반응형·숨김 처리 | +| `assets/` 아래 문의용 자산 | 히어로·로고·필요한 폰트 | +| `src/index.js` | 변경 없음. 기존 로그인 보호·API·SSO·조직 처리 유지 | +| `scripts/upload-r2.sh` | 새 자산 경로·MIME 확인 | +| `tests/page-*.test.js`, 필요 시 Worker 라우팅 검사 | 목록·모달·기능·기존 주소 회귀 | +| `README.md`, `docs/qa-login-to-save-transition.md` | 화면 구조·진입 주소·현재 API 흐름 설명 | + +원본 패키지는 비교 기준으로 보존한다. 필요한 자산을 선별해 서비스 경로에 반영하며 패키지 전체와 홈페이지 공통 인증 스크립트를 그대로 배포하지 않는다. + +## 9. 완료 기준 + +- [x] 제공한 세 화면의 배경·탭·목록·폼·모달 디자인을 반영했다. +- [x] 작성·상세를 목록 위 모달로 열며 기존 URL로도 같은 글·작성 화면에 진입한다. +- [x] 회사·부서가 SSO 조직 정보로 표시되고 기존 작성자 식별자를 유지한다. +- [x] 첨부·댓글·비밀글·수정/삭제·완료 확인이 실제 기존 API로 동작한다. +- [x] 게시글·댓글 버튼이 실제 작성자에게만 보이며 로그인 확인 전에도 잘못 노출되지 않는다. +- [x] fuzzy 검색과 전체 페이지 수집·필터·페이지 이동을 유지한다. +- [x] 입력 변경 후 닫기는 확인을 거치며, 닫기 취소 시 입력과 파일을 보존한다. +- [x] 모달을 닫아도 목록 조건·위치가 유지되고 새 글·수정·삭제 결과가 반영된다. +- [x] TOVA·GAIA 준비중 표시와 확정된 원격지원 링크가 동작한다. +- [x] 예시 데이터·미리보기 전용 문구가 남지 않고 모바일·키보드 사용과 회귀 검증을 통과했다. + + +## 10. 구현·검증 결과 + +변경은 정적 HTML/CSS/JS와 자산 업로드 MIME·검증·문서에 한정했다. Worker·API 계약·DB·SSO 설정·기존 배포 트리거는 변경하지 않았다. 기존 주소는 프론트의 `assets/inquiry/entry.js`로 연결하며, 새 화면은 기존 API를 그대로 호출한다. 원본 패키지 전체를 서비스 자산으로 복사하지 않았다. + +- `npm run check`, `npm test`: 기존 94개와 구형 주소 연결 4개, 총 98개 테스트 통과. +- R2 업로드 검사: 새 하위 경로의 진입 JS와 WebP 이미지가 기존 배포 경로에 포함되며 MIME은 각각 `application/javascript`, `image/webp`다. +- Chromium 데스크톱 1900×1000, 모바일 390×844에서 제공 스크린샷과 비교했다. 모바일 표는 기존 열을 유지한 가로 스크롤, 작성 폼은 1열, 모달은 내부 스크롤이다. +- 모의 API 브라우저 검사: fuzzy 검색·페이지 이동·목록 조건 보존·JSON/다중 첨부 작성·비밀글·첨부 링크·게시글 수정/삭제·댓글 수정/삭제/이미지 등록·완료 확인 통과. +- 모달 검사: 기존 URL·직접 진입·새로고침·뒤로/앞으로·X/ESC/배경 닫기·입력 폐기 확인·파일 유지·등록 실패 재시도·등록 중 닫기 방지·반복 열기·이전 글의 늦은 응답·키보드 모달 밖 이동 제한 통과. +- 권한 검사: 작성자·타인·동명이인/ID 없음·만료/오래된 저장값·세션 조회 실패·세션 응답 대기의 6조건에서 게시글/댓글 버튼 실제 가시성을 검사했다. +- 100건 이후 페이지 수집·분류·내 글·초성·오타·띄어쓰기와 기존 Worker/API 회귀는 기본 테스트에서 확인했다. + +위 체크는 로컬 및 모의 API 기준이다. 실제 SSO 로그인·관리페이지 DB 저장·실제 파일 다운로드/업로드 확인은 `UI-36`에 남겨 두었으며, `UI-37` 운영 배포도 아직 수행하지 않았다. 사용자가 push한 뒤 스테이징 배포 결과를 확인한다. + +### 브라우저 회귀 검증 + +`tests/browser/inquiry-ui.mjs`는 Playwright와 Chromium이 있는 별도 검증 환경에서 실행한다. 앱 의존성에는 브라우저 패키지를 추가하지 않았다. 모든 네트워크 요청을 모의 API 또는 로컬 정적 파일로 처리하고 외부 요청은 차단한다. + +```bash +# Playwright와 해당 Chromium이 이미 설치된 환경 +npm run test:ui + +# 검증 도구가 저장소 밖에 설치되어 있는 경우 +PLAYWRIGHT_MODULE=/absolute/path/to/playwright-core \ +CHROMIUM_EXECUTABLE_PATH=/absolute/path/to/chromium \ +npm run test:ui +``` + +기본 스크린샷 출력 위치는 임시 디렉토리의 `qa-inquiry-ui/`다. `UI_SCREENSHOT_DIR`로 바꿀 수 있으며, 데스크톱·모바일 목록/작성/상세 6장을 생성한다. 실제 사용자 세션·API Key는 필요하지 않다. diff --git a/docs/qa-login-to-save-transition.md b/docs/qa-login-to-save-transition.md index 7901982..01c29a5 100644 --- a/docs/qa-login-to-save-transition.md +++ b/docs/qa-login-to-save-transition.md @@ -1,165 +1,83 @@ -# EG-BIM Q&A 로그인부터 글 저장까지 전이맵 +# EG-BIM Q&A 로그인·화면·저장 흐름 -기준일: 2026-09-21 -대상: `qa-test.baroncs.co.kr` / EG-BIM Q&A -구성: 목록(`index.html`) · 작성(`write.html`) · 상세(`detail.html`) +기준일: 2026-11-02. 스테이징 `qa-test.baroncs.co.kr`, 운영 `qa.baroncs.co.kr`의 현재 구조를 설명한다. 이번 UI 개편은 프론트에 한정하며 Worker API·DB·SSO 설정을 변경하지 않는다. -## 1. 전체 흐름 +## 로그인과 화면 진입 -홈페이지 로그인 정보를 Q&A 사이트로 직접 전달하지 않는다. 홈페이지의 Q&A 링크는 별도 호스트로 이동하고, Q&A는 같은 BRSW OIDC SSO에 직접 인증을 요청한다. 이미 중앙 SSO 세션이 있으면 사용자는 로그인 화면을 보지 않고 Q&A 세션을 발급받는다. +Q&A는 기존 Worker의 OIDC Authorization Code + PKCE와 HttpOnly 세션을 사용한다. 홈페이지에서 토큰을 전달하지 않는다. 보호된 페이지에 세션 없이 접근하면 Worker가 `/auth/login?return_url=...`로 보내며, 인증 후 원래 경로와 쿼리로 돌아온다. 작성자와 회사·부서는 `GET /auth/session`의 검증된 정보로 표시한다. + +| 주소·사용자 동작 | 화면 | +| --- | --- | +| `/egbim/`, `/egbim/index.html` | 문의 목록 | +| 문의등록 / `?view=write` | 목록 위 작성 모달 | +| 글 제목 / `?view=detail&id=` | 목록 위 해당 글 상세 모달 | +| `/egbim/write.html` | 프론트에서 `/egbim/?view=write`로 replace 이동 | +| `/egbim/detail.html?id=` | 프론트에서 `/egbim/?view=detail&id=`로 replace 이동 | + +구형 주소도 Worker의 기존 로그인 보호를 거친다. 새로고침과 직접 링크는 URL에 지정한 모달을 복원한다. 모달의 열기·닫기는 브라우저 뒤로·앞으로 이동과 연결한다. 목록 위에서 열었을 때 검색어·분류·내 글·페이지·스크롤을 유지한다. 작성·수정·댓글 초안이나 파일 선택 변경이 있으면 닫기 전에 폐기를 확인한다. ```mermaid flowchart TD - A[바론 홈페이지
EG-BIM Q&A 링크] --> B[Q&A 목록 진입
https://qa-test.baroncs.co.kr/egbim/] - B --> C{Q&A 세션 쿠키
baron_qa_session_v2 유효?} - - C -- 예 --> G[정적 목록 페이지 표시] - C -- 아니오 --> D[302 /auth/login
return_url 보관] - D --> E[Worker가 PKCE 생성
state · verifier · nonce] - E --> F[BARON-SSO OIDC
sso.hmac.kr/oidc] - F --> F1{중앙 SSO 세션 존재?} - F1 -- 아니오 --> F2[SSO 로그인 화면] - F2 --> F3[최초 동의 화면
필요 시 1회] - F1 -- 예 --> H[승인 코드 발급] - F3 --> H - H --> I[/auth/callback?code&state] - I --> J[Worker가 state 검증
PKCE code_verifier 검증] - J --> K[Token Endpoint 호출
Public Client · client_secret 없음] - K --> L[UserInfo 조회 및 claim 정규화] - L --> M[baron_qa_session_v2 발급
HttpOnly · Secure · SameSite=Lax] - M --> G - - G --> N[문의 등록 버튼] - N --> O[작성 페이지
write.html] - O --> P[GET /auth/session
작성자·tenant 표시] - P --> Q{userUuid와 tenantId 존재?} - Q -- 아니오 --> R[제출 차단
다시 로그인 안내] - Q -- 예 --> S[카테고리·제목·내용·비밀글 입력] - S --> T[등록 버튼 클릭] - T --> U[브라우저 UUID 생성
feedbackId] - U --> V{첨부파일 존재?} - V -- 아니오 --> Y[첨부 메타데이터 빈 배열] - V -- 예 --> W[POST presign 요청] - W --> X[qa_cdn presigned URL로
파일 PUT 업로드] - X --> Y[첨부파일 메타데이터 확정] - Y --> Z[feedback envelope 생성] - Z --> AA{apiBaseUrl 설정 여부} - AA -- 비어 있음
현재 테스트 모드 --> AB[localStorage에 임시 저장] - AA -- 설정됨
실서비스 모드 --> AC[POST /v1/qa/feedbacks
feedback.hmac.kr API] - AB --> AD[등록 완료 toast
상세 페이지 이동] - AC --> AE{API 성공?} - AE -- 예 --> AD - AE -- 아니오 --> AF[오류 표시
재시도 가능] + A[Q&A 주소 진입] --> B{Q&A 세션 유효?} + B -->|아니오| C[기존 OIDC SSO 로그인] + C --> D[callback 검증·세션 발급] + D --> A + B -->|예| E[목록 + URL에 지정한 모달] + E --> F[GET /auth/session] + F --> G[작성자·회사·부서 표시] + G --> H[작성 모달 입력·검증] + H --> I{첨부파일 있음?} + I -->|아니오| J[POST /api/feedbacks JSON] + I -->|예| K[POST /api/feedbacks multipart images] + J --> L[기존 Worker: 세션·조직 확인 후 ABC 전달] + K --> L + L --> M{저장 성공?} + M -->|예| N[목록 갱신·해당 글 상세 모달] + M -->|아니오| O[모달 오류 표시·입력과 파일 유지] ``` -## 2. 상태 전이표 +## 작성·첨부 저장 -| 상태 | 화면/주소 | 진입 조건 | 주요 처리 | 다음 상태 | -|---|---|---|---|---| -| `S0` | 홈페이지 | Q&A 메뉴 클릭 | 외부 Q&A URL로 이동 | `S1` | -| `S1` | `/egbim/` | Q&A 세션 유효 | R2의 목록 HTML 제공 | `S5` | -| `S2` | `/auth/login` | 세션 없음 | PKCE 상태 생성, SSO로 redirect | `S3` | -| `S3` | `sso.hmac.kr/oidc` | SSO 세션 없음 | 최초 로그인 및 동의 | `S4` | -| `S4` | `/auth/callback` | `code`, `state` 수신 | state/PKCE 검증, token/userinfo 조회 | `S5` | -| `S5` | 목록 또는 작성 페이지 | `baron_qa_session_v2` 유효 | `/auth/session`에서 사용자 확인 | `S6` | -| `S6` | `write.html` | `userUuid`, `tenantId` 확인 | 작성 폼 입력 대기 | `S7` | -| `S7` | 작성 페이지 | 등록 클릭 | `feedbackId` UUID 생성 및 입력 검증 | `S8` | -| `S8` | 작성 페이지 | 첨부 있음 | presign → `qa_cdn` 업로드 | `S9` | -| `S9` | 작성 페이지 | payload 준비 | feedback/support ticket envelope 생성 | `S10` | -| `S10` | API 또는 localStorage | `apiBaseUrl` 상태에 따라 분기 | 실제 DB 저장 또는 테스트 저장 | `S11` | -| `S11` | `detail.html?id=...` | 저장 성공 | 등록 완료 및 상세 이동 | 종료 | +브라우저는 기존 UUID v7 생성 방식으로 `feedbackId`를 만들고 아래 필드를 보낸다. 회사·부서·작성자·이메일 칸은 읽기 전용이며, 서버가 기존 세션·조직 조회 결과에서 작성자 정보를 결정한다. -## 3. 화면별 사용자 경험 - -### A. 홈페이지 - -- EG-BIM 메뉴의 Q&A 링크만 제공한다. -- 로그인 정보, 쿠키, token, `SESSION_SECRET`을 Q&A로 전달하지 않는다. -- 링크 대상: - -```text -https://qa-test.baroncs.co.kr/egbim/ +```json +{ + "feedbackId": "", + "title": "문의 제목", + "contents": "문의 내용", + "Category": "GENERAL_QNA", + "is_secret": "false" +} ``` -### B. 중앙 SSO +- 첨부가 없으면 JSON으로 전송한다. +- 첨부가 있으면 같은 필드와 반복 `images` 파일 필드를 `FormData`에 넣는다. `Content-Type`의 boundary는 브라우저가 지정한다. +- 기존 Worker가 세션과 첨부를 검증하고 API Key 및 서버 작성자 정보를 붙여 ABC에 전달한다. 파일당 제한은 30MB다. +- 브라우저가 presign API나 R2 PUT을 직접 호출하지 않는다. API base가 빈 문자열이면 현재 호스트의 Worker를 사용한다. +- API 성공 후 기존 localStorage 캐시를 갱신하고 상세 모달을 연다. 실패를 로컬 저장 성공으로 처리하지 않는다. +- 등록 중에는 중복 클릭과 모달 닫기를 막고, 실패하면 입력·파일을 유지하며 재시도할 수 있게 한다. -- Q&A RP는 `https://sso.hmac.kr/oidc`를 사용한다. -- 최초 사용자는 SSO 로그인 및 동의 화면을 볼 수 있다. -- 같은 중앙 SSO 세션이 있으면 다음 접근부터 로그인 화면 없이 authorization code가 발급된다. -- Q&A RP는 PKCE Public Client이므로 client secret을 보내지 않는다. +## 상세·수정·댓글 -### C. Q&A 목록 +| 동작 | 기존 Worker 요청 | +| --- | --- | +| 목록 | `GET /api/feedbacks?limit=100&page=...` | +| 상세 | `GET /api/feedbacks/{id}` | +| 수정 | `PATCH /api/feedbacks/{id}`: `title`, `contents`, `Category`, `is_secret` | +| 삭제 | `DELETE /api/feedbacks/{id}` | +| 댓글 조회·등록 | `GET/POST /api/feedbacks/{id}/comments` | +| 댓글 수정·삭제 | `PATCH/DELETE /api/feedbacks/{id}/comments/{commentId}` | +| 완료 확인 | `POST /api/feedbacks/{id}/completion-confirmation` | -- Worker가 `baron_qa_session_v2`를 검사한다. -- 세션이 없으면 `/auth/login?return_url=...`로 이동한다. -- 세션이 있으면 R2의 `egbim/index.html`을 제공한다. +댓글 텍스트는 JSON `content`, 이미지 첨부 시에는 multipart `content`와 반복 `attachments` 필드를 사용한다. 기존 다중 이미지·형식·30MB 제한과 미리보기를 유지한다. 폐기된 미리보기와 모달을 닫을 때 임시 Blob URL을 해제한다. -### D. Q&A 작성 +수정·삭제 버튼은 검증된 기본 requester UUID가 해당 게시글·댓글의 작성자 ID와 같을 때만 표시한다. 이름·이메일 일치로 권한을 부여하지 않는다. 로그인 확인 중·세션 만료·확인 실패·작성자 ID 없음 상태에서는 숨긴다. 게시글과 댓글의 소유자는 각각 판단한다. -- 브라우저가 `GET /auth/session`을 호출한다. -- 다음 작성자 정보를 화면과 payload에 사용한다. +수정·완료 확인은 목록에도 반영하고, 삭제 후에는 목록으로 돌아간다. 늦게 도착한 이전 상세 응답은 닫힌 모달의 분리된 DOM에서 처리해 현재 모달을 덮지 않는다. 오류는 모달 안에 표시한다. -```text -userUuid -ssoSubject / requesterId -tenantId / requesterTenantId -tenantIds -scope -roles -email -name -department -phone -``` +## 확인과 배포 -- `requesterId` 또는 `requesterTenantId`가 없으면 제출을 차단한다. - -### E. 글 저장 - -- 브라우저에서 `feedbackId` UUID를 1회 생성한다. -- 같은 UUID를 다음 식별자로 재사용한다. - -```text -feedback.id -feedback.source_record_id -support_tickets.idempotency_key -``` - -- 첨부파일은 `qa_cdn`에 저장하고, DB에는 bucket/key 및 파일 메타데이터를 기록한다. -- 실제 운영에서는 feedback API가 서버 측에서 Q&A 세션 또는 SSO token을 검증해야 한다. - -## 4. 현재 테스트 모드와 운영 모드 - -현재 `egbim/config.js`는 다음과 같이 `apiBaseUrl`이 비어 있다. - -```js -apiBaseUrl: '' -``` - -따라서 현재 등록 결과는 다음과 같이 동작한다. - -```text -글 작성 → UUID 생성 → localStorage 저장 → 상세 페이지 이동 -``` - -운영 API 주소를 설정하면 다음 흐름으로 변경된다. - -```text -첨부 presign 요청 -→ qa_cdn 직접 업로드 -→ feedback.hmac.kr/v1/qa/feedbacks 호출 -→ feedbacks/support_tickets 트랜잭션 저장 -→ 상세 페이지 이동 -``` - -## 5. 주요 실패 전이 - -| 실패 지점 | 사용자 화면 | 원인/확인 항목 | -|---|---|---| -| SSO 로그인 전 | 로그인 화면 | 중앙 SSO 세션 없음 또는 다른 SSO 환경 | -| callback | `invalid_oauth_state` | OAuth cookie 만료, 호스트 변경, state 불일치 | -| token 교환 | `oauth_token_exchange_failed` | issuer/endpoint/client ID/PKCE 설정 불일치 | -| 세션 확인 | `authenticated:false` | `baron_qa_session_v2` 없음·만료·`SESSION_SECRET` 불일치 | -| 작성자 확인 | UUID/tenant 오류 | SSO scope 또는 claim mapping 누락 | -| 첨부 업로드 | 업로드 실패 | presign API 또는 `qa_cdn` 권한 오류 | -| DB 저장 | API 요청 오류 | `apiBaseUrl`, CORS, API 인증, DB envelope 규격 오류 | +- 기본 검사: `npm run check`, `npm test`. +- 별도 브라우저 검사: `npm run test:ui`. Playwright/Chromium 준비와 검증 범위는 [UI 작업 목록](qa-inquiry-ui-redesign-tasks.md#브라우저-회귀-검증)을 참고한다. 모든 요청을 모의 처리하며 실제 서버 데이터를 변경하지 않는다. +- 원격 push는 사용자가 진행한다. 스테이징은 기존 main push 자동 배포, 운영은 기존 수동 배포 절차를 사용한다. +- 실제 로그인·관리페이지 저장 연동은 스테이징 배포 후 확인한다. 모의 API 검증만으로 실서버 검증 완료로 표시하지 않는다. diff --git a/egbim/app.js b/egbim/app.js index 0391b5d..a669933 100644 --- a/egbim/app.js +++ b/egbim/app.js @@ -20,6 +20,7 @@ }; let authReady; let resolvedAuthUser = null; + let inquiryUI = null; function qs(selector, root) { return (root || document).querySelector(selector); } function qsa(selector, root) { return Array.prototype.slice.call((root || document).querySelectorAll(selector)); } @@ -590,6 +591,14 @@ let loadFailed = false; let receivedRemotePage = false; const searchStatus = qs('#searchStatus'); + const changes = new Map(); + const deleted = new Set(); + + function mergeChanges(remotePosts) { + const result = remotePosts.filter(function (post) { return !deleted.has(post.id); }).map(function (post) { return changes.get(post.id) || post; }); + for (const post of changes.values()) if (!deleted.has(post.id) && !result.some(function (item) { return item.id === post.id; })) result.unshift(post); + return result; + } function getPostCompany(post, user) { return getPostCompanyLabel(post, user); } @@ -621,15 +630,19 @@ const isNotice = post.category === "공지사항"; const number = isNotice ? "공지" : filtered.length - ((page - 1) * pageSize + index); const status = normalizeStatus(post.status); - const statusClass = status === "답변완료" ? "done" : (status === "문의검토" || status === "정밀검토" ? "review" : ""); + const statusClass = status === '완료' ? 'done' : status === '신규' ? 'new' : status === '보류' ? 'hold' : status === '진행중' ? 'patch' : 'review'; const company = getPostCompany(post, user); return "" + "" + number + "" + escapeHtml(post.category.replace("문의", "")) + "" + - "" + escapeHtml(company) + "" + escapeHtml(post.department) + "" + escapeHtml(post.author) + "" + - "" + (post.secret ? "🔒" : "") + escapeHtml(post.title) + "" + (!isNotice && post.date === "2026-09-17" ? "N" : "") + "" + - "" + escapeHtml(post.dateTime || post.date) + "" + (status ? "" + escapeHtml(status) + "" : "-") + ""; + '' + escapeHtml(company) + '' + escapeHtml(post.department) + '' + escapeHtml(post.author) + '' + + '
' + (post.secret ? '🔒' : '') + '' + escapeHtml(post.title) + '' + + '' + escapeHtml(post.dateTime || post.date) + '' + (status ? '' + escapeHtml(status) + '' : '-') + ''; }).join(""); - qsa("#qaRows tr").forEach(function (row) { row.addEventListener("click", function () { window.location.href = "detail.html?id=" + encodeURIComponent(row.dataset.id); }); }); + qsa("#qaRows tr").forEach(function (row) { row.addEventListener("click", function (event) { + if (event.metaKey || event.ctrlKey || event.shiftKey || event.altKey) return; + if (inquiryUI) { event.preventDefault(); inquiryUI.open('detail', row.dataset.id, row.querySelector('a')); } + else window.location.href = 'detail.html?id=' + encodeURIComponent(row.dataset.id); + }); }); const visiblePages = Array.from(new Set([1, totalPages, page - 2, page - 1, page, page + 1, page + 2])).filter(function (value) { return value >= 1 && value <= totalPages; }).sort(function (a, b) { return a - b; }); pagination.innerHTML = '' + visiblePages.map(function (value, index) { const gap = index > 0 && value - visiblePages[index - 1] > 1 ? '' : ''; @@ -642,8 +655,8 @@ qsa("input[name=category], #onlyMine").forEach(function (input) { input.addEventListener("change", function () { page = 1; render(); }); }); render(); if (authReady) authReady.then(render); - loadRemotePosts(function (result) { receivedRemotePage = true; posts = result.posts; render(); }).then(function (result) { - posts = result.posts; + loadRemotePosts(function (result) { receivedRemotePage = true; posts = mergeChanges(result.posts); render(); }).then(function (result) { + posts = mergeChanges(result.posts); remoteLoaded = true; render(); }).catch(function (error) { @@ -653,16 +666,26 @@ render(); if (!receivedRemotePage) refreshRemoteStatuses(posts).then(function (changed) { if (changed) render(); }); }); + return { + upsert: function (post) { changes.set(post.id, post); posts = mergeChanges(posts); render(); }, + remove: function (id) { changes.delete(id); deleted.add(id); posts = posts.filter(function (post) { return post.id !== id; }); render(); } + }; } - function initWrite() { - const form = qs('#qaForm'); + function initWrite(root) { + const find = function (selector, container) { return qs(selector, container || root); }; + const form = find('#qaForm'); if (!form) return; const user = getAuthUser(); - const errorBox = qs('#formError'); - if (!user) { - qs('#writeFields').setAttribute('aria-disabled', 'true'); - } + const errorBox = find('#formError'); + Promise.resolve(authReady || user).then(function (profile) { + for (const [selector, value] of [['#writeCompany', profile && profile.company], ['#writeDepartment', profile && profile.department], ['#writeAuthor', profile && (profile.name || profile.loginId)], ['#writeEmail', profile && profile.email]]) { + const field = find(selector); + if (field) field.value = value || '-'; + } + const notice = find('[data-auth-state]'); + if (notice) notice.textContent = profile ? '로그인한 사용자 정보로 문의가 등록됩니다.' : '로그인 상태를 확인하지 못했습니다. 새로고침 후 다시 시도해주세요.'; + }); form.addEventListener('submit', async function (event) { event.preventDefault(); console.info('[QA] feedback submit started'); @@ -671,17 +694,17 @@ console.info('[QA] SSO user resolved:', Boolean(currentUser), currentUser ? { hasRequesterId: Boolean(currentUser.requesterId), hasTenantId: Boolean(currentUser.requesterTenantId), hasPhone: Boolean(currentUser.phone) } : null); if (!currentUser) { errorBox.textContent = '로그인 후 문의를 등록할 수 있습니다.'; errorBox.style.display = 'block'; return; } if (!currentUser.requesterId || !currentUser.requesterTenantId) { errorBox.textContent = 'SSO에서 작성자 UUID와 테넌트 정보를 확인하지 못했습니다. 다시 로그인한 뒤 시도해주세요.'; errorBox.style.display = 'block'; return; } - const title = qs('#title').value.trim(); - const content = qs('#content').value.trim(); - if (!qs('#category').value || !title || !content) { errorBox.textContent = '구분, 제목, 내용을 모두 입력해주세요.'; errorBox.style.display = 'block'; return; } + const title = find('#title').value.trim(); + const content = find('#content').value.trim(); + if (!find('#category').value || !title || !content) { errorBox.textContent = '구분, 제목, 내용을 모두 입력해주세요.'; errorBox.style.display = 'block'; return; } const feedbackId = makeUuid(); const createdAt = new Date().toISOString(); - const post = { id: feedbackId, feedbackId: feedbackId, requesterId: currentUser.requesterId, requesterTenantId: currentUser.requesterTenantId, category: qs('#category').value, company: currentUser.company || currentUser.tenantName || currentUser.familyCompany || '외부 사용자', department: currentUser.department || '-', author: currentUser.name || currentUser.loginId, title: title, date: formatKoreanDateTime(createdAt, false), dateTime: formatKoreanDateTime(createdAt, true), createdAt: createdAt, status: '신규', secret: qs('#secret').checked, content: content }; - const submitButton = qs('#submitButton'); + const post = { id: feedbackId, feedbackId: feedbackId, requesterId: currentUser.requesterId, requesterTenantId: currentUser.requesterTenantId, category: find('#category').value, company: currentUser.company || currentUser.tenantName || currentUser.familyCompany || '외부 사용자', department: currentUser.department || '-', author: currentUser.name || currentUser.loginId, title: title, date: formatKoreanDateTime(createdAt, false), dateTime: formatKoreanDateTime(createdAt, true), createdAt: createdAt, status: '신규', secret: find('#secret').checked, content: content }; + const submitButton = find('#submitButton'); submitButton.disabled = true; submitButton.textContent = '등록 중...'; try { - const submission = await buildFeedbackSubmission(post, feedbackId, qs('#attachment').files); + const submission = await buildFeedbackSubmission(post, feedbackId, find('#attachment').files); console.info('[QA] sending feedback to Worker:', config.createFeedbackPath); const result = await postToApi(config.createFeedbackPath, submission.body); console.info('[QA] Worker feedback response:', result); @@ -689,7 +712,8 @@ post.attachments = submission.attachments; savePost(post); showToast(result.local ? '테스트 글로 저장했습니다. UUID가 생성되었습니다.' : '문의가 등록되었습니다.'); - window.setTimeout(function () { window.location.href = 'detail.html?id=' + encodeURIComponent(post.id); }, 500); + if (inquiryUI) inquiryUI.saved(post); + else window.setTimeout(function () { window.location.href = 'detail.html?id=' + encodeURIComponent(post.id); }, 500); } catch (error) { console.error('[QA] feedback submit failed:', error); errorBox.textContent = error.message || '등록 중 오류가 발생했습니다.'; @@ -700,14 +724,15 @@ }); } - async function initDetail() { - const detail = qs('#detail'); + async function initDetail(root, requestedId) { + const find = function (selector, container) { return qs(selector, container || root); }; + const detail = find('#detail'); if (!detail) return; - const detailListLink = qs('.detail-actions > a', detail); + const detailListLink = find('.detail-actions > a', detail); if (detailListLink) detailListLink.textContent = '목록으로'; - const bottomBackButton = qs('#backButton', detail); + const bottomBackButton = find('#backButton', detail); if (bottomBackButton && bottomBackButton.parentElement) bottomBackButton.parentElement.remove(); - const id = new URLSearchParams(window.location.search).get("id") || "EG-1048"; + const id = requestedId || new URLSearchParams(window.location.search).get("id") || "EG-1048"; const storedPost = getPosts().find(function (item) { return item.id === id; }); let post = storedPost; try { @@ -718,42 +743,45 @@ console.warn("[QA] feedback detail load failed:", error.message); } if (!post) { - qs('[data-detail-title]').textContent = '문의 내용을 찾을 수 없습니다.'; - qs('[data-detail-content]').textContent = '목록에 등록된 문의가 없습니다.'; + find('[data-detail-title]').textContent = '문의 내용을 찾을 수 없습니다.'; + find('[data-detail-content]').textContent = '목록에 등록된 문의가 없습니다.'; + const commentBox = find('.comment-box'); + if (commentBox) commentBox.hidden = true; return; } - qs('[data-detail-category]').textContent = post.category; - qs('[data-detail-title]').textContent = post.title; + find('[data-detail-category]').textContent = post.category; + find('[data-detail-title]').textContent = post.title; const renderDetailStatus = function () { - const statusElement = qs("[data-detail-status]"); + const statusElement = find("[data-detail-status]"); const status = normalizeStatus(post.status) || "공지"; if (!statusElement) return; statusElement.textContent = status; statusElement.classList.toggle("done", status === "완료"); statusElement.classList.toggle("review", status !== "완료"); + if (statusElement.setAttribute) statusElement.setAttribute('data-status', status); }; renderDetailStatus(); - qs('[data-detail-date]').textContent = post.dateTime || post.date; - qs('[data-detail-author]').textContent = post.author; + find('[data-detail-date]').textContent = post.dateTime || post.date; + find('[data-detail-author]').textContent = post.author; const detailUser = await (authReady || Promise.resolve(getAuthUser())); - qs('[data-detail-company]').textContent = getPostCompanyLabel(post, detailUser); - qs('[data-detail-department]').textContent = post.department; - qs('[data-detail-content]').textContent = post.content; - qs('[data-detail-secret]').hidden = !post.secret; - const attachmentBox = qs('[data-detail-attachments]'); + find('[data-detail-company]').textContent = getPostCompanyLabel(post, detailUser); + find('[data-detail-department]').textContent = post.department; + find('[data-detail-content]').textContent = post.content; + find('[data-detail-secret]').hidden = !post.secret; + const attachmentBox = find('[data-detail-attachments]'); const attachments = Array.isArray(post.attachments) ? post.attachments : []; if (attachmentBox) attachmentBox.innerHTML = attachments.length ? attachments.map(function (attachment) { const file = normalizeFeedbackAttachment(attachment); if (!file.url) return '
📎 ' + escapeHtml(file.name) + '
'; return ''; }).join('') : '첨부된 파일이 없습니다.'; - const editButton = qs('#editButton'); - const deleteButton = qs('#deleteButton'); - const editForm = qs('#editForm'); - const editError = qs('#editError'); - const completionNotice = qs("#completionNotice"); - const completionDoneNotice = qs("#completionDoneNotice"); - const completionButton = qs("#completionButton"); + const editButton = find('#editButton'); + const deleteButton = find('#deleteButton'); + const editForm = find('#editForm'); + const editError = find('#editError'); + const completionNotice = find("#completionNotice"); + const completionDoneNotice = find("#completionDoneNotice"); + const completionButton = find("#completionButton"); const renderCompletionNotice = function () { const owner = isPostOwner(post, detailUser); const completed = normalizeStatus(post.status) === "완료" || post.completionConfirmed; @@ -778,6 +806,7 @@ post.completionConfirmed = true; post.status = "완료"; updateStoredPost(post); + if (inquiryUI) inquiryUI.changed(post); renderDetailStatus(); renderCompletionNotice(); showToast("문의가 완료 처리되었습니다."); @@ -789,49 +818,55 @@ } }); if (editButton && editForm) editButton.addEventListener('click', function () { - qs('#editCategory').value = post.category || '일반문의'; - qs('#editTitle').value = post.title || ''; - qs('#editContent').value = post.content || ''; - qs('#editSecret').checked = Boolean(post.secret); + find('#editCategory').value = post.category || '일반문의'; + find('#editTitle').value = post.title || ''; + find('#editContent').value = post.content || ''; + find('#editSecret').checked = Boolean(post.secret); editError.hidden = true; editError.style.display = 'none'; editForm.hidden = false; detail.classList.add('is-editing'); editForm.scrollIntoView({ behavior: 'smooth', block: 'start' }); + if (inquiryUI) inquiryUI.markClean(editForm); + }); + const editCancel = find('#editCancel'); + if (editCancel && editForm) editCancel.addEventListener('click', function () { + if (inquiryUI && !inquiryUI.discardForm(editForm)) return; + editForm.reset(); editForm.hidden = true; detail.classList.remove('is-editing'); + if (inquiryUI) inquiryUI.markClean(editForm); }); - const editCancel = qs('#editCancel'); - if (editCancel && editForm) editCancel.addEventListener('click', function () { editForm.hidden = true; detail.classList.remove('is-editing'); }); if (editForm) editForm.addEventListener('submit', async function (event) { event.preventDefault(); editError.hidden = true; editError.style.display = 'none'; - const title = qs('#editTitle').value.trim(); - const content = qs('#editContent').value.trim(); - const category = qs('#editCategory').value; + const title = find('#editTitle').value.trim(); + const content = find('#editContent').value.trim(); + const category = find('#editCategory').value; if (!title || !content || !category) { editError.textContent = '구분, 제목, 내용을 모두 입력해주세요.'; editError.hidden = false; editError.style.display = 'block'; return; } - const saveButton = qs('button[type="submit"]', editForm); + const saveButton = find('button[type="submit"]', editForm); saveButton.disabled = true; saveButton.textContent = '저장 중...'; try { await requestJson('/api/feedbacks/' + encodeURIComponent(post.id), { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ title: title, contents: content, Category: CATEGORY_CODES[category], is_secret: qs('#editSecret').checked ? 'true' : 'false' }) + body: JSON.stringify({ title: title, contents: content, Category: CATEGORY_CODES[category], is_secret: find('#editSecret').checked ? 'true' : 'false' }) }); post.category = category; post.title = title; post.content = content; - post.secret = qs('#editSecret').checked; + post.secret = find('#editSecret').checked; updateStoredPost(post); - qs('[data-detail-category]').textContent = post.category; - qs('[data-detail-title]').textContent = post.title; - qs('[data-detail-content]').textContent = post.content; - qs('[data-detail-secret]').hidden = !post.secret; + if (inquiryUI) { inquiryUI.changed(post); inquiryUI.markClean(editForm); } + find('[data-detail-category]').textContent = post.category; + find('[data-detail-title]').textContent = post.title; + find('[data-detail-content]').textContent = post.content; + find('[data-detail-secret]').hidden = !post.secret; editForm.hidden = true; detail.classList.remove('is-editing'); showToast('문의가 수정되었습니다.'); @@ -850,7 +885,8 @@ try { await requestJson('/api/feedbacks/' + encodeURIComponent(post.id), { method: 'DELETE' }); removeStoredPost(post.id); - window.location.href = 'index.html'; + if (inquiryUI) inquiryUI.deleted(post.id); + else window.location.href = 'index.html'; } catch (error) { deleteButton.disabled = false; showToast(error.message || '문의 삭제에 실패했습니다.'); @@ -896,15 +932,34 @@ } let editingCommentId = null; + const objectUrls = new Set(); + const previewUrls = new Set(); + function imageObjectUrl(file, preview) { + const url = URL.createObjectURL(file); + objectUrls.add(url); + if (preview) previewUrls.add(url); + return url; + } + function clearImagePreview() { + previewUrls.forEach(function (url) { URL.revokeObjectURL(url); objectUrls.delete(url); }); + previewUrls.clear(); + } + if (inquiryUI) inquiryUI.onCleanup(root, function () { objectUrls.forEach(function (url) { URL.revokeObjectURL(url); }); }); function isCommentOwner(comment, user) { return matchesAuthorId(comment.authorId, user); } function renderComments(comments) { comments = (comments || []).map(normalizeComment).filter(function (comment) { return comment.content; }); - qs('#commentCount').textContent = comments.length; + const usedUrls = new Set(comments.flatMap(function (comment) { + return comment.attachments.flatMap(function (attachment) { return [attachment.url, attachment.thumbnailUrl]; }); + })); + objectUrls.forEach(function (url) { + if (!usedUrls.has(url) && !previewUrls.has(url)) { URL.revokeObjectURL(url); objectUrls.delete(url); } + }); + find('#commentCount').textContent = comments.length; const user = detailUser; - qs('#comments').innerHTML = comments.length ? comments.map(function (comment) { + find('#comments').innerHTML = comments.length ? comments.map(function (comment) { const owner = Boolean(comment.id) && isCommentOwner(comment, user); const editing = Boolean(editingCommentId) && editingCommentId === comment.id && owner; const actions = owner ? '
' : ''; @@ -923,7 +978,7 @@ }).catch(function (error) { console.warn('[QA] comments load failed:', error.message); }); - qs('#comments').addEventListener('click', async function (event) { + find('#comments').addEventListener('click', async function (event) { const editButton = event.target.closest('[data-comment-edit]'); const cancelButton = event.target.closest('[data-comment-cancel]'); const saveButton = event.target.closest('[data-comment-save]'); @@ -931,15 +986,19 @@ if (editButton) { editingCommentId = editButton.getAttribute('data-comment-edit'); comments = renderComments(comments); - const input = qs('[data-comment-edit-input]'); + const input = find('[data-comment-edit-input]'); if (input) { input.focus(); input.setSelectionRange(input.value.length, input.value.length); } return; } - if (cancelButton) { editingCommentId = null; comments = renderComments(comments); return; } + if (cancelButton) { + const input = find('[data-comment-edit-input]'); + if (inquiryUI && input && !inquiryUI.discardEditor(input)) return; + editingCommentId = null; comments = renderComments(comments); return; + } if (saveButton) { const commentId = saveButton.getAttribute('data-comment-save'); const comment = comments.find(function (item) { return item.id === commentId; }); - const input = qs('[data-comment-edit-input]'); + const input = find('[data-comment-edit-input]'); const content = input ? input.value.trim() : ''; if (!content) { showToast('댓글 내용을 입력해주세요.'); return; } saveButton.disabled = true; @@ -965,13 +1024,13 @@ } catch (error) { showToast(error.message || '댓글 삭제에 실패했습니다.'); deleteButton.disabled = false; } } }); - const commentForm = qs('#commentForm'); + const commentForm = find('#commentForm'); if (commentForm) commentForm.addEventListener('submit', async function (event) { event.preventDefault(); - const contentInput = qs('#commentContent'); - const errorBox = qs('#commentError'); - const submitButton = qs('#commentSubmit'); - const imageInput = qs('#commentImages'); + const contentInput = find('#commentContent'); + const errorBox = find('#commentError'); + const submitButton = find('#commentSubmit'); + const imageInput = find('#commentImages'); const files = imageInput ? Array.prototype.slice.call(imageInput.files || []) : []; const content = contentInput.value.trim(); errorBox.hidden = true; @@ -988,7 +1047,7 @@ files.forEach(function (file) { payload.append('attachments', file, file.name); }); } const result = await postToApi(commentsPath, payload); - const localAttachments = files.map(function (file) { const url = URL.createObjectURL(file); return { name: file.name, url: url, thumbnailUrl: url }; }); + const localAttachments = files.map(function (file) { const url = imageObjectUrl(file); return { name: file.name, url: url, thumbnailUrl: url }; }); const savedComment = normalizeComment(Object.assign({ content: content, author: (getAuthUser() || {}).name || '작성자', @@ -1002,8 +1061,10 @@ renderComments(comments); contentInput.value = ''; if (imageInput) imageInput.value = ''; - const imagePreview = qs('#commentImagePreview'); + const imagePreview = find('#commentImagePreview'); if (imagePreview) imagePreview.innerHTML = ''; + clearImagePreview(); + if (inquiryUI) inquiryUI.markClean(commentForm); } catch (error) { errorBox.textContent = error.message || '댓글 등록에 실패했습니다.'; errorBox.hidden = false; @@ -1012,21 +1073,181 @@ submitButton.textContent = '댓글 등록'; } }); - const commentImages = qs('#commentImages'); - const commentImagePreview = qs('#commentImagePreview'); - const commentImageLabel = qs('label[for="commentImages"]'); + const commentImages = find('#commentImages'); + const commentImagePreview = find('#commentImagePreview'); + const commentImageLabel = find('label[for="commentImages"]'); if (commentImageLabel) commentImageLabel.textContent = '댓글 이미지 첨부'; if (commentImages && commentImagePreview) commentImages.addEventListener('change', function () { + clearImagePreview(); const files = Array.prototype.slice.call(commentImages.files || []); commentImagePreview.innerHTML = files.filter(function (file) { return /^image\//i.test(file.type); }).map(function (file) { - const url = URL.createObjectURL(file); + const url = imageObjectUrl(file, true); return '
' + escapeHtml(file.name) + '' + escapeHtml(file.name) + '
'; }).join(''); }); refreshRemoteStatus(post).then(function (changed) { - if (changed) renderDetailStatus(); + if (changed && (!inquiryUI || inquiryUI.isCurrent(root))) { + renderDetailStatus(); + if (inquiryUI) inquiryUI.changed(post); + } }); } - document.addEventListener('DOMContentLoaded', function () { normalizePageHeading(); bindGlobal(); initList(); initWrite(); initDetail(); }); + function initInquiryUI(list) { + const dialogs = { write: qs('#composeModal'), detail: qs('#detailModal') }; + if (!dialogs.write || !dialogs.detail) return; + const baselines = new WeakMap(); + let active = null; + let restoringHistory = false; + let depth = 0; + + function snapshot(form) { + return JSON.stringify(Array.from(form.elements).filter(function (input) { + return !input.readOnly && !input.disabled && !['submit', 'button'].includes(input.type); + }).map(function (input) { + return [input.id, input.type === 'file' ? Array.from(input.files).map(function (file) { return [file.name, file.size, file.lastModified]; }) : input.type === 'checkbox' ? input.checked : input.value]; + })); + } + + function dirty(form) { return !form.hidden && snapshot(form) !== baselines.get(form); } + function dirtyEditor(input) { return input.value !== input.defaultValue; } + function busy() { return active && Boolean(active.root.querySelector('button:disabled')); } + function hasChanges() { + return active && (qsa('form', active.root).some(dirty) || qsa('[data-comment-edit-input]', active.root).some(dirtyEditor)); + } + function confirmDiscard() { return window.confirm('작성 중인 내용이 있습니다. 닫으면 입력한 내용이 사라집니다. 닫으시겠습니까?'); } + function canLeave(force) { + if (force || !active) return true; + if (busy()) { showToast('처리가 끝난 후 닫을 수 있습니다.'); return false; } + return !hasChanges() || confirmDiscard(); + } + + function clear(restoreFocus) { + if (!active) return; + const previous = active; + active = null; + previous.dialog.close(); + previous.cleanups.forEach(function (cleanup) { cleanup(); }); + const toast = qs('#toast'); + if (toast) document.body.append(toast); + previous.dialog.querySelector('[data-modal-content]').replaceChildren(); + document.documentElement.classList.remove('inquiry-modal-open'); + if (restoreFocus) { + window.scrollTo({ top: previous.scroll, behavior: 'instant' }); + const opener = previous.opener && previous.opener.isConnected ? previous.opener : qs('#writeButton') || qs('#boardHeading'); + if (opener) opener.focus({ preventScroll: true }); + } + } + + function routeURL(view, id) { + const url = new URL(window.location.href); + url.searchParams.delete('view'); url.searchParams.delete('id'); + if (view) url.searchParams.set('view', view); + if (view === 'detail' && id) url.searchParams.set('id', id); + return url; + } + + async function mount(view, id, opener) { + const scroll = active ? active.scroll : window.scrollY; + const previousOpener = active && active.opener; + clear(false); + const dialog = dialogs[view]; + if (!dialog) return; + const root = document.createElement('div'); + root.setAttribute('aria-busy', view === 'detail' ? 'true' : 'false'); + root.append(qs(view === 'write' ? '#writeTemplate' : '#detailTemplate').content.cloneNode(true)); + dialog.querySelector('[data-modal-content]').replaceChildren(root); + const toast = qs('#toast'); + if (toast) dialog.append(toast); + active = { view: view, id: id, root: root, dialog: dialog, opener: opener || previousOpener, scroll: scroll, cleanups: [] }; + qsa('form', root).forEach(function (form) { baselines.set(form, snapshot(form)); }); + document.documentElement.classList.add('inquiry-modal-open'); + dialog.showModal(); + const heading = root.querySelector('h2'); + if (heading) heading.focus({ preventScroll: true }); + try { + if (view === 'write') initWrite(root); + else if (id) await initDetail(root, id); + else { + root.querySelector('[data-detail-title]').textContent = '문의 내용을 찾을 수 없습니다.'; + root.querySelector('.comment-box').hidden = true; + } + } catch (error) { + if (active && active.root === root) showToast('화면을 불러오지 못했습니다. 잠시 후 다시 시도해주세요.'); + console.warn('[QA] dialog initialization failed:', error.message); + } finally { + root.setAttribute('aria-busy', 'false'); + } + } + + function open(view, id, opener, options) { + options = options || {}; + if (!canLeave(options.force)) return; + const url = routeURL(view, id); + if (active || options.replace) history.replaceState({ qaModal: true }, '', url); + else history.pushState({ qaModal: true }, '', url); + depth = 1; + mount(view, id, opener); + } + + function close(force) { + if (!canLeave(force)) return; + clear(true); + if (depth === 1) history.back(); + else history.replaceState({ qaModal: false }, '', routeURL()); + } + + inquiryUI = { + open: open, + isCurrent: function (root) { return Boolean(active && active.root === root); }, + markClean: function (form) { baselines.set(form, snapshot(form)); }, + discardForm: function (form) { return !dirty(form) || confirmDiscard(); }, + discardEditor: function (input) { return !dirtyEditor(input) || confirmDiscard(); }, + onCleanup: function (root, callback) { if (active && active.root === root) active.cleanups.push(callback); else callback(); }, + changed: function (post) { if (list) list.upsert(post); }, + saved: function (post) { if (list) list.upsert(post); open('detail', post.id, null, { force: true, replace: true }); }, + deleted: function (id) { if (list) list.remove(id); close(true); } + }; + + qsa('[data-open-write]').forEach(function (button) { + button.addEventListener('click', function (event) { event.preventDefault(); open('write', null, button); }); + }); + Object.values(dialogs).forEach(function (dialog) { + dialog.addEventListener('cancel', function (event) { event.preventDefault(); close(false); }); + dialog.addEventListener('click', function (event) { + if (event.target.closest('[data-modal-close]')) { close(false); return; } + const rect = dialog.getBoundingClientRect(); + if (event.target === dialog && (event.clientX < rect.left || event.clientX > rect.right || event.clientY < rect.top || event.clientY > rect.bottom)) close(false); + }); + }); + window.addEventListener('beforeunload', function (event) { + if (hasChanges() || busy()) { event.preventDefault(); event.returnValue = ''; } + }); + window.addEventListener('popstate', function (event) { + if (restoringHistory) { restoringHistory = false; return; } + const nextDepth = event.state && event.state.qaModal ? 1 : 0; + if (!canLeave(false)) { + restoringHistory = true; + history.go(depth > nextDepth ? 1 : -1); + return; + } + depth = nextDepth; + const url = new URL(window.location.href); + const view = url.searchParams.get('view'); + if (view === 'write' || view === 'detail') mount(view, url.searchParams.get('id')); + else clear(true); + }); + + const initial = new URL(window.location.href); + const view = initial.searchParams.get('view'); + history.replaceState({ qaModal: false }, '', routeURL()); + if (view === 'write' || view === 'detail') open(view, initial.searchParams.get('id')); + } + + document.addEventListener('DOMContentLoaded', function () { + normalizePageHeading(); bindGlobal(); + const list = initList(); + if (qs('#composeModal')) initInquiryUI(list); + else { initWrite(); initDetail(); } + }); })(); diff --git a/egbim/detail.html b/egbim/detail.html index c718c34..83ac042 100644 --- a/egbim/detail.html +++ b/egbim/detail.html @@ -1,8 +1 @@ - - -문의 상세 | EG-BIM Q&A - -
EG-BIM SUPPORT

Q&A

EG-BIM 관련 문의하기

-

문의 상세

문의 내용과 답변을 확인할 수 있습니다.

문의 문의 제목

문의접수
2026-09-17작성자회사부서

첨부파일

첨부된 파일이 없습니다.
목록

답변 0

-
- +S/W 문의 | 바론컨설턴트

문의 화면으로 이동합니다. 문의 목록

diff --git a/egbim/index.html b/egbim/index.html index a6b5587..98e1ca2 100644 --- a/egbim/index.html +++ b/egbim/index.html @@ -1,29 +1,208 @@ - - - - - - EG-BIM Q&A | 바론컨설턴트 - - - -
-
-
EG-BIM SUPPORT

Q&A

EG-BIM 관련 문의하기

- -
-
-

문의하기(Q&A)

-
-
-
- -
EG-BIM 문의 목록
번호구분회사부서작성자제목등록일상태
검색 조건에 맞는 문의가 없습니다.
- -
-
-
- - - +S/W 문의 | 바론컨설턴트 +
+
+
+

S/W 문의

+

소프트웨어 관련 문의하기

+
+
+ +
+
+
+ + + +
+
+
+
+

문의 내역

+ + + 원격지원 + +
+ +

좌우로 밀어 문의 목록을 확인하세요.

+
+ + + + + +
EG-BIM 문의 목록
번호구분회사부서작성자제목등록일상태
문의 목록을 불러오는 중입니다.
+
+ + +
+ + +
+
+
+
+
+
+ + +
+ diff --git a/egbim/write.html b/egbim/write.html index ae7d7d8..83ac042 100644 --- a/egbim/write.html +++ b/egbim/write.html @@ -1,8 +1 @@ - - -문의 등록 | EG-BIM Q&A - -
EG-BIM SUPPORT

Q&A

EG-BIM 관련 문의하기

-

문의 등록

로그인한 사용자 정보로 문의가 등록됩니다.

로그인 상태를 확인하고 있습니다.
공개 설정
개인정보, 비밀번호, 인증번호 등 민감한 정보는 입력하지 마세요.
파일은 문의와 함께 안전하게 업로드됩니다. 파일당 최대 30MB
-
- +S/W 문의 | 바론컨설턴트

문의 화면으로 이동합니다. 문의 목록

diff --git a/package.json b/package.json index 8469fe5..9a87df2 100644 --- a/package.json +++ b/package.json @@ -6,8 +6,9 @@ "deploy": "wrangler deploy", "deploy:production": "node scripts/deploy-production.mjs", "r2:upload": "bash scripts/upload-r2.sh", - "check": "node --check src/index.js && node --check src/org-context.js && node --check egbim/app.js && node --check scripts/verify-org-context.mjs && node --check scripts/production-settings.mjs && node --check scripts/deploy-production.mjs && node --check scripts/verify-production.mjs && bash -n scripts/upload-r2.sh", + "check": "node --check src/index.js && node --check src/org-context.js && node --check egbim/app.js && node --check assets/inquiry/entry.js && node --check scripts/verify-org-context.mjs && node --check scripts/production-settings.mjs && node --check scripts/deploy-production.mjs && node --check scripts/verify-production.mjs && bash -n scripts/upload-r2.sh", "test": "node --test --test-isolation=none tests/*.test.js", + "test:ui": "node tests/browser/inquiry-ui.mjs", "org:verify": "node scripts/verify-org-context.mjs", "production:verify": "node scripts/verify-production.mjs" }, diff --git a/scripts/upload-r2.sh b/scripts/upload-r2.sh index 47c8316..f22f48e 100755 --- a/scripts/upload-r2.sh +++ b/scripts/upload-r2.sh @@ -33,6 +33,7 @@ while IFS= read -r file; do *.woff2) content_type="font/woff2" ;; *.svg) content_type="image/svg+xml" ;; *.png) content_type="image/png" ;; + *.webp) content_type="image/webp" ;; *.jpg|*.jpeg) content_type="image/jpeg" ;; *) content_type="application/octet-stream" ;; esac diff --git a/tests/browser/inquiry-ui.mjs b/tests/browser/inquiry-ui.mjs new file mode 100644 index 0000000..abcd032 --- /dev/null +++ b/tests/browser/inquiry-ui.mjs @@ -0,0 +1,185 @@ +// Isolated browser regression: every network request is served locally or mocked. +// Requires Playwright and Chromium; see docs/qa-inquiry-ui-redesign-tasks.md. +import assert from 'node:assert/strict'; +import {readFile, mkdir} from 'node:fs/promises'; +import {createRequire} from 'node:module'; +import {fileURLToPath} from 'node:url'; +import {join} from 'node:path'; +import {tmpdir} from 'node:os'; +const require = createRequire(import.meta.url); +const {chromium} = require(process.env.PLAYWRIGHT_MODULE || 'playwright'); +const root = fileURLToPath(new URL('../../', import.meta.url)).replace(/\/$/, ''); +const OWNER = '9951229e-eea5-4376-96f4-78989bc3a506'; +const id = n => '01900000-0000-7000-8000-' + String(n).padStart(12, '0'); +const browser = await chromium.launch({executablePath:process.env.CHROMIUM_EXECUTABLE_PATH || undefined,headless:true,args:['--no-sandbox','--disable-dev-shm-usage']}); +const images = process.env.UI_SCREENSHOT_DIR || join(tmpdir(), 'qa-inquiry-ui'); +await mkdir(images,{recursive:true}); +const errors = []; +try { + const context = await browser.newContext({viewport:{width:1900,height:1000}}); + const page = await context.newPage();page.setDefaultTimeout(8000);page.setDefaultNavigationTimeout(8000); + page.on('pageerror', error => errors.push(error.message)); + const records = Array.from({length:15},(_,i)=>({id:id(i+1),requester_id:OWNER,requester_name:'송대일',requester_affiliation:'총괄기획&기술개발센터',requester_department:'IS3',title:['PDF 출력 시 이미지 표시 관련 문의','EG-BIM 로그인 오류 문의','모델링 기능 개선 요청'][i%3] + (i>2?' '+(i+1):''),contents:'EG-BIM 이용 중 궁금한 사항을 문의합니다.\n\n이 영역에 문의 내용과 첨부파일, 답변이 표시됩니다.',Category:['ERROR_QNA','GENERAL_QNA','IMPROVEMENT_QNA'][i%3],created_at:'2026-11-02T01:14:00Z',feedback_status:['NEW','RECEIVED','IN_PROGRESS','PENDING','COMPLETED'][i%5],is_secret:i===0,attachments:i===0?[{url:'/fixture/manual.pdf',original_file_name:'화면 설명.pdf'}]:[]})); + const comments = new Map(records.map(r=>[r.id,[{id:id(101),author_id:OWNER,author_name:'송대일',content:'확인 부탁드립니다.',created_at:'2026-11-02T02:00:00Z'},{id:id(102),author_id:'other',author_name:'고객지원',content:'접수되었습니다.',created_at:'2026-11-02T02:15:00Z'}]])); + const mutations = []; + let failCreate = false, holdCreate = null, slowId = '', releaseSlow; + await page.route('**/*',async route=>{ + const request = route.request(); const url = new URL(request.url()); + if(url.origin!=='https://qa.example.test') return route.abort(); + const send = (body,status=200)=>route.fulfill({status,contentType:'application/json',body:JSON.stringify(body)}); + if(url.pathname==='/auth/session') return send({authenticated:true,user:{requesterId:OWNER,userUuid:OWNER,tenantId:id(555),name:'송대일',email:'fixture@example.test',company:'총괄기획&기술개발센터',department:'IS3'}}); + if(url.pathname.startsWith('/api/')) { + const method = request.method(); if(method!=='GET') mutations.push({url:url.pathname,method,body:request.postData(),type:request.headers()['content-type']}); + if(url.pathname==='/api/feedbacks') { + if(method==='GET') return send({items:records,meta:{total:records.length}}); + if(holdCreate) await holdCreate; + if(failCreate) {failCreate=false;return send({message:'모의 등록 오류'},500);} + let payload; if(request.headers()['content-type'].includes('application/json')) payload=JSON.parse(request.postData()); + else {const fields={};const text=request.postData(); for(const name of ['feedbackId','title','contents','Category','is_secret']) fields[name]=text.match(new RegExp('name="'+name+'"\\r\\n\\r\\n([^\\r]*)'))?.[1];payload=fields;} + const record={...records[0],...payload,id:payload.feedbackId,attachments:[]};records.unshift(record); comments.set(record.id,[]);return send({id:record.id}); + } + const parts = url.pathname.split('/'); const record=records.find(r=>r.id===parts[3]); + if(parts[3]===slowId && method==='GET' && parts.length===4) await new Promise(r=>{releaseSlow=r;}); + if(!record) return send({message:'not found'},404); + if(parts[4]==='comments') { + let list=comments.get(record.id)||[]; + if(method==='GET') return send({comments:list}); + if(method==='POST') { const content=request.headers()['content-type'].includes('application/json')?JSON.parse(request.postData()).content:request.postData().match(/name="content"\r\n\r\n([^\r]*)/)[1];const comment={id:id(200+list.length),author_id:OWNER,author_name:'송대일',content};list.push(comment);comments.set(record.id,list);return send({comment}); } + if(method==='PATCH') {const comment=list.find(c=>c.id===parts[5]);Object.assign(comment,JSON.parse(request.postData()));return send({comment});} + if(method==='DELETE') {comments.set(record.id,list.filter(c=>c.id!==parts[5]));return send({ok:true});} + } + if(parts[4]==='completion-confirmation') {record.feedback_status='COMPLETED'; record.completion_requested=false;return send({ok:true});} + if(method==='PATCH') {Object.assign(record,JSON.parse(request.postData()));return send(record);} + if(method==='DELETE') {records.splice(records.indexOf(record),1);return send({ok:true});} + return send({feedback:record}); + } + if(url.pathname.startsWith('/fixture/')) return route.fulfill({body:'fixture'}); + const path=url.pathname.endsWith('/')?url.pathname+'index.html':url.pathname; + const type={css:'text/css',js:'application/javascript',html:'text/html',svg:'image/svg+xml',webp:'image/webp',woff2:'font/woff2'}[path.split('.').pop()]||'application/octet-stream'; + try{return route.fulfill({contentType:type,body:await readFile(root+path)});}catch{return route.abort();} + }); + const open = async n=>{await page.locator('#qaRows tr[data-id="'+id(n)+'"] a').click();await page.waitForFunction(()=>document.querySelector('#commentCount')?.textContent==='2');}; + const close = async()=>{await page.locator('dialog[open] [data-modal-close]').first().click();await page.waitForFunction(()=>!document.querySelector('dialog[open]'));await page.waitForURL(url=>!url.searchParams.has('view'));}; + const decide = accept => page.once('dialog',d=>accept?d.accept():d.dismiss()); + await page.goto('https://qa.example.test/egbim/'); + await page.waitForFunction(()=>document.querySelector('#resultCount').textContent==='총 15건'); + assert.equal(await page.locator('#tab-tova').isDisabled(),true);assert.equal(await page.locator('.inquiry-remote').getAttribute('href'),'https://939.co.kr/saman/'); + await page.screenshot({path:images+'/desktop-list.png',fullPage:true}); + await page.locator('#query').fill('로그임');await page.locator('#searchForm').evaluate(f=>f.requestSubmit());assert.equal(await page.locator('#qaRows tr').count(),5); + await page.locator('#qaRows tr a').first().click();await page.waitForSelector('#detailModal[open]');await close();assert.equal(await page.locator('#query').inputValue(),'로그임'); + await page.locator('#query').fill('');await page.locator('#searchForm').evaluate(f=>f.requestSubmit()); + await page.locator('#pagination [data-page="2"]').first().click();assert.equal(await page.locator('#qaRows tr').count(),5); + await page.locator('#pagination [data-page="1"]').first().click(); + await open(1);assert.equal(await page.locator('#editButton').isVisible(),true);assert.equal(await page.locator('#editForm').isVisible(),false); + assert.equal(await page.locator('[data-detail-attachments] a').getAttribute('href'),'/fixture/manual.pdf'); + assert.equal(await page.locator('[data-comment-edit="'+id(102)+'"]').count(),0); + await page.screenshot({path:images+'/desktop-detail.png'}); + await page.locator('#editButton').click();await page.locator('#editTitle').fill('수정한 제목');decide(false);await page.locator('#editCancel').click();assert.equal(await page.locator('#editForm').isVisible(),true); + await page.locator('#editForm').evaluate(f=>f.requestSubmit());await page.waitForFunction(()=>document.querySelector('[data-detail-title]').textContent==='수정한 제목'); + await page.locator('[data-comment-edit="'+id(101)+'"]').click();await page.locator('[data-comment-edit-input]').fill('수정한 댓글');decide(false);await page.keyboard.press('Escape');assert.equal(await page.locator('[data-comment-edit-input]').inputValue(),'수정한 댓글'); + await page.locator('[data-comment-save]').click();await page.waitForFunction(()=>document.querySelector('.comment-content')?.textContent==='수정한 댓글'); + await page.locator('#commentContent').fill('이미지 댓글');await page.locator('#commentImages').setInputFiles({name:'example.png',mimeType:'image/png',buffer:Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mP8/x8AAwMCAO+jqnsAAAAASUVORK5CYII=','base64')});assert.equal(await page.locator('#commentImagePreview img').count(),1); + await page.locator('#commentForm').evaluate(f=>f.requestSubmit());await page.waitForFunction(()=>document.querySelector('#commentCount').textContent==='3');assert.match(mutations.at(-1).body,/name="attachments"; filename="example.png"/); + decide(true);await page.locator('[data-comment-delete="'+id(202)+'"]').click();await page.waitForFunction(()=>document.querySelector('#commentCount').textContent==='2');await close();assert.match(await page.locator('#qaRows').textContent(),/수정한 제목/); + await page.locator('#writeButton').click();await page.waitForFunction(()=>document.querySelector('#writeDepartment')?.value==='IS3');assert.equal(await page.locator('#writeCompany').inputValue(),'총괄기획&기술개발센터');assert.equal(await page.locator('#writeCompany').getAttribute('readonly'),''); + await page.screenshot({path:images+'/desktop-write.png'}); + await page.keyboard.press('Shift+Tab'); + assert.equal(await page.evaluate(()=>Boolean(document.activeElement.closest('#composeModal'))),true); + await page.keyboard.press('Tab'); + assert.equal(await page.evaluate(()=>Boolean(document.activeElement.closest('#composeModal'))),true); + await page.locator('#category').selectOption('일반문의');await page.locator('#title').fill('등록 테스트');await page.locator('#content').fill('문의 내용');await page.locator('#secret').check();await page.locator('#attachment').setInputFiles([{name:'a.txt',mimeType:'text/plain',buffer:Buffer.from('one')},{name:'b.txt',mimeType:'text/plain',buffer:Buffer.from('two')}]); + decide(false);await page.keyboard.press('Escape');assert.equal(await page.locator('#attachment').evaluate(i=>i.files.length),2); + decide(false);await page.goBack();await page.waitForURL(url=>url.searchParams.get('view')==='write');assert.equal(await page.locator('#title').inputValue(),'등록 테스트'); + failCreate=true;await page.locator('#qaForm').evaluate(f=>f.requestSubmit());await page.waitForFunction(()=>document.querySelector('#formError').textContent.includes('모의 등록 오류'));assert.equal(await page.locator('#attachment').evaluate(i=>i.files.length),2); + let releaseCreate;holdCreate=new Promise(r=>releaseCreate=r);await page.locator('#qaForm').evaluate(f=>f.requestSubmit());await page.waitForFunction(()=>document.querySelector('#submitButton').disabled);await page.keyboard.press('Escape');assert.equal(await page.locator('#composeModal').isVisible(),true);releaseCreate();holdCreate=null; + await page.waitForFunction(()=>document.querySelector('#detailModal[open] [data-detail-title]')?.textContent==='등록 테스트');await page.waitForFunction(()=>document.querySelector('#commentCount')?.textContent==='0'); + assert.match(mutations.at(-1).body,/name="images"; filename="a.txt"/);assert.match(mutations.at(-1).body,/name="images"; filename="b.txt"/);assert.equal(mutations.filter(m=>m.url==='/api/feedbacks'&&m.method==='POST').length,2); + decide(true);await page.locator('#deleteButton').click();await page.waitForFunction(()=>!document.querySelector('dialog[open]'));await page.waitForURL(url=>!url.searchParams.has('view'));assert.equal(await page.locator('#resultCount').textContent(),'총 15건'); + // Direct old addresses and clean browser back/forward. + await page.goto('https://qa.example.test/egbim/detail.html?id='+id(2));await page.waitForFunction(()=>document.querySelector('#detailModal[open] [data-detail-title]')?.textContent==='EG-BIM 로그인 오류 문의');await page.goBack();await page.waitForFunction(()=>!document.querySelector('dialog[open]'));await page.goForward();await page.waitForSelector('#detailModal[open]');await close(); + await page.goto('https://qa.example.test/egbim/write.html');await page.waitForSelector('#composeModal[open]');await page.locator('#title').fill('폐기 확인');decide(false);await page.mouse.click(2,2);assert.equal(await page.locator('#composeModal').isVisible(),true);decide(true);await page.mouse.click(2,2);await page.waitForFunction(()=>!document.querySelector('dialog[open]'));await page.waitForURL(url=>!url.searchParams.has('view')); + // Late detail responses stay in detached modal roots. + slowId=id(4);await page.locator('#qaRows tr[data-id="'+slowId+'"] a').click();await page.waitForFunction(()=>document.querySelector('#detailModal[open]')); + for(let tries=0;!releaseSlow&&tries<400;tries++) await new Promise(r=>setTimeout(r,20));assert.equal(typeof releaseSlow,'function');await close();await open(2);releaseSlow();slowId='';await page.waitForTimeout(100);assert.equal(await page.locator('[data-detail-title]').textContent(),'EG-BIM 로그인 오류 문의');await close(); + records[0].completion_requested=true;await page.locator('#qaRows tr[data-id="'+records[0].id+'"] a').click();await page.waitForSelector('#completionButton:visible');await page.locator('#completionButton').click();await page.waitForSelector('#completionDoneNotice:visible');await close();assert.equal(await page.locator('#qaRows tr').first().locator('.inquiry-status').textContent(),'완료'); + // Repeat open/close: one request per submit, no previous draft retained. + for(let n=0;n<3;n++){await page.locator('#writeButton').click();assert.equal(await page.locator('#title').inputValue(),'');await close();} + await page.locator('#writeButton').click();await page.locator('#category').selectOption('오류문의');await page.locator('#title').fill('JSON 등록');await page.locator('#content').fill('첨부 없이 등록');await page.locator('#qaForm').evaluate(f=>f.requestSubmit());await page.waitForFunction(()=>document.querySelector('[data-detail-title]')?.textContent==='JSON 등록');assert.match(mutations.at(-1).type,/application\/json/);assert.equal(mutations.filter(m=>m.url==='/api/feedbacks'&&m.method==='POST').length,3);await close(); + await page.setViewportSize({width:390,height:844});await page.evaluate(()=>scrollTo(0,0));await page.screenshot({path:images+'/mobile-list.png',fullPage:true}); + assert.equal(await page.evaluate(()=>document.documentElement.scrollWidth<=innerWidth),true); + await page.locator('#writeButton').click();assert.equal(await page.locator('.inquiry-form-grid').evaluate(e=>getComputedStyle(e).gridTemplateColumns.split(' ').length),1);await page.screenshot({path:images+'/mobile-write.png'}); + assert.equal(await page.locator('#composeModal').evaluate(d=>d.scrollHeight>d.clientHeight),true);await close(); + await page.locator('#qaRows tr a').first().evaluate(a=>a.click());await page.waitForSelector('#commentForm');await page.screenshot({path:images+'/mobile-detail.png'});assert.equal(await page.locator('#detailModal').evaluate(d=>d.getBoundingClientRect().right<=innerWidth),true);await close(); + await page.goto('https://qa.example.test/egbim/?view=detail&id=missing'); + await page.waitForFunction(()=>document.querySelector('[data-detail-title]')?.textContent==='문의 내용을 찾을 수 없습니다.'); + assert.equal(await page.locator('#editButton').isVisible(),false); + assert.equal(await page.locator('#commentForm').isVisible(),false); + await close(); + assert.deepEqual(errors,[]);console.log(JSON.stringify({result:'PASS',mutations:mutations.length,screenshots:images}));await context.close(); + for (const scenario of ['owner', 'other', 'same-name-no-id', 'anonymous-with-stale-user', 'session-unavailable', 'delayed-session']) { + const context = await browser.newContext(); + const page = await context.newPage(); + const viewer = scenario === 'owner' || scenario === 'delayed-session' ? OWNER : 'ee2f39ac-fe52-4cfb-b4e3-4ae1d114c916'; + const currentUser = { requesterId: viewer, userUuid: viewer, ssoSubject: viewer, tenantId: 'fixture-tenant', name: '동명이인', email: 'fixture@example.test', loginId: 'fixture@example.test' }; + let releaseSession; + const sessionGate = new Promise(resolve => { releaseSession = resolve; }); + if (scenario.includes('stale') || scenario === 'session-unavailable') await page.addInitScript(({ OWNER }) => { + sessionStorage.setItem('descopeUserId', OWNER); + sessionStorage.setItem('loginId', 'fixture@example.test'); + sessionStorage.setItem('userName', '동명이인'); + }, { OWNER }); + await page.route('**/*', async route => { + const url = new URL(route.request().url()); + if (url.origin !== 'https://qa.example.test') return route.abort(); + if (route.request().method() !== 'GET') throw new Error('Unexpected mutation during visibility test'); + const send = body => route.fulfill({ contentType: 'application/json', body: JSON.stringify(body) }); + if (url.pathname === '/auth/session') { + if (scenario === 'delayed-session') await sessionGate; + if (scenario === 'session-unavailable') return route.fulfill({ status: 503, contentType: 'application/json', body: '{}' }); + return send(scenario === 'anonymous-with-stale-user' ? { authenticated: false, user: null } : { authenticated: true, user: currentUser }); + } + if (url.pathname === '/api/feedbacks') return send({items: [], meta: {total: 0}}); + if (url.pathname.endsWith('/comments')) return send({ comments: [ + { id: id(101), author_id: viewer, author_name: '동명이인', content: '본인 댓글' }, + { id: id(102), author_id: scenario === 'same-name-no-id' ? '' : 'another-user', author_name: '동명이인', content: '다른 사람 댓글' } + ] }); + if (url.pathname === '/api/feedbacks/' + id(1)) return send({ feedback: { + id: id(1), requester_id: scenario === 'same-name-no-id' ? '' : OWNER, requester_name: '동명이인', title: '테스트 문의', contents: '본문', Category: 'GENERAL_QNA' + } }); + try { + const path = url.pathname.endsWith('/') ? url.pathname + 'index.html' : url.pathname; + const contentType = path.endsWith('.css') ? 'text/css' : path.endsWith('.js') ? 'application/javascript' : path.endsWith('.html') ? 'text/html' : 'application/octet-stream'; + return route.fulfill({ contentType, body: await readFile(root + path) }); + } catch { return route.abort(); } + }); + await page.goto('https://qa.example.test/egbim/?view=detail&id=' + id(1), { waitUntil: 'domcontentloaded' }); + if (scenario === 'delayed-session') { + await page.waitForFunction(() => document.querySelector('[data-detail-title]').textContent === '테스트 문의'); + assert.equal(await page.locator('#editButton').isVisible(), false, 'edit must be hidden while session is pending'); + assert.equal(await page.locator('#deleteButton').isVisible(), false, 'delete must be hidden while session is pending'); + releaseSession(); + } + await page.waitForFunction(() => document.querySelector('#commentCount').textContent === '2'); + await page.waitForTimeout(50); + const result = { + scenario, postEdit: await page.locator('#editButton').isVisible(), postDelete: await page.locator('#deleteButton').isVisible(), + ownComment: await page.locator('[data-comment-edit="' + id(101) + '"]').isVisible(), + otherCommentCount: await page.locator('[data-comment-edit="' + id(102) + '"], [data-comment-delete="' + id(102) + '"]').count() + }; + console.log(JSON.stringify(result)); + const expectedPost = scenario === 'owner' || scenario === 'delayed-session'; + const expectedComment = !['anonymous-with-stale-user', 'session-unavailable'].includes(scenario); + assert.equal(result.postEdit, expectedPost, scenario + ' post edit'); + assert.equal(result.postDelete, expectedPost, scenario + ' post delete'); + assert.equal(result.ownComment, expectedComment, scenario + ' own comment'); + assert.equal(result.otherCommentCount, 0, scenario + ' other comment'); + if (scenario === 'owner') { + await page.locator('#editButton').click(); + assert.equal(await page.locator('#editForm').isVisible(), true); + await page.locator('#editCancel').click(); + assert.equal(await page.locator('#editForm').isVisible(), false); + } + await context.close(); + } + +}finally{await browser.close();} diff --git a/tests/page-entry.test.js b/tests/page-entry.test.js new file mode 100644 index 0000000..51a85f4 --- /dev/null +++ b/tests/page-entry.test.js @@ -0,0 +1,22 @@ +import test from 'node:test'; +import assert from 'node:assert/strict'; +import { readFileSync } from 'node:fs'; +import vm from 'node:vm'; + +const script = readFileSync(new URL('../assets/inquiry/entry.js', import.meta.url), 'utf8'); + +for (const [name, source, expected] of [ + ['write link', '/egbim/write.html', '/egbim/?view=write'], + ['detail link preserves its feedback ID', '/egbim/detail.html?id=feedback-uuid', '/egbim/?id=feedback-uuid&view=detail'], + ['write link cannot inherit a detail ID', '/egbim/write.html?view=detail&id=old&check=1#form', '/egbim/?view=write&check=1#form'], + ['detail link preserves encoded IDs and query values', '/egbim/detail.html?id=a%26b&check=x%20y', '/egbim/?id=a%26b&check=x+y&view=detail'] +]) { + test(name + ' opens the matching list modal using replace navigation', () => { + const navigations = []; + vm.runInNewContext(script, { + URL, + window: { location: { href: 'https://qa.example.test' + source, replace: value => navigations.push(value) } } + }); + assert.deepEqual(navigations, ['https://qa.example.test' + expected]); + }); +} diff --git a/tests/production-deployment.test.js b/tests/production-deployment.test.js index b8c9a97..2043422 100644 --- a/tests/production-deployment.test.js +++ b/tests/production-deployment.test.js @@ -103,10 +103,17 @@ test('production uploader rejects root uploads and sends every file to the selec mkdirSync(join(dir, 'assets')); for (const file of ['index.html', 'write.html', 'detail.html', 'app.js', 'config.js']) writeFileSync(join(dir, 'egbim', file), 'fixture'); writeFileSync(join(dir, 'assets/styles.css'), 'body {}'); + mkdirSync(join(dir, 'assets/inquiry')); + writeFileSync(join(dir, 'assets/inquiry/inquiry-hero.webp'), 'fixture-image'); + writeFileSync(join(dir, 'assets/inquiry/entry.js'), 'fixture-entry'); const uploaded = spawnSync('bash', [script, 'baron-qa-write', 'wrangler.production.toml', 'releases/' + SHA], { env, cwd: dir, encoding: 'utf8' }); assert.equal(uploaded.status, 0, uploaded.stderr); const commands = readFileSync(capture, 'utf8').trim().split('\n').map(JSON.parse); - assert.equal(commands.length, 6); + assert.equal(commands.length, 8); + const heroUpload = commands.find(command => command.args[5].endsWith('/assets/inquiry/inquiry-hero.webp')); + assert.equal(heroUpload.args[heroUpload.args.indexOf('--content-type') + 1], 'image/webp'); + const entryUpload = commands.find(command => command.args[5].endsWith('/assets/inquiry/entry.js')); + assert.equal(entryUpload.args[entryUpload.args.indexOf('--content-type') + 1], 'application/javascript'); for (const command of commands) { assert.ok(command.args[5].startsWith('baron-qa-write/releases/' + SHA + '/')); assert.ok(command.args.includes('wrangler.production.toml'));