Skip to content

Commit

Permalink
[김수영] sprint10 (#126)
Browse files Browse the repository at this point in the history
* feat: 폴더구조 수정, header 추가

* feat:베스트게시글 ui

* feat: 게시물리스트 ui

* feat:전체게시물 불러오기

* feat:게시물 검색

* style:반응형

* fix:github branch-config

* feat:add board 페이지

* feat:detail페이지

* feat:댓글불러옴

* feat: api Response type 추가

---------

Co-authored-by: KimSuyoung <kimsuyoung@KimSuyoungui-MacBookAir.local>
  • Loading branch information
김수영 and KimSuyoung authored Nov 5, 2024
1 parent 48cfdc8 commit 9a96ed5
Show file tree
Hide file tree
Showing 19 changed files with 950 additions and 23 deletions.
24 changes: 24 additions & 0 deletions components/_styled/addboardStyled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import styled from "styled-components";
export const AddBoardPage = styled.div`
width:100%;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
`;
export const AddBoardPageContainer = styled.div`
max-width:1200px;
width:100%;
height:100%;
margin-top:24px;
display:flex;
gap:40px;
flex-direction:column;
@media(max-width:1199px){
max-width:696px;
}
@media(max-width:767px){
max-width:343px;
}
`;
24 changes: 24 additions & 0 deletions components/_styled/boardsStyled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import styled from "styled-components";

export const BoardPage = styled.div`
width:100%;
display:flex;
flex-direction:column;
align-items:center;
justify-content:center;
`;
export const BoardPageContainer = styled.div`
max-width:1200px;
width:100%;
margin-top:24px;
display:flex;
gap:40px;
flex-direction:column;
@media(max-width:1199px){
max-width:696px;
}
@media(max-width:767px){
max-width:343px;
}
`;
148 changes: 148 additions & 0 deletions components/addboard/AddBoard.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import React, { useState, useRef, useEffect } from 'react';
import * as AS from './Styled';
import { faPlus, faXmark } from "@fortawesome/free-solid-svg-icons";

export interface AddItemValues {
image: File | null;
description: string;
title: string;
}

export default function AddBoard() {
const [values, setValues] = useState<AddItemValues>({
image: null,
title: '',
description: '',
});
const [isValid, setIsValid] = useState(false);
const [preview, setPreview] = useState<string | null>(null);
const [errorMessage, setErrorMessage] = useState<string>("");
const inputRef = useRef<HTMLInputElement | null>(null);

const onChange = (name: keyof AddItemValues, value: File | null) => {
setValues((prevValues) => ({
...prevValues,
[name]: value,
}));
};

const handleTitleChange = (event: React.ChangeEvent<HTMLInputElement>) => {
const { value } = event.target;
setValues((prevValues) => ({
...prevValues,
title: value,
}));
};

const handleTextareaChange = (event: React.ChangeEvent<HTMLTextAreaElement>) => {
const { value } = event.target;
setValues((prevValues) => ({
...prevValues,
description: value,
}));
};

const handleImageChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const file: File | null = e.target.files?.[0] || null;

if (file) {
if (values.image) {
setErrorMessage("이미지 파일이 이미 선택되었습니다.");
return;
}
onChange("image", file);
const objectUrl = URL.createObjectURL(file);
setPreview(objectUrl);

} else {
onChange("image", null);
setPreview(null);
}
};

const handleClearClick = () => {
if (inputRef.current) {
inputRef.current.value = '';
}
onChange("image", null);
setPreview(null);
setErrorMessage("");
};

const isFormValid = (values: AddItemValues) => {
return (
values.title.trim() !== '' &&
values.description.trim() !== '' &&
values.image !== null
);
};

const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
e.preventDefault();
if (!isFormValid(values)) {
return;
}
console.log(values);

};

useEffect(() => {
setIsValid(isFormValid(values));
}, [values]);

