Skip to content

Commit 2ce9bb2

Browse files
authored
Merge pull request #57 from KWcapstone/56-feat-파일-다운로드
56 feat 파일 다운로드
2 parents 32c8be8 + 7848384 commit 2ce9bb2

File tree

12 files changed

+287
-75
lines changed

12 files changed

+287
-75
lines changed

src/types/conferanceData.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ export interface conferenceData {
22
projectId: string;
33
projectName: string;
44
imageUrl: string;
5-
updateAt: string;
5+
updatedAt: string;
66
scriptions?: scriptionsData[];
77
summary?: summarysWithTitleData;
88
}
Lines changed: 134 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,161 @@
1+
// style
12
import "@/views/main/style/dwn-modal.sass";
3+
4+
// components
25
import Modal from "@/views/components/modal";
36

7+
// libraries
8+
import { useEffect, useMemo, useState } from "react";
9+
10+
// apis
11+
import { getFileDwn } from "@/api/main/fileDwn";
12+
import { downloadAs } from "@/utils/download";
13+
14+
type FileKind = "MINDMAP" | "RECORDING" | "SUMMARY";
15+
type FileFormat = "jpg" | "png" | "zip" | "txt";
16+
417
interface DwnModalProps {
518
onCloseModal: () => void;
19+
projectId: string;
20+
projectName: string;
621
}
722

8-
const DwnModal = ({
23+
const KIND_FORMAT_OPTIONS: Record<
24+
FileKind,
25+
{ value: FileFormat; label: string }[]
26+
> = {
27+
MINDMAP: [
28+
{ value: "jpg", label: "JPG 이미지(.jpg)" },
29+
{ value: "png", label: "PNG 이미지(.png)" },
30+
],
31+
RECORDING: [{ value: "zip", label: "ZIP(.zip)" }],
32+
SUMMARY: [{ value: "txt", label: "텍스트(.txt)" }],
33+
};
34+
35+
export default function DwnModal({
936
onCloseModal,
10-
}: DwnModalProps) => {
37+
projectId,
38+
projectName,
39+
}: DwnModalProps) {
40+
const [kind, setKind] = useState<FileKind | "">("");
41+
const [format, setFormat] = useState<FileFormat>("jpg");
42+
43+
const canSubmit = !!kind;
44+
45+
// 현재 kind에 따른 형식 옵션
46+
const formatOptions = useMemo(() => {
47+
return kind ? KIND_FORMAT_OPTIONS[kind as FileKind] : [];
48+
}, [kind]);
49+
50+
// kind 바뀌면 해당 kind의 첫 옵션으로 format 동기화
51+
useEffect(() => {
52+
if (!kind) return;
53+
const first = KIND_FORMAT_OPTIONS[kind as FileKind][0]?.value;
54+
if (first && format !== first) setFormat(first);
55+
}, [kind]); // eslint-disable-line react-hooks/exhaustive-deps
56+
57+
const handleSubmit = async (e: React.FormEvent) => {
58+
e.preventDefault();
59+
if (!canSubmit) return;
60+
61+
await getFileDwn(projectId, kind).then((res) => {
62+
const url = res.data.data.projectUrl;
63+
downloadAs(url, `${projectName}.${format}`);
64+
});
65+
};
66+
1167
return (
1268
<Modal onCloseModal={onCloseModal}>
1369
<p className="modal-title">다운로드</p>
14-
<div className="modal-wrap">
70+
71+
<form className="modal-wrap" onSubmit={handleSubmit}>
72+
{/* 파일 종류 선택 */}
1573
<div className="file-wrap">
1674
<h3>파일 선택</h3>
1775
<div className="radio-container">
18-
<div className="radio-wrap mind-map">
19-
<div className="img"></div>
20-
<input type="radio" name="file" id="mind-map" value="1"/>
76+
<label
77+
className={`radio-wrap mind-map ${
78+
kind === "MINDMAP" ? "active" : ""
79+
}`}
80+
htmlFor="mind-map"
81+
>
82+
<div className="img" />
83+
<input
84+
type="radio"
85+
name="file"
86+
id="mind-map"
87+
value="MINDMAP"
88+
checked={kind === "MINDMAP"}
89+
onChange={() => setKind("MINDMAP")}
90+
required
91+
/>
2192
<div className="radio-title">마인드맵</div>
22-
</div>
23-
<div className="radio-wrap record">
24-
<div className="img"></div>
25-
<input type="radio" name="file" id="record" value="2"/>
93+
</label>
94+
95+
<label
96+
className={`radio-wrap record ${
97+
kind === "RECORDING" ? "active" : ""
98+
}`}
99+
htmlFor="record"
100+
>
101+
<div className="img" />
102+
<input
103+
type="radio"
104+
name="file"
105+
id="record"
106+
value="RECORDING"
107+
checked={kind === "RECORDING"}
108+
onChange={() => setKind("RECORDING")}
109+
required
110+
/>
26111
<div className="radio-title">음성・스크립트</div>
27-
</div>
28-
<div className="radio-wrap summary">
29-
<div className="img"></div>
30-
<input type="radio" name="file" id="summary" value="3"/>
112+
</label>
113+
114+
<label
115+
className={`radio-wrap summary ${
116+
kind === "SUMMARY" ? "active" : ""
117+
}`}
118+
htmlFor="summary"
119+
>
120+
<div className="img" />
121+
<input
122+
type="radio"
123+
name="file"
124+
id="summary"
125+
value="SUMMARY"
126+
checked={kind === "SUMMARY"}
127+
onChange={() => setKind("SUMMARY")}
128+
required
129+
/>
31130
<div className="radio-title">요약본</div>
32-
</div>
131+
</label>
33132
</div>
34133
</div>
134+
135+
{/* 형식 선택 */}
35136
<div className="type-wrap">
36137
<h3>형식 선택</h3>
37138
<div className="select-wrap">
38-
<select>
39-
<option>JPG 이미지(.jpg)</option>
40-
<option>PNG 이미지(.png)</option>
139+
<select
140+
aria-label="파일 형식"
141+
value={format}
142+
onChange={(e) => setFormat(e.target.value as FileFormat)}
143+
disabled={!kind} // 종류 선택 전엔 비활성화
144+
>
145+
{!kind && <option value="">다운로드 파일을 선택하세요</option>}
146+
{formatOptions.map((opt) => (
147+
<option key={opt.value} value={opt.value}>
148+
{opt.label}
149+
</option>
150+
))}
41151
</select>
42152
</div>
43153
</div>
44-
</div>
154+
155+
<button type="submit" className="btn-primary" disabled={!canSubmit}>
156+
다운로드
157+
</button>
158+
</form>
45159
</Modal>
46160
);
47-
};
48-
49-
export default DwnModal;
161+
}

