Commit Graph

96 Commits

Author SHA1 Message Date
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 9e29d0d0b0 refactor: 업무 삭제 모달을 공통 다이얼로그로 흡수
업무 삭제 모달은 공통 다이얼로그와 동일 구조(아이콘+제목+설명+취소/삭제)라
공통 시스템으로 통합한다. 구조가 다른 할 일 검토 모달은 그대로 유지.

- showDelete·confirmDelete·삭제 모달 마크업 제거
- askDelete() 에서 dialog.confirm(danger, 삭제)로 확인받아 삭제

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 23:35:03 +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 118d12f829 style: 지시자 카드의 지시 시각을 카드 우측 상단으로 이동
업무 상세 지시자 카드 하단에 있던 '언제 지시했는지' 텍스트를 '지시자' 라벨과
같은 줄의 우측 상단으로 배치한다.

- side-head(라벨 + 우측 보조 텍스트) 한 줄 배치 추가, issued-at 우측 정렬
- 하단 .meta-line 제거(미사용 스타일 정리)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 22:16:27 +09:00
ttipo 5c3b1e5ab4 feat: 겹친 담당자 아바타 스택을 최대 3개 + N 형태로 통일
가로로 겹쳐 표시되는 프로필 스택이 3개를 넘으면 마지막에 +N 으로 표기한다.

- 업무 목록(ProjectDetailPage): 담당자 스택 3개 초과 시 +N
- 내 업무(MyTasksPage): 미니 담당자 스택 3개 초과 시 +N(.mini-more 추가)
- 프로젝트 멤버 스택(목록/헤더)은 이미 백엔드 AVATAR_LIMIT=3 으로 동일 처리됨
- 담당자 세로 목록(상세)·칩(지시 작성)은 겹침 스택이 아니라 제외

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 22:12:57 +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 d9a343e5e9 fix: 프로젝트 목록에서 설명 없을 때 제목·메타 사이 빈 여백 제거
설명(desc)이 비어도 .repo-desc 가 빈 줄로 렌더되어 제목과 프로필·업데이트
시간 영역 사이 간격이 과하게 벌어지던 문제 수정 — 설명이 있을 때만 렌더.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:48:09 +09:00
ttipo 32815a0627 fix: 로그아웃을 사이드바 하단 상시 노출 버튼으로 변경
기존 드롭다운 메뉴 안에 숨어 보이지 않던 로그아웃을 항상 노출되는
버튼으로 변경한다.

- 사용자 프로필 행은 표시 전용, 아래에 로그아웃 버튼 상시 노출(빨강 호버)
- 접힘 시 아이콘만 표시
- 미사용 드롭다운(menuOpen·me-menu·me-overlay) 로직/스타일 제거

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:40:30 +09:00
ttipo f4a1253526 feat: 사이드바 접기 + 모바일 드로어 + 검색 아이콘 제거
- 데스크톱: 사이드바 접기 토글(아이콘 레일, 상태 localStorage 보존)
  접힘 시 브랜드/네비/사용자가 아이콘만 표시, 메뉴는 레일 우측으로 펼침
- 모바일(≤48rem): 사이드바를 오버레이 드로어로 전환, 툴바 햄버거로 열고
  배경/화면 전환 시 닫힘(접힘 상태는 모바일에서 무시)
- 전역 도구에서 미사용 검색 아이콘 제거

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:33:39 +09:00
ttipo f60f705f56 refactor: 상단 헤더 네비게이션을 좌측 사이드바로 전환
추후 메뉴 확장을 고려해 AppShell 레이아웃을 사이드바 구조로 변경한다.

