항해 4주차 주특기 과제

임효진·2022년 10월 16일
0
// src/App.jsimport React from "react";
import Home from "./Pages/Home";
import Router from "./shared/Router";

const App = () => {
  return <Router></Router>;
};

export default App;
//Routerimport React from "react";
import { BrowserRouter, Route, Routes } from "react-router-dom";
import Home from "../Pages/Home";
import Detail from "../Pages/Detail";

const Router = () => {
  return (
    <BrowserRouter>
      <Routes>
        {/*
						Routes안에 이렇게 작성합니다.
						Route에는 react-router-dom에서 지원하는 props들이 있습니다.

						paht는 우리가 흔히 말하는 사용하고싶은 "주소"를 넣어주면 됩니다.
						element는 해당 주소로 이동했을 때 보여주고자 하는 컴포넌트를 넣어줍니다.
				 */}
        <Route path='/' element={<Home />} />
        <Route path='/:id' element={<Detail />} />

      </Routes>
    </BrowserRouter>
  );
};

export default Router;
//Home.jsximport React from "react";
import Layout from "../component/Layout";
import Header from "../component/Header";
import Form from "../component/Form";
import List from "../component/List";

const Home = () => {
  return (
    <Layout>
      <Header></Header>
      <Form></Form>
      <List></List>
    </Layout>
  );
};

export default Home;
//Detail.jsximport React, { useEffect } from "react";
import styled from "styled-components";
import { useSelector, useDispatch } from "react-redux";
import { useParams, Link } from "react-router-dom";
import { getDetail } from "../redux/modules/todos";

const StDetailWrapDiv = styled.div`
  border: 2px solid rgb(238, 238, 238);
  width: 100%;
  height: 100vh;
  display: flex;
  -webkit-box-align: center;
  align-items: center;
  -webkit-box-pack: center;
  justify-content: center;
`;
const StDetailBorderDiv = styled.div`
  width: 600px;
  height: 400px;
  border: 1px solid rgb(238, 238, 238);
  display: flex;
  flex-direction: column;
  -webkit-box-pack: justify;
  justify-content: space-between;
`;
const StDetailTitleDiv = styled.div`
  display: flex;
  height: 80px;
  -webkit-box-pack: justify;
  justify-content: space-between;
  padding: 0px 24px;
  -webkit-box-align: center;
  align-items: center;
`;
const StBackButton = styled.button`
  border: 1px solid rgb(221, 221, 221);
  height: 40px;
  width: 120px;
  background-color: rgb(255, 255, 255);
  border-radius: 12px;
  cursor: pointer;
`;
const StDetailH1 = styled.h1`
  padding: 0px 24px;
  display: block;
  font-size: 2em;
  margin-block-start: 0.67em;
  margin-block-end: 0.67em;
  margin-inline-start: 0px;
  margin-inline-end: 0px;
  font-weight: bold;
`;
const StDetailMain = styled.main`
  padding: 0px 24px;
  display: block;
`;

const Detail = () => {
  const selTodo = useSelector((state) => state.todos.todo);
  console.log(selTodo);
  const { id } = useParams();
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(getDetail(Number(id)));
  }, [dispatch, id]);

  return (
    <StDetailWrapDiv>
      <StDetailBorderDiv>
        <div>
          <StDetailTitleDiv>
            <div>ID : {selTodo.id}</div>
            <Link to='/'>
              <StBackButton>이전으로</StBackButton>
            </Link>
          </StDetailTitleDiv>
          <StDetailH1>{selTodo.title}</StDetailH1>
          <StDetailMain>{selTodo.content}</StDetailMain>
        </div>
      </StDetailBorderDiv>
    </StDetailWrapDiv>
  );
};

export default Detail;
//Header.jsximport React from "react";
import styled from "styled-components";

const Header = () => {
  return <STTitle>투두리스트</STTitle>;
};

const STTitle = styled.h1`
  text-align: center;
`;

export default Header;
//Layout.jsximport React from "react";
import styled from "styled-components";

const Layout = ({ children }) => {
  return <STLayout>{children}</STLayout>;
};

const STLayout = styled.div`
  max-width: 1200px;
  min-width: 800px;
  margin: 0 auto;
`;

