Commit Graph

99 Commits

Author SHA1 Message Date
ttipo 5303b2e92e feat: 할 일 완료 항목 정리 기능 추가
오래 쓴 라벨에 완료 항목이 계속 쌓여 카드가 길어지는 문제를 해소한다.

- 목록 상단에 '완료 항목 숨기기' 토글 — 화면에서만 걸러내고 데이터는 유지
- 라벨 메뉴에 '완료 항목 N개 삭제' — 확인 후 일괄 삭제(DELETE labels/:id/done-items)
- 완료 항목이 없으면 두 진입점 모두 노출하지 않는다
- 숨김 상태에서도 진행 집계는 전체 기준을 유지한다

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 23:08:50 +09:00
ttipo 8d2436cd10 feat: 업무 상세에서 개인 할 일 만들기 추가
담당자가 업무를 자기 할 일 목록으로 옮길 수 있게 한다. 항목 출처는 두 갈래이며
확정 모달은 하나를 공유한다.

- 체크리스트가 있으면 그 항목을 카테고리 순으로 복사(완료 항목은 기본 해제)
- 없고 본문만 있으면 AI 가 본문을 분석해 담당자 관점의 실행 단계를 제안
- 둘 다 비어 있으면 버튼을 비활성화

- todo_labels.source_task_id 추가 — 같은 업무로 다시 만들면 새 라벨 대신 병합
- 병합 시 판정 기준은 '보완 내용을 뗀 원문', 반려된 항목은 완료를 되돌린다
- 수정 요청된 항목은 보완 내용을 제목에 함께 담는다
- 라벨 이름은 '프로젝트 - 업무 제목', 길면 프로젝트명부터 줄인다
- AI 프롬프트는 근거 없는 항목을 만들지 않도록 개수 하한 없이 제약

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 23:03:07 +09:00
ttipo 65d7abcd68 fix: 업무 완료일을 마감일이 아닌 실제 완료 시각으로 표시
목록의 "N월 N일 완료" 라벨이 완료 시각이 아니라 마감일(dueDate)을 쓰고 있어,
마감일을 지시 당일로 잡은 업무는 지시 날짜가 완료일처럼 보였다.
완료 시각을 저장하는 컬럼 자체가 없던 것이 원인이다.

- tasks.completed_at 추가 — done 진입 시 기록, 이탈 시 null(재승인 시 갱신)
- 목록/내 업무 응답에 completedAt 노출, taskDueInfo 가 이 값으로 라벨 생성
- 기존 완료 업무는 활동 로그(task.status_changed)의 시각으로 백필
- 백필 불가 건과 마감일 없는 완료 업무는 날짜 없이 "완료"만 표시

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 21:11:11 +09:00
ttipo 38b3762b2c refactor: 헤더 AI 채팅 어시스턴트 제거
사용하지 않는 기능이라 헤더 버튼과 관련 로직을 모두 걷어낸다.
업무 생성 화면의 AI 할 일 추천(/ai/suggest-checklist)은 그대로 유지한다.

- 프론트: AgentChatPanel, ai.store, 헤더 버튼·패널 마킹, useAi 의 대화 함수와
  types/ai 의 대화 타입 제거. 채팅 전용이던 shared/utils/markdown.ts 도 함께 삭제
- 백엔드: AiController(대화 5개 엔드포인트), AiConversation·AiMessage 엔티티,
  SendMessageDto, AiService 의 채팅 로직 제거. AiModule 의 TaskModule 의존도 해제
- ai_conversations·ai_messages 테이블 삭제 마이그레이션 추가(down 은 초기 스키마 복원)
- 문서: api-contract 의 채팅 API 절 삭제, integration-progress 8-6 은 이력이라
  삭제 대신 제거됨 표기

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 00:24:14 +09:00
ttipo 29a8ef841f feat: 개인 할 일(체크리스트) 기능 추가
사이드바에 '할 일' 메뉴를 추가하고, 라벨 1개에 할 일 N개를 담는
개인 전용 체크리스트를 구현한다.

- todo_labels / todo_items 엔티티 + 마이그레이션 (소유자·라벨 CASCADE)
- 라벨·항목 CRUD API — 항목 변경 응답은 '변경된 라벨' 단위라 카드 하나만 갱신
- 모든 라벨·항목은 소유자 본인만 접근하며 타인 것은 존재를 숨김(404)
- 화면: 라벨 카드 세로 나열, 인라인 추가·수정, 완료 항목은 취소선 후 하단 정렬
- 기존 .list / .toolbar / BaseModal 등 공용 디자인 규격 사용

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-21 00:23:32 +09:00
ttipo f08b9b0c03 feat: 개인 일정 캘린더 추가 및 사내 일정 수정 권한 제한
카테고리를 사내 공용(owner=null)과 개인 전용(owner=사용자)으로 나누고,
일정의 공개 범위를 소속 카테고리에서 파생시켜 두 종류가 섞이지 않게 한다.

- 조회: 사내 일정 + 본인 개인 일정만 반환, 타인의 개인 항목은 존재를 숨김
- 개인 일정은 소유자만 수정·삭제하며 참석자·알림을 사용하지 않음
- 사내 일정은 등록자만 수정·삭제하도록 제한
  (등록자가 탈퇴한 기존 일정은 잠기지 않도록 예외 처리)
