Commit Graph

57 Commits

Author SHA1 Message Date
198334c6f8 feat(rework): 타임라인 [00:00] 형식 번역 옵션 추가
'[00:00] 번역' 버튼 추가 — 세그먼트별로 번역해 '[mm:ss] 한국어' 줄로 재작성 칸에 내린다.
LibreTranslate의 q 배열 일괄번역(1회 호출, 입력순서 1:1 정렬)을 사용.
기존 '번역→재작성'(평문 흐름)은 그대로 유지(format=plain|timeline).

- TranslateService.translateBatch + buildBatchRequestBody/extractTranslatedTexts(+단위테스트)
- translateScript(id,target,format): timeline 분기, 세그먼트 시각 [mm:ss] 머리표시
- 컨트롤러 format 파라미터, rework.html '[00:00] 번역' 버튼

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 11:10:05 +09:00
77e77894c9 feat(rework): 원본 스크립트 번역 → 재작성(내 버전) 버튼 추가
자가호스팅 LibreTranslate(h-etc2.tolag.shop) 연동. 재가공 화면 '번역→재작성'
버튼으로 원본 전사 스크립트를 한국어로 번역해 재작성 칸 초안으로 채운다.
소스 언어는 전사 언어로 자동(없으면 auto), 타깃 기본 ko.

- TranslateService 신규: POST /translate {q,source,target} → translatedText
  (요청 조립/응답 파싱 순수 메서드 + 단위테스트)
- POST /{id}/translate 엔드포인트, CurationService.translateScript
- rework.html: '번역→재작성' 버튼 + translateToEditor()
- 설정 translate.base-url(기본값 有)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 10:41:23 +09:00
ff9609c9d1 feat(rework): 받은 원본을 왼쪽 플레이어에 싣고 타임라인 클릭으로 seek
원본 다운로드/캐시 보유 시 왼쪽 플레이어를 YouTube 임베드 대신 받은 원본 mp4로
전환한다. 세그먼트(타임라인) 클릭 시 기존 seekTo/하이라이트가 그대로 동작해
해당 지점으로 이동·재생된다. (업로드본이 있으면 업로드본 우선)

- GET /{id}/download/file: 받은 원본 스트리밍(Resource 반환 → HTTP Range 206 자동 지원)
- CurationService.cachedDownloadFile(id)
- rework.html: showServerVideoInPlayer/showYoutubeInPlayer, updateCacheUI가 플레이어 전환,
  다운로드 직후/진입 시 캐시 있으면 자동 적용, 삭제 시 YouTube로 복귀

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 09:58:23 +09:00
986dcffd55 feat(rework): 받은 원본 삭제 + 영상 삭제 시 캐시 자동정리
1) 재가공 화면에 '받은 원본 삭제' 버튼 추가(캐시 보유 시에만 노출).
   GET/DELETE /{id}/download 로 캐시 상태 조회·삭제(자막/세그먼트는 유지).
   진입 시 캐시 상태를 조회해 삭제버튼 노출 + 렌더 캐시 사용 여부를 갱신.
2) 수집함 영상 삭제 시 downloads/{id}.mp4 도 함께 삭제(고아 파일 방지).

- VideoDownloadService.deleteCache(id) + 단위테스트(@TempDir)
- CurationService: downloadStatus/deleteDownloadCache, delete() 캐시 정리

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 09:10:02 +09:00
f4ac1be171 docs(rework): 사용법 모달에 '원본 다운로드' 이미지 가이드 추가
재가공 사용법 모달 상단에 실제 화면 캡쳐 3장으로 단계별 안내 추가:
1) 원본 다운로드 버튼  2) 자동 전사된 시간싱크 자막  3) 내보내기(업로드 불필요).
?help=1 딥링크로 사용법을 바로 펼칠 수 있게 함.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 16:09:30 +09:00
26b8be1ff1 feat(rework): yt-dlp 원본 다운로드 → 전사·렌더 파이프라인 자동연결
재가공 화면 '원본 다운로드' 버튼으로 저장된 videoId를 yt-dlp(로컬 ProcessBuilder)로
받아 서버에 캐시하고, 그 파일을 기존 Python /transcribe·/render 에 그대로 투입한다.
수동 다운로드+업로드 단계를 전사·렌더 양쪽에서 제거.

