Skip to content

Commit 08413cd

Browse files
committed
✨ feat : Add Post component APIs & layout
- Create Post API - Delete Post API - Get post API - Add interface files for props Related Issue : YJU-OKURA#101
1 parent e9c993c commit 08413cd

File tree

18 files changed

+744
-42
lines changed

18 files changed

+744
-42
lines changed
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import req from '../apiUtils';
2+
3+
const DeleteClassBoard = async (bid: number, cid: number, uid: number) => {
4+
const response = await req(
5+
`/cb/${bid}?cid=${cid}&uid=${uid}`,
6+
'delete',
7+
'gin'
8+
);
9+
10+
return response;
11+
};
12+
13+
export default DeleteClassBoard;
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import req from '../apiUtils';
2+
3+
const getClassAnnounced = async (classCode: number) => {
4+
const response = await req(`/cb/announced?cid=${classCode}`, 'get', 'gin');
5+
6+
return response;
7+
};
8+
9+
export default getClassAnnounced;

src/api/classBoard/getClassBoard.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
import req from '../apiUtils';
2+
3+
const getClassBoardList = async (postId: number) => {
4+
const response = await req(`/cb/${postId}`, 'get', 'gin');
5+
6+
return response.data;
7+
};
8+
9+
export default getClassBoardList;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
import req from '../apiUtils';
2+
3+
const getClassBoardList = async (
4+
classCode: number,
5+
pageNum: number,
6+
pageSize: number
7+
) => {
8+
const totalResponse = await req(
9+
`/cb?cid=${classCode}&pageSize=99`,
10+
'get',
11+
'gin'
12+
);
13+
14+
const totalPosts = totalResponse.data.length;
15+
16+
const response = await req(
17+
`/cb?cid=${classCode}&page=${pageNum}&pageSize=${pageSize}`,
18+
'get',
19+
'gin'
20+
);
21+
22+
return {
23+
data: response.data,
24+
total: totalPosts,
25+
};
26+
};
27+
28+
export default getClassBoardList;

src/api/classBoard/index.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import getClassAnnounced from './getClassAnnounced';
2+
import getClassBoard from './getClassBoard';
3+
import getClassBoardList from './getClassBoardList';
4+
import postCreateClassPost from './postCreateClassPost';
5+
import deleteClassBoard from './deleteClassBoard';
6+
7+
const classBoardAPI = {
8+
getClassAnnounced,
9+
getClassBoard,
10+
getClassBoardList,
11+
postCreateClassPost,
12+
deleteClassBoard,
13+
};
14+
15+
export default classBoardAPI;
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
import req from '../apiUtils';
2+
import {PostBoardData} from '@/src/interfaces/api/_class';
3+
4+
const postCreateClassPost = async (postData: PostBoardData) => {
5+
const formData = new FormData();
6+
formData.append('title', postData.title);
7+
formData.append('content', postData.content);
8+
formData.append('cid', postData.cid.toString());
9+
formData.append('uid', postData.uid.toString());
10+
formData.append('is_announced', postData.is_announced.toString());
11+
12+
if (postData.image) {
13+
formData.append('image', postData.image);
14+
}
15+
16+
const response = await req('/cb', 'post', 'gin', formData);
17+
18+
return response;
19+
};
20+
21+
export default postCreateClassPost;
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import req from '../apiUtils';
2+
3+
const getCheckClassSecret = async (classCode: string) => {
4+
const response = await req(
5+
`/cc/checkSecretExists?code=${classCode}`,
6+
'get',
7+
'gin'
8+
);
9+
10+
return response.data;
11+
};
12+
13+
export default getCheckClassSecret;

src/app/[className]/components/card/PostCard.tsx

Lines changed: 56 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,54 @@
1+
import {useState} from 'react';
12
import Image from 'next/image';
3+
import {ClassEditPost} from '../modal';
4+
import {Dropdown} from '@/src/app/components/_class/dropdown';
25
import {PostCardProps} from '@/src/interfaces/_class';
36
import icons from '@/public/svgs/_class';
47

