From 5c72b97567dd632c2f772a5da2a93cca8feb4eb7 Mon Sep 17 00:00:00 2001 From: ValeriiaFrolenko Date: Wed, 18 Jun 2025 14:08:19 +0300 Subject: [PATCH 1/4] hw4 --- index.html | 2 +- src/index.html | 228 +++++++++++++++++++++ src/main.js | 23 +++ src/style.css | 518 ++++++++++++++++++++++++++++++++++++++++++++++++ style/style.css | 0 5 files changed, 770 insertions(+), 1 deletion(-) create mode 100644 src/index.html create mode 100644 src/style.css delete mode 100644 style/style.css diff --git a/index.html b/index.html index 09c6dc0..b2f535d 100644 --- a/index.html +++ b/index.html @@ -4,7 +4,7 @@ Simple HTML Page - + diff --git a/src/index.html b/src/index.html new file mode 100644 index 0000000..ed4d797 --- /dev/null +++ b/src/index.html @@ -0,0 +1,228 @@ + + + + + + + + + + + To-Do List + + + + + + +
+ +

To-Do List

+ + + +
+ + + + + + +
+ + +
+ +
+ + +
+ + +
+ + +
+
+ + +
+ + +
+ +
+ + +
+ +
Complete the project
+ +
Medium
+
+ + +
+ +
May 1, 2024
+ +
+ + + + +
+
+
+ + +
+ +
+ +
+ +
Read the book
+ +
Low
+
+ +
+
April 25, 2024
+
+ + +
+
+
+ + +
+
+ +
+
Go to the gym
+ +
High
+
+ +
+
April 23, 2024
+
+ + +
+
+
+ + +
+
+ +
+
Make a doctor's appointment
+
Medium
+
+ +
+
April 20, 2024
+
+ + +
+
+
+
+
+ + + + + + + + diff --git a/src/main.js b/src/main.js index e69de29..8acd78e 100644 --- a/src/main.js +++ b/src/main.js @@ -0,0 +1,23 @@ + +function openPopup(mode = 'add') { + // Отримуємо посилання на popup overlay елемент + const popupElement = document.getElementById('popup'); + + // Додаємо клас 'active' який робить popup видимим + // (в CSS є правило .popup-overlay.active { display: flex; }) + popupElement.classList.add('active'); +} + +function closePopup() { + // Отримуємо посилання на popup overlay елемент + const popupElement = document.getElementById('popup'); + + // Видаляємо клас 'active' - popup стає невидимим + popupElement.classList.remove('active'); +} + +function saveTask() { + // Поки що просто закриваємо popup + closePopup(); +} + diff --git a/src/style.css b/src/style.css new file mode 100644 index 0000000..17840ad --- /dev/null +++ b/src/style.css @@ -0,0 +1,518 @@ +/* ===== ГЛОБАЛЬНІ СТИЛІ ===== */ +/* Скидання стандартних стилів браузера для всіх елементів */ +* { + margin: 0; /* Прибираємо зовнішні відступи */ + padding: 0; /* Прибираємо внутрішні відступи */ + box-sizing: border-box; /* Включаємо padding і border у загальну ширину/висоту елемента */ +} + +/* Базові стилі для всієї сторінки */ +body { + font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; + background-color: #f5f5f7; /* Світло-сірий фон сторінки */ + color: #1d1d1f; /* Темно-сірий колір тексту */ + line-height: 1.4; /* Міжрядковий інтервал для кращої читабельності */ +} + +/* ===== ОСНОВНИЙ КОНТЕЙНЕР ===== */ +/* Головний контейнер всього додатку */ +.container { + max-width: 800px; /* Максимальна ширина - обмежуємо на великих екранах */ + margin: 0 auto; /* Центруємо по горизонталі */ + padding: 40px 20px; /* Внутрішні відступи: 40px зверху/знизу, 20px з боків */ +} + +/* ===== ЗАГОЛОВОК ДОДАТКУ ===== */ +/* Головний заголовок "To-Do List" */ +h1 { + text-align: center; /* Центруємо текст */ + font-size: 3rem; /* Великий розмір шрифту (48px) */ + font-weight: 700; /* Жирний шрифт */ + margin-bottom: 40px; /* Відступ знизу */ + color: #1d1d1f; /* Темно-сірий колір */ +} + +/* ===== СЕКЦІЯ ШВИДКОГО ДОДАВАННЯ ЗАВДАНЬ ===== */ +/* Контейнер для поля вводу та кнопки "Add" */ +.add-task-section { + display: flex; /* Flexbox для розташування елементів в ряд */ + gap: 12px; /* Проміжок між полем вводу та кнопкою */ + margin-bottom: 30px; /* Відступ знизу від наступної секції */ +} + +/* Поле швидкого вводу завдання */ +.task-input { + flex: 1; /* Займає всю доступну ширину */ + padding: 16px 20px; /* Внутрішні відступи для зручності */ + border: 2px solid #e5e5e7; /* Сіра рамка */ + border-radius: 12px; /* Закруглені кути */ + font-size: 16px; /* Розмір шрифту */ + background: white; /* Білий фон */ + outline: none; /* Прибираємо стандартний outline при фокусі */ + transition: border-color 0.2s ease; /* Плавна анімація зміни кольору рамки */ +} + +/* Стиль поля вводу при отриманні фокусу */ +.task-input:focus { + border-color: #007aff; /* Синя рамка при фокусі */ +} + +/* Стиль placeholder тексту */ +.task-input::placeholder { + color: #86868b; /* Сірий колір для підказки */ +} + +/* Кнопка "Add" для швидкого додавання */ +.add-btn { + padding: 16px 24px; /* Внутрішні відступи */ + background: white; /* Білий фон */ + color: #1d1d1f; /* Темний текст */ + border: 2px solid #e5e5e7; /* Сіра рамка */ + border-radius: 12px; /* Закруглені кути */ + font-size: 16px; /* Розмір шрифту */ + font-weight: 600; /* Напівжирний шрифт */ + cursor: pointer; /* Курсор-рука при наведенні */ + transition: all 0.2s ease; /* Плавна анімація всіх змін */ + white-space: nowrap; /* Запобігаємо переносу тексту */ +} + +/* Ефект наведення на кнопку "Add" */ +.add-btn:hover { + border-color: #007aff; /* Синя рамка */ + background: #f9f9fb; /* Світло-сірий фон */ +} + +/* ===== ПАНЕЛЬ УПРАВЛІННЯ (СОРТУВАННЯ/ФІЛЬТРУВАННЯ) ===== */ +/* Контейнер для dropdown-ів сортування та фільтрування */ +.controls { + display: flex; /* Flexbox для горизонтального розташування */ + justify-content: space-between; /* Розташовуємо елементи по краях */ + margin-bottom: 30px; /* Відступ знизу */ + gap: 20px; /* Проміжок між елементами */ +} + +/* Група управління (обгортка для кожного dropdown) */ +.control-group { + display: flex; /* Flexbox для вирівнювання */ + align-items: center; /* Вертикальне центрування */ +} + +/* Приховуємо label-и (якщо є) */ +.control-group label { + display: none; +} + +/* Стилі для dropdown селектів */ +.select { + padding: 12px 16px; /* Внутрішні відступи */ + border: 2px solid #e5e5e7; /* Сіра рамка */ + border-radius: 8px; /* Закруглені кути (менше ніж у інших елементів) */ + background: white; /* Білий фон */ + font-size: 14px; /* Розмір шрифту */ + cursor: pointer; /* Курсор-рука */ + min-width: 150px; /* Мінімальна ширина */ + appearance: none; /* Прибираємо стандартний вигляд браузера */ + + /* Кастомна стрілка dropdown через SVG */ + background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236b7280' stroke-linecap='round' stroke-linejoin='round' stroke-width='1.5' d='m6 8 4 4 4-4'/%3e%3c/svg%3e"); + background-position: right 12px center; /* Позиція стрілки */ + background-repeat: no-repeat; /* Без повторення */ + background-size: 16px; /* Розмір стрілки */ + padding-right: 40px; /* Додатковий відступ справа для стрілки */ +} + +/* Стиль dropdown при фокусі */ +.select:focus { + outline: none; /* Прибираємо стандартний outline */ + border-color: #007aff; /* Синя рамка */ +} + +/* ===== КОНТЕЙНЕР СПИСКУ ЗАВДАНЬ ===== */ +/* Головний контейнер для всіх завдань */ +.tasks-container { + background: white; /* Білий фон */ + border-radius: 16px; /* Закруглені кути */ + overflow: hidden; /* Приховуємо зайвий контент */ + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); /* Тінь для об'ємності */ +} + +/* ===== СТИЛІ ОКРЕМОГО ЗАВДАННЯ ===== */ +/* Контейнер одного завдання */ +.task-item { + display: flex; /* Flexbox для горизонтального розташування */ + align-items: center; /* Вертикальне центрування всіх елементів */ + padding: 20px; /* Внутрішні відступи */ + border-bottom: 1px solid #f2f2f7; /* Тонка сіра лінія знизу */ + transition: background-color 0.2s ease; /* Плавна зміна фону при наведенні */ +} + +/* Прибираємо лінію у останнього завдання */ +.task-item:last-child { + border-bottom: none; +} + +/* Ефект наведення на завдання */ +.task-item:hover { + background-color: #f9f9fb; /* Світло-сірий фон при наведенні */ +} + +/* ===== ЧЕКБОКС ЗАВДАННЯ ===== */ +/* Кастомний чекбокс для позначення виконання завдання */ +.task-checkbox { + width: 24px; /* Ширина */ + height: 24px; /* Висота */ + border: 2px solid #d1d1d6; /* Сіра рамка */ + border-radius: 50%; /* Робимо круглим */ + margin-right: 16px; /* Відступ справа */ + cursor: pointer; /* Курсор-рука */ + display: flex; /* Flexbox для центрування галочки */ + align-items: center; /* Вертикальне центрування */ + justify-content: center; /* Горизонтальне центрування */ + transition: all 0.2s ease; /* Плавна анімація всіх змін */ +} + +/* Стиль чекбокса коли завдання виконано */ +.task-checkbox.completed { + background: white; /* Білий фон */ + border-color: #34c759; /* Зелена рамка */ +} + +/* Галочка у виконаному завданні */ +.task-checkbox.completed::after { + content: '✓'; /* Символ галочки */ + color: #34c759; /* Зелений колір */ + font-size: 14px; /* Розмір */ + font-weight: bold; /* Жирний */ +} + +/* ===== КОНТЕНТ ЗАВДАННЯ ===== */ +/* Контейнер для назви та пріоритету завдання */ +.task-content { + flex: 1; /* Займає всю доступну ширину */ + display: flex; /* Flexbox для вертикального розташування */ + flex-direction: column; /* Вертикальне розташування елементів */ + gap: 8px; /* Проміжок між назвою та пріоритетом */ + align-items: flex-start; /* Вирівнювання по лівому краю */ +} + +/* Назва/заголовок завдання */ +.task-title { + font-size: 18px; /* Розмір шрифту */ + font-weight: 500; /* Середня жирність */ + color: #1d1d1f; /* Темно-сірий колір */ +} + +/* Стиль назви виконаного завдання */ +.task-title.completed { + text-decoration: line-through; /* Закреслений текст */ + color: #86868b; /* Сірий колір */ +} + +/* ===== ІНДИКАТОР ПРІОРИТЕТУ ===== */ +/* Базові стилі для всіх індикаторів пріоритету */ +.task-priority { + display: inline-block; /* Інлайн-блок для розміщення поруч */ + padding: 4px 12px; /* Внутрішні відступи */ + border-radius: 12px; /* Закруглені кути (пілюля) */ + font-size: 12px; /* Маленький розмір шрифту */ + font-weight: 600; /* Напівжирний */ + text-transform: uppercase; /* Великі літери */ + letter-spacing: 0.5px; /* Проміжки між літерами */ +} + +/* Високий пріоритет - червоний */ +.priority-high { + background: #ff3b30; /* Червоний фон */ + color: white; /* Білий текст */ +} + +/* Середній пріоритет - помаранчевий */ +.priority-medium { + background: #ff9500; /* Помаранчевий фон */ + color: white; /* Білий текст */ +} + +/* Низький пріоритет - сірий */ +.priority-low { + background: #e5e5e7; /* Сірий фон */ + color: #86868b; /* Сірий текст */ +} + +/* ===== МЕТАДАНІ ТА ДІЇ ЗАВДАННЯ ===== */ +/* Контейнер для дати та кнопок дій */ +.task-meta { + display: flex; /* Flexbox для горизонтального розташування */ + align-items: center; /* Вертикальне центрування */ + gap: 16px; /* Проміжок між датою та кнопками */ +} + +/* Дата завдання */ +.task-date { + color: #86868b; /* Сірий колір */ + font-size: 14px; /* Маленький розмір шрифту */ +} + +/* Контейнер для кнопок дій (редагування, видалення) */ +.task-actions { + display: flex; /* Flexbox для горизонтального розташування */ + gap: 8px; /* Проміжок між кнопками */ +} + +/* Базові стилі для кнопок дій */ +.action-btn { + width: 32px; /* Ширина */ + height: 32px; /* Висота */ + border: none; /* Без рамки */ + background: transparent; /* Прозорий фон */ + cursor: pointer; /* Курсор-рука */ + border-radius: 6px; /* Закруглені кути */ + display: flex; /* Flexbox для центрування іконки */ + align-items: center; /* Вертикальне центрування */ + justify-content: center; /* Горизонтальне центрування */ + transition: background-color 0.2s ease; /* Плавна зміна фону */ +} + +/* Ефект наведення на кнопки дій */ +.action-btn:hover { + background: #f2f2f7; /* Світло-сірий фон при наведенні */ +} + +/* Іконка для кнопки редагування */ +.edit-btn::after { + content: '✏️'; /* Emoji олівця */ + font-size: 14px; /* Розмір */ +} + +/* Іконка для кнопки видалення */ +.delete-btn::after { + content: '🗑️'; /* Emoji смітника */ + font-size: 14px; /* Розмір */ +} + +/* ===== МОДАЛЬНЕ ВІКНО (POPUP) ===== */ +/* Затемнений фон за модальним вікном */ +.popup-overlay { + position: fixed; /* Фіксована позиція відносно вікна */ + top: 0; /* Прилипає до верху */ + left: 0; /* Прилипає до лівого краю */ + width: 100%; /* На всю ширину екрану */ + height: 100%; /* На всю висоту екрану */ + background: rgba(0, 0, 0, 0.5); /* Півпрозорий чорний фон */ + display: none; /* Приховано за замовчуванням */ + align-items: center; /* Вертикальне центрування popup */ + justify-content: center; /* Горизонтальне центрування popup */ + z-index: 1000; /* Високий z-index для показу поверх всього */ + backdrop-filter: blur(4px); /* Розмиття фону */ +} + +/* Активний стан overlay (коли popup відкритий) */ +.popup-overlay.active { + display: flex; /* Показуємо через flexbox */ +} + +/* Саме модальне вікно */ +.popup { + background: white; /* Білий фон */ + border-radius: 20px; /* Закруглені кути */ + padding: 32px; /* Внутрішні відступи */ + width: 90%; /* 90% ширини екрану */ + max-width: 500px; /* Максимальна ширина */ + position: relative; /* Для позиціювання дочірніх елементів */ + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15); /* Тінь для об'ємності */ +} + +/* ===== ЗАГОЛОВОК POPUP ===== */ +/* Контейнер заголовка та кнопки закриття */ +.popup-header { + display: flex; /* Flexbox для розташування */ + justify-content: space-between; /* Заголовок зліва, кнопка справа */ + align-items: center; /* Вертикальне центрування */ + margin-bottom: 24px; /* Відступ знизу */ +} + +/* Заголовок popup */ +.popup-title { + font-size: 24px; /* Великий розмір шрифту */ + font-weight: 700; /* Жирний шрифт */ + color: #1d1d1f; /* Темно-сірий колір */ +} + +/* Кнопка закриття popup (хрестик) */ +.close-btn { + width: 32px; /* Ширина */ + height: 32px; /* Висота */ + border: none; /* Без рамки */ + background: transparent; /* Прозорий фон */ + cursor: pointer; /* Курсор-рука */ + border-radius: 50%; /* Круглий */ + display: flex; /* Flexbox для центрування */ + align-items: center; /* Вертикальне центрування */ + justify-content: center; /* Горизонтальне центрування */ + font-size: 20px; /* Розмір символу × */ + color: #86868b; /* Сірий колір */ + transition: background-color 0.2s ease; /* Плавна зміна фону */ +} + +/* Ефект наведення на кнопку закриття */ +.close-btn:hover { + background: #f2f2f7; /* Світло-сірий фон */ +} + +/* ===== ФОРМА В POPUP ===== */ +/* Група полів форми */ +.form-group { + margin-bottom: 24px; /* Відступ знизу між групами */ +} + +/* Підписи до полів форми */ +.form-label { + display: block; /* Блоковий елемент */ + margin-bottom: 8px; /* Відступ знизу */ + font-weight: 600; /* Напівжирний шрифт */ + color: #1d1d1f; /* Темно-сірий колір */ + font-size: 16px; /* Розмір шрифту */ +} + +/* Поля вводу в формі */ +.form-input { + width: 100%; /* На всю ширину контейнера */ + padding: 16px; /* Внутрішні відступи */ + border: 2px solid #e5e5e7; /* Сіра рамка */ + border-radius: 12px; /* Закруглені кути */ + font-size: 16px; /* Розмір шрифту */ + background: white; /* Білий фон */ + outline: none; /* Без стандартного outline */ + transition: border-color 0.2s ease; /* Плавна зміна кольору рамки */ +} + +/* Стиль полів вводу при фокусі */ +.form-input:focus { + border-color: #007aff; /* Синя рамка при фокусі */ +} + +/* Стиль placeholder в полях форми */ +.form-input::placeholder { + color: #86868b; /* Сірий колір підказки */ +} + +/* ===== ВИБІР ПРІОРИТЕТУ В POPUP ===== */ +/* Контейнер для radio кнопок пріоритету */ +.priority-options { + display: inline-flex; /* Інлайн flexbox */ + gap: 16px; /* Проміжок між опціями */ + margin-top: 8px; /* Відступ зверху */ +} + +/* Окрема опція пріоритету */ +.priority-option { + display: flex; /* Flexbox для вирівнювання */ + align-items: center; /* Вертикальне центрування */ + gap: 8px; /* Проміжок між radio та текстом */ + cursor: pointer; /* Курсор-рука для всієї опції */ +} + +/* Кастомна radio кнопка */ +.priority-radio { + width: 20px; /* Ширина */ + height: 20px; /* Висота */ + border: 2px solid #d1d1d6; /* Сіра рамка */ + border-radius: 50%; /* Круглий */ + position: relative; /* Для позиціювання внутрішнього кружка */ + transition: all 0.2s ease; /* Плавна анімація змін */ +} + +/* Стиль вибраної radio кнопки */ +.priority-radio.selected { + border-color: #007aff; /* Синя рамка */ + background: #007aff; /* Синій фон */ +} + +/* Внутрішній кружок у вибраної radio кнопки */ +.priority-radio.selected::after { + content: ''; /* Порожній контент */ + position: absolute; /* Абсолютне позиціювання */ + top: 50%; /* Центр по вертикалі */ + left: 50%; /* Центр по горизонталі */ + transform: translate(-50%, -50%); /* Центрування через transform */ + width: 8px; /* Ширина внутрішнього кружка */ + height: 8px; /* Висота внутрішнього кружка */ + background: white; /* Білий колір */ + border-radius: 50%; /* Круглий */ +} + +/* Підпис до radio кнопки */ +.priority-label { + font-size: 16px; /* Розмір шрифту */ + color: #1d1d1f; /* Темно-сірий колір */ +} + +/* ===== КНОПКИ POPUP ===== */ +/* Контейнер для кнопок дій внизу popup */ +.popup-actions { + display: flex; /* Flexbox для розташування */ + gap: 12px; /* Проміжок між кнопками */ + justify-content: flex-end; /* Вирівнювання по правому краю */ +} + +/* Базові стилі для всіх кнопок */ +.btn { + padding: 12px 24px; /* Внутрішні відступи */ + border: none; /* Без рамки */ + border-radius: 12px; /* Закруглені кути */ + font-size: 16px; /* Розмір шрифту */ + font-weight: 600; /* Напівжирний шрифт */ + cursor: pointer; /* Курсор-рука */ + transition: all 0.2s ease; /* Плавна анімація змін */ +} + +/* Первинна кнопка (збереження) */ +.btn-primary { + background: #007aff; /* Синій фон */ + color: white; /* Білий текст */ +} + +/* Ефект наведення на первинну кнопку */ +.btn-primary:hover { + background: #0056b3; /* Темніший синій при наведенні */ +} + +/* Вторинна кнопка (скасування) */ +.btn-secondary { + background: #f2f2f7; /* Світло-сірий фон */ + color: #1d1d1f; /* Темний текст */ +} + +/* Ефект наведення на вторинну кнопку */ +.btn-secondary:hover { + background: #e5e5e7; /* Трохи темніший сірий при наведенні */ +} + +/* Стилі для екранів менше 768px */ +@media (max-width: 768px) { + /* Зменшуємо відступи контейнера */ + .container { + padding: 20px 16px; /* Менші відступи */ + } + + /* Зменшуємо розмір заголовка */ + h1 { + font-size: 2.5rem; /* Менший розмір шрифту */ + } + + /* Зменшуємо проміжки між елементами управління */ + .controls { + gap: 12px; /* Менший проміжок */ + } + + /* Адаптуємо popup для мобільних */ + .popup { + margin: 20px; /* Відступи з усіх боків */ + padding: 24px; /* Менші внутрішні відступи */ + } + + /* Вертикальне розташування опцій пріоритету на мобільних */ + .priority-options { + flex-direction: column; /* Вертикальне розташування */ + gap: 12px; /* Проміжок між опціями */ + } +} \ No newline at end of file diff --git a/style/style.css b/style/style.css deleted file mode 100644 index e69de29..0000000 From 5697f69d118e02cf698ec1dd0fd5e30cc2ea56b2 Mon Sep 17 00:00:00 2001 From: ValeriiaFrolenko Date: Wed, 18 Jun 2025 14:08:44 +0300 Subject: [PATCH 2/4] hw4 --- .idea/.gitignore | 8 ++++++++ .idea/JS_Template.iml | 9 +++++++++ .idea/misc.xml | 6 ++++++ .idea/modules.xml | 8 ++++++++ .idea/vcs.xml | 6 ++++++ 5 files changed, 37 insertions(+) create mode 100644 .idea/.gitignore create mode 100644 .idea/JS_Template.iml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/JS_Template.iml b/.idea/JS_Template.iml new file mode 100644 index 0000000..d6ebd48 --- /dev/null +++ b/.idea/JS_Template.iml @@ -0,0 +1,9 @@ + + + + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..454992c --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..8811d7b --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file From f96f91284d79ef345bc47c5e0dc8121c67e5edc7 Mon Sep 17 00:00:00 2001 From: ValeriiaFrolenko Date: Sat, 21 Jun 2025 18:53:56 +0300 Subject: [PATCH 3/4] hw5 --- src/index.html | 230 ++++++++---------------------- src/main.js | 375 +++++++++++++++++++++++++++++++++++++++++++++++-- src/style.css | 179 ++++++++++++++--------- 3 files changed, 539 insertions(+), 245 deletions(-) diff --git a/src/index.html b/src/index.html index ed4d797..02812b6 100644 --- a/src/index.html +++ b/src/index.html @@ -1,228 +1,116 @@ - - - - - To-Do List - - -
- -

To-Do List

- - - -
- - - - - - -
- - -
- -
- - -
- - -
- - -
-
- - -
- - -
- -
- - -
- -
Complete the project
- -
Medium
-
- - -
- -
May 1, 2024
- -
- - - - -
-
-
- - -
- -
- -
- -
Read the book
- -
Low
-
- -
-
April 25, 2024
-
- - -
-
-
- - -
-
- -
-
Go to the gym
- -
High
-
- -
-
April 23, 2024
-
- - -
-
+
+

To-Do List

+
+ +
- - -
-
- -
-
Make a doctor's appointment
-
Medium
+
+
+
- -
-
April 20, 2024
-
- - -
+
+ +
+
+
- - - - + + + + + - + \ No newline at end of file diff --git a/src/main.js b/src/main.js index 8acd78e..db2611c 100644 --- a/src/main.js +++ b/src/main.js @@ -1,23 +1,378 @@ +let tasks = []; -function openPopup(mode = 'add') { - // Отримуємо посилання на popup overlay елемент +// Відкриває модальне вікно для додавання або редагування завдання +function openPopup(mode = 'add', taskId = null) { const popupElement = document.getElementById('popup'); + const form = document.getElementById('task-form'); + const titleInput = document.getElementById('task-name-input'); + const dateInput = document.getElementById('task-date-input'); + const popupTitle = document.querySelector('.popup-title'); + const quickTaskInput = document.getElementById('quick-task-input'); - // Додаємо клас 'active' який робить popup видимим - // (в CSS є правило .popup-overlay.active { display: flex; }) - popupElement.classList.add('active'); + if (mode === 'edit' && taskId !== null) { + popupTitle.textContent = 'Edit Task'; + const task = tasks.find(t => t.id === taskId); + if (task) { + titleInput.value = task.title; + dateInput.value = task.date; + document.querySelector(`input[name="priority"][value="${task.priority}"]`).checked = true; + form.dataset.taskId = taskId; + } + } else { + popupTitle.textContent = 'Add Task'; + form.reset(); + if (quickTaskInput.value.trim()) { + titleInput.value = quickTaskInput.value; + quickTaskInput.value = ''; + } + delete form.dataset.taskId; + } + + popupElement.showModal(); } +// Закриває модальне вікно і очищує форму function closePopup() { - // Отримуємо посилання на popup overlay елемент const popupElement = document.getElementById('popup'); + popupElement.close(); + document.getElementById('task-form').reset(); +} - // Видаляємо клас 'active' - popup стає невидимим - popupElement.classList.remove('active'); +// Завантажує завдання з localStorage або ініціалізує початковими +function loadTasks() { + const savedTasks = localStorage.getItem('tasks'); + if (savedTasks) { + tasks = JSON.parse(savedTasks); + } else { + tasks = [ + { + "id": 1, + "title": "Complete the project", + "priority": "medium", + "date": "2024-05-01", + "completed": false + }, + { + "id": 2, + "title": "Read the book", + "priority": "low", + "date": "2024-04-25", + "completed": true + }, + { + "id": 3, + "title": "Go to the gym", + "priority": "high", + "date": "2024-04-23", + "completed": false + }, + { + "id": 4, + "title": "Make a doctor's appointment", + "priority": "medium", + "date": "2024-04-20", + "completed": false + } + ]; + localStorage.setItem('tasks', JSON.stringify(tasks)); + } + renderTasks(); } -function saveTask() { - // Поки що просто закриваємо popup +// Зберігає нове завдання або оновлює існуюче в localStorage як JSON +function saveTask(event) { + event.preventDefault(); + const title = document.getElementById('task-name-input').value; + const date = document.getElementById('task-date-input').value; + const priority = document.querySelector('input[name="priority"]:checked').value; + const form = document.getElementById('task-form'); + + if (form.dataset.taskId) { + const taskId = parseInt(form.dataset.taskId); + const taskIndex = tasks.findIndex(t => t.id === taskId); + if (taskIndex !== -1) { + tasks[taskIndex] = { + id: taskId, + title, + priority, + date, + completed: tasks[taskIndex].completed + }; + } + } else { + const newTask = { + id: tasks.length > 0 ? Math.max(...tasks.map(t => t.id)) + 1 : 1, + title, + priority, + date, + completed: false + }; + tasks.push(newTask); + } + + localStorage.setItem('tasks', JSON.stringify(tasks)); + renderTasks(); closePopup(); } +// Перемикає статус завершення завдання +function toggleTaskCompletion(taskId) { + const taskIndex = tasks.findIndex(t => t.id === taskId); + if (taskIndex !== -1) { + tasks[taskIndex].completed = !tasks[taskIndex].completed; + localStorage.setItem('tasks', JSON.stringify(tasks)); + } + renderTasks(); +} + +// Видаляє завдання за ID +function deleteTask(taskId) { + tasks = tasks.filter(t => t.id !== taskId); + localStorage.setItem('tasks', JSON.stringify(tasks)); + renderTasks(); +} + +// Відкриває модальне вікно кастомного фільтру +function openCustomFilterPopup() { + const popupElement = document.getElementById('custom-filter-popup'); + const form = document.getElementById('custom-filter-form'); + const filterTypeSelect = document.getElementById('filter-date-type'); + const dateInput = document.getElementById('custom-filter-date-input'); + + // Завантажуємо збережені налаштування фільтру або встановлюємо сьогоднішню дату + const savedFilterType = localStorage.getItem('currentFilterType') || 'today'; + const savedDate = localStorage.getItem('currentFilterDate') || new Date().toISOString().split('T')[0]; + + filterTypeSelect.value = savedFilterType; + dateInput.value = savedDate; + + updateFilterInputVisibility(); + popupElement.showModal(); +} + +// Закриває модальне вікно кастомного фільтру +function closeCustomFilterPopup() { + const popupElement = document.getElementById('custom-filter-popup'); + popupElement.close(); +} + +// Оновлює видимість поля для введення дати +function updateFilterInputVisibility() { + const filterType = document.getElementById('filter-date-type').value; + const dateInputGroup = document.getElementById('date-input-group'); + const dateInput = document.getElementById('custom-filter-date-input'); + + // Показуємо поле дати для всіх типів фільтрів + dateInputGroup.style.display = 'block'; + + // Якщо дата не встановлена, встановлюємо за замовчуванням + if (!dateInput.value) { + const today = new Date().toISOString().split('T')[0]; + dateInput.value = today; + } +} + +// Функція для оновлення фільтру при зміні типу +function updateFilter() { + updateFilterInputVisibility(); +} + +// Обробка форми кастомного фільтру +function handleCustomFilterForm(event) { + event.preventDefault(); + const filterType = document.getElementById('filter-date-type').value; + const filterDate = document.getElementById('custom-filter-date-input').value; + + // Зберігаємо налаштування фільтру + localStorage.setItem('currentFilterType', filterType); + localStorage.setItem('currentFilterDate', filterDate); + localStorage.setItem('isCustomFilterActive', 'true'); + + closeCustomFilterPopup(); + renderTasks(); + updateFilterButtonText(); +} + +// Оновлює текст кнопки фільтру та видимість кнопки очищення +function updateFilterButtonText() { + const filterBtn = document.querySelector('.filter-btn'); + const clearFilterBtn = document.querySelector('.clear-filter-btn'); + const isActive = localStorage.getItem('isCustomFilterActive') === 'true'; + + if (isActive) { + const filterType = localStorage.getItem('currentFilterType'); + const filterDate = localStorage.getItem('currentFilterDate'); + + let buttonText = 'Filter: '; + if (filterType === 'today') { + buttonText += `Today (${formatDate(filterDate)})`; + } else if (filterType === 'overdue') { + buttonText += `Overdue (before ${formatDate(filterDate)})`; + } else if (filterType === 'upcoming') { + buttonText += `Upcoming (after ${formatDate(filterDate)})`; + } + + filterBtn.textContent = buttonText; + filterBtn.classList.add('active-filter'); + clearFilterBtn.style.display = 'inline-block'; + } else { + filterBtn.textContent = 'Filter by Date'; + filterBtn.classList.remove('active-filter'); + clearFilterBtn.style.display = 'none'; + } +} + +// Форматує дату для відображення +function formatDate(dateString) { + const date = new Date(dateString); + return date.toLocaleDateString('en-GB', { + day: '2-digit', + month: '2-digit', + year: 'numeric' + }); +} + +// Очищає фільтр +function clearFilter() { + localStorage.removeItem('isCustomFilterActive'); + localStorage.removeItem('currentFilterType'); + localStorage.removeItem('currentFilterDate'); + renderTasks(); + updateFilterButtonText(); +} + +// Сортує завдання за вибраним критерієм +function sortTasks(sortBy) { + localStorage.setItem('sortBy', sortBy); + const sortSelect = document.getElementById('sort-select'); + + // Оновлюємо текст вибраного елементу + Array.from(sortSelect.options).forEach(option => { + if (option.value === sortBy) { + option.textContent = `Sort by: ${option.value.charAt(0).toUpperCase() + option.value.slice(1)}`; + } else { + option.textContent = option.value.charAt(0).toUpperCase() + option.value.slice(1); + } + }); + + sortSelect.value = sortBy; + renderTasks(); +} + +// Рендерить завдання у DOM +function renderTasks() { + const tasksContainer = document.getElementById('tasks-container'); + tasksContainer.innerHTML = ''; + + const sortBy = localStorage.getItem('sortBy') || 'date'; + const isCustomFilterActive = localStorage.getItem('isCustomFilterActive') === 'true'; + + let filteredTasks = [...tasks]; + + // Застосовуємо фільтр якщо активний + if (isCustomFilterActive) { + const filterType = localStorage.getItem('currentFilterType'); + const filterDate = localStorage.getItem('currentFilterDate'); + + if (filterType === 'today') { + filteredTasks = tasks.filter(task => task.date === filterDate); + } else if (filterType === 'overdue') { + filteredTasks = tasks.filter(task => task.date < filterDate); + } else if (filterType === 'upcoming') { + filteredTasks = tasks.filter(task => task.date > filterDate); + } + } + + // Сортуємо завдання + const priorityOrder = { high: 1, medium: 2, low: 3 }; + + filteredTasks.sort((a, b) => { + // Спочатку сортуємо за статусом виконання + if (a.completed !== b.completed) { + return a.completed ? 1 : -1; + } + + // Потім за вибраним критерієм + if (sortBy === 'date') { + return new Date(a.date) - new Date(b.date); + } else if (sortBy === 'priority') { + return priorityOrder[a.priority] - priorityOrder[b.priority]; + } else if (sortBy === 'name') { + return a.title.localeCompare(b.title); + } + return 0; + }); + + // Відображаємо завдання + filteredTasks.forEach(task => { + const taskItem = document.createElement('div'); + taskItem.className = 'task-item'; + taskItem.innerHTML = ` +
+
+
${task.title}
+
${task.priority.charAt(0).toUpperCase() + task.priority.slice(1)}
+
+
+
${new Date(task.date).toLocaleDateString('en-GB', { month: 'long', day: 'numeric', year: 'numeric' })}
+
+ + +
+
+ `; + tasksContainer.appendChild(taskItem); + }); + + // Показуємо повідомлення якщо немає завдань + if (filteredTasks.length === 0) { + const noTasksMessage = document.createElement('div'); + noTasksMessage.className = 'no-tasks-message'; + noTasksMessage.style.cssText = ` + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + height: 100%; + text-align: center; + padding: 20px; + `; + noTasksMessage.innerHTML = ` +

No tasks to display

+ ${isCustomFilterActive ? '' : ''} + `; + tasksContainer.appendChild(noTasksMessage); + } +} + +// Ініціалізація після завантаження DOM +document.addEventListener('DOMContentLoaded', () => { + loadTasks(); + + const sortBy = localStorage.getItem('sortBy') || 'date'; + sortTasks(sortBy); + updateFilterButtonText(); + + // Event listeners + document.getElementById('task-form').addEventListener('submit', saveTask); + + document.getElementById('quick-task-input').addEventListener('keypress', (e) => { + if (e.key === 'Enter' && e.target.value.trim()) { + openPopup(); + } + }); + + document.getElementById('sort-select').addEventListener('change', (e) => { + sortTasks(e.target.value); + }); + + document.getElementById('custom-filter-form').addEventListener('submit', handleCustomFilterForm); + + document.getElementById('filter-date-type').addEventListener('change', updateFilter); + + // Додаємо обробник для кнопки очищення фільтру + const clearFilterBtn = document.querySelector('.clear-filter-btn'); + if (clearFilterBtn) { + clearFilterBtn.addEventListener('click', clearFilter); + } +}); \ No newline at end of file diff --git a/src/style.css b/src/style.css index 17840ad..46b19b6 100644 --- a/src/style.css +++ b/src/style.css @@ -17,18 +17,26 @@ body { /* ===== ОСНОВНИЙ КОНТЕЙНЕР ===== */ /* Головний контейнер всього додатку */ .container { - max-width: 800px; /* Максимальна ширина - обмежуємо на великих екранах */ - margin: 0 auto; /* Центруємо по горизонталі */ - padding: 40px 20px; /* Внутрішні відступи: 40px зверху/знизу, 20px з боків */ + max-width: 800px; + margin: 0 auto; + padding: 15px 12px; + display: flex; + flex-direction: column; + height: 100vh; +} + +/* ===== ФІКСОВАНИЙ ЗАГОЛОВОК ===== */ +.fixed-header { + flex-shrink: 0; } /* ===== ЗАГОЛОВОК ДОДАТКУ ===== */ /* Головний заголовок "To-Do List" */ h1 { text-align: center; /* Центруємо текст */ - font-size: 3rem; /* Великий розмір шрифту (48px) */ + font-size: 2.2rem; /* Зменшений розмір шрифту */ font-weight: 700; /* Жирний шрифт */ - margin-bottom: 40px; /* Відступ знизу */ + margin-bottom: 20px; /* Зменшений відступ знизу */ color: #1d1d1f; /* Темно-сірий колір */ } @@ -37,13 +45,13 @@ h1 { .add-task-section { display: flex; /* Flexbox для розташування елементів в ряд */ gap: 12px; /* Проміжок між полем вводу та кнопкою */ - margin-bottom: 30px; /* Відступ знизу від наступної секції */ + margin-bottom: 20px; /* Зменшений відступ знизу */ } /* Поле швидкого вводу завдання */ .task-input { flex: 1; /* Займає всю доступну ширину */ - padding: 16px 20px; /* Внутрішні відступи для зручності */ + padding: 12px 16px; /* Зменшені внутрішні відступи */ border: 2px solid #e5e5e7; /* Сіра рамка */ border-radius: 12px; /* Закруглені кути */ font-size: 16px; /* Розмір шрифту */ @@ -62,9 +70,26 @@ h1 { color: #86868b; /* Сірий колір для підказки */ } +.controls .add-btn { + padding: 10px 14px; + background: white; + color: #1d1d1f; + border: 2px solid #e5e5e7; + border-radius: 8px; + font-size: 14px; + font-weight: 600; + cursor: pointer; + transition: all 0.2s ease; +} + +.controls .add-btn:hover { + border-color: #007aff; + background: #f9f9fb; +} + /* Кнопка "Add" для швидкого додавання */ .add-btn { - padding: 16px 24px; /* Внутрішні відступи */ + padding: 12px 20px; /* Зменшені внутрішні відступи */ background: white; /* Білий фон */ color: #1d1d1f; /* Темний текст */ border: 2px solid #e5e5e7; /* Сіра рамка */ @@ -82,12 +107,29 @@ h1 { background: #f9f9fb; /* Світло-сірий фон */ } +/* Додаткові стилі для кнопки фільтру */ +.filter-btn { + padding: 10px 14px; + background: white; + color: #1d1d1f; + border: 2px solid #e5e5e7; + border-radius: 8px; + font-size: 14px; + cursor: pointer; + transition: all 0.2s ease; +} + +.filter-btn:hover { + border-color: #007aff; + background: #f9f9fb; +} + /* ===== ПАНЕЛЬ УПРАВЛІННЯ (СОРТУВАННЯ/ФІЛЬТРУВАННЯ) ===== */ /* Контейнер для dropdown-ів сортування та фільтрування */ .controls { display: flex; /* Flexbox для горизонтального розташування */ justify-content: space-between; /* Розташовуємо елементи по краях */ - margin-bottom: 30px; /* Відступ знизу */ + margin-bottom: 15px; /* Зменшений відступ знизу */ gap: 20px; /* Проміжок між елементами */ } @@ -104,9 +146,9 @@ h1 { /* Стилі для dropdown селектів */ .select { - padding: 12px 16px; /* Внутрішні відступи */ + padding: 10px 14px; /* Зменшені внутрішні відступи */ border: 2px solid #e5e5e7; /* Сіра рамка */ - border-radius: 8px; /* Закруглені кути (менше ніж у інших елементів) */ + border-radius: 8px; /* Закруглені кути */ background: white; /* Білий фон */ font-size: 14px; /* Розмір шрифту */ cursor: pointer; /* Курсор-рука */ @@ -130,10 +172,33 @@ h1 { /* ===== КОНТЕЙНЕР СПИСКУ ЗАВДАНЬ ===== */ /* Головний контейнер для всіх завдань */ .tasks-container { - background: white; /* Білий фон */ - border-radius: 16px; /* Закруглені кути */ - overflow: hidden; /* Приховуємо зайвий контент */ - box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); /* Тінь для об'ємності */ + background: white; + border-radius: 16px; + overflow-y: auto; + box-shadow: 0 4px 20px rgba(0, 0, 0, 0.08); + flex-grow: 1; + max-height: calc(100vh - 200px); /* Максимальна висота з урахуванням заголовка */ + -webkit-overflow-scrolling: touch; /* Плавний скрол на iOS */ +} + +.tasks-container::-webkit-scrollbar { + width: 8px; + background: transparent; /* Прозорий фон для збереження закруглень */ +} + +.tasks-container::-webkit-scrollbar-track { + background: transparent; /* Прозорий трек */ + margin: 16px 0; /* Відступи зверху/знизу для закруглень */ +} + +.tasks-container::-webkit-scrollbar-thumb { + background: #86868b; + border-radius: 4px; + border: 2px solid white; /* Біла обводка для збереження закруглень */ +} + +.tasks-container::-webkit-scrollbar-thumb:hover { + background: #6b7280; } /* ===== СТИЛІ ОКРЕМОГО ЗАВДАННЯ ===== */ @@ -141,9 +206,10 @@ h1 { .task-item { display: flex; /* Flexbox для горизонтального розташування */ align-items: center; /* Вертикальне центрування всіх елементів */ - padding: 20px; /* Внутрішні відступи */ + padding: 14px 16px; /* Зменшені внутрішні відступи */ border-bottom: 1px solid #f2f2f7; /* Тонка сіра лінія знизу */ transition: background-color 0.2s ease; /* Плавна зміна фону при наведенні */ + min-height: 60px; /* Мінімальна висота завдання */ } /* Прибираємо лінію у останнього завдання */ @@ -159,11 +225,11 @@ h1 { /* ===== ЧЕКБОКС ЗАВДАННЯ ===== */ /* Кастомний чекбокс для позначення виконання завдання */ .task-checkbox { - width: 24px; /* Ширина */ - height: 24px; /* Висота */ + width: 20px; /* Зменшена ширина */ + height: 20px; /* Зменшена висота */ border: 2px solid #d1d1d6; /* Сіра рамка */ border-radius: 50%; /* Робимо круглим */ - margin-right: 16px; /* Відступ справа */ + margin-right: 12px; /* Зменшений відступ справа */ cursor: pointer; /* Курсор-рука */ display: flex; /* Flexbox для центрування галочки */ align-items: center; /* Вертикальне центрування */ @@ -181,7 +247,7 @@ h1 { .task-checkbox.completed::after { content: '✓'; /* Символ галочки */ color: #34c759; /* Зелений колір */ - font-size: 14px; /* Розмір */ + font-size: 12px; /* Зменшений розмір */ font-weight: bold; /* Жирний */ } @@ -191,15 +257,16 @@ h1 { flex: 1; /* Займає всю доступну ширину */ display: flex; /* Flexbox для вертикального розташування */ flex-direction: column; /* Вертикальне розташування елементів */ - gap: 8px; /* Проміжок між назвою та пріоритетом */ + gap: 6px; /* Зменшений проміжок між назвою та пріоритетом */ align-items: flex-start; /* Вирівнювання по лівому краю */ } /* Назва/заголовок завдання */ .task-title { - font-size: 18px; /* Розмір шрифту */ + font-size: 16px; /* Зменшений розмір шрифту */ font-weight: 500; /* Середня жирність */ color: #1d1d1f; /* Темно-сірий колір */ + line-height: 1.3; /* Щільніший міжрядковий інтервал */ } /* Стиль назви виконаного завдання */ @@ -212,12 +279,12 @@ h1 { /* Базові стилі для всіх індикаторів пріоритету */ .task-priority { display: inline-block; /* Інлайн-блок для розміщення поруч */ - padding: 4px 12px; /* Внутрішні відступи */ - border-radius: 12px; /* Закруглені кути (пілюля) */ - font-size: 12px; /* Маленький розмір шрифту */ + padding: 2px 8px; /* Зменшені внутрішні відступи */ + border-radius: 10px; /* Закруглені кути (пілюля) */ + font-size: 10px; /* Менший розмір шрифту */ font-weight: 600; /* Напівжирний */ text-transform: uppercase; /* Великі літери */ - letter-spacing: 0.5px; /* Проміжки між літерами */ + letter-spacing: 0.3px; /* Проміжки між літерами */ } /* Високий пріоритет - червоний */ @@ -243,25 +310,25 @@ h1 { .task-meta { display: flex; /* Flexbox для горизонтального розташування */ align-items: center; /* Вертикальне центрування */ - gap: 16px; /* Проміжок між датою та кнопками */ + gap: 12px; /* Зменшений проміжок між датою та кнопками */ } /* Дата завдання */ .task-date { color: #86868b; /* Сірий колір */ - font-size: 14px; /* Маленький розмір шрифту */ + font-size: 12px; /* Менший розмір шрифту */ } /* Контейнер для кнопок дій (редагування, видалення) */ .task-actions { display: flex; /* Flexbox для горизонтального розташування */ - gap: 8px; /* Проміжок між кнопками */ + gap: 6px; /* Зменшений проміжок між кнопками */ } /* Базові стилі для кнопок дій */ .action-btn { - width: 32px; /* Ширина */ - height: 32px; /* Висота */ + width: 28px; /* Зменшена ширина */ + height: 28px; /* Зменшена висота */ border: none; /* Без рамки */ background: transparent; /* Прозорий фон */ cursor: pointer; /* Курсор-рука */ @@ -280,45 +347,33 @@ h1 { /* Іконка для кнопки редагування */ .edit-btn::after { content: '✏️'; /* Emoji олівця */ - font-size: 14px; /* Розмір */ + font-size: 12px; /* Зменшений розмір */ } /* Іконка для кнопки видалення */ .delete-btn::after { content: '🗑️'; /* Emoji смітника */ - font-size: 14px; /* Розмір */ + font-size: 12px; /* Зменшений розмір */ } -/* ===== МОДАЛЬНЕ ВІКНО (POPUP) ===== */ -/* Затемнений фон за модальним вікном */ .popup-overlay { - position: fixed; /* Фіксована позиція відносно вікна */ - top: 0; /* Прилипає до верху */ - left: 0; /* Прилипає до лівого краю */ - width: 100%; /* На всю ширину екрану */ - height: 100%; /* На всю висоту екрану */ - background: rgba(0, 0, 0, 0.5); /* Півпрозорий чорний фон */ - display: none; /* Приховано за замовчуванням */ - align-items: center; /* Вертикальне центрування popup */ - justify-content: center; /* Горизонтальне центрування popup */ - z-index: 1000; /* Високий z-index для показу поверх всього */ - backdrop-filter: blur(4px); /* Розмиття фону */ -} - -/* Активний стан overlay (коли popup відкритий) */ -.popup-overlay.active { - display: flex; /* Показуємо через flexbox */ + background: none; /* Прибираємо кастомний фон, щоб використовувати нативний backdrop */ + border: none; /* Прибираємо рамку, якщо є */ + padding: 0; /* Без відступів, щоб не впливати на вміст */ + max-width: none; /* Дозволяємо діалогу займати потрібну ширину */ + width: 100%; /* На всю ширину екрану */ + height: 100%; /* На всю висоту екрану */ } /* Саме модальне вікно */ .popup { - background: white; /* Білий фон */ - border-radius: 20px; /* Закруглені кути */ - padding: 32px; /* Внутрішні відступи */ - width: 90%; /* 90% ширини екрану */ - max-width: 500px; /* Максимальна ширина */ - position: relative; /* Для позиціювання дочірніх елементів */ - box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15); /* Тінь для об'ємності */ + background: white; + border-radius: 20px; + padding: 32px; + width: 90%; + max-width: 500px; + margin: auto; /* Центруємо по горизонталі та вертикалі */ + box-shadow: 0 20px 40px rgba(0, 0, 0, 0.15); } /* ===== ЗАГОЛОВОК POPUP ===== */ @@ -489,14 +544,10 @@ h1 { /* Стилі для екранів менше 768px */ @media (max-width: 768px) { - /* Зменшуємо відступи контейнера */ - .container { - padding: 20px 16px; /* Менші відступи */ - } - /* Зменшуємо розмір заголовка */ h1 { - font-size: 2.5rem; /* Менший розмір шрифту */ + font-size: 2rem; /* Менший розмір шрифту */ + margin-bottom: 15px; /* Менший відступ */ } /* Зменшуємо проміжки між елементами управління */ From b8232dd804d570c74cd0eadf9c77be6386aa81e6 Mon Sep 17 00:00:00 2001 From: ValeriiaFrolenko Date: Mon, 23 Jun 2025 10:51:08 +0300 Subject: [PATCH 4/4] bonus --- .idea/JS_Template.iml | 2 + .idea/jsLibraryMappings.xml | 6 + index.html | 13 - src/analytics.html | 64 +++ src/analytics.js | 156 ++++++++ src/index.html | 59 ++- src/main.js | 233 ++++++++--- src/style.css | 762 ++++++++++++++++++++---------------- 8 files changed, 889 insertions(+), 406 deletions(-) create mode 100644 .idea/jsLibraryMappings.xml delete mode 100644 index.html create mode 100644 src/analytics.html create mode 100644 src/analytics.js diff --git a/.idea/JS_Template.iml b/.idea/JS_Template.iml index d6ebd48..3ad4ebd 100644 --- a/.idea/JS_Template.iml +++ b/.idea/JS_Template.iml @@ -5,5 +5,7 @@ + + \ No newline at end of file diff --git a/.idea/jsLibraryMappings.xml b/.idea/jsLibraryMappings.xml new file mode 100644 index 0000000..3565144 --- /dev/null +++ b/.idea/jsLibraryMappings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/index.html b/index.html deleted file mode 100644 index b2f535d..0000000 --- a/index.html +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - Simple HTML Page - - - - - - - \ No newline at end of file diff --git a/src/analytics.html b/src/analytics.html new file mode 100644 index 0000000..05a755d --- /dev/null +++ b/src/analytics.html @@ -0,0 +1,64 @@ + + + + + + Task Analytics + + + + + + + + +
+ +
+ +

📊 Task Analytics

+ + + + + + +
+ +
+
+ + +
+ + +
+ +
+
+
+ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/src/analytics.js b/src/analytics.js new file mode 100644 index 0000000..e39be57 --- /dev/null +++ b/src/analytics.js @@ -0,0 +1,156 @@ +/** + * Ініціалізація статистичної панелі та pivot таблиці після завантаження DOM + */ +document.addEventListener('DOMContentLoaded', () => { + // Завантажуємо завдання з localStorage з fallback на порожній масив + const tasks = JSON.parse(localStorage.getItem('tasks')) || []; + + // Розраховуємо ключові метрики для швидкого огляду продуктивності користувача + + const totalTasks = tasks.length; // Загальна кількість завдань + + // filter() створює новий масив з елементів що відповідають умові + // Більш читабельно ніж циклі for або reduce для простого підрахунку + const completedTasks = tasks.filter(t => t.completed).length; + + // Обчислюємо кількість незавершених завдань через віднімання + const pendingTasks = totalTasks - completedTasks; + + // Розраховуємо відсоток завершення з округленням до цілого числа + // Перевіряємо на ділення на нуль для уникнення NaN + const completionRate = totalTasks > 0 ? Math.round((completedTasks / totalTasks) * 100) : 0; + + // Підраховуємо завдання з високим пріоритетом для виділення критичних задач + const highPriorityTasks = tasks.filter(t => t.priority === 'high').length; + + // ===== ВІДОБРАЖЕННЯ СТАТИСТИЧНИХ КАРТОК ===== + // Використовуємо innerHTML для швидкого створення статичного контенту + // Template literals забезпечують чисту інтерполяцию змінних + document.getElementById('stats-bar').innerHTML = ` +
+
${totalTasks}
+
Total Tasks
+
+
+
${completedTasks}
+
Completed
+
+
+
${pendingTasks}
+
Pending
+
+
+
${completionRate}%
+
Progress
+
+
+
${highPriorityTasks}
+
High Priority
+
+ `; + + // Трансформуємо сирі дані завдань у структуру придатну для аналітики + // map() створює новий масив з трансформованими об'єктами + const reportData = tasks.map(task => { + // Створюємо об'єкт Date для роботи з датою завдання + const dateObj = new Date(task.date); + + // Витягуємо компоненти дати для різних рівнів групування + const year = dateObj.getFullYear(); + const month = dateObj.getMonth() + 1; // +1 оскільки getMonth() повертає 0-11 + const day = dateObj.getDate(); + + // Отримуємо назви днів тижня та місяців для кращої читабельності + const dayName = dateObj.toLocaleDateString('en-US', { weekday: 'short' }); + const monthName = dateObj.toLocaleDateString('en-US', { month: 'long' }); + + // Мапінг пріоритетів для відображення з великої літери + const priorityMap = { 'high': 'High', 'medium': 'Medium', 'low': 'Low' }; + + // Повертаємо структуровані дані для pivot таблиці + // Кожне поле стане доступним для групування та аналізу + return { + 'Month': `${monthName} ${year}`, // Комбінована мітка для місячного групування + 'Day': `${day} (${dayName})`, // День з назвою дня тижня для контексту + 'Task': task.title, // Назва завдання для деталізації + 'Priority': priorityMap[task.priority], // Пріоритет з правильним форматуванням + 'Status': task.completed ? 'Completed' : 'Pending', // Статус у зрозумілому форматі + 'Count': 1 // Лічильник для агрегації (кожне завдання = 1 одиниця) + }; + }); + + // WebDataRocks - стороння бібліотека для створення інтерактивних pivot таблиць + const pivot = new WebDataRocks({ + container: '#pivot-container', // CSS селектор контейнера + toolbar: true, // Показуємо панель інструментів для користувача + height: 500, // Фіксована висота для консистентного вигляду + + report: { + // Джерело даних - наш трансформований масив + dataSource: { data: reportData }, + + // Конфігурація структури pivot таблиці + slice: { + // Рядки - ієрархічне групування даних + // collapsed: false/true контролює початковий стан розгортання + rows: [ + { uniqueName: 'Month', collapsed: false }, // Місяці розгорнуті за замовчанням + { uniqueName: 'Day', collapsed: true }, // Дні згорнуті для компактності + { uniqueName: 'Task', collapsed: true }, // Індивідуальні завдання згорнуті + { uniqueName: 'Priority', collapsed: true } // Пріоритети згорнуті + ], + + // Стовпці - горизонтальне групування + columns: [ + { uniqueName: 'Status' } // Розділення на Completed/Pending + ], + + // Метрики - що рахуємо та як відображаємо + measures: [ + { + uniqueName: 'Count', + aggregation: 'sum', // Сумуємо значення Count (кількість завдань) + format: 'count' // Використовуємо кастомний формат для відображення + } + ] + }, + + // Налаштування форматування чисел + formats: [ + { + name: 'count', + decimalPlaces: 0, // Цілі числа без десяткових знаків + thousandsSeparator: '', // Без розділювача тисяч для простоти + currencySymbol: '' // Без валютних символів + } + ], + + // Додаткові опції відображення + options: { + grid: { + type: 'classic', // Класичний вигляд таблиці + showTotals: 'off', // Вимикаємо проміжні підсумки для чистоти + showGrandTotals: 'off', // Вимикаємо загальні підсумки + dragging: true, // Дозволяємо перетягування полів для реорганізації + showEmptyData: false // Приховуємо порожні комірки + } + }, + + // Локалізація текстових міток + localization: { + grid: { + blankMember: '(Empty)', // Мітка для порожніх значень + total: 'Total', // Мітка для підсумків + grandTotal: 'Grand Total' // Мітка для загальних підсумків + }, + toolbar: { + connect: 'Connect', // Кнопка підключення до джерел даних + clear: 'Clear', // Кнопка очищення + format: 'Format', // Кнопка форматування + export: 'Export', // Кнопка експорту + settings: 'Settings' // Кнопка налаштувань + } + } + } + }); +}); \ No newline at end of file diff --git a/src/index.html b/src/index.html index 02812b6..f89ce2f 100644 --- a/src/index.html +++ b/src/index.html @@ -7,59 +7,68 @@ +
+
+

To-Do List

+
- + + + + + 📊 Analytics
+
+
+
+
+ +
-
-
+ +
+ + - + +