- 일정이 남은 사내 카테고리는 삭제를 막아 타인 일정 연쇄 삭제를 방지
- 툴바를 캘린더 선택(사내/개인 체크박스)과 '내 관련' 참석자 필터로 분리
- 일정·카테고리 작성 시 사내/개인 구분을 명시적으로 선택
- 기존 데이터는 전부 사내 공유(company)로 유지되는 마이그레이션 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-20 23:01:37 +09:00
ttipo 5ff9bf0df8 feat: 업무 공동 지시자·검토자 표기·마감 임박/초과 알림 추가
- 공동 지시자(coIssuers): 업무에 공동 지시자를 지정해 원 지시자와 함께
  완료 검토(승인/수정요청) 권한을 부여. 생성/수정 UI, 검토 권한 체크,
  보고 열람, 승인요청·시작 알림 수신, '내가 지시한 업무' 목록에 포함.
- 검토자 표기(reviewedBy): 승인 완료/수정 요청 카드에 실제 검토를 수행한
  사람(원 지시자 또는 공동 지시자)을 표기하도록 추적·노출.
- 마감 알림: 매일 오전 9시(KST) cron으로 미완료 업무의 마감 임박(24h 이내)
  ·초과를 담당자에게 1회 알림(task.due_soon/task.overdue). 발송 플래그로
  중복 방지(다중 인스턴스 대비 원자적 선점), 마감 변경 시 초기화.
  웹푸시·인앱 문구 동시 지원(커버리지 스펙 갱신).

DB: task_co_issuers 조인테이블, tasks.reviewed_by,
    tasks.due_soon_notified_at/overdue_notified_at 마이그레이션 추가
    (dev synchronize 자동, 운영 migrationsRun).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 00:40:04 +09:00
ttipo de92fc01b4 docs: env 예시 파일의 웹 푸시(VAPID) 섹션 주석 구조 통일
VAPID 섹션만 설명 주석을 배너 박스 안에 넣거나(dev) 배너 자체가
없어(prod) 다른 섹션과 어긋나던 것을, 다른 섹션과 동일하게
'배너(제목만) → 아래 설명 주석' 구조로 맞춘다. 값 변화 없음.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:42:17 +09:00
ttipo 9a79e169c8 feat: 알림 신뢰성·내용 보강 (SSE 재동기화·웹푸시·전직원 일정·파일첨부)
[신뢰성]
- SSE 재연결 시 fetchInitial 로 재동기화: 끊긴 사이 놓친 알림 복구 +
  access 토큰 자동 refresh 로 만료 토큰 재연결 무한루프 해소
- 서비스워커 pushsubscriptionchange 처리로 구독 회전 시 자동 재등록
- 읽은 알림 90일 retention 정리 배치(@Cron)
- 웹푸시 다기기 병렬 발송(allSettled) + TTL/urgency 지정
- 알림 문구 SSOT 상호참조 주석 + web-push 커버리지 테스트 추가

[내용]
- 전 직원(allHands) 일정도 전 사용자에게 알림(생성/수정/삭제)
- task.file_attached 신규 타입: 파일 첨부 시 지시자·담당자 알림
- task.started 재개(수정요청 후) 문구 '재개/시작' 구분
- task.due_changed 푸시에 날짜/해제 상세 반영

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 11:31:25 +09:00
ttipo 8d00d71072 feat: PC 데스크톱 알림(웹 푸시) 추가 — 인앱/SSE에 OS 토스트 연동
카카오톡처럼 PC에 OS 토스트가 뜨도록 Web Notifications(Level1) +
Web Push/서비스워커(Level2)를 기존 인앱·SSE 알림 위에 얹는다.
중복 방지: 페이지가 열려 있으면 SSE 로컬 알림(포커스면 인앱만),
닫혀 있으면 서비스워커 푸시가 담당한다.

- 백엔드: push_subscriptions 엔티티 + WebPushService(VAPID 설정·
  type→{title,body,url} 포매터·만료 구독 정리), notify()가 SSE와
  함께 푸시 발송, push/subscribe·unsubscribe 엔드포인트, 마이그레이션,
  web-push 의존성
- 프론트: public/sw.js(push/클릭), usePush 컴포저블(권한·구독, 결과
  안내), notification.store 로컬 토스트, AppShell 'PC 알림 켜기' 버튼
- env/compose: VAPID 키 주입(공개키→프론트, 비밀키→백엔드)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 00:49:33 +09:00
ttipo e42ccf7139 feat: 드라이브를 전사 공유 드라이브로 전환하고 전역 감사 로그 추가
개인별 격리(owner 기준)를 제거해 로그인 사용자 전원이 하나의 공유
트리를 보고 관리하도록 전환한다. 수정/삭제는 전원 허용하며, 행위
추적을 위해 전역 감사 로그(drive_activities, GET /drive/activity)를
추가한다. 업로더 탈퇴 시 공유 파일이 사라지지 않도록 owner FK를
CASCADE에서 SET NULL로 변경한다.

- 백엔드: 접근 권한 해석 제거(단순 로더화), 업로드/이름변경/삭제
  6지점에 감사 적재, DriveAuditService 신설, 운영 마이그레이션
  1783200000000 추가
