Commit Graph

194 Commits

Author SHA1 Message Date
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 30b4b87368 Merge feature/video-meeting-ui: 화상회의 UI 개편 및 코드·로직 검토 반영 2026-06-24 17:57:01 +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 251830d421 Merge feature/video-meeting-phase6: 화상회의 단계 6(셀프호스팅 배포 구성) 2026-06-23 22:27:10 +09:00
ttipo c5a8fc8697 feat: 화상회의 단계 6 — 셀프호스팅 배포 구성/가이드
운영에서 LiveKit(SFU)을 직접 호스팅하기 위한 구성과 배포 가이드를 추가한다.
실제 배포는 서버/도메인/TLS 가 필요하므로, 클론 후 값만 채워 배포하는 템플릿 형태.

- docker-compose.yml: livekit 서비스를 opt-in 프로파일(selfhost-livekit)로 추가.
  기존 dev/운영 동작 불변(프로파일 미지정 시 미생성). 시그널링 7880 은 expose(프록시 TLS 라우팅),
  미디어 7881/tcp·7882/udp 는 호스트 공개. 키는 LIVEKIT_KEYS env 주입.
- livekit/livekit.prod.yaml: 단일 노드 운영 설정(use_external_ip, tcp/udp 포트).
  클러스터링용 redis 블록은 비밀값이라 주석으로 두고 배포 시 사본에 채움.
- .env.production.example: LIVEKIT_* 섹션(셀프호스팅/ Cloud 양쪽 안내).
- livekit/DEPLOY.md: 사전준비/키생성/기동/프록시(wss)/방화벽/검증(load-test)/
  멀티노드 클러스터링(Redis·캐스케이딩)/운영팁.
