seanoh

Finish README

...@@ -3,7 +3,6 @@ tunnel_BE/server/node_modules ...@@ -3,7 +3,6 @@ tunnel_BE/server/node_modules
3 # dependencies 3 # dependencies
4 /.pnp 4 /.pnp
5 .pnp.js 5 .pnp.js
6 ->>>>>>> board
7 6
8 ### VisualStudioCode ### 7 ### VisualStudioCode ###
9 .vscode/* 8 .vscode/*
......
...@@ -16,6 +16,7 @@ ENGINE = InnoDB; ...@@ -16,6 +16,7 @@ ENGINE = InnoDB;
16 CREATE TABLE tunnel.posts( 16 CREATE TABLE tunnel.posts(
17 id INT NOT NULL AUTO_INCREMENT, 17 id INT NOT NULL AUTO_INCREMENT,
18 userid VARCHAR(30) NOT NULL, 18 userid VARCHAR(30) NOT NULL,
19 +title TEXT NOT NULL,
19 post TEXT NOT NULL, 20 post TEXT NOT NULL,
20 created_at DATETIME NOT NULL DEFAULT now(), 21 created_at DATETIME NOT NULL DEFAULT now(),
21 status TINYINT NOT NULL, 22 status TINYINT NOT NULL,
......
...@@ -3,6 +3,10 @@ const Sequelize = require('sequelize'); ...@@ -3,6 +3,10 @@ const Sequelize = require('sequelize');
3 module.exports = class Comment extends Sequelize.Model { 3 module.exports = class Comment extends Sequelize.Model {
4 static init(sequelize) { 4 static init(sequelize) {
5 return super.init({ 5 return super.init({
6 + postid:{
7 + type: Sequelize.INTEGER,
8 + allowNull: false,
9 + },
6 userid:{ 10 userid:{
7 type: Sequelize.STRING(30), 11 type: Sequelize.STRING(30),
8 allowNull: false, 12 allowNull: false,
...@@ -31,6 +35,6 @@ module.exports = class Comment extends Sequelize.Model { ...@@ -31,6 +35,6 @@ module.exports = class Comment extends Sequelize.Model {
31 35
32 36
33 static associate(db) { 37 static associate(db) {
34 - db.Comment.belongsTo(db.Post,{foreignKey: 'postid', targetKey:'id' }); 38 + //db.Comment.belongsTo(db.Post,{foreignKey: 'postid', targetKey:'id' });
35 } 39 }
36 }; 40 };
...\ No newline at end of file ...\ No newline at end of file
......
1 +const express = require("express");
2 +const bodyParser = require("body-parser");
3 +const router = express.Router();
4 +const auth = require("../obj/authorize");
5 +
6 +const {User}=require('../models'); //유저정보 db연결
7 +const {Post}=require('../models'); //게시물정보 db연결
8 +const {Comment}=require('../models');
9 +
10 +
11 +//현재 로그인된 사용자의 게시물 배열 응답
12 +router.post('/reply',auth,(req,res)=>{
13 +
14 + Comment.findAll({
15 + where:{postid: req.body.id},
16 + order: [['created_at', 'ASC']],
17 + })
18 + .then((result)=>{
19 + //console.log(result);
20 + res.send(result);
21 + //게시물이 0개인 경우
22 + // if(result === null || result === undefined){
23 + // console.log("해당유저의 게시물이 없습니다.")
24 + // res.status(401).send("null");
25 + // }
26 + // else{
27 + // console.log(result.length);
28 + // res.sendStatus(200);
29 + // }
30 + })
31 +});
32 +
33 +//게시물 작성
34 +router.post('/write',auth,(req,res)=>{
35 + console.log(req.params.id);
36 + try{
37 + Comment.create({
38 + userid : req.session.name,
39 + postid : req.body.postid,
40 + comment : req.body.comment,
41 + })
42 + console.log("게시");
43 + res.sendStatus(200);
44 + } catch(err){
45 + console.log("실패");
46 + res.send(err);
47 + }
48 + /*
49 + User.create({
50 +
51 + name: req.body.Id,
52 + pw:req.body.Password,
53 + personality:req.body.Personality,
54 + status:false
55 + */
56 +});
57 +
58 +
59 +
60 +
61 +
62 +
63 +module.exports = router;
...\ No newline at end of file ...\ No newline at end of file
...@@ -8,9 +8,9 @@ const {Post}=require('../models'); //게시물정보 db연결 ...@@ -8,9 +8,9 @@ const {Post}=require('../models'); //게시물정보 db연결
8 8
9 //현재 로그인된 사용자의 게시물 배열 응답 9 //현재 로그인된 사용자의 게시물 배열 응답
10 router.get('/',auth,(req,res)=>{ 10 router.get('/',auth,(req,res)=>{
11 -
12 Post.findAll({ 11 Post.findAll({
13 - //where:{userid: req.session.name} 12 + // where:{userid: req.session.name},
13 + order: [['created_at', 'DESC']],
14 }) 14 })
15 .then((result)=>{ 15 .then((result)=>{
16 //console.log(result); 16 //console.log(result);
......
...@@ -18,6 +18,7 @@ const logoutRouter = require('./routes/logout.js'); ...@@ -18,6 +18,7 @@ const logoutRouter = require('./routes/logout.js');
18 const authRouter = require('./routes/auth.js'); 18 const authRouter = require('./routes/auth.js');
19 const userRouter = require('./routes/user.js'); 19 const userRouter = require('./routes/user.js');
20 const postRouter = require('./routes/post.js'); 20 const postRouter = require('./routes/post.js');
21 +const commentRouter = require('./routes/comment.js');
21 22
22 const app = express(); 23 const app = express();
23 app.set('port', process.env.PORT || 3001); 24 app.set('port', process.env.PORT || 3001);
...@@ -57,6 +58,7 @@ app.use('/api/auth',authRouter);//가입여부 확인 ...@@ -57,6 +58,7 @@ app.use('/api/auth',authRouter);//가입여부 확인
57 app.use('/api/logout',logoutRouter);//로그아웃 58 app.use('/api/logout',logoutRouter);//로그아웃
58 app.use('/api/user',userRouter);//유저정보 응답 59 app.use('/api/user',userRouter);//유저정보 응답
59 app.use('/api/post',postRouter);//유저정보 응답 60 app.use('/api/post',postRouter);//유저정보 응답
61 +app.use('/api/comment',commentRouter);//유저정보 응답
60 62
61 63
62 //에러처리 미들웨어 64 //에러처리 미들웨어
......
1 import Axios from 'axios'; 1 import Axios from 'axios';
2 import React, { useState, useEffect} from 'react'; 2 import React, { useState, useEffect} from 'react';
3 import '../style/Board.scss' 3 import '../style/Board.scss'
4 -import ReactHtmlParser from 'react-html-parser'; 4 +import ContentModal from '../Modal/ContentModal';
5 -import BoardModal from "../Modal/BoardModal";
6 5
7 function Board() { 6 function Board() {
8 const [viewContent,setViewContent] = useState([]); 7 const [viewContent,setViewContent] = useState([]);
...@@ -15,16 +14,12 @@ function Board() { ...@@ -15,16 +14,12 @@ function Board() {
15 },[viewContent]) 14 },[viewContent])
16 return ( 15 return (
17 <div className="Board"> 16 <div className="Board">
18 - <div className="write-button">
19 - <BoardModal/>
20 - </div>
21 <div className="contents"> 17 <div className="contents">
22 {viewContent&&viewContent.map(element =>{ 18 {viewContent&&viewContent.map(element =>{
23 return <div className="ui segment"> 19 return <div className="ui segment">
24 <h2>{element.title}</h2> 20 <h2>{element.title}</h2>
25 - <div> 21 + <h4>{element.created_at.slice(0,10)+" " +element.created_at.slice(11,16)}</h4>
26 - {ReactHtmlParser(element.post)} 22 + <ContentModal element={element}/>
27 - </div>
28 </div>} 23 </div>}
29 )} 24 )}
30 </div> 25 </div>
......
...@@ -4,6 +4,7 @@ import "../style/MainPage.scss"; ...@@ -4,6 +4,7 @@ import "../style/MainPage.scss";
4 import { useNavigate } from "react-router-dom"; 4 import { useNavigate } from "react-router-dom";
5 import Board from "../Board/Board" 5 import Board from "../Board/Board"
6 import React from "react"; 6 import React from "react";
7 +import BoardModal from "../Modal/BoardModal";
7 8
8 function MainPage(props) { 9 function MainPage(props) {
9 const navigate = useNavigate(); 10 const navigate = useNavigate();
...@@ -19,44 +20,22 @@ function MainPage(props) { ...@@ -19,44 +20,22 @@ function MainPage(props) {
19 return ( 20 return (
20 <div id="Main"> 21 <div id="Main">
21 <div className="Main-header"> 22 <div className="Main-header">
22 - <div className="title"> 23 + <div className="wrapper">
23 - <h1>"말하기 어려운 고민 여기에 털어놓으세요 :)"</h1> 24 + <div className="title">
25 + <h1 style={{color: 'white'}}>"말하기 어려운 고민 여기에 털어놓으세요 :)"</h1>
26 + </div>
27 + <div className="None-title">
28 + <Button className="ui right floated button" onClick={()=>onLogout()}>
29 + Logout
30 + </Button>
31 + </div>
24 </div> 32 </div>
25 - <div className="None-title"> 33 + <div className="write-btn">
26 - <Button className="ui right floated button" onClick={()=>onLogout()}> 34 + <BoardModal/>
27 - Logout
28 - </Button>
29 </div> 35 </div>
30 </div> 36 </div>
31 <div className="Main-body"> 37 <div className="Main-body">
32 <Board/> 38 <Board/>
33 - {/* <div className="user-container">
34 - <div className="userInfo">
35 - <h1>User ID</h1>
36 - </div>
37 - <div className="checkIssue-button">
38 - <Button className="ui animated button"
39 - tabIndex="0">
40 - <div className="visible content">도착한 글</div>
41 - <div className="hidden content">
42 - <i className="paper plane icon"></i>
43 - </div>
44 - </Button>
45 - </div>
46 - <div className="user">
47 - <div className="Answer">
48 - <div className="ui segment">
49 - <p>a</p>
50 - </div>
51 - <div className="ui segment">
52 - <p>a</p>
53 - </div>
54 - <div className="ui segment">
55 - <p>a</p>
56 - </div>
57 - </div>
58 - </div>
59 - </div> */}
60 </div> 39 </div>
61 </div> 40 </div>
62 ); 41 );
......
...@@ -4,6 +4,7 @@ import { Button, Modal } from 'semantic-ui-react' ...@@ -4,6 +4,7 @@ import { Button, Modal } from 'semantic-ui-react'
4 import {CKEditor} from "@ckeditor/ckeditor5-react"; 4 import {CKEditor} from "@ckeditor/ckeditor5-react";
5 import ClassicEditor from "@ckeditor/ckeditor5-build-classic"; 5 import ClassicEditor from "@ckeditor/ckeditor5-build-classic";
6 6
7 +
7 function BoardModal() { 8 function BoardModal() {
8 const handleClose = (event) => { 9 const handleClose = (event) => {
9 event.preventDefault(); 10 event.preventDefault();
...@@ -50,12 +51,9 @@ function BoardModal() { ...@@ -50,12 +51,9 @@ function BoardModal() {
50 onClose={() => setOpen(false)} 51 onClose={() => setOpen(false)}
51 onOpen={() => setOpen(true)} 52 onOpen={() => setOpen(true)}
52 open={open} 53 open={open}
53 - trigger={<Button className="ui animated button" tabIndex="0"> 54 + trigger={<Button className="ui right floated button" tabIndex="0" >
54 - <div className="visible content">게시글 작성하기</div> 55 + 게시글 작성하기
55 - <div className="hidden content"> 56 + </Button>}
56 - <i className="pencil alternate icon"></i>
57 - </div>
58 - </Button>}
59 > 57 >
60 <Modal.Header>고민이 있나요?</Modal.Header> 58 <Modal.Header>고민이 있나요?</Modal.Header>
61 <Modal.Content > 59 <Modal.Content >
......
1 +import React, {useState, useEffect} from 'react'
2 +import Axios from 'axios'
3 +import ReactHtmlParser from 'react-html-parser';
4 +import { Button, Modal, Comment, Form } from 'semantic-ui-react'
5 +import '../style/ContentModal.scss'
6 +
7 +function ContentModal({element}) {
8 + const [viewComment,setviewComment] = useState([]);
9 + useEffect(()=>{
10 + Axios.post('/api/comment/reply',{id : element.id}).then((response)=>{
11 + setviewComment(response.data);
12 + })
13 + },[viewComment])
14 +
15 + const handleClose = (event) => {
16 + event.preventDefault();
17 + setOpen(false);
18 + }
19 + const [open, setOpen] = useState(false)
20 + const [BoardComment, setBoardComment] = useState('')
21 + const onCommentHandler = (event) => {
22 + setBoardComment(event.currentTarget.value)
23 + console.log(BoardComment)
24 + }
25 + const onSubmitHandler = () => {
26 + Axios.post('/api/comment/write',{
27 + postid: element.id,
28 + comment: BoardComment
29 + })
30 + .then((res)=>{
31 + if(res.status === 200){
32 + alert("댓글 작성을 완료하였습니다.")
33 + setOpen(false);
34 + }
35 + }).catch((error) => {
36 + console.log(error.response)
37 + alert("댓글 작성을 실패하였습니다.")
38 + })
39 + }
40 + return (
41 + <Modal
42 + onClose={() => setOpen(false)}
43 + onOpen={() => setOpen(true)}
44 + open={open}
45 + trigger={<Button basic color='purple' className="ui floated button" tabIndex="0">
46 + 보기
47 + </Button>}
48 + >
49 + <Modal.Header><h2>{element.title}</h2></Modal.Header>
50 + <Modal.Content>
51 + <Modal.Description>
52 + <div className="problems">
53 + {ReactHtmlParser(element.post)}
54 + </div>
55 + </Modal.Description>
56 + </Modal.Content>
57 + <Modal.Content>
58 + {viewComment&&viewComment.map(elem =>{
59 + return <div className="ui segment">
60 + <h4>{elem.userid}</h4>
61 + <h4>{elem.comment}</h4>
62 + </div>}
63 + )}
64 + </Modal.Content>
65 + <Modal.Actions>
66 + <Comment>
67 + <Form reply>
68 + <Form.TextArea value={BoardComment} onChange={onCommentHandler}/>
69 + <Button content='댓글 남기기' onClick={onSubmitHandler} labelPosition='left' icon='edit' primary />
70 + <Button onClick={handleClose} color='black'>닫기</Button>
71 + </Form>
72 + </Comment>
73 + </Modal.Actions>
74 + </Modal>
75 + )
76 +}
77 +export default ContentModal
...\ No newline at end of file ...\ No newline at end of file
1 import React, {useState, useCallback} from "react"; 1 import React, {useState, useCallback} from "react";
2 import { useNavigate } from "react-router-dom"; 2 import { useNavigate } from "react-router-dom";
3 import "../style/RegisterPage.scss"; 3 import "../style/RegisterPage.scss";
4 -import { Button, Icon, Input } from "semantic-ui-react"; 4 +import { Button, Dropdown, Icon, Input} from "semantic-ui-react";
5 import Axios from 'axios' 5 import Axios from 'axios'
6 function RegisterPage(props) { 6 function RegisterPage(props) {
7 const navigate = useNavigate(); 7 const navigate = useNavigate();
...@@ -16,7 +16,7 @@ function RegisterPage(props) { ...@@ -16,7 +16,7 @@ function RegisterPage(props) {
16 setPassword(event.currentTarget.value); 16 setPassword(event.currentTarget.value);
17 }; 17 };
18 const onPersonalityHandler = (event) => { 18 const onPersonalityHandler = (event) => {
19 - setPersonality(event.currentTarget.value); 19 + setPersonality(event.currentTarget.value.toUpperCase());
20 }; 20 };
21 const onPasswordChkHandler = (event) => { 21 const onPasswordChkHandler = (event) => {
22 //비밀번호를 입력할때마다 password 를 검증하는 함수 22 //비밀번호를 입력할때마다 password 를 검증하는 함수
...@@ -30,6 +30,11 @@ function RegisterPage(props) { ...@@ -30,6 +30,11 @@ function RegisterPage(props) {
30 if (Password !== PasswordCheck) { 30 if (Password !== PasswordCheck) {
31 return alert('비밀번호가 일치하지 않습니다.') 31 return alert('비밀번호가 일치하지 않습니다.')
32 } 32 }
33 + else if((Personality[0] !== 'I' && Personality[0] !== 'E') || (Personality[1] !== 'S' && Personality[1] !== 'N')
34 + || (Personality[2] !== 'F' && Personality[2] !== 'T') || (Personality[3] !== 'J' && Personality[3] !== 'P'))
35 + {
36 + return alert('올바르지 않은 MBTI입니다.')
37 + }
33 else{ 38 else{
34 Axios.post('/api/register',{ 39 Axios.post('/api/register',{
35 Id, 40 Id,
...@@ -67,6 +72,7 @@ function RegisterPage(props) { ...@@ -67,6 +72,7 @@ function RegisterPage(props) {
67 icon={<Icon name='heart'/>} 72 icon={<Icon name='heart'/>}
68 iconPosition='left' 73 iconPosition='left'
69 placeholder="Your MBTI" 74 placeholder="Your MBTI"
75 + maxlength='4'
70 type="text" 76 type="text"
71 value={Personality} 77 value={Personality}
72 autoComplete="off" 78 autoComplete="off"
......
...@@ -11,16 +11,22 @@ ...@@ -11,16 +11,22 @@
11 margin: 10px; 11 margin: 10px;
12 } 12 }
13 .write-button{ 13 .write-button{
14 + margin-bottom: 30px;
14 height: 70px; 15 height: 70px;
15 display: flex; 16 display: flex;
16 justify-content: center; 17 justify-content: center;
17 align-items: center; 18 align-items: center;
18 } 19 }
19 .contents{ 20 .contents{
21 + background-color: rgb(36, 83, 121);
20 width: 100%; 22 width: 100%;
21 display: flex; 23 display: flex;
22 flex-direction: column; 24 flex-direction: column;
23 align-items: center; 25 align-items: center;
24 - border-radius: 30px; 26 + .ui.segment{
25 - border: 2px solid #333; 27 + padding: 20px;
28 + width: 67%;
29 + text-align: center;
30 +
31 + }
26 } 32 }
...\ No newline at end of file ...\ No newline at end of file
......
1 -.title-input{
2 - width: 500px;
3 - height: 40px;
4 - margin: 10px;
5 -}
6 -.write-button{
7 - height: 70px;
8 - display: flex;
9 - justify-content: center;
10 - align-items: center;
11 -}
...\ No newline at end of file ...\ No newline at end of file
1 +.header{
2 + text-align: center;
3 + display: flex;
4 +
5 +}
6 +.content{
7 + height: fit-content;
8 + text-align: left;
9 +}
...\ No newline at end of file ...\ No newline at end of file
1 #Main{ 1 #Main{
2 - background-color: beige;
3 display: flex; 2 display: flex;
4 - flex-direction: column; 3 + justify-content: center;
4 + align-items: center;
5 .Main-header{ 5 .Main-header{
6 + background-color: rgb(77, 62, 161);
7 + position: fixed;
8 + top: 0;
9 + left: 0;
10 + z-index: 10;
11 + text-align: center;
6 display: flex; 12 display: flex;
7 - flex-direction: row; 13 + flex-direction: column;
8 - height: 70px; 14 + justify-content: center;
15 + align-items: center;
16 + width: 100%;
17 + height: 120px;
18 + .wrapper{
19 + display: flex;
20 + flex-direction: row;
21 + margin-bottom: 20px;
22 + width:100%;
23 + }
9 .title{ 24 .title{
10 display: flex; 25 display: flex;
11 justify-content: center; 26 justify-content: center;
12 align-items: center; 27 align-items: center;
13 - width: 90%; 28 + width: 100%;
14 .h1{ 29 .h1{
15 font-family:Arial, Helvetica, sans-serif; 30 font-family:Arial, Helvetica, sans-serif;
16 font-size: 60px; 31 font-size: 60px;
...@@ -19,44 +34,25 @@ ...@@ -19,44 +34,25 @@
19 } 34 }
20 .None-title{ 35 .None-title{
21 display: flex; 36 display: flex;
22 - justify-content: center; 37 + justify-content:right;
23 align-items: center; 38 align-items: center;
24 width: 10%; 39 width: 10%;
25 .ui button{ 40 .ui button{
26 height: 40px; 41 height: 40px;
27 } 42 }
28 } 43 }
44 +
29 } 45 }
30 .Main-body{ 46 .Main-body{
47 + padding-top: 120px;
31 display: flex; 48 display: flex;
32 flex-direction: row; 49 flex-direction: row;
33 - height: 100vh; 50 + justify-content: center;
34 - border: 3px solid black; 51 + align-items: center;
52 + width: 100%;
35 .Board{ 53 .Board{
36 - text-align: center;
37 - display: flex;
38 - justify-content: flex-start;
39 - flex-direction: column;
40 width: 100%; 54 width: 100%;
41 - height: 100%; 55 +
42 } 56 }
43 - .user-container{ 57 + }
44 - display: flex;
45 - flex-direction: column;
46 - width: 25%;
47 - border: 2px solid black;
48 - padding: 10px 0 30px 0;
49 - .userInfo{
50 - display: flex;
51 - justify-content: center;
52 - align-items: center;
53 - }
54 - .checkIssue-button{
55 - height: 70px;
56 - display: flex;
57 - justify-content: center;
58 - align-items: center;
59 - }
60 - }
61 - }
62 } 58 }
......