return (
<AS.AddBoardForm onSubmit={handleSubmit}>
<AS.TitleContainer>
<AS.Title>게시물 쓰기</AS.Title>
<AS.AddButton type="submit" disabled={!isValid}>등록</AS.AddButton>
</AS.TitleContainer>
<AS.InputFormContainer>
<AS.InputContainer>
<AS.InputLabel>*제목</AS.InputLabel>
<AS.ContentInput
type='text'
name='title'
value={values.title}
placeholder="제목을 입력해주세요"
onChange={handleTitleChange}
/>
</AS.InputContainer>
<AS.InputContainer>
<AS.InputLabel>*내용</AS.InputLabel>
<AS.TextareaInput
name='description'
value={values.description}
placeholder="내용을 입력해주세요"
onChange={handleTextareaChange}
/>
</AS.InputContainer>
<AS.InputContainer>
<AS.InputLabel>이미지</AS.InputLabel>
<AS.ImgBox>
<AS.CustomButton onClick={() => {inputRef.current?.click();}}>
<AS.ContentInput
type='file'
name='image'
ref={inputRef}
onChange={handleImageChange}
style={{ display: 'none' }}
/>
<AS.PlusIcon icon={faPlus} style={{ color: "#9CA3AF" }} />
<AS.ButtonText>이미지 등록</AS.ButtonText>
</AS.CustomButton>
<AS.Upload>
{preview && (
<AS.Preview src={preview} alt="이미지 미리보기" style={{ objectFit: "cover" }} />
)}
{values.image && (
<AS.DeleteButton icon={faXmark} onClick={handleClearClick} />
)}
</AS.Upload>

</AS.ImgBox>
{errorMessage && <AS.ErrorMessage>{errorMessage}</AS.ErrorMessage>}
</AS.InputContainer>
</AS.InputFormContainer>
</AS.AddBoardForm>
);
}
189 changes: 189 additions & 0 deletions components/addboard/Styled.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
import styled from "styled-components";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";

export const AddBoardForm = styled.form`
width:100%;
height:100%;
display:flex;
flex-direction:column;
gap:32px;
`;

export const TitleContainer = styled.div`
width:100%;
display:flex;
justify-content:space-between;
`;

export const Title = styled.div`
color: #1F2937;
font-size: 20px;
font-weight: 700;
line-height: 32px;
`;

export const AddButton = styled.button`
width:74px;
height: 42px;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
border:none;
background:#3692FF;
color: #F3F4F6;
font-size: 16px;
font-weight: 600;
line-height: 26px;
cursor:pointer;
&:disabled {
background:#9CA3AF;
}
`;

export const InputFormContainer = styled.div`
width:100%;
display:flex;
gap:24px;
flex-direction:column;
`;
export const InputContainer = styled.div`
width:100%;
display:flex;
gap:12px;
flex-direction:column;
`;

export const InputLabel = styled.label`
color: #1F2937;
font-size: 18px;
font-weight: 700;
line-height: 26px;
`;
export const ContentInput = styled.input`
display: flex;
height: 56px;
padding: 16px 24px;
border-radius: 12px;
background: #F3F4F6;
border:none;
z-index:0
&::placeholder {
color: #9CA3AF;
font-size: 16px;
font-weight: 400;
line-height: 26px;
}
`;
export const TextareaInput = styled.textarea`
height: 250px;
padding: 16px 24px;
border:none;
border-radius: 12px;
background: var(--Cool-Gray-100, #F3F4F6);
resize:none;
&::placeholder {
color: #9CA3AF;
font-size: 16px;
font-weight: 400;
line-height: 26px;
}
`;


export const ImgBox = styled.div`
width: 588px;
display: flex;
gap: 24px;
@media (max-width: 1199px) {
width: 346px;
gap: 10px;
}
`;

export const CustomButton = styled.button`
width: 282px;
height: 282px;
display: flex;
background: var(--Secondary-200, #F3F4F6);
border-radius: 12px;
justify-content: center;
align-items: center;
flex-direction: column;
border: none;
@media (max-width: 1199px) {
width: 168px;
height: 168px;
}
`;


export const PlusIcon = styled(FontAwesomeIcon)`
width: 48px;
height: 48px;
background: none;
@media (max-width: 1199px) {
width: 24px;
height: 24px;
}
`;

export const ButtonText = styled.span`
color: #9CA3AF;
font-size: 16px;
font-weight: 400;
line-height: 26px;
background: none;
`;

export const Upload = styled.div`
width: 282px;
height: 282px;
@media (max-width: 1199px) {
width: 168px;
height: 168px;
}
`;

export const Preview = styled.img`
width: 100%;
height: 100%;
background-color: blue;
border-radius: 12px;
object-fit: cover;
`;

export const UploadButton = styled.button`
background: none;
border: none;
position: relative;
left: 248px;
bottom: 276px;
cursor: pointer;
color: #9CA3AF;
@media (max-width: 1199px) {
left: 130px;
bottom: 160px;
}
`;

export const ErrorMessage = styled.span`
color: #F74747;
font-size: 16px;
font-weight: 400;
line-height: 26px;
`;
export const DeleteButton = styled(FontAwesomeIcon)`
width:24px;
height:24px;
position:relative;
bottom:270px;
left:240px;
@media(max-width:1199px) {
bottom:160px;
left:130px;
}
`;
Loading

0 comments on commit 9a96ed5

Please sign in to comment.