Commit Graph

52 Commits

Author SHA1 Message Date
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 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 f7b8a4c42d refactor: 활동 피드 공용 컴포넌트 추출 및 드라이브 기록 디자인 통일
프로젝트 활동 페이지에 인라인 중복 구현돼 있던 활동 피드(원형 아이콘+
아바타+본문+날짜그룹) 마크업·아이콘맵·스타일을 공용 ActivityFeed로
추출한다. 드라이브 '기록' 패널도 단색 점 목록에서 이 공용 컴포넌트로
교체해 다른 화면과 동일한 디자인 시스템을 쓰도록 통일한다.

- 신규 components/common/ActivityFeed.vue (모달 임베드용 bare 옵션 포함)
- ProjectActivityPage: 인라인 피드 제거 → ActivityFeed 사용
- drive.store: 감사 로그를 ProjectActivityDay[]로 변환(activityDays),
  tone setting·repo/pencil 아이콘, escapeHtml로 XSS 방지

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 23:03:43 +09:00
ttipo 2cbd19e791 refactor: 프로젝트 상세 AI 검토 문서를 버튼+모달 리스트로 변경
인라인 wrap 나열 제거, 'AI 검토 문서' 버튼 클릭 시 모달에서 파일종류 배지·AI상태·크기를 세로 리스트로 표시

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 20:56:01 +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 f023bd0ed7 fix: 화상회의 로비 입장/링크복사 버튼 순서 변경
입장 버튼을 맨 왼쪽으로, 그다음 링크복사 순으로 배치

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 18:21:07 +09:00
ttipo 638d1265f6 feat: 드라이브(파일 저장소) 프론트엔드 UI 구현
폴더 탐색·드래그앤드롭 업로드(presigned 3단계)·다운로드·이름변경/삭제, 라우트(/drive)·사이드바 네비 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-30 18:20:51 +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 2015b794fc feat: 화상회의 수신 화질을 스테이지 역할 기반으로 중앙 제어
스테이지(featured) 유저만 HIGH, 나머지 원격 참여자는 LOW 로 구독한다.
스테이지가 바뀔 때마다 강등된 참여자가 LOW 로 내려가도록 보장한다.

- adaptiveStream 비활성화: 화질이 "엘리먼트 크기"가 아니라 "스테이지 역할"로만
  결정되도록(스테이지=HIGH 보장, 강등 반영 결정적). dynacast 는 유지.
- 화질 제어를 useLiveKitRoom 로 중앙화: featuredIdentity 변경·참여자/트랙 변동
  시마다 전원 재적용. 기존 타일 리마운트 의존(레이스로 강등자가 HIGH 유지되던 버그) 제거.
- ParticipantTile 의 개별 setVideoQuality 제거(중복·레이스 원인), 영상 연결만 담당.

검증: type-check, lint 통과.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 15:15:40 +09:00
ttipo 9ad10afca6 feat: 화상회의 목록 소유자 표기를 아바타+이름으로 통일
회의방 목록의 진행자(소유자) 표기를 별 아이콘+이름에서 UserAvatar(프로필
이미지/실루엣)+이름으로 변경 — 프로젝트·업무 화면의 사람 표기 방식과 일치.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 21:18:13 +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 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 511efec64d feat: 일정 — 내 일정 필터 + 월간 '+N' 팝오버 + 필수표기/참석자 필수
- '내 일정' 토글 추가(참석자 기준: 전 직원 또는 참석자에 본인 포함). 주간/월간 공통
- 월간 '+N' 클릭 시 그날 전체 일정 팝오버(Teleport, 바깥클릭/ESC 닫기, 클릭→상세)
- 일정/카테고리 모달 필수 입력 표기(*) — 제목·유형·시작일·종료일·시간·참석자, 카테고리 이름
  (.form-label .req 전역 스타일 추가)
- 참석자 필수화(폼 검증: 전 직원 또는 1명 이상)
- 월간 일정바 두께 소폭 증가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 13:33:52 +09:00
ttipo 4ff746638c feat: 일정 주간/월간 뷰 전환 추가
상단 토글(주간/월간)로 보기 전환. 월간은 전통 달력 그리드(주×요일).