- 프론트: 목록 행에 업로더 표시, 툴바 '기록' 패널 추가
- 프로젝트 폴더 연동의 '본인 소유 폴더만' 제한 해제(공유 모델 일관성)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 22:40:45 +09:00
ttipo ec54ba0e8d feat: 드라이브 업로드·삭제 행위자 추적 추가
연동 프로젝트 폴더의 파일 업로드/삭제와 폴더 삭제를 활동 피드에 기록하고,
파일 삭제를 소프트 삭제(status=deleted + deletedBy/deletedAt 묘비)로 전환해
누가·언제 지웠는지 추적할 수 있게 한다. 실제 객체는 즉시 영구 삭제(복구 제외).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 21:54:17 +09:00
ttipo 5267b759be fix: 드라이브 전환 마이그레이션 멱등·견고화 (운영 적용 실패 방지)
ADD COLUMN IF NOT EXISTS, project_attachments 는 제약 이름 비의존 DROP TABLE IF EXISTS ... CASCADE 로 변경. 과거 synchronize 로 만들어진 운영 DB나 부분 적용 상태에서도 실패하지 않도록 함

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 19:43:23 +09:00
ttipo 524c6f6782 feat: 드라이브 연동 전환 운영 마이그레이션 추가
drive_files 에 extracted_text/extract_status 추가, project_attachments 폐기(완전 대체). dev 는 synchronize, 운영은 이 마이그레이션으로 반영

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 19:15:02 +09:00
ttipo 5a92813a31 fix: AI 체크리스트 추천 응답 견고 파싱으로 502 방지
Claude가 깨진/잘린 JSON을 반환해도 정규식으로 카테고리·항목 복구(폴백), 프롬프트에 완결 JSON 규칙 추가, max_tokens 상향

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 18:21:37 +09:00
ttipo cf1f4db6af feat: 프로젝트 드라이브 폴더 연동 + AI 검토 문서 전환
프로젝트 직접 업로드(project_attachments) 폐기, AI 검토 문서를 드라이브 폴더 연동으로 전환. 폴더 연동 CRUD·연동 폴더 문서에서 텍스트 지연 추출(NCP 다운로드, xlsx 포함)·AI 추천 주입. 폴더 선택 피커 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 18:21:23 +09:00
ttipo 46f1875ca3 style: 화상회의 방 수정 삼항 연산자 포맷 정리
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 21:11:59 +09:00
ttipo 22a02be937 feat: 드라이브(파일 저장소/폴더 관리) 기능 추가
NCP Object Storage(S3 호환) 기반 파일 업로드/폴더 관리 기능을 백엔드에 추가한다.

- 스토리지 계층(common/storage): presign 기반 업로드/다운로드 URL 발급 래퍼
- 드라이브 모듈: 폴더 CRUD, 파일 presign 업로드→complete→다운로드, 이름변경/삭제
- 엔티티 3종: drive_folders(폴더 트리), drive_files(객체 메타), project_folder_links(프로젝트 공유)
- 마이그레이션 AddDriveTables: 3개 테이블 + 인덱스/외래키 생성
- stale pending 파일 정리 @Cron(매시) 추가
- env/docker-compose: OBJECT_STORAGE_* 주입(비시크릿은 루트, ACCESS/SECRET 키는 backend env)
- 의존성: @aws-sdk/client-s3, @aws-sdk/s3-request-presigner 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 21:11:48 +09:00
ttipo 2d1026466f feat: 화상회의 방에 고유 입장 코드 도입(이름과 식별자 분리)
방 식별을 이름이 아닌 불변 코드로 전환 — 이름을 바꿔도 링크·세션이 유지되고
초대 링크의 안정적 기준이 된다.

- 백엔드: meeting_rooms.code(고유·불변) 추가, name 고유 해제(표시용 라벨, 중복 허용).
  마이그레이션 AddMeetingRoomCode(코드 백필+name 고유 인덱스 제거). 토큰 발급은
  code 로 방 조회(자동 생성 제거 — 명시 생성된 방만 입장, 없으면 404). LiveKit 방=code.
  createRoom 이 고유 코드 발급. MeetingRoomResponse 에 code 추가.
- 프론트: 입장/라우팅을 code 기준으로(/meeting/:code), 로비 입장은 방 객체 전달,
  '초대 링크 복사' 버튼(코드 기반 URL) 추가. 생성 폼 이름 중복 검사 제거.

검증: 백엔드 build, 프론트 type-check·lint 통과.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 17:46:57 +09:00
ttipo bde4b561f3 feat: 화상회의 내가 만든 방 편집 기능
진행자(생성자)가 방의 이름·예약 시각·참여 제한(allow-list)을 수정할 수 있다.

- 백엔드: PATCH /meetings/rooms/:roomId (updateRoom, 진행자만, 이름 중복 검사).
  UpdateMeetingRoomDto(생성과 동일 필드). MeetingRoomResponse 에 allowedUsers 추가
  (진행자에게만 노출 — 편집 폼 사전 채움용). User repo 주입해 ID→PublicUser 해석.
- 프론트: useMeeting.updateRoom, 로비 각 방에 편집 버튼(진행자), MeetingCreateForm
  를 편집 모드로 재사용(현재 값 프리필·중복검사 자기제외·저장 분기), MeetingIcon edit 추가.

