Skip to content
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
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
120 changes: 0 additions & 120 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 16 additions & 0 deletions src/_apis/crew/crew-review-apis.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import { fetchApi } from '@/src/utils/api';
import { CrewReviewResponse } from '@/src/types/review';

// 크루 리뷰 조회
export async function getCrewReviews(crewId: number, page: number): Promise<CrewReviewResponse> {
const url = `/api/review/${crewId}?page=${page}&size=5`;

const response = await fetchApi<{ data: CrewReviewResponse }>(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
},
});

return response.data;
}
1 change: 0 additions & 1 deletion src/_apis/crew/crew.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ import {
CreateCrewRequestTypes,
CreateCrewResponseTypes,
EditCrewRequestTypes,
EditCrewResponseTypes,
} from '@/src/types/create-crew';

export async function createCrew(data: CreateCrewRequestTypes) {
Expand Down
9 changes: 9 additions & 0 deletions src/_queries/crew/crew-review-queries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { useQuery } from '@tanstack/react-query';
import { getCrewReviews } from '@/src/_apis/crew/crew-review-apis';

export function useGetCrewReviewsQuery(crewId: number, page: number) {
return useQuery({
queryKey: ['crewReviews', crewId, page],
queryFn: () => getCrewReviews(crewId, page - 1),
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import { FormEvent, useEffect, useState } from 'react';
import { Controller, useForm, useWatch } from 'react-hook-form';
import useFormPersist from 'react-hook-form-persist';
import { toast } from 'react-toastify';
import { useRouter } from 'next/navigation';
import { NumberInput } from '@mantine/core';
import { getImageUrl } from '@/src/_apis/image/get-image-url';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
'use client';

import { FormEvent, useEffect } from 'react';
import { FormEvent } from 'react';
import { Controller, useForm, useWatch } from 'react-hook-form';
import { toast } from 'react-toastify';
import { NumberInput } from '@mantine/core';
import { getImageUrl } from '@/src/_apis/image/get-image-url';
import Button from '@/src/components/common/input/button';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@

import { useRouter } from 'next/navigation';
import { useDisclosure } from '@mantine/hooks';
import { useGetCrewDetailQuery } from '@/src/_queries/crew/crew-detail-queries';
import { useGetGatheringListQuery } from '@/src/_queries/crew/gathering-list-queries';
import { useAuth } from '@/src/hooks/use-auth';
import CreateGatheringModalContainer from '@/src/app/(crew)/crew/detail/[id]/_components/create-gathering/create-gathering-modal/container';
import Button from '@/src/components/common/input/button';
import { CrewDetail } from '@/src/types/crew-card';
import { CreateGatheringFormTypes } from '@/src/types/gathering-data';

export default function CreateGathering({ crewId }: { crewId: number }) {
const { isAuth } = useAuth();
const router = useRouter();
const [opened, { open, close }] = useDisclosure(false);

const { data: gatheringList, isLoading, error, refetch } = useGetGatheringListQuery(crewId);
const { data: gatheringList } = useGetGatheringListQuery(crewId);

// totalGatheringCount 추출
const totalGatheringCount = gatheringList?.length ?? 0;
Expand All @@ -39,9 +37,9 @@ export default function CreateGathering({ crewId }: { crewId: number }) {

return (
<div className="flex items-center justify-between px-3 md:px-7 lg:px-11">
<div className="flex items-end space-x-2">
<div className="flex flex-col space-y-1 md:flex-row md:items-end md:space-x-2">
<h2 className="text-2xl font-semibold text-gray-800">약속 잡기</h2>
<span className="text-base font-semibold text-blue-500">
<span className="text-sm font-semibold text-blue-500 md:text-base">
현재 {totalGatheringCount}개의 약속이 개설되어 있습니다.
</span>
</div>
Expand Down
20 changes: 9 additions & 11 deletions src/app/(crew)/crew/detail/[id]/_components/crew-review-list.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import React from 'react';
import { Pagination } from '@mantine/core';
import { cn } from '@/src/utils/cn';
import ReviewCard from '@/src/components/common/review-list/review-card';
import { CrewReview } from '@/src/types/review';

Expand All @@ -21,7 +22,7 @@ export default function CrewReviewList({
return (
<div className="flex flex-col justify-between p-6">
<div className="mb-6 grid flex-grow gap-4">
{/* {reviews.map((review) => (
{reviews.map((review) => (
<ReviewCard
key={review.id}
rate={review.rate}
Expand All @@ -30,30 +31,27 @@ export default function CrewReviewList({
crewId={review.crewId}
reviewer={review.reviewer}
/>
))} */}
))}
</div>
<div className="mt-6 flex justify-center">
<Pagination
total={totalPages}
value={currentPage}
onChange={onPageChange}
classNames={{
control: cn(
'data-[active="true"]:text-blue-500 data-[active="true"]:font-bold',
'border-none bg-transparent hover:bg-transparent',
),
}}
styles={{
control: {
border: 'none',
backgroundColor: 'transparent',
'&[data-active]': {
backgroundColor: 'transparent',
fontWeight: 'var(--pagination-active-font-weight)',
color: 'var(--pagination-active-color)',
boxShadow: 'none',
},
'&:hover': {
backgroundColor: 'transparent',
},
},
root: {
'--pagination-active-color': '#3388FF',
'--pagination-active-font-weight': 'bold',
},
}}
size="sm"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,11 @@ export default function DetailCrew({ id }: DetailCrewContainerProps) {
await refetch();
} catch (joinError) {
if (joinError instanceof ApiError) {
toast.error(joinError.message);
if (joinError.status === 401) {
router.push(`/login?redirect=${encodeURIComponent(window.location.pathname)}`);
} else {
toast.error(joinError.message);
}
} else {
toast.error('🚫 크루 참여 중 에러가 발생했습니다.');
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use client';

import { toast } from 'react-toastify';
import { useRouter } from 'next/navigation';
import {
CancelGathering,
JoinGathering,
Expand All @@ -26,16 +27,19 @@ export default function GatheringDetailModalContainer({
const showToast = (message: string, type: 'success' | 'error' | 'warning') => {
toast(message, { type });
};
const router = useRouter();

const handleJoin = async () => {
try {
await JoinGathering(data.crewId, data.id);
showToast('약속에 참여했습니다.', 'success');
close();
onUpdate?.();
} catch (error) {
if (error instanceof ApiError) {
showToast(`참여 중 에러 발생: ${error.message}`, 'error');
if (error instanceof ApiError && error.status === 401) {
const redirectUrl = `/login?redirect=${encodeURIComponent(window.location.pathname)}`;
router.push(redirectUrl);
} else {
showToast('참여 중 에러가 발생했습니다.', 'error');
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,19 +22,25 @@ export default function GatheringListSection({ id }: GatheringListSectionProps)
const handleLike = async (gatheringId: number) => {
try {
await addLike(gatheringId);
toast.success('찜하기가 완료되었습니다!');
} catch (apiError) {
if (apiError instanceof ApiError) {
toast.error(`찜하기에 실패했습니다`);
if (apiError instanceof ApiError && apiError.status === 401) {
toast.error('로그인이 필요합니다.');
} else {
toast.error('찜하기에 실패했습니다.');
}
}
};

const handleUnlike = async (gatheringId: number) => {
try {
await removeLike(gatheringId);
toast.success('찜하기 해제가 완료되었습니다!');
} catch (apiError) {
if (apiError instanceof ApiError) {
toast.error(`찜하기 해제에 실패했습니다`);
if (apiError instanceof ApiError && apiError.status === 401) {
toast.error('로그인이 필요합니다.');
} else {
toast.error('찜하기 해제에 실패했습니다.');
}
}
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,29 +7,29 @@ export default {
component: RatingDisplay,
tags: ['autodocs'],
argTypes: {
totalReviewCount: { control: 'number', description: '총 평가 개수' },
totalRate: { control: 'number', description: '총 평가 개수' },
averageRate: { control: 'number', description: '평균 평점' },
ratingsData: { control: 'object', description: '각 점수별 평가 개수' },
},
} as Meta<typeof RatingDisplay>;

interface RatingDisplayStoryProps {
totalReviewCount: number;
totalRate: number;
averageRate: number;
ratingsData: { score: number; count: number }[];
}

// Template을 함수 선언으로 변경하고 StoryFn 타입을 사용
const Template: StoryFn<RatingDisplayStoryProps> = function Template(args) {
const { totalReviewCount, averageRate, ratingsData } = args;
const reviewRateInfo: ReviewRateInfo = { totalReviewCount, averageRate, ratingsData };
const { totalRate, averageRate, ratingsData } = args;
const reviewRateInfo: ReviewRateInfo = { totalRate, averageRate, ratingsData };
return <RatingDisplay reviewRateInfo={reviewRateInfo} />;
};

// 스토리 정의
export const Default = Template.bind({});
Default.args = {
totalReviewCount: 24,
totalRate: 24,
averageRate: 3.5,
ratingsData: [
{ score: 5, count: 6 },
Expand All @@ -42,7 +42,7 @@ Default.args = {

export const HighRating = Template.bind({});
HighRating.args = {
totalReviewCount: 15,
totalRate: 15,
averageRate: 4.7,
ratingsData: [
{ score: 5, count: 10 },
Expand All @@ -55,7 +55,7 @@ HighRating.args = {

export const LowRating = Template.bind({});
LowRating.args = {
totalReviewCount: 20,
totalRate: 20,
averageRate: 1.8,
ratingsData: [
{ score: 5, count: 1 },
Expand Down
Loading