- 좌측 사이드바: 브랜드 + 세로 네비(내 업무/프로젝트, 아이콘) + 하단 사용자 프로필·메뉴(위로 펼침)
- 우측 본문 컬럼: 슬림 툴바(페이지 액션 슬롯 + AI/검색/알림) + 기존 페이지 슬롯
- 알림 드롭다운·AI 패널 동작은 그대로 유지(.page 본문은 컬럼 내 중앙 정렬)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 21:23:09 +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 70d75cf88d refactor: 승인 요청 흐름에서 메시지 입력·표시 제거
수정 요청(검토 모달)을 제외한 나머지 승인 흐름에서는 메시지를 사용하지 않는다.

- 진행 중 → 승인 요청 카드의 메시지 입력창 제거
- 승인 요청 도착 카드의 승인 메시지 표시 제거
- approvalNote 상태 제거, sendApproval 은 메시지 없이 요청
- 미사용이 된 .ap-note 입력 스타일 정리(수정 요청 메모 .ap-note-box 는 유지)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:28:16 +09:00
ttipo 8b3cebbc62 style: 승인 요청 카드의 설명과 버튼·입력 간격 확대
설명 텍스트와 아래 버튼/입력창이 너무 붙어 보여 간격을 넓힌다.

- 액션 버튼 영역 상단 여백 0.8125rem → 1.125rem
- 승인 메시지 입력창 상단 여백 추가(0.875rem)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:16:07 +09:00
ttipo b387b7e378 refactor: 수정 요청 후 다시 승인 요청 시 메시지 입력 제거
작업자가 수정 요청을 받은 뒤 다시 승인을 요청하는 화면에서
보완 메시지 입력창을 제거한다. (지시자의 수정 요청 내용은 그대로 표시)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 19:03:55 +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 c32df3cf39 feat: 수정 요청 상태에서 보완 항목을 체크박스에 X로 표기
업무가 '수정 요청됨(changes)' 상태일 때 상세 화면 체크리스트의
미완료 항목을 빨강 X(보완 필요)로 표시해 담당자가 보완 대상을 명확히 보게 한다.

- 완료 항목은 초록 체크, 보완 항목은 빨강 X + '보완 필요' 태그
- changes 상태가 아닐 때는 기존대로 체크/빈 박스 유지

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:10:19 +09:00
ttipo 5f0b80593f refactor: 승인/수정 요청을 '할 일 검토' 모달로 단일화
지시자 검토 카드의 '승인하기' 버튼과 별도 승인 모달을 제거하고,
단일 '할 일 검토하기' 버튼이 검토 모달을 열도록 통합한다.

- 검토 카드 버튼을 '할 일 검토하기' 하나로 정리(승인하기 제거)
- 미사용이 된 승인 확인 모달·approveTask·showApprove 상태 제거
- 승인은 검토 모달에서 전 항목 완료 시 '승인 완료' 버튼으로 수행

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 18:06:59 +09:00
ttipo 20f593bd5a feat: 수정 요청 모달을 '할 일 완료 검토' 디자인으로 개편
시안(수정 요청 모달.html)을 반영해 모달 UI를 재구성한다.

- 헤더(그라데이션 배지·안내)·스크롤 본문·고정 푸터 구조로 변경
- 항목별 완료/보완을 세그먼트 토글(완료=초록 / 보완=빨강 좌측 바)로 표시,
  카테고리 그룹·진행률 바·완료 카운트 동봉
- 보완 항목 수에 따라 안내 배너(warn/good)와 메시지 필수 여부가 전환
- 모든 항목 완료 시 버튼이 '승인 완료'(초록)로 바뀌어 곧바로 승인,
  보완 항목이 있으면 '수정 요청 보내기 (N건)'(빨강)으로 메시지와 함께 반려
- 모달 진입 시 전 항목을 '완료'로 초기화(담당자가 완료를 주장하며 요청)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 17:18:53 +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
ttipo 93c8ae2a22 docs: 단독 실행(non-Docker) 관련 내용 전부 제거
Docker 만 사용하므로 standalone 안내를 정리:
- 프론트 env(.example): '단독 실행 시 주석 해제' 안내 + 주석 처리된 VITE 라인 제거
  (VITE_ 값은 compose build arg 로만 주입됨)
