diff --git a/backend/db.py b/backend/db.py index 281ce5b..e48f3b9 100644 --- a/backend/db.py +++ b/backend/db.py @@ -151,6 +151,18 @@ def setup_db() -> None: FOREIGN KEY (user_id) REFERENCES users(id), FOREIGN KEY (notification_service) REFERENCES notification_services(id) ); + CREATE TABLE IF NOT EXISTS static_reminders( + id INTEGER PRIMARY KEY, + user_id INTEGER NOT NULL, + title VARCHAR(255) NOT NULL, + text TEXT, + notification_service INTEGER NOT NULL, + + color VARCHAR(7), + + FOREIGN KEY (user_id) REFERENCES users(id), + FOREIGN KEY (notification_service) REFERENCES notification_services(id) + ); CREATE TABLE IF NOT EXISTS config( key VARCHAR(255) PRIMARY KEY, value TEXT NOT NULL diff --git a/backend/notification_service.py b/backend/notification_service.py index 4d6a1af..130d3f6 100644 --- a/backend/notification_service.py +++ b/backend/notification_service.py @@ -96,7 +96,15 @@ class NotificationService: ) if cursor.fetchone(): raise NotificationServiceInUse('template') - + + # Check if no static reminders exist with this service + cursor.execute( + "SELECT 1 FROM static_reminders WHERE notification_service = ? LIMIT 1;", + (self.id,) + ) + if cursor.fetchone(): + raise NotificationServiceInUse('static reminder') + cursor.execute( "DELETE FROM notification_services WHERE id = ?", (self.id,) diff --git a/backend/static_reminders.py b/backend/static_reminders.py new file mode 100644 index 0000000..c390578 --- /dev/null +++ b/backend/static_reminders.py @@ -0,0 +1,209 @@ +#-*- coding: utf-8 -*- + +from sqlite3 import IntegrityError +from typing import List + +from apprise import Apprise + +from backend.custom_exceptions import NotificationServiceNotFound, ReminderNotFound +from backend.db import get_db + + +class StaticReminder: + """Represents a static reminder + """ + def __init__(self, reminder_id: int) -> None: + self.id = reminder_id + + # Check if reminder exists + if not get_db().execute( + "SELECT 1 FROM static_reminders WHERE id = ? LIMIT 1;", + (self.id,) + ).fetchone(): + raise ReminderNotFound + + def get(self) -> dict: + """Get info about the static reminder + + Returns: + dict: The info about the reminder + """ + reminder = get_db(dict).execute(""" + SELECT + r.id, + r.title, r.text, + r.notification_service, + ns.title AS notification_service_title, + r.color + FROM static_reminders r + INNER JOIN notification_services ns + ON r.notification_service = ns.id + WHERE r.id = ? + LIMIT 1; + """, + (self.id,) + ).fetchone() + + return dict(reminder) + + def update( + self, + title: str = None, + notification_service: int = None, + text: str = None, + color: str = None + ) -> dict: + """Edit the static reminder + + Args: + title (str, optional): The new title of the entry. Defaults to None. + notification_service (int, optional): The new id of the notification service to use to send the reminder. Defaults to None. + text (str, optional): The new body of the reminder. Defaults to None. + color (str, optional): The new hex code of the color of the reminder, which is shown in the web-ui. Defaults to None. + + Raises: + NotificationServiceNotFound: The notification service with the given id was not found + + Returns: + dict: The new static reminder info + """ + # Get current data and update it with new values + data = self.get() + new_values = { + 'title': title, + 'notification_service': notification_service, + 'text': text, + 'color': color + } + for k, v in new_values.items(): + if k == 'color' or v is not None: + data[k] = v + + # Update database + try: + get_db().execute(""" + UPDATE static_reminders + SET + title = ?, text = ?, + notification_service = ?, + color = ? + WHERE id = ?; + """, + (data['title'], data['text'], + data['notification_service'], + data['color'], + self.id) + ) + except IntegrityError: + raise NotificationServiceNotFound + + return self.get() + + def delete(self) -> None: + """Delete the static reminder + """ + get_db().execute("DELETE FROM static_reminders WHERE id = ?", (self.id,)) + return + +class StaticReminders: + """Represents the static reminder library of the user account + """ + + def __init__(self, user_id: int) -> None: + self.user_id = user_id + + def fetchall(self) -> List[dict]: + """Get all static reminders + + Returns: + List[dict]: The id, title, text, notification_service, notification_service_title and color of each static reminder + """ + reminders: list = list(map( + dict, + get_db(dict).execute(""" + SELECT + r.id, + r.title, r.text, + r.notification_service, + ns.title AS notification_service_title, + r.color + FROM static_reminders r + INNER JOIN notification_services ns + ON r.notification_service = ns.id + WHERE r.user_id = ? + ORDER BY r.title, r.id; + """, + (self.user_id,) + ) + )) + + return reminders + + def fetchone(self, id: int) -> StaticReminder: + """Get one static reminder + + Args: + id (int): The id of the static reminder to fetch + + Returns: + StaticReminder: A StaticReminder instance + """ + return StaticReminder(id) + + def add( + self, + title: str, + notification_service: int, + text: str = '', + color: str = None + ) -> StaticReminder: + """Add a static reminder + + Args: + title (str): The title of the entry + notification_service (int): The id of the notification service to use to send the reminder. + text (str, optional): The body of the reminder. Defaults to ''. + color (str, optional): The hex code of the color of the reminder, which is shown in the web-ui. Defaults to None. + + Raises: + NotificationServiceNotFound: The notification service with the given id was not found + + Returns: + StaticReminder: A StaticReminder instance representing the newly created static reminder + """ + try: + id = get_db().execute(""" + INSERT INTO static_reminders(user_id, title, text, notification_service, color) + VALUES (?,?,?,?,?); + """, + (self.user_id, title, text, notification_service, color) + ).lastrowid + except IntegrityError: + raise NotificationServiceNotFound + + return self.fetchone(id) + + def trigger_reminder(self, id: int) -> None: + """Trigger a static reminder to send it's reminder + + Args: + id (int): The id of the static reminder to trigger + + Raises: + ReminderNotFound: The static reminder with the given id was not found + """ + reminder = get_db(dict).execute(""" + SELECT r.title, r.text, ns.url + FROM static_reminders r + INNER JOIN notification_services ns + ON r.notification_service = ns.id + WHERE r.id = ?; + """, (id,)).fetchone() + if not reminder: + raise ReminderNotFound + reminder = dict(reminder) + + a = Apprise() + a.add(reminder['url']) + a.notify(title=reminder['title'], body=reminder['text']) + return diff --git a/backend/users.py b/backend/users.py index e267861..e107fbd 100644 --- a/backend/users.py +++ b/backend/users.py @@ -6,6 +6,7 @@ from backend.db import get_db from backend.notification_service import NotificationServices from backend.reminders import Reminders from backend.security import generate_salt_hash, get_hash +from backend.static_reminders import StaticReminders from backend.templates import Templates ONEPASS_USERNAME_CHARACTERS = 'abcedfghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-.!@$' @@ -64,6 +65,17 @@ class User: self.templates_instance = Templates(self.user_id) return self.templates_instance + @property + def static_reminders(self) -> StaticReminders: + """Get access to the static reminders of the user account + + Returns: + StaticReminders: StaticReminders instance that can be used to access the static reminders of the user account + """ + if not hasattr(self, 'static_reminders_instance'): + self.static_reminders_instance = StaticReminders(self.user_id) + return self.static_reminders_instance + def edit_password(self, new_password: str) -> None: """Change the password of the account diff --git a/frontend/api.py b/frontend/api.py index 8119e8f..b22b18b 100644 --- a/frontend/api.py +++ b/frontend/api.py @@ -1,9 +1,9 @@ #-*- coding: utf-8 -*- from os import urandom +from re import compile from time import time as epoch_time from typing import Any, Tuple -from re import compile from flask import Blueprint, g, request @@ -16,6 +16,7 @@ from backend.custom_exceptions import (AccessUnauthorized, InvalidKeyValue, from backend.notification_service import (NotificationService, NotificationServices) from backend.reminders import Reminders, reminder_handler, test_reminder +from backend.static_reminders import StaticReminders from backend.templates import Template, Templates from backend.users import User, register_user @@ -243,7 +244,7 @@ def api_add_user(): user_id = register_user(username, password) return return_api({'user_id': user_id}, code=201) -@api.route('/user', methods=['PUT','DELETE']) +@api.route('/user', methods=['PUT', 'DELETE']) @error_handler @auth def api_manage_user(): @@ -287,7 +288,7 @@ def api_manage_user(): # Notification service endpoints #=================== -@api.route('/notificationservices', methods=['GET','POST']) +@api.route('/notificationservices', methods=['GET', 'POST']) @error_handler @auth def api_notification_services_list(): @@ -387,7 +388,7 @@ def api_notification_service(n_id: int): # Library endpoints #=================== -@api.route('/reminders', methods=['GET','POST']) +@api.route('/reminders', methods=['GET', 'POST']) @error_handler @auth def api_reminders_list(): @@ -498,7 +499,7 @@ def api_test_reminder(): test_reminder(title, notification_service, text) return return_api({}, code=201) -@api.route('/reminders/', methods=['GET','PUT','DELETE']) +@api.route('/reminders/', methods=['GET', 'PUT', 'DELETE']) @error_handler @auth def api_get_reminder(r_id: int): @@ -677,3 +678,122 @@ def api_get_template(t_id: int): elif request.method == 'DELETE': template.delete() return return_api({}) + +#=================== +# Static reminder endpoints +#=================== + +@api.route('/staticreminders', methods=['GET', 'POST']) +@error_handler +@auth +def api_static_reminders_list(): + """ + Endpoint: /staticreminders + Description: Manage the static reminders + Requires being logged in: Yes + Methods: + GET: + Description: Get a list of all static reminders + Returns: + 200: + The id, title, text, notification_service, notification_service_title and color of each static reminder + POST: + Description: Add a static reminder + Parameters (body): + title (required): the title of the static reminder + notification_service (required): the id of the notification service to use to send the notification + text: the body of the static reminder + color: The hex code of the color of the static reminder, which is shown in the web-ui + Returns: + 200: + The info about the new static reminder entry + 400: + KeyNotFound: One of the required parameters was not given + """ + reminders: StaticReminders = g.user_data.static_reminders + + if request.method == 'GET': + result = reminders.fetchall() + return return_api(result) + + elif request.method == 'POST': + data = request.get_json() + title = extract_key(data, 'title') + notification_service = extract_key(data, 'notification_service') + text = extract_key(data, 'text', check_existence=False) + color = extract_key(data, 'color', check_existence=False) + + result = reminders.add(title=title, + notification_service=notification_service, + text=text, + color=color) + return return_api(result.get(), code=201) + +@api.route('/staticreminders/', methods=['GET', 'POST', 'PUT', 'DELETE']) +@error_handler +@auth +def api_get_static_reminder(r_id: int): + """ + Endpoint: /staticreminders/ + Description: Manage a specific static reminder + Requires being logged in: Yes + URL Parameters: + : + The id of the static reminder + Methods: + GET: + Returns: + 200: + All info about the static reminder + 404: + No static reminder found with the given id + POST: + Description: Trigger the static reminder + Returns: + 200: + Static reminder triggered successfully + PUT: + Description: Edit the static reminder + Parameters (body): + title: The new title of the static reminder. + notification_service: The new id of the notification service to use to send the reminder. + text: The new body of the static reminder. + color: The new hex code of the color of the static reminder, which is shown in the web-ui. + Returns: + 200: + Static reminder updated successfully + 404: + No static reminder found with the given id + DELETE: + Description: Delete the static reminder + Returns: + 200: + Static reminder deleted successfully + 404: + No static reminder found with the given id + """ + reminders: StaticReminders = g.user_data.static_reminders + if request.method == 'GET': + result = reminders.fetchone(r_id).get() + return return_api(result) + + elif request.method == 'POST': + reminders.trigger_reminder(r_id) + return return_api({}) + + elif request.method == 'PUT': + data = request.get_json() + title = extract_key(data, 'title', check_existence=False) + notification_service = extract_key(data, 'notification_service', check_existence=False) + text = extract_key(data, 'text', check_existence=False) + color = extract_key(data, 'color', check_existence=False) + + result = reminders.fetchone(r_id).update(title=title, + notification_service=notification_service, + text=text, + color=color) + return return_api(result) + + elif request.method == 'DELETE': + reminders.fetchone(r_id).delete() + return return_api({}) diff --git a/frontend/static/css/info.css b/frontend/static/css/info.css index 6ca1146..456aa54 100644 --- a/frontend/static/css/info.css +++ b/frontend/static/css/info.css @@ -167,6 +167,31 @@ div.options > button { } } +/* */ +/* Adding */ +/* */ +#info.show-add-reminder #delete-info { + display: none; +} + +#info.show-add-static-reminder #time-input, +#info.show-add-static-reminder #normal-button, +#info.show-add-static-reminder #repeat-button, +#info.show-add-static-reminder .repeat-bar, +#info.show-add-static-reminder #delete-info { + display: none; +} + +#info.show-add-static-reminder #text-input { + margin-top: -1rem; +} + +#info.show-add-static-reminder #template-selection, +#info.show-add-static-reminder #color-toggle, +#info.show-add-static-reminder #notification-service-input { + width: 100%; +} + #info.show-add-template #template-selection, #info.show-add-template #time-input, #info.show-add-template #normal-button, @@ -181,15 +206,14 @@ div.options > button { margin-top: -1rem; } -#info.show-add-template form > *, -#info.show-add-template .sub-inputs > * { +#info.show-add-template #color-toggle, +#info.show-add-template #notification-service-input { width: 100%; } -#info.show-add-reminder #delete-info { - display: none; -} - +/* */ +/* Editing */ +/* */ #info.show-edit-reminder #template-selection, #info.show-edit-reminder #test-reminder { display: none; @@ -199,6 +223,23 @@ div.options > button { width: 100%; } +#info.show-edit-static-reminder #template-selection, +#info.show-edit-static-reminder #time-input, +#info.show-edit-static-reminder #normal-button, +#info.show-edit-static-reminder #repeat-button, +#info.show-edit-static-reminder .repeat-bar { + display: none; +} + +#info.show-edit-static-reminder #text-input { + margin-top: -1rem; +} + +#info.show-edit-static-reminder #color-toggle, +#info.show-edit-static-reminder #notification-service-input { + width: 100%; +} + #info.show-edit-template #template-selection, #info.show-edit-template #time-input, #info.show-edit-template #normal-button, diff --git a/frontend/static/css/library.css b/frontend/static/css/library.css index 5d525b8..80562e9 100644 --- a/frontend/static/css/library.css +++ b/frontend/static/css/library.css @@ -47,6 +47,7 @@ /* REMINDER LIST */ #reminder-list, +#static-reminder-list, #template-list { --gap: 1rem; --entry-width: 13rem; @@ -115,11 +116,6 @@ button.entry.fit { font-weight: 500; } -#delete-template { - border-color: var(--color-error); - color: var(--color-error); -} - @media (max-width: 543px) { header > div { transform: translateX(0); diff --git a/frontend/static/js/general.js b/frontend/static/js/general.js index f083811..2481954 100644 --- a/frontend/static/js/general.js +++ b/frontend/static/js/general.js @@ -1,5 +1,6 @@ const types = { 'reminder': document.getElementById('reminder-list'), + 'static_reminder': document.getElementById('static-reminder-list'), 'template': document.getElementById('template-list') }; @@ -9,7 +10,10 @@ const icons = { 'delete': '' }; -const info_classes = ['show-add-reminder', 'show-add-template', 'show-edit-reminder', 'show-edit-template']; +const info_classes = [ + 'show-add-reminder', 'show-add-template', 'show-add-static-reminder', + 'show-edit-reminder', 'show-edit-template', 'show-edit-static-reminder' +]; function toggleNav() { document.querySelector('.nav-divider').classList.toggle('show-nav'); diff --git a/frontend/static/js/library.js b/frontend/static/js/library.js index a628fb5..3f9d990 100644 --- a/frontend/static/js/library.js +++ b/frontend/static/js/library.js @@ -16,7 +16,7 @@ function showTab(button) { // function fillTable(table, results) { table.querySelectorAll('button.entry:not(.add-entry)').forEach(e => e.remove()); - + results.forEach(r => { const entry = document.createElement('button'); entry.classList.add('entry'); @@ -75,6 +75,10 @@ function fillReminders() { fillLibrary(`/api/reminders?api_key=${api_key}`, types.reminder); }; +function fillStaticReminders() { + fillLibrary(`/api/staticreminders?api_key=${api_key}`, types.static_reminder); +} + function fillTemplates() { fillLibrary(`/api/templates?api_key=${api_key}`, types.template); }; @@ -110,6 +114,7 @@ document.querySelectorAll('.tab-selector > button').forEach(b => { }); fillReminders(); +fillStaticReminders(); fillTemplates(); setInterval(fillReminders, 60000); diff --git a/frontend/static/js/show.js b/frontend/static/js/show.js index d8a5952..25ccf7e 100644 --- a/frontend/static/js/show.js +++ b/frontend/static/js/show.js @@ -6,6 +6,7 @@ function showAdd(type) { inputs.notification_service.value = document.querySelector('#notification-service-input option[selected]').value; toggleNormal(); toggleColor(true); + document.getElementById('test-reminder').classList.remove('show-sent'); const cl = document.getElementById('info').classList; cl.forEach(c => { @@ -15,10 +16,17 @@ function showAdd(type) { if (type === types.reminder) { cl.add('show-add-reminder'); document.querySelector('#info h2').innerText = 'Add a reminder'; + document.querySelector('#test-reminder > div:first-child').innerText = 'Test'; inputs.time.setAttribute('required', ''); } else if (type === types.template) { cl.add('show-add-template'); document.querySelector('#info h2').innerText = 'Add a template'; + document.querySelector('#test-reminder > div:first-child').innerText = 'Test'; + inputs.time.removeAttribute('required'); + } else if (type === types.static_reminder) { + cl.add('show-add-static-reminder'); + document.querySelector('#info h2').innerText = 'Add a static reminder'; + document.querySelector('#test-reminder > div:first-child').innerText = 'Test'; inputs.time.removeAttribute('required'); } else return; @@ -34,6 +42,11 @@ function showEdit(id, type) { url = `${url_prefix}/api/templates/${id}?api_key=${api_key}`; inputs.time.removeAttribute('required'); type_buttons.repeat_interval.removeAttribute('required'); + } else if (type === types.static_reminder) { + url = `${url_prefix}/api/staticreminders/${id}?api_key=${api_key}`; + document.getElementById('test-reminder').classList.remove('show-sent'); + inputs.time.removeAttribute('required'); + type_buttons.repeat_interval.removeAttribute('required'); } else return; fetch(url) @@ -89,14 +102,26 @@ function showEdit(id, type) { if (type === types.reminder) { cl.add('show-edit-reminder'); document.querySelector('#info h2').innerText = 'Edit a reminder'; + document.querySelector('#test-reminder > div:first-child').innerText = 'Test'; } else if (type === types.template) { cl.add('show-edit-template'); document.querySelector('#info h2').innerText = 'Edit a template'; + document.querySelector('#test-reminder > div:first-child').innerText = 'Test'; + } else if (type === types.static_reminder) { + cl.add('show-edit-static-reminder'); + document.querySelector('#info h2').innerText = 'Edit a static reminder'; + document.querySelector('#test-reminder > div:first-child').innerText = 'Trigger'; } else return; }; // code run on load -document.getElementById('add-reminder').addEventListener('click', e => showAdd(types.reminder)); +document.getElementById('add-reminder').addEventListener('click', e => { + if (document.getElementById('add-reminder').classList.contains('error')) + showWindow('notification'); + else + showAdd(types.reminder); +}); +document.getElementById('add-static-reminder').addEventListener('click', e => showAdd(types.static_reminder)); document.getElementById('add-template').addEventListener('click', e => showAdd(types.template)); diff --git a/frontend/static/js/templates.js b/frontend/static/js/templates.js index e75e90a..ee9c7a1 100644 --- a/frontend/static/js/templates.js +++ b/frontend/static/js/templates.js @@ -58,136 +58,3 @@ function applyTemplate() { // code run on load loadTemplateSelection(); - -// function addTemplate() { -// const data = { -// 'title': template_inputs.title.value, -// 'notification_service': template_inputs["notification-service"].value, -// 'text': template_inputs.text.value, -// 'color': null -// }; -// if (!template_inputs.color.classList.contains('hidden')) { -// data['color'] = template_inputs.color.querySelector('button[data-selected="true"]').dataset.color; -// }; -// fetch(`${url_prefix}/api/templates?api_key=${api_key}`, { -// 'method': 'POST', -// 'headers': {'Content-Type': 'application/json'}, -// 'body': JSON.stringify(data) -// }) -// .then(response => { -// // catch errors -// if (!response.ok) { -// return Promise.reject(response.status); -// }; - -// loadTemplateSelection(); -// closeAddTemplate(); -// return -// }) -// .catch(e => { -// if (e === 401) { -// window.location.href = `${url_prefix}/`; -// } else { -// console.log(e); -// }; -// }); -// }; - -// function closeAddTemplate() { -// hideWindow(); -// setTimeout(() => { -// template_inputs.title.value = ''; -// template_inputs['notification-service'].value = document.querySelector('#notification-service-template-input option[selected]').value; -// template_inputs.text.value = ''; -// if (!template_inputs.color.classList.contains('hidden')) { -// toggleColor(inputs.color); -// }; -// }, 500); -// }; - -// function showEditTemplate(id) { -// fetch(`${url_prefix}/api/templates/${id}?api_key=${api_key}`) -// .then(response => { -// // catch errors -// if (!response.ok) { -// return Promise.reject(response.status); -// }; -// return response.json(); -// }) -// .then(json => { -// document.getElementById('template-edit-form').dataset.id = id; -// edit_template_inputs.title.value = json.result.title; -// edit_template_inputs['notification-service'].value = json.result.notification_service; -// edit_template_inputs.text.value = json.result.text; -// if (json.result.color !== null) { -// if (edit_template_inputs.color.classList.contains('hidden')) { -// toggleColor(edit_template_inputs.color); -// }; -// selectColor(edit_template_inputs.color, json.result.color); -// }; -// showWindow('edit-template'); -// }) -// .catch(e => { -// if (e === 401) { -// window.location.href = `${url_prefix}/`; -// } else { -// console.log(e); -// }; -// }); -// }; - -// function saveTemplate() { -// const id = document.getElementById('template-edit-form').dataset.id; -// const data = { -// 'title': edit_template_inputs.title.value, -// 'notification_service': edit_template_inputs['notification-service'].value, -// 'text': edit_template_inputs.text.value, -// 'color': null -// }; -// if (!edit_template_inputs.color.classList.contains('hidden')) { -// data['color'] = edit_template_inputs.color.querySelector('button[data-selected="true"]').dataset.color; -// }; -// fetch(`${url_prefix}/api/templates/${id}?api_key=${api_key}`, { -// 'method': 'PUT', -// 'headers': {'Content-Type': 'application/json'}, -// 'body': JSON.stringify(data) -// }) -// .then(response => { -// // catch errors -// if (!response.ok) { -// return Promise.reject(response.status); -// }; -// loadTemplateSelection(); -// hideWindow(); -// }) -// .catch(e => { -// if (e === 401) { -// window.location.href = `${url_prefix}/`; -// } else { -// console.log(e); -// }; -// }); -// }; - -// function deleteTemplate() { -// const id = document.getElementById('template-edit-form').dataset.id; -// fetch(`${url_prefix}/api/templates/${id}?api_key=${api_key}`, { -// 'method': 'DELETE' -// }) -// .then(response => { -// // catch errors -// if (!response.ok) { -// return Promise.reject(response.status); -// }; - -// loadTemplateSelection(); -// hideWindow(); -// }) -// .catch(e => { -// if (e === 401) { -// window.location.href = `${url_prefix}/`; -// } else { -// console.log(e); -// }; -// }); -// }; diff --git a/frontend/static/js/window.js b/frontend/static/js/window.js index 74c1f56..5751efd 100644 --- a/frontend/static/js/window.js +++ b/frontend/static/js/window.js @@ -63,24 +63,35 @@ function toggleRepeated() { function testReminder() { const input = document.getElementById('test-reminder'); - if (inputs.title.value === '') { - input.classList.add('error-input'); - input.title = 'No title set'; - return - } else { - input.classList.remove('error-input'); - input.removeAttribute('title'); - }; - const data = { - 'title': inputs.title.value, - 'notification_service': inputs.notification_service.value, - 'text': inputs.text.value - }; - fetch(`${url_prefix}/api/reminders/test?api_key=${api_key}`, { + const cl = document.getElementById('info').classList; + const r_id = document.getElementById('info').dataset.id; + const headers = { 'method': 'POST', - 'headers': {'Content-Type': 'application/json'}, - 'body': JSON.stringify(data) - }) + 'headers': {'Content-Type': 'application/json'} + }; + let url; + if (cl.contains('show-edit-static-reminder')) { + // Trigger static reminder + url = `${url_prefix}/api/staticreminders/${r_id}?api_key=${api_key}`; + } else { + // Test reminder draft + if (inputs.title.value === '') { + input.classList.add('error-input'); + input.title = 'No title set'; + return + } else { + input.classList.remove('error-input'); + input.removeAttribute('title'); + }; + const data = { + 'title': inputs.title.value, + 'notification_service': inputs.notification_service.value, + 'text': inputs.text.value + }; + headers.body = JSON.stringify(data); + url = `${url_prefix}/api/reminders/test?api_key=${api_key}`; + }; + fetch(url, headers) .then(response => { if (!response.ok) return Promise.reject(response.status); input.classList.add('show-sent'); @@ -103,6 +114,9 @@ function deleteInfo() { } else if (cl.contains('show-edit-template')) { // Delete template url = `${url_prefix}/api/templates/${e_id}?api_key=${api_key}`; + } else if (cl.contains('show-edit-static-reminder')) { + // Delete static reminder + url = `${url_prefix}/api/staticreminders/${e_id}?api_key=${api_key}`; } else return; fetch(url, {'method': 'DELETE'}) @@ -111,6 +125,7 @@ function deleteInfo() { fillNotificationSelection(); fillReminders(); + fillStaticReminders(); fillTemplates(); hideWindow(); }) @@ -156,6 +171,11 @@ function submitInfo() { fetch_data.url = `${url_prefix}/api/templates?api_key=${api_key}`; fetch_data.method = 'POST'; + } else if (cl.contains('show-add-static-reminder')) { + // Add static reminder + fetch_data.url = `${url_prefix}/api/staticreminders?api_key=${api_key}`; + fetch_data.method = 'POST'; + } else if (cl.contains('show-edit-reminder')) { // Edit reminder data['time'] = (new Date(inputs.time.value) / 1000) + (new Date(inputs.time.value).getTimezoneOffset() * 60) @@ -171,6 +191,11 @@ function submitInfo() { fetch_data.url = `${url_prefix}/api/templates/${e_id}?api_key=${api_key}`; fetch_data.method = 'PUT'; + } else if (cl.contains('show-edit-static-reminder')) { + // Edit a static reminder + fetch_data.url = `${url_prefix}/api/staticreminders/${e_id}?api_key=${api_key}`; + fetch_data.method = 'PUT'; + } else return; fetch(fetch_data.url, { @@ -182,6 +207,7 @@ function submitInfo() { if (!response.ok) return Promise.reject(response.status); fillReminders(); + fillStaticReminders(); fillTemplates(); hideWindow(); }) diff --git a/frontend/templates/reminders.html b/frontend/templates/reminders.html index 6609615..4c47245 100644 --- a/frontend/templates/reminders.html +++ b/frontend/templates/reminders.html @@ -78,6 +78,7 @@
+
@@ -120,6 +121,19 @@
+