Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12,022 changes: 12,022 additions & 0 deletions hw-4-5/package-lock.json

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions hw-4-5/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"name": "hw-4-5",
"version": "0.1.0",
"private": true,
"dependencies": {
"@testing-library/jest-dom": "^5.16.4",
"@testing-library/react": "^13.3.0",
"@testing-library/user-event": "^13.5.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.2",
"react-router": "^6.3.0",
"react-router-dom": "^6.3.0",
"react-scripts": "5.0.1",
"redux": "^4.2.0",
"web-vitals": "^2.1.4"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": [
"react-app",
"react-app/jest"
]
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
12 changes: 12 additions & 0 deletions hw-4-5/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Hw 4-5</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>
</html>
17 changes: 17 additions & 0 deletions hw-4-5/src/App.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-size: 20px;
-webkit-touch-callout: none;
-webkit-user-select: none;
-khtml-user-select: none;
-moz-user-select: none;
-ms-user-select: none;
user-select: none;
}

.app {
height: 100vh;
width: 100vw;
}
12 changes: 12 additions & 0 deletions hw-4-5/src/App.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import "./App.css";
import AppRoutes from "./pages/AppRoutes";

const App = () => {
return (
<div className="app">
<AppRoutes />
</div>
);
};

export default App;
16 changes: 16 additions & 0 deletions hw-4-5/src/assets/Check.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";

const Check = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M9 16.17L4.83 12l-1.42 1.41L9 19 21 7l-1.41-1.41z" />
</svg>
);
};

export default Check;
16 changes: 16 additions & 0 deletions hw-4-5/src/assets/Delete.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";

const Delete = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M6 19c0 1.1.9 2 2 2h8c1.1 0 2-.9 2-2V7H6v12zM19 4h-3.5l-1-1h-5l-1 1H5v2h14V4z" />
</svg>
);
};

export default Delete;
16 changes: 16 additions & 0 deletions hw-4-5/src/assets/Edit.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";

const Edit = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M3 17.25V21h3.75L17.81 9.94l-3.75-3.75L3 17.25zM20.71 7.04c.39-.39.39-1.02 0-1.41l-2.34-2.34c-.39-.39-1.02-.39-1.41 0l-1.83 1.83 3.75 3.75 1.83-1.83z" />
</svg>
);
};

export default Edit;
16 changes: 16 additions & 0 deletions hw-4-5/src/assets/Undo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import React from "react";

const Undo = () => {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24">
<path d="M0 0h24v24H0z" fill="none" />
<path d="M12.5 8c-2.65 0-5.05.99-6.9 2.6L2 7v9h9l-3.62-3.62c1.39-1.16 3.16-1.88 5.12-1.88 3.54 0 6.55 2.31 7.6 5.5l2.37-.78C21.08 11.03 17.15 8 12.5 8z" />
</svg>
);
};

export default Undo;
32 changes: 32 additions & 0 deletions hw-4-5/src/components/Header/Header.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import React from "react";
import styles from "./Header.module.css";
import { useSelector, useDispatch } from "react-redux";
import { logout } from "../../store/actions/userActions";

const Header = () => {
const { todos } = useSelector((state) => state.todo);
const dispatch = useDispatch();
const { username } = useSelector((state) => state.user);

const onLogout = () => {
dispatch(logout());
};

const calculateActiveTodos = (todos) =>
todos.reduce((prev, curr) => prev + !curr.isDone, 0);

return (
<div className={styles.header}>
<div>
Пользователь: {username}
<br />
Активных задач: {calculateActiveTodos(todos)}
</div>
<div onClick={onLogout} className={styles.btn}>
Выйти
</div>
</div>
);
};

export default Header;
11 changes: 11 additions & 0 deletions hw-4-5/src/components/Header/Header.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
.header {
display: flex;
justify-content: space-around;
padding-top: 20px;
padding-bottom: 20px;
}