- CLAUDE.md: '단독 실행 시 주석 예시 활성화' 규칙 삭제
- README: 사전요구사항/cp 명령/‘단독 실행’ 섹션/env 표의 standalone 문구 제거
  (모바일은 compose 비대상이라 별도 실행 안내만 한 줄 유지)
- integration-progress: Redis 폴백 설명의 '단독 실행' 표현을 'REDIS_HOST 미설정 시'로 교정

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 15:53:31 +09:00
ttipo 1f2a93b01a chore: 모든 env 파일 구조·주석 정리 및 env↔example 규격 통일
- 죽은 설정 제거: GITEA_*/REPO_IMPORT_ADMIN_EMAIL (저장소·Gitea 기능 삭제됨)
- 섹션 헤더 스타일을 # === 블록으로 전 파일 통일(루트/백엔드/프론트)
- 루트: AI 섹션(AI_TIMEOUT_MS) 형식 통일 + production 파일에 누락됐던 AI 섹션 추가
- 백엔드: JWT/앱URL/OAuth/AI 4섹션으로 일관 정렬, production 에 누락됐던 URL/OAuth/AI 추가
- 프론트: 빌드 주입 변수 안내(VITE_API_BASE_URL·VITE_AI_TIMEOUT_MS) 명시
- 각 .env 와 .example 의 키·순서·주석을 동일하게(값만 실값/placeholder 차이)

(실제 .env.* 는 gitignore 라 미커밋 — 로컬에선 동일 구조로 함께 정리함)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 13:21:40 +09:00
ttipo 042d724b3a refactor: AI 타임아웃을 백엔드·프론트 동일 값 + 환경변수(AI_TIMEOUT_MS)로 통일
기존: 백엔드 30초(하드코딩 const)·프론트 60초(하드코딩)로 불일치
변경: 루트 .env 의 AI_TIMEOUT_MS(기본 60000)를 단일 소스로
- 백엔드 ai.service: REQUEST_TIMEOUT_MS const → ConfigService(AI_TIMEOUT_MS) 주입, 기본 60s
- 프론트 useAi: import.meta.env.VITE_AI_TIMEOUT_MS 사용, 기본 60s
- docker-compose: 백엔드 environment 에 AI_TIMEOUT_MS, 프론트 build arg 에 VITE_AI_TIMEOUT_MS=${AI_TIMEOUT_MS}
- 프론트 Dockerfile ARG/ENV, env.d.ts 타입, 루트 .env.example 추가
미설정/비정상 값이면 양쪽 모두 기본 60초로 폴백.

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 13:01:51 +09:00
ttipo ff33fb2e6c fix: AI 추천 프론트 타임아웃(10초) → 60초, 응답 잘림 없이 수신
근본 원인: useApi 기본 axios timeout 이 10초인데 suggestChecklist 가 이를
덮어쓰지 않아, AI 생성이 10초를 넘으면(항목 제한 해제 후 ~10.5초로 일관 초과)
백엔드가 200 으로 완료해도 프론트가 먼저 끊어 '추천을 가져오지 못했습니다' 발생.
(백엔드 에러 로그가 없던 이유 — 백엔드는 성공했음)
suggestChecklist 호출에 timeout 60000(백엔드 abort 30초보다 길게) 지정.

런타임 측정: 백엔드 suggest 응답 10.48초/HTTP 200 확인.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 12:54:40 +09:00
ttipo 0d47e989ba feat: AI 추천 카테고리당 항목 수 제한 해제
- 프롬프트의 '3~6개씩' 제한 제거 → 카테고리별로 필요한 만큼 제안
- parseSuggestions 의 카테고리당 15개 슬라이스 캡 제거(응답 길이는 max_tokens 가 자연 제한)
- 항목이 늘어 잘리지 않도록 suggest max_tokens 3072 → 4096

