Skip to content

Commit

Permalink
Merge pull request #32 from team-Ollie/14-feature-Admin-API
Browse files Browse the repository at this point in the history
feat: 관리자용 api 연결
  • Loading branch information
iOdiO89 authored Jul 1, 2024
2 parents 4cbad02 + 6a7587c commit 88bb7ff
Show file tree
Hide file tree
Showing 32 changed files with 361 additions and 110 deletions.
25 changes: 25 additions & 0 deletions apis/admin.ts
Original file line number Diff line number Diff line change
@@ -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<ResponseBody> {
const { data } = await client.post(`/challenges/attendance/${challengeIdx}`);
return data;
}

async function postProgram(body: Program): Promise<ResponseBody> {
const { data } = await client.post(`/programs`, body);
return data;
}

export { postAttendanceCode, postProgram };
2 changes: 1 addition & 1 deletion apis/calendar.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ export interface MonthCalendarProps {
};
}

type CalendarDate = {
export type CalendarDate = {
year: number;
month: number;
day: number;
Expand Down
36 changes: 36 additions & 0 deletions apis/hooks/admin.ts
Original file line number Diff line number Diff line change
@@ -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 };
2 changes: 1 addition & 1 deletion components/Divider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ interface DividerProps {
export default function Divider({ height }: DividerProps) {
return (
<div
className="border-t border-gray-100 bg-gray-100 w-full"
className="border-t border-grey-100 bg-grey-100 w-full"
style={{ height: `${height}px` }}
/>
);
Expand Down
4 changes: 2 additions & 2 deletions components/Input.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,11 @@ export default function TextInput({
<div
className={`w-full border ${
isError ? "border-red-500" : "border-transparent"
} bg-gray-100 rounded-lg p-2`}
} bg-grey-100 rounded-lg p-2`}
>
<input
type={type}
className="w-full outline-none border-none bg-gray-100 h4"
className="w-full outline-none border-none bg-grey-100 h4 placeholder:text-grey-400"
value={value}
onChange={(event) => onChangeText(event)}
placeholder={placeholder}
Expand Down
71 changes: 36 additions & 35 deletions components/NavBar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -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 (
<div
className="flex flex-basis h-[4rem] w-full absolute inset-x-0 bottom-0 z-50"
style={{
borderTop: "0.5px solid rgba(112, 115, 124, 0.16)",
backgroundColor: "#ffffff",
}}
>
<div className="flex flex-basis h-full w-full flex-row justify-between items-center ">
<NavBarItem
isActive={pathname === "/"}
text="홈"
onClick={() => handleNavigate("/")}
iconType="home"
>
<HomeIcon />
</NavBarItem>
<NavBarItem
isActive={pathname === "/calendar"}
text="캘린더"
onClick={() => handleNavigate("/calendar")}
iconType="calendar"
>
<CalenderIcon />
</NavBarItem>
<NavBarItem
isActive={pathname === "/mypage"}
text="마이페이지"
onClick={() => handleNavigate("/mypage")}
iconType="mypage"
>
<MypageIcon />
</NavBarItem>
<>
<div className="w-full min-h-[4rem]" />
<div
className="flex flex-basis h-[4rem] w-full fixed inset-x-0 bottom-0 "
style={{
borderTop: "0.5px solid rgba(112, 115, 124, 0.16)",
backgroundColor: "#ffffff",
}}
>
<FlexBox className="w-full justify-between">
<NavBarItem
isActive={pathname === "/"}
text="홈"
onClick={() => handleNavigate("/")}
iconType="home"
>
<HomeIcon />
</NavBarItem>
<NavBarItem
isActive={pathname === "/calendar"}
text="캘린더"
onClick={() => handleNavigate("/calendar")}
iconType="calendar"
>
<CalenderIcon />
</NavBarItem>
<NavBarItem
isActive={pathname === "/mypage"}
text="마이페이지"
onClick={() => handleNavigate("/mypage")}
iconType="mypage"
>
<MypageIcon />
</NavBarItem>
</FlexBox>
</div>
</div>
</>
);
};

Expand Down
4 changes: 2 additions & 2 deletions components/TextArea.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,10 @@ export default function TextArea({
<div
className={`w-full border ${
isError ? "border-red-500" : "border-transparent"
} bg-gray-100 rounded-lg p-2`}
} bg-grey-100 rounded-lg p-2`}
>
<textarea
className="w-full outline-none border-none bg-gray-100 h4"
className="w-full outline-none border-none bg-grey-100 h4"
value={value}
onChange={(event) => onChangeText(event)}
placeholder={placeholder}
Expand Down
33 changes: 28 additions & 5 deletions components/challenge/ChallengeInput.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import FlexBox from "../Flexbox";
import TextInput, { TextInputProps } from "../Input";
import TextArea from "../TextArea";
import Dropdown from "react-dropdown";
import "react-dropdown/style.css";

const locationOptions = ["서울", "경기", "그 외"];
const typeOptions = ["학술", "운동", "예술", "기타"];

interface ChallengeInputProps extends TextInputProps {
title: string;
inputType: "text" | "calendar" | "textarea";
inputType: "text" | "calendar" | "textarea" | "dropdown";
value2?: string;
setValue2?: React.Dispatch<React.SetStateAction<string>>;
}
Expand Down Expand Up @@ -42,16 +47,34 @@ export default function ChallengeInput({
isError={false}
type="date"
/>
<span className="h3 text-gray-700">~</span>
<span className="h3 text-grey-700">~</span>
<TextInput
value={value}
setValue={setValue}
value={value2}
setValue={setValue2}
isError={false}
type="date"
/>
</FlexBox>
);
break;
case "dropdown":
return (
<FlexBox className="w-full justify-between gap-2 text-black">
<Dropdown
options={locationOptions}
placeholder="지역"
value={value}
onChange={(arg) => setValue(arg.value)}
/>
<Dropdown
options={typeOptions}
placeholder="분야"
value={value2}
onChange={(arg) => setValue2(arg.value)}
/>
</FlexBox>
);
break;
default:
return (
<TextArea
Expand All @@ -66,7 +89,7 @@ export default function ChallengeInput({
};

return (
<FlexBox className="w-full gap-1 items-start" direction="col">
<FlexBox className="w-full items-start" direction="col">
<div className="h4">{title}</div>
{returnInput()}
</FlexBox>
Expand Down
58 changes: 58 additions & 0 deletions components/home/adminChallenge.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
import { NextPage } from "next";
import FlexBox from "../Flexbox";
import { useRouter } from "next/router";
import { Challenge as ChallengeType } from "@/apis/challenge";
import { usePostAttendanceCode } from "@/apis/hooks/admin";

interface ChallengeProps {
setIsModalVisible: React.Dispatch<React.SetStateAction<boolean>>;
challengeInfo: ChallengeType;
}

const AdminChallenge: NextPage<ChallengeProps> = ({
setIsModalVisible,
challengeInfo,
}) => {
const router = useRouter();
const { mutate } = usePostAttendanceCode(challengeInfo.challengeIdx);

const showAttendanceCode = () => mutate();

return (
<FlexBox direction="col" className="p-4 w-full rounded-lg border gap-4">
<FlexBox direction="col" className="w-full gap-1">
<FlexBox className="w-full justify-between items-start">
<div className="h2">{challengeInfo.name}</div>
</FlexBox>
<div className="h4 self-start">
{challengeInfo.location} | {challengeInfo.schedule}
</div>
</FlexBox>
<FlexBox direction="col" className="w-full gap-2">
<FlexBox className="gap-1 w-full">
<div className="h5 text-grey-500">전체달성률</div>
<div className="h3">
{challengeInfo.totalAttendanceRate}%(
{challengeInfo.participantsNum}명)
</div>
</FlexBox>
<FlexBox className="w-full gap-2">
<div
className="w-full border border-main-color rounded-lg py-2 text-center text-main-color h3"
onClick={showAttendanceCode}
>
인증번호 보기
</div>
<div
className="w-full border border-main-color rounded-lg py-2 text-center text-main-color h3"
onClick={() => router.push("/challenge")}
>
현황 보러가기
</div>
</FlexBox>
</FlexBox>
</FlexBox>
);
};

export default AdminChallenge;
29 changes: 10 additions & 19 deletions components/home/challenge.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
import { NextPage } from "next";
import FlexBox from "../Flexbox";
import { useRouter } from "next/router";
import { useAtom } from "jotai";
import { isAdminAtom } from "@/utils/atom";
import { Challenge as ChallengeType } from "@/apis/challenge";

interface ChallengeProps {
Expand All @@ -15,18 +13,17 @@ const Challenge: NextPage<ChallengeProps> = ({
challengeInfo,
}) => {
const router = useRouter();
const [isAdmin] = useAtom(isAdminAtom);

return (
<FlexBox direction="col" className="p-4 w-full rounded-lg border gap-4">
<FlexBox
direction="col"
className="w-full gap-1"
onClick={isAdmin ? null : () => router.push("/challenge")}
onClick={() => router.push("/challenge")}
>
<FlexBox className="w-full justify-between items-start">
<div className="h2">{challengeInfo.name}</div>
<div className="h4 text-gray-500">
<div className="h4 text-grey-500">
{challengeInfo.participantsNum}명 참여 중
</div>
</FlexBox>
Expand All @@ -37,28 +34,22 @@ const Challenge: NextPage<ChallengeProps> = ({
<FlexBox direction="col" className="w-full gap-1">
<FlexBox
className="w-full items-start justify-between"
onClick={isAdmin ? null : () => router.push("/challenge")}
onClick={() => router.push("/challenge")}
>
{!isAdmin && (
<FlexBox className="gap-1">
<div className="h5 text-gray-500">개인달성률</div>
<div className="h3">{challengeInfo.attendanceRate}%</div>
</FlexBox>
)}
<FlexBox className="gap-1">
<div className="h5 text-gray-500">전체달성률</div>
<div className="h5 text-grey-500">개인달성률</div>
<div className="h3">{challengeInfo.attendanceRate}%</div>
</FlexBox>
<FlexBox className="gap-1">
<div className="h5 text-grey-500">전체달성률</div>
<div className="h3">{challengeInfo.totalAttendanceRate}%</div>
</FlexBox>
</FlexBox>
<div
className="w-full border border-main-color rounded-lg py-2 text-center text-main-color h2"
onClick={
isAdmin
? () => router.push("/challenge")
: () => setIsModalVisible(true)
}
onClick={() => setIsModalVisible(true)}
>
{isAdmin ? "현황 보러가기" : "인증하기"}
인증하기
</div>
</FlexBox>
</FlexBox>
Expand Down
Loading

0 comments on commit 88bb7ff

Please sign in to comment.