- VideoDownloadService 신규: 인자 리스트 ProcessBuilder(셸 미사용), videoId 검증, 캐시 조회
- ChannelService: 전사·렌더를 Resource 기반 공통 메서드로 추출(업로드/캐시 공유)
- 컨트롤러: POST /{id}/download(다운로드+전사), /render 의 file 을 선택값으로(없으면 캐시)
- rework.html: '원본 다운로드' 버튼 + downloadOriginal(), 렌더가 서버 캐시 재사용
- 설정 ytdlp.*/download.dir(기본값 有), downloads/ gitignore
- buildCommand/videoId 검증 단위테스트(src/test 신규)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 15:58:50 +09:00
a177b0c987 docs(spec): yt-dlp 다운로드 → 재가공 파이프라인 자동연결 설계 추가
재가공 화면 '원본 다운로드' 버튼으로 yt-dlp 로컬 실행 → 서버 캐시 →
기존 Python 전사/렌더 파이프라인 재사용. 전사·렌더 양쪽에서 수동 업로드 제거.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-24 14:29:05 +09:00
8ec6268a1e fix(ui): 모바일에서 사이드바 펼치면 텍스트 라벨도 표시
모바일(≤768px)에서 사이드바를 펼쳐도 아이콘만 보이던 문제 수정.
태블릿용(≤1024px) 규칙이 .nav-text/.brand-text/.nav-section/.user-info를
display:none 처리하는데, 모바일 오버레이로 펼친 상태(.sidebar:not(.collapsed))
에서 그게 안 풀려 아이콘만 남았다.
→ 펼친 상태에서 라벨 다시 표시(display:revert) + nav 좌측 정렬. 모바일 사이드바를
전체폭 대신 280px 드로어 오버레이(그림자)로 정리.
CSS 변경에 맞춰 캐시버스팅 버전 20260617b 로 bump.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 07:26:42 +09:00
921c24f143 fix(ui): 모바일 깨짐 수정 — 표 카드뷰 opt-in, 대시보드 반응형, CSS 캐시버스팅
폰에서 화면이 깨지던 문제 수정.
- 전역 반응형 표 '카드뷰'가 모든 table에 적용돼, data-label 없는 목록 표
  (수집함·발굴·발행 등)가 거대한 행으로 변해 페이지가 32만px로 늘어남.
  → 카드뷰를 .rcards opt-in 으로 스코프. 그 클래스 가진 레거시 표
  (multi_channel_videos·videos)만 카드뷰, 나머지는 모바일에서 가로 스크롤.
- 대시보드: 모바일(≤680px) 미디어쿼리 추가 — 마스트헤드 세로 정렬, 긴
  브레드크럼 숨김, 떡상 리더보드 액션 버튼을 아랫줄 우측 정렬, 숫자 축소.
- base.html: CSS/JS 링크에 v= 캐시버스팅 — 배포 후 브라우저가 옛 CSS를
  캐시해 스타일이 깨지는 것 방지(CSS 변경 시 버전 올리기).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-17 06:56:50 +09:00