런타임 재현: 동일 입력에서 필수8·관리6·보안8·부가6(총 28개)로 더 풍부하게 반환.
백엔드 build/lint 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 12:32:35 +09:00
ttipo f8ae813c05 fix: AI 추천 토큰 한도 부족으로 응답 잘림 → 추천 실패 수정
4개 카테고리(필수/관리/보안/부가) 구조화 JSON 응답이 max_tokens=1024 를
넘으면 중간에 잘려 JSON 파싱이 실패하고 '추천을 가져오지 못했습니다' 로 떴음.
- callClaude 에 maxTokens 파라미터 추가, suggestChecklist 는 3072 사용
- parseSuggestions 파싱 실패/빈 결과 시 원본 일부를 warn 로그로 남겨 진단 가능

런타임 재현: 동일 입력으로 필수5·관리5·보안5·부가4(총 19개) 정상 반환 확인.
백엔드 build/lint 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 12:27:31 +09:00
ttipo 3379c54033 feat: AI 추천 모달 디자인 개편 + 4개 고정 카테고리(필수/관리/보안/부가)
백엔드:
- SUGGEST_SYSTEM_PROMPT 를 정확히 4개 카테고리(필수/관리/보안/부가 기능)로 고정
  (parseSuggestions 가 빈 카테고리는 자동 제외)

프론트(시안 'AI 추천 모달.html' 반영):
- 그라데이션 헤더(배지 아이콘 + '자동 생성' 뱃지 + 분석 요약 서브타이틀)
- 카테고리별 색상 아이콘 dot + 개수 pill + 전체 추가/해제 토글
- 항목을 체크 토글로 '선택' 후 푸터의 'N개 업무에 추가'로 일괄 반영(기존 즉시추가 → 선택→커밋 모델)
- 이미 체크리스트에 있는 항목은 잠금(added) 표시
- 푸터: 선택 개수 + 취소/추가 버튼(.ai-btn — 페이지 .btn 과 분리)

검증: 백엔드 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 12:19:03 +09:00
ttipo bd21557e39 fix: AI 추천 검증을 제목·내용 둘 다로 확장
AI 추천은 제목과 내용을 함께 분석하므로, 둘 중 하나라도 비면 해당 필드에
인라인 에러(빨강 테두리+메시지)를 표시하고 요청을 중단하도록 수정.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:55:20 +09:00
ttipo 867f47ba8f feat: AI 추천 버튼도 비활성화 제거 + 인라인 검증으로 통일
기존: 제목 미입력 시 'AI 추천' 버튼 비활성화(canRequestAi)
변경: 제출 버튼과 동일 로직 — 버튼 상시 활성(분석 중 in-flight 만 disabled),
클릭 시 제목이 비어 있으면 titleError 로 인라인 안내(빨강 테두리+메시지) 후 중단.
사용 안 하게 된 canRequestAi computed 제거.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:46:07 +09:00
ttipo 116560b549 style: 업무 제목 인풋도 에러 시 빨간 테두리 표시
borderless 제목 인풋에 has-error 시 빨간 테두리(+가로 패딩) 부여 —
에디터·담당자 필드와 일관. box-sizing:border-box 라 overflow 없음,
세로 패딩 유지로 레이아웃 점프 최소화.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:41:10 +09:00
ttipo 612d6c7de8 fix: 업무 내용 필수 검증 추가 + 제목 에러 표기 정리
- 업무 내용(*)이 표기상 필수인데 검증이 없던 문제 → 필수로 강제
  · 프론트: 인라인 검증(에디터 빨강 테두리 + '업무 내용을 입력해 주세요.'),
    입력 시 즉시 해제
  · 백엔드: CreateTaskDto.content 를 @ArrayNotEmpty 필수로(데이터 계약 일치)