- scheduleDate: firstOfMonth/addMonths/monthLabel/monthGridWeeks(+MonthDay) 추가
- schedule.store: viewMode + anchor 기반으로 주간/월간 범위·라벨·네비 일원화
  (goPrev/goNext 가 모드별로 주/월 이동, setViewMode 시 범위 재조회)
- ScheduleMonthGrid: 주 단위 다중일 막대(레인 패킹) + 초과 '+N',
  카테고리 색·유형 필터·오늘/주말/이전·다음달 표기, 막대 클릭 시 상세 패널
- SchedulePage: SegmentedTabs 토글 + 주간/월간 조건부 렌더

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 12:45:52 +09:00
ttipo cc06fd9816 style: 일정 타임라인 빈 상태가 표 영역을 가득 채우도록
카테고리가 없을 때 안내 문구를 상단에 작게 두지 않고, 본문(min-height:100%)과
.tl-empty(flex:1)로 표 영역 전체 높이를 채워 중앙 배치.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 12:03:18 +09:00
ttipo a094283e62 style: 일정/업무 UI 다듬기 — placeholder·참석자 콤보·AvatarStack 공용화
- 새 일정 모달 placeholder 를 프로젝트 기존 톤("~을 입력하세요/선택하세요")으로 통일
- 참석자 등록을 업무 생성의 담당자 등록과 동일한 콤보(검색 입력+후보 드롭다운+선택 칩)로 개편
  ('전 직원'은 드롭다운 항목/단일 칩으로 처리)
- 아바타 스택+오버플로(+N)+팝오버 목록을 공용 AvatarStack 으로 추출,
  일정 상세 참석자 + 업무 상세 담당자 카드에 적용(담당자 팝오버에 역할 표시)
- 사용처가 사라진 ScheduleAvatar 삭제(UserAvatar/AvatarStack 으로 일원화)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 11:32:58 +09:00
ttipo 7568637de2 style: 보고서 모달·일정 화면 다듬기 + 일정 컴포넌트 rem 통일
- 보고서 모달: '전체' 탭 제거(기본=항목 있는 첫 분류), 상태 드롭다운 크기 축소,
  푸터 배경을 다른 모달(.bm-foot)과 동일한 #fafbfc 로 통일
- 일정 화면: 콘텐츠 영역 상하좌우 여백 1.5rem 통일, 툴바 가로 여백 정렬 +
  전역 .toolbar 의 margin-bottom 상속 차단(상단 여분 마진 제거)
- 일정 하위 컴포넌트 px→rem 일괄 변환(보더 width·box-shadow 는 px 유지),
  ScheduleAvatar 도 size 를 rem 렌더 → '전 직원' 표식과 아바타 크기 정합 유지

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 00:38:59 +09:00
ttipo 3380bbdcff refactor: 작업자 진행상태(대기/진행중/완료) 라벨·색 SSOT 통합 (Phase 4)
StatusSelect 와 업무 상세에 각각 중복 정의되던 WORK_STATUS_META/ORDER 를
shared/constants/workStatus.ts 단일 소스로 추출(값 동일, 표시 변화 없음).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 00:00:21 +09:00
ttipo 21cabbe20b refactor: ScheduleAvatar 를 UserAvatar 어댑터로 정리 (Phase 3-4)
ScheduleAvatar 가 복제하던 이미지/실루엣 렌더링을 제거하고, 공용 UserAvatar 를
size(px)로 감싸는 얇은 래퍼로 전환. 아바타 표시 로직을 UserAvatar 단일 소스로 통일
(.sa 클래스는 상세 패널 스택 CSS 훅으로 유지, 사용처 변경 없음).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:57:22 +09:00
ttipo d3e2861440 refactor: 빈/상태 메시지 박스 EmptyState 로 통일 (Phase 3-3)
목록 로딩·빈 상태 박스(state-msg/feed-state/list-empty)를 전역 .empty-state
카드 스타일 + EmptyState 컴포넌트로 통일. 5개 페이지(프로젝트 목록/상세,
내 업무, 활동, 멤버)의 중복 박스 CSS 제거.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:56:07 +09:00
ttipo e7760e9ddc refactor: 더보기 버튼 LoadMore 컴포넌트로 통일 (Phase 3-2)
목록 페이지네이션 '더보기' 버튼 마크업/문구를 LoadMore 로 추출(전역 .load-more 사용).
- 프로젝트 목록/상세, 내 업무, 프로젝트 활동 4곳 적용
- 활동 화면의 비표준 .more-row/.more-btn 제거하고 표준 .load-more 로 통일
  ('더 보기' → '더보기' 문구도 통일)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:51:45 +09:00
