/* 아리 — 작업 관리 데이터 (중첩 프로젝트 트리) */ (function () { /* 최상위 = 폴더(업무/개인) — 그 아래가 프로젝트. 폴더·프로젝트 모두 사용자가 생성 가능 */ const tree = [ { id: "work", name: "업무", folder: true, tone: "ink", icon: "folder", children: [ { id: "biz", name: "경영 전략", tone: "coral", children: [ { id: "biz-okr", name: "2026 Q2 OKR", tone: "coral" }, { id: "biz-report", name: "분기 리포트", tone: "coral" }, { id: "biz-budget", name: "예산 관리", tone: "coral" }, ], }, { id: "onb", name: "온보딩 리디자인", tone: "violet", children: [ { id: "onb-research", name: "사용자 리서치", tone: "violet" }, { id: "onb-wire", name: "와이어프레임", tone: "violet" }, { id: "onb-voice", name: "음성 인터페이스", tone: "violet" }, ], }, { id: "team", name: "팀 운영", tone: "green", children: [ { id: "team-hr", name: "채용 & 온보딩", tone: "green" }, { id: "team-retro", name: "회고", tone: "green" }, ], }, ], }, { id: "life", name: "개인", folder: true, tone: "blue", icon: "heart", children: [ { id: "me", name: "일상", tone: "blue" }, { id: "life-trip", name: "여행 — 한국", tone: "coral" }, { id: "life-fam", name: "가족", tone: "green" }, ], }, ]; /* 트리 → 인덱스 (동적 트리에서도 재계산할 수 있게 함수로 분리) */ function buildIndex(tree) { const projects = []; const byId = {}; (function walk(nodes, parentId, depth) { nodes.forEach((n) => { const rec = { id: n.id, name: n.name, tone: n.tone, folder: !!n.folder, parentId, depth, hasChildren: !!(n.children && n.children.length), }; projects.push(rec); byId[n.id] = rec; if (n.children) walk(n.children, n.id, depth + 1); }); })(tree, null, 0); function isDescendant(pid, ancestorId) { let cur = byId[pid]; while (cur) { if (cur.id === ancestorId) return true; cur = cur.parentId ? byId[cur.parentId] : null; } return false; } function pathOf(id) { const out = []; let cur = byId[id]; while (cur) { out.unshift(cur); cur = cur.parentId ? byId[cur.parentId] : null; } return out; } return { projects, byId, isDescendant, pathOf }; } const { projects, byId, isDescendant, pathOf } = buildIndex(tree); const people = { jiwoo: { name: "지우", initial: "지", c: "var(--blue)", me: true }, hyunwoo: { name: "현우", initial: "현", c: "var(--violet)" }, minseo: { name: "민서", initial: "민", c: "var(--green)" }, jaeho: { name: "재호", initial: "재", c: "var(--coral)" }, sua: { name: "수아", initial: "수", c: "oklch(0.66 0.13 200)" }, }; const columns = [ { id: "todo", label: "할 일", accent: "var(--faint)" }, { id: "doing", label: "진행 중", accent: "var(--blue)" }, { id: "waiting", label: "대기 중", accent: "var(--violet)" }, { id: "review", label: "검토", accent: "var(--coral)" }, { id: "done", label: "완료", accent: "var(--green)" }, ]; /* 직무 맞춤형 업무 쪼개기 (Auto-Scaffolding) 템플릿 */ const scaffolds = { research: { kind: "연구 · 논문 작성", icon: "brain", items: [ { title: "선행 연구 조사", est: "1일" }, { title: "실험 데이터 취합", est: "1일" }, { title: "아키텍처 다이어그램 작성", est: "반나절" }, { title: "서론 초안", est: "반나절" }, { title: "결론 작성", est: "반나절" }, ], }, dev: { kind: "개발 업무", icon: "branch", items: [ { title: "API 엔드포인트 설계", est: "2시간" }, { title: "UI 폼 컴포넌트 개발", est: "3시간" }, { title: "토큰 저장소 세팅", est: "1.5시간" }, { title: "테스트 코드 작성", est: "2시간" }, ], }, doc: { kind: "문서 · 리포트", icon: "file", items: [ { title: "목차 & 범위 정의", est: "30분" }, { title: "핵심 데이터 수집", est: "2시간" }, { title: "섹션별 초안 작성", est: "3시간" }, { title: "리뷰 & 수정", est: "1시간" }, { title: "최종 정리 & 공유", est: "30분" }, ], }, generic: { kind: "일반 업무", icon: "list", items: [ { title: "범위 정의 & 자료 수집", est: "1시간" }, { title: "초안 작성", est: "2시간" }, { title: "리뷰 & 피드백 반영", est: "1시간" }, { title: "최종 정리 & 공유", est: "30분" }, ], }, }; function pickScaffold(title) { const t = title || ""; if (/(논문|연구|리서치|실험|학회|발표|조사|인터뷰)/.test(t)) return scaffolds.research; if (/(구현|개발|API|로직|컴포넌트|버그|배포|인증|앱)/.test(t)) return scaffolds.dev; if (/(리포트|문서|보고|자료|작성|초안|회고|정산)/.test(t)) return scaffolds.doc; return scaffolds.generic; } /* 위임 추천 — 아리는 동료의 업무량을 알 수 없으므로, 내 과부하 + 같은 프로젝트 공동작업자만 근거로 제안하고 넘긴 일을 추적한다 */ const delegateHint = { who: "minseo" }; /* 작업 노드 — 하위 작업은 그 자체가 완전한 작업(무한 중첩) */ let _t = 0; const tuid = () => "kx" + (++_t); function T(o) { return { id: o.id || tuid(), title: o.title, status: o.status || "todo", assignee: o.assignee || "jiwoo", due: o.due || "", prio: o.prio || "보통", project: o.project || "me", notes: o.notes || "", est: o.est || "", delegated: o.delegated || false, comments: o.comments || [], children: (o.children || []).map(T), }; } const tasks = [ T({ id: "k1", title: "분기 리포트 초안 마무리", project: "biz-report", assignee: "jiwoo", due: "06-08", prio: "높음", status: "doing", notes: "

목표

경영진 미팅용 Q2 성과 리포트. 리텐션·매출·예산 세 섹션으로 구성한다.

핵심 지표는 시각화해서 한눈에 들어오게.
", comments: [ { id: "c1", who: "hyunwoo", text: "매출 섹션은 제가 오늘 안으로 넘겨드릴게요.", time: "2시간 전" }, { id: "c2", who: "jiwoo", text: "좋아요, 예산 섹션만 확정되면 취합할게요.", time: "1시간 전" }, ], children: [ T({ title: "리텐션 데이터 취합", status: "done", assignee: "sua", due: "06-08", project: "biz-report", children: [ T({ title: "코호트 정의 확정", status: "done", assignee: "sua", due: "06-07" }), T({ title: "주차별 잔존율 추출", status: "done", assignee: "sua", due: "06-08" }), T({ title: "이탈 사유 태깅", status: "done", assignee: "jaeho", due: "06-08", children: [ T({ title: "인터뷰 발췌 5건", status: "done", assignee: "jaeho" }), T({ title: "사유 카테고리 분류", status: "done", assignee: "jaeho" }), ] }), ] }), T({ title: "매출 섹션 작성", status: "doing", assignee: "jiwoo", due: "06-09", prio: "높음", project: "biz-report", notes: "

MRR/ARR 표는 완료. 증감 코멘트 마무리 필요.

", children: [ T({ title: "MRR / ARR 표 정리", status: "done", assignee: "jiwoo" }), T({ title: "전분기 대비 증감 코멘트", status: "doing", assignee: "jiwoo" }), T({ title: "예측 시나리오 3종", status: "todo", assignee: "jiwoo" }), ] }), T({ title: "예산 섹션 작성", status: "todo", assignee: "jiwoo", due: "06-10", project: "biz-budget" }), T({ title: "경영진 검토 요청 메일", status: "todo", assignee: "jiwoo", due: "06-10", project: "biz-report" }), ] }), T({ id: "k2", title: "온보딩 와이어프레임 피드백 정리", project: "onb-wire", assignee: "hyunwoo", due: "06-09", prio: "높음", status: "review", notes: "

디자인 리뷰에서 나온 3개 화면 수정사항을 취합하고 우선순위를 매긴다.

", comments: [ { id: "c3", who: "sua", text: "3번 화면 CTA 위치는 아래로 내리는 게 좋겠어요.", time: "어제" }, ], children: [ T({ title: "환영 화면 수정안", status: "done", assignee: "hyunwoo", project: "onb-wire", children: [ T({ title: "카피 톤 조정", status: "done", assignee: "hyunwoo" }), T({ title: "일러스트 교체 요청", status: "doing", assignee: "minseo" }), ] }), T({ title: "권한 요청 화면 재배치", status: "doing", assignee: "hyunwoo" }), T({ title: "음성 안내 추가 검토", status: "todo", assignee: "sua" }), ] }), T({ id: "k5", title: "사용자 인터뷰 5건 정리", project: "onb-research", assignee: "sua", due: "06-10", prio: "보통", status: "doing", children: [ T({ title: "녹취 요약 (5건)", status: "doing", assignee: "sua" }), T({ title: "인사이트 태깅", status: "todo", assignee: "sua" }), T({ title: "리서치 보드 업데이트", status: "todo", assignee: "minseo" }), ] }), T({ id: "k6", title: "OKR 중간 점검 자료 준비", project: "biz-okr", assignee: "jiwoo", due: "06-11", prio: "높음", status: "todo", children: [ T({ title: "Objective별 진척도 집계", status: "todo", assignee: "jiwoo" }), T({ title: "리스크 항목 표시", status: "todo", assignee: "jiwoo" }), ] }), T({ id: "k4", title: "구독 결제 카드 갱신", project: "me", assignee: "jiwoo", due: "06-12", prio: "보통", status: "todo", children: [] }), T({ id: "k3", title: "신규 입사자 환영 메일 발송", project: "team-hr", assignee: "minseo", due: "06-07", prio: "보통", status: "done", children: [ T({ title: "메일 템플릿 작성", status: "done", assignee: "minseo" }), T({ title: "수신자 명단 확인", status: "done", assignee: "minseo" }), ] }), T({ id: "k13", title: "스프린트 회고 문서 배포", project: "team-retro", assignee: "minseo", due: "06-08", prio: "낮음", status: "review", children: [ T({ title: "액션 아이템 담당자 지정", status: "done", assignee: "minseo" }), T({ title: "다음 스프린트 반영 확인", status: "doing", assignee: "jaeho" }), ] }), T({ id: "k10", title: "모바일 푸시 알림 QA", project: "onb-wire", assignee: "sua", due: "06-09", prio: "높음", status: "doing", children: [ T({ title: "iOS 시나리오 3종", status: "done", assignee: "sua" }), T({ title: "Android 시나리오 3종", status: "doing", assignee: "sua" }), T({ title: "딥링크 라우팅 확인", status: "todo", assignee: "jaeho" }), ] }), T({ id: "k14", title: "데이터 전처리 파이프라인", project: "onb-research", assignee: "minseo", due: "06-10", prio: "보통", status: "waiting", delegated: true, notes: "

일정이 빠듯해 같은 프로젝트의 민서님께 위임 — 현재 완료를 기다리는 중입니다.

", children: [ T({ title: "원천 데이터 스키마 정리", status: "done", assignee: "minseo" }), T({ title: "결측치 처리 규칙 정의", status: "doing", assignee: "minseo" }), T({ title: "정제 스크립트 작성", status: "todo", assignee: "minseo" }), ] }), T({ id: "k20", title: "한국행 비행기 티켓 구매", project: "life-trip", assignee: "jiwoo", due: "06-14", prio: "높음", status: "todo", notes: "

스마트 인박스에서 자동 생성된 작업이에요. 아리가 가격 추적 알림을 켜뒀고, 적정가가 보이면 결재함으로 알려드려요.

", children: [ T({ title: "날짜 후보 확정", status: "doing", assignee: "jiwoo", project: "life-trip" }), T({ title: "가격 알림 확인 후 결제", status: "todo", assignee: "jiwoo", project: "life-trip" }), ] }), T({ id: "k21", title: "엄마 생신 선물 알아보기", project: "life-fam", assignee: "jiwoo", due: "06-20", prio: "보통", status: "todo", children: [] }), ]; window.TaskData = { tree, buildIndex, projects, byId, isDescendant, pathOf, people, columns, tasks, scaffolds, pickScaffold, delegateHint }; })();