- 제목 에러 표기 정리: 1.5rem 큰 placeholder 가 빨갛게 변하며 아래 메시지와
  이중으로 강조되던 것을 제거 → 작은 인라인 메시지만 표시(차분하게)

검증: 백엔드 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 11:36:58 +09:00
ttipo 9f66c6d0ec feat: 업무 지시 필드별 인라인 검증 + 제출 버튼 상시 활성화
기존: 제목·담당자 미입력 시 '지시 보내기' 버튼이 비활성화(이유 안내 없음)
변경:
- 버튼 비활성화 해제(제출 중 중복 클릭 방지용 submitting 만 유지)
- 제출 시 validateForm 으로 검증 → 미입력 필드에 인라인 에러 표시
  · 제목: 빨강 placeholder + '업무 제목을 입력해 주세요.'
  · 담당자: combo 빨강 테두리 + '담당자를 1명 이상 지정해 주세요.'
- 해당 필드를 채우면 watch 로 에러 즉시 해제

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 11:29:36 +09:00
ttipo ae6af518dd style: 업무 생성 AI 추천을 하단 인라인 패널 → 모달로 변경
기존: 체크리스트 아래에 인라인으로 펼쳐지던 AI 추천 패널
변경: Teleport 기반 중앙 모달(반투명 배경, 헤더 고정 + 본문 스크롤).
배경 클릭/× 로 닫기(분석 중에는 닫기 비활성). 추천 항목 추가 동작은 동일.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:50:41 +09:00
ttipo 544d49aa97 style: 업무 생성 첨부파일 선택을 강조 없는 일반 버튼으로 변경
기존: 점선 테두리 + 강조색(accent) 굵은 '클릭하여 파일 선택' 드롭존(강조 스타일)
변경: 클립 아이콘 + '파일 선택' 의 차분한 보조 버튼(.attach-btn). 미사용
.dropzone.is-disabled 잔여 스타일도 제거.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:46:35 +09:00
ttipo 73eb53625c fix: 멤버 초대/역할변경/제거 알림에 actorName 누락 → '누군가' 표기 수정
ProjectMemberService 의 notify payload 에 actorName 이 빠져 있어, 프론트
notification.store 가 actorName 부재 시 '누군가'로 폴백하던 문제.
세 member.* 알림(invited/role_changed/removed) payload 에 actorName 추가
(actingUserId 로 사용자명 조회하는 actorName 헬퍼 신설). 활동 피드는 actor
관계로 별도 해결되므로 영향 없음.

런타임 검증: 초대 시 수신자 알림 payload.actorName = 초대자 이름으로 채워짐.
백엔드 build/lint 0·8 tests.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 10:31:03 +09:00
ttipo 32a87d8f62 chore: 잔존 '저장소' 주석·문구를 '프로젝트'로 정리
저장소 기능 제거 후 남은 '저장소' 표현을 프로젝트로 통일:
- 백엔드: activity/notification/task 주석·ApiResponse 문구, AI 시스템 프롬프트
  (SUGGEST_SYSTEM_PROMPT 의 저장소→프로젝트, 무의미해진 'slug=영문이름' 지시 제거),
  CONTEXT_REPO_LIMIT→CONTEXT_PROJECT_LIMIT(프로젝트 컨텍스트 한도)
- 프론트: 페이지/스토어 주석 및 TaskCreate/TaskDetail 의 사용자 노출 문구('저장소'→'프로젝트')
- 단, auth OAuth state 'store' 의미의 '저장소'(cookie-state/strategies)는 정당하여 유지

검증: 백엔드 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:57:06 +09:00
ttipo be0a1bc9d1 refactor: 'Repo' 접두 타입·헬퍼 식별자를 'Project' 로 일괄 rename
저장소 기능 제거 후 남은 레거시 'Repo' 명칭을 프로젝트 의미에 맞게 정리(동작 변화 없음):
- 타입: ApiRepoMember→ApiProjectMember, ApiRepoTask→ApiProjectTask,
  ApiRepoTaskListResult→ApiProjectTaskListResult, RepoTask(View)→ProjectTask(View),
  RepoMember(Response)→ProjectMember(Response), RepoActivityItem/Day→ProjectActivityItem/Day,
  RepoTaskResponse/ListResult→ProjectTaskResponse/ListResult (백엔드 task.service 포함)
