From 5f25a55c3c9335037c3677dc19f30d870abd4b55 Mon Sep 17 00:00:00 2001 From: dsyoon Date: Wed, 15 Apr 2026 16:57:29 +0900 Subject: [PATCH] =?UTF-8?q?=ED=9A=8C=EC=9D=98=EB=A1=9D=20AI:=20=EC=B2=B4?= =?UTF-8?q?=ED=81=AC=EB=A6=AC=EC=8A=A4=ED=8A=B8=20=EC=B2=B4=ED=81=AC?= =?UTF-8?q?=EB=B0=95=EC=8A=A4=20=EC=A0=9C=EA=B1=B0,=20README=EC=97=90=20?= =?UTF-8?q?=EA=B8=B0=EB=B3=B8=20=EC=B6=94=EA=B0=80=20=EC=A7=80=EC=8B=9C=20?= =?UTF-8?q?=EC=9C=84=EC=B9=98=20=EC=95=88=EB=82=B4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Made-with: Cursor --- README.md | 1 + public/styles.css | 19 ------------------- views/meeting-minutes.ejs | 5 ----- 3 files changed, 1 insertion(+), 24 deletions(-) diff --git a/README.md b/README.md index e6e32d3..20919a6 100644 --- a/README.md +++ b/README.md @@ -41,6 +41,7 @@ - 학습센터 UI (좌측 메뉴 + 상단 헤더 + 강의 카드 레이아웃) - **AI 탐색** (`/ai-explore`): 전체 너비 레이아웃, AI 서비스 카드(프롬프트·회의록 등). 검색창에 **「프롬프트」**가 포함된 채 검색(Enter) 시 프롬프트 라이브러리로 이동 +- **회의록 AI** (`/ai-explore/meeting-minutes`): 출력 형식 패널에서 체크리스트는 별도 체크박스 없이 시스템 프롬프트에 항상 반영됩니다(업무 체크리스트 AI 연동). 기본 **추가 지시** 문구는 `views/meeting-minutes.ejs`의 `mmDefaultCustomInstructions`입니다. - **대시보드** (`/dashboard`): AI 탐색과 유사한 카드 그리드·검색으로 대시보드를 모아 표시. 첫 카드 **경영성과 대시보드**는 `/dashboard/business-performance`로 연결 - **경영성과 대시보드** (`/dashboard/business-performance`): **위쪽 대시보드 조회**(Chart.js 인라인), **아래 엑셀 업로드**(`.xlsx`, 매출일보 시트) 순서. 본문은 AI 프롬프트 페이지와 동일하게 **`main.container.container-ai-full`**(전체 너비·좌우 24px)로 맞춤. 대시보드 상단 **연도·분기**로 `mgmt_perf_uploads`에 저장된 해당 기간 **최신 스냅샷**을 불러오며, 쿼리 **`?year=2026&quarter=1`** 또는 폼 조회와 동일. 해당 기간 업로드가 없으면 기본 JSON 샘플을 쓰고 안내 문구를 표시합니다. `public/mgmt-perf/dashboard.css`에 있던 **범용 `.container { background: white }`** 는 앱 페이지에서 `main.container`까지 적용되어 회색 본문이 가려졌으므로 제거하고, 흰 카드는 **`.mgmt-perf-embed .container`** 만 사용합니다. 업로드는 DB(`mgmt_perf_uploads` / `mgmt_perf_snapshots`) 또는 DB 미연결 시 `data/mgmt-perf-last-state.json`에 스냅샷 저장. 최근 업로드 행 **`DELETE /api/mgmt-perf/upload/:id`** 로 삭제(PG는 CASCADE, 파일 전용 모드는 `id=file`). 단독 임베드 페이지는 `/dashboard/business-performance/embed`(본문에 `body.mgmt-perf-standalone`으로 어두운 배경). Express에서 **`/mgmt-perf/*` → `public/mgmt-perf/`** 정적 제공이 등록되어 있어 `dashboard-app.js`·`chart.umd.min.js`(CDN 대신 동봉)가 항상 같은 오리진에서 로드됩니다. 업로드 시 **한글 파일명**은 multer 기본(`defParamCharset` 생략 시 latin1)으로 온 `originalname`을 **`lib/decode-upload-filename.js`**의 `decodeUploadFilename`으로 보정합니다(`decodeURIComponent(escape(...))` 우선, 이어서 `Buffer` latin1→utf8). Busboy에 `defParamCharset: 'utf8'`를 켜면 이중 디코딩으로 깨질 수 있어 두지 않습니다. 탭 전환·차트 렌더는 **ASCII 섹션 id**(`mgmt-sec-sales` 등)와 `state.currentSection`으로 동기화합니다. 리버스 프록시 사용 시 업로드 실패하면 **`client_max_body_size`**(예: 64m)와 **`/api/`·`/mgmt-perf/` → Node** 전달 여부를 확인. 엑셀 집계 치환은 `npm install`로 `xlsx` 설치 후 서버 재시작. - **경영성과 데이터 확인**: 브라우저에서 `GET /api/mgmt-perf/status`(JSON)로 최근 스냅샷의 `payloadKeys`, `_uploadMeta`(행 수 등)를 확인할 수 있습니다. **현재 구현**은 엑셀에서 **매출일보 행 수·시트명만** `payload._uploadMeta`에 넣고, **차트 수치는 기본 시드 JSON**(`data/mgmt-perf-default-payload.json`)을 씁니다. 5,000행이어도 차트가 엑셀 집계와 일치하려면 **별도 집계·매핑 로직**이 필요합니다. diff --git a/public/styles.css b/public/styles.css index 5919bc7..b362ac5 100644 --- a/public/styles.css +++ b/public/styles.css @@ -2480,25 +2480,6 @@ body.ai-explore-dev-guest .search-input:disabled { .mm-checkbox-item span { flex: 0 1 auto; } -.mm-checkbox-item-locked { - cursor: default; -} -.mm-checkbox-item-locked input[type="checkbox"] { - opacity: 0.85; - cursor: not-allowed; -} -.mm-checkbox-badge { - display: inline-block; - margin-left: 4px; - padding: 1px 6px; - font-size: 11px; - font-weight: 600; - color: #0f766e; - background: #ecfdf5; - border: 1px solid #99f6e4; - border-radius: 4px; - vertical-align: middle; -} .mm-custom-block { margin-top: 18px; } diff --git a/views/meeting-minutes.ejs b/views/meeting-minutes.ejs index 799e918..4af5b61 100644 --- a/views/meeting-minutes.ejs +++ b/views/meeting-minutes.ejs @@ -59,10 +59,6 @@ -
@@ -431,7 +427,6 @@ document.getElementById('mmIncAtt').checked = p.includeAttendees !== false; document.getElementById('mmIncSum').checked = p.includeSummary !== false; document.getElementById('mmIncAct').checked = p.includeActionItems !== false; - document.getElementById('mmIncChk').checked = true; var saved = (p.customInstructions && String(p.customInstructions).trim()) || ''; document.getElementById('mmCustomInstr').value = saved || MM_DEFAULT_CUSTOM_INSTRUCTIONS; });