5-
const PostCard = ({ImageSrc, PostName, managerRole}: PostCardProps) => {
8+
const PostCard = ({
9+
imageSrc,
10+
postName,
11+
managerRole,
12+
zIndex,
13+
postId,
14+
deletePost,
15+
}: PostCardProps & {zIndex: number} & {
16+
deletePost: (postId: number) => void;
17+
}) => {
18+
const dropdownItems = [
19+
{
20+
modalId: 'postEdit',
21+
icon: icons.edit,
22+
alt: 'Edit Icon',
23+
text: 'Edit',
24+
},
25+
{
26+
modalId: 'postDelete',
27+
icon: icons.delete,
28+
alt: 'Delete Icon',
29+
text: 'Delete',
30+
},
31+
];
32+
const [isModalOpen, setIsModalOpen] = useState(false);
33+
const [selectedModalId, setSelectedModalId] = useState<string | null>(null);
34+
35+
const setActiveModalId = (modalId: string) => {
36+
setSelectedModalId(modalId);
37+
setIsModalOpen(true);
38+
if (modalId === 'postDelete') {
39+
deletePost(postId);
40+
}
41+
};
42+
643
return (
7-
<div className="border rounded-lg min-w-52 max-w-72 h-44 border-gray-300 mr-10 mb-10 hover:shadow-md active:bg-neutral-50 focus:ring focus:ring-gray-400 transform hover:scale-105 transition duration-200 ease-in-out">
44+
<div
45+
className="border rounded-lg h-44 border-gray-300 mr-2 mb-6 hover:shadow-md active:bg-neutral-50 focus:ring focus:ring-gray-400 transform hover:scale-105 transition duration-200 ease-in-out"
46+
style={{zIndex}}
47+
>
848
<div className="flex justify-center items-center mt-2">
9-
<div className="mt-2 w-72 h-24 relative overflow-hidden">
49+
<div className="mt-2 w-80 h-24 relative overflow-hidden">
1050
<Image
11-
src={ImageSrc}
51+
src={imageSrc || icons.noneImage}
1252
alt={'Thumbnail'}
1353
fill={true}
1454
sizes="(max-width: 200px), (max-hight:78px)"
@@ -25,17 +65,22 @@ const PostCard = ({ImageSrc, PostName, managerRole}: PostCardProps) => {
2565
className="me-2 w-auto h-auto max-w-5 max-h-5"
2666
/>
2767
<div className="flex w-full justify-between">
28-
<h3 className="font-bold text-lg">{PostName}</h3>
68+
<h3 className="font-bold text-lg">{postName}</h3>
2969
{managerRole && (
30-
<Image
31-
src={icons.moreVert}
32-
alt={'moreVert'}
33-
width={30}
34-
height={30}
35-
/>
70+
<>
71+
<div style={{width: 30, height: 30}}>
72+
<Dropdown
73+
dropdownImageSrc={icons.moreVert}
74+
items={dropdownItems}
75+
setActiveModalId={setActiveModalId}
76+
zIndex={zIndex}
77+
/>
78+
</div>
79+
</>
3680
)}
3781
</div>
3882
</div>
83+
{isModalOpen && selectedModalId === 'postEdit' && <ClassEditPost />}
3984
</div>
4085
);
4186
};

src/app/[className]/components/main/Notice.tsx

Lines changed: 103 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,113 @@
1+
'use client';
2+
3+
import {useEffect, useState} from 'react';
14
import Image from 'next/image';
2-
import icons from '@/public/svgs/_class';
5+
import {ClassEditPost} from '../modal';
6+
import {Dropdown} from '@/src/app/components/_class/dropdown';
7+
import getClassAnnounced from '@/src/api/classBoard/getClassAnnounced';
8+
import DeleteClassBoard from '@/src/api/classBoard/deleteClassBoard';
9+
import User from '@/src/model/User';
310
import {RoleProps} from '@/src/interfaces/_class';
11+
import icons from '@/public/svgs/_class';
12+
13+
const Notice = ({
14+
managerRole,
15+
classId,
16+
isOpen,
17+
}: RoleProps & {isOpen: boolean}) => {
18+
const dropdownItems = [
19+
{
20+
modalId: 'noticeEdit',
21+
icon: icons.edit,
22+
alt: 'Edit Icon',
23+
text: 'Edit',
24+
},
25+
{
26+
modalId: 'noticeDelete',
27+
icon: icons.delete,
28+
alt: 'Delete Icon',
29+
text: 'Delete',
30+
},
31+
];
32+
const [classAnnounced, setClassAnnounced] = useState<
33+
{ID: number; Title: string; Content: string}[]
34+
>([]);
35+
const [isModalOpen, setIsModalOpen] = useState(false);
36+
const [selectedModalId, setSelectedModalId] = useState<string | null>(null);
37+
38+
const deleteNotice = async (postId: number) => {
39+
if (classId !== undefined) {
40+
try {
41+
if (confirm('Are you sure you want to delete this notice?')) {
42+
await DeleteClassBoard(postId, classId, User.uid);
43+
alert('Notice deleted successfully!');
44+
}
45+
const notices = await getClassAnnounced(classId);
46+
if (Array.isArray(notices.data)) {
47+
setClassAnnounced(notices.data);
48+
} else {
49+
console.error('getClassAnnounced did not return an array');
50+
}
51+
} catch (error) {
52+
console.error(error);
53+
alert('Failed to delete notice');
54+
}
55+
} else {
56+
alert('Failed to delete notice');
57+
}
58+
};
59+
60+
const setActiveModalId = (modalId: string) => {
61+
setSelectedModalId(modalId);
62+
setIsModalOpen(true);
63+
if (modalId === 'noticeDelete') {
64+
deleteNotice(classAnnounced[0].ID);
65+
}
66+
};
67+
68+
useEffect(() => {
69+
if (classId !== undefined && isOpen) {
70+
getClassAnnounced(classId).then(schedules =>
71+
setClassAnnounced(schedules.data)
72+
);
73+
}
74+
}, [classId, isOpen]);
475

5-
const Notice = ({managerRole}: RoleProps) => {
676
return (
777
<div className="flex items-center border rounded-lg mt-2 ps-2 h-14">
8-
<Image
9-
className="mx-2"
10-
src={icons.notice}
11-
alt={'noticeIcon'}
12-
width={24}
13-
height={24}
14-
/>
15-
<div className="flex w-full justify-between">
16-
<p className="font-semibold text-lg">Professor : Hi</p>
17-
{managerRole && (
78+
{classAnnounced.length > 0 ? (
79+
<>
1880
<Image
19-
className="me-5"
20-
src={icons.moreVert}
21-
alt={'moreVert'}
22-
width={30}
23-
height={30}
81+
className="mx-2"
82+
src={icons.notice}
83+
alt={'noticeIcon'}
84+
width={24}
85+
height={24}
2486
/>
25-
)}
26-
</div>
87+
<div className="flex w-full justify-between">
88+
<p className="font-semibold text-lg">
89+
{classAnnounced[0].Title} : {classAnnounced[0].Content}
90+
</p>
91+
{managerRole && (
92+
<>
93+
<div style={{width: 30, height: 30}}>
94+
<Dropdown
95+
dropdownImageSrc={icons.moreVert}
96+
items={dropdownItems}
97+
setActiveModalId={setActiveModalId}
98+
zIndex={10}
99+
/>
100+
</div>
101+
</>
102+
)}
103+
</div>
104+
{isModalOpen && selectedModalId === 'noticeEdit' && <ClassEditPost />}
105+
</>
106+
) : (
107+
<p className="ms-2 text-xl font-bold">
108+
No announcement currently available...
109+
</p>
110+
)}
27111
</div>
28112
);
29113
};

0 commit comments

Comments
 (0)