- 헬퍼: toRepoTaskView→toProjectTaskView, toRepoActivityItem→toProjectActivityItem,
  loadRepo→loadProject, filteredRepos→filteredProjects, removeRepo→removeProject
- 백엔드 spec mock repoRepo→projectRepo, 잔존 RepoDetailPage/저장소(Repo) 주석 정리

검증: 백엔드 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:52:23 +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 861c8efa6b chore: 테스트 편의를 위해 rate limit 을 분당 1000회로 완화
세 겹의 제한을 모두 분당 1000회로 상향(테스트 중 429 회피):
- main.ts express-rate-limit: 15분 150회 → 1분 1000회
- 전역 ThrottlerModule: 분당 100회 → 1000회
- 라우트별 @Throttle(login/signup/resend/forgot/reset/ai): 3~20회 → 1000회

각 라우트 주석에 운영 권장값을 남겨 추후 복원/ env 화 용이.
운영 배포 전 적정값으로 되돌릴 것.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:27:41 +09:00
ttipo d0599787f7 style: 저장소 탭 breadcrumb 을 다른 탭과 동일하게 통일
ProjectReposPage 가 .crumb .lnk(accent색)·.sep(여백) 를 로컬로 덮어써 다른 탭과
달라 보이던 것을 제거 → 전역 relay.css 의 crumb 스타일(text-2 링크·opacity 구분자)
을 따르도록 통일.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:19:42 +09:00
ttipo 647336b14d style: 저장소 탭 가이드 문구를 멤버 탭과 동일한 형식(tab-note)으로 변경
기존 .lede 단락 → 멤버 탭의 안내(정보 아이콘 + 작은 회색 텍스트) 형식으로 통일

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:12:15 +09:00
ttipo 3667f48897 fix: 저장소 연동 추가 폼이 열려 있을 때 기존 저장소 목록 숨김
연동 추가 화면(showForm)에서는 하단의 연동 저장소 목록 섹션을 표시하지 않는다.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 01:06:43 +09:00
ttipo 4d943e8d0d refactor: 저장소 공개 범위(visibility) UI 제거 + 기본 공개(public) 생성
사용자 입력/표시에서 저장소 공개 범위를 제거하고 새 저장소는 공개로 생성한다.
(Gitea 역방향 동기화에 쓰이는 Repo 엔티티 visibility 컬럼은 유지)

백엔드:
- CreateRepoDto/UpdateRepoDto 에서 visibility 제거
- RepoService.create 가 visibility 를 'public' 으로 고정
- update 의 visibility 변경·Gitea private 푸시 로직 제거(공개범위는 Gitea 기준)
- RepoResponse/toResponse 에서 visibility 제거

프론트:
- types/repo ApiRepo/Create/Update 페이로드에서 visibility 제거
- ProjectReposPage 연동 폼의 공개 범위 입력 + 목록 배지 + 관련 CSS 제거
- mock Visibility 타입·orphan Repo 뷰의 visibility 제거

런타임 검증: 저장소 응답에 visibility 없음, repos.visibility 컬럼 유지(Gitea 동기화).
백엔드 build/lint 0·14 tests, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:51:21 +09:00
ttipo 135cc0759f refactor: 저장소 연동 폼 — 표시 제목 최상단 + 템플릿 셀렉트 복원
- 필드 순서 변경: 표시 제목(name)을 최상단으로(기존 slug 가 최상단)
- 템플릿을 체크박스 → 기존 셀렉트 구조로 복원
  (없음[빈 저장소] / {템플릿slug} 템플릿, 가용 시에만 옵션 노출)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-21 00:36:48 +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 9fdcecace8 feat: 이메일 인증 링크를 결과 페이지(창 닫기)로 분리 + 가입 대기 화면에 로그인 버튼