export default Layout;
//Form.jsximport React from "react";
import { useState, useSelector } from "react";
import { useDispatch } from "react-redux";
import { useEffect } from "react";
import styled from "styled-components";
import todos from "../redux/modules/todos";
import { addTodo } from "../redux/modules/todos";

const Form = () => {
  const [todo, setTodo] = useState({
// id: 3,// title: "",// content: "",// isDone: false,
  });
  const dispatch = useDispatch();

  const onChangeHandler = (e) => {
    const { name, value } = e.target;
    setTodo({
      ...todo,
      id: Date.now(),
      isDone: false,
      [name]: value,
// [e.target.name]: e.target.value,
    });
  };

// useEffect(() => {//   console.log(todo);// }, [todo]);const onSubmitHandler = (event) => {
    event.preventDefault();
// 양식 미작성 시, alert 표시console.log(todo.content);
    if (
      todo.title == undefined ||
      todo.content == undefined ||
      todo.content == "" ||
      todo.title == ""
    ) {
      alert("양식을 작성하여 제출하십시오.");
      return;
    }
    dispatch(addTodo(todo));
// submit후 빈칸 유지를 위해 공객체 생성
    setTodo({
      id: "",
      title: "",
      content: "",
      isDone: false,
    });
  };

  return (
    <STForm>
      <STInputContainer>
        <STInputLabel>제목</STInputLabel>
        <STInput
          type='text'
          name='title'
          value={todo.title}
          onChange={onChangeHandler}
        ></STInput>
        <STInputLabel>내용</STInputLabel>
        <STInput
          value={todo.content}
          type='text'
          name='content'
          onChange={onChangeHandler}
        ></STInput>
      </STInputContainer>
      <STButton onClick={onSubmitHandler}>추가</STButton>
    </STForm>
  );
};

const STForm = styled.form`
  background-color: rgb(238, 238, 238);
  border-radius: 12px;
  margin: 0px auto;
  display: flex;
  -webkit-box-align: center;
  align-items: center;
  -webkit-box-pack: justify;
  justify-content: space-between;
  padding: 30px;
  gap: 20px;
  display: flex;
  align-items: center;
  justify-content: center;
`;

const STInputContainer = styled.label`
  display: flex;
  -webkit-box-align: center;
  align-items: center;
  gap: 20px;
`;

const STInputLabel = styled.label`
  font-size: 16px;
  font-weight: 700;
`;

const STInput = styled.input`
  height: 40px;
  width: 240px;
  border: none;
  border-radius: 12px;
  padding: 0px 12px;
`;

const STButton = styled.button`
  border: none;
  height: 40px;
  cursor: pointer;
  border-radius: 10px;
  background-color: teal;
  width: 140px;
  color: rgb(255, 255, 255);
  font-weight: 700;
`;

export default Form;
//List.jsximport React, { useEffect, useState } from "react";
import styled from "styled-components";
import { useDispatch, useSelector } from "react-redux";
import todos from "../redux/modules/todos";
import { deleteTodo } from "../redux/modules/todos";
import { changeTodo } from "../redux/modules/todos";
import { eDiT } from "../redux/modules/todos";
import { Link } from "react-router-dom";

const List = () => {
  const dispatch = useDispatch();
// const [title, setTitle] = useState("");// const [content, setContent] = useState("");const todos = useSelector((state) => state.todos.todos);
  const [edit, setEdit] = useState({
    title: "",
    content: "",
  });

// 토글이 열고 닫힐때 화면 리랜더링을 위한 useState 추가const [openToggle, setOpenToggle] = useState(false);
// useEffect의 두번째 인자에 openToggle을 넣어// openToggle의 값이 변할때마다 화면을 리랜더링 하도록 함
  useEffect(() => {
    setOpenToggle(false);
  }, [openToggle]);
  const onDeleteHandler = (id) => {
    dispatch(deleteTodo(id));
  };

  const onChangeHandler = (id) => {
    dispatch(changeTodo(Number(id)));
  };

  const onEditHandler = (e) => {
    setEdit({
      ...edit,
      [e.target.name]: e.target.value,
    });
  };

  const openEdit = (id) => {
// if (editValue == “none”) {//   setEditValue(“block”);// }const [newTodoList] = todos.filter((item, i, arr) => {
// if (item.id == id) {//   arr[i].title = edit.title;//   arr[i].content = edit.content;// }return item.id == id;
    });
    const restTodo = todos.filter((item, i, arr) => {
// if (item.id == id) {//   arr[i].title = edit.title;//   arr[i].content = edit.content;// }return item.id !== id;
    });
    console.log(restTodo);
    newTodoList.isEdit = true;
    restTodo.map((item, i, arr) => {
      arr[i].isEdit = false;
    });
