Skip to content

Commit

Permalink
Feat: 마이페이지 작성글보기 api (#399)
Browse files Browse the repository at this point in the history
  • Loading branch information
naraeng committed Feb 22, 2025
1 parent 79bd223 commit 642516f
Show file tree
Hide file tree
Showing 16 changed files with 609 additions and 60 deletions.
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
"@radix-ui/react-slot": "^1.0.2",
"@radix-ui/react-tabs": "^1.0.4",
"@radix-ui/react-tooltip": "^1.0.7",
"@shadcn/ui": "^0.0.4",
"@tanstack/react-query": "^5.28.9",
"@toast-ui/react-editor": "^3.2.3",
"axios": "^1.6.8",
Expand Down
Binary file removed public/image/mypage/arrow_down.png
Binary file not shown.
Binary file removed public/image/mypage/arrow_left.png
Binary file not shown.
Binary file removed public/image/mypage/arrow_right.png
Binary file not shown.
14 changes: 14 additions & 0 deletions src/apis/getUserPosts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { GetUserPostsResponse } from '@/types/apis/get';
import { AxiosResponse } from 'axios';
import { clientAuth } from './client';

export const getUserPosts = async (page: number, take: number) => {
const accessToken = localStorage.getItem('accessToken');
console.log(accessToken);
const response: AxiosResponse<GetUserPostsResponse> = await clientAuth({
baseURL: 'http://13.125.101.7:8080',
url: `board/mypost?page=${page}&take=${take}`,
method: 'get',
});
return response.data;
};
9 changes: 3 additions & 6 deletions src/apis/getUserProfile.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,12 @@
import { getUserProfileResponse } from '@/types/apis/get';
import axios, { AxiosResponse } from 'axios';
import { AxiosResponse } from 'axios';
import { clientAuth } from './client';

export const getUserProfile = async () => {
const accessToken = localStorage.getItem('accessToken');
const response: AxiosResponse<getUserProfileResponse> = await axios({
const response: AxiosResponse<getUserProfileResponse> = await clientAuth({
baseURL: 'http://13.125.101.7:8080',
url: '/users/mypage',
method: 'get',
headers: {
Authorization: `Bearer ${accessToken}`,
},
});
return response.data;
};
4 changes: 0 additions & 4 deletions src/apis/patchUserProfile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,10 @@ export const patchUserProfile = async (data: {
newPassword: string;
confirmNewPassword: string;
}) => {
const accessToken = localStorage.getItem('accessToken');
const response: AxiosResponse<patchUserProfileResponse> = await clientAuth({
baseURL: 'http://13.125.101.7:8080',
url: '/users/mypage',
method: 'PATCH',
headers: {
Authorization: `Bearer ${accessToken}`,
},
data,
});
return response.data;
Expand Down
38 changes: 23 additions & 15 deletions src/pages/mypage/component/UserContainer.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,37 @@
import { useGetUserProfile } from '../profile/hooks/useGetUserProfile';
import { Avatar, AvatarImage, AvatarFallback } from '@/components/ui/avatar';

export default function UserContainer() {
const { data, isLoading, error } = useGetUserProfile();
const userData = data?.data;

if (isLoading) console.log('loading');
if (error) console.log('error : ', error);
if (isLoading) {
console.log('loading');
return null;
}

if (error || !userData) {
console.log('error : ', error);
return null;
}

return (
<div className="w-full px-4">
{userData?.isUnion && userData ? (
{userData.isUnion ? (
<div className="mx-auto mb-10 mt-16 flex flex-row items-center rounded-2xl border-2 border-[#D9D9D9] bg-white px-10 py-8 xs:mx-[20%] xs:flex-col xs:pb-4 sm:mx-[22%] sm:flex-col md:mx-10 md:py-6 lg:mx-10 xl:mx-10 xxl:mx-10">
<img
className="mr-8 h-24 w-24 xs:mb-4 xs:mr-0 xs:h-20 xs:w-20 sm:mr-0 md:mr-6"
src="/image/mypage/profile_img.png"
alt="profile_default_img"
/>
<Avatar className="mr-8 h-24 w-24 xs:mb-4 xs:mr-0 xs:h-20 xs:w-20 sm:mr-0 md:mr-6">
<AvatarImage src="/image/mypage/profile_img.png" alt="profile_default_img" />
<AvatarFallback>User</AvatarFallback>
</Avatar>

<div className="mb-4">
<div className="mb-2 flex items-center xs:justify-center sm:mt-6 sm:justify-center md:mt-6">
<span className="text-lg font-bold">{userData.nickname}</span>
</div>
<div className="mb-1 text-sm xs:text-xs sm:mb-3 sm:text-xs md:mb-2">
<span>{userData?.memberCode}</span>
{userData?.majorCode ? <span></span> : ''}
<span>{userData?.majorCode}</span>
<span>{userData.memberCode}</span>
{userData.majorCode ? <span></span> : ''}
<span>{userData.majorCode}</span>
</div>
</div>
</div>
Expand All @@ -36,13 +44,13 @@ export default function UserContainer() {
/>
<div className="mb-4">
<div className="mb-2 flex items-center xs:justify-center sm:mt-6 sm:justify-center md:mt-6">
<span className="text-lg font-bold xs:text-sm sm:text-base">{userData?.name}</span>
<span className="text-lg font-bold xs:text-sm sm:text-base">{userData.name}</span>
<span className="ml-1 mr-1 text-xl font-light"> | </span>
<span className="text-lg font-bold xs:m-0 xs:text-sm sm:text-base">{userData?.studentId}</span>
<span className="text-lg font-bold xs:m-0 xs:text-sm sm:text-base">{userData.studentId}</span>
</div>
<div className="mb-1 text-sm xs:text-xs sm:mb-3 sm:text-xs md:mb-2">
<span>{userData?.memberCode}</span>
<span>{userData?.majorCode}</span>
<span>{userData.memberCode}</span>
<span>{userData.majorCode}</span>
<span>재학</span>
</div>
{/* <div className="flex text-sm xs:flex-col xs:items-center xs:text-xs sm:flex-col sm:items-center sm:text-xs md:flex-col">
Expand Down
Empty file.
22 changes: 22 additions & 0 deletions src/pages/mypage/myPosts/hooks/useGetUserPosts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { getUserPosts } from '@/apis/getUserPosts';
import { GetUserPostsResponse } from '@/types/apis/get';
import { useQuery, UseQueryResult } from '@tanstack/react-query';
import axios, { AxiosError } from 'axios';

export const useGetUserPosts = (page: number, take: number): UseQueryResult<GetUserPostsResponse, AxiosError> => {
return useQuery<GetUserPostsResponse, AxiosError>({
queryKey: ['get-user-posts', page, take],
// queryFn: () => getUserPosts(page, take),
queryFn: async () => {
try {
return await getUserPosts(page, take);
} catch (error) {
console.error('API Error:', error);
if (axios.isAxiosError(error)) {
console.error('Error response:', error.response?.data);
}
throw error;
}
},
});
};
52 changes: 39 additions & 13 deletions src/pages/mypage/myPosts/myPostsContent/myPostsContent.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,49 @@
import CommentMark from '@/assets/image/comentMark.svg';

interface MyPostsPageProps {
boardCode: string;
postId: number;
import { PostListResDto } from '@/types/apis/get';
import { useNavigate } from 'react-router-dom';
interface MyPostsContentProps {
data: PostListResDto;
}
export function MyPostsContent({ boardCode, postId }: MyPostsPageProps) {
const boardRoutes: { [key: string]: string } = {
질의응답게시판: '/qna',
공지사항: '/notice',
제휴게시판: '/partnership',
분실물게시판: '/lost-article',
학생자치기구: '/petition-notice',
인권신고게시판: '/human-rights',
건의게시판: '/sug-notice',
자료집게시판: '/data',
감사게시판: '/audit',
서비스공지사항: '/service-notice',
};

export function MyPostsContent({ data }: MyPostsContentProps) {
const navigate = useNavigate();

const onClickMyPost = () => {
const basePath = boardRoutes[data.boardCode];
if (basePath) {
navigate(`${basePath}/${data.postId}`);
} else {
console.error(`Invalid boardCode: ${data.boardCode}`);
}
};

return (
<div className="mx-16 mt-[14px] flex flex-col border-b-[1px] border-solid border-gray-400 pr-2 text-[14px]">
<div className="flex cursor-pointer flex-row justify-between xs:flex-col xs:items-end">
<div className="flex items-center gap-[20px]">
<div className="ml-[10px] h-[20px] w-[52px] text-[#2F4BF7]">{postId.toString()}</div>
<div className="text-[#374151]">[{boardCode}] 교수님 종강은 언제하시나요..</div>
<div className="flex items-center">
<div className="ml-[10px] h-[20px] w-[52px] text-[#2F4BF7]">{data.postId.toString()}</div>
<div className="text-[#374151]" onClick={onClickMyPost}>
[{data.boardCode}] {data.title}
</div>
</div>
<div className="font-[500] text-[#6B7280]">2023/10/02</div>
<div className="self-end font-[400] text-[#6B7280]">{data.date}</div>
</div>
<div className="mb-[10px] flex flex-col items-end">
<div className="flex gap-[4px] text-[#2F4BF7]">
<img src={CommentMark} className="mt-[2px] h-[14px] w-[14px]" />
32
<div className="mb-3 flex flex-col items-end">
<div className="flex gap-1 text-[#2F4BF7]">
<img src={CommentMark} className="size-4" />
{data.commentCount}
</div>
</div>
</div>
Expand Down
80 changes: 67 additions & 13 deletions src/pages/mypage/myPosts/page.tsx
Original file line number Diff line number Diff line change
@@ -1,39 +1,93 @@
import { useState } from 'react';
import UserContainer from '../component/UserContainer';
import { MyPostsContent } from './myPostsContent/myPostsContent';
import { useGetUserPosts } from './hooks/useGetUserPosts';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';

function MyPostsPage() {
const [page, setPage] = useState(0);
const [take] = useState(6);
const [searchText, setSearchText] = useState('');

const { data, isLoading, error } = useGetUserPosts(page, take);
const totalPages = data?.data?.pageInfo?.totalPages ?? 1;

const onClickSearch = () => {
console.log(searchText);
};

const handlePageClick = (pageNumber: number) => {
setPage(pageNumber);
};

const getPaginationRange = () => {
const totalPagesToShow = 6;
const startPage = Math.max(0, page - Math.floor(totalPagesToShow / 2));
const endPage = Math.min(totalPages, startPage + totalPagesToShow);
return Array.from({ length: endPage - startPage }, (_, i) => startPage + i);
};

if (isLoading) {
console.log('loading');
return null;
}

if (error || !data) {
console.log('error : ', error);
return null;
}

return (
<div className="flex flex-col">
<UserContainer />
<div>
<div className="mx-16 flex items-center border-b-[1px] border-solid border-gray-500 pb-1">
<span className="pl-2">작성 글</span>
<span className="ml-2 text-center font-semibold">45</span>
<span className="ml-2 text-center font-semibold">{data?.data.pageInfo.totalElements}</span>
</div>
<MyPostsContent boardCode="건의게시판" postId={12345} />
<MyPostsContent boardCode="건의게시판" postId={13213} />
<MyPostsContent boardCode="건의게시판" postId={125} />
<MyPostsContent boardCode="건의게시판" postId={54321} />
<MyPostsContent boardCode="건의게시판" postId={222} />
<MyPostsContent boardCode="건의게시판" postId={5658} />
{data?.data?.postListResDto?.map((post) => <MyPostsContent key={post.postId} data={post} />)}
</div>
<div className="my-16 flex justify-center gap-3">
<input
<div className="mb-6 mt-14 flex items-center justify-center text-sm">
<button
onClick={() => setPage((prev) => Math.max(prev - 1, 0))}
disabled={page === 0}
className="border-none px-3 py-1 text-gray-700 disabled:opacity-30"
>
&lt;
</button>

{getPaginationRange().map((pageNumber) => (
<button
key={pageNumber}
onClick={() => handlePageClick(pageNumber)}
className={`rounded-sm border-none px-3 py-1 text-gray-700 ${page === pageNumber ? 'bg-gray-200' : 'bg-white'}`}
>
{pageNumber + 1}
</button>
))}

<button
onClick={() => setPage((prev) => (prev + 1 < totalPages ? prev + 1 : prev))}
disabled={page + 1 >= totalPages}
className="border-none px-3 py-1 text-gray-700 disabled:opacity-30"
>
&gt;
</button>
</div>
<div className="mb-16 flex justify-center gap-3">
<Input
type="text"
onChange={(e) => setSearchText(e.target.value)}
className="w-[40%] rounded-md border border-gray-300 p-3 text-[13px]"
onChange={(e: { target: { value: string } }) => setSearchText(e.target.value)}
className="w-56 rounded-md border border-gray-300 p-3 text-xs md:w-80"
placeholder="원하시는 키워드를 입력하세요"
/>
<button onClick={onClickSearch} className="rounded-md bg-[#2F4BF7] px-7 text-[13px] text-white">
<Button
onClick={onClickSearch}
className="h-12 rounded-md bg-[#2F4BF7] px-7 text-[13px] text-white xs:px-4 sm:px-5"
>
검색
</button>
</Button>
</div>
</div>
);
Expand Down
9 changes: 3 additions & 6 deletions src/pages/mypage/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import Sidebar from './component/Sidebar';
import DropdownUserMenu from './component/DropdownUserMenu';
import { ServiceNoticePage } from './service-notice/page';
import MyPostsPage from './myPosts/page';
import { ChevronDown } from 'lucide-react';

export default function MyPage() {
const [selectedMenu, setSelectedMenu] = useState('내 정보');
Expand All @@ -30,11 +31,7 @@ export default function MyPage() {
className="mb-5 ml-3 hidden items-center xs:block sm:block"
onClick={() => setIsDropdown(!isDropdown)}
>
<img
src="/image/mypage/arrow_down.png"
alt="arrow down"
className={`h-2 w-3 transform transition-transform duration-200 ${isDropdown ? 'rotate-180' : ''}`}
/>
<ChevronDown className="h-4 w-6" color="#9CA3AF" />
</button>
{isDropdown && (
<div className="absolute top-10">
Expand All @@ -47,7 +44,7 @@ export default function MyPage() {
)}
</div>
<div className="w-full border-b border-[#E7E7E7]"></div>
<div className="flex md:flex-row lg:flex-row xl:flex-row xxl:flex-row">
<div className="flex">
<Sidebar selectedMenu={selectedMenu} setSelectedMenu={setSelectedMenu} />
<div className="flex-grow">{renderMenu()}</div>
</div>
Expand Down
9 changes: 9 additions & 0 deletions src/style.css
Original file line number Diff line number Diff line change
Expand Up @@ -225,3 +225,12 @@ body {
background: #2f4bf7;
opacity: 0.1;
}

@layer base {
* {
@apply border-border outline-ring/50;
}
body {
@apply bg-background text-foreground;
}
}
22 changes: 22 additions & 0 deletions src/types/apis/get/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,25 @@ export interface patchUserProfileResponse {
memberCode: string;
isUnion: boolean;
}
export interface PostListResDto {
postId: number;
title: string;
content: string;
date: string;
commentCount: number;
boardCode: string;
}

export interface GetUserPostsResponse {
data: {
postListResDto: PostListResDto[];
pageInfo: UserPostsPageInfo;
};
}

export interface UserPostsPageInfo {
pageNum: number;
pageSize: number;
totalElements: number;
totalPages: number;
}
Loading

0 comments on commit 642516f

Please sign in to comment.