Skip to content

Commit

Permalink
[Feat] 게더링 및 포트폴리오 미흡한 부분 수정 (#113)
Browse files Browse the repository at this point in the history
* refactor: 게더링 디테일 헤더 게시글 작성자 정보로 수정

* refactor: 게더링 연락 버튼 유저네임 연결

* refactor: 게더링 카드 수정

* refactor: 포트폴리오 카드 링크 추가 부분 추가

* reafactor: 포트폴리오 유저페이지 연결

* feat: 포트폴리오 좋아요

* [Fix] 무한 스크롤 수정 (#107)

* fix: useCumtomInfiniteQuery 수정

* feat: 실시간 알림 기능 추가

* docs: README 작성

* feat: 포폴 좋아요한 리스트 훅

* feat: 메인 게더링 그리드 구현

* feat: 게더링 좋아요 리스트

* reafactor: console.log 지우기

* refactor: main.tsx mock 코드 삭제

* refactor: 팔레트 철자 변경

* refactor: alert 정리

* refactor: 태크 키씹힘 수정

* refactor: 게더링 좋아요 수정

* fix: 배포오류 수정

* fix: 배포 오류 수정

---------

Co-authored-by: Cho-heejung <[email protected]>
  • Loading branch information
joarthvr and he2e2 authored Dec 9, 2024
1 parent 4fa7dc9 commit 743b113
Show file tree
Hide file tree
Showing 54 changed files with 697 additions and 150 deletions.
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

<p>자신만의 경험을 색상과 함께 공유하고,<br>네트워킹을 통해 팔레트를 채워주세요!</p>

https://www.palettee.site/
https://www.palette.site/

<br>

Expand Down
2 changes: 1 addition & 1 deletion index.html
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
name="viewport"
content="width=device-width, initial-scale=1, minimum-scale=1, maximum-scale=1, viewport-fit=cover, user-scalable=no"
/>
<title>Palettee</title>
<title>Palette</title>
</head>
<body>
<div id="root"></div>
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "palettee",
"name": "palette",
"private": true,
"version": "1.0.1",
"type": "module",
Expand Down
6 changes: 0 additions & 6 deletions src/app/main.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@ import { createRoot } from 'react-dom/client';
import App from './App';
import './styles/globals.scss';

import { worker } from '@/mocks/browser';

if (process.env.NODE_ENV === 'development') {
void worker.start({ onUnhandledRequest: 'warn' });
}

createRoot(document.getElementById('root')!).render(
<StrictMode>
<App />
Expand Down
52 changes: 52 additions & 0 deletions src/features/gathering/api/gathering.api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import type {

import api from '@/shared/api/baseApi';

// 기본 게더링 API
export const gatheringApi = {
getGatherings: async (params: GatheringListParams): Promise<GatheringPageResponse> => {
// params를 URLSearchParams로 변환
Expand Down Expand Up @@ -41,6 +42,7 @@ export const gatheringApi = {
});
return data;
},

update: async (
gatheringId: string,
data: CreateGatheringRequest,
Expand All @@ -53,10 +55,60 @@ export const gatheringApi = {
const { data } = await api.post<GatheringLikeResponse>(`/gathering/${gatheringId}/like`);
return data;
},

deleteGathering: async (gatheringId: string): Promise<void> => {
await api.delete(`/gathering/${gatheringId}`);
},

completeGathering: async (gatheringId: string): Promise<void> => {
await api.patch(`/gathering/${gatheringId}`);
},

// 좋아요한 게더링 목록 조회 추가
getGatheringLikeList: async (params: {
size: number;
gatheringId?: number;
}): Promise<GatheringPageResponse> => {
const queryString = new URLSearchParams();

Object.entries(params).forEach(([key, value]) => {
if (value !== undefined) {
queryString.append(key, value.toString());
}
});

const { data } = await api.get<GatheringPageResponse>(
`/gathering/my-page?${queryString.toString()}`,
);
return data;
},
};

// 메인 페이지용 게더링 API
export const mainGatheringApi = {
// 메인 페이지용 게더링 목록 조회 (최신 4개)
getMainGatherings: async (): Promise<GatheringPageResponse> => {
const params: GatheringListParams = {
page: 1,
size: 4,
status: '모집중', // 활성화된 게더링만 표시
};

// params를 URLSearchParams로 변환
const queryString = new URLSearchParams();
Object.entries(params).forEach(([key, value]) => {
if (value !== undefined && value !== 'undefined') {
queryString.append(key, value.toString());
}
});

const { data } = await api.get<GatheringPageResponse>(`/gathering?${queryString.toString()}`);
return data;
},
};

// 두 API 객체를 모두 export
export default {
...gatheringApi,
...mainGatheringApi,
};
5 changes: 3 additions & 2 deletions src/features/gathering/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
export * from './lib/hooks';
export * from './model/index';
export * from './model/options';
export * from './model/types';
export * from './ui/GatheringCard';
export { GatheringDatePicker } from './ui/GatheringDatePicker';
export * from './ui/GatheringDetail/index';
export { GatheringLinkInput } from './ui/GatheringLinkInput';
export { GatheringMarkdownEditor } from './ui/GatheringMarkdownEditor';
export { GatheringSelect } from './ui/GatheringSelect';
export { GatheringTagInput } from './ui/GatheringTagInput';
export { GatheringTitleInput } from './ui/GatheringTitIeInput';
export * from './lib/hooks';
export * from './model/index';
3 changes: 3 additions & 0 deletions src/features/gathering/lib/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,8 @@ export * from './useCreateGathering';
export * from './useDeleteGathering';
export * from './useGatheringDetail';
export * from './useGatheringLike';
export * from './useGatheringLikeList';
export * from './useInfiniteGatheringId';
export * from './useMainGathering';
export * from './useUpdateGathering';

23 changes: 18 additions & 5 deletions src/features/gathering/lib/hooks/useCreateGathering.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { useNavigate } from 'react-router-dom';
import { gatheringApi } from '../../api/gathering.api';
import type { CreateGatheringRequest, CreateGatheringResponse } from '../../model/dto/request.dto';

import { errorAlert } from '@/shared/ui';
export const useCreateGathering = () => {
const queryClient = useQueryClient();
const navigate = useNavigate();
Expand All @@ -27,18 +28,30 @@ export const useCreateGathering = () => {
alert('게더링은 생성되었으나 추가 처리 중 오류가 발생했습니다.');
}
},
onError: (error: AxiosError) => {
onError: async (error: AxiosError) => {
if (error.response?.status === 401) {
alert('로그인이 필요합니다.');
await errorAlert({
title: '로그인 필요',
text: '로그인이 필요한 서비스입니다. 로그인 페이지로 이동합니다.',
});
navigate('/login');
} else if (error.response?.status === 403) {
alert('권한이 부족합니다. 다시 로그인해주세요.');
await errorAlert({
title: '권한 없음',
text: '권한이 없습니다. 로그인 후 다시 시도해주세요.',
});
navigate('/login');
} else if (error.response?.status === 400) {
alert('입력하신 정보를 다시 확인해주세요.');
await errorAlert({
title: '입력값 오류',
text: '입력값을 확인해주세요',
});
} else {
console.error('게더링 생성 실패:', error);
alert('게더링 생성에 실패했습니다. 잠시 후 다시 시도해주세요.');
await errorAlert({
title: '게더링 생성 실패',
text: '게더링 생성에 실패했습니다.',
});
}
},
});
Expand Down
22 changes: 15 additions & 7 deletions src/features/gathering/lib/hooks/useGatheringLike.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,22 +20,32 @@ export const useGatheringLike = ({ gatheringId, onSuccess, onError }: UseGatheri
mutationFn: () => gatheringApi.toggleLike(gatheringId),

onMutate: async () => {
// 진행 중인 쿼리 취소
await queryClient.cancelQueries({
queryKey: ['/gatheringDetail', gatheringId],
});


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

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

if (previousDetail?.data) {
queryClient.setQueryData<GatheringDetailResponse>(['/gatheringDetail', gatheringId], {
...previousDetail,
data: {
...previousDetail.data,
isLiked: !previousDetail.data.isLiked,
},
});
}

return { previousDetail };
},

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

const currentDetail = queryClient.getQueryData<GatheringDetailResponse>([
'/gatheringDetail',
gatheringId,
Expand All @@ -51,18 +61,16 @@ export const useGatheringLike = ({ gatheringId, onSuccess, onError }: UseGatheri
data: {
...currentDetail.data,
likeCounts: newLikeCounts,
isLiked: !!response.data,
},
});

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

onSuccess?.(response);
},

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


if (context?.previousDetail) {
queryClient.setQueryData(['/gatheringDetail', gatheringId], context.previousDetail);
}
Expand Down
51 changes: 51 additions & 0 deletions src/features/gathering/lib/hooks/useGatheringLikeList.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
import { useInfiniteQuery } from '@tanstack/react-query';

import { gatheringApi } from '../../api/gathering.api';
import type { GatheringPageResponse, GatheringItem } from '../../model/dto/gathering.dto';

interface UseGatheringLikeListProps {
size?: number;
}

interface GatheringLikeListParams {
size: number;
likeId: number;
}

export const useGatheringLikeList = ({ size = 10 }: UseGatheringLikeListProps = {}) => {
const query = useInfiniteQuery<GatheringPageResponse>({
queryKey: ['infiniteLikeGatherings'],
queryFn: async ({ pageParam }) => {
const params: GatheringLikeListParams = {
size,
likeId: pageParam as number,
};

return gatheringApi.getGatheringLikeList(params);
},
getNextPageParam: lastPage => {
if (!lastPage?.data?.hasNext) return undefined;
return lastPage?.data.nextId ?? undefined;
},
initialPageParam: undefined,
});

const gatherings =
query.data?.pages?.reduce<GatheringItem[]>((acc, page) => {
const content = page.data?.content || [];

const newGatherings = content.filter(
newGathering =>
!acc.some(
existingGathering => existingGathering.gatheringId === newGathering.gatheringId,
),
);

return [...acc, ...newGatherings];
}, []) ?? [];

return {
...query,
gatherings,
};
};
11 changes: 4 additions & 7 deletions src/features/gathering/lib/hooks/useInfiniteGatheringId.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,20 +50,17 @@ export const useInfiniteGatheringId = ({

...(pageParam ? { gatheringId: pageParam } : {}),
};
console.log('API Request:', params);


const response = await gatheringApi.getGatherings(params);
console.log('API Response:', response);

return response;
},
getNextPageParam: (lastPage: GatheringPageResponse) => {
// console.log('Getting next page param:', {
// hasNext: lastPage.data.hasNext,
// nextLikeId: lastPage.data.nextLikeId,
// });


if (!lastPage.data.hasNext) return undefined;
return lastPage.data.nextLikeId ?? undefined;
return lastPage.data.nextId ?? undefined;
},
initialPageParam: undefined,
});
Expand Down
21 changes: 21 additions & 0 deletions src/features/gathering/lib/hooks/useMainGathering.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import { useQuery } from '@tanstack/react-query';

import { mainGatheringApi } from '../../api/gathering.api';

export const useMainGathering = () => {
const query = useQuery({
queryKey: ['mainGatheringList'],
queryFn: async () => {
const response = await mainGatheringApi.getMainGatherings();
return response;
},
});

// items 배열 추출
const items = query.data?.data.content ?? [];

return {
...query,
items,
};
};
4 changes: 3 additions & 1 deletion src/features/gathering/model/dto/gathering.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,12 +39,14 @@ export interface GatheringItem {
deadLine: string;
username: string;
tags: string[];
positions: GatheringPosition[];
subject: string;
}
export interface GatheringPageResponse {
data: {
content: GatheringItem[];
hasNext: boolean;
nextLikeId: number | null;
nextId: number | null;
};
timeStamp: string;
}
Expand Down
5 changes: 3 additions & 2 deletions src/features/gathering/model/dto/request.dto.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export interface CreateGatheringRequest {
interface GatheringListContent {
content: GatheringDetailContent[];
hasNext: boolean;
nextLikeId: number;
nextId: number;
}

export interface GetGatheringsParams {
Expand All @@ -36,7 +36,7 @@ export interface GetGatheringsParams {
position?: GatheringPosition;
status?: '모집중' | '모집완료' | '기간만료';
size?: number;
nextLikeId?: number;
nextId?: number;
}

// 게더링 상세 조회 응답
Expand All @@ -56,6 +56,7 @@ export interface GatheringDetailContent {
title: string;
content: string;
likeCounts: number;
isLiked: boolean;
status: '모집중' | '모집완료' | '기간만료';
}

Expand Down
2 changes: 1 addition & 1 deletion src/features/gathering/model/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const gatheringFilterOptions: GatheringFilterOptions = {
contact: [
{ value: '온라인', label: '온라인' },
{ value: '오프라인', label: '오프라인' },
{ value: '온라인&오프라인', label: '온라인&오프라인' },
{ value: '온라인 & 오프라인', label: '온라인 & 오프라인' },
],
sort: [
{ value: '스터디', label: '스터디' },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@
font-weight: 500;
}

&__introduction {
&__sort {
display: -webkit-box; // 추가: Webkit 기반 브라우저 지원
display: -moz-box; // 추가: Firefox 지원
max-height: 2.4em; // 추가: line-height * 2
Expand Down
Loading

1 comment on commit 743b113

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚡ Lighthouse report for http://localhost:3000/

Category Score
🔴 Performance 21
🟠 Accessibility 81
🟢 Best Practices 92
🟠 SEO 85

Detailed Metrics

Metric Value
🔴 First Contentful Paint 50.7 s
🔴 Largest Contentful Paint 90.7 s
🔴 Total Blocking Time 1,100 ms
🟢 Cumulative Layout Shift 0.067
🔴 Speed Index 67.1 s

Please sign in to comment.