diff --git a/src/App.css b/src/App.css deleted file mode 100644 index 0304cec..0000000 --- a/src/App.css +++ /dev/null @@ -1,71 +0,0 @@ -html { - background-color: #dbe2ef; -} - -/* Lists */ -.list-item { - box-sizing: border-box; - padding: 10px; - margin: auto; - display: flex; - justify-content: space-between; - width: 1000px; - height: 80px; - border-bottom: 2px solid #3f72af; -} - -.list-item:hover { - background-color: rgba(37, 127, 234, 0.2); -} - -.list-item-col1 { - display: flex; - flex-direction: column; - justify-content: space-between; -} - -.list-item-col1-row1 { - display: flex; - align-items: center; -} - -.list-item-col1-row1__title { - font-weight: 500; - font-size: 18px; - line-height: 20px; - color: #000000; - margin-right: 16px; -} - -.list-item-col1-row1__category { - font-weight: 400; - font-size: 18px; - color: #3f72af; -} - -.list-item-col1-row1__category::before { - content: url("./icons/folder.svg"); - margin-right: 4px; - position: relative; - top: 3px; -} - -.list-item-col1-row2 { - color: #808080; - font-weight: 400; - font-size: 18px; - line-height: 20px; -} - -.list-item-col2__btn { - cursor: pointer; - box-sizing: border-box; - width: 25px; - height: 25px; - border: none; - background: transparent; -} - -.list-item-col2__btn:first-child { - margin-right: 15px; -} diff --git a/src/App.test.tsx b/src/App.test.tsx deleted file mode 100644 index 659cc13..0000000 --- a/src/App.test.tsx +++ /dev/null @@ -1,15 +0,0 @@ -import React from 'react'; -import { render } from '@testing-library/react'; -import { Provider } from 'react-redux'; -import { store } from './app/store'; -import App from './App'; - -test('renders learn react link', () => { - const { getByText } = render( - - - - ); - - expect(getByText(/learn/i)).toBeInTheDocument(); -}); diff --git a/src/Lists/Categories.tsx b/src/Lists/Categories.tsx deleted file mode 100644 index 35d1b6d..0000000 --- a/src/Lists/Categories.tsx +++ /dev/null @@ -1,18 +0,0 @@ -/* VENDOR */ -import { useSelector } from "react-redux"; - -/* APPLICATION */ -import { ListItem } from "./ListItem"; -import { selectAllCategories } from "../features/categoriesSlice"; - -export const Categories = () => { - const categories = useSelector(selectAllCategories); - - return ( - - ); -}; diff --git a/src/Lists/ListItem.tsx b/src/Lists/ListItem.tsx deleted file mode 100644 index d2cc84e..0000000 --- a/src/Lists/ListItem.tsx +++ /dev/null @@ -1,74 +0,0 @@ -/* VENDOR */ -import { useState } from "react"; -import { useSelector } from "react-redux"; - -/* APPLICATION */ -import edit from "../icons/edit.svg"; -import remove from "../icons/remove.svg"; -import { selectAllCategories } from "../features/categoriesSlice"; -import { ModalEditItem } from "../Modal/ModalEditItem"; -import { ModalRemoveItem } from "../Modal/ModalRemoveItem"; - -interface ListItemProps { - item: { - id: string; - name: string; - description: string; - category?: string; - }; -} - -export const ListItem: React.FC = ({ item }) => { - const categories = useSelector(selectAllCategories), - [editModalActive, setEditModalActive] = useState(false) - let [removeModalActive, setRemoveModalActive] = useState(false); - - return ( - <> -
  • -
    -
    -

    {item.name}

    - {item.category && ( - - { - categories.find((category) => category.id === item.category) - ?.name - } - - )} -
    -
    {item.description}
    -
    -
    - - -
    - - -
  • - - ); -}; diff --git a/src/Modal/Modal.css b/src/Modal/Modal.css deleted file mode 100644 index 3595bd9..0000000 --- a/src/Modal/Modal.css +++ /dev/null @@ -1,213 +0,0 @@ -.modal { - height: 100vh; - width: 100vw; - background-color: rgba(0, 0, 0, 0.4); - position: fixed; - top: 0; - left: 0; - display: flex; - align-items: center; - justify-content: center; - opacity: 0; - pointer-events: none; - z-index: 1; -} - -.modal.active { - opacity: 1; - pointer-events: all; -} - -.modal__content { - box-sizing: border-box; - width: 800px; - padding: 24px; - background: #dbe2ef; - box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.5); - border-radius: 4px; -} - -.modal__content.small { - width: 600px; -} - -.modal__content-header { - display: flex; - justify-content: space-between; -} - -.modal__content-title { - font-style: normal; - font-weight: 500; - font-size: 40px; - line-height: 56px; - color: #3f72af; - margin-bottom: 32px; -} - -.modal__content-header__btn { - cursor: pointer; - box-sizing: border-box; - width: 25px; - height: 25px; - border: none; - background: transparent; -} - -.modal__content-text { - margin-bottom: 24px; - font-style: normal; - font-weight: 400; - font-size: 20px; - line-height: 24px; - color: #000000; -} - -.modal__content-footer { - display: flex; - justify-content: flex-end; -} - -.modal__content_row { - display: flex; - justify-content: space-between; -} - -.modalinput { - box-sizing: border-box; - padding: 8px; - height: 46px; - width: 100%; - background: transparent; - border: 2px solid #3f72af; - border-radius: 4px; - font-style: normal; - font-weight: 400; - font-size: 20px; - line-height: 46px; -} - -.modalinput::placeholder { - color: rgba(0, 0, 0, 0.5); -} - -.modalinput-wrapper { - box-sizing: border-box; - width: 364px; - position: relative; - margin-bottom: 34px; -} - -.modalinput-wrapper.large { - width: 100%; -} - -.modalinput:focus { - outline: none; -} - -[for="modalinput"], -[for="modaltextarea"], -.dropdown-label { - font-style: normal; - display: inline-block; - background-color: #dbe2ef; - font-weight: 400; - padding: 4px; - font-size: 18px; - line-height: 20px; - color: #3f72af; - position: absolute; - left: 12px; - top: -15px; -} - -.modalinput-icon { - position: absolute; - left: 55px; - top: -11px; - z-index: 1; -} - -.modaltextarea-wrapper { - position: relative; -} - -.modaltextarea { - resize: none; - box-sizing: border-box; - width: 100%; - height: 72px; - margin-bottom: 24px; - background: transparent; - border: 2px solid #3f72af; - border-radius: 4px; - font-style: normal; - font-weight: 400; - font-size: 20px; - line-height: 26px; - padding: 8px; -} - -.modaltextarea::placeholder { - color: rgba(0, 0, 0, 0.5); -} - -.modaltextarea:focus { - outline: none; -} - -.dropdown { - cursor: pointer; - box-sizing: border-box; - width: 364px; - height: 24px; - position: relative; - z-index: 1; -} - -.dropdown .dropdown-btn { - box-sizing: border-box; - padding: 8px; - display: flex; - align-items: center; /* для треугольника */ - justify-content: space-between; - background: #dbe2ef; - border: 2px solid #3f72af; - border-radius: 4px; - font-style: normal; - font-weight: 400; - font-size: 20px; - height: 46px; - color: #000; -} - -.dropdown .dropdown-btn.placeholder { - color: rgba(0, 0, 0, 0.5); -} - -.dropdown .dropdown-content { - box-sizing: border-box; - position: absolute; - top: 45px; - background: #dbe2ef; - border: 2px solid #3f72af; - border-radius: 4px; - width: 364px; -} - -.dropdown .dropdown-content .dropdown-item { - position: relative; - left: -2px; - box-sizing: border-box; - padding: 10px; - width: 364px; - border: none; - border-radius: 4px; - color: #3f72af; -} - -.dropdown .dropdown-content .dropdown-item:hover { - background-color: #3f72ff; - color: #fff; -} diff --git a/src/Modal/ModalRow.tsx b/src/Modal/ModalRow.tsx deleted file mode 100644 index 2f82d6c..0000000 --- a/src/Modal/ModalRow.tsx +++ /dev/null @@ -1,23 +0,0 @@ -import { ModalInput } from "./ModalInput"; -import { ModalDropdown } from "./ModalDropdown"; - -interface ModalRowProps { - name: string; - setName: React.Dispatch>; - selected: string | undefined; - setSelected: React.Dispatch>; -} - -export const ModalRow: React.FC = ({ - name, - setName, - selected, - setSelected, -}) => { - return ( -
    - - -
    - ); -}; diff --git a/src/app/App.test.tsx b/src/app/App.test.tsx new file mode 100644 index 0000000..2d46f6f --- /dev/null +++ b/src/app/App.test.tsx @@ -0,0 +1,18 @@ +import React from "react"; +import { screen, render } from "@testing-library/react"; +import { Provider } from "react-redux"; +import { store } from "./store/store"; +import App from "./App"; +import { BrowserRouter } from "react-router-dom"; + +test("renders learn react link", () => { + render( + + + + + , + ); + + expect(screen.getByText(/learn/i)).toBeInTheDocument(); +}); diff --git a/src/App.tsx b/src/app/App.tsx similarity index 68% rename from src/App.tsx rename to src/app/App.tsx index b5a956b..c85eec1 100644 --- a/src/App.tsx +++ b/src/app/App.tsx @@ -2,10 +2,10 @@ import { Route, Routes } from "react-router-dom"; /* APPLICATION */ -import "./App.css"; -import { Header } from "./Header/Header"; -import { Tasks } from "./Lists/Tasks"; -import { Categories } from "./Lists/Categories"; +import "./styles/App.css"; +import { Header } from "../components/"; +import { Tasks } from "../pages/Tasks/Tasks"; +import { Categories } from "../pages/Categories/Categories"; function App() { return ( diff --git a/src/app/hooks.ts b/src/app/hooks/hooks.ts similarity index 82% rename from src/app/hooks.ts rename to src/app/hooks/hooks.ts index 520e84e..3b36dca 100644 --- a/src/app/hooks.ts +++ b/src/app/hooks/hooks.ts @@ -1,5 +1,5 @@ import { TypedUseSelectorHook, useDispatch, useSelector } from 'react-redux'; -import type { RootState, AppDispatch } from './store'; +import type { RootState, AppDispatch } from '../store/store'; // Use throughout your app instead of plain `useDispatch` and `useSelector` export const useAppDispatch = () => useDispatch(); diff --git a/src/app/store.ts b/src/app/store/store.ts similarity index 76% rename from src/app/store.ts rename to src/app/store/store.ts index 1bed9d7..6b40fad 100644 --- a/src/app/store.ts +++ b/src/app/store/store.ts @@ -1,6 +1,6 @@ import { configureStore, ThunkAction, Action } from "@reduxjs/toolkit"; -import categoriesReducer from "../features/categoriesSlice"; -import tasksReducer from "../features/tasksSlice"; +import categoriesReducer from "../../pages/Categories/categoriesSlice"; +import tasksReducer from "../../pages/Tasks/tasksSlice"; export const store = configureStore({ reducer: { diff --git a/src/app/styles/App.css b/src/app/styles/App.css new file mode 100644 index 0000000..d29d158 --- /dev/null +++ b/src/app/styles/App.css @@ -0,0 +1,3 @@ +html { + background-color: #dbe2ef; +} \ No newline at end of file diff --git a/src/index.css b/src/app/styles/index.css similarity index 100% rename from src/index.css rename to src/app/styles/index.css diff --git a/src/icons/close.svg b/src/assets/icons/close.svg similarity index 100% rename from src/icons/close.svg rename to src/assets/icons/close.svg diff --git a/src/icons/down.svg b/src/assets/icons/down.svg similarity index 100% rename from src/icons/down.svg rename to src/assets/icons/down.svg diff --git a/src/icons/edit.svg b/src/assets/icons/edit.svg similarity index 100% rename from src/icons/edit.svg rename to src/assets/icons/edit.svg diff --git a/src/icons/folder.svg b/src/assets/icons/folder.svg similarity index 100% rename from src/icons/folder.svg rename to src/assets/icons/folder.svg diff --git a/src/icons/important.svg b/src/assets/icons/important.svg similarity index 100% rename from src/icons/important.svg rename to src/assets/icons/important.svg diff --git a/src/logo.svg b/src/assets/icons/logo.svg similarity index 100% rename from src/logo.svg rename to src/assets/icons/logo.svg diff --git a/src/icons/remove.svg b/src/assets/icons/remove.svg similarity index 100% rename from src/icons/remove.svg rename to src/assets/icons/remove.svg diff --git a/src/Header/Header.css b/src/components/Header/Header.css similarity index 100% rename from src/Header/Header.css rename to src/components/Header/Header.css diff --git a/src/Header/Header.tsx b/src/components/Header/Header.tsx similarity index 94% rename from src/Header/Header.tsx rename to src/components/Header/Header.tsx index 5c09729..2912402 100644 --- a/src/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -4,7 +4,7 @@ import { Link, useLocation } from "react-router-dom"; /* APPLICATION */ import "./Header.css"; -import { ModalCreateItem } from "../Modal/ModalCreateItem"; +import { ModalCreateItem } from "../../features/ModalCreateItem/ModalCreateItem"; export const Header = () => { const { pathname } = useLocation(), diff --git a/src/components/index.ts b/src/components/index.ts new file mode 100644 index 0000000..a500e76 --- /dev/null +++ b/src/components/index.ts @@ -0,0 +1,3 @@ +import {Header} from "./Header/Header"; + +export {Header} \ No newline at end of file diff --git a/src/Modal/ModalCreateItem.tsx b/src/features/ModalCreateItem/ModalCreateItem.tsx similarity index 54% rename from src/Modal/ModalCreateItem.tsx rename to src/features/ModalCreateItem/ModalCreateItem.tsx index d58b075..d99f7a8 100644 --- a/src/Modal/ModalCreateItem.tsx +++ b/src/features/ModalCreateItem/ModalCreateItem.tsx @@ -1,28 +1,26 @@ /* VENDOR */ -import { useState } from "react"; -import { useDispatch } from "react-redux"; +import React, { useState } from "react"; import { useLocation } from "react-router-dom"; /* APPLICATION */ -import { Modal } from "./Modal"; -import { ModalHeader } from "./ModalHeader"; -import { ModalInput } from "./ModalInput"; -import { ModalRow } from "./ModalRow"; -import { ModalTextarea } from "./ModalTextarea"; -import { ModalFooter } from "./ModalFooter"; -import { tasksAdded } from "../features/tasksSlice"; -import { categoriesAdded } from "../features/categoriesSlice"; - -interface ModalCreateItemProps { - active: boolean; - setActive: React.Dispatch>; -} +import { + Modal, + ModalHeader, + ModalInput, + ModalRow, + ModalTextarea, + ModalFooter, +} from "../../shared/ui/Modal"; +import { tasksAdded } from "../../pages/Tasks/tasksSlice"; +import { categoriesAdded } from "../../pages/Categories/categoriesSlice"; +import { useAppDispatch } from "../../app/hooks/hooks"; +import { ModalCreateItemProps } from "../../shared/types/item"; export const ModalCreateItem: React.FC = ({ active, setActive, }) => { - const dispatch = useDispatch(), + const dispatch = useAppDispatch(), { pathname } = useLocation(), isCategories = pathname.includes("categories"), [name, setName] = useState(""), @@ -35,6 +33,21 @@ export const ModalCreateItem: React.FC = ({ setSelected(""); } + const handleSubmit = () => { + if (!name) return; + dispatch( + isCategories + ? categoriesAdded({ name, description }) + : tasksAdded({ + name, + description, + category: selected, + }), + ); + clearState(); + setActive(false); + }; + return ( = ({ clearState={clearState} submitBtnText="Создать" size="large" - onSubmit={ - name - ? () => { - dispatch( - isCategories - ? categoriesAdded({ name, description }) - : tasksAdded({ - name, - description, - category: setSelected, - }) - ); - clearState(); - setActive(false); - } - : () => {} - } + onSubmit={handleSubmit} /> ); diff --git a/src/Modal/ModalEditItem.tsx b/src/features/ModalEditItem/ModalEditItem.tsx similarity index 53% rename from src/Modal/ModalEditItem.tsx rename to src/features/ModalEditItem/ModalEditItem.tsx index f6fde0c..8c3c8e7 100644 --- a/src/Modal/ModalEditItem.tsx +++ b/src/features/ModalEditItem/ModalEditItem.tsx @@ -1,41 +1,48 @@ /* VENDOR */ -import { useState } from "react"; -import { useDispatch } from "react-redux"; +import React, { useState } from "react"; import { useLocation } from "react-router-dom"; /* APPLICATION */ -import { Modal } from "./Modal"; -import { ModalHeader } from "./ModalHeader"; -import { ModalRow } from "./ModalRow"; -import { ModalInput } from "./ModalInput"; -import { ModalTextarea } from "./ModalTextarea"; -import { ModalFooter } from "./ModalFooter"; -import { tasksUpdated } from "../features/tasksSlice"; -import { categoriesUpdated } from "../features/categoriesSlice"; - -interface ModalEditItemProps { - item: { - id: string; - name: string; - description: string; - category?: string; - }; - active: boolean; - setActive: React.Dispatch>; -} +import { + Modal, + ModalHeader, + ModalRow, + ModalInput, + ModalTextarea, + ModalFooter, +} from "../../shared/ui/Modal"; +import { tasksUpdated } from "../../pages/Tasks/tasksSlice"; +import { categoriesUpdated } from "../../pages/Categories/categoriesSlice"; +import { useAppDispatch } from "../../app/hooks/hooks"; +import { ModalEditItemProps } from "../../shared/types/item"; export const ModalEditItem: React.FC = ({ item, active, setActive, }) => { - const dispatch = useDispatch(), + const dispatch = useAppDispatch(), { pathname } = useLocation(), isCategories = pathname.includes("categories"), [name, setName] = useState(item.name), [selected, setSelected] = useState(item.category || ""), [description, setDescription] = useState(item.description); + const handleSubmit = () => { + if (isCategories) { + dispatch(categoriesUpdated({ id: item.id, name, description })); + } else { + dispatch( + tasksUpdated({ + id: item.id, + name, + description, + category: selected, + }), + ); + } + setActive(false); + }; return ( = ({ setActive={setActive} submitBtnText="Сохранить" size="large" - onSubmit={() => { - dispatch( - isCategories - ? categoriesUpdated({ id: item.id, name, description }) - : tasksUpdated({ - id: item.id, - name, - description, - category: selected, - }) - ); - setActive(false); - }} + onSubmit={handleSubmit} /> ); diff --git a/src/Modal/ModalRemoveItem.tsx b/src/features/ModalRemoveItem/ModalRemoveItem.tsx similarity index 60% rename from src/Modal/ModalRemoveItem.tsx rename to src/features/ModalRemoveItem/ModalRemoveItem.tsx index 58b47f6..4a6ab56 100644 --- a/src/Modal/ModalRemoveItem.tsx +++ b/src/features/ModalRemoveItem/ModalRemoveItem.tsx @@ -1,32 +1,20 @@ /* VENDOR */ -import { useDispatch } from "react-redux"; import { useLocation } from "react-router-dom"; +import React from "react"; /* APPLICATION */ -import { Modal } from "./Modal"; -import { ModalHeader } from "./ModalHeader"; -import { ModalText } from "./ModalText"; -import { ModalFooter } from "./ModalFooter"; -import { tasksRemoved, tasksClearedCategories } from "../features/tasksSlice"; -import { categoriesRemoved } from "../features/categoriesSlice"; - -interface ModalRemoveItemProps { - item: { - id: string; - name: string; - description: string; - category?: string; - }; - active: boolean; - setActive: React.Dispatch>; -} +import { Modal, ModalHeader, ModalText, ModalFooter } from "../../shared/ui/Modal"; +import { tasksRemoved, tasksClearedCategories } from "../../pages/Tasks/tasksSlice"; +import { categoriesRemoved } from "../../pages/Categories/categoriesSlice"; +import {useAppDispatch} from "../../app/hooks/hooks"; +import {ModalRemoveItemProps} from "../../shared/types/item"; export const ModalRemoveItem: React.FC = ({ item, active, setActive, }) => { - const dispatch = useDispatch(), + const dispatch = useAppDispatch(), { pathname } = useLocation(), isCategories = pathname.includes("categories"), text = `Вы уверены, что хотите удалить задачу "${item.name}"?`; diff --git a/src/index.tsx b/src/index.tsx index ba2f6f0..032f1ea 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -5,10 +5,10 @@ import { createRoot } from "react-dom/client"; import { Provider } from "react-redux"; /* APPLICATION */ -import { store } from "./app/store"; -import App from "./App"; +import { store } from "./app/store/store"; +import App from "./app/App"; //import reportWebVitals from './reportWebVitals'; -import "./index.css"; +import "./app/styles/index.css"; const container = document.getElementById("root")!; const root = createRoot(container); diff --git a/src/pages/Categories/Categories.tsx b/src/pages/Categories/Categories.tsx new file mode 100644 index 0000000..6be67cb --- /dev/null +++ b/src/pages/Categories/Categories.tsx @@ -0,0 +1,18 @@ +/* VENDOR */ + +/* APPLICATION */ +import { ListItem } from "../../shared/ui/ListItem"; +import { selectAllCategories } from "./categoriesSlice"; +import {useAppSelector} from "../../app/hooks/hooks"; + +export const Categories = () => { + const categories = useAppSelector(selectAllCategories); + + return ( +
      + {categories.map((category) => ( + + ))} +
    + ); +}; diff --git a/src/features/categoriesSlice.ts b/src/pages/Categories/categoriesSlice.ts similarity index 79% rename from src/features/categoriesSlice.ts rename to src/pages/Categories/categoriesSlice.ts index e4876f7..56afa60 100644 --- a/src/features/categoriesSlice.ts +++ b/src/pages/Categories/categoriesSlice.ts @@ -3,13 +3,10 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { v4 as uuidv4 } from "uuid"; /* APPLICATION */ -import { RootState } from "../app/store"; +import { RootState } from "../../app/store/store"; +import {Item} from "../../shared/types/item"; -export interface CategoriesState { - id: string; - name: string; - description: string; -} +type CategoriesState = Omit const initialState: CategoriesState[] = [ { @@ -33,13 +30,13 @@ export const categoriesSlice = createSlice({ name: "categories", initialState, reducers: { - categoriesAdded: (state, action) => { + categoriesAdded: (state, action: PayloadAction>) => { state.push({ id: uuidv4(), ...action.payload, }); }, - categoriesUpdated: (state, action) => { + categoriesUpdated: (state, action: PayloadAction) => { const { id, name, description } = action.payload, existingCategory = state.find((category) => category.id === id); @@ -50,9 +47,9 @@ export const categoriesSlice = createSlice({ }, categoriesRemoved: ( state: CategoriesState[], - action: PayloadAction + action: PayloadAction ) => { - let rm = (el: CategoriesState, i: number, arr: CategoriesState[]) => + let rm = (el: CategoriesState) => el.id === action.payload, rmTaskIndex = state.findIndex(rm); diff --git a/src/Lists/Tasks.tsx b/src/pages/Tasks/Tasks.tsx similarity index 60% rename from src/Lists/Tasks.tsx rename to src/pages/Tasks/Tasks.tsx index 27a5ff1..14a0354 100644 --- a/src/Lists/Tasks.tsx +++ b/src/pages/Tasks/Tasks.tsx @@ -1,12 +1,12 @@ /* VENDOR */ -import { useSelector } from "react-redux"; /* APPLICATION */ -import { ListItem } from "./ListItem"; -import { selectAllTasks } from "../features/tasksSlice"; +import { ListItem } from "../../shared/ui/ListItem"; +import { selectAllTasks } from "./tasksSlice"; +import {useAppSelector} from "../../app/hooks/hooks"; export const Tasks: React.FC = () => { - const tasks = useSelector(selectAllTasks); + const tasks = useAppSelector(selectAllTasks); return (
      diff --git a/src/features/tasksSlice.ts b/src/pages/Tasks/tasksSlice.ts similarity index 79% rename from src/features/tasksSlice.ts rename to src/pages/Tasks/tasksSlice.ts index 4ba7f07..f75d30c 100644 --- a/src/features/tasksSlice.ts +++ b/src/pages/Tasks/tasksSlice.ts @@ -3,16 +3,12 @@ import { createSlice, PayloadAction } from "@reduxjs/toolkit"; import { v4 as uuidv4 } from "uuid"; /* APPLICATION */ -import { RootState } from "../app/store"; +import { RootState } from "../../app/store/store"; +import {Item} from "../../shared/types/item"; -export interface CategoriesState { - id: string; - name: string; - description: string; - category: string; -} +type TaskState = Item; -const initialState: CategoriesState[] = [ +const initialState: TaskState[] = [ { id: "dcf6c7ea-56fe-4e36-960b-686ebf86d651", name: "Задача", @@ -37,13 +33,13 @@ export const tasksSlice = createSlice({ name: "tasks", initialState, reducers: { - tasksAdded: (state, action) => { + tasksAdded: (state, action: PayloadAction>) => { state.push({ id: uuidv4(), ...action.payload, }); }, - tasksUpdated: (state, action) => { + tasksUpdated: (state, action:PayloadAction) => { const { id, name, description, category } = action.payload, existingTask = state.find((task) => task.id === id); @@ -53,15 +49,15 @@ export const tasksSlice = createSlice({ existingTask.category = category; } }, - tasksRemoved: (state, action) => { - let rm = (el: CategoriesState, i: number, arr: CategoriesState[]) => + tasksRemoved: (state, action:PayloadAction) => { + let rm = (el: TaskState) => el.id === action.payload, rmTaskIndex = state.findIndex(rm); state.splice(rmTaskIndex, 1); }, tasksClearedCategories: (state, action) => { - state.map((task) => { + state.forEach((task) => { if (task.category === action.payload) task.category = ""; }); }, diff --git a/src/shared/types/item.ts b/src/shared/types/item.ts new file mode 100644 index 0000000..4d8a3d7 --- /dev/null +++ b/src/shared/types/item.ts @@ -0,0 +1,27 @@ +import React from "react"; + +export type Item = { + id: string; + name: string; + description: string; + category?: string; +} +export interface ModalActiveProps{ + active: boolean; + setActive: React.Dispatch>; +} +export interface ModalCreateItemProps extends ModalActiveProps{} +export interface ModalEditItemProps extends ModalActiveProps{ + item: Item; +} +export interface ModalRemoveItemProps extends ModalActiveProps{ + item: Item; +} +export interface ModalNameProps { + name: string; + setName: React.Dispatch>; +} +export interface ModalDropdownProps { + selected: string | undefined; + setSelected: React.Dispatch>; +} \ No newline at end of file diff --git a/src/shared/ui/ListItem/ListItem.css b/src/shared/ui/ListItem/ListItem.css new file mode 100644 index 0000000..40f6adb --- /dev/null +++ b/src/shared/ui/ListItem/ListItem.css @@ -0,0 +1,72 @@ +.list-item { + box-sizing: border-box; + padding: 10px; + margin: auto; + display: flex; + justify-content: space-between; + width: 1000px; + height: 100%; + border-bottom: 2px solid #3f72af; + word-break: break-all; +} + +.list-item:hover { + background-color: rgba(37, 127, 234, 0.2); +} + +.list-item-container { + display: flex; + flex-direction: column; + justify-content: space-between; +} + +.titleContainer { + display: flex; + align-items: center; +} + +.itemTitle { + font-weight: 500; + font-size: 18px; + line-height: 20px; + color: #000000; + margin-right: 16px; +} + +.itemCategory { + font-weight: 400; + font-size: 18px; + color: #3f72af; +} + +.itemCategory::before { + content: url("../../../assets/icons/folder.svg"); + margin-right: 4px; + position: relative; + top: 3px; +} + +.itemDescription{ + color: #808080; + font-weight: 400; + font-size: 18px; + line-height: 20px; +} + +.buttonContainer { + display: flex; + align-items: center; +} + +.itemBtn { + cursor: pointer; + box-sizing: border-box; + width: 25px; + height: 25px; + border: none; + background: transparent; +} + +.itemBtn:first-child { + margin-right: 15px; +} \ No newline at end of file diff --git a/src/shared/ui/ListItem/ListItem.tsx b/src/shared/ui/ListItem/ListItem.tsx new file mode 100644 index 0000000..d19e420 --- /dev/null +++ b/src/shared/ui/ListItem/ListItem.tsx @@ -0,0 +1,71 @@ +/* VENDOR */ +import React, { useState } from "react"; + +/* APPLICATION */ +import edit from "../../../assets/icons/edit.svg"; +import remove from "../../../assets/icons/remove.svg"; +import { selectAllCategories } from "../../../pages/Categories/categoriesSlice"; +import { ModalEditItem } from "../../../features/ModalEditItem/ModalEditItem"; +import { ModalRemoveItem } from "../../../features/ModalRemoveItem/ModalRemoveItem"; +import './ListItem.css' +import {useAppSelector} from "../../../app/hooks/hooks"; +import {Item} from "../../types/item"; + +interface ListItemProps { + item: Item; +} + +export const ListItem: React.FC = ({ item }) => { + const categories = useAppSelector(selectAllCategories), + [editModalActive, setEditModalActive] = useState(false), + [removeModalActive, setRemoveModalActive] = useState(false); + + return ( + <> +
    • +
      +
      +

      {item.name}

      + {item.category && ( + + { + categories.find((category) => category.id === item.category) + ?.name + } + + )} +
      +
      {item.description}
      +
      +
      + + +
      + + +
    • + + ); +}; diff --git a/src/shared/ui/ListItem/index.ts b/src/shared/ui/ListItem/index.ts new file mode 100644 index 0000000..64d728d --- /dev/null +++ b/src/shared/ui/ListItem/index.ts @@ -0,0 +1,3 @@ +import {ListItem} from "./ListItem"; + +export {ListItem} \ No newline at end of file diff --git a/src/shared/ui/Modal/Modal.css b/src/shared/ui/Modal/Modal.css new file mode 100644 index 0000000..b1d48da --- /dev/null +++ b/src/shared/ui/Modal/Modal.css @@ -0,0 +1,32 @@ +.modal { + height: 100vh; + width: 100vw; + background-color: rgba(0, 0, 0, 0.4); + position: fixed; + top: 0; + left: 0; + display: flex; + align-items: center; + justify-content: center; + opacity: 0; + pointer-events: none; + z-index: 1; +} + +.modal.active { + opacity: 1; + pointer-events: all; +} + +.modal__content { + box-sizing: border-box; + width: 800px; + padding: 24px; + background: #dbe2ef; + box-shadow: 0 8px 24px rgba(0, 0, 0, 0.5); + border-radius: 4px; +} + +.modal__content.small { + width: 600px; +} \ No newline at end of file diff --git a/src/Modal/Modal.tsx b/src/shared/ui/Modal/Modal.tsx similarity index 72% rename from src/Modal/Modal.tsx rename to src/shared/ui/Modal/Modal.tsx index 5075fed..7b8aa98 100644 --- a/src/Modal/Modal.tsx +++ b/src/shared/ui/Modal/Modal.tsx @@ -3,16 +3,10 @@ import React from "react"; /* APPLICATION */ import "./Modal.css"; +import {Item, ModalActiveProps} from "../../types/item"; -interface ModalProps { - item?: { - id: string; - name: string; - description: string; - category?: string; - }; - active: boolean; - setActive: React.Dispatch>; +interface ModalProps extends ModalActiveProps{ + item?: Item; children: React.ReactNode; clearState?(): void; } diff --git a/src/Modal/ModalBtn.css b/src/shared/ui/Modal/ModalBtn.css similarity index 73% rename from src/Modal/ModalBtn.css rename to src/shared/ui/Modal/ModalBtn.css index a676482..292d060 100644 --- a/src/Modal/ModalBtn.css +++ b/src/shared/ui/Modal/ModalBtn.css @@ -4,7 +4,6 @@ width: 120px; font-weight: 400; font-size: 20px; - line-height: 46px; color: #3f72af; border: 2px solid #3f72af; background: transparent; @@ -12,15 +11,21 @@ cursor: pointer; } +.modalbtn:hover { + color: #ffffff; + background-color: #3f72af; + transition: all 300ms; +} + .modalbtn:first-child { margin-right: 24px; } -.modalbtn.primary { +.primary { color: #fff; background-color: #3f72af; } -.modalbtn.primary.large { +.primary.large { width: 200px; -} +} \ No newline at end of file diff --git a/src/Modal/ModalBtn.tsx b/src/shared/ui/Modal/ModalBtn.tsx similarity index 69% rename from src/Modal/ModalBtn.tsx rename to src/shared/ui/Modal/ModalBtn.tsx index f9bbdcf..8379bc5 100644 --- a/src/Modal/ModalBtn.tsx +++ b/src/shared/ui/Modal/ModalBtn.tsx @@ -1,3 +1,5 @@ +import React from "react"; + import "./ModalBtn.css"; interface ModalBtnProps { @@ -13,12 +15,7 @@ export const ModalBtn: React.FC = ({ size, onClick, }) => { - const btnClass = - type === "primary" - ? size === "large" - ? "modalbtn primary large" - : "modalbtn primary" - : "modalbtn"; + const btnClass = `modalbtn ${type && "primary"} ${size && "large"}`; return (