From 6b768637fe039f0b90e55b22b794899f2cb694d0 Mon Sep 17 00:00:00 2001 From: CJY Date: Mon, 27 Jan 2025 00:26:26 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EC=A0=9D?= =?UTF-8?q?=ED=8A=B8=20=EC=97=85=EB=A1=9C=EB=93=9C=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/apis/admin.ts | 17 +++ src/apis/gallery.ts | 10 +- src/apis/project.ts | 5 +- src/components/admin/PostAdmin.tsx | 144 ++++++++++++++++++ src/components/archiving/Archiving.tsx | 3 +- src/components/gallery/GalleryListSection.tsx | 12 +- src/hooks/useInput.ts | 25 ++- src/pages/admin/index.tsx | 34 +++++ src/pages/login/index.tsx | 3 +- src/types/request.d.ts | 26 ++++ 10 files changed, 249 insertions(+), 30 deletions(-) create mode 100644 src/apis/admin.ts create mode 100644 src/components/admin/PostAdmin.tsx create mode 100644 src/pages/admin/index.tsx diff --git a/src/apis/admin.ts b/src/apis/admin.ts new file mode 100644 index 00000000..e607870a --- /dev/null +++ b/src/apis/admin.ts @@ -0,0 +1,17 @@ +import axios from 'axios'; +import { url } from '.'; +import { getAuthAxios } from './authAxios'; +import { IToken } from '@utils/state'; +import { IProjectDetail, UploadData } from '@@types/request'; + +// export function postGalleryData(data: IProjectDetail, token: IToken) { +// const authAxios = getAuthAxios(token); +// return authAxios.post(`/api/admin/postProject`, { +// data, +// }); +// } + +export async function postGalleryData(data: FormData) { + const result = await axios.post(`http://3.36.228.42:8000/api/project`, data); + return result; +} diff --git a/src/apis/gallery.ts b/src/apis/gallery.ts index 6967e9b8..4670751c 100644 --- a/src/apis/gallery.ts +++ b/src/apis/gallery.ts @@ -3,15 +3,13 @@ import axios from 'axios'; import { url } from '.'; export async function getGalleries() { - const data = await axios.get>>( - `${url}/api/gallery`, - ).then(res => res.data.data); + const data = await axios + .get>>(`${url}/api/gallery`) + .then((res) => res.data.data); return data; } export async function getGalleryDetail(id: string) { - const data = await axios.get>( - `${url}/api/gallery/${id}`, - ).then(res => res.data.data); + const data = await axios.get>(`${url}api/gallery/${id}`).then((res) => res.data.data); return data; } diff --git a/src/apis/project.ts b/src/apis/project.ts index d0f38ef2..276c59e3 100644 --- a/src/apis/project.ts +++ b/src/apis/project.ts @@ -10,8 +10,7 @@ export async function getProjects() { } export async function getProjectDetail(id: string) { - const data = await axios - .get>(`${url}/api/project/${id}`) - .then((res) => res.data.data); + const data = await axios.get>(`${url}/api/project/${id}`).then((res) => res.data.data); + console.log(data); return data; } diff --git a/src/components/admin/PostAdmin.tsx b/src/components/admin/PostAdmin.tsx new file mode 100644 index 00000000..65095d3e --- /dev/null +++ b/src/components/admin/PostAdmin.tsx @@ -0,0 +1,144 @@ +import { IProjectDetail, UploadData } from '@@types/request'; +import React, { useState } from 'react'; +import { useRecoilValue } from 'recoil'; +import { postGalleryData } from 'src/apis/admin'; +import { token } from '@utils/state'; +import useInput from 'src/hooks/useInput'; +import styled from 'styled-components'; + +const PostAdmin = () => { + // Input 상태 관리 + const [title, onChangeTitle] = useInput(''); + const [subTitle, onChangeSubTitle] = useInput(''); + const [devStack, onChangeDevStack] = useInput(''); + const [version, onChangeVersion] = useInput(''); + const [teamName, onChangeTeamName] = useInput(''); + const [pm, onChangePm] = useInput(''); + const [de, onChangeDe] = useInput(''); + const [fe, onChangeFe] = useInput(''); + const [be, onChangeBe] = useInput(''); + const [start, onChangeStart] = useInput(''); + const [end, onChangeEnd] = useInput(''); + const [description, onChangeDescription] = useInput(''); + const [webLink, onChangeWebLink] = useInput(''); + const [category, onChangeCategory] = useInput(''); + const [thumbnail, setThumbnail] = useState(null); + const [images, setImages] = useState([]); + const a: string = 'aaa'; + // 파일 업로드 핸들러 + const handleThumbnailChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files[0]) { + setThumbnail(e.target.files[0]); + } + }; + + const handleImagesChange = (e: React.ChangeEvent) => { + if (e.target.files) { + // e.target.files가 null이 아니면 Array.from()으로 파일을 배열로 변환하여 추가 + setImages((prevImages) => [ + ...prevImages, + ...Array.from(e.target.files as FileList), // FileList로 명시적 타입 캐스팅 + ]); + } + }; + + // 제출 핸들러 + const handleSubmit = async () => { + if (!thumbnail || images.length === 0) { + alert('썸네일과 이미지를 모두 업로드해주세요.'); + return; + } + + const formData = new FormData(); + + formData.append('title', title); + formData.append('subtitle', subTitle); + formData.append('dev_stack', devStack); + formData.append('version', version); + formData.append('team_name', teamName); + formData.append('start_date', start); + formData.append('end_date', end); + formData.append('description', description); + formData.append('category', category); + formData.append('login_email', 'adsf'); // 로그인 이메일은 임시 데이터 + formData.append( + 'team_member', + JSON.stringify({ + pm: pm.split(','), + design: de.split(','), + frontend: fe.split(','), + backend: be.split(','), + }), + ); + + formData.append( + 'link', + JSON.stringify({ + github: 'fadsf', + youtube: 'ffasd', + web: webLink, + }), + ); + + // 썸네일 파일 추가 + formData.append('thumbnail', thumbnail); + + // 이미지 배열 추가 + images.forEach((image) => { + formData.append('images', image); + }); + + try { + const result = await postGalleryData(formData); + console.log(result); + alert('업로드 성공'); + } catch (error) { + console.error(error); + alert('업로드 실패'); + } + }; + + return ( + +