e0d92792b1 feat(ui): 전체 화면을 편집(editorial) 디자인으로 통일
대시보드의 editorial 정체성을 공용 디자인 토큰으로 끌어올려 전 페이지에 전파.
- variables.css: 종이 캔버스 + 잉크 텍스트 + 버밀리언(#ff4d23) 단색 액센트,
  Bricolage Grotesque(헤딩, 라틴) + Hanken Grotesk(본문) + JetBrains Mono,
  샤프한 모서리(radius 축소). 라이트 기본 + 다크. 기존 변수 별칭 유지로
  각 페이지가 자동 적응.
- style.css: 헤딩 디스플레이 페이스, primary 버튼 단색 버밀리언, 배지 각짐 등
  편집풍 마무리 터치.
한글은 디스플레이/모노 폰트에 없으므로 시스템 폰트로 자연 폴백.
수집함·칸반 라이트/다크 Playwright 검증.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 18:15:59 +09:00
c0ade287c2 feat(ui): 썸네일 클릭 미리보기를 칸반·채널상세·프로덕션상세에 추가
수집함·발굴·대시보드처럼, 썸네일 있는 나머지 화면에서도 썸네일을 클릭하면
YouTube 9:16 미리보기 팝업이 뜨도록 통일.
- 칸반(board): 카드 썸네일 클릭(드래그와 분리 위해 stopPropagation)
- 채널상세(channel_detail): 영상 목록 썸네일(제목 링크는 YouTube 유지)
- 프로덕션상세(production_detail): videoUrl에서 11자리 id 추출해 임베드,
  id 없으면 새 탭 폴백
각 페이지에 동일 모달(배경/ESC 닫힘, 닫을 때 iframe 비워 재생 중지) 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 15:18:09 +09:00
c7ce8de90e feat(ui): 대시보드 떡상 리더보드 썸네일 클릭 미리보기
수집함처럼, 떡상 후보 리더보드의 썸네일을 클릭하면 YouTube 임베드(9:16)
미리보기 팝업이 바로 뜨도록 추가. 편집 정체성에 맞춘 모달(.fd-vmodal),
배경 클릭/ESC로 닫힘, 닫을 때 iframe 비워 재생 중지.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:52:24 +09:00
b97e4644ea fix(ui): 대시보드 빈 화면 수정 — Thymeleaf [[ 충돌 제거
대시보드 인라인 스크립트의 `const stages = [['미검토',...],[...]]` 에서
앞쪽 `[[` 가 Thymeleaf 인라인 표현식 문법(`[[${...}]]`)과 충돌해,
렌더링이 그 지점에서 예외로 잘려 응답이 truncate → JS 미완성 → 빈 화면이었다.
(로그의 "response committed already" 예외가 이 증상)

배열 리터럴을 줄바꿈/공백으로 풀어 `[[`·`]]` 인접을 제거. 렌더 결과가
온전해짐(잘림 21217B → 정상 24519B, </html>·loadDashboard() 복구).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:38:28 +09:00
a36783f81f chore: 원격 푸시 재테스트
푸시가 반복적으로 정상 동작하는지 다시 확인.
docs/push-test.md 에 재테스트 줄 추가.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:22:14 +09:00
97192d7372 chore: 원격 푸시 동작 테스트
Gitea 원격(origin) 푸시가 정상 동작하는지 확인하는 테스트 커밋.
docs/push-test.md 추가 — 확인 후 삭제 가능.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:07:01 +09:00
22dd2f6d87 feat(ui): action-first editorial dashboard (frontend-design)
Rebuild the dashboard with a distinctive editorial identity (Bricolage Grotesque
display + JetBrains Mono tickers + paper canvas + vermilion signal accent, ruled
layout, grain, staggered load) scoped to .fd-dash so other pages keep the SaaS
theme. UX is action-first:
- action bar: 미검토/떡상 후보/재가공 대기 with primary CTAs (real counts)
- 떡상 후보 leaderboard wired to real /api/dashboard/summary outperformers, with
  working per-row 재가공(/rework/{id}) and 제외(status EXCLUDED) actions
- pipeline funnel with bottleneck callout + 칸반 CTA
- source/format + publish with empty-state guidance
Light/dark aware. Editorial fonts loaded in base.html.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 14:07:01 +09:00
4508c3ef18 Add Jenkinsfile: build/test/deploy pipeline 2026-06-16 12:22:57 +09:00
cc3904c307 docs: rewrite README with badges and accurate stack
Replace the outdated React/Vite/8080/H2 README with an accurate, shielded
overview: Spring Boot 3.4 / Java 21 Thymeleaf SSR, the collection→discovery→
curation→rework→publish pipeline, subtitle studio (Whisper+ffmpeg), light/dark
UI, getting-started, env vars, and project structure.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 10:36:44 +09:00
dd42499f43 feat(ui): localize production page + page-header
Production -> 프로덕션, Crawl History -> 크롤 이력, table headers to Korean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 06:33:45 +09:00
652d25a483 feat(ui): discover table fits viewport (merged 영상 column)
Merge thumbnail+title+channel into one "영상" column like 수집함 so the table
fits without horizontal scroll. Promote .coll-table/.row-sel to global style.css
for reuse across list pages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 06:32:27 +09:00
f3be6bd803 feat(ui): localize channels page to Korean
채널/추적 안내/전체 선택/채널 추가/구독자/영상 수/총 조회수/상세/방문/삭제.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 06:24:49 +09:00
1ebe2dda44 feat(ui): discover filter chips + help modals, localize, tokenize long pages
- discover: filter converted to toggle chips + inline selects (matches 수집함);
  page-header + 사용법 modal (배율 지표/필터/고른 뒤 안내)
- rework: 사용법 modal (전사·세그먼트·무음제거·내보내기·발행 guide) + page-header
- dashboard: h1 "Dashboard" -> "대시보드"
- channel_detail: sort-active header uses accent color (was invisible text-white)
- multi_channel_videos/videos/production_detail: tokenize dark-assuming colors
  and title-link text-white for light-theme readability

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 06:22:55 +09:00
52c6b51e61 feat(ui): roll out light/dark theme to board, publish, rework, discover, etc.
- board: tokenized kanban columns/cards; page-header + 사용법 modal (drag guide)
- publish: tokenized table/tabs; accent tab-active; page-header + 사용법 modal
- rework: tokenized the many dark-assuming inline styles so the editor renders
  correctly in the light theme
- discover/channels/production/channel_detail: tokenized dark-assuming colors
  (white text, translucent-white surfaces, #1e1e2d) for light-theme readability

Verified board/publish/rework/discover render cleanly in light.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-13 06:07:51 +09:00
616003479b feat(ui): collection — compact category, help modal, fit-to-width table
- Category management collapsed into a slim single-row strip
- "사용법" button opens a reusable help modal (.modal-overlay/.modal-card +
  .help-item) explaining categories, filters, list view, and row actions
- Merge thumbnail+title+channel into one "영상" column and tighten cells so the
  whole table fits the viewport — no horizontal scroll on page or table card
- #mainContent min-width:0 to avoid flex overflow

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 23:12:03 +09:00
fc58a32a20 feat(ui): collection filter toolbar — toggle chips + tidy inline selects
Replace raw checkboxes + stacked-label dropdowns with pill toggle chips
(.chip, accent when checked via :has) and a single aligned toolbar row;
result count as a badge. Adds reusable .toolbar/.chip/.field components.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 22:45:05 +09:00
7497cb08ce fix(ui): collection page readable in light theme
Tokenized dark-assuming inline colors (white title/dropdown text, translucent
white surfaces, #1e1e2d modal) so the collection table renders correctly in the
light theme; header uses shared page-header.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 22:39:45 +09:00
adb51943a3 feat(ui): light/dark SaaS design system + redesigned sidebar & dashboard
Phase A+dashboard of the UI redesign:
- variables.css: light-default tokens + [data-theme=dark] overrides; old var
  names aliased to theme tokens so existing markup adapts to both themes
- style.css: refined components (card/btn/nav/badge/bar/table/forms), tokenized
  the dark-assuming rgba colors
- theme toggle: pre-paint init in base.html <head> + toggleTheme() in common.js,
  persisted to localStorage; toggle button in sidebar
- sidebar.html: labeled nav with sections (분석/파이프라인/제작), active state,
  account; Korean labels
- dashboard.html: tokenized inline colors; verified light & dark with real data

Spec: docs/superpowers/specs/2026-06-12-ui-redesign-design.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 22:26:01 +09:00
9c276789f3 docs: add UI redesign design spec (light/dark SaaS design system)
Light-default + dark toggle, refined SaaS components, phased rollout across
all SSR pages. Approved dashboard mockup as the visual reference.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 22:19:09 +09:00
fa6342f97e fix(rework): long-op timeout, dedup query, clearer error, logging
Review follow-ups (safe fixes):
- pythonRestTemplate bean (10min read timeout) for /transcribe and /render so
  ffmpeg encoding / Whisper don't hit the shared 120s timeout; resolved by bean
  name so existing restTemplate injections are unaffected
- getScriptData: fetch the script row once instead of 3 separate queries
- renderTrimmed "no segments" now IllegalArgumentException (400 + visible message)
  instead of IllegalStateException swallowed as generic 500
- ProductionService: System.out.println -> log.info (3x)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 20:06:00 +09:00
8178d45209 feat(rework): speech-gap trimming + render, language override
Phase 3: remove "no-talk" gaps (Whisper-segment based, not audio silencedetect
which finds nothing under background music) and render a trimmed (+speed) video
via ffmpeg, with subtitles remapped to match.

- KeepIntervalPlanner + TimelineRemapper (pure, unit-tested): keep/remove plan
  from segments (pad/minGap) and timestamp remap f(t)=t-removedBefore(t)
- GET /{id}/trim-plan (preview: keep/remove/remapped segments/kept duration)
- POST /{id}/render (multipart: file,pad,minGap,speed) -> proxy Python /render
  (ffmpeg trim/atrim+concat+atempo) -> mp4 download; ffmpeg graph validated locally
- rework.html: export panel (speed + speech-gap trim preview + SRT/video export),
  client-side SRT from working segments, language selector (auto/ko/en/zh/ja)
- transcribeFromFile forwards optional language (Whisper auto-detect misfired -> zh)

Spec updated with the audio-silence -> speech-gap design correction.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 16:53:32 +09:00
b9aa04d4a3 feat(rework): synced subtitle extraction via file upload + Whisper, SRT export
Phase 1 of subtitle timeline studio. Upload a video in the rework editor →
proxy to Python /transcribe (faster-whisper) → store segments → render a
playback-synced segment list and export CapCut-importable SRT.

- ScriptSegment + SrtFormatter (pure, unit-tested) with speed rescaling
- ChannelVideoScript.segmentsJson column (ddl-auto adds it)
- ChannelService.transcribeFromFile + getSegments/parseSegments
- POST /{id}/transcribe (multipart), GET /{id}/script.srt?speed=, /script now returns segments
- rework.html: upload button, local <video>, segment list, SRT export + speed
- multipart 200MB limit; python.base-url config (PYTHON_BASE_URL)
- repo: findFirstByVideoIdOrderByIdDesc/findAllByVideoId (dedup-safe lookups)

Spec: docs/superpowers/specs/2026-06-12-subtitle-timeline-studio-design.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 16:23:55 +09:00
fa7cec7f14 docs: add subtitle timeline studio design spec
File-upload + local faster-whisper synced transcription, segment editor,
SRT export, with speed/silence-removal phases. N150 CPU, small int8 model.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-12 15:34:53 +09:00
hehih
aaf8901f13 chore: untrack stale .playwright-mcp snapshots (already in .git/info/exclude)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:06:33 +09:00
hehih
c08a9bfa93 chore: ignore tooling scratch artifacts; untrack git-ignored .claude files
Add .playwright-mcp/ and verify-*.png to .gitignore (transient scratch).
Untrack .claude/settings.local.json and scheduled_tasks.lock, which were
already listed in .gitignore but still indexed; settings.local.json stays
on disk as a local-only file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 17:00:39 +09:00
hehih
9edd4c5d97 fix(publish): dashboard 'recent' = latest-updated 5, stable status order
dashboardSummary surfaced the earliest-scheduled packages as "recent"
(it reused the queue's scheduledAt-asc sort); query updatedAt-desc instead.
Build byStatus from an explicit ordered list since Set.of iteration order
is undefined, so the dashboard renders DRAFT/READY/PUBLISHED consistently.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:40:58 +09:00
hehih
fd8403438e chore(security): externalize secrets to git-ignored application-local.yml
Remove hardcoded DB credentials and YouTube API key fallbacks from
application.yml; resolve them from env vars or an optional, git-ignored
application-local.yml (spring.config.import). Add a tracked
application-local.yml.example template and ignore the real local file.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 16:08:26 +09:00
2cdae03998 test: add unit test infrastructure and core-logic tests
First tests in the project (src/test was absent). Add testRuntimeOnly
junit-platform-launcher (required by Gradle 9 to load the JUnit Platform),
then cover:
- VideoMetrics: duration parse, isShorts boundary, viewsPerHour clamp/
  division, viewsPerSubRatio rounding & zero-guards (9 cases, pure)
- DashboardService.summary(): composes each domain service under the
  right key (Mockito)
- PublishService.dashboardSummary(): status counts + recent capped at 5

12 tests, all green. No Spring context / DB needed — fast.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 13:37:52 +09:00
9828918b97 style: dedupe to shared components (publish badge, filter selects, gap token)
- publish.html: status column uses shared .badge (badge-muted/warning/
  success) instead of its own .st-badge inline-color span
- collection.html / discover.html: drop .filter-sel rules now that the
  shared `select` styling covers them (row-sel kept for compact inline)
- style.css: .gap-2 uses var(--space-2) for token consistency

No behavior change; follow-up cleanup from the design-uplift review.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 10:45:15 +09:00
152dc0bae4 fix(discover): cast nullable params so Postgres can infer type
The discover @Query used the `(:param is null or col >= :param)` idiom.
For the timestamp (publishedAfter) and numeric (minRatio) params, Postgres
threw "could not determine data type of parameter $1" because the bare
`$1 is null` placeholder is untyped — returning HTTP 500 and breaking the
whole /discover page. (The search() query survives because its nullable
params are only bigint/varchar, which Postgres can null-type.) Wrap the
two problematic params in cast(... as timestamp/big_decimal) in the
is-null check so the type is explicit. ORDER BY ... nulls last / fetch
first were never the problem.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 09:56:28 +09:00
632a2d4f8a fix: scope form focus rule, robust .hidden, drop unused shadow tokens, funnel amber stage 2026-05-31 01:38:08 +09:00
04891a7838 feat(collection): apply initial filters from URL params (dashboard deep-link target) 2026-05-31 01:01:18 +09:00
8fe90e431f fix(dashboard): null-ratio badge color, escape categoryId, robust refresh selector, animate once 2026-05-31 00:59:20 +09:00
2d6c962567 feat(dashboard): count-up, 3-tone bars, rank/ratio badges, click-through deep links
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 00:53:25 +09:00
5ec04eaa60 style(css): refine shared components (buttons, badge, bar, forms, table, skeleton) 2026-05-31 00:48:14 +09:00
390037efe2 style(css): backfill utility classes used across templates 2026-05-31 00:45:05 +09:00
7f2227133d style(css): add design tokens (primary-rgb, warning, spacing, shadow, tabular-nums) 2026-05-31 00:29:55 +09:00
15d8ec0b88 docs: add all-pages design uplift implementation plan
Phase 1 (CSS tokens, utility backfill, shared components) with complete
CSS; Phase 2 (dashboard visuals + deep links, collection URL filters,
per-page sweep). Verification via bootRun + browser observation.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 00:25:53 +09:00
d8c63724b2 docs: add all-pages design/UX uplift spec
Shared-CSS-first strategy (variables.css + style.css) to lift all 12
Thymeleaf pages, then per-page polish + light interactions. Minimal /
data-density direction; no backend or new libraries.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-31 00:18:42 +09:00
2ec3915789 feat(dashboard): add full-set pipeline summary with single endpoint
Add GET /api/dashboard/summary aggregating pipeline status, category
distribution, publish summary, and outperformers in one call. Rewrite
dashboard.html with 5 KPI cards, pipeline funnel, publish status, and
category/source-format breakdowns (CSS bars, no chart lib).

Backend: ChannelVideoRepository counts (shorts/uncategorized),
PublishPackageRepository.countByStatus, pipelineStats shorts/longForm,
CategoryService.distribution, PublishService.dashboardSummary, new
DashboardService + DashboardApiController.

Fix: PublishService.list(null) hit UnsupportedOperationException because
findAll(Sort) uses Criteria, which rejects nullsLast precedence. Route the
no-status path through a @Query method so Sort is appended as HQL ORDER BY
(supports NULLS LAST). Also fixes the latent bug in /api/v1/publish all-list.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-30 22:31:21 +09:00
hehih
92ae575646 docs: add dashboard full-set enhancement spec 2026-05-30 22:02:10 +09:00