diff --git a/.github/workflows/build-test.yml b/.github/workflows/build-test.yml new file mode 100644 index 0000000..7b8b73a --- /dev/null +++ b/.github/workflows/build-test.yml @@ -0,0 +1,91 @@ +# Sample workflow for building and deploying a Next.js site to GitHub Pages +# +# To get started with Next.js see: https://nextjs.org/docs/getting-started +# +name: Test Next Build + +on: + pull_request: + branches: ["main"] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# Sets permissions of the GITHUB_TOKEN to allow deployment to GitHub Pages +permissions: + contents: read + pages: write + id-token: write + +# Allow only one concurrent deployment, skipping runs queued between the run in-progress and latest queued. +# However, do NOT cancel in-progress runs as we want to allow these production deployments to complete. +concurrency: + group: "pages" + cancel-in-progress: false + +jobs: + # Build job + build: + runs-on: ubuntu-latest + + env: + NEXT_PUBLIC_API_URL: ${{secrets.NEXT_PUBLIC_API_URL}} + + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Detect package manager + id: detect-package-manager + run: | + if [ -f "${{ github.workspace }}/yarn.lock" ]; then + echo "manager=yarn" >> $GITHUB_OUTPUT + echo "command=install" >> $GITHUB_OUTPUT + echo "runner=yarn" >> $GITHUB_OUTPUT + exit 0 + elif [ -f "${{ github.workspace }}/package.json" ]; then + echo "manager=npm" >> $GITHUB_OUTPUT + echo "command=ci" >> $GITHUB_OUTPUT + echo "runner=npx --no-install" >> $GITHUB_OUTPUT + exit 0 + else + echo "Unable to determine package manager" + exit 1 + fi + - name: Setup Node + uses: actions/setup-node@v4 + with: + node-version: "20" + cache: ${{ steps.detect-package-manager.outputs.manager }} + - name: Setup Pages + uses: actions/configure-pages@v5 + with: + # Automatically inject basePath in your Next.js configuration file and disable + # server side image optimization (https://nextjs.org/docs/api-reference/next/image#unoptimized). + # + # You may remove this line if you want to manage the configuration yourself. + static_site_generator: next + - name: Restore cache + uses: actions/cache@v4 + with: + path: | + .next/cache + # Generate a new cache whenever packages or source files change. + key: ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}-${{ hashFiles('**.[jt]s', '**.[jt]sx') }} + # If source files changed but packages didn't, rebuild from a prior cache. + restore-keys: | + ${{ runner.os }}-nextjs-${{ hashFiles('**/package-lock.json', '**/yarn.lock') }}- + - name: Install dependencies + run: ${{ steps.detect-package-manager.outputs.manager }} ${{ steps.detect-package-manager.outputs.command }} + - name: Check environment variable + run: echo NEXT_PUBLIC_API_URL + - name: Create .env file + run: | + echo NEXT_PUBLIC_API_URL=${{ secrets.NEXT_PUBLIC_API_URL }} >> .env + - name: Build with Next.js + run: ${{ steps.detect-package-manager.outputs.runner }} next build + - name: Export with Next.js + run: ${{ steps.detect-package-manager.outputs.runner }} next export + - name: Upload artifact + uses: actions/upload-pages-artifact@v3 + with: + path: ./out diff --git a/.github/workflows/nextjs.yml b/.github/workflows/deploy.yml similarity index 97% rename from .github/workflows/nextjs.yml rename to .github/workflows/deploy.yml index bc11cdd..d9e111e 100644 --- a/.github/workflows/nextjs.yml +++ b/.github/workflows/deploy.yml @@ -9,7 +9,7 @@ on: push: branches: ["deploy"] pull_request: - branches: ["deploy", "main"] + branches: ["deploy"] # Allows you to run this workflow manually from the Actions tab workflow_dispatch: @@ -31,9 +31,9 @@ jobs: build: runs-on: ubuntu-latest - env: - NEXT_PUBLIC_API_URL: ${{secrets.NEXT_PUBLIC_API_URL}} - + env: + NEXT_PUBLIC_API_URL: ${{secrets.NEXT_PUBLIC_API_URL}} + steps: - name: Checkout uses: actions/checkout@v4 @@ -93,7 +93,6 @@ jobs: with: path: ./out - # Deployment job deploy: environment: diff --git a/apis/admin.ts b/apis/admin.ts new file mode 100644 index 0000000..73a36b5 --- /dev/null +++ b/apis/admin.ts @@ -0,0 +1,25 @@ +import { CalendarDate } from "./calendar"; +import client, { ResponseBody } from "./client"; + +export interface Program { + name: string; + dueDate: CalendarDate; + openDate: CalendarDate; + location: string; + category: string; + host: string; + schedule: string; + description: string; +} + +async function postAttendanceCode(challengeIdx: number): Promise { + const { data } = await client.post(`/challenges/attendance/${challengeIdx}`); + return data; +} + +async function postProgram(body: Program): Promise { + const { data } = await client.post(`/programs`, body); + return data; +} + +export { postAttendanceCode, postProgram }; diff --git a/apis/calendar.ts b/apis/calendar.ts index 19d1183..23d1d95 100644 --- a/apis/calendar.ts +++ b/apis/calendar.ts @@ -7,6 +7,8 @@ interface GetMonthCalendarResponse extends ResponseBody { export interface MonthCalendarProps { programIdx: number; name: string; + category: string | null; + location: string | null; openDate: { year: number; month: number; @@ -19,7 +21,7 @@ export interface MonthCalendarProps { }; } -type CalendarDate = { +export type CalendarDate = { year: number; month: number; day: number; @@ -38,7 +40,7 @@ interface GetProgramDetailBody { // 챌린지 월별 조회 export const getMonthCalendar = async (): Promise => { - const response = await client.get("/programs"); + const response = await client.get("/programs/list"); // console.log("calenderData", response.data.result); return response.data.result; }; @@ -48,6 +50,5 @@ export const getProgramDetail = async ( ): Promise => { // const response = await client.get(`/programs/${programIdx}`); const response = await client.get(`/programs/2`); - // console.log("calenderDetail", response.data.result); return response.data.result; }; diff --git a/apis/challenge.ts b/apis/challenge.ts index 40afdc3..a7e55a2 100644 --- a/apis/challenge.ts +++ b/apis/challenge.ts @@ -35,6 +35,23 @@ async function getMyChallengeList(): Promise { return data; } +type AttendanceDate = { + year: number; + month: number; + day: number; +}; + +export interface GetChallengeDetailBody { + attendanceDate: AttendanceDate; +} + +async function getChallengDetail(): Promise { + const response = await client.get( + `/challenges/attendance/2?year=2024&month=6`, + ); + return response.data.result; +} + async function getChallengeAds(): Promise { const { data } = await client.get(`/challenges/ads`); return data; @@ -67,4 +84,5 @@ export { getChallengeSearch, postNewChallenge, postAttendance, + getChallengDetail, }; diff --git a/apis/client.ts b/apis/client.ts index 04a1d94..3e200f3 100644 --- a/apis/client.ts +++ b/apis/client.ts @@ -22,6 +22,18 @@ const getTokenFromLocalStorage = () => { return accessToken; }; +export const setIsAdminAtLocalStorage = (is_admin: string) => { + localStorage.setItem("is_admin", is_admin); +}; + +const getIsAdminFromLocalStorage = () => { + const isAdmin = localStorage.getItem("is_admin"); + if (!isAdmin) { + return null; + } + return isAdmin; +}; + const client = axios.create({ baseURL: process.env.NEXT_PUBLIC_API_URL, withCredentials: true, diff --git a/apis/hooks/admin.ts b/apis/hooks/admin.ts new file mode 100644 index 0000000..6e9d0af --- /dev/null +++ b/apis/hooks/admin.ts @@ -0,0 +1,36 @@ +import { useMutation, useQueryClient } from "@tanstack/react-query"; +import { Program, postAttendanceCode, postProgram } from "../admin"; +import { useRouter } from "next/router"; + +function usePostAttendanceCode(challengeIdx: number) { + const { mutate } = useMutation({ + mutationKey: ["postAttendanceCode", challengeIdx], + mutationFn: () => postAttendanceCode(challengeIdx), + onSuccess: (data) => window.alert(`인증번호: ${data}`), + onError: () => window.alert("에러 발생. 앱 관리자에게 문의해주세요."), + }); + + return { mutate }; +} + +function usePostProgram() { + const router = useRouter(); + const queryclient = useQueryClient(); + + const { mutate } = useMutation({ + mutationKey: ["postProgram"], + mutationFn: (body: Program) => postProgram(body), + onSuccess: () => { + queryclient.invalidateQueries({ + queryKey: ["getMyChallengeList"], + }); + window.alert("프로그램이 성공적으로 등록되었습니다."); + router.push("/"); + }, + onError: () => router.push("/404"), + }); + + return { mutate }; +} + +export { usePostAttendanceCode, usePostProgram }; diff --git a/apis/hooks/challenge.ts b/apis/hooks/challenge.ts index e990c9a..fa63eb8 100644 --- a/apis/hooks/challenge.ts +++ b/apis/hooks/challenge.ts @@ -5,6 +5,7 @@ import { getMyChallengeList, postAttendance, postNewChallenge, + getChallengDetail, } from "../challenge"; import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"; @@ -17,6 +18,14 @@ function useGetMyChallengeList() { return { data }; } +function useGetChallengeDetail() { + const { data } = useQuery({ + queryKey: ["getChallengeDetail"], + queryFn: getChallengDetail, + }); + return { data }; +} + function useGetChallengeAds() { const { data } = useQuery({ queryKey: ["getChallengeAds"], @@ -72,4 +81,5 @@ export { useGetChallengeSearch, usePostNewChallenge, usePostAttendance, + useGetChallengeDetail, }; diff --git a/components/Divider.tsx b/components/Divider.tsx index 1e77b17..5d8e953 100644 --- a/components/Divider.tsx +++ b/components/Divider.tsx @@ -5,7 +5,7 @@ interface DividerProps { export default function Divider({ height }: DividerProps) { return (
); diff --git a/components/Input.tsx b/components/Input.tsx index 1f77be0..3c1a2eb 100644 --- a/components/Input.tsx +++ b/components/Input.tsx @@ -26,11 +26,11 @@ export default function TextInput({
onChangeText(event)} placeholder={placeholder} diff --git a/components/NavBar.tsx b/components/NavBar.tsx index 0b58248..ab19362 100644 --- a/components/NavBar.tsx +++ b/components/NavBar.tsx @@ -9,50 +9,51 @@ import FlexBox from "./Flexbox"; const NavBar = () => { const router = useRouter(); const pathname = router.pathname; - const handleNavigate = (path: string) => { router.push(path); }; - useEffect(() => { window.scrollTo(0, 0); }, []); return ( -
-
- handleNavigate("/")} - iconType="home" - > - - - handleNavigate("/calendar")} - iconType="calendar" - > - - - handleNavigate("/mypage")} - iconType="mypage" - > - - + <> +
+
+ + handleNavigate("/")} + iconType="home" + > + + + handleNavigate("/calendar")} + iconType="calendar" + > + + + handleNavigate("/mypage")} + iconType="mypage" + > + + +
-
+ ); }; diff --git a/components/TextArea.tsx b/components/TextArea.tsx index 9b152a8..77ac9c2 100644 --- a/components/TextArea.tsx +++ b/components/TextArea.tsx @@ -24,10 +24,10 @@ export default function TextArea({