- 인증 링크(verify-email)의 리다이렉트 대상을 /login?verified → /email-verified?ok=1|0 으로 변경
- 신규 EmailVerifiedPage(/email-verified): 인증 완료/실패 표시 후 창 자동 닫기 시도.
  브라우저가 사용자가 연 탭의 close 를 막을 수 있어 안내 문구 + 수동 로그인/가입 링크 병행
- SignupPage 인증 대기 화면에 '인증을 완료했어요 · 로그인하러 가기' 기본 버튼 추가
  (다른 탭에서 인증을 마친 뒤 기존 화면에서 로그인 진입)

런타임 검증: 유효 토큰→/email-verified?ok=1·무효→ok=0, 인증 후 로그인 200, SPA 라우트 제공.
백엔드 build/lint 0, 프론트 type-check/lint/build 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:22:23 +09:00
ttipo 72b05f1b69 feat: 비밀번호 찾기 화면을 로그인 2단 레이아웃으로 변경
- 시안(비밀번호 찾기.html) 적용 — 좌측 브랜드 패널 + 우측 폼(로그인 화면과 동일 구조)
- 1단계 이메일 입력(뒤로가기 링크·재설정 링크 보내기) → 2단계 전송 완료
  (메일 아이콘·수신 이메일 칩·스팸함 안내 박스·다시 보내기·로그인 복귀)
- 다시 보내기 시 동일 이메일 재요청 + '전송됨 ✓' 일시 피드백
- 시안의 '30분 유효'는 실제 백엔드 토큰 TTL(1시간)에 맞춰 표기

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 21:15:22 +09:00
ttipo 52b70363d9 feat: 비밀번호 재설정 플로우 (이메일 링크 기반)
백엔드:
- User 엔티티에 passwordResetTokenHash/passwordResetExpiresAt(select:false) 추가
- UserService: setPasswordResetToken/findByPasswordResetTokenHash/updatePassword
- MailService: sendPasswordResetEmail (dev=로그 출력, prod=수신자만)
- AuthService: requestPasswordReset(존재 비노출·로컬계정만·1시간 토큰),
  resetPassword(토큰 해시 검증→새 비번 저장→전 세션 폐기)
- POST /auth/forgot-password(3/분)·/auth/reset-password(5/분), 가입과 동일 비번 규칙
- OAuth 콜백의 잔여 /repos 리다이렉트를 /projects 로 교정(리팩터 누락분)

프론트:
- useAuth: forgotPassword/resetPassword
- ForgotPasswordPage(/forgot-password), ResetPasswordPage(/reset-password, 토큰=쿼리)
- LoginPage '비밀번호 찾기' 링크 연결 + 재설정 완료(?reset=1) 배너

런타임 검증: 재설정→새 비번 로그인 200·구 비번 거부·토큰 재사용 거부·약한 비번 VAL_001·
없는 이메일 동일응답(비노출). 백엔드 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 20:50:34 +09:00
ttipo cb6aa07ebc docs: 프로젝트 우선 리팩터 런타임 점검 완료 기록 (Phase 3)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 20:12:19 +09:00
ttipo 9aa22f092d fix: 백엔드·프론트 헬스체크 오탐 수정 (런타임 점검 발견)
- 백엔드: 앱이 전역 프리픽스 /api 하위라 루트(/) 프로브가 404 → /api/health 프로브로 교정
- 공통: localhost 가 IPv6(::1)로 풀려 IPv4 전용 바인딩에 연결 실패하던 것을 127.0.0.1 고정
- 두 컨테이너가 정상 동작에도 unhealthy 로 표시되던 선존 버그(리팩터 무관)

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