검증: 백엔드 build, 프론트 type-check·lint 통과.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:15:31 +09:00
ttipo d8d2ff21f9 fix: 화상회의 후속 보정(권한 헤더·빈 상태·한글 방이름 등)
- nginx Permissions-Policy: 운영에서 camera/microphone 을 self 에 허용
  (기존 microphone=()/camera=() 가 LiveKit 장치 접근을 차단하던 문제 수정)
- 회의 로비 빈 상태/검색결과 없음을 공용 EmptyState 로 통일(내 업무·프로젝트 화면과 동일)
- 미구현인 공유 링크 안내 문구 제거(lede)
- 프리조인 "카메라·마이크 권한이 없습니다" 문구 제거
- 회의실 상단 노란색 권한 안내 배너(room-err) 제거 + 관련 prop/이벤트 정리
- 회의방 이름에 한글 허용(백엔드 DTO 2종 + 프론트 검증 정규식/문구, 공백·특수문자는 계속 차단)
- 멤버 초대 등급 셀렉트(DropdownSelect block) 라벨 좌측 정렬(버튼 기본 가운데정렬 상속 수정)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 18:17:22 +09:00
ttipo 999a8730dd feat: 화상회의 UI 전면 개편 및 코드·로직 검토 반영
디자인 시안(프로토타입)을 실제 Vue 컴포넌트로 이식하고, 코드·로직·보안
검토에서 도출한 항목을 반영한다.

UI 개편
- 컴포넌트 분리: MeetingLobby/CreateForm/Prejoin/Room/SidePanel/PeopleList/
  Chat/ControlBar/ParticipantTile/QualityBars/MeetingIcon
- 로비: 검색 툴바 + "새 회의 만들기" 모달(BaseModal) + 방 목록(삭제=휴지통)
- 프리조인: 실제 카메라/마이크 미리보기 + 초기 on/off, 실제 프로필 아바타
- 회의실: 축소 스테이지 + 썸네일 그리드(한 페이지 50명) + 페이지네이션,
  말하는 사람 하이라이트/연결 품질 막대
- 채팅: 데이터 채널 실제 구현(Shift+Enter 줄바꿈, 실제 프로필 아바타)

디자인 시스템 정합
- rem 단위/토큰 정렬, 공용 .btn/.form-*/.search/BaseModal/UserAvatar 재사용
- 회의실 다크 캔버스 토큰(--d-*, --d-red/green/amber/accent-soft) 추가

화질/성능
- 스테이지=고화질, 나머지=저화질 구독(setVideoQuality)
- 타일 backdrop-filter 제거 + 발화/품질 이벤트 전체 리렌더 제거(버벅임 완화)

코드·로직·보안 검토 반영
- 채팅 표시 이름은 서명 토큰의 name/identity 만 신뢰(클라이언트 name 사칭 차단)
- promote 는 보낸 사람 metadata role=host 일 때만 적용(인가)
- 원격 오디오를 룸 레벨에서 전원 재생(페이지 밖 참여자 무음 수정)
- 타일 언마운트 시 트랙 detach(누수 방지)
- 마이크/카메라 토글 실패 시 상태 롤백
- 입장 토큰 TTL 1h → 12h(장시간 회의 재연결 안정성)
- 채팅 길이 상한(MAX_CHAT_LENGTH)
- 토큰 metadata 에 avatarUrl 포함(참여자/채팅 아바타)

기타
- 임시 명세/디자인 참고 파일 제거

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 17:56:53 +09:00
ttipo 54fe3d36e7 feat: 화상회의 단계 4 — 방 관리(목록·생성·예약·참여 제한)
암묵적 방 생성을 명시적 방 관리로 확장한다. (기능 완성 + 임시 UI)

백엔드
- meeting_rooms 에 scheduled_at(예약), allowed_user_ids(참여 허용 allow-list) 추가 + 마이그레이션
- GET /meetings/rooms(목록: 접근 가능/진행자 여부 포함)
- POST /meetings/rooms(생성: name+예약+허용멤버, 생성자=진행자, 이름 중복 409)
- DELETE /meetings/rooms/:id(진행자만, 아니면 403)
- 토큰 발급 시 참여 제한 enforcement: 비허용자는 AUTH_003(403).
  방이 없으면 자동 생성(공개)으로 직접 링크 입장은 유지.

프론트
- useMeeting: listRooms/createRoom/removeRoom + types/meeting.ts
- MeetingPage 로비: 방 목록(입장/삭제) + 생성 폼(예약시각·참여 제한 멤버 피커, useUser 검색 재사용) + 빠른 입장
- 화상회의_화면명세.md 단계 4 반영(범위/로비/데이터 계약/엔드포인트)

권한 모델은 기존 결정대로 "누구나 생성=진행자" 유지(전역 admin 없음).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 20:48:51 +09:00
ttipo f519723260 feat: 화상회의 단계 2 — 권한 분기(생성자=진행자)
방을 처음 만든 사용자를 진행자로 보고, 역할별로 LiveKit 권한을 차등 부여한다.