ttipo d3a916dbd7 refactor: 세그먼트 토글 SegmentedTabs 컴포넌트로 통일 (Phase 3-1)
3개 페이지(활동/프로젝트 상세/멤버)에 반복되던 .segmented 버튼 마크업을
options 기반 SegmentedTabs 로 추출(전역 .segmented 스타일은 그대로 사용).
수량(.n) 표기는 option.count 로 처리.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:49:09 +09:00
ttipo 7425d57134 refactor: Teleport 드롭다운 위치/토글 로직 useDropdownMenu 로 통합 (Phase 2)
DropdownSelect·StatusSelect 에 중복되던 fixed 위치 계산·open 토글·
바깥클릭/ESC/스크롤 닫기 로직을 useDropdownMenu 훅으로 추출.

- composables/useDropdownMenu.ts 신설 (align/matchWidth/gap/esc 옵션)
- DropdownSelect, StatusSelect 가 훅을 사용하도록 정리
- 절대배치 메뉴(일정 멀티셀렉트/유형필터/참석자 스택)는 Phase 0 useClickOutside 로 이미 공유

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:44:48 +09:00
ttipo 0c4f0817e6 refactor: 멤버 초대 모달을 BaseModal 로 전환 (Phase 1b)
- 중복 모달 셸/헤더/푸터/필드 CSS(~150줄) 제거 → BaseModal + 전역 .form-*
- BaseModal 에 subtitle 슬롯 + 푸터 버튼 아이콘 정렬 추가
- 본문 폼은 display:contents 로 BaseModal 본문 간격 상속,
  고유 요소(자동완성/역할 힌트/푸터 노트)만 .invite-body 로 재스코프

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:41:41 +09:00
ttipo 5f8b2597bf refactor: 공용 폼 모달 BaseModal + 전역 .form-* 필드 클래스 (Phase 1a)
- components/common/BaseModal.vue 신설: backdrop+헤더(제목/닫기)+본문/푸터 슬롯,
  Teleport·ESC·바깥클릭 닫기 내장. 전역 .modal* 와 충돌 없게 bm- 접두 클래스 사용
- relay.css 에 .form-field/-label/-row/-input/-textarea/-select(+wrap/chev) 표준 추가
- 일정·카테고리 모달을 BaseModal + .form-* 로 전환(중복 모달/필드 CSS 제거,
  푸터 버튼은 전역 .mbtn 사용)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:35:33 +09:00
ttipo 92ed6c332f refactor: 바깥클릭/ESC 닫기 공용 훅 useClickOutside 도입 (Phase 0)
11곳에 복붙되던 document 리스너 등록/해제·바깥클릭·ESC·스크롤 닫기 로직을
useClickOutside 훅으로 통합. 재사용 드롭다운 5종에 우선 적용.

- composables/useClickOutside.ts 신설 (event/esc/closeOnScroll 옵션)
- 적용: DropdownSelect, StatusSelect, ScheduleTypeFilter,
  ScheduleDetailPanel(참석자 스택), ScheduleEventModal(참석자 멀티셀렉트)