.btn {
font-weight: bold;
cursor: pointer;
}
40 changes: 40 additions & 0 deletions hw-4-5/src/components/NewTodo/NewTodo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React, { useState } from "react";
import styles from "./NewTodo.module.css";
import Check from "./../../assets/Check";
import { useDispatch } from "react-redux";
import { addTodo } from "./../../store/actions/todoActions";

const NewTodo = () => {
const [input, setInput] = useState("");

const dispatch = useDispatch();

const onChangeHandler = (e) => {
setInput(e.target.value);
};

const onClickHandler = () => {
if (input.length) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

пустая строка с 1+ пробелом обойдет твою проверку

dispatch(addTodo(input));
setInput("");
}
};

return (
<>
<div>Новая задача</div>
<div className={styles.add}>
<input
type="text"
placeholder="Поле не может быть пустым!"
value={input}
onChange={onChangeHandler}/>
<div onClick={onClickHandler}>
<Check />
</div>
</div>
</>
);
};

export default NewTodo;
17 changes: 17 additions & 0 deletions hw-4-5/src/components/NewTodo/NewTodo.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.add > input {
width: 280px;
border: 0;
}

.add {
border: 1px solid black;
display: flex;
flex-direction: row;
width: 300px;
margin-bottom: 20px;
}

.add > div {
cursor: pointer;
color: black;
}
24 changes: 24 additions & 0 deletions hw-4-5/src/components/Tab/Tab.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from "react";
import styles from "./Tab.module.css";

const Tab = ({ isActiveSelected, toggleIsActiveSelected }) => {
return (
<>
<div>Задачи</div>
<div className={styles.tab}>
<div
onClick={isActiveSelected ? toggleIsActiveSelected : () => {}}
className={`${!isActiveSelected && styles.selected}`}>
активные
</div>
<div
onClick={!isActiveSelected ? toggleIsActiveSelected : () => {}}
className={`${isActiveSelected && styles.selected}`}>
неактивные
</div>
</div>
</>
);
};

export default Tab;
23 changes: 23 additions & 0 deletions hw-4-5/src/components/Tab/Tab.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.tab {
display: flex;
width: 300px;
border: 1px solid black;
align-self: center;
}

.tab > :first-child {
border-right: 1px solid black;
}

.tab > div {
width: 150px;
text-align: center;
color: grey;
cursor: pointer;
}

div.selected {
color: black;
font-weight: bold;
background-color: lightgray;
}
76 changes: 76 additions & 0 deletions hw-4-5/src/components/Todo/Todo.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import React, { useState } from "react";
import styles from "./Todo.module.css";
import Edit from "../../assets/Edit";
import Delete from "../../assets/Delete";
import Check from "./../../assets/Check";
import { useDispatch } from "react-redux";
import {
editTodo,
deleteTodo,
toggleTodo,
} from "./../../store/actions/todoActions";
import Undo from "../../assets/Undo";

const Todo = ({ todo: { text, isDone, id } }) => {
const [isEdit, setIsEdit] = useState(false);
const dispatch = useDispatch();

const toggleIsEdit = () => {
setIsEdit(!isEdit);
};

const [input, setInput] = useState(text);

const handleInputChange = (e) => {
setInput(e.target.value);
};

const onSave = () => {
if (input.length !== 0) {
dispatch(editTodo(id, input));
setIsEdit(false);
}
};

const onDelete = () => {
dispatch(deleteTodo(id));
};

const onToggle = () => {
dispatch(toggleTodo(id));
};

return (
<div className={styles.main}>
{isEdit
?
<>
<input
placeholder="Поле не может быть пустым!"
value={input}
onChange={handleInputChange}/>
<div className={styles.button} onClick={onSave}>
<Check />
</div>
</>
:
<>
<div className={`${styles.text} ${isDone && styles.crossed}`}>
{text}
</div>
<div className={styles.button} onClick={onToggle}>
{isDone ? <Undo /> : <Check />}
</div>
<div className={styles.button} onClick={toggleIsEdit}>
<Edit />
</div>
<div className={styles.button} onClick={onDelete}>
<Delete />
</div>
</>
}
</div>
);
};

export default Todo;
Loading