Skip to content

Commit

Permalink
Merge pull request #31 from Team-BomBomBom/feat/study_start#BBB-122
Browse files Browse the repository at this point in the history
Feat: #BBB-122 μŠ€ν„°λ”” μ‹œμž‘ κ΅¬ν˜„
  • Loading branch information
msjang4 authored Aug 13, 2024
2 parents 57e86d6 + 2f13c63 commit 165bf4b
Show file tree
Hide file tree
Showing 14 changed files with 146 additions and 60 deletions.
57 changes: 52 additions & 5 deletions app/study/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,41 @@ import { useEffect, useState } from 'react';

import StudyDashBoard from '@/components/study/dashboard/dashboard';
import JoinStudyDialog from '@/components/study/study-join-dialog';
import { Button } from '@/components/ui/button/button';
import Spinner from '@/components/ui/spinner/spinner';
import getStudyDetails from '@/lib/api/study/get-details';
import startStudy from '@/lib/api/study/start';
import { userState } from '@/recoil/userAtom';
import { AlgorithmRound, StudyDetails } from '@/types/study/study-detail';
import {
AlgorithmRound,
StudyDetails,
StudyStatus
} from '@/types/study/study-detail';
import { toast } from 'react-toastify';
import { useRecoilState } from 'recoil';

export default function StudyPage() {
const params = useParams();
const studyId = params.id.toString();

const studyId = Number(params.id);

const [details, setDetails] = useState<StudyDetails | undefined>();
const [round, setRound] = useState<AlgorithmRound | undefined>();
const [isParticipant, setIsParticipant] = useState(false);
const [canStart, setCanStart] = useState(false);
const [myData, setMyData] = useRecoilState(userState);
const [trigger, setTrigger] = useState(Date.now());

const handleStart = async () => {
try {
const response = await startStudy(studyId);
toast.success('μŠ€ν„°λ””λ₯Ό μ‹œμž‘ν•˜μ˜€μŠ΅λ‹ˆλ‹€.');
refresh();
} catch (error: any) {
toast.error(error.response.data.error);
console.error(error);
}
};
useEffect(() => {
async function fetchStudyDetails() {
try {
Expand All @@ -35,26 +55,53 @@ export default function StudyPage() {
break;
}
}

if (
studyDetailsAndRound.details.leaderId === myData?.id &&
studyDetailsAndRound.details.status === StudyStatus.READY
) {
setCanStart(true);
} else {
setCanStart(false);
}
} catch (error) {
console.error('Failed to fetch study details:', error);
}
}
fetchStudyDetails();
}, [studyId]);
}, [studyId, trigger]);

const refresh = () => {
setTrigger(Date.now());
};
if (!details || !round) {
return <Spinner />;
}

