Skip to content

Commit 8cfdfbc

Browse files
Feature/upload pdf in whitebook (#90)
Co-authored-by: Seokyun Ha <[email protected]>
1 parent f6e1f2e commit 8cfdfbc

File tree

4 files changed

+240
-53
lines changed

4 files changed

+240
-53
lines changed

components/board/whitebook/whitebook.create.modal.jsx

+104-24
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,53 @@
1-
import { Form, Modal } from 'semantic-ui-react';
1+
import { Form, Modal, Radio } from 'semantic-ui-react';
22
import { useState } from 'react';
33
import { PoPoAxios } from '@/utils/axios.instance';
44

55
const WhitebookCreateModal = (props) => {
66
const [open, setOpen] = useState(false);
77
const [title, setTitle] = useState('');
8-
const [link, setLink] = useState('');
98
const [content, setContent] = useState('');
109
const [showOnlyLogin, setShowOnlyLogin] = useState(false);
1110

11+
const [inputType, setInputType] = useState('link');
12+
const [link, setLink] = useState('');
13+
const [pdfFile, setPdfFile] = useState(null);
14+
15+
const handleFileChange = (file) => {
16+
if (!file || file.type !== 'application/pdf') {
17+
alert('PDF 파일만 업로드 가능합니다.');
18+
setPdfFile(null);
19+
return;
20+
}
21+
setPdfFile(file);
22+
};
23+
1224
const handleSubmit = async () => {
13-
try {
14-
await PoPoAxios.post(
15-
'/whitebook',
16-
{
17-
title: title,
18-
link: link,
19-
content: content,
20-
show_only_login: showOnlyLogin,
21-
},
22-
{ withCredentials: true },
23-
);
24-
setOpen(false);
25-
window.location.reload();
26-
} catch (e) {
27-
alert('생활백서 생성에 실패했습니다.');
28-
console.log(e);
25+
const formData = new FormData();
26+
formData.append('title', title);
27+
formData.append('content', content);
28+
formData.append('show_only_login', showOnlyLogin);
29+
30+
if (inputType === 'link' && link) {
31+
formData.append('link', link);
32+
} else if (inputType === 'pdf' && pdfFile) {
33+
formData.append('pdf_file', pdfFile);
34+
} else {
35+
alert('링크 또는 PDF 파일을 입력해주세요.');
36+
return;
2937
}
38+
39+
await PoPoAxios.post(`/whitebook`, formData, {
40+
withCredentials: true,
41+
headers: { 'Content-Type': 'multipart/form-data' },
42+
})
43+
.then(() => {
44+
alert('생활백서를 생성 했습니다.');
45+
window.location.reload();
46+
})
47+
.catch((err) => {
48+
alert('생활백서 생성에 실패했습니다.');
49+
console.error(err);
50+
});
3051
};
3152

3253
return (
@@ -45,12 +66,71 @@ const WhitebookCreateModal = (props) => {
4566
label={'생활백서 제목'}
4667
onChange={(e) => setTitle(e.target.value)}
4768
/>
48-
<Form.Input
49-
required
50-
label={'생활백서 링크'}
51-
placeholder={'https://xxxx.postech.ac.kr'}
52-
onChange={(e) => setLink(e.target.value)}
53-
/>
69+
70+
{/* PDF 또는 링크 선택 */}
71+
<Form.Group inline>
72+
<label>파일 타입 선택:</label>
73+
<Form.Field>
74+
<Radio
75+
label="링크 입력"
76+
name="inputType"
77+
value="link"
78+
checked={inputType === 'link'}
79+
onChange={() => {
80+
setInputType('link');
81+
}}
82+
/>
83+
</Form.Field>
84+
<Form.Field>
85+
<Radio
86+
label="PDF 업로드"
87+
name="inputType"
88+
value="pdf"
89+
checked={inputType === 'pdf'}
90+
onChange={() => {
91+
setInputType('pdf');
92+
}}
93+
/>
94+
</Form.Field>
95+
</Form.Group>
96+
97+
{/* 조건부 렌더링: 링크 입력 필드 */}
98+
{inputType === 'link' && (
99+
<Form.Input
100+
required
101+
label={'생활백서 링크'}
102+
placeholder={'https://xxxx.postech.ac.kr'}
103+
onChange={(e) => setLink(e.target.value)}
104+
/>
105+
)}
106+
107+
{/* 조건부 렌더링: PDF 업로드 필드 */}
108+
{inputType === 'pdf' && (
109+
<Form.Field>
110+
<Form.Input
111+
required
112+
label={'생활백서 PDF'}
113+
type="file"
114+
accept="application/pdf"
115+
onChange={(e) => handleFileChange(e.target.files[0])}
116+
/>
117+
<label>
118+
{pdfFile && (
119+
<a
120+
href={URL.createObjectURL(pdfFile)}
121+
target="_blank"
122+
rel="noopener noreferrer"
123+
style={{
124+
textDecoration: 'underline',
125+
}}
126+
>
127+
{pdfFile.name}
128+
</a>
129+
)}
130+
</label>
131+
</Form.Field>
132+
)}
133+
54134
<Form.TextArea
55135
required
56136
label={'생활백서 설명글'}

components/board/whitebook/whitebook.table.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const WhitebookTable = ({ whitebooks }) => {
77
<Table celled selectable textAlign={'center'}>
88
<Table.Header>
99
<Table.Row>
10-
<Table.HeaderCell>idx.</Table.HeaderCell>
10+
<Table.HeaderCell>번호</Table.HeaderCell>
1111
<Table.HeaderCell>제목</Table.HeaderCell>
1212
<Table.HeaderCell>내용</Table.HeaderCell>
1313
<Table.HeaderCell>생성일</Table.HeaderCell>

components/board/whitebook/whitebook.update.modal.jsx

+131-27
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Button, Form, Icon, Modal } from 'semantic-ui-react';
1+
import { Button, Form, Icon, Modal, Radio } from 'semantic-ui-react';
22
import { useState } from 'react';
33
import DeleteConfirmModal from '../../common/delete.confirm.modal';
44
import { PoPoAxios } from '@/utils/axios.instance';
@@ -8,28 +8,70 @@ const WhitebookUpdateModal = ({ trigger, whitebook }) => {
88
const [deleteModalOpen, setDeleteModalOpen] = useState(false);
99

1010
const [title, setTitle] = useState(whitebook.title);
11-
const [link, setLink] = useState(whitebook.link);
1211
const [content, setContent] = useState(whitebook.content);
1312
const [showOnlyLogin, setShowOnlyLogin] = useState(whitebook.show_only_login);
13+
const [inputType, setInputType] = useState(whitebook.link ? 'link' : 'pdf');
14+
const [link, setLink] = useState(whitebook.link || '');
15+
const [uploadedPDFLink, setUploadedPDFLink] = useState(null);
16+
const [pdfFile, setPdfFile] = useState(null);
17+
18+
const handleModalOpen = () => {
19+
setOpen(true);
20+
// 링크에 whitebook이 들어간다면 S3에 있는 pdf 파일임
21+
if (whitebook.link && whitebook.link.includes('whitebook')) {
22+
const link = whitebook.link;
23+
setLink('');
24+
setUploadedPDFLink(link);
25+
setInputType('pdf');
26+
}
27+
};
28+
29+
const handleFileChange = (file) => {
30+
if (!file || file.type !== 'application/pdf') {
31+
alert('PDF 파일만 업로드 가능합니다.');
32+
setPdfFile(null);
33+
return;
34+
}
35+
setPdfFile(file);
36+
};
1437

1538
const handleSubmit = async () => {
16-
try {
17-
await PoPoAxios.put(
18-
`/whitebook/${whitebook.uuid}`,
19-
{
20-
title: title,
21-
link: link,
22-
content: content,
23-
show_only_login: showOnlyLogin,
24-
},
25-
{ withCredentials: true },
26-
);
27-
setOpen(false);
28-
window.location.reload();
29-
} catch (e) {
30-
alert('생활백서 수정에 실패했습니다.');
31-
console.log(e);
39+
const formData = new FormData();
40+
formData.append('title', title);
41+
formData.append('content', content);
42+
formData.append('show_only_login', showOnlyLogin);
43+
44+
if (inputType === 'link' && link) {
45+
formData.append('link', link);
46+
} else if (inputType === 'pdf' && pdfFile) {
47+
formData.append('pdf_file', pdfFile);
48+
} else {
49+
// title, content, show_only_login 중 하나라도 변경되었다면 수정 가능
50+
if (
51+
title !== whitebook.title ||
52+
content !== whitebook.cotent ||
53+
showOnlyLogin !== whitebook.show_only_login
54+
) {
55+
const link = inputType === 'link' ? link : uploadedPDFLink;
56+
formData.append('link', link);
57+
} else {
58+
alert('링크 또는 PDF 파일을 입력해주세요.');
59+
return;
60+
}
3261
}
62+
63+
await PoPoAxios.put(`/whitebook/${whitebook.uuid}`, formData, {
64+
withCredentials: true,
65+
headers: { 'Content-Type': 'multipart/form-data' },
66+
})
67+
.then(() => {
68+
alert('생활백서 정보를 수정 했습니다.');
69+
window.location.reload();
70+
})
71+
.catch((err) => {
72+
alert('생활백서 수정에 실패했습니다.');
73+
console.error(err);
74+
});
3375
};
3476

3577
return (
@@ -38,7 +80,7 @@ const WhitebookUpdateModal = ({ trigger, whitebook }) => {
3880
open={open}
3981
trigger={trigger}
4082
onClose={() => setOpen(false)}
41-
onOpen={() => setOpen(true)}
83+
onOpen={handleModalOpen}
4284
>
4385
<Modal.Header>생활백서 수정</Modal.Header>
4486
<Modal.Content>
@@ -49,19 +91,81 @@ const WhitebookUpdateModal = ({ trigger, whitebook }) => {
4991
value={title}
5092
onChange={(e) => setTitle(e.target.value)}
5193
/>
52-
<Form.Input
53-
required
54-
label={'생활백서 링크'}
55-
value={link}
56-
placeholder={'https://xxxx.postech.ac.kr'}
57-
onChange={(e) => setLink(e.target.value)}
58-
/>
94+
95+
{/* PDF 또는 링크 선택 */}
96+
<Form.Group inline>
97+
<label>파일 타입 선택:</label>
98+
<Form.Field>
99+
<Radio
100+
label="링크 입력"
101+
name="inputType"
102+
value="link"
103+
checked={inputType === 'link'}
104+
onChange={() => {
105+
setInputType('link');
106+
}}
107+
/>
108+
</Form.Field>
109+
<Form.Field>
110+
<Radio
111+
label="PDF 업로드"
112+
name="inputType"
113+
value="pdf"
114+
checked={inputType === 'pdf'}
115+
onChange={() => {
116+
setInputType('pdf');
117+
}}
118+
/>
119+
</Form.Field>
120+
</Form.Group>
121+
122+
{/* 조건부 렌더링: 링크 입력 필드 */}
123+
{inputType === 'link' && (
124+
<Form.Input
125+
required
126+
label={'생활백서 링크'}
127+
placeholder={'https://xxxx.postech.ac.kr'}
128+
value={link}
129+
onChange={(e) => setLink(e.target.value)}
130+
/>
131+
)}
132+
133+
{/* 조건부 렌더링: PDF 업로드 필드 */}
134+
{inputType === 'pdf' && (
135+
<Form.Field>
136+
<Form.Input
137+
required
138+
label={'생활백서 PDF'}
139+
type="file"
140+
accept="application/pdf"
141+
onChange={(e) => handleFileChange(e.target.files[0])}
142+
/>
143+
<label>
144+
{(pdfFile || uploadedPDFLink) && (
145+
<a
146+
href={
147+
pdfFile ? URL.createObjectURL(pdfFile) : uploadedPDFLink
148+
}
149+
target="_blank"
150+
rel="noopener noreferrer"
151+
style={{
152+
textDecoration: 'underline',
153+
}}
154+
>
155+
{pdfFile ? pdfFile.name : '기존 PDF 확인'}
156+
</a>
157+
)}
158+
</label>
159+
</Form.Field>
160+
)}
161+
59162
<Form.TextArea
60163
required
61164
label={'생활백서 설명글'}
62165
value={content}
63166
onChange={(e) => setContent(e.target.value)}
64167
/>
168+
65169
<Form.Checkbox
66170
required
67171
label={'로그인 유저에게만 보이기'}
@@ -76,7 +180,7 @@ const WhitebookUpdateModal = ({ trigger, whitebook }) => {
76180
</Form.Button>
77181
<DeleteConfirmModal
78182
open={deleteModalOpen}
79-
target={name}
183+
target={whitebook.title}
80184
deleteURI={`whitebook/${whitebook.uuid}`}
81185
trigger={
82186
<Button negative onClick={() => setDeleteModalOpen(true)}>

pages/board/whitebook.jsx

+4-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,10 @@ const WhitebookPage = () => {
2626
<div style={{ marginBottom: '1rem' }}>
2727
<WhitebookCreateModal trigger={<Button>생활백서 생성</Button>} />
2828
</div>
29-
<p>생활백서는 조회순으로 정렬되어 표시됩니다!</p>
29+
<p>
30+
생활백서는 조회순으로 정렬되어 표시됩니다. 박스 안을 클릭하면 수정할 수
31+
있습니다.
32+
</p>
3033
<div>
3134
<WhitebookTable whitebooks={whitebooks} />
3235
</div>

0 commit comments

Comments
 (0)