백엔드
- meeting_rooms 엔티티/마이그레이션: 방 이름(고유)→진행자(host) 보관
- 토큰 발급 시 역할 결정: 방이 없으면 생성자=진행자, 있으면 생성자 일치로 판정
  (동시 생성 경쟁은 name unique 충돌 후 재조회로 처리)
- grant 차등: 진행자 roomAdmin+발행/구독, 일반 발행/구독
- 역할을 토큰 metadata({"role":...})에 실어 다른 참여자도 진행자 식별
- 응답에 role 추가

프론트
- useMeeting: role 반환
- ParticipantTile: metadata 파싱 → 진행자 타일에 "진행자" 배지
- MeetingPage: 본인이 진행자면 헤더에 역할 칩 표시

방 목록/예약/참여 제한 UI 는 단계 4 로 분리.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 17:51:21 +09:00
ttipo bf2321f2d5 feat: 화상회의 단계 1 — LiveKit 기반 인프라 토대
LiveKit(SFU)에 미디어 전송을 위임하고, 앱은 입장 토큰 발급 + 회의 화면만
담당하는 구조로 단계 1(기본 연결)을 구현한다.

백엔드
- meeting 모듈: POST /api/meetings/token (JWT 인증)
  livekit-server-sdk 로 입장 JWT 발급(identity=userId, name, grants:
  roomJoin/publish/subscribe/publishData, TTL 1h). 단계 2에서 역할별 분기 예정.

프론트
- useMeeting(토큰 발급), useLiveKitRoom(연결/참여자/컨트롤 상태)
- ParticipantTile(카메라/마이크 트랙 attach, 본인 거울·음소거, 이니셜 플레이스홀더)
- MeetingPage(로비 + 영상 그리드), 라우트 /meeting/:room?, 사이드바 네비 추가
- VITE_LIVEKIT_URL 주입(build arg + dev 런타임 env)

인프라/설정
- 개발 미디어 서버는 LiveKit Cloud 사용(로컬 self-host 는 WSL2/Docker Desktop
  WebRTC 네트워킹 제약으로 제외). env 만으로 운영 전환 가능.
- livekit/livekit.yaml 은 단계 6 셀프호스팅 참고용으로 보관.
- 화상회의_화면명세.md: 현재 화면 기능/구조/데이터 계약 문서(UI 개편 참고).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 17:40:18 +09:00
ttipo e719829f0b feat: 프로젝트 문서의 AI 반영 상태 배지 표시
AI 추천이 문서 내용을 반영하지 못해도(추출 실패·미지원 형식) 추천은
정상 응답되어 사용자가 인지할 수 없던 문제를 해소한다. 문서 목록에
반영 상태 배지를 노출한다.

- ProjectAttachmentResponse.aiStatus 추가(ready/failed/unsupported)
  · ready: 추출 성공(반영 가능) · failed: 지원 형식이나 추출 실패
  · unsupported: 미지원 형식(이미지·xlsx·ppt·zip 등)
- isExtractableType + deriveAiStatus 로 상태 산출
  (추출 본문은 응답에 노출하지 않고 상태 판별에만 사용)
- listFiles 에서 extracted_text 를 명시적으로 선택(select:false 우회)
- 공용 AttachmentAiBadge 컴포넌트 추가 → 프로젝트 상세·설정 문서 목록에 적용

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 15:10:20 +09:00
ttipo 739a5a70f3 feat: AI 업무 추천에 프로젝트 문서 내용 반영(DB 캐싱)
업무 작성 화면의 AI 추천이 프로젝트 제목·설명과 업무 제목·내용에 더해
프로젝트 첨부 문서의 내용까지 파악하도록 확장한다.

매 호출마다 문서를 재파싱하지 않도록, 업로드 시점에 1회 텍스트를
추출해 project_attachments.extracted_text 에 캐싱하고 AI 추천에서
꺼내 읽는 구조로 구현한다.

- ProjectAttachment.extractedText 컬럼 추가(text, nullable, select:false)
- extract-text.ts: txt/csv(직접)·pdf(pdf-parse)·docx(mammoth) 추출
  (문서당 8천 자 상한, 추출 불가/실패 시 null)
- addFile: 업로드 시 텍스트 추출·캐싱
- getAttachmentsTextForAi: 문서별 추출 본문을 합쳐 반환(총 1.6만 자 상한)
- suggestChecklist: [프로젝트 문서] 섹션을 프롬프트에 추가 + 시스템 규칙 보강
- 운영용 마이그레이션 추가(dev 는 synchronize 로 자동 반영)
- 추출 텍스트는 AI 내부용 — 프론트 응답 DTO 에는 노출하지 않음

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 14:54:29 +09:00
ttipo 8fc9287747 feat: 프로젝트 문서 첨부(생성/편집/상세)
프로젝트에 문서 파일을 첨부할 수 있도록 추가(업무 첨부 인프라·업로드 설정 재사용).

백엔드:
- ProjectAttachment 엔티티 + 마이그레이션(project_attachments)
- 업로드(관리자)/삭제(관리자)/다운로드(멤버) 엔드포인트, 멀터 디스크 저장
- 프로젝트 상세 응답에 attachments 포함

