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 new file mode 100644 index 00000000..1ec85a60 --- /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(`${process.env.NEXT_PUBLIC_API_KEY}/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/Card.tsx b/src/components/archiving/Card.tsx index dfb1fc14..6461155f 100644 --- a/src/components/archiving/Card.tsx +++ b/src/components/archiving/Card.tsx @@ -16,13 +16,7 @@ const Card = ({ id, thumbnail, title, dev_stack, category, link, subtitle }: ICa - + {category} @@ -36,8 +30,7 @@ const Card = ({ id, thumbnail, title, dev_stack, category, link, subtitle }: ICa }; export default Card; -const CustomImage = styled(Image)` -`; +const CustomImage = styled(Image)``; const Wrapper = styled.div` border-radius: 20px; @@ -98,9 +91,8 @@ const TextWrapper = styled.div` @media (min-width: 330px) and (max-width: 444px) { width: 180px; } - `; -const Category = styled.div<{ link: string; }>` +const Category = styled.div<{ link: string }>` border-radius: 25px; border: ${(props) => (props.link === '/gallery' ? 'none' : `1px solid ${GreyScale.default}`)}; display: flex; @@ -135,5 +127,4 @@ const ProjectDesc = styled.div` @media (max-width: 1300px) { font-size: 10px; } - `; diff --git a/src/components/gallery/GalleryListSection.tsx b/src/components/gallery/GalleryListSection.tsx index 44e3df8f..49b47490 100644 --- a/src/components/gallery/GalleryListSection.tsx +++ b/src/components/gallery/GalleryListSection.tsx @@ -6,14 +6,16 @@ import { useQuery } from 'react-query'; import { getGalleries } from 'src/apis/gallery'; import styled from 'styled-components'; -const GalleryListSection = ({ staticData }: { staticData: ArchivingArrayType; }) => { +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/components/signup/component/ToggleBox.tsx b/src/components/signup/component/ToggleBox.tsx index 48d1a9ad..0eeadec0 100644 --- a/src/components/signup/component/ToggleBox.tsx +++ b/src/components/signup/component/ToggleBox.tsx @@ -3,119 +3,119 @@ import React from 'react'; import styled from 'styled-components'; interface IProps { - title: string; - toggle: boolean[]; - description: string; - setToggle: (a: boolean[]) => 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]} > -