- 모달 ESC·페이지 레벨 오버레이 조정 로직은 후속 단계(BaseModal)에서 처리

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:31:45 +09:00
ttipo d5b54c1afb style: 일정 화면 UI 다듬기 (기존 디자인 규격에 통일)
- 툴바 '오늘' 버튼 제거, 컨트롤 간격을 전역 .toolbar(0.625rem)와 통일
- 타임라인 레인 hover 편집/삭제 버튼 제거(카테고리 관리는 유형 필터에서)
- 유형 필터 버튼을 기존 DropdownSelect pill 디자인으로 통일 + 수량 뱃지 제거
- '새 일정' 버튼의 스코프 .btn 재정의 제거 → 전역 .btn primary 표준 사용
- 일정/카테고리 모달을 사이트 폼 모달 규격(rem + 멤버 초대 모달)으로 재정렬,
  라벨 좌측정렬, 취소/저장 버튼을 전역 .mbtn 으로 통일
- 참석자 아바타를 공용 UserAvatar 방식(이미지/실루엣)으로 교체,
  '전 직원' 표식 크기를 아바타와 동일(20px)하게 고정(루트 14px rem 보정)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 23:24:33 +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 1f7d7dcebb feat: 진행 보고 작성/열람 모달 UI 개편(시안 반영)
시안(진행 상태 보고서/받은 진행 보고) 기준으로 두 모달 전면 개편:
- 전체 탭 + 카테고리 탭(색상 dot·건수), 전체 탭에서 카테고리별 그룹 헤더로 묶음
- 작성(작업자): 항목별 상태색 드롭다운(StatusSelect, body Teleport), 보고 메모, 하단 상태 요약(대기/진행/완료 N)
- 열람(지시자·담당자): '진행 보고 도착' 태그 + 보고자 아바타/이름/점검일·시각, 읽기전용 상태 배지, 보고자 메모 박스
- 신규 StatusSelect 컴포넌트 추가, 기존 cp-modal/ws-* 스타일 정리, 미사용 DropdownSelect import 제거

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:19:08 +09:00
ttipo 69708b2b8b fix: 드롭다운 메뉴를 body Teleport(fixed)로 띄워 잘림 해결 + 보고 모달 높이 확대
DropdownSelect 메뉴를 absolute → body Teleport + fixed 위치 계산으로 변경.
스크롤/overflow 컨테이너(보고 모달 등) 안에서도 메뉴가 잘리지 않음(스크롤·리사이즈 시 닫힘).
보고 열람/작성 모달 max-height 를 calc(100vh - 3rem) 로 확대.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 16:26:57 +09:00
ttipo 433901312d feat: Relay 브랜드 로고·favicon 전면 적용
사이드바·인증 화면 'R' 텍스트 로고를 브랜드 더블 셰브론 마크(RelayMark)로 교체.
favicon을 SVG+PNG+ICO 세트와 웹 매니페스트로 정비(create-vue 기본 아이콘 대체), 가운데 셰브론을 키워 작은 크기 가독성 개선.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 12:06:31 +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 b1afe5480c feat: alert/confirm을 공통 모달 다이얼로그로 통합
모든 window.alert/confirm 을 업무 삭제 모달과 동일한 구조/디자인의
공통 모달로 대체하고, 한 곳에서 관리하도록 다이얼로그 시스템을 도입한다.

- dialog.store: 알림/확인 큐 + Promise 기반 상태 관리
- useDialog(): alert/confirm 컴포저블 API
- AppDialog: App 루트 1회 마운트, 전역 .modal-*/.mbtn 클래스로 렌더
  (variant: info/success/warning/danger → 아이콘·버튼 색)
- relay.css: modal-ico.info/.warning, mbtn.primary 변형 추가
- 교체: API 에러 알림(useApi), 검증/성공 알림(Signup/ProjectCreate/
  ProjectSettings/TaskCreate), 삭제·제거 확인(ProjectSettings/
  ProjectMembers/AgentChatPanel)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 22:46:28 +09:00