프론트:
- 생성 화면: 파일 staged 후 생성 직후 업로드
- 편집(설정) 화면: 기존 문서 목록 + 추가/삭제(즉시), 폼 미저장 보존
- 프로젝트 상세: 문서 목록(다운로드 전용)
- useProject.uploadFile/removeFile, ApiProjectAttachment 타입

권한: 업로드/삭제=관리자, 조회/다운로드=멤버. 형식/용량은 업무 첨부와 동일(25MB).
런타임 검증: 업로드·목록·다운로드·삭제 + 비관리자 403 확인.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 14:32:35 +09:00
ttipo 42bf97636a feat: 일정 참석자 알림(지정/수정/삭제)
일정 CRUD 시 참석자에게 인앱 알림 발송(기존 NotificationService + SSE 재사용).

- 생성: 참석자로 지정된 사용자에게 schedule.invited (전 직원 일정은 개별 알림 생략)
- 수정: 신규 참석자 → schedule.invited, 기존 참석자 → schedule.updated
- 삭제: 참석자에게 schedule.removed (삭제 전 참석자 확보)
- 행위자 본인은 자동 제외, payload(actorName/eventTitle/start), 클릭 시 /schedule 이동
- NotificationType(백/프론트) 3종 추가 + notification.store 렌더 케이스

런타임 검증: 지정/수정/삭제 알림이 참석자에게만 정상 도달.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 13:56:03 +09:00
ttipo 34b5b1d552 feat: 사이드바 순서 변경 + 기본 진입 경로 일정으로
- 사이드바 nav 순서: 일정 → 내 업무 → 프로젝트
- 기본 진입을 /schedule 로: 루트 redirect, 로그인 후 이동, 로그인 상태에서
  로그인/가입 진입 가드, 소셜 로그인(구글/카카오) 콜백

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 13:39:38 +09:00
ttipo 1ad95ff7e8 chore: 일정 기본(시드) 카테고리 제거
기본 카테고리 12종 onModuleInit 시드를 삭제 — 카테고리는 사용자가 직접 추가한다.
(dev DB 의 기존 시드 카테고리/일정은 별도로 정리)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 11:47:13 +09:00
ttipo b8756d34ab fix: 토큰 갱신/로그인 유지(keepLogin) 정상화
세션이 짧게 유지되고 '로그인 유지'가 동작하지 않던 문제 수정.

원인1(프론트): 401 자동 refresh 가 '/auth/' 포함 URL을 모두 제외해,
부트스트랩 /auth/me 가 access 만료(15분)로 401 이어도 refresh 를 시도하지 않아
새로고침/재방문 시 로그아웃됨.
 - useApi 인터셉터: refresh-재시도 제외 대상을 /auth/refresh·/auth/login 으로만 한정
   → /auth/me 등은 access 만료 시 refresh 후 재시도되어 세션 복원

원인2(백엔드): keepLogin 값이 무시되어 쿠키 영속 여부에 반영되지 않음.
 - RefreshSession 에 persistent 플래그 추가(마이그레이션, default true)
 - 로그인 시 keepLogin 저장, refresh 회전 시에도 그대로 유지
 - setAuthCookies: persistent=true 면 maxAge(영속), false 면 세션 쿠키
 - 소셜 로그인은 체크박스가 없어 영속(true) 기본

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 10:56:25 +09:00
ttipo 346ec75553 feat: 전사 공유 일정(주간 캘린더) 기능 추가
상단 nav 독립 메뉴 '일정' — 주간 타임라인(간트형) 캘린더.
카테고리=레인, 일정=막대(다중일 지원), 우측 상세 패널, 일정/카테고리 CRUD.

백엔드(Schedule 모듈):
- ScheduleCategory/ScheduleEvent 엔티티 + 참석자(다대다) + 작성자
- 카테고리/일정 CRUD, 기간 겹침 조회, 참석자 풀(전사 사용자)
- 기본 카테고리 12종 onModuleInit 멱등 시드
- 인증 사용자 누구나 관리(전역 일정, 글로벌 관리자 개념 없음)
- 운영 마이그레이션(AddSchedule) 추가, @nestjs/schedule 임포트는 CronModule 로 alias

프론트:
- /schedule 라우트 + 사이드바 nav 항목
- 주간 네비(이전/다음/오늘), 유형 필터·카테고리 관리 드롭다운
- 타임라인/상세 패널/일정 모달/카테고리 모달(hue 슬라이더)/참석자 멀티셀렉트
- schedule.store + useSchedule + scheduleDate 유틸

런타임 검증: dev 부팅·시드(12건)·라우트 매핑 정상, 인증 게이트(401),
일정 생성/조회/수정/삭제 + 참석자/전직원/다중일 스모크 전부 정상.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 21:23:10 +09:00
ttipo 2c02c4afc1 perf: 활동 피드 무제한 렌더링 방지(상한 + 페이지네이션)
활발한 프로젝트/업무에서 활동이 한 번에 전부 내려와 렌더링되던 문제 보완.