src/views/main/components/SideBar.tsx

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,6 @@ import PasswordChangeModal from "@/views/main/components/ChangePWModal";
2222
import News from "@/views/main/components/News";
2323

2424
const SideBar = ({ haveUnreadNews, setHaveUnreadNews }: sideBarPropsOfNews) => {
25-
console.log("SideBar Rendered: ", haveUnreadNews);
26-
2725
const [sort, setSort] = useState<string>("/");
2826
const navigate = useNavigate();
2927
const { pathname } = useLocation();
@@ -56,8 +54,6 @@ const SideBar = ({ haveUnreadNews, setHaveUnreadNews }: sideBarPropsOfNews) => {
5654
} else {
5755
setHaveUnreadNews(false);
5856
}
59-
60-
console.log("Unread News Num:", res.data.data.num);
6157
});
6258
}, [newsUnreadResponse]);
6359

@@ -92,8 +88,6 @@ const SideBar = ({ haveUnreadNews, setHaveUnreadNews }: sideBarPropsOfNews) => {
9288
b.noticeId.timestamp - a.noticeId.timestamp
9389
);
9490

95-
console.log("All News:", allNews);
96-
console.log("Unread News:", unreadNews);
9791
let message = "";
9892
if (allNews.length == 0) {
9993
message = "소식이 없습니다.";

src/views/main/components/UserModal.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,6 @@ const UserModal = ({ onCloseModal, onOpenChangePW }: UserModalProps) => {
8686
name: newName,
8787
imageUrl: profile?.imageUrl ?? "",
8888
} as profileData).then(() => {
89-
// console.log("프로필 변경 성공");
9089
setProfile((prev) => prev && { ...prev, name: newName });
9190
window.location.reload();
9291
});

src/views/main/page/ProjectPage.tsx

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ const ProjectPage = () => {
4040
type ModalType = "dwn" | "share" | null;
4141
const [modalType, setModalType] = useState<ModalType>(null);
4242
const [projectId, setProjectId] = useState<string>("");
43+
const [projectName, setProjectName] = useState<string>("");
4344
const closeModal = () => setModalType(null);
4445

4546
// function
@@ -96,8 +97,6 @@ const ProjectPage = () => {
9697
} else {
9798
setIsHaveUnreadNews(false);
9899
}
99-
100-
console.log("Unread News Num:", res.data.data.num);
101100
});
102101
}, []);
103102