ttipo f6b01b01a8 fix: 프로젝트 헤더에서 설명 없을 때 제목·메타 사이 빈 여백 제거
ProjectHeader(상세·멤버·활동·설정 공용)의 .rh-desc 가 설명이 비어도 빈 줄로
렌더되어 제목과 프로필·업데이트 시간 사이 간격이 벌어지던 문제 수정 —
설명이 있을 때만 렌더. (프로젝트 목록과 동일 처리)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 22:04:08 +09:00
ttipo ef1757181d refactor: 저장소(Repo)·Gitea 연동 기능 완전 제거 — 프로젝트 단독 구조
복잡도를 줄이기 위해 저장소 연동 기능을 전부 제거하고 프로젝트를 단독 단위로 운영한다.

백엔드:
- modules/repo, modules/gitea 모듈 전체 삭제(엔티티/서비스/컨트롤러/DTO/역방향 동기화)
- app.module 에서 RepoModule 제거
- ProjectService: repoCount 집계·repoRepo·"미분류" 시스템 프로젝트(ensureUnassigned/
  onApplicationBootstrap)·isSystem 게이트 전부 제거. ProjectResponse 에서 repoCount/isSystem 제거
- Project 엔티티 is_system 컬럼 제거(dev synchronize 가 DROP)
- ProjectModule forFeature 에서 Repo 제거
- Activity 타입 repo.linked/unlinked 제거
- env example 에서 Gitea/REPO_IMPORT 섹션 제거

프론트:
- ProjectReposPage, useRepo 삭제. ProjectTabs 의 '저장소' 탭·repoCount prop 제거
- 4개 페이지의 :repo-count 제거, 라우트 /projects/:id/repos 제거
- types/project·project.store·mock 에서 repoCount/isSystem·Repo 뷰 제거
- ProjectListPage 저장소수/시스템 배지·gitea CSS 제거, activity 의 repo.* 케이스 제거
- types/repo 는 공용 ApiMember 만 유지
- 잔여 '저장소'/Gitea 사용자 문구를 프로젝트로 정리

검증: 백엔드 build/lint 0·8 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:44:15 +09:00
ttipo 58c0b9510d refactor: 프로젝트에서 공개 범위(visibility) 제거
불필요한 프로젝트 공개 범위 데이터를 백엔드·프론트에서 일괄 제거(저장소 Repo 의 visibility 는 Gitea 연동용이라 유지).

백엔드:
- Project 엔티티 visibility 컬럼·ProjectVisibility 타입 제거(dev synchronize 가 컬럼 DROP)
- create/update DTO, ProjectResponse, create/update/ensureUnassigned/toResponse 에서 제거
- ai.service 프로젝트 컨텍스트 문자열에서 공개범위 표기 제거
- activity 타입 project.visibility_changed 제거

프론트:
- types/project·project.store·mock Project 에서 visibility 제거
- ProjectCreatePage·ProjectSettingsPage 공개 범위 입력 폼+CSS 제거
- ProjectListPage 공개여부 세그먼트 필터+배지 제거, ProjectHeader 배지 제거
- activity 타입/스토어 project.visibility_changed 케이스 제거

런타임 검증: DB projects.visibility 컬럼 DROP, 생성/목록 응답에 visibility 없음, CRUD 정상.
백엔드 build/lint 0·14 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:39:48 +09:00
ttipo 8a2e34d617 feat: 프로젝트 저장소 연동 UI + 화면 문구 정리 (Phase 2b)
- useRepo 복구(프로젝트 종속 /projects/:projectId/repos): list/meta/checkName/create/remove
- types/repo ApiRepo 를 새 RepoResponse 와 정합(projectId 추가, members/counts 제거)
- ProjectTabs 에 '저장소' 탭(repoCount) 추가, 4개 페이지 repo-count 전달
- ProjectReposPage 신설(/projects/:id/repos): 연동 저장소 목록 + 연동(생성) 폼
  (slug 가용성 디바운스 체크·템플릿 옵션) + Gitea 열기 + 연동 해제(admin)
