Skip to content

Commit

Permalink
feat: 게더링 리스트
Browse files Browse the repository at this point in the history
  • Loading branch information
joarthvr committed Dec 7, 2024
1 parent bd12dc9 commit fdf7c0c
Show file tree
Hide file tree
Showing 20 changed files with 251 additions and 57 deletions.
8 changes: 7 additions & 1 deletion src/features/gathering/api/gathering.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import type {
GatheringDetailResponse,
CreateGatheringRequest,
CreateGatheringResponse,
GatheringLikeResponse,
} from '../model/dto/request.dto';

import api from '@/shared/api/baseApi';
Expand Down Expand Up @@ -36,8 +37,13 @@ export const gatheringApi = {
create: async (requestData: CreateGatheringRequest): Promise<CreateGatheringResponse> => {
const { data } = await api.post<CreateGatheringResponse>('/gathering', {
...requestData,
gatheringImages: [],
gatheringImages: [],
});
return data;
},

toggleLike: async (gatheringId: string): Promise<GatheringLikeResponse> => {
const { data } = await api.post<GatheringLikeResponse>(`/gathering/${gatheringId}/like`);
return data;
},
};
1 change: 1 addition & 0 deletions src/features/gathering/lib/hooks/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './useGatheringDetail';
export * from './useCreateGathering';
export * from './useInfiniteGatheringId';
export * from './useGatheringLike';
2 changes: 0 additions & 2 deletions src/features/gathering/lib/hooks/useCreateGathering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,8 @@ export const useCreateGathering = () => {
mutationFn: gatheringApi.create,
onSuccess: async response => {
try {
// gatherings 쿼리를 무효화하여 리스트가 새로고침되도록 함
await queryClient.invalidateQueries({ queryKey: ['gathering'] });

// 생성된 게더링 상세 페이지로 이동
const gatheringId = response.data?.gatheringId;
if (gatheringId) {
navigate(`/gathering/${gatheringId}`);
Expand Down
2 changes: 1 addition & 1 deletion src/features/gathering/lib/hooks/useGatheringDetail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import type { GatheringDetailResponse } from '../../model/dto/request.dto';

export const useGatheringDetail = (gatheringId: string) => {
return useQuery<GatheringDetailResponse, AxiosError>({
queryKey: ['/gathering', gatheringId],
queryKey: ['/gatheringDetail',gatheringId],
queryFn: () => gatheringApi.getGatheringDetail(gatheringId),
enabled: !!gatheringId,
});
Expand Down
72 changes: 72 additions & 0 deletions src/features/gathering/lib/hooks/useGatheringLike.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import { useMutation, useQueryClient } from '@tanstack/react-query';

import { gatheringApi } from '../../api/gathering.api';
import type { GatheringLikeResponse, GatheringDetailResponse } from '../../model/dto/request.dto';

interface UseGatheringLikeProps {
gatheringId: string;
onSuccess?: (response: GatheringLikeResponse) => void;
onError?: (error: unknown) => void;
}

interface MutationContext {
previousDetail: GatheringDetailResponse | undefined;
}

export const useGatheringLike = ({ gatheringId, onSuccess, onError }: UseGatheringLikeProps) => {
const queryClient = useQueryClient();

return useMutation<GatheringLikeResponse, Error, void, MutationContext>({
mutationFn: () => gatheringApi.toggleLike(gatheringId),

onMutate: async () => {
await queryClient.cancelQueries({
queryKey: ['/gatheringDetail', gatheringId],
});

const previousDetail = queryClient.getQueryData<GatheringDetailResponse>([
'/gatheringDetail',
gatheringId,
]);

console.log('이전 상태:', previousDetail);
return { previousDetail };
},

onSuccess: response => {
console.log('좋아요 API 응답:', response);

const currentDetail = queryClient.getQueryData<GatheringDetailResponse>([
'/gatheringDetail',
gatheringId,
]);

if (currentDetail?.data) {
const newLikeCounts = response.data
? currentDetail.data.likeCounts + 1
: Math.max(0, currentDetail.data.likeCounts - 1);

queryClient.setQueryData<GatheringDetailResponse>(['/gatheringDetail', gatheringId], {
...currentDetail,
data: {
...currentDetail.data,
likeCounts: newLikeCounts,
},
});

console.log('캐시 업데이트 완료, 새로운 좋아요 수:', newLikeCounts);
}

onSuccess?.(response);
},

onError: (error, _, context) => {
console.log('에러 발생:', error);

if (context?.previousDetail) {
queryClient.setQueryData(['/gatheringDetail', gatheringId], context.previousDetail);
}
onError?.(error);
},
});
};
10 changes: 6 additions & 4 deletions src/features/gathering/model/dto/request.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,13 +30,11 @@ interface GatheringListContent {
nextLikeId: number;
}

export type GatheringListResponse = ApiResponse<GatheringListContent>;

export interface GetGatheringsParams {
sort?: GatheringSortType;
period?: GatheringPeriod;
position?: GatheringPosition;
status?: '모집중' | '모집완료';
status?: '모집중' | '모집완료' | '기간만료';
size?: number;
nextLikeId?: number;
}
Expand All @@ -52,11 +50,13 @@ export interface GatheringDetailContent {
personnel: number;
period: GatheringPeriod;
deadLine: string;
position: string;
positions: string[];
gatheringTag: string[];
contactUrl: string;
title: string;
content: string;
likeCounts: number;
status: '모집중' | '모집완료' | '기간만료';
}

// response
Expand All @@ -65,5 +65,7 @@ interface CreateGatheringContent {
gatheringId: number;
}

export type GatheringListResponse = ApiResponse<GatheringListContent>;
export type GatheringLikeResponse = ApiResponse<boolean>;
export type CreateGatheringResponse = ApiResponse<CreateGatheringContent>;
export type GatheringDetailResponse = ApiResponse<GatheringDetailContent>;
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export const GatheringDetailUserInfo = ({
// profileImage = '/default-profile.png',
}: GatheringDetailUserInfoProps) => {
const userData = useUserStore(state => state.userData);
console.log(userData);
return (
<div className={styles.author}>
<img alt={username} className={styles.profileImg} src={userData?.imageUrl} />
Expand Down
12 changes: 11 additions & 1 deletion src/features/gathering/ui/GatheringDetail/GatheringInfoItem.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,17 @@ export const GatheringInfoItem = ({ label, value }: GatheringInfoItemProps) => {
return (
<li className={styles.infoItem}>
<span className={styles.label}>{label}</span>
<span className={styles.value}>{value}</span>
{value instanceof Array ? (
<div>
{value.map((tag, index) => (
<span className={styles.value} key={index}>
{index === value.length - 1 ? tag : tag + ','}
</span>
))}
</div>
) : (
<span className={styles.value}>{value}</span>
)}
</li>
);
};
1 change: 1 addition & 0 deletions src/features/portfolio/api/portfolio.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,4 @@ export const getPortfolioList = async (

return response.data;
};

15 changes: 12 additions & 3 deletions src/features/portfolio/ui/PortfolioCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@ import styles from './PortfolioCard.module.scss';
import { getJobGroupDisplayName } from '../utils/jobGroupConverter';

import type { Portfolio } from '@/features';
import Heart from '@/shared/assets/heart.svg';
import profileImg from '@/shared/assets/paletteLogo.svg';

type PortfolioCardProps = Portfolio;

export const PortfolioCard = ({
Expand All @@ -17,6 +17,7 @@ export const PortfolioCard = ({
minorJobGroup,
memberImageUrl,
jobTitle,
userId,
}: PortfolioCardProps) => {
return (
<div className={styles.container}>
Expand All @@ -30,11 +31,19 @@ export const PortfolioCard = ({
<div className={styles.cardFooter}>
<div className={styles.firstInfo}>
<span className={styles.name}>{username}</span>
<span className={styles.heart}></span>
<button
onClick={(e) => {
e.stopPropagation();
}}
>
<img alt='isLiked-icon' className={styles.heart} src={Heart} />
</button>
</div>
<div className={styles.job}>
<div>
<span>{getJobGroupDisplayName(majorJobGroup)}</span>/
<Link to={`/user/${userId}`}>
<span>{getJobGroupDisplayName(majorJobGroup)}</span>/
</Link>
<span>{getJobGroupDisplayName(minorJobGroup)}</span>
</div>
<span>@{jobTitle}</span>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,12 @@
.stats {
display: flex;
gap: 2rem;
color: #666;
color: $third-color;
}

.likeCount {
display: flex;
align-items: center;
justify-content: center;
}
}
55 changes: 35 additions & 20 deletions src/pages/GatheringDetailPage/GatheringDetailPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,44 @@ import styles from './GatheringDetailPage.module.scss';

import { MarkdownPreview } from '@/features';
import { GatheringDetailUserInfo } from '@/features/gathering';
import { useGatheringDetail } from '@/features/gathering/lib/hooks';
import { useGatheringDetail, useGatheringLike } from '@/features/gathering/lib/hooks';
import { Loader, TripleDot } from '@/shared/ui';
import { LikeBtn } from '@/shared/ui/LikeBtn/LikeBtn';
import { GatheringDetailBtnCon, GatheringDetailHeader, GatheringDetailGrid } from '@/widgets';

export const GatheringDetailPage = () => {
const { gatheringId } = useParams();
const { data, isLoading, isError } = useGatheringDetail(gatheringId!);
console.log('gatheringId:', gatheringId);
console.log('isLoading:', isLoading);
console.log('isError:', isError);
console.log('data:', data);

const { mutate: toggleLike, isPending } = useGatheringLike({
gatheringId: gatheringId!,
onSuccess: response => {
console.log('좋아요 성공:', response);
},
onError: error => {
console.error('좋아요 실패:', error);
},
});
if (isLoading) {
return <div>로딩 중...</div>;
return <Loader />;
}

if (isError) {
return <div>게더링 정보를 불러오는데 실패했습니다.</div>;
return (
<div>
<TripleDot />
게더링 정보를 불러오는데 실패했습니다.
</div>
);
}

if (!data?.data) {
return <div>게더링을 찾을 수 없습니다.</div>;
return (
<div>
<TripleDot />
게더링을 찾을 수 없습니다.
</div>
);
}

const gatheringDetail = data?.data;
Expand All @@ -39,7 +57,7 @@ export const GatheringDetailPage = () => {
gatheringTag={gatheringDetail.gatheringTag}
period={gatheringDetail.period}
personnel={gatheringDetail.personnel}
position={gatheringDetail.position}
positions={gatheringDetail.positions}
sort={gatheringDetail.sort}
subject={gatheringDetail.subject}
username={gatheringDetail.username}
Expand All @@ -53,18 +71,15 @@ export const GatheringDetailPage = () => {
</section>
<div className={styles.footer}>
<div className={styles.stats}>
<span>
<i className='eye-icon'></i> 1.1K
</span>
<span>
<i className='heart-icon'></i> 1.1K
</span>
<LikeBtn
disabled={isPending}
likeCount={gatheringDetail.likeCounts}
onLikeClick={toggleLike}
/>
<span className={styles.likeCount}>{gatheringDetail.likeCounts}</span>
</div>
<GatheringDetailUserInfo
position={gatheringDetail.position}
username={gatheringDetail.username}
/>
<GatheringDetailBtnCon />
<GatheringDetailUserInfo username={gatheringDetail.username} />
<GatheringDetailBtnCon gatheringId={gatheringId} />
</div>
</div>
);
Expand Down
1 change: 0 additions & 1 deletion src/pages/PortfolioListPage/PortfolioListPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ import { MobileSidebarFilter, SidebarFilter } from '@/shared/ui';
import { Button, SelectBtn } from '@/shared/ui';
import { PortFolioGrid } from '@/widgets';

// PortfolioListPage.tsx
export const PortfolioListPage = () => {
const navigate = useNavigate();
const [sort, setSort] = useState({ label: '최신순', value: 'latest' });
Expand Down
2 changes: 2 additions & 0 deletions src/shared/api/baseApi.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ api.interceptors.request.use(config => {
if (accessToken) {
config.headers.Authorization = `Bearer ${accessToken}`;
}
// const decodedUrl = decodeURIComponent(`${config.baseURL}${config.url}${config.params ? '?' + new URLSearchParams(config.params).toString() : ''}`);
// console.log('Decoded URL:', decodedUrl);
return config;
});

Expand Down
34 changes: 34 additions & 0 deletions src/shared/ui/LikeBtn/LikeBtn.module.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
.likeButton {
display: flex;
gap: 4px;
align-items: center;
padding: 8px;
cursor: pointer;
background: transparent;
border: none;
transition: transform 0.2s;

&:hover {
transform: scale(1.1);
}

&:disabled {
cursor: not-allowed;
opacity: 0.5;
}
}

.heart {
width: 24px;
height: 24px;
transition: all 0.2s;

&.liked {
filter: invert(40%) sepia(100%) saturate(1000%) hue-rotate(340deg);
}
}

.count {
font-size: 14px;
color: $primary-color;
}
Loading

0 comments on commit fdf7c0c

Please sign in to comment.