An open API service indexing awesome lists of open source software.

https://github.com/ketrindorofeeva/to-do-list

Список дел
https://github.com/ketrindorofeeva/to-do-list

bootstrap5 html iconify javascript moment-js pinia scss vite vue-router vue3

Last synced: 3 months ago
JSON representation

Список дел

Awesome Lists containing this project

README

          

# to-do-list


Технологии разработки
Языки программирования
Фреймворки
Сервер разработки
Библиотеки
Иконки



HTML
JavaScript
Vue3.js
Vite
Pinia
Iconify



SCSS

Bootstrap4

vue-router







moment.js

##

Оглавление


- Документация
- Техническое задание
- Установка и запуск фреймворка Vue 3 (Vite)
- Установка Pinia
- Установка Vue Router
- Реализация программного продукта
- Авторизация
- Страница задач
- Папки с задачами
- Поиск задач по описанию
- Сортировка задач
- Итоговый шаблон задач
- Добавить задачу
- Редактировать задачу
- Удалить задачу
- Развертывание проекта на GitHub
- Неполадки с проектом на GitHub

_________________________________________________________________________________________________________________________________________________________________
##

Документация


- [Vite](https://vitejs.dev/)
- [Vue 3](https://v3.ru.vuejs.org/)
- [Pinia](https://pinia.vuejs.org/)
- [Vue Router](https://router.vuejs.org/)
- [Moment.js](https://momentjs.com/)



:bookmark_tabs: Оглавление

##

Техническое задание


Задача: Разработать клиентское приложение для ведение списка дел, используя заданные технологии.

Функционал:
- Создание задачи:
- Название задачи;
- Приоритет (низкий / средний / высокий);
- Дата создания (указывается автоматически).
- Пометка задач выполненными;
- Поиск задач по названию;
- Сорировка задач по:
- Дате;
- Приоритету;
- Алфавиту;
- Изменение порядка сортировки.
- Редактирование и удаление задач;
- (Дополнительно) дополнить задачи пунктом "Срок выполнения" (с точностью до дня), выделять просроченные задачи;
- (Дополнительно) добавить функционал группировки задач (подсписки):
- Пользователь может создать подсписок задач и, либо указать его при создании новой задачи, либо переместить в него существующую задачу.
- (Дополнительно) возможность авторизации:
- Регистрация не нужна, используются готовые учётки, записанные где-то в коде приложения;
- Неавторизированный пользователь видит только форму авторизации;
- Авторизованный пользователь видит своё имя, может разлогиниться;
- Авторизованный пользователь автоматически авторизуется после перезагрузки приложения;
- Разграничение того, какой пользователь какие задачи создаёт и видит делать НЕ нужно.

Технические требования:
- Требуется только клиентская часть приложения, все необходимые данные должны храниться в браузере;
- (Дополнительно) Использовать Vite вместо Webpack:
- Примечание: делать это следует в самую первую очередь.
- (Дополнительно) Поддержка препроцессора CSS (SCSS / Sass / Less):
- Примечание: важно чтобы просто была возможность пользоваться препроцессором, изучать его синтаксис и активно им пользоваться необязательно.
- Vue 3;
- Pinia;
- Библиотека для иконок (предпочтительно Iconify);
- Использование дополнительных библиотек не возбраняется.



:bookmark_tabs: Оглавление

##

Установка и запуск фреймворка Vue 3 (Vite)


Для начала нужно [установить и настроить Vite](https://vitejs.dev/guide/)
**Vite** — это инструмент сборки, цель которого — обеспечить более быструю и экономичную разработку современных веб-проектов.

**1. Установка Vite**
```js
npm create vite@latest
```
**2. Дать название проекту**

Название проекта

**3. Выбрать фреймворк Vue**

Выбор фреймворка

**4. Выбрать язык программирования JavaScript**

Язык программирования

Итог:

Итог

**5. Завершить настройку проекта**
Перейти в проект:
```js
cd to-do-list
```

Установить соответствующие пакеты:
```js
npm i
```

Итог:

Итог

**6. Запустить проект**
```js
npm run dev
```
Итог:

Запуск проекта

Перейдите по ссылке Local, чтобы увидеть проект в браузере.



:bookmark_tabs: Оглавление

##

Установка Pinia


**[Pinia](https://pinia.vuejs.org/introduction.html)** — это легковесная библиотека хранилища и структура управления состоянием для Vue.js. Разработанный в первую очередь для создания интерфейсных веб-приложений, он использует декларативный синтаксис и предлагает собственный API управления состоянием.

**1. Установка pinia**
```js
npm install pinia
```

**2. Создать экземпляр pinia в ```/src/main.js``` и передать его в приложение как плагин**
```js
import {createApp} from 'vue'
import {createPinia} from "pinia" //!
import router from "./router.js";

import './style.scss'
import "bootstrap/dist/css/bootstrap.min.css";

import App from './App.vue'

createApp(App)
.use(createPinia()) //!
.use(router)
.mount('#app')
```



:bookmark_tabs: Оглавление

##

Установка Vue Router


**[Vue Router](https://router.vuejs.org/installation.html)** — маршрутизатор для Vue.js.

**1. Установка vue-router**
```js
npm install vue-router@4
```

**2. Создадим ```router.js``` в папке ```/src/```**
Создается объект маршрутизатора с помощью функции ```createRouter()```, которая поставляется библиотекой ```vue-router```. Затем в массиве ```routes``` определяются маршруты, которые сопоставляют пути запроса и компоненты. Каждый маршрут определяет свойство ```path```, которое представляет путь запроса, и свойство ```component``` - компонент, который будет обрабатывать запрос по этому пути.
```js
import {createRouter} from "vue-router";
import Tasks from "./components/Tasks.vue";
import AuthForm from "./components/AuthForm.vue";

const routes = [
{
path: '/',
component: AuthForm
},
{
path: '/tasks',
component: Tasks
}
]

const router = createRouter({
history: createWebHistory(process.env.NODE_ENV === 'production' ? '/to-do-list/' : '/'),
routes
})

export default router
```

**3. Подключение ```router.js``` в ```/src/main.js```**
```js
import {createApp} from 'vue'
import {createPinia} from "pinia"
import router from "./router.js"; //!

import './style.scss'
import "bootstrap/dist/css/bootstrap.min.css";

import App from './App.vue'

createApp(App)
.use(createPinia())
.use(router) //!
.mount('#app')
```

**4. Подключение ```router-view``` в ```/src/App.vue```**
**router-view** - отобразит компонент, соответствующий URL-адресу. Его можно разместить в любом месте, чтобы адаптировать к макету.
```vue




```



:bookmark_tabs: Оглавление

##

Реализация программного продукта


###

Авторизация


Поля и их заполнение:


Поля
Обязательность заполнения
Правила заполнения


Логин
Да
test


Пароль
Да
test

Создадим хранилище ```UserStore.js``` в папке ```/src/store/```.
Хранилище определяется с помощью ```defineStore()``` и что для него требуется уникальное имя, передаваемое в качестве первого аргумента.

В хранилище ```UserStore.js``` указаны:

1. Реактивный ref-объект ```users```, содержащий в себе свойства ```id```, ```login```, ```password``` и ```name```. Значения логина и пароля используются при авторизации;
2. Реактивная переменная ```auth```, указывающая на состояние авторизованного пользователя;
3. Функция ```logout()```, использующаяся для выхода авторизованного пользователя из аккаунта.

```js
import {defineStore} from "pinia";

export const useUserStore = defineStore('userStore', () => {
const users = ref({
id: 1,
login: 'test',
password: 'test',
name: 'Екатерина'
})

const auth = ref(false)

const logout = () => {
router.push(`/`)
auth.value = false
localStorage.removeItem('users')
}

return {users, auth, logout}
})
```

Далее, создадим компонент ```AuthForm.vue``` в папке ```/src/components/```
Определяем хранилище, потому что хранилище не будет создано до тех пор, пока ```use...Store()``` не вызвано внутри компонента `````` (или внутри ```setup()```).
В функции ```data()```, которая возвращает объект данных для экземпляра компонента, хранятся свойства ```login``` и ```password```, которые будут использоваться в шаблоне ```template``` компонента. Также в скрипте подключаем ```useVuelidate``` для валидации форм. В ```methods``` прописываем функцию ```signIn()```, используемая для авторизации. Рассмотрим ее поподробнее.

Если поля логина и пароля не пустые и введенные данные совпадают с данными, хранящимися в объекте ```users``` хранилища ```UserStore.js```, то информация о пользователе сохраняется в ```localStorage```.
Объекты веб-хранилища ```localStorage``` позволяют хранить пары ключ/значение в браузере. Данные, которые записаны в ```localStorage```, сохраняются после перезапуска браузера.

После идет проверка, если ```localStorage``` не пустой, то пользователя перенаправляет на страницу задач.

```vue
<script>
import {useUserStore} from "../store/UserStore.js";

import useVuelidate from "@vuelidate/core";
import {required} from "@vuelidate/validators";

export default {
name: 'AuthForm',
setup() {
const userStore = useUserStore();
return {userStore}
},
data() {
return {
v$: useVuelidate(),
login: "",
password: ""
};
},
validations() {
return {
login: {required},
password: {required},
}
},
methods: {
signIn() {
if(this.login && this.password) {
if (this.login === this.userStore.users.login && this.password === this.userStore.users.password) {
const parsed = JSON.stringify(this.userStore.users);
localStorage.setItem('users', parsed);

if (localStorage.getItem("users") != null) {
this.$router.push(`/tasks`)
this.userStore.auth = true
}
}
}
}
}
}

```

Далее, прописываем шаблон ```template```.
```vue

```

В итоге, в компонент ```App.vue``` подключаем компонент ```AuthForm.vue``` и хранилище ```UserStore.js``` и прописываем условия в шаблоне ```template```, при которых при запуске приложения проверяется, есть ли информация о пользователе. Если данных нет, то пользователя перенапрявляет на авторизацию, если данные есть - то пользователь при перезагрузке страницы остается на странице с задачами.
```vue




```
```js

import AuthForm from "./components/AuthForm.vue";
import {useUserStore} from "./store/UserStore.js";

export default {
name: 'App',
setup() {
const userStore = useUserStore();
return {userStore};
},
components: {
AuthForm
}
}

```

Незаполненная авторизация

Заполненная авторизация

https://user-images.githubusercontent.com/93386515/236624246-f6233fca-ed01-47fd-84e0-c4e4896b7cf6.mp4



:bookmark_tabs: Оглавление

###

Страница задач


Создадим хранилище ```TasksStore.js``` в папке ```/src/store/```.
Хранилище определяется с помощью ```defineStore()``` и что для него требуется уникальное имя, передаваемое в качестве первого аргумента.

В хранилище ```TasksStore.js``` указаны:

1. Реактивный ref-массив ```tasks```, содержащий в себе объекты со свойствами ```id```, ```description```, ```priority```, ```date_creation```, ```date_completion``` и ```folder```;
2. Реактивная переменная ```dialogVisible```, указывающая на статус открытия модального окна;
3. Реактивный ref-массив ```prioritys```, содержащий в себе объекты со свойствами ```id```, ```label``` и ```value```;
4. Функция ```showModal()``` для открытия модального окна;
5. Функция ```closeModal()``` для закрытия модального окна;
6. Функция ```deleteTask()``` для удаления задачи;
7. Функция ```watch()```, в которой информация о задачах сохраняется в ```localStorage```. При перезагрузке страницы созданные задачи не пропадут.

```js
import {defineStore} from "pinia";
import {ref, watch} from "vue";

export const useTasksStore = defineStore('tasksStore', () => {
//Массив задач
const tasks = ref([
{
id: 1,
description: "Размещение новостей на сайте",
priority: "Низкий",
date_creation: "22.04.2023",
date_completion: "23.06.2023",
folder: 'Все задачи'
},
{
id: 2,
description: "Внедрить Wi-fi для читателей",
priority: "Средний",
date_creation: "25.03.2023",
date_completion: "22.06.2023",
folder: 'Все задачи'
},
{
id: 3,
description: "Отредактировать раздел 'Доступная среда'",
priority: "Высокий",
date_creation: "15.03.2023",
date_completion: "21.06.2023",
folder: 'Все задачи'
},
{
id: 4,
description: "Презентация 'Информационные технологии'",
priority: "Средний",
date_creation: "15.03.2023",
date_completion: "20.06.2023",
folder: 'Все задачи'
},
{
id: 5,
description: "Счетчики - внедрить дизайн",
priority: "Средний",
date_creation: "09.03.2023",
date_completion: "10.03.2023",
folder: 'Все задачи'
},
{
id: 6,
description: "Внедрит новый layout",
priority: "Средний",
date_creation: "07.03.2023",
date_completion: "08.03.2023",
folder: 'Все задачи'
},
{
id: 7,
description: "Скролл в новостях",
priority: "Низкий",
date_creation: "01.03.2023",
date_completion: "02.03.2023",
folder: 'Все задачи'
},
{
id: 8,
description: "Форма сброса пароля",
priority: "Средний",
date_creation: "25.02.2023",
date_completion: "27.02.2023",
folder: 'Все задачи'
},
{
id: 9,
description: "Внедрение модуля Chat",
priority: "Низкий",
date_creation: "20.02.2023",
date_completion: "21.02.2023",
folder: 'Все задачи'
}
]);

//Статус открытия модального окна
const dialogVisible = ref(false)

//Массив приоритетов
const prioritys = ref([
{
id: 1,
label: 'Низкий',
value: 'low'
},
{
id: 2,
label: 'Средний',
value: 'medium'
},
{
id: 3,
label: 'Высокий',
value: 'high'
}
])

//Показать модальное окно
const showModal = () => {
dialogVisible.value = true
}

//Закрыть модальное окно
const closeModal = () => {
dialogVisible.value = false
}

//Удалить задачу
const deleteTask = (id) => {
if (confirm(`Вы уверены, что хотите удалить данную задачу?`)) {
tasks.value = tasks.value.filter((el) => el.id !== id);
}
};

const tasksInLocalStorage = localStorage.getItem("tasks")
if(tasksInLocalStorage) {
tasks.value = JSON.parse(tasksInLocalStorage)._value
}

watch(
() => tasks,
(state) => {
localStorage.setItem("tasks", JSON.stringify(state))
},
{deep: true}
)

return {
tasks, prioritys, dialogVisible, showModal, closeModal, deleteTask
}
})
```


Создадим хранилище ```FoldersStore.js``` в папке ```/src/store/```. В нем будут указаны:

1. Реактивный ref-массив ```folders```, содержащий в себе объекты со свойствами ```id``` и ```title```;
2. Реактивная переменная ```dialogVisible```, указывающая на статус открытия модального окна;
3. Функция ```showModal()``` для открытия модального окна;
4. Функция ```closeModal()``` для закрытия модального окна;
5. Функция ```watch()```, в которой информация о папках сохраняется в ```localStorage```. При перезагрузке страницы созданные папки не пропадут.

```js
import {defineStore} from "pinia";
import {ref, watch} from "vue";

export const useFoldersStore = defineStore('foldersStore', () => {
const folders = ref([
{
id: 1,
title: 'Все задачи'
},
{
id: 2,
title: 'В процессе'
},
{
id: 3,
title: 'Выполнено'
}
])

//Статус открытия модального окна
const dialogVisible = ref(false)

//Показать модальное окно
const showModal = () => {
dialogVisible.value = true
}

//Закрыть модальное окно
const closeModal = () => {
dialogVisible.value = false
}

const foldersInLocalStorage = localStorage.getItem("folders")
if(foldersInLocalStorage) {
folders.value = JSON.parse(foldersInLocalStorage)._value
}

watch(
() => folders,
(state) => {
localStorage.setItem("folders", JSON.stringify(state))
},
{deep: true}
)

return {
folders, dialogVisible, showModal, closeModal
}
})
```


Создадим компонент ```Tasks.vue``` в папке ```/src/components/```.
В нем будут реализованы:

1. Обращение к авторизованному пользователю;
2. Иконка создания задачи;
3. Иконка выхода пользователя из аккаунта.

И подключены компоненты:

1. Создания задачи ```ModalFormCreateTask.vue``` в папке ```/src/components/```;
2. Задачи ```Task.vue``` в папке ```/src/components/```

В скрипте подключаем компоненты ```Task.vue``` и ```ModalFormCreateTask``` и хранилища ```TasksStore.js``` и ```UserStore.js```.
```js

import {useTasksStore} from "../store/TasksStore.js";
import {useUserStore} from "../store/UserStore.js";

import Task from "./Task.vue";
import ModalFormCreateTask from "./ModalFormCreateTask.vue";

const tasksStore = useTasksStore()
const userStore = useUserStore()

```

В шаблоне ```template``` обращаемся к хранилищам из скрипта. Из ```UserStore.js``` достаем свойство ```name``` объекта ```users```, при клике на иконку создания задачи срабатывает функция ```showModal``` хранилища ```TasksStore.js```, при клике на иконку выхода пользователя из аккаунта срабатывает функция ```logout``` хранилища ```UserStore.js```.
```vue


{{userStore.users.name}}, это ваш To do list













```


Создадим компонент ```Task.vue``` в папке ```/src/components/```.
В нем будут реализованы:


  1. Список папок;

  2. Поле поиска задач по описанию;

  3. Сортировка по дате завершения, приоритету, от А до Я и от Я до А;

  4. Задачи. Каждая задача включает в себя:


    1. Статус выполнения (галочка);

    2. Описание;

    3. Приоритет;

    4. Дата создания;

    5. Срок выполнения;

    6. Папка;

    7. Иконка редактирования задачи;

    8. Иконка удаления задачи.


В `````` подключаем хранилища ```TasksStore.js``` и ```FoldersStore.js```.
```js
<script setup>
import {useFoldersStore} from "../store/FoldersStore.js";
import {useTasksStore} from "../store/TasksStore.js";

const folderStore = useFoldersStore()
const taskStore = useTasksStore()

```

:exclamation: В следующих подпунктах описана поэтапная реализация структуры компонента ```Task.vue```.



:bookmark_tabs: Оглавление

####

Папки с задачами


В компоненте ```Task.vue``` в скрипте подключаем хранилища ```FoldersStore.js``` и ```TasksStore.js```.
В функции ```data()``` создаем свойство ```specificFolder```, отвечающее за выбор определенной папки.
В ```computed``` создаем функцию ```getTasksInFolder()```, в которой прописываем связь с массивом ```tasks``` хранилища ```TasksStore.js```. Метод массива ```.filter()``` позволяет получить новый массив, отфильтровав элементы с помощью переданной колбэк-функции. Прописываем условия, в которых сказано, что, если пользователь выберет определенное значение, то в браузере выведутся задачи, в которых ```folder``` равен данному значению.
```js

import {useFoldersStore} from "../store/FoldersStore.js";
import {useTasksStore} from "../store/TasksStore.js";

export default {
name: 'Task',
data() {
return {
specificFolder: 'all-tasks'
}
},
computed: {
getTasksInFolder() {
let array = useTasksStore().tasks;

//Определенные задачи в определенных папках
return array.filter(task => {
if (this.specificFolder === 'all-tasks') {
return task.folder
}

if (this.specificFolder === 'in-process') {
return task.folder === 'В процессе'
}

if (this.specificFolder === 'done-tasks') {
return task.folder === 'Выполнено'
}
})
},
}
}

```

https://user-images.githubusercontent.com/93386515/236671929-225e16d9-19b6-46c6-9920-09ba8fa34bd0.mp4



:bookmark_tabs: Оглавление

####

Поиск задач по описанию


В компоненте ```Task.vue``` в ```computed``` скрипта создаем функцию ```searchDesc()```, в которой прописываем связь с функцией ```getTasksInFolder()```. Метод массива ```.filter()``` позволяет получить новый массив, отфильтровав элементы с помощью переданной колбэк-функции. Введенные данные в поле поиска сравниваются с данными из массива ```tasks```, а именно со свойством ```description```. Метод ```toLowerCase()``` возвращает значение строки, на которой он был вызван, преобразованное в нижний регистр.
```js

import {useFoldersStore} from "../store/FoldersStore.js";
import {useTasksStore} from "../store/TasksStore.js";

export default {
name: 'Task',
data() {
return {
specificFolder: 'all-tasks',
searchDescription: '', //!
}
},
computed: {
getTasksInFolder() {
let array = useTasksStore().tasks;

//Определенные задачи в определенных папках
return array.filter(task => {
if (this.specificFolder === 'all-tasks') {
return task.folder
}

if (this.specificFolder === 'in-process') {
return task.folder === 'В процессе'
}

if (this.specificFolder === 'done-tasks') {
return task.folder === 'Выполнено'
}
})
},
searchDesc() { //!
//Поиск задач по названию
return this.getTasksInFolder.filter(task => {
return task.description.toLowerCase().includes(this.searchDescription.toLowerCase())
})
},
}
}

```

https://user-images.githubusercontent.com/93386515/236672096-d8077c14-aa21-4243-a2e4-b4afe07c56c1.mp4



:bookmark_tabs: Оглавление

####

Сортировка задач


В компоненте ```Task.vue``` в скрипте подключаем ```moment.js```.
**[Moment.js](https://momentjs.com/)** — это одна из самых популярных JavaScript-библиотек для разбора и форматирования дат.

В функции ```data()``` создаем свойство ```sortby```, отвечающее за выбор определенной сортировки.
В ```computed``` скрипта создаем функцию ```searchAndSort()```, в которой прописываем связь с функцией ```searchDesc()```. Метод массива ```.sort()``` на месте сортирует элементы массива и возвращает отсортированный массив.

Сортировка задач происходит:
- По дате завершения;
- По приоритету;
- От А до Я;
- От Я до А.

При сортировке "По дате завершения" обращаемся к библиотеке ```moment.js```. Идет обращение к свойству ```date_completion``` массива ```tasks```, задается формат даты ```DD.MM.YY```. При данной сортировке сначала идут задачи, созданные позже предыдущих.

При сортировке "По приоритету" идет обращение к длине свойства ```priority``` массива ```tasks```, также используется метод ```localeCompare()```.
**localeCompare()** - возвращает число, указывающее, должна ли данная строка находиться до, после или в том же самом месте, что и строка, переданная через параметр, при сортировке этих строк.
При данной сортировке сначала идут задачи высокого, затем среднего и только потом низкого приоритета.

При сортировке "От А до Я" и "От Я до А" идет обращение к свойству ```description``` массива ```tasks```, также используется метод ```localeCompare()```. При сортировке "От А до Я" задачи сначала идут по возрастанию, а при сортировке "От Я до А" - по убыванию.

```js

import {useFoldersStore} from "../store/FoldersStore.js";
import {useTasksStore} from "../store/TasksStore.js";
import moment from "moment/moment.js";

export default {
name: 'Task',
data() {
return {
specificFolder: 'all-tasks',
searchDescription: '',
sortby: 'date', //!
}
},
computed: {
getTasksInFolder() {
let array = useTasksStore().tasks;

//Определенные задачи в определенных папках
return array.filter(task => {
if (this.specificFolder === 'all-tasks') {
return task.folder
}

if (this.specificFolder === 'in-process') {
return task.folder === 'В процессе'
}

if (this.specificFolder === 'done-tasks') {
return task.folder === 'Выполнено'
}
})
},
searchDesc() {
//Поиск задач по названию
return this.getTasksInFolder.filter(task => {
return task.description.toLowerCase().includes(this.searchDescription.toLowerCase())
})
},
searchAndSort() { //!
//Сортировка
return this.searchDesc.sort((a, b) => {
//По дате завершения
if (this.sortby === 'date') {
return moment(b.date_completion, 'DD.MM.YY') - moment(a.date_completion, 'DD.MM.YY')
}

//По приоритету
if (this.sortby === 'priority') {
return b.priority.length - a.priority.length || a.priority.localeCompare(b.priority);
}

//От А до Я
if (this.sortby === 'from_a_to_z') {
return a.description.localeCompare(b.description)
}

//От Я до А
if (this.sortby === 'from_z_to_a') {
return b.description.localeCompare(a.description)
}
})
}
}
}

```

https://user-images.githubusercontent.com/93386515/236672225-88417e0b-3735-4cd1-bf0c-faedf2ef9e9d.mp4



:bookmark_tabs: Оглавление

####

Итоговый шаблон задач


```vue



Папки:


Все задачи
В процессе
Выполнено








Сортировать по:


Дата
Приоритет
От А до Я
От Я до А






Описание
Приоритет
Дата создания
Срок выполнения
Папка














{{task.description}}





{{task.priority}}


{{priority.label}}


{{task.date_creation}}


{{task.date_completion}}





{{task.folder}}


{{folder.title}}























```

https://user-images.githubusercontent.com/93386515/236671687-3aca15ed-b857-4aec-86a4-8c357798c230.mp4



:bookmark_tabs: Оглавление

####

Добавить задачу


В компоненте ```Tasks.vue``` указана иконка создания задачи. При клике на нее идет обращение к функции ```showModal()``` хранилища ```TasksStore.js```, которая отвечает за всплывание модального окна.

Иконка в шаблоне ```template``` компонента ```Tasks.vue```.
```vue






```

Создадим компонент ```ModalFormCreateTask.vue```.
В `````` компонента подключаем хранилище ```TasksStore.js```.
```js
<script setup>
import {useTasksStore} from "../store/TasksStore.js";

const taskStore = useTasksStore();

```

В функции ```data()``` создаем свойства ```id```, ```description```, ```priority```, ```date_creation```, ```date_completion```, ```isDone``` и ```folder```.
В ```methods``` прописываем функцию ```createTask()```, в которой создаем объект ```newTask```, ссылающийся на свойства из ```data()```. Далее, проверяем поля ```description```, ```priority``` и ```date_completion``` на пустоту. Если они не пустые, то при помощи метода ```.push()``` добавляем объект ```newTask``` в массив ```tasks``` и закрываем модальное окно.

```js

import {useTasksStore} from "../store/TasksStore.js";

import useVuelidate from "@vuelidate/core";
import {required} from "@vuelidate/validators";

import {vMaska} from "maska";

const date = new Date();
const day = date.getDate();
const month = date.getMonth() + 1;
const year = date.getFullYear();
const format = ('0' + day).slice(-2) + '.' + ('0' + month).slice(-2) + '.' + year;

export default {
name: "ModalFormCreateTask",
data() {
return {
v$: useVuelidate(),
id: Date.now(),
description: '',
priority: '',
date_creation: `${format}`,
date_completion: '',
isDone: false,
folder: 'Все задачи'
}
},
validations() {
return {
description: {required},
priority: {required},
date_completion: {required}
}
},
methods: {
createTask(event) {
const newTask = {
id: this.id,
description: this.description,
priority: this.priority,
date_creation: this.date_creation,
date_completion: this.date_completion,
isDone: this.isDone,
folder: this.folder
}

if (this.description && this.priority && this.date_completion) {
useTasksStore().tasks.push(newTask)

useTasksStore().dialogVisible = false;
}
}
}
}

```

Шаблон ```template``` компонента ```ModalFormCreateTask.vue```.
```vue

```

https://user-images.githubusercontent.com/93386515/236677694-49a3e82a-0d57-4270-8cb6-e9bc00349a2d.mp4



:bookmark_tabs: Оглавление

####

Редактировать задачу


В скрипте компонента ```Task.vue``` в функции ```data()``` прописываем свойство ```edit```, отвечающее за доступ к редактированию задачи.
В ```methods``` создаем функции ```showEdit()```, которая отвечает за открытие полей редактирования, и ```hideEdit()```, которая отвечает за закрытие полей редактирования.
```js

import {useFoldersStore} from "../store/FoldersStore.js";
import {useTasksStore} from "../store/TasksStore.js";
import moment from "moment/moment.js";

export default {
name: 'Task',
data() {
return {
specificFolder: 'all-tasks',
searchDescription: '',
sortby: 'date',
edit: false //!
}
},
methods: {
showEdit() { //!
this.edit = true
},
hideEdit() { //!
this.edit = false
}
},
computed: {
getTasksInFolder() {
let array = useTasksStore().tasks;

//Определенные задачи в определенных папках
return array.filter(task => {
if (this.specificFolder === 'all-tasks') {
return task.folder
}

if (this.specificFolder === 'in-process') {
return task.folder === 'В процессе'
}

if (this.specificFolder === 'done-tasks') {
return task.folder === 'Выполнено'
}
})
},
searchDesc() {
//Поиск задач по названию
return this.getTasksInFolder.filter(task => {
return task.description.toLowerCase().includes(this.searchDescription.toLowerCase())
})
},
searchAndSort() { //!
//Сортировка
return this.searchDesc.sort((a, b) => {
//По дате завершения
if (this.sortby === 'date') {
return moment(b.date_completion, 'DD.MM.YY') - moment(a.date_completion, 'DD.MM.YY')
}

//По приоритету
if (this.sortby === 'priority') {
return b.priority.length - a.priority.length || a.priority.localeCompare(b.priority);
}

//От А до Я
if (this.sortby === 'from_a_to_z') {
return a.description.localeCompare(b.description)
}

//От Я до А
if (this.sortby === 'from_z_to_a') {
return b.description.localeCompare(a.description)
}
})
}
}
}

```

Иконки шаблона ```template``` компонента ```Task.vue```, отвечающие за открытие и закрытие полей редактирования.
```vue











```

Редактирование на примере поля ```description``` в шаблоне ```template``` компонента ```Task.vue```.
```vue

{{task.description}}



```

https://user-images.githubusercontent.com/93386515/236679749-391a17de-aaa4-43cb-8a52-06c4d65d4e4e.mp4



:bookmark_tabs: Оглавление

####

Удалить задачу


При клике на иконку шаблона ```template``` компонента ```Task.vue```, отвечающую за удаление задачи по ```id```, срабатывает функция ```deleteTask()``` хранилища ```TasksStore.js```. В этот момент всплывает окно подтверждения удаления задачи. Если нажать "ОК" задача удалится.

Иконка шаблона ```template``` компонента ```Task.vue```.
```vue






```

Функция ```deleteTask()``` хранилища ```TasksStore.js```.
```js
//Удалить задачу
const deleteTask = (id) => {
if (confirm(`Вы уверены, что хотите удалить данную задачу?`)) {
tasks.value = tasks.value.filter((el) => el.id !== id);
}
};
```

https://user-images.githubusercontent.com/93386515/236680774-a517c287-e025-41b5-861f-c72a9bc70c22.mp4



:bookmark_tabs: Оглавление

##

Развертывание проекта на GitHub


**1. Установите ```base``` на ```/vite.config.js```.**
```js
import vue from '@vitejs/plugin-vue'

export default defineConfig({
plugins: [vue()],
base:
process.env.NODE_ENV === "production"
? "/to-do-list/"
: "/",
})
```

**2. Удалить ```dist``` из ```/.gitignore```.**

**3. Публиковать проект.**
```js
npm run build
```

**4. Убедиться, что зафиксировано и нажато origin master/main.**

**5. Использовать поддерево.**
```js
git subtree push --prefix dist origin gh-pages
```

**6. Перейти в репозиторий проекта и открыть ```Settings > Pages > Build and deployment```.**
В ```Branch``` выбрать ветку ```gh-pages```. Нажать "Сохранить".

**7. Сделано.**
Открыть проект в браузере.



:bookmark_tabs: Оглавление

##

Неполадки с проектом на GitHub


При перезагрузке страницы задач, возникает ошибка 404. На локальном сервере данной ошибки нет.

https://user-images.githubusercontent.com/93386515/236690112-6cd2f634-4b80-4768-ae24-5d5abae3a022.mp4



:bookmark_tabs: Оглавление