@@ -246,6 +245,7 @@ const ProjectPage = () => {
246245
setModalType("dwn");
247246
setOpenMenuId(null);
248247
setProjectId(list.projectId);
248+
setProjectName(list.projectName);
249249
}}
250250
>
251251
다운로드하기
@@ -258,6 +258,7 @@ const ProjectPage = () => {
258258
setModalType("share");
259259
setOpenMenuId(null);
260260
setProjectId(list.projectId);
261+
setProjectName(list.projectName);
261262
}}
262263
>
263264
공유하기
@@ -270,6 +271,7 @@ const ProjectPage = () => {
270271
clickProjectDelete(list.projectId);
271272
setOpenMenuId(null);
272273
setProjectId(list.projectId);
274+
setProjectName(list.projectName);
273275
}}
274276
>
275277
삭제하기
@@ -332,7 +334,11 @@ const ProjectPage = () => {
332334
}
333335
}}
334336
>
335-
<DwnModal onCloseModal={closeModal} />
337+
<DwnModal
338+
onCloseModal={closeModal}
339+
projectId={projectId}
340+
projectName={projectName}
341+
/>
336342
</div>
337343
)}
338344
{modalType === "share" && (

src/views/main/page/ProjrctViewPage.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export default function ProjectViewPage() {
2020
projectId: "",
2121
projectName: "",
2222
imageUrl: "",
23-
updateAt: "",
23+
updatedAt: "",
2424
scriptions: [],
2525
summary: { title: "", content: "" },
2626
});

src/views/main/page/RecordPage.tsx

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import arrowDown from "@/assets/imgs/icon/arrow_down_black.svg";
77
import { getSearch } from "@/api/common/common";
88
import { getRecord } from "@/api/main/record";
99
import { getNewsNum } from "@/api/main/news";
10+
import { getFileDwn } from "@/api/main/fileDwn";
1011

1112
// component
1213
import SideBar from "@/views/main/components/SideBar";
@@ -17,6 +18,7 @@ import { Link } from "react-router-dom";
1718

1819
// type
1920
import { recordData } from "@/types/recordData";
21+
import { downloadAs } from "@/utils/download";
2022

2123
const RecordPage = () => {
2224
// value
@@ -104,7 +106,6 @@ const RecordPage = () => {
104106
}));
105107
setRecord(dataWithSelection);
106108
});
107-
// console.log(record)
108109
};
109110

110111
useEffect(() => {
@@ -137,8 +138,6 @@ const RecordPage = () => {
137138
} else {
138139
setIsHaveUnreadNews(false);
139140
}
140-
141-
console.log("Unread News Num:", res.data.data.num);
142141
});
143142
}, []);
144143

@@ -237,15 +236,34 @@ const RecordPage = () => {
237236
<th>
238237
<div className="craft-wrap">
239238
<div>{checkCount}개 선택됨</div>
240-
<button className="dwn">다운로드 하기</button>
239+
<button
240+
className="dwn"
241+
onClick={async () => {
242+
await getFileDwn(
243+
record
244+
.filter((row) => row.selected)
245+
.map((row) => row.recordId)
246+
.join(","),
247+
"RECORDING"
248+
).then((res) => {
249+
console.log(res);
250+
downloadAs(
251+
res.data.data.projectUrl,
252+
record.filter((row) => row.selected)[0].name
253+
);
254+
});
255+
}}
256+
>
257+
다운로드 하기
258+
</button>
241259
<button className="del">삭제하기</button>
242260
<button
243261
className="cancel"
244-
onClick={() =>
262+
onClick={async () => {
245263
setRecord(
246264
record.map((row) => ({ ...row, selected: false }))
247-
)
248-
}
265+
);
266+
}}
249267
>
250268
취소
251269
</button>

src/views/main/page/SummaryPage.tsx

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,6 @@ const SummaryPage = () => {
9797
}));
9898
setSummary(dataWithSelection);
9999
});
100-
// console.log(summary)
101100
};
102101

103102
useEffect(() => {
@@ -130,8 +129,6 @@ const SummaryPage = () => {
130129
} else {
131130
setIsHaveUnreadNews(false);
132131
}
133-
134-
console.log("Unread News Num:", res.data.data.num);
135132
});
136133
}, []);
137134

0 commit comments

Comments
 (0)