Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion apps/web/public/sitemap-0.xml
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9" xmlns:news="http://www.google.com/schemas/sitemap-news/0.9" xmlns:xhtml="http://www.w3.org/1999/xhtml" xmlns:mobile="http://www.google.com/schemas/sitemap-mobile/1.0" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xmlns:video="http://www.google.com/schemas/sitemap-video/1.1">
<url><loc>https://www.singcode.kr</loc><lastmod>2026-04-05T15:20:24.064Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
<url><loc>https://www.singcode.kr</loc><lastmod>2026-04-08T12:54:39.507Z</lastmod><changefreq>weekly</changefreq><priority>0.7</priority></url>
</urlset>
10 changes: 10 additions & 0 deletions apps/web/src/Header.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
'use client';

import { format } from 'date-fns';
import { CalendarCheck, MessageCircleQuestion, Moon, Sun } from 'lucide-react';
import { useTheme } from 'next-themes';
import { useRouter } from 'next/navigation';
Expand Down Expand Up @@ -58,6 +59,8 @@ export default function Header() {
const { data: user, isLoading } = useUserQuery();

const lastCheckIn = user?.last_check_in ?? new Date();
const today = format(new Date(), 'yyyy-MM-dd');
const isCheckedIn = format(new Date(lastCheckIn), 'yyyy-MM-dd') >= today;
Comment on lines 61 to +63
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Null check-in treated done 🐞 Bug ≡ Correctness

Header에서 last_check_in이 null인 경우 lastCheckIn을 new Date()로 대체해 isCheckedIn이 true가 되어, 실제로는 미출석인데도 자동
오픈이 되지 않고 모달이 DONE 상태로 초기화됩니다. last_check_in이 null 가능한 타입 정의와 충돌해 신규/초기 사용자에서 출석 기능이 비활성화될 수 있습니다.
Agent Prompt
### Issue description
`last_check_in`이 `null`일 수 있는데도 `?? new Date()`로 오늘 날짜로 치환해버려, 미출석 사용자가 `isCheckedIn=true`로 판정됩니다.

### Issue Context
- `User.last_check_in` 타입은 `Date | null` 입니다.
- 현재 로직은 `null`을 '오늘 출석함'으로 간주합니다.

### Fix Focus Areas
- apps/web/src/Header.tsx[59-64]
- apps/web/src/components/CheckInModal.tsx[16-33]

### Suggested fix direction
- `lastCheckIn`을 `Date | null`로 유지하고, `isCheckedIn` 계산 시 `lastCheckIn`이 없으면 `false`로 처리하세요.
  - 예: `const lastCheckIn = user?.last_check_in ?? null;`
  - 예: `const isCheckedIn = lastCheckIn ? format(new Date(lastCheckIn), 'yyyy-MM-dd') >= today : false;`
- `CheckInModalProps.lastCheckIn`도 `Date | null`로 바꾸고(`useCheckInTimer`는 이미 `Date | null` 허용), 내부 사용처를 이에 맞게 조정하세요.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

그럼 >=가 아니라 =로 처리하면 되는 거 아니야?


const handleClickContact = () => {
const contactUrl = 'https://walla.my/survey/K79c5bC6alDqc1qiaaES';
Expand All @@ -78,6 +81,12 @@ export default function Header() {
}
};

useEffect(() => {
if (!isLoading && !!user && !isCheckedIn) {
setOpen(true);
}
}, [isLoading, user, isCheckedIn]);

useEffect(() => {
const handleClickOutside = (e: MouseEvent) => {
if (headerRef.current && !headerRef.current.contains(e.target as Node)) {
Expand Down Expand Up @@ -124,6 +133,7 @@ export default function Header() {
<DialogContent>
<CheckInModal
lastCheckIn={lastCheckIn}
isCheckedIn={isCheckedIn}
isLogin={!!user}
handleNavigateLogin={handleNavigateLogin}
/>
Expand Down
9 changes: 3 additions & 6 deletions apps/web/src/components/CheckInModal.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
'use client';

import { format } from 'date-fns';
import { Clock } from 'lucide-react';

import Checked from '@/assets/lotties/Checked.json';
Expand All @@ -16,12 +15,14 @@ import ActionAnimationFlow from './ActionAnimationFlow';

interface CheckInModalProps {
lastCheckIn: Date;
isCheckedIn: boolean;
isLogin: boolean;
handleNavigateLogin: () => void;
}

export default function CheckInModal({
lastCheckIn,
isCheckedIn,
isLogin,
handleNavigateLogin,
}: CheckInModalProps) {
Expand All @@ -33,10 +34,6 @@ export default function CheckInModal({

const { mutate: patchUserCheckIn } = usePatchUserCheckInMutation();

const today = format(new Date(), 'yyyy-MM-dd');

const parseLastCheckIn = format(new Date(lastCheckIn), 'yyyy-MM-dd');

const handleClickCheckIn = () => {
patchUserCheckIn();
};
Expand All @@ -54,7 +51,7 @@ export default function CheckInModal({
<ActionAnimationFlow
animationData={Checked}
clickCallback={handleClickCheckIn}
initalStatus={parseLastCheckIn >= today ? 'DONE' : 'IDLE'}
initalStatus={isCheckedIn ? 'DONE' : 'IDLE'}
// 1. 대기 화면 (trigger 함수를 받아서 버튼에 연결)
idleView={trigger => (
<div className="flex flex-col items-center gap-4 text-center">
Expand Down
2 changes: 1 addition & 1 deletion apps/web/src/components/FallingIcons.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,5 +154,5 @@ export default function FallingIcons({ count }: FallingIconsProps) {
}
}, [count]);

return <div ref={sceneRef} className="h-[300px] w-full overflow-hidden bg-amber-50" />;
return <div ref={sceneRef} className="h-[300px] w-full overflow-hidden border" />;
}
12 changes: 6 additions & 6 deletions packages/crawling/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,11 +118,11 @@ taggingSongs.ts

### GitHub Actions 워크플로우

| 워크플로우 파일 | 스케줄 (UTC) | 실행 스크립트 |
| ----------------------- | ------------ | -------------------- |
| `crawl_recent_tj.yml` | 매일 14:00 | `pnpm recent-tj` |
| `tagging_song.yml` | 매일 14:00 | `pnpm tag-songs` |
| `update_ky_youtube.yml` | 수동 | `pnpm ky-youtube` |
| `verify_ky_youtube.yml` | 수동 | `pnpm ky-verify` |
| 워크플로우 파일 | 스케줄 (UTC) | 실행 스크립트 |
| ----------------------- | ------------ | ----------------- |
| `crawl_recent_tj.yml` | 매일 14:00 | `pnpm recent-tj` |
| `tagging_song.yml` | 매일 14:00 | `pnpm tag-songs` |
| `update_ky_youtube.yml` | 수동 | `pnpm ky-youtube` |
| `verify_ky_youtube.yml` | 수동 | `pnpm ky-verify` |

모든 워크플로우는 `workflow_dispatch`로 수동 실행도 가능하다.
2 changes: 1 addition & 1 deletion packages/crawling/src/utils/getSongTag.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import OpenAI from 'openai';
import dotenv from 'dotenv';
import OpenAI from 'openai';

import { getClient } from '@/supabase/getClient';

Expand Down