- 업무 상세 활동 탭: 최신 100건으로 조회 상한 추가(기존 무제한)
- 프로젝트 활동 피드: 200건 통째 조회 → 오프셋 페이지네이션 전환
  - 컨트롤러 page/size 쿼리 지원, 서비스 findAndCount
  - 프론트 스토어 누적 + hasMore/loadMore, 활동 페이지 "더 보기" 버튼

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 18:03:19 +09:00
ttipo 2b534743d6 feat: 중간 점검 보고 시 활동 피드 기록 추가
보고 제출 시 알림만 보내고 활동(activity)에는 적재되지 않아
프로젝트 활동 피드·업무 활동 탭에 표시되지 않던 누락을 보완.

- ActivityType/ApiActivityType 에 task.checkpoint_reported 추가
- submitCheckpointReport 에서 activity.record 호출
- 프론트 활동 렌더링 케이스(프로젝트 피드/업무 탭) 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:49:37 +09:00
ttipo 6577550937 feat: 중간 점검 보고 내용은 지시자·담당자만 열람(상태는 전원 노출)
보고 '내용' 열람을 지시자 + 해당 업무 담당자로 제한:
- 백엔드 buildDetail: viewerId 기준으로 권한 없으면 checkpointReports 를 빈 배열로 응답(findOne 에 현재 사용자 전달). 점검일에는 reported 플래그를 두어 상태는 전원 공유
- 프론트: checkpointState 는 reported 플래그 기준, '보고 완료' 클릭(모달)은 지시자·담당자만. 그 외는 비클릭 상태 배지

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 16:01:50 +09:00
ttipo b717ecd9a5 fix: 보고된 중간 점검일 삭제 방지(보고 이력 보존)
수정 시 보고가 접수된 점검일을 지우면 CASCADE 로 보고서까지 삭제되던 문제 방지:
- 백엔드 syncCheckpoints: 보고가 있는 점검일은 삭제 대상에서 제외
- 프론트 수정 폼: 보고된 점검일은 '보고됨' 잠금 배지로 표시하고 삭제 버튼 비노출

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 15:49:51 +09:00
ttipo 98a2308c4e feat: 중간 점검 보고를 모달 + 카테고리 탭으로 열람
하단 토글 목록 제거 → 상단 '보고 완료' 체크포인트 클릭 시 모달로 보고 내용 표시.
모달은 업무 생성의 카테고리 탭(필수/관리/보안/부가) 방식으로 항목을 구분해 보여준다.
보고 스냅샷에 category 추가(백엔드 엔티티/서비스 + 프론트 타입/뷰/스토어).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 15:33:19 +09:00
ttipo 4ef07f65a7 refactor: 중간 점검을 점검일 당일 보고서 방식으로 변경
진행 상태(미착수/진행중/완료)를 상시 토글 대신 '보고서'로 전환:
- 보고는 점검일 당일(KST)에만 가능, 지나면 누락 — 마감 전 상시 전송 제거
- 작업자가 점검일 당일 보고서 폼에서 항목별 상태 + 메모를 작성해 전송(상태는 보고에 포함)
- 지시자는 상세 화면에서 보고 기록을 접힌 목록으로 보고 클릭해 펼쳐 열람
- 체크리스트 항목은 마지막 보고 상태를 읽기 전용 배지로 표시(인라인 편집 제거)
- 상시 진행상태 갱신 엔드포인트/DTO 제거(work-status)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 15:18:58 +09:00
ttipo 20664dab0f feat: 중간 점검(체크포인트) 백엔드 — 진행상태/점검일/보고
체크리스트에 작업자 진행상태(work_status: 미착수/진행중/완료) 필드 추가(지시자 검토와 분리).
업무당 중간 점검일(task_checkpoints) 여러 개 + 작업자 보고 스냅샷(checkpoint_reports) 도입.
- 생성/수정 DTO에 checkpoints(날짜 목록) 추가, 날짜 기준 동기화(보고 보존)
- PATCH .../work-status(담당자 진행상태 갱신), POST .../checkpoints/:id/report(점검일~마감 구간, 스냅샷+지시자 알림 task.checkpoint_reported)
- 상세 응답에 checkpoints/checkpointReports/항목 workStatus 포함
- 운영용 마이그레이션 추가(dev 는 synchronize 자동 반영)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 14:46:07 +09:00
ttipo 5e742e5c8b fix: 미인증 계정 재가입 허용 및 로그인 화면 인증 메일 재발송
인증 미완료 상태에서 재가입/로그인이 모두 막혀 데드락되던 문제 해결.
- signup: 인증 완료 계정만 409로 거부하고, 미인증 로컬 계정은 입력값으로 갱신 후 인증 메일 재발송(중복 계정 생성 안 함)
- 로그인 화면: 미인증(403) 응답 시 '인증 메일 다시 보내기' 인라인 액션 노출

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 13:42:34 +09:00
ttipo 944b8efbce feat: 이메일 인증·비밀번호 재설정 링크를 운영 환경에서도 로그 출력
SMTP 미구현 상태에서 운영 환경의 isProd 분기가 인증/재설정 링크를 가려 사실상 인증 불가였던 제한 해제.
개발·운영 공통으로 전체 링크를 로그로 남긴다. 실 메일러 도입 시 토큰 노출 방지를 위해 반드시 제거 필요(주석 경고 명시).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:34:53 +09:00
ttipo 6767316cdb feat: 정렬·필터 서버 측 동작 구현 및 커스텀 드롭다운 적용
프로젝트 목록·업무 목록·내 업무에 정렬/검색/프로젝트 필터를 백엔드 쿼리 파라미터로 추가(페이지네이션과 일관). 활동 멤버 필터는 전량 로드 피드 기준 클라이언트 처리.
모든 정렬/필터 및 멤버 초대 역할 선택을 네이티브 select → 공통 DropdownSelect 컴포넌트로 통일.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:06:17 +09:00
ttipo 791c74f388 feat: 초기 마이그레이션에 베이스라인 가드 추가(기존 스키마 DB 자동 처리)
이미 스키마가 존재하는 DB(과거 synchronize 로 생성·데이터 보존)에 배포해도
부팅이 실패하지 않도록, up() 진입 시 핵심 테이블(users) 존재를 확인해 DDL 을
건너뛰고 마이그레이션을 '적용됨'으로만 기록한다(데이터/스키마 보존). 빈 DB 는
기존대로 전체 스키마를 생성한다.