- 화면 잔여 '저장소' 문구를 '프로젝트'로 정리(crumb/설정/멤버 모달/주석/not-found)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:52:23 +09:00
ttipo 7cd6676ef9 feat: 프론트엔드 프로젝트 우선 구조 재구성 (Phase 2-a)
- 데이터 계층 신설: types/project·useProject·project.store (프로젝트 CRUD·페이지네이션)
- useTask/useMember/useActivity 경로를 /projects/:projectId/... 로 전환
- task·my-task·activity 타입/스토어 repoId/repoName → projectId/projectName
- 알림 store 라우팅·문구 프로젝트화, 활동 타입 repo.linked/unlinked·project.* 반영
- 라우팅 /projects/... 전면 교체, / → /projects 리다이렉트
- 페이지 rename Repo*→Project* (List/Create/Detail/Settings/Members/Activity)
- ProjectCreatePage: Gitea 저장소 폼 → 단순 프로젝트 생성 폼으로 재작성
- RepoHeader→ProjectHeader, RepoTabs→ProjectTabs, AppShell 네비 프로젝트화
- 죽은 파일 삭제: repo.store·useRepo·GiteaRepoBar

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:37:51 +09:00
ttipo 51b608efeb feat: AI 채팅 다듬기 — 저장소 컨텍스트·표시제목·마크다운 렌더·말투
- 컨텍스트에 저장소 목록 추가(RepoService.findAll), slug 빼고 표시 제목만 노출
- AI 응답 마크다운 렌더링: shared/utils/markdown.ts (escapeHtml 후 신뢰 태그만 조립, 의존성 0, XSS 안전 — 굵게/목록/코드/제목/링크)
- 시스템 프롬프트: 간결·이모지/사과 남발 금지·과도한 존칭 지양·마크다운 사용·저장소는 표시제목으로 안내

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 15:03:59 +09:00
ttipo e432832c92 feat: AI 대화 DB 영속 + 다중 대화/기록 목록
- AiConversation·AiMessage 엔티티(user/conversation CASCADE), 제목 자동 생성
- 대화 스코프 API: GET/POST /ai/conversations, GET/:id, POST/:id/messages, DELETE/:id (전부 본인 소유 스코프 → IDOR 차단)
- 클라는 새 메시지 1건만 전송, 서버가 DB 이력으로 Claude 호출(무상태 /ai/chat 폐기)
- Claude 실패 시 사용자 메시지·빈 대화 롤백(고아 데이터 방지)
- 프론트: AgentChatPanel 채팅↔지난 대화 목록 뷰(열기/삭제), ai.store 재작성(목록·현재 대화)
- 다른 기기·재로그인·새로고침에도 기록 유지

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 08:58:40 +09:00
ttipo 5da3ec8818 feat: AI 채팅 패널 (Claude API)
- 백엔드 modules/ai: POST /ai/chat — Anthropic Messages API 프록시(키 서버 env 한정, 30s 타임아웃, throttle 20/분, 미설정 503/외부오류 502)
- ChatDto 검증(role enum, content 8000자, 메시지 50개 상한), 모델 CLAUDE_MODEL(기본 claude-sonnet-4-6)
- 프론트 AgentChatPanel: 우측 슬라이드 채팅(말풍선/타이핑/에러/새 대화), plain text 렌더로 XSS 안전
- AppShell 헤더에 AI 버튼 상시 노출 — 모든 화면에서 열림
- 대화 이력은 stores/ai.store(싱글톤)에 보관 → 화면 이동에도 유지, '새 대화'로만 초기화
- TaskDetailPage 의 기존 목업 에이전트 패널 전부 제거(보안 L1 해소)
- env.example 에 CLAUDE_MODEL 추가

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 00:04:48 +09:00
ttipo 8935f6e488 feat: 저장소 설정 권한 게이팅 + Gitea 역동기화 + 설정 로직 정비
설정 탭/권한:
- findOne/update 응답에 현재 사용자 권한(canManage=admin, isOwner=소유자) 포함
  (목록은 캐시·사용자 무관이라 미포함). RepoTabs 가 canManage 일 때만 설정 탭 노출,
  비관리자의 설정 화면 직접 진입은 저장소 상세로 리다이렉트. 입력 비활성화 처리 제거.