return (
<div className="flex space-x-4 justify-center">
<StudyDashBoard
details={details}
studyId={Number(studyId)}
studyId={studyId}
round={round}
setRound={setRound}
/>
<div className="mt-4">
{isParticipant ? '' : <JoinStudyDialog {...details} key={studyId} />}
{isParticipant ? (
canStart ? (
<Button onClick={handleStart} className="bg-cyan-300 w-full">
μ‹œμž‘ν•˜κΈ°
</Button>
) : (
''
)
) : (
<JoinStudyDialog
details={details}
studyId={studyId}
refresh={refresh}
/>
)}
<StudyAbout details={details} users={round.users} />
</div>
</div>
Expand Down
8 changes: 4 additions & 4 deletions components/study/dashboard/dashboard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ function DashBoardBody({
studyId: number;
}) {
const [my, _] = useRecoilState(userState);
const myTasks = round.users[my!.id].tasks;
const myTasks = round.users[my!.id]?.tasks;
return (
<div className="overflow-auto rounded-lg border">
<Table>
Expand All @@ -63,7 +63,7 @@ function DashBoardBody({
<TableHead key={index} className="text-center">
<p>{problem.title}</p>

{myTasks[Number(problemId)] && (
{myTasks?.[Number(problemId)] && (
<FeedbackDialog
problem={{ ...problem, problemId: Number(problemId) }}
studyId={studyId}
Expand Down Expand Up @@ -114,9 +114,9 @@ function SelectRound({
setRound: (round: AlgorithmRound) => void;
}) {
const params = useParams();
const studyId = params.id.toString();
const studyId = Number(params.id);
const handleRoundChange = async (value: string) => {
const newRound = await getRound(parseInt(studyId), parseInt(value));
const newRound = await getRound(studyId, parseInt(value));
setRound(newRound);
};
const renderSelectContent = () => {
Expand Down
20 changes: 14 additions & 6 deletions components/study/difficulty-level-dialog.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,17 @@
'use client';
import { useState } from 'react';
import {
MAX_DIFFICULTY_LEVEL,
MIN_DIFFICULTY_LEVEL
} from '@/constants/study/study';
import { DialogDescription } from '@radix-ui/react-dialog';
import {
Dialog,
DialogContent,
DialogHeader,
DialogTitle
} from '../ui/dialog/dialog';
import { RadioGroup } from '../ui/radio/radio-group';
import { MAX_DIFFICULTY_LEVEL } from '@/constants/study/study';
import TierRadioItem from './tier';
import { DialogDescription } from '@radix-ui/react-dialog';

export default function DifficultyLevelDialog({
open,
Expand Down Expand Up @@ -37,11 +39,17 @@ export default function DifficultyLevelDialog({
}}
>
<div className="grid grid-cols-5 grid-rows-6 gap-4 py-4">
{Array.from({ length: MAX_DIFFICULTY_LEVEL + 1 }).map((x, i) => (
{Array.from({
length: MAX_DIFFICULTY_LEVEL - MIN_DIFFICULTY_LEVEL + 1
}).map((x, i) => (
<TierRadioItem
key={i}
className={difficultyLevel == i ? 'bg-gray-200' : ''}
difficultyLevel={i}
className={
difficultyLevel == i + MIN_DIFFICULTY_LEVEL
? 'bg-gray-200'
: ''
}
difficultyLevel={i + MIN_DIFFICULTY_LEVEL}
></TierRadioItem>
))}
</div>
Expand Down
3 changes: 2 additions & 1 deletion components/study/study-create-modal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ import {
DAYS_PER_WEEK,
MAX_DIFFICULTY_LEVEL,
MAX_WEEKS,
MIN_DIFFICULTY_LEVEL,
StudyType
} from '@/constants/study/study';
import registerAlgorithmStudy from '@/lib/api/study/create-algorithm-study';
Expand Down Expand Up @@ -88,7 +89,7 @@ export default function StudyCreateModal({
}) {
const [open, setOpen] = useState(false);
const [user, setUser] = useRecoilState(userState);
const [difficultyBegin, setDifficultyBegin] = useState(0);
const [difficultyBegin, setDifficultyBegin] = useState(MIN_DIFFICULTY_LEVEL);
const [weeks, setWeeks] = useState(1);
const [difficultyEnd, setDifficultyEnd] = useState(MAX_DIFFICULTY_LEVEL);
const [studyType, setStudyType] = useState('');
Expand Down
1 change: 1 addition & 0 deletions components/study/study-grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ export default function StudyGrid({ trigger }: { trigger: number }) {
{studyPage?.contents.map((study: Study) => {
return (
<StudyGroup
key={study.id}
study={study}
onClick={() => {
router.push(`/study/${study.id}`);
Expand Down
14 changes: 4 additions & 10 deletions components/study/study-group.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,17 +28,11 @@ function TierBadge({ difficultyLevel }: { difficultyLevel: number }) {

function AlgorithmStudyInfo(algorithmStudy: AlgorithmStudy) {
const as = algorithmStudy;
let difficultyAvg =
as.difficultyDp +
as.difficultyDs +
as.difficultyGeometry +
as.difficultyGraph +
as.difficultyGreedy +
as.difficultyImpl +
as.difficultyMath +
as.difficultyString;
difficultyAvg /= 8;
const difficultyAvg =
Object.values(as.difficultySpreadMap).reduce((a, b) => a + b.left, 0) /
Object.keys(as.difficultySpreadMap).length;
const difficultyBegin = Math.round(difficultyAvg);

const difficultyEnd = difficultyBegin + as.difficultyGap;
return (
<div className="flex items-center justify-between mb-4">
Expand Down
13 changes: 11 additions & 2 deletions components/study/study-join-dialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,20 @@ import { StudyDetails } from '@/types/study/study-detail';
import { useState } from 'react';
import { toast } from 'react-toastify';

export default function JoinStudyDialog(details: StudyDetails, key: string) {
export default function JoinStudyDialog({
details,
studyId,
refresh
}: {
details: StudyDetails;
studyId: number;
refresh: () => void;
}) {
const handleSubmit = async () => {
try {
const response = await joinStudy(parseInt(key));
const response = await joinStudy(studyId);
toast.success('μŠ€ν„°λ””μ— μ°Έμ—¬ν•˜μ˜€μŠ΅λ‹ˆλ‹€.');
refresh();
} catch (error: any) {
toast.error(error.response.data.error);
console.error(error);
Expand Down
18 changes: 8 additions & 10 deletions components/study/tier.tsx
Original file line number Diff line number Diff line change
@@ -1,22 +1,20 @@
import StudyGroup from './study-group';
import { Study, StudyPage } from '../../types/study/study';
import { RadioGroupItem } from '@radix-ui/react-radio-group';
import { Label } from '@radix-ui/react-label';
import { ShieldIcon } from '../ui/icon/icon';
import {
MAX_DIFFICULTY_LEVEL,
MIN_DIFFICULTY_LEVEL,
Tier,
colorClassMap
} from '@/constants/study/study';
import { ITier } from '@/types/study/tier';
import { cn } from '@/lib/utils';
import { ITier } from '@/types/study/tier';
import { Label } from '@radix-ui/react-label';
import { RadioGroupItem } from '@radix-ui/react-radio-group';
import { ShieldIcon } from '../ui/icon/icon';

export function getTierInfo(difficultyLevel: number): ITier {
difficultyLevel = Math.min(difficultyLevel, MAX_DIFFICULTY_LEVEL);
const tierIndex = Math.floor(difficultyLevel / 5);
const difficultyLevelFromMin = difficultyLevel - MIN_DIFFICULTY_LEVEL;
const tierIndex = Math.floor(difficultyLevelFromMin / 5);
const tier: string = Tier[tierIndex];
const colorClass = colorClassMap[tierIndex];
const division = 5 - (difficultyLevel % 5);
const division = 5 - (difficultyLevelFromMin % 5);
return { colorClass, tier, division };
}
export function TierIcon({ colorClass, tier, division }: ITier) {
Expand Down
3 changes: 2 additions & 1 deletion constants/study/study.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ export enum StudyType {
BOOK = 'κΈ°μˆ μ„œμ  μŠ€ν„°λ””'
}

export const MAX_DIFFICULTY_LEVEL = 29;
export const MAX_DIFFICULTY_LEVEL = 30;
export const MIN_DIFFICULTY_LEVEL = 1;
export const MAX_RELIABILITY_LIMIT = 100;
export const MAX_PENALTY = 100_000;
export const MAX_WEEKS = 52;
Expand Down
2 changes: 1 addition & 1 deletion lib/api/study/get-details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import axios from 'axios';
* μŠ€ν„°λ”” λ””ν…ŒμΌ 쑰회 API
*/
export default async function getStudyDetails(
id: string
id: number
): Promise<StudyDetailsAndRound> {
return axios
.get(`${process.env.NEXT_PUBLIC_API_SERVER_URL}/api/v1/studies/` + id)
Expand Down
22 changes: 22 additions & 0 deletions lib/api/study/start.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import axios from 'axios';

/**
* μŠ€ν„°λ”” μ‹œμž‘ API
* body : studyId
*/

export default function startStudy(studyId: number) {
const token = localStorage.getItem('accessToken');

return axios.post(
`${process.env.NEXT_PUBLIC_API_SERVER_URL}/api/v1/studies/start`,
{
studyId: studyId
},
{
headers: {
Authorization: `Bearer ${token}`
}
}
);
}
16 changes: 12 additions & 4 deletions types/study/register-study.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
MAX_PROBLEM_COUNT,
MAX_RELIABILITY_LIMIT,
MAX_WEEKS,
MIN_DIFFICULTY_LEVEL,
StudyType
} from '@/constants/study/study';
import { RawCreateParams, z } from 'zod';
Expand Down Expand Up @@ -102,15 +103,21 @@ export function getStudySchema(user: User) {
studyType: z.literal(StudyType.ALGORITHM),
difficultyBegin: z
.number()
.min(0, '0 이상이여야 ν•©λ‹ˆλ‹€')
.min(
MIN_DIFFICULTY_LEVEL,
`${MIN_DIFFICULTY_LEVEL} 이상이여야 ν•©λ‹ˆλ‹€`
)
.max(
MAX_DIFFICULTY_LEVEL,
`${MAX_DIFFICULTY_LEVEL} μ΄ν•˜μ—¬μ•Ό ν•©λ‹ˆλ‹€.`
)
.default(0),
.default(1),
difficultyEnd: z
.number()
.min(0, '0 이상이여야 ν•©λ‹ˆλ‹€')
.min(
MIN_DIFFICULTY_LEVEL,
`${MIN_DIFFICULTY_LEVEL} 이상이여야 ν•©λ‹ˆλ‹€`
)
.max(
MAX_DIFFICULTY_LEVEL,
`${MAX_DIFFICULTY_LEVEL} μ΄ν•˜μ—¬μ•Ό ν•©λ‹ˆλ‹€.`
Expand All @@ -135,7 +142,8 @@ export function getStudySchema(user: User) {
studyType: z.literal(StudyType.BOOK),
isbn: z.number({
required_error: 'ν•„μˆ˜μž…λ‹ˆλ‹€.'
})})
})
})
],
{
errorMap: (issue, ctx) => {
Expand Down
8 changes: 7 additions & 1 deletion types/study/study-detail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,16 @@ export interface StudyDetails {
headCount: number;
capacity: number;
penalty: number;
leaderId: number;
reliabilityLimit: number;
startDate: Date;
weeks: number;
status: string;
status: StudyStatus;
}
export enum StudyStatus {
READY = 'READY',
RUNNING = 'RUNNING',
END = 'END'
}

export interface AlgorithmRound {
Expand Down
Loading

0 comments on commit 165bf4b

Please sign in to comment.