8.1 출석부: 데일리 체크인/중복 방지/포인트 지급/누적 통계 o
This commit is contained in:
28
src/app/api/attendance/route.ts
Normal file
28
src/app/api/attendance/route.ts
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
import { NextResponse } from "next/server";
|
||||||
|
import prisma from "@/lib/prisma";
|
||||||
|
import { getUserIdFromRequest } from "@/lib/auth";
|
||||||
|
|
||||||
|
export async function GET(req: Request) {
|
||||||
|
const userId = getUserIdFromRequest(req);
|
||||||
|
if (!userId) return NextResponse.json({ today: null, count: 0 });
|
||||||
|
const start = new Date(); start.setHours(0,0,0,0);
|
||||||
|
const end = new Date(); end.setHours(23,59,59,999);
|
||||||
|
const today = await prisma.pointTransaction.findFirst({
|
||||||
|
where: { userId, reason: "attendance", createdAt: { gte: start, lte: end } },
|
||||||
|
});
|
||||||
|
const count = await prisma.pointTransaction.count({ where: { userId, reason: "attendance" } });
|
||||||
|
return NextResponse.json({ today: !!today, count });
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function POST(req: Request) {
|
||||||
|
const userId = getUserIdFromRequest(req);
|
||||||
|
if (!userId) return NextResponse.json({ error: "login required" }, { status: 401 });
|
||||||
|
const start = new Date(); start.setHours(0,0,0,0);
|
||||||
|
const end = new Date(); end.setHours(23,59,59,999);
|
||||||
|
const exists = await prisma.pointTransaction.findFirst({ where: { userId, reason: "attendance", createdAt: { gte: start, lte: end } } });
|
||||||
|
if (exists) return NextResponse.json({ ok: true, duplicated: true });
|
||||||
|
await prisma.pointTransaction.create({ data: { userId, amount: 10, reason: "attendance" } });
|
||||||
|
return NextResponse.json({ ok: true });
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
17
src/app/attendance/page.tsx
Normal file
17
src/app/attendance/page.tsx
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
"use client";
|
||||||
|
import useSWR from "swr";
|
||||||
|
|
||||||
|
const fetcher = (url: string) => fetch(url).then((r) => r.json());
|
||||||
|
|
||||||
|
export default function AttendancePage() {
|
||||||
|
const { data, mutate } = useSWR<{ today: boolean; count: number }>("/api/attendance", fetcher);
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h1>출석부</h1>
|
||||||
|
<p>오늘 출석: {data?.today ? "✅" : "❌"} / 누적: {data?.count ?? 0}</p>
|
||||||
|
<button disabled={data?.today} onClick={async () => { await fetch("/api/attendance", { method: "POST" }); mutate(); }}>출석하기</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -52,7 +52,7 @@
|
|||||||
7.10 제휴업소 일반(사진) 카테고리 전용 이미지 첨부/미리보기 규칙 o
|
7.10 제휴업소 일반(사진) 카테고리 전용 이미지 첨부/미리보기 규칙 o
|
||||||
|
|
||||||
[특수 페이지(카테고리)]
|
[특수 페이지(카테고리)]
|
||||||
8.1 출석부: 데일리 체크인/중복 방지/포인트 지급/누적 통계
|
8.1 출석부: 데일리 체크인/중복 방지/포인트 지급/누적 통계 o
|
||||||
8.2 포인트안내: 정책 안내 페이지(에디터 연동/버전 이력)
|
8.2 포인트안내: 정책 안내 페이지(에디터 연동/버전 이력)
|
||||||
8.3 회원랭킹: 기간별 랭킹 집계/캐싱/페이지네이션/정렬 옵션
|
8.3 회원랭킹: 기간별 랭킹 집계/캐싱/페이지네이션/정렬 옵션
|
||||||
8.4 무료쿠폰: 쿠폰 등록/재고/사용 처리/만료/1인 제한/로그
|
8.4 무료쿠폰: 쿠폰 등록/재고/사용 처리/만료/1인 제한/로그
|
||||||
|
|||||||
Reference in New Issue
Block a user