프로젝트 업로드

+ + + + + + + + + + + + + + + + 프로젝트 썸네일 이미지 + + + 프로젝트 이미지 + + + +
+ ); +}; + +export default PostAdmin; + +const Wrapper = styled.div` + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 10px; +`; + +const Title = styled.div` + font-size: 2rem; + font-weight: bold; +`; diff --git a/src/components/archiving/Archiving.tsx b/src/components/archiving/Archiving.tsx index f278e8c7..94790f17 100644 --- a/src/components/archiving/Archiving.tsx +++ b/src/components/archiving/Archiving.tsx @@ -33,7 +33,8 @@ const Archiving = ({ <> {archivingData.length ? ( - {title} + {/* {title} */} + 아악 {archivingData.map((data, index) => ( ; }) => { +const GalleryListSection = ({ staticData }: { staticData: ArchivingArrayType }) => { const { data, isLoading } = useQuery>(['galleries'], getGalleries); return ( - {sortArchivingListDesc(isLoading ? staticData : (data as ArchivingArrayType))!.map(([year, value]) => ( - - ))} + {sortArchivingListDesc(isLoading ? staticData : (data as ArchivingArrayType))!.map( + ([year, value]) => ( + + ), + )} ); }; @@ -24,4 +26,4 @@ const Wrapper = styled.div` display: flex; flex-direction: column; margin: 3rem; -`; \ No newline at end of file +`; diff --git a/src/hooks/useInput.ts b/src/hooks/useInput.ts index cadce129..cc95808e 100644 --- a/src/hooks/useInput.ts +++ b/src/hooks/useInput.ts @@ -4,19 +4,18 @@ type Handler = (e: any) => void; type ReturnTypes = [T, Handler, Dispatch>]; const useInput = (initialValue: T, regExp?: RegExp): ReturnTypes => { - const [value, setValue] = useState(initialValue); - const handler: Handler = (e) => { - if (regExp) { - if (regExp.test(e.target.value)) { - setValue(e.target.value); - } - } - else { - setValue(e.target.value); - } - }; + const [value, setValue] = useState(initialValue); + const handler: Handler = (e) => { + if (regExp) { + if (regExp.test(e.target.value)) { + setValue(e.target.value); + } + } else { + setValue(e.target.value); + } + }; - return [value, handler, setValue]; + return [value, handler, setValue]; }; -export default useInput; \ No newline at end of file +export default useInput; diff --git a/src/pages/admin/index.tsx b/src/pages/admin/index.tsx new file mode 100644 index 00000000..835f5b90 --- /dev/null +++ b/src/pages/admin/index.tsx @@ -0,0 +1,34 @@ +import React from 'react'; +import PostAdmin from 'src/components/admin/PostAdmin'; +import styled from 'styled-components'; + +const Admin = () => { + return ( + + 관리자 페이지 + + + ); +}; + +export default Admin; + +const Wrapper = styled.div` + position: relative; + width: 100%; + /* height: 100vh; */ + margin-top: 30px; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + @media (max-width: 900px) { + height: 100%; + margin: 90px 0px; + } +`; + +const Title = styled.div` + font-size: 4rem; + font-weight: bold; +`; diff --git a/src/pages/login/index.tsx b/src/pages/login/index.tsx index fe58c647..42c992f4 100644 --- a/src/pages/login/index.tsx +++ b/src/pages/login/index.tsx @@ -69,7 +69,7 @@ const StContentsWrapper = styled.div` position: relative; height: 100rem; width: 100rem; - + @media (max-width: 1200px) { width: 100%; padding: 0; @@ -84,5 +84,4 @@ const StBorderWrapper = styled.div` @media (max-width: 1200px) { display: none; } - `; diff --git a/src/types/request.d.ts b/src/types/request.d.ts index 87615a03..116d3dfb 100644 --- a/src/types/request.d.ts +++ b/src/types/request.d.ts @@ -11,6 +11,32 @@ export interface IShareURL { youtube: string; } +export interface UploadData { + title: string; + subtitle: string; + dev_stack: string; + tumbnail: File | null; + version: number; + team_name: string; + team_member: { + pm: string[]; + design: string[]; + frontend: string[]; + backend: string[]; + }; + start_date: string; + end_date: string; + description: string; + link: { + github: string; + youtube: string; + web: string; + }; + category: string; + login_email: string; + images: File[]; +} + export interface IArchivingData { id: number; title: string; From 40811b92208ba6bc20a805ac5dfaf8ca41996386 Mon Sep 17 00:00:00 2001 From: CJY Date: Mon, 27 Jan 2025 21:46:45 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix:=20api=20=ED=98=B8=EC=B6=9C=20base=20UR?= =?UTF-8?q?L=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- next.config.js | 1 + src/apis/admin.ts | 2 +- src/components/archiving/Archiving.tsx | 3 +- src/components/signup/component/ToggleBox.tsx | 170 +++++++++--------- 4 files changed, 88 insertions(+), 88 deletions(-) diff --git a/next.config.js b/next.config.js index 3ac320c3..6f7f1a88 100644 --- a/next.config.js +++ b/next.config.js @@ -12,6 +12,7 @@ const nextConfig = { 'd1sgygn8l0lfd5.cloudfront.net', 'dcpshnp4boilw.cloudfront.net', 'd1e39uzon1ymuo.cloudfront.net', + 'likelion13bucket.s3.amazonaws.com', ], }, webpack(config) { diff --git a/src/apis/admin.ts b/src/apis/admin.ts index e607870a..1ec85a60 100644 --- a/src/apis/admin.ts +++ b/src/apis/admin.ts @@ -12,6 +12,6 @@ import { IProjectDetail, UploadData } from '@@types/request'; // } export async function postGalleryData(data: FormData) { - const result = await axios.post(`http://3.36.228.42:8000/api/project`, data); + const result = await axios.post(`${process.env.NEXT_PUBLIC_API_KEY}/api/project`, data); return result; } diff --git a/src/components/archiving/Archiving.tsx b/src/components/archiving/Archiving.tsx index 94790f17..f278e8c7 100644 --- a/src/components/archiving/Archiving.tsx +++ b/src/components/archiving/Archiving.tsx @@ -33,8 +33,7 @@ const Archiving = ({ <> {archivingData.length ? ( - {/* {title} */} - 아악 + {title} {archivingData.map((data, index) => ( void; + title: string; + toggle: boolean[]; + description: string; + setToggle: (a: boolean[]) => void; } const ToggleBox = ({ title, toggle, description, setToggle }: IProps) => { - const handleToggleButton = (i: number) => { - const copy = new Array(toggle.length).fill(false); - copy[i] = true; - setToggle(copy); - }; - return ( - - {title} - - - {toggle.map((t, i) => ( - - handleToggleButton(i)} isClicked={toggle[i]} > -