juni
TeamProject_뉴스피드 프로젝트 본문
import { useState, useRef, useEffect } from 'react';
import { useDispatch } from 'react-redux';
import { useNavigate } from 'react-router-dom';
import { createPost } from '../../redux/slices/postSlice';
import { Quill } from 'react-quill';
import {
Wrapper,
Container,
Header,
Title,
Subtitle,
Form,
Label,
Input,
StyledReactQuill,
ButtonContainer,
Button
} from './WritePostPage.styles';
const ImageBlot = Quill.import('formats/image');
ImageBlot.className = 'custom-image';
Quill.register(ImageBlot, true);
const WritePostPage = () => {
const dispatch = useDispatch();
const navigate = useNavigate();
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const quillRef = useRef(null);
const handleCreatePost = () => {
if (title && content) {
dispatch(createPost({ title, content }));
navigate('/');
} else {
alert('제목과 내용을 입력해주세요.');
}
};
const handleDrop = (event) => {
event.preventDefault();
const quill = quillRef.current.getEditor();
const cursorPosition = quill.getSelection().index;
const files = event.dataTransfer.files;
if (files && files.length > 0) {
const file = files[0];
uploadImage(file, cursorPosition);
}
};
const handlePaste = (event) => {
const quill = quillRef.current.getEditor();
const cursorPosition = quill.getSelection().index;
const clipboard = event.clipboardData;
const items = clipboard.items;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
const file = items[i].getAsFile();
uploadImage(file, cursorPosition);
}
}
};
const uploadImage = async (file, cursorPosition) => {
const formData = new FormData();
formData.append('image', file);
const response = await fetch('YOUR_IMAGE_UPLOAD_ENDPOINT', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error('Image upload failed');
}
const data = await response.json();
const imageUrl = data.url;
const quill = quillRef.current.getEditor();
quill.insertEmbed(cursorPosition, 'image', imageUrl);
};
const modules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['bold', 'italic', 'underline', 'strike', 'blockquote', 'code-block'],
[{ list: 'ordered' }, { list: 'bullet' }],
[{ script: 'sub' }, { script: 'super' }],
[{ indent: '-1' }, { indent: '+1' }, { direction: 'rtl' }],
[{ color: [] }, { background: [] }],
[{ align: [] }],
['link', 'image', 'video'],
['clean']
]
};
useEffect(() => {
const quill = quillRef.current.getEditor();
quill.root.addEventListener('drop', handleDrop, false);
quill.root.addEventListener('paste', handlePaste, false);
return () => {
quill.root.removeEventListener('drop', handleDrop);
quill.root.removeEventListener('paste', handlePaste);
};
}, []);
return (
<Wrapper>
<Container>
<Header>
<Title>게시글 작성</Title>
<Subtitle>게시글을 작성하고 업로드하세요</Subtitle>
</Header>
<Form>
<Label>게시글 제목</Label>
<Input type="text" value={title} onChange={(e) => setTitle(e.target.value)} />
<Label>게시글 내용</Label>
<StyledReactQuill ref={quillRef} value={content} onChange={setContent} modules={modules} />
<ButtonContainer>
<Button onClick={handleCreatePost}>업로드</Button>
<Button onClick={() => navigate('/')}>뒤로가기</Button>
</ButtonContainer>
</Form>
</Container>
</Wrapper>
);
};
export default WritePostPage;
import styled from 'styled-components';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
export const Wrapper = styled.div`
display: flex;
justify-content: center;
align-items: center;
`;
export const Container = styled.div`
width: 50%;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
padding: 20px;
background-color: #f9f9f9;
`;
export const Header = styled.header`
margin-bottom: 20px;
text-align: center;
`;
export const Title = styled.h1`
font-size: 24px;
color: #333;
`;
export const Subtitle = styled.p`
font-size: 20px;
color: #666;
`;
export const Form = styled.form`
display: flex;
flex-direction: column;
width: 100%;
`;
export const Label = styled.label`
margin-bottom: 10px;
font-size: 20px;
color: #333;
`;
export const Input = styled.input`
font-size: 18px;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 5px;
`;
export const StyledReactQuill = styled(ReactQuill)`
.ql-editor {
font-size: 18px;
min-height: 400px;
}
.ql-container {
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 20px;
}
`;
export const ButtonContainer = styled.div`
display: flex;
justify-content: space-between;
`;
export const Button = styled.button`
padding: 10px 20px;
font-size: 18px;
border: none;
border-radius: 5px;
cursor: pointer;
background-color: #007bff;
color: white;
&:hover {
background-color: #0056b3;
}
`;
import { useState, useEffect, useRef } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { useParams, useNavigate } from 'react-router-dom';
import { fetchPosts, deletePost, updatePost } from '../../redux/slices/postSlice';
import {
Wrapper,
Container,
Header,
Title,
Subtitle,
Form,
Label,
Input,
StyledReactQuill,
ButtonContainer,
Button
} from './EditPostPage.styles';
const EditPostPage = () => {
const { id } = useParams();
const postId = parseInt(id, 10);
const dispatch = useDispatch();
const navigate = useNavigate();
const post = useSelector((state) => state.posts.posts.find((post) => post.id === postId));
const [title, setTitle] = useState('');
const [content, setContent] = useState('');
const quillRef = useRef(null);
const handleUpdatePost = () => {
if (title && content) {
dispatch(updatePost({ id: postId, title, content }));
navigate('/');
} else {
alert('제목과 내용을 입력해주세요.');
}
};
const handleDeletePost = () => {
const confirmDelete = confirm('정말 이 글을 삭제하시겠습니까?');
if (confirmDelete) {
dispatch(deletePost(postId));
navigate('/');
}
};
const handleDrop = (event) => {
event.preventDefault();
const quill = quillRef.current.getEditor();
const cursorPosition = quill.getSelection().index;
const files = event.dataTransfer.files;
if (files && files.length > 0) {
const file = files[0];
uploadImage(file, cursorPosition);
}
};
const handlePaste = (event) => {
const quill = quillRef.current.getEditor();
const cursorPosition = quill.getSelection().index;
const clipboard = event.clipboardData;
const items = clipboard.items;
for (let i = 0; i < items.length; i++) {
if (items[i].type.indexOf('image') !== -1) {
const file = items[i].getAsFile();
uploadImage(file, cursorPosition);
}
}
};
const uploadImage = async (file, cursorPosition) => {
const formData = new FormData();
formData.append('image', file);
const response = await fetch('YOUR_IMAGE_UPLOAD_ENDPOINT', {
method: 'POST',
body: formData
});
if (!response.ok) {
throw new Error('Image upload failed');
}
const data = await response.json();
const imageUrl = data.url;
const quill = quillRef.current.getEditor();
quill.insertEmbed(cursorPosition, 'image', imageUrl);
};
const modules = {
toolbar: [
[{ header: '1' }, { header: '2' }, { font: [] }],
[{ size: [] }],
['bold', 'italic', 'underline', 'strike', 'blockquote', 'code-block'],
[{ list: 'ordered' }, { list: 'bullet' }],
[{ script: 'sub' }, { script: 'super' }],
[{ indent: '-1' }, { indent: '+1' }, { direction: 'rtl' }],
[{ color: [] }, { background: [] }],
[{ align: [] }],
['link', 'image', 'video'],
['clean']
]
};
useEffect(() => {
const quill = quillRef.current.getEditor();
quill.root.addEventListener('drop', handleDrop, false);
quill.root.addEventListener('paste', handlePaste, false);
return () => {
quill.root.removeEventListener('drop', handleDrop);
quill.root.removeEventListener('paste', handlePaste);
};
}, []);
useEffect(() => {
if (!post) {
dispatch(fetchPosts());
} else {
setTitle(post.title);
setContent(post.content);
}
}, [dispatch, post]);
return (
<Wrapper>
<Container>
<Header>
<Title>게시글 수정</Title>
<Subtitle>게시글을 수정하고 업로드하세요</Subtitle>
</Header>
<Form>
<Label>게시글 제목</Label>
<Input type="text" value={title} onChange={(e) => setTitle(e.target.value)} />
<Label>게시글 내용</Label>
<StyledReactQuill ref={quillRef} value={content} onChange={setContent} modules={modules} />
<ButtonContainer>
<Button onClick={handleUpdatePost}>수정</Button>
<Button onClick={handleDeletePost}>삭제</Button>
<Button onClick={() => navigate('/')}>돌아가기</Button>
</ButtonContainer>
</Form>
</Container>
</Wrapper>
);
};
export default EditPostPage;
import styled from 'styled-components';
import ReactQuill from 'react-quill';
import 'react-quill/dist/quill.snow.css';
export const Wrapper = styled.div`
font-family: Axiforma;
display: flex;
justify-content: center;
align-items: center;
`;
export const Container = styled.div`
width: 50%;
display: flex;
flex-direction: column;
align-items: center;
padding: 20px;
background-color: #f9f9f9;
`;
export const Header = styled.header`
margin-bottom: 20px;
text-align: center;
`;
export const Title = styled.h1`
font-size: 24px;
color: #333;
`;
export const Subtitle = styled.p`
font-size: 20px;
color: #666;
`;
export const Form = styled.form`
display: flex;
flex-direction: column;
width: 100%;
`;
export const Label = styled.label`
margin-bottom: 10px;
font-size: 20px;
color: #333;
`;
export const Input = styled.input`
font-size: 18px;
padding: 10px;
margin-bottom: 20px;
border: 1px solid #ccc;
border-radius: 5px;
`;
export const StyledReactQuill = styled(ReactQuill)`
.ql-editor {
font-size: 18px;
min-height: 400px;
}
.ql-container {
border: 1px solid #ccc;
border-radius: 5px;
margin-bottom: 20px;
}
`;
export const ButtonContainer = styled.div`
display: flex;
justify-content: space-between;
`;
export const Button = styled.button`
padding: 10px 20px;
font-size: 18px;
border: none;
border-radius: 5px;
cursor: pointer;
background-color: #007bff;
color: white;
&:hover {
background-color: #0056b3;
}
`;
'프로젝트 > 미니 프로젝트' 카테고리의 다른 글
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.06 |
---|---|
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.04 |
TeamProject_뉴스피드 프로젝트 (0) | 2024.06.01 |
SingleProject_리액트 가계부 RTK 개선 (0) | 2024.05.30 |
SingleProject_리액트 가계부 redux (0) | 2024.05.28 |