- 저장소 삭제는 isOwner 일 때만(비소유자 admin 엔 비활성+안내).

설정 로직 버그/정리:
- update 가 이전 값과 비교해 실제 변경분만 활동/Gitea/캐시 반영
  (무변경 저장 시 허위 'renamed'/'visibility_changed' 활동이 쌓이던 버그 수정).
- 위험구역의 미구현 'Gitea 저장소 이름 변경'(slug rename) 스텁 제거.
- 조직 owner 아바타(하드코딩 'M') 제거.

Gitea 역동기화(Gitea=SSOT):
- 동기화 매칭 시 slug(rename)·공개범위·기본 브랜치를 Gitea 기준으로 끌어옴
  → Gitea 에서 직접 바꿔도 Relay 가 따라가고 slugName 도 갱신돼 쓰기 경로 일치
  (rename 후 옛 이름으로 쓰기 호출이 404 나던 desync 해소). 중복 매칭 가드 추가.
- 표시명/설명은 Gitea 단일 description 합본 구조라 Relay-SSOT 유지(외부 편집 시 깨짐 방지).
- GiteaService.updateRepo 에 default_branch 추가 + update 가 브랜치도 Gitea 에 푸시(양방향 일치).

검증: 백엔드 build/lint 0·20 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 15:39:41 +09:00
ttipo 5351373694 feat: 프로필 이미지(avatarUrl) 필드 + UserAvatar 도입, 색상·이니셜 아바타 제거
- 백엔드: User.avatarUrl(nullable) 추가 → PublicUser/toPublic 로 모든 유저 응답에 노출.
- 프론트: 공용 UserAvatar(이미지 있으면 img, 없으면 사람 실루엣 placeholder)로
  전 화면 아바타 통일(상단바·저장소·멤버·담당자·활동·내업무·댓글·AI패널).
  화면마다 달랐던 색상+이니셜/하드코딩 청록 아바타 제거.
- 죽은 코드 정리: 색상·이니셜 시스템(avatarColor/initialOf/AvatarColor 유틸·타입,
  User/RepoActivityItem 의 initial/color, 전역·컴포넌트 .av-a~.av-f CSS) +
  미사용 정적 목업 데이터(USERS/REPOS/REPO_TASKS/TASK_DETAIL/MY_TASKS_*/REPO_MEMBERS/
  REPO_ACTIVITY 등) 전부 삭제. relay.mock.ts 는 뷰 타입 + countActive 만 유지.

avatar_url 컬럼은 dev synchronize 가 추가. 업로드(실제 사진 설정)는 추후 단계.
검증: 백엔드 build 0, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 14:04:09 +09:00
ttipo 875eddf659 feat: 업무(Task) 모듈 4단계 — CRUD·상태머신·진행률 집계·역할 기반 상세 UI
백엔드
- Task/ChecklistItem 엔티티(저장소 내 순번 seq, jsonb content, assignees ManyToMany, checklist)
- GET/POST/PATCH/DELETE /repos/:repoId/tasks[/:taskId] + POST .../status
- 상태 머신(todo→prog→review→done, review→changes, changes→{review,prog}) + 전이별 역할 검증
- 담당자=저장소 멤버만 지정, 승인 완료(→done) 시 체크리스트 일괄 완료(영속)
- 저장소 doneCount/totalCount·멤버 taskCount 실집계(RepoModule→TaskModule)
- Task.repo FK 컬럼명 snake_case 고정(@JoinColumn), task.service 단위 테스트

프론트
- types/task·useTask·task.store, format.ts 라벨 파생(상태/마감/dday)
- RepoDetailPage 업무 목록, TaskCreatePage 생성/수정 겸용(편집 라우트)
- TaskDetailPage 역할 기반 액션 분기(지시자 승인/수정요청, 담당자 시작/승인요청)
- 액션 카드·상태 뱃지 디자인 시안 정렬, 작성폼/목록 UI 정리

문서: api-contract·integration-progress 4단계 반영

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