Files
sam-docs/presentations/weekly-report-20260322.cjs

296 lines
20 KiB
JavaScript

const path = require('path');
const fs = require('fs');
module.paths.unshift(path.join(require('os').homedir(), '.claude/skills/pptx-skill/scripts/node_modules'));
const PptxGenJS = require('pptxgenjs');
const C = {
white: 'FFFFFF', bg: 'F8FAFC', cardBg: 'FFFFFF', headerBg: 'F1F5F9',
title: '0F172A', text: '334155', textMuted: '64748B', textLight: '94A3B8',
primary: '2563EB', primaryLight: 'DBEAFE',
green: '16A34A', greenLight: 'DCFCE7', greenDark: '15803D',
orange: 'EA580C', orangeLight: 'FFEDD5',
red: 'DC2626', redLight: 'FEE2E2',
purple: '7C3AED', purpleLight: 'EDE9FE',
teal: '0D9488', tealLight: 'CCFBF1',
border: 'E2E8F0', divider: 'CBD5E1', dark: '1E293B',
};
// BI 로고 base64
const biBlackPath = '/home/aweso/sam/docs/assets/bi/sam_bi_black.png';
const biWhitePath = '/home/aweso/sam/docs/assets/bi/sam_bi_white.png';
const biBlack = fs.existsSync(biBlackPath) ? 'image/png;base64,' + fs.readFileSync(biBlackPath).toString('base64') : null;
const biWhite = fs.existsSync(biWhitePath) ? 'image/png;base64,' + fs.readFileSync(biWhitePath).toString('base64') : null;
function addFooter(slide, pageNum, totalPages) {
slide.addShape(slide._slideLayout ? 'rect' : 'rect', { x: 0, y: 5.18, w: 10, h: 0.01, fill: { color: C.border } });
slide.addShape('rect', { x: 0, y: 5.19, w: 10, h: 0.435, fill: { color: C.headerBg } });
slide.addText('SAM 주간 개발 실적 | (주)코드브릿지엑스', { x: 0.5, y: 5.22, w: 6, h: 0.35, fontSize: 8, color: C.textLight, fontFace: 'Arial' });
slide.addText(`${pageNum} / ${totalPages}`, { x: 8, y: 5.22, w: 1.5, h: 0.35, fontSize: 8, color: C.textLight, align: 'right', fontFace: 'Arial' });
}
function addPageTitle(slide, title, subtitle) {
slide.addShape('rect', { x: 0.5, y: 0.35, w: 0.06, h: 0.4, fill: { color: C.primary } });
slide.addText(title, { x: 0.7, y: 0.3, w: 5, h: 0.5, fontSize: 18, bold: true, color: C.title, fontFace: 'Arial' });
if (subtitle) {
slide.addText(subtitle, { x: 0.7, y: 0.7, w: 7, h: 0.3, fontSize: 10, color: C.textMuted, fontFace: 'Arial' });
}
}
async function main() {
const pres = new PptxGenJS();
pres.defineLayout({ name: 'CUSTOM_16x9', width: 10, height: 5.625 });
pres.layout = 'CUSTOM_16x9';
const totalPages = 6;
// ========== 슬라이드 1: 표지 ==========
const s1 = pres.addSlide();
s1.background = { fill: C.white };
s1.addShape('rect', { x: 0, y: 0, w: 0.08, h: 5.625, fill: { color: C.primary } });
s1.addShape('rect', { x: 0, y: 0, w: 10, h: 0.08, fill: { color: C.primary } });
if (biBlack) s1.addImage({ data: biBlack, x: 0.6, y: 0.4, w: 0.7, h: 0.7 });
s1.addText('SAM 프로젝트', { x: 0.5, y: 1.6, w: 9, h: 0.6, fontSize: 14, color: C.textMuted, fontFace: 'Arial' });
s1.addText('주간 개발 실적 보고', { x: 0.5, y: 2.1, w: 9, h: 0.9, fontSize: 32, bold: true, color: C.title, fontFace: 'Arial' });
s1.addShape('rect', { x: 0.5, y: 3.0, w: 2, h: 0.04, fill: { color: C.primary } });
s1.addText('2026. 03. 15 (토) ~ 03. 22 (토)', { x: 0.5, y: 3.2, w: 5, h: 0.4, fontSize: 14, color: C.text, fontFace: 'Arial' });
s1.addText('R&D실', { x: 0.5, y: 3.6, w: 5, h: 0.35, fontSize: 12, color: C.textMuted, fontFace: 'Arial' });
s1.addShape('rect', { x: 0, y: 5.19, w: 10, h: 0.435, fill: { color: C.headerBg } });
s1.addText('(주)코드브릿지엑스 | SAM (Smart Automation Management)', { x: 0.5, y: 5.22, w: 8, h: 0.35, fontSize: 8, color: C.textLight, fontFace: 'Arial' });
// ========== 슬라이드 2: 주간 핵심 성과 요약 ==========
const s2 = pres.addSlide();
s2.background = { fill: C.bg };
addPageTitle(s2, '주간 핵심 성과 요약', '2026.03.15 ~ 03.22 | 7일간 개발 성과');
// 상단 KPI 카드 4개
const kpis = [
{ label: '총 커밋', value: '541', sub: 'MNG 160 + API 160 + Docs 221', color: C.primary, bg: C.primaryLight },
{ label: '신규 기능', value: '121', sub: 'feat 커밋 (MNG 50 + API 71)', color: C.green, bg: C.greenLight },
{ label: '버그 수정', value: '174', sub: 'fix 커밋 (MNG 100 + API 74)', color: C.orange, bg: C.orangeLight },
{ label: '문서 성장', value: '+32%', sub: '205,490 → 271,525 행', color: C.purple, bg: C.purpleLight },
];
kpis.forEach((k, i) => {
const x = 0.5 + i * 2.3;
s2.addShape('roundRect', { x, y: 1.15, w: 2.1, h: 1.45, rectRadius: 0.1, fill: { color: C.cardBg }, line: { color: C.border, width: 0.5 } });
s2.addShape('rect', { x, y: 1.15, w: 2.1, h: 0.06, fill: { color: k.color } });
s2.addText(k.label, { x: x + 0.15, y: 1.3, w: 1.8, h: 0.25, fontSize: 9, color: C.textMuted, fontFace: 'Arial' });
s2.addText(k.value, { x: x + 0.15, y: 1.55, w: 1.8, h: 0.5, fontSize: 28, bold: true, color: k.color, fontFace: 'Arial' });
s2.addText(k.sub, { x: x + 0.15, y: 2.1, w: 1.8, h: 0.3, fontSize: 7.5, color: C.textMuted, fontFace: 'Arial' });
});
// 하단 주요 완료 항목
s2.addShape('roundRect', { x: 0.5, y: 2.85, w: 9, h: 2.15, rectRadius: 0.1, fill: { color: C.cardBg }, line: { color: C.border, width: 0.5 } });
s2.addText('주요 완료 항목', { x: 0.7, y: 2.95, w: 3, h: 0.3, fontSize: 11, bold: true, color: C.title, fontFace: 'Arial' });
const achievements = [
{ icon: '✅', text: '바로빌 전체 서비스 이관 완료 (12/12 영역 100%)', tag: '이관', tagColor: C.green, tagBg: C.greenLight },
{ icon: '✅', text: '생산관리 자재투입·문서양식·현황판 종합 개선', tag: '생산', tagColor: C.primary, tagBg: C.primaryLight },
{ icon: '✅', text: '조직도 관리 API 이관 (8개 엔드포인트)', tag: 'API', tagColor: C.teal, tagBg: C.tealLight },
{ icon: '✅', text: '온보딩 가이드 시스템 구현 (Driver.js)', tag: 'R&D', tagColor: C.purple, tagBg: C.purpleLight },
{ icon: '✅', text: 'BOM V2 범용 멀티테넌트 아키텍처 설계', tag: '설계', tagColor: C.orange, tagBg: C.orangeLight },
];
achievements.forEach((a, i) => {
const y = 3.35 + i * 0.33;
s2.addText(a.icon + ' ' + a.text, { x: 0.7, y, w: 6.5, h: 0.3, fontSize: 10, color: C.text, fontFace: 'Arial' });
s2.addShape('roundRect', { x: 7.5, y: y + 0.03, w: 0.65, h: 0.22, rectRadius: 0.03, fill: { color: a.tagBg } });
s2.addText(a.tag, { x: 7.5, y: y + 0.03, w: 0.65, h: 0.22, fontSize: 7, bold: true, color: a.tagColor, align: 'center', fontFace: 'Arial' });
});
addFooter(s2, 2, totalPages);
// ========== 슬라이드 3: 프로젝트별 커밋 상세 ==========
const s3 = pres.addSlide();
s3.background = { fill: C.bg };
addPageTitle(s3, '프로젝트별 개발 현황', '커밋 유형 분석 및 주요 작업 영역');
// MNG 카드
s3.addShape('roundRect', { x: 0.5, y: 1.1, w: 4.3, h: 3.9, rectRadius: 0.1, fill: { color: C.cardBg }, line: { color: C.border, width: 0.5 } });
s3.addShape('rect', { x: 0.5, y: 1.1, w: 4.3, h: 0.45, fill: { color: C.dark } });
s3.addText('MNG (백오피스)', { x: 0.7, y: 1.13, w: 2, h: 0.4, fontSize: 12, bold: true, color: C.white, fontFace: 'Arial' });
s3.addShape('roundRect', { x: 3.6, y: 1.17, w: 0.9, h: 0.25, rectRadius: 0.03, fill: { color: C.primary } });
s3.addText('160 커밋', { x: 3.6, y: 1.17, w: 0.9, h: 0.25, fontSize: 8, bold: true, color: C.white, align: 'center', fontFace: 'Arial' });
const mngItems = [
['feat (신규기능)', '50', C.green], ['fix (버그수정)', '100', C.orange],
['refactor', '6', C.primary], ['chore/style', '4', C.textMuted],
];
mngItems.forEach((item, i) => {
const y = 1.7 + i * 0.38;
const bgColor = i % 2 === 0 ? C.headerBg : C.white;
s3.addShape('rect', { x: 0.6, y, w: 4.1, h: 0.35, fill: { color: bgColor } });
s3.addText(item[0], { x: 0.8, y, w: 2.5, h: 0.35, fontSize: 10, color: C.text, fontFace: 'Arial' });
s3.addText(item[1] + '건', { x: 3.2, y, w: 1.2, h: 0.35, fontSize: 10, bold: true, color: item[2], align: 'right', fontFace: 'Arial' });
});
s3.addText('주요 작업', { x: 0.7, y: 3.35, w: 2, h: 0.3, fontSize: 9, bold: true, color: C.textMuted, fontFace: 'Arial' });
const mngWorks = ['문서양식 R2 이미지 프록시 전환', '절곡품 기초자료 복사·코드관리', '재무 계정과목 관리 개선', '온보딩 도움말 기능 (Driver.js)'];
mngWorks.forEach((w, i) => {
s3.addText('• ' + w, { x: 0.8, y: 3.65 + i * 0.3, w: 3.8, h: 0.28, fontSize: 9, color: C.text, fontFace: 'Arial' });
});
// API 카드
s3.addShape('roundRect', { x: 5.2, y: 1.1, w: 4.3, h: 3.9, rectRadius: 0.1, fill: { color: C.cardBg }, line: { color: C.border, width: 0.5 } });
s3.addShape('rect', { x: 5.2, y: 1.1, w: 4.3, h: 0.45, fill: { color: C.dark } });
s3.addText('API (서비스)', { x: 5.4, y: 1.13, w: 2, h: 0.4, fontSize: 12, bold: true, color: C.white, fontFace: 'Arial' });
s3.addShape('roundRect', { x: 8.3, y: 1.17, w: 0.9, h: 0.25, rectRadius: 0.03, fill: { color: C.green } });
s3.addText('160 커밋', { x: 8.3, y: 1.17, w: 0.9, h: 0.25, fontSize: 8, bold: true, color: C.white, align: 'center', fontFace: 'Arial' });
const apiItems = [
['feat (신규기능)', '71', C.green], ['fix (버그수정)', '74', C.orange],
['refactor', '5', C.primary], ['chore', '3', C.textMuted],
];
apiItems.forEach((item, i) => {
const y = 1.7 + i * 0.38;
const bgColor = i % 2 === 0 ? C.headerBg : C.white;
s3.addShape('rect', { x: 5.3, y, w: 4.1, h: 0.35, fill: { color: bgColor } });
s3.addText(item[0], { x: 5.5, y, w: 2.5, h: 0.35, fontSize: 10, color: C.text, fontFace: 'Arial' });
s3.addText(item[1] + '건', { x: 7.9, y, w: 1.2, h: 0.35, fontSize: 10, bold: true, color: item[2], align: 'right', fontFace: 'Arial' });
});
s3.addText('주요 작업', { x: 5.4, y: 3.35, w: 2, h: 0.3, fontSize: 9, bold: true, color: C.textMuted, fontFace: 'Arial' });
const apiWorks = ['바로빌 구독/과금/카카오톡 이관', '조직도 관리 8개 엔드포인트 이관', '재공품 STOCK 자재매칭·자동완료', '재고 거래이력 API 추가'];
apiWorks.forEach((w, i) => {
s3.addText('• ' + w, { x: 5.5, y: 3.65 + i * 0.3, w: 3.8, h: 0.28, fontSize: 9, color: C.text, fontFace: 'Arial' });
});
addFooter(s3, 3, totalPages);
// ========== 슬라이드 4: 문서 성장 현황 ==========
const s4 = pres.addSlide();
s4.background = { fill: C.bg };
addPageTitle(s4, '문서 성장 현황', 'sam/docs 저장소 — 1주간 +32% 성장');
// 비교 카드
const compareCards = [
{ label: '3/15 (1주 전)', value: '205,490', unit: '행', color: C.textMuted, accent: C.border },
{ label: '3/22 (현재)', value: '271,525', unit: '행', color: C.primary, accent: C.primary },
{ label: '증가분', value: '+66,035', unit: '행 (+32%)', color: C.green, accent: C.green },
];
compareCards.forEach((c, i) => {
const x = 0.5 + i * 3.1;
s4.addShape('roundRect', { x, y: 1.15, w: 2.9, h: 1.2, rectRadius: 0.1, fill: { color: C.cardBg }, line: { color: C.border, width: 0.5 } });
s4.addShape('rect', { x, y: 1.15, w: 2.9, h: 0.05, fill: { color: c.accent } });
s4.addText(c.label, { x: x + 0.15, y: 1.25, w: 2.6, h: 0.25, fontSize: 9, color: C.textMuted, fontFace: 'Arial' });
s4.addText(c.value, { x: x + 0.15, y: 1.5, w: 2.6, h: 0.45, fontSize: 24, bold: true, color: c.color, fontFace: 'Arial' });
s4.addText(c.unit, { x: x + 0.15, y: 1.95, w: 2.6, h: 0.2, fontSize: 9, color: C.textMuted, fontFace: 'Arial' });
});
// 파일 통계
s4.addShape('roundRect', { x: 0.5, y: 2.6, w: 4.3, h: 2.4, rectRadius: 0.1, fill: { color: C.cardBg }, line: { color: C.border, width: 0.5 } });
s4.addText('파일 통계', { x: 0.7, y: 2.7, w: 2, h: 0.35, fontSize: 11, bold: true, color: C.title, fontFace: 'Arial' });
const fileStats = [
['전체 md 파일', '700개', C.primary],
['신규 추가 파일', '202개', C.green],
['수정된 파일', '98개', C.orange],
['docs 커밋 수', '221개', C.purple],
['일 평균 성장', '9,400행/일', C.teal],
];
fileStats.forEach((fs, i) => {
const y = 3.1 + i * 0.35;
const bgColor = i % 2 === 0 ? C.headerBg : C.white;
s4.addShape('rect', { x: 0.6, y, w: 4.1, h: 0.32, fill: { color: bgColor } });
s4.addText(fs[0], { x: 0.8, y, w: 2.2, h: 0.32, fontSize: 9.5, color: C.text, fontFace: 'Arial' });
s4.addText(fs[1], { x: 3, y, w: 1.5, h: 0.32, fontSize: 10, bold: true, color: fs[2], align: 'right', fontFace: 'Arial' });
});
// 폴더별 행수 차트 (수평 바)
s4.addShape('roundRect', { x: 5.2, y: 2.6, w: 4.3, h: 2.4, rectRadius: 0.1, fill: { color: C.cardBg }, line: { color: C.border, width: 0.5 } });
s4.addText('폴더별 비중 (Top 5)', { x: 5.4, y: 2.7, w: 3, h: 0.35, fontSize: 11, bold: true, color: C.title, fontFace: 'Arial' });
const folders = [
{ name: 'dev/', lines: 132717, pct: 49, color: C.primary },
{ name: 'projects/', lines: 38870, pct: 14, color: C.green },
{ name: 'features/', lines: 25041, pct: 9, color: C.purple },
{ name: 'plans/', lines: 17356, pct: 6, color: C.orange },
{ name: 'frontend/', lines: 14055, pct: 5, color: C.teal },
];
folders.forEach((f, i) => {
const y = 3.15 + i * 0.35;
s4.addText(f.name, { x: 5.4, y, w: 1.2, h: 0.3, fontSize: 8.5, color: C.text, fontFace: 'Arial' });
const barW = Math.max(0.2, (f.pct / 50) * 2.2);
s4.addShape('roundRect', { x: 6.65, y: y + 0.02, w: barW, h: 0.24, rectRadius: 0.04, fill: { color: f.color } });
s4.addText(f.pct + '%', { x: 6.65 + barW + 0.1, y, w: 0.6, h: 0.3, fontSize: 8, bold: true, color: f.color, fontFace: 'Arial' });
});
addFooter(s4, 4, totalPages);
// ========== 슬라이드 5: 주요 기능 상세 ==========
const s5 = pres.addSlide();
s5.background = { fill: C.bg };
addPageTitle(s5, '주요 개발 내역 상세', '이번 주 핵심 작업 영역별 세부 내용');
const areas = [
{
title: '바로빌 이관 완료', color: C.green, bg: C.greenLight,
items: ['전체 12개 영역 100% 이관 완료', '구독/과금/사용량 API 이관', '카카오톡/SMS 발송 API 이관', 'CEO 설명용 서비스 출시 계획 PPT'],
},
{
title: '생산관리 개선', color: C.primary, bg: C.primaryLight,
items: ['자재투입 dynamic_bom 자동 생성', '검사완료→WO→수주 자동 완료', '작업지시 bending_info fallback', '문서양식 R2 presigned URL 전환'],
},
{
title: '절곡품 관리', color: C.orange, bg: C.orangeLight,
items: ['기초자료 복사·코드 관리 기능', 'Canvas 이미지 R2 CORS 프록시', '품목코드 BD-XX.nnn 표시', 'LOT 재고생산 API 명세 작성'],
},
{
title: 'R&D / 기획', color: C.purple, bg: C.purpleLight,
items: ['온보딩 가이드 시스템 (Driver.js)', 'BOM V2 범용 아키텍처 설계', '특허 변리사 브리핑 자료 작성', 'SAM 슈퍼 솔루션 비전 문서'],
},
];
areas.forEach((area, i) => {
const x = 0.5 + (i % 2) * 4.7;
const y = 1.1 + Math.floor(i / 2) * 2.1;
s5.addShape('roundRect', { x, y, w: 4.5, h: 1.95, rectRadius: 0.1, fill: { color: C.cardBg }, line: { color: C.border, width: 0.5 } });
s5.addShape('roundRect', { x: x + 0.12, y: y + 0.12, w: 1.6, h: 0.28, rectRadius: 0.04, fill: { color: area.bg } });
s5.addText(area.title, { x: x + 0.12, y: y + 0.12, w: 1.6, h: 0.28, fontSize: 9, bold: true, color: area.color, align: 'center', fontFace: 'Arial' });
area.items.forEach((item, j) => {
s5.addText('• ' + item, { x: x + 0.2, y: y + 0.52 + j * 0.32, w: 4.1, h: 0.3, fontSize: 9, color: C.text, fontFace: 'Arial' });
});
});
addFooter(s5, 5, totalPages);
// ========== 슬라이드 6: 다음 주 계획 ==========
const s6 = pres.addSlide();
s6.background = { fill: C.bg };
addPageTitle(s6, '다음 주 계획', '2026.03.23 ~ 03.29 | 주요 예정 작업');
s6.addShape('roundRect', { x: 0.5, y: 1.1, w: 9, h: 3.9, rectRadius: 0.1, fill: { color: C.cardBg }, line: { color: C.border, width: 0.5 } });
const nextWeek = [
{ priority: '🔴', category: '이관', task: '바로빌 서비스 출시 준비 (테넌트 온보딩 절차 확립)', tag: '필수', tagColor: C.red, tagBg: C.redLight },
{ priority: '🔴', category: '생산', task: '재공품 작업자화면 종합 개선 운영 반영', tag: '필수', tagColor: C.red, tagBg: C.redLight },
{ priority: '🟡', category: 'QA', task: 'QA V2 점검 계속 진행 (현재 72%, 목표 90%)', tag: '중요', tagColor: C.orange, tagBg: C.orangeLight },
{ priority: '🟡', category: '재무', task: '손익계산서·계정별원장 서비스 이관 착수', tag: '중요', tagColor: C.orange, tagBg: C.orangeLight },
{ priority: '🟡', category: 'BOM', task: 'BOM 트리 3단계 React 프론트엔드 개발 협업', tag: '중요', tagColor: C.orange, tagBg: C.orangeLight },
{ priority: '🟢', category: 'R&D', task: 'HTMX 버전 통일 검토 (CDN 1.9 → NPM 2.0)', tag: '권장', tagColor: C.green, tagBg: C.greenLight },
{ priority: '🟢', category: '특허', task: '방법특허 청구항 보완 및 변리사 제출', tag: '권장', tagColor: C.green, tagBg: C.greenLight },
];
// 테이블 헤더
s6.addShape('rect', { x: 0.6, y: 1.2, w: 8.8, h: 0.38, fill: { color: C.dark } });
s6.addText('우선', { x: 0.7, y: 1.22, w: 0.5, h: 0.35, fontSize: 9, bold: true, color: C.white, align: 'center', fontFace: 'Arial' });
s6.addText('영역', { x: 1.2, y: 1.22, w: 0.8, h: 0.35, fontSize: 9, bold: true, color: C.white, align: 'center', fontFace: 'Arial' });
s6.addText('작업 내용', { x: 2.1, y: 1.22, w: 5.5, h: 0.35, fontSize: 9, bold: true, color: C.white, fontFace: 'Arial' });
s6.addText('등급', { x: 7.8, y: 1.22, w: 1.2, h: 0.35, fontSize: 9, bold: true, color: C.white, align: 'center', fontFace: 'Arial' });
nextWeek.forEach((n, i) => {
const y = 1.62 + i * 0.42;
const bgColor = i % 2 === 0 ? C.headerBg : C.white;
s6.addShape('rect', { x: 0.6, y, w: 8.8, h: 0.4, fill: { color: bgColor } });
s6.addText(n.priority, { x: 0.7, y, w: 0.5, h: 0.4, fontSize: 11, align: 'center', fontFace: 'Arial' });
s6.addText(n.category, { x: 1.2, y, w: 0.8, h: 0.4, fontSize: 9, bold: true, color: C.text, align: 'center', fontFace: 'Arial' });
s6.addText(n.task, { x: 2.1, y, w: 5.5, h: 0.4, fontSize: 9.5, color: C.text, fontFace: 'Arial' });
s6.addShape('roundRect', { x: 8, y: y + 0.08, w: 0.6, h: 0.22, rectRadius: 0.03, fill: { color: n.tagBg } });
s6.addText(n.tag, { x: 8, y: y + 0.08, w: 0.6, h: 0.22, fontSize: 7.5, bold: true, color: n.tagColor, align: 'center', fontFace: 'Arial' });
});
// 하단 메시지
s6.addShape('roundRect', { x: 0.5, y: 4.6, w: 9, h: 0.4, rectRadius: 0.06, fill: { color: C.primaryLight } });
s6.addText('💡 일주일간 541 커밋, 66,035행 문서 성장 — 꾸준한 개발 속도를 유지하겠습니다.', { x: 0.7, y: 4.6, w: 8.6, h: 0.4, fontSize: 10, color: C.primary, align: 'center', fontFace: 'Arial' });
addFooter(s6, 6, totalPages);
// 저장
const outputPath = '/home/aweso/sam/docs/presentations/weekly-report-20260322.pptx';
await pres.writeFile({ fileName: outputPath });
console.log('PPTX created: ' + outputPath);
}
main().catch(console.error);