검증: 빈 DB 전체 생성 / 스키마 존재 DB(이력 없음) 건너뛰고 기록·데이터 보존 /
재실행 멱등(No migrations pending) 모두 확인.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 02:06:50 +09:00
ttipo 43254fc388 style: 백엔드 lint 포맷 정리 (eslint --fix)
마이그레이션 셋업 중 백엔드 lint 실행으로 정리된 포맷(줄바꿈/따옴표) 반영. 동작 변경 없음.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 01:54:30 +09:00
ttipo 0d453fc426 chore: TypeORM 마이그레이션 셋업 + 초기 스키마 마이그레이션
운영(synchronize 비활성, migrationsRun 활성)에서 스키마를 구성할 마이그레이션이
전무해 신규 운영 DB 배포 시 테이블이 생성되지 않던 문제를 해결한다.

- data-source.ts(CLI 전용 DataSource) + package.json 마이그레이션 스크립트
  (typeorm/migration:generate/create/run/revert) 추가
- 초기 마이그레이션(CreateInitialSchema) 생성 — 전체 스키마(users·projects·
  project_members·tasks·task_assignees·checklist_items·comments·attachments·
  activities·notifications·refresh_sessions·ai_conversations·ai_messages,
  인덱스·FK 포함), uuid-ossp 확장 생성 보장
- 임시 빈 DB에 up/down 적용 검증, 현재 dev 스키마와 드리프트 없음(No changes) 확인

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 01:54:15 +09:00
ttipo 3b78574855 feat: 수정 요청 메시지를 선택 사항으로 변경
수정 요청 시 공용 메시지 입력을 필수에서 선택으로 완화한다.

- 백엔드: ApprovalChangesDto.note 를 선택값으로 변경, requestChanges 가
  빈 메시지면 changesNote 를 null 로 저장
- 프론트: 제출 버튼 비활성(메시지 필수) 해제, 빈 메시지는 미전송,
  안내/라벨 문구를 '선택'으로 정리

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:42:37 +09:00
ttipo 9d004dd64e feat: 할 일 항목별 보완 내용 개별 입력·표시 추가
공용 수정 요청 메시지는 그대로 두고, 검토 모달에서 각 보완 항목 아래에
항목별 보완 내용을 개별 등록할 수 있게 하고 상세 화면에 함께 표시한다.

- 백엔드: ChecklistItem.reviewNote 컬럼 추가, ChecklistReviewDto.note 수용,
  requestChanges 가 미완성 항목에 보완 내용 보관(완료/승인 시 비움), 상세 응답 동봉
- 프론트: 검토 모달 보완 항목 아래 보완 내용 textarea(개별), 상세 체크리스트
  보완 항목 아래 보완 내용 표시. 공용 메시지 입력은 유지
- 테스트: 미완성 항목 보완 내용 보관·완료 항목 비움 검증(9 tests)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:26:48 +09:00
ttipo 79e0c820ba feat: 수정 요청 시 할 일 항목별 완료 체크/X 검토 추가
지시자가 승인을 검토하며 수정 요청할 때, 단순 텍스트 입력에 더해
할 일 목록에서 어떤 항목이 완성/미완성인지 체크(✓)·X로 표시할 수 있게 한다.

- 백엔드: ApprovalChangesDto 에 항목별 검토 결과(checklist) 추가,
  requestChanges 가 전달된 항목만 done 상태로 반영(미전달 항목은 보존)
- 프론트: 수정 요청 모달에 카테고리별 체크/X 토글 UI 추가, 검토 결과를
  note 와 함께 전송
- 테스트: 전달된 항목만 완료 체크가 반영되는지 검증(9 tests)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 16:46:03 +09:00
ttipo c86c04f99e feat: 체크리스트를 필수·관리·보안·부가 카테고리 탭으로 구조화
업무 지시(생성/편집)·상세 화면의 '해야 할 일'을 AI 추천과 동일한
4개 영역(필수/관리/보안/부가)으로 분류한다.

- 백엔드: ChecklistItem 에 category 컬럼(기본 '필수') 추가, 생성/동기화
  DTO·서비스·응답에 category 반영
- 프론트: 생성/편집은 카테고리 탭 + 탭별 추가 입력, AI 추천 항목은
  소속 영역으로 매핑해 일괄 추가, 상세는 카테고리별 그룹 표시

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 16:18:37 +09:00