// 토글이 오픈될때 toggle값을 바꿈
    setOpenToggle(!openToggle);
  };
  const submitEdit = (id) => {
//none을 block으로 바꾸어줌// if (editValue == “none”) {//   setEditValue(“block”);// }const [newTodoList] = todos.filter((item, i, arr) => {
// if (item.id == id) {//   arr[i].title = edit.title;//   arr[i].content = edit.content;// }return item.id == id;
    });

//수정버튼을 닫았으니, 뉴튜두리스트 방금 찍은 아이디에 투두리스트니까//여기다가 추가를 해줘야한다 newTodoList.isEdit값을 추가해줘야함
    newTodoList.isEdit = false;
    newTodoList.title = edit.title;
    newTodoList.content = edit.content;
    console.log("이거", newTodoList);

    dispatch(eDiT(newTodoList));
// if (editValue == "block") {//   setEditValue("none");// }
  };

  return (
    <STList>
      <STListLeft>
        <div style={{ backgroundColor: "gray" }}>
          <strong>In Progress</strong>
        </div>
        {todos.map((item) => {
          if (item.isDone === false) {
            return (
              <STTodoItemContainer backgroundColor='white'>
                <STTodoItem key={item.id}>
                  <div>
                    <Link
                      to={`/${item.id}`}
                      style={{ textDecoration: "none" }}
                      key={item.id}
                    >
                      {" "}
                      상세보기{" "}
                    </Link>
                    <h2>{item.title}</h2>
                    <p>{item.content}</p>
                  </div>
                  <STFooter>
                    <STButton
                      borderColor='red'
                      onClick={() => onDeleteHandler(item.id)}
                    >
                      삭제
                    </STButton>
                    <STButton
                      borderColor='green'
                      onClick={() => onChangeHandler(item.id)}
                    >
                      완료
                    </STButton>
                  </STFooter>
                  <button
                    onClick={() => {
                      openEdit(item.id);
                    }}
                  >
                    수정버튼
                  </button>
                  {/* ⭐️요부분 굉장히 많이 씀, 어떤 버튼을 토글할때 이렇게 씀 */}
                  {item.isEdit === true ? (
                    <STModifyContainer>
                      <button onClick={() => submitEdit(item.id)}>
                        수정완료
                      </button>
                      <STModifyitem
                        name='title'
                        placeholder='제목을 입력하세요'
                        onChange={onEditHandler}
                      ></STModifyitem>
                      <STModifyitem
                        name='content'
                        placeholder='내용을 입력하세요'
                        onChange={onEditHandler}
                      ></STModifyitem>
                    </STModifyContainer>
                  ) : (
                    <div></div>
                  )}
                </STTodoItem>
              </STTodoItemContainer>
            );
          }
        })}
      </STListLeft>
      <STListRight>
        <div style={{ backgroundColor: "red" }}>
          <strong>Done</strong>
        </div>
        {todos.map((item) => {
          if (item.isDone === true) {
            return (
              <STTodoItemContainer backgroundColor='beige'>
                <STTodoItem key={todos.id}>
                  <div>
                    <Link
                      to={`/${item.id}`}
                      style={{ textDecoration: "none" }}
                      key={item.id}
                    >
                      {" "}
                      상세보기{" "}
                    </Link>
                    {/* 타입 바꿔  -> navi로*/}
                    <h2>{item.title}</h2>
                    <p>{item.content}</p>
                  </div>
                  <STFooter>
                    <STButton
                      borderColor='red'
                      onClick={() => onDeleteHandler(item.id)}
                    >
                      삭제
                    </STButton>
                    <STButton
                      borderColor='blue'
                      onClick={() => onChangeHandler(item.id)}
                    >
                      취소
                    </STButton>
                  </STFooter>
                </STTodoItem>
              </STTodoItemContainer>
            );
          }
        })}
      </STListRight>
    </STList>
  );
};

const STModifyContainer = styled.div`
  padding: 32px 0 32px 30px;
  /* display: ${(props) => props.display}; */
`;