- .gitignore: livekit/*.local.yaml(클러스터 설정 사본) 제외.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 22:27:10 +09:00
ttipo b40e795664 Merge feature/video-meeting-phase5: 화상회의 단계 5(부하 테스트 도구) 2026-06-23 21:05:37 +09:00
ttipo 88ef92628d feat: 화상회의 단계 5 — 부하 테스트 도구/방법론
livekit-cli(lk load-test) 기반 부하 테스트 러너와 가이드를 추가한다.
다수 publisher/subscriber 를 시뮬레이션해 SFU 부하·미디어 품질을 측정한다.

- livekit/load-test.sh: docker 로 lk load-test 실행. preset(smoke|medium|full)
  + 환경변수 조정(PUBLISHERS/SUBSCRIBERS/DURATION/RESOLUTION/LAYOUT/SIMULCAST).
  자격증명은 루트 .env 에서 읽는다.
- livekit/LOAD_TEST.md: 실행/결과 해석/합격 기준/실행 대상(Cloud=smoke만,
  셀프호스팅=full·클러스터 부하분산)/서버 모니터링/한계 정리.

검증: smoke(2 pub + 3 sub, 15s) 를 dev(LiveKit Cloud) 대상으로 실행 →
6/6 트랙 구독, 패킷 손실 0%, 정상 비트레이트 확인(도구 동작 검증 완료).
대규모(160명) 검증은 단계 6 셀프호스팅 환경에서 수행한다.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 21:05:36 +09:00
ttipo 423f2197ed Merge feature/video-meeting-phase4: 화상회의 단계 4(방 관리) 2026-06-23 20:48:52 +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 2416f728ba Merge feature/video-meeting-phase3: 화상회의 단계 3(화면 구성, 임시 레이아웃) 2026-06-23 18:06:23 +09:00
ttipo 3c7aec86a2 feat: 화상회의 단계 3 — 화면 구성(임시 레이아웃)
진행자/승격 참여자 큰 화면 + 썸네일 + 페이지네이션 + 화면 승격을 구현한다.
(레이아웃은 임시 — UI 는 추후 개편 예정. 데이터/로직만 확정.)

- useLiveKitRoom: featuredIdentity 상태 + promote(identity) 추가.
  진행자가 데이터 채널로 승격을 브로드캐스트 → RoomEvent.DataReceived 로 전원 동기화.
- MeetingPage:
  · stage(featured: 승격>진행자>첫 참여자) 큰 화면 + 나머지 썸네일
  · 진행자에게만 썸네일 "크게"(승격) 버튼
  · 썸네일 페이지네이션(페이지당 12명) — 현재 페이지만 렌더 →
    adaptiveStream 이 안 보이는 참여자 미디어 자동 미구독(대역폭 절약)
- 화상회의_화면명세.md: 단계 2~3 반영(범위/화면 구성/데이터 계약 갱신)

백엔드 변경 없음(승격은 클라이언트 데이터 채널).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 18:06:23 +09:00
ttipo 9a4d287ffd Merge feature/video-meeting-phase2: 화상회의 단계 2(권한 분기) 2026-06-23 17:51:22 +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 85b6b65e24 Merge feature/video-meeting-phase1: 화상회의 단계 1(인프라 토대) 2026-06-23 17:40:18 +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 145b4611d3 Merge tweak/detail-hide-ai-badge: 상세 화면 AI 배지 제거 2026-06-23 15:23:48 +09:00
ttipo c52630af27 refactor: 프로젝트 상세 화면에서 AI 반영 상태 배지 제거
반영 상태는 생성·설정 화면의 업로드 시점에 이미 확인 가능하므로,
조회 전용인 상세 화면 문서 목록에서는 배지를 노출하지 않는다.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 15:23:48 +09:00
ttipo 3f2a72ab31 Merge feature/attachment-ai-badge: 문서 AI 반영 상태 배지 2026-06-23 15:10:21 +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 d964f9101a Merge feature/ai-read-project-docs: AI 추천에 프로젝트 문서 반영(DB 캐싱) 2026-06-23 14:54:44 +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 87c9019446 Merge: 프로젝트 문서 첨부(생성/편집/상세) 2026-06-23 14:32:35 +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 916dd49887 Merge: 일정 참석자 알림(지정/수정/삭제) 2026-06-23 13:56:04 +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 a344945e4e Merge: 사이드바 순서 변경 + 기본 진입 일정으로 2026-06-23 13:39:38 +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 ad950e88ad Merge: 일정 주간/월간 뷰 + 내 일정 필터 + 부가 개선
feature/schedule-month-view 머지 — 주간/월간 전환, 월간 달력 그리드(+N 팝오버),
'내 일정' 필터(참석자 기준), 필수 입력 표기·참석자 필수, 월간 바 두께.
2026-06-23 13:34:03 +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 3f134ccca3 Merge: 일정 타임라인 빈 상태 표 영역 가득 채우기
tweak/schedule-empty-fill 머지.
2026-06-23 12:05:17 +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 f2ead261c8 Merge: 일정 기본(시드) 카테고리 제거
chore/schedule-remove-default-categories 머지 — 기본 카테고리 시드 삭제(사용자 추가 방식).
2026-06-23 11:49:19 +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 b9d1990162 Merge: 토큰 갱신/로그인 유지 수정 + 일정/업무 UI 다듬기
fix/auth-token-refresh 머지:
- 401 자동 refresh 가 /auth/me 에서 동작하도록 수정(짧은 로그아웃 해결)
- keepLogin 구현(persistent 세션 플래그·세션/영속 쿠키)
- 일정 모달 placeholder·참석자 콤보 UI, AvatarStack 공용화(일정 참석자·업무 담당자)
2026-06-23 11:33:42 +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 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 0cdd2bb98e Merge: 보고서 모달·일정 화면 다듬기 + 일정 컴포넌트 rem 통일
tweak/schedule-polish 머지 — 보고서 모달(전체탭 제거·상태버튼 축소·푸터 배경),
일정 화면 여백/마진 정리, 일정 하위 컴포넌트 px→rem 일괄 변환.
2026-06-23 10:36:50 +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 61d247de08 Merge: 프론트 공용화 리팩터 (Phase 0-4)
refactor/frontend-commonization 머지 — useClickOutside/useDropdownMenu 훅,
BaseModal/SegmentedTabs/LoadMore/EmptyState 공용 컴포넌트, .form-*/.empty-state
전역 클래스, 작업상태 SSOT(workStatus) + 업무상태 'todo' 라벨 '대기' 통일.
2026-06-23 00:10:16 +09:00
ttipo ddf94b188b Merge: 전사 공유 일정(주간 캘린더) 기능
feature/schedule-calendar 머지 — 백엔드 Schedule 모듈 + 프론트 일정 화면
(주간 타임라인·카테고리/일정 CRUD·상세 패널·모달) + UI 다듬기.
공용화 리팩터(refactor/frontend-commonization)는 별도 브랜치로 분리 유지.
2026-06-23 00:09:12 +09:00
ttipo daff61c85e refactor: 업무 상태 'todo' 라벨을 '대기' 로 통일 (Phase 4)
todo 표기가 화면마다 '할 일'(taskStatusLabel) vs '대기'(ProjectDetail 배지)로
갈리던 불일치 해소. SSOT(format.taskStatusLabel)를 '대기' 로 통일하고
ProjectDetailPage 의 자체 STATUS_LABEL 제거 → taskStatusLabel 사용.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 00:02:42 +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