const STModifyitem = styled.input`
  line-height: 27px;
  float: left;
  height: 27px;
  padding: 0 0 0 7px;
  vertical-align: top;
  color: #333;
  border: 1px solid #ccc;
  opacity: 1;
`;

const StDetail = styled.a`
  text-decoration: none;
`;

const STButton = styled.button`
  border: 1px solid ${(props) => props.borderColor};
  height: 40px;
  width: 120px;
  background-color: rgb(255, 255, 255);
  border-radius: 12px;
  cursor: pointer;
`;

const STFooter = styled.footer`
  display: flex;
  -webkit-box-pack: end;
  justify-content: end;
  padding: 12px;
  gap: 12px;
`;

const STTodoItemContainer = styled.div`
  background-color: ${(props) => props.backgroundColor};
  display: flex;
  flex-wrap: wrap;
  gap: 12px; ;
`;

const STTodoItem = styled.div`
  width: 220px;
  border: 4px solid teal;
  min-height: 100px;
  border-radius: 12px;
  padding: 12px 24px 24px;
  margin: auto;
  margin-top: 15px;
`;

const STList = styled.div`
  width: 100%;
  height: 1000px;
  border: 1px solid #003458;
`;

const STListLeft = styled.div`
  width: 50%;
  height: 100%;
  float: left;
  box-sizing: border-box;
  background: white;
  text-align: center;
  border-right: 2px solid black;
`;

const STListRight = styled.div`
  width: 50%;
  float: left;
  box-sizing: border-box;
  background: white;
  text-align: center;
`;

export default List;
//reducer.//todos.jsimport react from "react-dom/test-utils";

const initialState = {
  todos: [
    {
      id: 0,
      title: "React",
      content: "리액트는 어려워",
      isDone: false,
    },
    {
      id: 1,
      title: "Redux",
      content: "리덕스는 더 어려워",
      isDone: true,
    },
  ],
  todo: {
    id: 1,
    title: "Redux",
    content: "리덕스는 더 어려워",
    isDone: true,
  },
};

export const addTodo = (payload) => {
// console.log("페이로드add", payload);return { type: ADD_TODOLIST, payload };
};

export const deleteTodo = (payload) => {
// console.log("페이로드delete", payload);return { type: DELETE_TODOLIST, id: payload };
};

export const changeTodo = (payload) => {
  return { type: CHANGE_TODOLIST, id: payload };
};

export const getDetail = (id) => {
  return {
    type: GET_DETAIL,
    id: id,
  };
};

export const eDiT = (payload) => {
  console.log("페이로드임", payload);
  return {
    type: EDIT,
    id: payload.id,
    title: payload.title,
    content: payload.content,
    isDone: payload.isDone,
  };
};

const ADD_TODOLIST = "ADD_TODOLIST";
const DELETE_TODOLIST = "DELETE_TODOLIST";
const CHANGE_TODOLIST = "CHANGE_TODOLIST";
const GET_DETAIL = "GET_DETAIL";
const EDIT = "EDIT";

function todos(state = initialState, action) {
  switch (action.type) {
    case ADD_TODOLIST:
      return {
        ...state,
        todos: [...state.todos, { ...action.payload }],
      };
    case DELETE_TODOLIST:
      return {
        ...state,
        todos: state.todos.filter((todos) => todos.id !== parseInt(action.id)),
      };
    case CHANGE_TODOLIST:
      state.todos.map((item, i, arr) => {
        if (item.id === action.id) {
// item.isDone ? (item.isDone = false) : (item.isDone = true);
          arr[i].isDone ? (arr[i].isDone = false) : (arr[i].isDone = true);
        }
      });
      return {
        ...state,
        todos: [...state.todos],
      };
    case GET_DETAIL:
// return {//   ...state,//   todo: state.todos.find((todo) => {//     return todo.id === action.payload;//   }),// };console.log("겟디테일");
      const [selTodo] = state.todos.filter((item) => {
        return item.id === action.id;
      });
      console.log("겟디테일", selTodo);
      return { ...state, todo: selTodo };

    case EDIT:
      state.todos.map((item, i, arr) => {
        if (item.id == action.id) {
          arr[i].title = action.title;
          arr[i].content = action.content;
        }
      });
      return {
        ...state,
        todos: [...state.todos],
      };

    default:// need this for default casereturn state;
  }
}

export default todos;
profile
핫바리임

0개의 댓글