mirror of
https://github.com/Casvt/MIND.git
synced 2026-02-19 11:54:46 -05:00
Frontend Update
This commit is contained in:
11
.vscode/launch.json
vendored
Normal file
11
.vscode/launch.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Main File",
|
||||
"type": "debugpy",
|
||||
"request": "launch",
|
||||
"python": "/bin/python3",
|
||||
"program": "${workspaceFolder}/MIND.py"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -166,6 +166,11 @@ class Reminder:
|
||||
).fetchone()
|
||||
reminder = dict(reminder)
|
||||
|
||||
reminder["weekdays"] = [
|
||||
int(n)
|
||||
for n in reminder["weekdays"].split(",")
|
||||
if n
|
||||
] if reminder["weekdays"] else None
|
||||
reminder['notification_services'] = self._get_notification_services()
|
||||
|
||||
return reminder
|
||||
@@ -277,7 +282,7 @@ class Reminder:
|
||||
if repeated_reminder:
|
||||
next_time = _find_next_time(
|
||||
data["time"],
|
||||
RepeatQuantity(data["repeat_quantity"]),
|
||||
RepeatQuantity(data["repeat_quantity"]) if data["repeat_quantity"] else None,
|
||||
data["repeat_interval"],
|
||||
weekdays
|
||||
)
|
||||
@@ -408,6 +413,12 @@ class Reminders:
|
||||
(self.user_id,)
|
||||
)
|
||||
]
|
||||
for r in reminders:
|
||||
r["weekdays"] = [
|
||||
int(n)
|
||||
for n in r["weekdays"].split(",")
|
||||
if n
|
||||
] if r["weekdays"] else None
|
||||
|
||||
# Sort result
|
||||
reminders.sort(key=sort_by.value[0], reverse=sort_by.value[1])
|
||||
|
||||
@@ -23,7 +23,7 @@ class User:
|
||||
|
||||
def __init__(self, id: int) -> None:
|
||||
result = get_db(dict).execute(
|
||||
"SELECT username, admin FROM users WHERE id = ? LIMIT 1;",
|
||||
"SELECT username, admin, salt FROM users WHERE id = ? LIMIT 1;",
|
||||
(id,)
|
||||
).fetchone()
|
||||
if not result:
|
||||
@@ -32,6 +32,7 @@ class User:
|
||||
self.username: str = result['username']
|
||||
self.user_id = id
|
||||
self.admin: bool = result['admin'] == 1
|
||||
self.salt: bytes = result['salt']
|
||||
return
|
||||
|
||||
@property
|
||||
|
||||
@@ -49,7 +49,7 @@ main {
|
||||
overflow-y: auto;
|
||||
|
||||
padding: .5rem;
|
||||
padding-top: calc(1rem + var(--nav-width));
|
||||
padding-top: var(--nav-width);
|
||||
}
|
||||
|
||||
#settings-form {
|
||||
@@ -63,12 +63,13 @@ h2 {
|
||||
width: 100%;
|
||||
|
||||
border-bottom: 1px solid var(--color-gray);
|
||||
padding: 1rem 1rem 0rem 1rem;
|
||||
padding: 1rem 1rem .25rem 1rem;
|
||||
|
||||
font-size: clamp(1rem, 10vw, 2rem);
|
||||
}
|
||||
|
||||
.table-container {
|
||||
.settings-table-container,
|
||||
.user-table-container {
|
||||
width: 100%;
|
||||
overflow-x: auto;
|
||||
|
||||
@@ -108,6 +109,10 @@ h2 {
|
||||
font-size: .9rem;
|
||||
}
|
||||
|
||||
.settings-table td > p {
|
||||
margin-top: .25rem;
|
||||
}
|
||||
|
||||
.number-input {
|
||||
width: fit-content;
|
||||
display: flex;
|
||||
@@ -126,8 +131,12 @@ h2 {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
.number-input > * {
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
|
||||
.number-input > p {
|
||||
padding: .75rem .75rem .75rem 0rem;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.settings-table select {
|
||||
@@ -170,11 +179,10 @@ h2 {
|
||||
#user-table {
|
||||
min-width: 25rem;
|
||||
border-spacing: 0px;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
#user-table th,
|
||||
#user-table td {
|
||||
#user-table :where(th, td) {
|
||||
height: 2.65rem;
|
||||
padding: .25rem .5rem;
|
||||
text-align: left;
|
||||
}
|
||||
@@ -183,54 +191,53 @@ h2 {
|
||||
border-top: 1px solid var(--color-gray);
|
||||
}
|
||||
|
||||
#user-table th:first-child,
|
||||
#user-table td:first-child {
|
||||
#user-table :where(th, td):first-child {
|
||||
width: 10rem;
|
||||
padding-left: 2rem;
|
||||
}
|
||||
|
||||
#user-table th:last-child,
|
||||
#user-table td:last-child {
|
||||
#user-table :where(td, td):nth-child(2) {
|
||||
width: 15rem;
|
||||
}
|
||||
|
||||
#user-table :where(th, td):last-child {
|
||||
width: 5.75rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
padding-right: 2rem;
|
||||
}
|
||||
|
||||
#user-table td:first-child {
|
||||
width: 100%;
|
||||
#user-table button {
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
#user-table svg {
|
||||
aspect-ratio: 1/1;
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
height: 100%;
|
||||
width: auto;
|
||||
}
|
||||
|
||||
#new-username-input,
|
||||
#new-password-input {
|
||||
width: 40%;
|
||||
padding: .25rem;
|
||||
}
|
||||
|
||||
#user-list form {
|
||||
margin-top: .5rem;
|
||||
width: 75%;
|
||||
}
|
||||
|
||||
#user-list input[type="password"] {
|
||||
#user-table input {
|
||||
width: 100%;
|
||||
padding: .25rem;
|
||||
}
|
||||
|
||||
@media (max-width: 40rem) {
|
||||
#settings-form,
|
||||
.table-container {
|
||||
#settings-form {
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
|
||||
h2 {
|
||||
text-align: center;
|
||||
padding-inline: 0;
|
||||
}
|
||||
|
||||
.settings-table-container,
|
||||
.user-table-container {
|
||||
justify-content: left;
|
||||
}
|
||||
|
||||
.settings-table tbody {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
@@ -239,6 +246,7 @@ h2 {
|
||||
.settings-table tr {
|
||||
display: inline-flex;
|
||||
flex-direction: column;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
.settings-table td {
|
||||
@@ -246,14 +254,11 @@ h2 {
|
||||
}
|
||||
|
||||
.settings-table td:first-child {
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.settings-table td:nth-child(2) {
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
#user-table {
|
||||
width: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -28,30 +28,32 @@ img {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
button {
|
||||
button,
|
||||
label {
|
||||
border: 0;
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-dark);
|
||||
color: var(--color-light);
|
||||
}
|
||||
|
||||
button:hover {
|
||||
button:hover,
|
||||
label:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
input:not([type="checkbox"]),
|
||||
select,
|
||||
textarea {
|
||||
width: 100%;
|
||||
|
||||
textarea,
|
||||
.as-button {
|
||||
border: 2px solid var(--color-gray);
|
||||
border-radius: 4px;
|
||||
padding: .75rem;
|
||||
padding: .6rem;
|
||||
outline: 0;
|
||||
box-shadow: var(--default-shadow);
|
||||
background-color: var(--color-dark);
|
||||
color: var(--color-light);
|
||||
|
||||
box-shadow: var(--default-shadow);
|
||||
|
||||
font-size: 1rem;
|
||||
}
|
||||
|
||||
@@ -69,6 +71,21 @@ svg rect {
|
||||
fill: var(--color-light);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar {
|
||||
width: 12px;
|
||||
height: 12px;
|
||||
background-color: var(--color-dark);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb {
|
||||
-webkit-border-radius: 6px;
|
||||
background-color: var(--color-gray);
|
||||
}
|
||||
|
||||
::-webkit-scrollbar-thumb:hover {
|
||||
background-color: var(--color-light-gray);
|
||||
}
|
||||
|
||||
/* */
|
||||
/* Utility classes */
|
||||
/* */
|
||||
@@ -76,17 +93,25 @@ svg rect {
|
||||
display: none !important;
|
||||
}
|
||||
|
||||
.error {
|
||||
.error,
|
||||
.error-container p {
|
||||
color: var(--color-error) !important;
|
||||
font-size: 1rem !important;
|
||||
}
|
||||
|
||||
.error-container p {
|
||||
display: block !important;
|
||||
}
|
||||
|
||||
.error-icon path,
|
||||
.error-icon rect {
|
||||
.error-icon rect,
|
||||
.error-container path,
|
||||
.error-container rect {
|
||||
fill: var(--color-error) !important;
|
||||
}
|
||||
|
||||
.error-input {
|
||||
.error-input,
|
||||
.error-container input {
|
||||
border: 2px solid var(--color-error) !important;
|
||||
}
|
||||
|
||||
@@ -173,11 +198,11 @@ header img {
|
||||
padding-block: var(--rem-clamp);
|
||||
}
|
||||
|
||||
.nav-divider.show-nav > nav {
|
||||
body:has(#nav-switch:checked) .nav-divider > nav {
|
||||
left: var(--rem-clamp);
|
||||
}
|
||||
|
||||
.nav-divider.show-nav > .window-container {
|
||||
body:has(#nav-switch:checked) .nav-divider > .window-container {
|
||||
margin-left: calc(var(--nav-width) + var(--rem-clamp));
|
||||
}
|
||||
|
||||
@@ -237,72 +262,70 @@ nav > div > button svg {
|
||||
}
|
||||
|
||||
/* */
|
||||
/* Main window */
|
||||
/* Window management */
|
||||
/* */
|
||||
.window-container {
|
||||
margin-left: calc(4rem + var(--rem-clamp));
|
||||
width: 100%;
|
||||
|
||||
|
||||
display: flex;
|
||||
overflow-y: auto;
|
||||
overflow-x: hidden;
|
||||
|
||||
overflow: hidden;
|
||||
|
||||
transition: margin-left .3s ease-in-out;
|
||||
}
|
||||
|
||||
.window-container > div {
|
||||
.window-container > :where(#home, .extra-window-container) {
|
||||
width: 100%;
|
||||
flex: 0 0 auto;
|
||||
|
||||
transform: translateX(0);
|
||||
|
||||
transition: transform .5s ease-in-out;
|
||||
translate: 0 0;
|
||||
transition: translate .5s ease-in-out;
|
||||
}
|
||||
|
||||
.window-container > div.show-window {
|
||||
transform: translateX(-100%);
|
||||
.window-container.inter-window-ani > :where(#home, .extra-window-container) {
|
||||
transition: translate .5s ease-in-out,
|
||||
transform .5s ease-in-out;
|
||||
}
|
||||
|
||||
.window-container > div:not(#home) {
|
||||
.extra-window-container {
|
||||
--y-offset: 0%;
|
||||
transform: translateY(var(--y-offset));
|
||||
}
|
||||
|
||||
.extra-window-container > div {
|
||||
height: 100%;
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.window-container.show-window > :where(#home, .extra-window-container) {
|
||||
translate: -100% 0;
|
||||
}
|
||||
|
||||
.window-container.show-window > .extra-window-container {
|
||||
transform: translateY(var(--y-offset));
|
||||
}
|
||||
|
||||
/* */
|
||||
/* Styling extra window */
|
||||
/* */
|
||||
.extra-window-container > div {
|
||||
padding: var(--rem-clamp);
|
||||
}
|
||||
|
||||
.window-container > div:not(#home) > h2 {
|
||||
.extra-window-container > div > h2 {
|
||||
text-align: center;
|
||||
font-size: clamp(1.3rem, 5vw, 2rem);
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.window-container > div:not(#home) > h2:not(:first-of-type) {
|
||||
.extra-window-container > div > h2:not(:first-of-type) {
|
||||
margin-top: 1.5rem;
|
||||
}
|
||||
|
||||
.window-container > div:not(#home) > p {
|
||||
.extra-window-container > div > p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.tab-selector {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
|
||||
padding-inline: .5rem;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.tab-selector > button {
|
||||
border: 2px solid var(--color-gray);
|
||||
padding: .5rem 1rem;
|
||||
transition: background-color .3s ease-in-out;
|
||||
}
|
||||
|
||||
.tab-selector > button[data-selected="true"] {
|
||||
background-color: var(--color-gray);
|
||||
}
|
||||
|
||||
@media (max-width: 543px) {
|
||||
.window-container {
|
||||
margin-left: 0;
|
||||
@@ -311,4 +334,4 @@ nav > div > button svg {
|
||||
nav {
|
||||
left: -100%;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,10 +10,9 @@
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.form-container > form input,
|
||||
.form-container > form select,
|
||||
.form-container > form textarea,
|
||||
.form-container > form button {
|
||||
.form-container > form :where(
|
||||
input, select, textarea, button, label
|
||||
) {
|
||||
padding: 1rem;
|
||||
}
|
||||
|
||||
@@ -27,14 +26,14 @@
|
||||
gap: var(--gap);
|
||||
}
|
||||
|
||||
.sub-inputs > input,
|
||||
.sub-inputs > select,
|
||||
.sub-inputs > button {
|
||||
.sub-inputs > :where(
|
||||
input, select, button, label
|
||||
) {
|
||||
width: calc(50% - (var(--gap) / 2));
|
||||
}
|
||||
|
||||
.form-container > form > button,
|
||||
.sub-inputs > button {
|
||||
.sub-inputs > :where(button, label) {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
@@ -55,8 +54,17 @@
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.color-list {
|
||||
#color-button {
|
||||
--color: var(--color-dark);
|
||||
background-color: var(--color);
|
||||
}
|
||||
|
||||
#info-form:has(#color-toggle:checked) .color-list {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.color-list {
|
||||
display: none;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
@@ -68,25 +76,26 @@
|
||||
box-shadow: var(--default-shadow);
|
||||
}
|
||||
|
||||
.color-list > button {
|
||||
height: 1.5rem;
|
||||
width: 1.5rem;
|
||||
|
||||
.color-list > label {
|
||||
padding: 1rem;
|
||||
border: 1px solid transparent;
|
||||
background-color: var(--color);
|
||||
}
|
||||
|
||||
.color-list > button[data-selected='true'] {
|
||||
.color-list > label:has(input:checked) {
|
||||
border-color: var(--color-white);
|
||||
}
|
||||
|
||||
.notification-service-list {
|
||||
#info-form:has(#notification-service-selection-toggle:checked) .notification-service-selection {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.notification-service-selection {
|
||||
width: 100%;
|
||||
max-height: 10rem;
|
||||
overflow-y: auto;
|
||||
|
||||
display: flex;
|
||||
display: none;
|
||||
flex-direction: column;
|
||||
|
||||
border: 2px solid var(--color-gray);
|
||||
@@ -95,19 +104,20 @@
|
||||
box-shadow: var(--default-shadow);
|
||||
}
|
||||
|
||||
.notification-service-list > div {
|
||||
.notification-service-selection > div {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
padding: .5rem .75rem;
|
||||
}
|
||||
|
||||
.notification-service-list > div:not(:first-child) {
|
||||
.notification-service-selection > div:not(:first-child) {
|
||||
border-top: 1px solid var(--color-gray);
|
||||
}
|
||||
|
||||
.form-container > form .repeat-options > button {
|
||||
width: calc((100% / 3) - (var(--gap) / 1.5));
|
||||
min-width: min-content;
|
||||
padding: 1rem 0rem;
|
||||
}
|
||||
|
||||
.repeat-bar {
|
||||
@@ -214,6 +224,7 @@ div.options > button {
|
||||
.sub-inputs > input,
|
||||
.sub-inputs > select,
|
||||
.sub-inputs > button,
|
||||
.sub-inputs > label,
|
||||
.form-container > form .repeat-options > button {
|
||||
width: 100%;
|
||||
}
|
||||
@@ -258,8 +269,8 @@ div.options > button {
|
||||
}
|
||||
|
||||
#info.show-add-static-reminder #template-selection,
|
||||
#info.show-add-static-reminder #color-toggle,
|
||||
#info.show-add-static-reminder #toggle-notification-service-list {
|
||||
#info.show-add-static-reminder #color-button,
|
||||
#info.show-add-static-reminder #notification-service-selection-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -279,8 +290,8 @@ div.options > button {
|
||||
margin-top: -1rem;
|
||||
}
|
||||
|
||||
#info.show-add-template #color-toggle,
|
||||
#info.show-add-template #toggle-notification-service-list {
|
||||
#info.show-add-template #color-button,
|
||||
#info.show-add-template #notification-service-selection-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -292,7 +303,7 @@ div.options > button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#info.show-edit-reminder #color-toggle {
|
||||
#info.show-edit-reminder #color-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -310,8 +321,8 @@ div.options > button {
|
||||
margin-top: -1rem;
|
||||
}
|
||||
|
||||
#info.show-edit-static-reminder #color-toggle,
|
||||
#info.show-edit-static-reminder #toggle-notification-service-list {
|
||||
#info.show-edit-static-reminder #color-button,
|
||||
#info.show-edit-static-reminder #notification-service-selection-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -330,7 +341,7 @@ div.options > button {
|
||||
margin-top: -1rem;
|
||||
}
|
||||
|
||||
#info.show-edit-template #color-toggle,
|
||||
#info.show-edit-template #toggle-notification-service-list {
|
||||
#info.show-edit-template #color-button,
|
||||
#info.show-edit-template #notification-service-selection-button {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
@@ -1,8 +1,10 @@
|
||||
/* */
|
||||
/* SEARCH BAR */
|
||||
/* */
|
||||
.search-container {
|
||||
max-width: 40rem;
|
||||
margin: auto;
|
||||
|
||||
|
||||
padding-block: clamp(1rem, 4vw, 2rem);
|
||||
}
|
||||
|
||||
@@ -12,24 +14,28 @@
|
||||
|
||||
.search-bar {
|
||||
display: flex;
|
||||
|
||||
|
||||
border: 2px solid var(--color-gray);
|
||||
border-radius: 4px;
|
||||
|
||||
|
||||
box-shadow: var(--default-shadow);
|
||||
}
|
||||
|
||||
.search-bar button {
|
||||
width: 3.5rem;
|
||||
padding: .8rem;
|
||||
.search-bar :where(button, label) {
|
||||
width: clamp(2rem, 7.5vw, 3rem);
|
||||
flex-shrink: 0;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.search-bar button svg {
|
||||
width: 1rem;
|
||||
.search-bar :where(button, label) svg {
|
||||
height: 1rem;
|
||||
}
|
||||
|
||||
.search-bar input {
|
||||
width: 100%;
|
||||
border: 0;
|
||||
padding-block: 1rem;
|
||||
box-shadow: none;
|
||||
@@ -37,7 +43,7 @@
|
||||
|
||||
#clear-button {
|
||||
opacity: 0;
|
||||
|
||||
|
||||
transition: opacity .1s linear;
|
||||
}
|
||||
|
||||
@@ -46,26 +52,44 @@
|
||||
}
|
||||
|
||||
#sort-input {
|
||||
width: min-content;
|
||||
width: clamp(6rem, 25vw, 12rem);
|
||||
border: 0;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.window-container:has(
|
||||
:where(#static-tab-selector, #template-tab-selector):checked
|
||||
) #sort-input > option:where(
|
||||
[value="time"], [value="time_reversed"]
|
||||
) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* */
|
||||
/* REMINDER LIST */
|
||||
#reminder-tab,
|
||||
#static-reminder-tab,
|
||||
#template-tab {
|
||||
/* */
|
||||
.tab-container > div {
|
||||
--gap: 1rem;
|
||||
--entry-width: 13rem;
|
||||
max-width: 43rem;
|
||||
margin-inline: auto;
|
||||
|
||||
display: flex;
|
||||
display: none;
|
||||
justify-content: left;
|
||||
gap: var(--gap);
|
||||
flex-wrap: wrap;
|
||||
|
||||
padding: 1rem;
|
||||
|
||||
transition: max-width .75s ease-in-out;
|
||||
}
|
||||
|
||||
body:has(#wide-toggle:checked) .tab-container > div {
|
||||
max-width: 85rem;
|
||||
}
|
||||
|
||||
#home {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
.entry.add-entry {
|
||||
@@ -122,10 +146,61 @@ button.entry.fit {
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
/* */
|
||||
/* Tab selector */
|
||||
/* */
|
||||
.tab-selector {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
|
||||
padding-inline: 1rem;
|
||||
padding-top: 1rem;
|
||||
}
|
||||
|
||||
.tab-selector > input {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.tab-selector > label {
|
||||
min-width: 9.55rem;
|
||||
|
||||
padding: .5rem 1rem;
|
||||
border-radius: 4px;
|
||||
border: 2px solid var(--color-gray);
|
||||
background-color: var(--color-dark);
|
||||
color: var(--color-light);
|
||||
|
||||
text-align: center;
|
||||
|
||||
transition: background-color .3s ease-in-out;
|
||||
}
|
||||
|
||||
.tab-selector > input:checked + label {
|
||||
background-color: var(--color-gray);
|
||||
}
|
||||
|
||||
.window-container:has(#reminder-tab-selector:checked) #reminder-tab,
|
||||
.window-container:has(#static-tab-selector:checked) #static-reminder-tab,
|
||||
.window-container:has(#template-tab-selector:checked) #template-tab {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
@media (max-width: 543px) {
|
||||
header > div {
|
||||
transform: translateX(0);
|
||||
}
|
||||
|
||||
.tab-selector > label {
|
||||
flex: 1 0 25%;
|
||||
}
|
||||
|
||||
#wide-button {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.entry {
|
||||
flex-grow: 1;
|
||||
|
||||
@@ -4,7 +4,7 @@ main {
|
||||
overflow-y: hidden;
|
||||
}
|
||||
|
||||
main.show-create > .form-container {
|
||||
main:has(#form-switch:checked) .form-container {
|
||||
transform: translateY(-100%);
|
||||
}
|
||||
|
||||
@@ -16,7 +16,7 @@ main.show-create > .form-container {
|
||||
align-items: center;
|
||||
|
||||
padding: 1rem;
|
||||
|
||||
|
||||
transition: transform .25s ease-in-out;
|
||||
}
|
||||
|
||||
@@ -40,8 +40,24 @@ form h2 {
|
||||
font-size: clamp(1.2rem, 7vw, 2rem);
|
||||
}
|
||||
|
||||
#username-error-container,
|
||||
#password-error-container {
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: .5rem;
|
||||
}
|
||||
|
||||
form input {
|
||||
max-width: 20rem;
|
||||
width: min(100%, 20rem);
|
||||
}
|
||||
|
||||
#username-error-container p,
|
||||
#password-error-container p {
|
||||
width: min(100%, 19rem);
|
||||
}
|
||||
|
||||
.switch-button {
|
||||
@@ -50,6 +66,11 @@ form input {
|
||||
color: var(--color-light);
|
||||
|
||||
text-decoration: underline;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.switch-button:hover {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button[type="submit"] {
|
||||
@@ -57,13 +78,3 @@ button[type="submit"] {
|
||||
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
p.error {
|
||||
width: 100%;
|
||||
max-width: 20rem;
|
||||
margin-top: -.5rem;
|
||||
|
||||
padding-inline: .5rem;
|
||||
|
||||
text-align: left;
|
||||
}
|
||||
@@ -8,19 +8,30 @@
|
||||
}
|
||||
|
||||
#add-service-button {
|
||||
padding: .5rem 8rem;
|
||||
width: min(100%, 17rem);
|
||||
height: 2rem;
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
|
||||
border-radius: 4px;
|
||||
background-color: var(--color-gray);
|
||||
}
|
||||
|
||||
#add-service-button > svg {
|
||||
height: 1rem;
|
||||
width: 1rem;
|
||||
transition: transform .125s linear;
|
||||
}
|
||||
|
||||
.table-container:has(#service-list-toggle:checked) #add-service-button > svg {
|
||||
transform: rotate(45deg);
|
||||
}
|
||||
|
||||
.overflow-container {
|
||||
margin-inline: auto;
|
||||
width: 100%;
|
||||
max-width: 50rem;
|
||||
width: min(100%, 50rem);
|
||||
|
||||
overflow-x: auto;
|
||||
}
|
||||
@@ -29,6 +40,10 @@
|
||||
border-spacing: 0px;
|
||||
}
|
||||
|
||||
.overflow-container > table:not(:has(tbody > tr)) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.overflow-container > table th,
|
||||
.overflow-container > table td {
|
||||
text-align: left;
|
||||
@@ -38,7 +53,7 @@
|
||||
padding: .5rem;
|
||||
}
|
||||
|
||||
#services-list > tr > td {
|
||||
.overflow-container td {
|
||||
border-top: 1px solid var(--color-gray);
|
||||
padding: .25rem;
|
||||
}
|
||||
@@ -46,7 +61,7 @@
|
||||
.title-column {
|
||||
min-width: 9.5rem;
|
||||
width: 25%;
|
||||
|
||||
|
||||
padding-left: 1.5rem;
|
||||
padding-right: 1rem;
|
||||
}
|
||||
@@ -56,17 +71,18 @@
|
||||
width: 65%;
|
||||
}
|
||||
|
||||
#services-list input {
|
||||
border-radius: 2px;
|
||||
.overflow-container table input {
|
||||
width: 100%;
|
||||
border-radius: 4px;
|
||||
padding: .25rem;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
#services-list input:read-only {
|
||||
.overflow-container input:read-only {
|
||||
border-color: transparent;
|
||||
}
|
||||
|
||||
#services-list > tr > td.action-column {
|
||||
.overflow-container .action-column {
|
||||
min-width: 4rem;
|
||||
width: 20%;
|
||||
|
||||
@@ -77,18 +93,24 @@
|
||||
padding-right: 1.5rem;
|
||||
}
|
||||
|
||||
.action-column svg {
|
||||
width: 1rem;
|
||||
height: 1rem;
|
||||
.action-column > button {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
#services-list > tr:not(.edit) > td.action-column > button[data-type="edit"],
|
||||
#services-list > tr.edit > td.action-column > button[data-type="save"] {
|
||||
display: inline-block;
|
||||
.overflow-container .action-column svg {
|
||||
width: 1.25rem;
|
||||
height: 1.25rem;
|
||||
}
|
||||
|
||||
#services-list > tr.edit > td.action-column > button[data-type="edit"],
|
||||
#services-list > tr:not(.edit):not(#add-row) > td.action-column > button[data-type="save"] {
|
||||
tr:has(input:not(:read-only)) button[data-type="save"],
|
||||
tr:has(input:read-only) button[data-type="edit"] {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
tr:has(input:not(:read-only)) button[data-type="edit"],
|
||||
tr:has(input:read-only) button[data-type="save"] {
|
||||
display: none;
|
||||
}
|
||||
|
||||
@@ -99,14 +121,22 @@
|
||||
display: none;
|
||||
}
|
||||
|
||||
.overflow-container.show-add #add-service-container {
|
||||
.overflow-container:has(#service-list-toggle:checked) table {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.overflow-container:has(#service-list-toggle:checked) #add-service-container {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.overflow-container.show-add > table {
|
||||
.overflow-container:has(#add-service-toggle:checked) #service-list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.overflow-container:has(#add-service-toggle:checked) #add-service-window {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#service-list {
|
||||
display: flex;
|
||||
gap: 1rem;
|
||||
@@ -130,14 +160,6 @@
|
||||
font-size: 1.1rem;
|
||||
}
|
||||
|
||||
#add-service-container.show-add-window #add-service-window {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
#add-service-container.show-add-window #service-list {
|
||||
display: none;
|
||||
}
|
||||
|
||||
/* */
|
||||
/* Add service form */
|
||||
/* */
|
||||
@@ -205,7 +227,7 @@
|
||||
min-height: 5rem;
|
||||
max-height: 15rem;
|
||||
overflow-y: auto;
|
||||
|
||||
|
||||
align-items: center;
|
||||
|
||||
background-color: var(--color-dark);
|
||||
@@ -228,19 +250,23 @@
|
||||
}
|
||||
|
||||
.add-row {
|
||||
height: 2rem;
|
||||
width: 80%;
|
||||
width: min(100%, 21rem);
|
||||
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.add-row input {
|
||||
flex-grow: 1;
|
||||
height: 2rem;
|
||||
min-width: 0rem;
|
||||
font-size: .8rem;
|
||||
}
|
||||
|
||||
.add-row button {
|
||||
height: 2rem;
|
||||
padding: .35rem .75rem;
|
||||
background-color: var(--color-gray);
|
||||
border-radius: 4px;
|
||||
@@ -265,3 +291,9 @@
|
||||
height: inherit;
|
||||
fill: var(--color-dark);
|
||||
}
|
||||
|
||||
@media (max-width: 543px) {
|
||||
#service-list button {
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ h2 {
|
||||
|
||||
p {
|
||||
font-size: clamp(1rem, 4.2vw, 1.75rem);
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
a {
|
||||
|
||||
@@ -1,45 +1,39 @@
|
||||
.settings-container {
|
||||
max-width: 50rem;
|
||||
#settings-table {
|
||||
margin-inline: auto;
|
||||
|
||||
text-align: center;
|
||||
width: min(100%, 50rem);
|
||||
}
|
||||
|
||||
.settings-container > h3 {
|
||||
margin-bottom: 1.25rem;
|
||||
|
||||
padding-bottom: .5rem;
|
||||
border-bottom: 2px solid var(--color-gray);
|
||||
|
||||
font-size: 1.4rem;
|
||||
#settings-table td {
|
||||
padding: .5rem;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.settings-container > h3:not(:first-of-type) {
|
||||
margin-top: 2.5rem;
|
||||
#settings-table tr > td:first-child {
|
||||
text-align: right;
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.settings-container button {
|
||||
#settings-table button {
|
||||
padding: .5rem 2rem;
|
||||
background-color: var(--color-gray);
|
||||
|
||||
box-shadow: var(--default-shadow);
|
||||
}
|
||||
|
||||
.settings-container input,
|
||||
.settings-container textarea,
|
||||
.settings-container select {
|
||||
max-width: 20rem;
|
||||
#settings-table tr:has(#default-service-input) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#settings-table tr:has(#default-service-input option) {
|
||||
display: table-row;
|
||||
}
|
||||
|
||||
#change-password-form {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
#delete-account-button {
|
||||
#settings-table #delete-account-button {
|
||||
background-color: var(--color-error);
|
||||
}
|
||||
|
||||
@@ -67,4 +61,4 @@
|
||||
|
||||
text-decoration: none;
|
||||
font-size: 1.2rem;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,12 @@
|
||||
const setting_inputs = {
|
||||
'allow_new_accounts': document.querySelector('#allow-new-accounts-input'),
|
||||
'login_time': document.querySelector('#login-time-input'),
|
||||
'login_time_reset': document.querySelector('#login-time-reset-input')
|
||||
allow_new_accounts: document.querySelector('#allow-new-accounts-input'),
|
||||
login_time: document.querySelector('#login-time-input'),
|
||||
login_time_reset: document.querySelector('#login-time-reset-input')
|
||||
};
|
||||
|
||||
const user_inputs = {
|
||||
username: document.querySelector('#new-username-input'),
|
||||
password: document.querySelector('#new-password-input')
|
||||
};
|
||||
|
||||
function checkLogin() {
|
||||
@@ -58,8 +63,8 @@ function toggleAddUser() {
|
||||
const el = document.querySelector('#add-user-row');
|
||||
if (el.classList.contains('hidden')) {
|
||||
// Show row
|
||||
document.querySelector('#new-username-input').value = '';
|
||||
document.querySelector('#new-password-input').value = '';
|
||||
user_inputs.username.value = '';
|
||||
user_inputs.password.value = '';
|
||||
el.classList.remove('hidden');
|
||||
} else {
|
||||
// Hide row
|
||||
@@ -68,9 +73,11 @@ function toggleAddUser() {
|
||||
};
|
||||
|
||||
function addUser() {
|
||||
user_inputs.username.classList.remove('error-input');
|
||||
user_inputs.username.title = '';
|
||||
const data = {
|
||||
'username': document.querySelector('#new-username-input').value,
|
||||
'password': document.querySelector('#new-password-input').value
|
||||
'username': user_inputs.username.value,
|
||||
'password': user_inputs.password.value
|
||||
};
|
||||
fetch(`${url_prefix}/api/admin/users?api_key=${api_key}`, {
|
||||
'method': 'POST',
|
||||
@@ -85,12 +92,23 @@ function addUser() {
|
||||
loadUsers();
|
||||
})
|
||||
.catch(e => {
|
||||
console.log(e);
|
||||
if (e === "UsernameTaken") {
|
||||
user_inputs.username.classList.add('error-input');
|
||||
user_inputs.username.title = 'Username already taken';
|
||||
|
||||
} else if (e === "UsernameInvalid") {
|
||||
user_inputs.username.classList.add('error-input');
|
||||
user_inputs.username.title = 'Username contains invalid characters';
|
||||
|
||||
} else
|
||||
console.log(e);
|
||||
});
|
||||
};
|
||||
|
||||
function editUser(id) {
|
||||
const new_password = document.querySelector(`#user-table tr[data-id="${id}"] input`).value;
|
||||
const new_password = document.querySelector(
|
||||
`#user-table tr[data-id="${id}"] input`
|
||||
).value;
|
||||
fetch(`${url_prefix}/api/admin/users/${id}?api_key=${api_key}`, {
|
||||
'method': 'PUT',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
@@ -103,7 +121,7 @@ function deleteUser(id) {
|
||||
document.querySelector(`#user-table tr[data-id="${id}"]`).remove();
|
||||
fetch(`${url_prefix}/api/admin/users/${id}?api_key=${api_key}`, {
|
||||
'method': 'DELETE'
|
||||
})
|
||||
});
|
||||
};
|
||||
|
||||
function loadUsers() {
|
||||
@@ -117,9 +135,10 @@ function loadUsers() {
|
||||
entry.dataset.id = user.id;
|
||||
|
||||
const username = document.createElement('td');
|
||||
const username_text = document.createElement('p');
|
||||
username_text.innerText = user.username;
|
||||
username.appendChild(username_text);
|
||||
username.innerText = user.username;
|
||||
entry.appendChild(username);
|
||||
|
||||
const password = document.createElement('td');
|
||||
const new_password_form = document.createElement('form');
|
||||
new_password_form.classList.add('hidden');
|
||||
new_password_form.action = `javascript:editUser(${user.id})`;
|
||||
@@ -127,23 +146,29 @@ function loadUsers() {
|
||||
new_password.type = 'password';
|
||||
new_password.placeholder = 'New password';
|
||||
new_password_form.appendChild(new_password);
|
||||
username.appendChild(new_password_form);
|
||||
entry.appendChild(username);
|
||||
password.appendChild(new_password_form);
|
||||
entry.appendChild(password);
|
||||
|
||||
const actions = document.createElement('td');
|
||||
entry.appendChild(actions);
|
||||
|
||||
const edit_user = document.createElement('button');
|
||||
edit_user.onclick = e => e.currentTarget.parentNode.previousSibling.querySelector('form').classList.toggle('hidden');
|
||||
edit_user.innerHTML = icons.edit;
|
||||
edit_user.onclick = e => e
|
||||
.currentTarget
|
||||
.parentNode
|
||||
.previousSibling
|
||||
.querySelector('form')
|
||||
.classList
|
||||
.toggle('hidden');
|
||||
edit_user.innerHTML = Icons.edit;
|
||||
actions.appendChild(edit_user);
|
||||
|
||||
if (user.username !== 'admin') {
|
||||
const delete_user = document.createElement('button');
|
||||
delete_user.onclick = e => deleteUser(user.id);
|
||||
delete_user.innerHTML = icons.delete;
|
||||
delete_user.innerHTML = Icons.delete;
|
||||
actions.appendChild(delete_user);
|
||||
}
|
||||
};
|
||||
|
||||
table.appendChild(entry);
|
||||
});
|
||||
@@ -156,9 +181,9 @@ checkLogin();
|
||||
loadSettings();
|
||||
loadUsers();
|
||||
|
||||
document.querySelector('#logout-button').onclick = (e) => logout();
|
||||
document.querySelector('#logout-button').onclick = e => logout();
|
||||
document.querySelector('#settings-form').action = 'javascript:submitSettings();';
|
||||
document.querySelector('#add-user-button').onclick = e => toggleAddUser();
|
||||
document.querySelector('#add-user-form').action = 'javascript:addUser()';
|
||||
document.querySelector('#download-db-button').onclick = e =>
|
||||
window.location.href = `${url_prefix}/api/admin/database?api_key=${api_key}`
|
||||
window.location.href = `${url_prefix}/api/admin/database?api_key=${api_key}`;
|
||||
|
||||
@@ -1,46 +1,49 @@
|
||||
const types = {
|
||||
// The duration of the animation set for the window translation
|
||||
const window_ani_ms = 500;
|
||||
|
||||
const Types = {
|
||||
'reminder': document.getElementById('reminder-tab'),
|
||||
'static_reminder': document.getElementById('static-reminder-tab'),
|
||||
'template': document.getElementById('template-tab')
|
||||
};
|
||||
|
||||
const icons = {
|
||||
const Icons = {
|
||||
'save': '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve"><g><path d="M12,10a4,4,0,1,0,4,4A4,4,0,0,0,12,10Zm0,6a2,2,0,1,1,2-2A2,2,0,0,1,12,16Z"></path><path d="M22.536,4.122,19.878,1.464A4.966,4.966,0,0,0,16.343,0H5A5.006,5.006,0,0,0,0,5V19a5.006,5.006,0,0,0,5,5H19a5.006,5.006,0,0,0,5-5V7.657A4.966,4.966,0,0,0,22.536,4.122ZM17,2.08V3a3,3,0,0,1-3,3H10A3,3,0,0,1,7,3V2h9.343A2.953,2.953,0,0,1,17,2.08ZM22,19a3,3,0,0,1-3,3H5a3,3,0,0,1-3-3V5A3,3,0,0,1,5,2V3a5.006,5.006,0,0,0,5,5h4a4.991,4.991,0,0,0,4.962-4.624l2.16,2.16A3.02,3.02,0,0,1,22,7.657Z"></path></g></svg>',
|
||||
'edit': '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve"><g><g id="_01_align_center" data-name="01 align center"><path d="M22.94,1.06a3.626,3.626,0,0,0-5.124,0L0,18.876V24H5.124L22.94,6.184A3.627,3.627,0,0,0,22.94,1.06ZM4.3,22H2V19.7L15.31,6.4l2.3,2.3ZM21.526,4.77,19.019,7.277l-2.295-2.3L19.23,2.474a1.624,1.624,0,0,1,2.3,2.3Z"></path></g></g></svg>',
|
||||
'delete': '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve"><g><g id="_01_align_center" data-name="01 align center"><path d="M22,4H17V2a2,2,0,0,0-2-2H9A2,2,0,0,0,7,2V4H2V6H4V21a3,3,0,0,0,3,3H17a3,3,0,0,0,3-3V6h2ZM9,2h6V4H9Zm9,19a1,1,0,0,1-1,1H7a1,1,0,0,1-1-1V6H18Z"></path><rect x="9" y="10" width="2" height="8"></rect><rect x="13" y="10" width="2" height="8"></rect></g></g></svg>',
|
||||
'add': '<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve"><g><g><path d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z"></path></g></g></svg>'
|
||||
};
|
||||
|
||||
const info_classes = [
|
||||
const InfoClasses = [
|
||||
'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');
|
||||
};
|
||||
|
||||
function showWindow(id) {
|
||||
document.querySelectorAll('.window-container > div').forEach(e => {
|
||||
if (e.id === id || e.id === 'home') {
|
||||
e.classList.remove('hidden');
|
||||
setTimeout(() => e.classList.add('show-window'), 0);
|
||||
} else {
|
||||
e.classList.add('hidden');
|
||||
e.classList.remove('show-window');
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
function hideWindow() {
|
||||
document.querySelectorAll('.show-window').forEach(e => {
|
||||
e.classList.remove('show-window');
|
||||
});
|
||||
setTimeout(() => {
|
||||
document.querySelectorAll('.window-container > div:not(#home)').forEach(
|
||||
e => e.classList.add('hidden')
|
||||
const window_container = document.querySelector('.window-container');
|
||||
if (id === "home") {
|
||||
window_container.classList.remove(
|
||||
'show-window',
|
||||
'inter-window-ani'
|
||||
);
|
||||
}, 500);
|
||||
} else {
|
||||
const extra_window_container =
|
||||
document.querySelector('.extra-window-container');
|
||||
|
||||
const offset = [
|
||||
...extra_window_container.children
|
||||
].indexOf(document.getElementById(id)) * -100;
|
||||
|
||||
extra_window_container.style.setProperty(
|
||||
'--y-offset',
|
||||
`${offset}%`
|
||||
)
|
||||
window_container.classList.add('show-window');
|
||||
setTimeout(
|
||||
() => window_container.classList.add('inter-window-ani'),
|
||||
window_ani_ms
|
||||
);
|
||||
};
|
||||
};
|
||||
|
||||
function logout() {
|
||||
@@ -59,7 +62,10 @@ function logout() {
|
||||
const default_values = {
|
||||
'api_key': null,
|
||||
'locale': 'en-GB',
|
||||
'default_service': null
|
||||
'default_service': null,
|
||||
'sorting_reminders': 'time',
|
||||
'sorting_static': 'title',
|
||||
'sorting_templates': 'title'
|
||||
};
|
||||
|
||||
function setupLocalStorage() {
|
||||
|
||||
@@ -1,73 +1,80 @@
|
||||
const sorting_options = {};
|
||||
sorting_options[types.reminder.id] = [
|
||||
['time', 'Time'],
|
||||
['time_reversed', 'Time Reversed'],
|
||||
['title', 'Title'],
|
||||
['title_reversed', 'Title Reversed'],
|
||||
['date_added', 'Date Added'],
|
||||
['date_added_reversed', 'Date Added Reversed']
|
||||
];
|
||||
sorting_options[types.static_reminder.id] = [
|
||||
['title', 'Title'],
|
||||
['title_reversed', 'Title Reversed'],
|
||||
['date_added', 'Date Added'],
|
||||
['date_added_reversed', 'Date Added Reversed']
|
||||
];
|
||||
sorting_options[types.template.id] = [
|
||||
['title', 'Title'],
|
||||
['title_reversed', 'Title Reversed'],
|
||||
['date_added', 'Date Added'],
|
||||
['date_added_reversed', 'Date Added Reversed']
|
||||
];
|
||||
|
||||
function showTab(button) {
|
||||
// Apply styling to selected button
|
||||
document.querySelectorAll('.tab-selector > button').forEach(
|
||||
b => b.dataset.selected = b === button ? 'true' : 'false'
|
||||
);
|
||||
|
||||
// Show desired tab and hide all others
|
||||
document.querySelectorAll('#home > div:not(.tab-selector):not(.search-container)').forEach(
|
||||
e => e.classList.add('hidden')
|
||||
);
|
||||
document.getElementById(button.dataset.target).classList.remove('hidden');
|
||||
|
||||
fillSortOptions();
|
||||
document.querySelector('#search-input').value = '';
|
||||
const NavButtons = {
|
||||
home: document.querySelector('#home-button'),
|
||||
notification_services: document.querySelector('#notification-services-button'),
|
||||
settings: document.querySelector('#settings-button'),
|
||||
log_out: document.querySelector('#logout-button')
|
||||
};
|
||||
|
||||
//
|
||||
// Filling library
|
||||
//
|
||||
function getWeekDays(locale)
|
||||
{
|
||||
let baseDate = new Date(Date.UTC(2017, 0, 2)); // just a Monday
|
||||
let weekDays = [];
|
||||
for(i = 0; i < 7; i++)
|
||||
const LibEls = {
|
||||
tab_selector: document.querySelector('.tab-selector'),
|
||||
tab_container: document.querySelector('.tab-container'),
|
||||
search_bar: {
|
||||
form: document.querySelector('#search-form'),
|
||||
input: document.querySelector('#search-input'),
|
||||
clear: document.querySelector('#clear-button'),
|
||||
sort: document.querySelector('#sort-input'),
|
||||
wide: document.querySelector('#wide-button')
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Helpers
|
||||
//
|
||||
function getSorting(type, key=false) {
|
||||
let sorting_key;
|
||||
if (type === Types.reminder)
|
||||
sorting_key = 'sorting_reminders';
|
||||
else if (type === Types.static_reminder)
|
||||
sorting_key = 'sorting_static';
|
||||
else if (type === Types.template)
|
||||
sorting_key = 'sorting_templates';
|
||||
|
||||
if (key)
|
||||
return sorting_key;
|
||||
else
|
||||
return getLocalStorage(sorting_key)[sorting_key];
|
||||
};
|
||||
|
||||
function getWeekDays(locale) {
|
||||
const baseDate = new Date(Date.UTC(2017, 0, 2)); // just a Monday
|
||||
const weekDays = [];
|
||||
for (i = 0; i < 7; i++)
|
||||
{
|
||||
weekDays.push(baseDate.toLocaleDateString(locale, { weekday: 'short' }));
|
||||
baseDate.setDate(baseDate.getDate() + 1);
|
||||
}
|
||||
return weekDays;
|
||||
};
|
||||
const week_days = getWeekDays(getLocalStorage('locale')['locale']);
|
||||
|
||||
function getActiveTab() {
|
||||
for (let t of Object.values(Types)) {
|
||||
if (getComputedStyle(t).display === 'flex')
|
||||
return t
|
||||
};
|
||||
return null;
|
||||
};
|
||||
|
||||
//
|
||||
// Filling library
|
||||
//
|
||||
function fillTable(table, results) {
|
||||
table.querySelectorAll('button.entry:not(.add-entry)').forEach(e => e.remove());
|
||||
table.querySelectorAll('button.entry:not(.add-entry)').forEach(
|
||||
e => e.remove()
|
||||
);
|
||||
|
||||
results.forEach(r => {
|
||||
const entry = document.createElement('button');
|
||||
entry.classList.add('entry');
|
||||
entry.dataset.id = r.id;
|
||||
entry.addEventListener('click', e => showEdit(r.id, table));
|
||||
entry.onclick = e => showEdit(r.id, table);
|
||||
if (r.color !== null)
|
||||
entry.style.setProperty('--color', r.color);
|
||||
|
||||
|
||||
const title = document.createElement('h2');
|
||||
title.innerText = r.title;
|
||||
entry.appendChild(title);
|
||||
|
||||
if (table === types.reminder) {
|
||||
if (table === Types.reminder) {
|
||||
const time = document.createElement('p');
|
||||
let offset = new Date(r.time * 1000).getTimezoneOffset() * -60;
|
||||
let d = new Date((r.time + offset) * 1000);
|
||||
@@ -83,140 +90,43 @@ function fillTable(table, results) {
|
||||
formatted_date += interval_text;
|
||||
|
||||
} else if (r.weekdays !== null)
|
||||
formatted_date += ` (each ${r.weekdays.split(',').map(d => week_days[parseInt(d)]).join(', ')})`;
|
||||
formatted_date += ` (each ${r.weekdays.map(d => week_days[d]).join(', ')})`;
|
||||
|
||||
time.innerText = formatted_date;
|
||||
entry.appendChild(time);
|
||||
};
|
||||
|
||||
|
||||
table.appendChild(entry);
|
||||
|
||||
if (title.clientHeight < title.scrollHeight)
|
||||
entry.classList.add('expand');
|
||||
});
|
||||
table.querySelectorAll('button.entry:not(.add-entry)').forEach(r => r.classList.add('fit'));
|
||||
evaluateSizing();
|
||||
};
|
||||
|
||||
function fillLibrary(url, type) {
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) return Promise.reject(response.status);
|
||||
return response.json();
|
||||
})
|
||||
.then(json => fillTable(type, json.result))
|
||||
.catch(e => {
|
||||
if (e === 401)
|
||||
window.location.href = `${url_prefix}/`;
|
||||
else
|
||||
console.log(e);
|
||||
});
|
||||
};
|
||||
function fillLibrary(type=null) {
|
||||
let tab_type = type || getActiveTab();
|
||||
|
||||
function fillReminders() {
|
||||
const sorting = document.querySelector('#sort-input').value;
|
||||
fillLibrary(`/api/reminders?api_key=${api_key}&sort_by=${sorting}`, types.reminder);
|
||||
};
|
||||
|
||||
function fillStaticReminders(assume_sorting=false) {
|
||||
let sorting;
|
||||
if (assume_sorting)
|
||||
sorting = sorting_options[types.static_reminder.id][0][0];
|
||||
else
|
||||
sorting = document.querySelector('#sort-input').value;
|
||||
fillLibrary(`/api/staticreminders?api_key=${api_key}&sort_by=${sorting}`, types.static_reminder);
|
||||
}
|
||||
|
||||
function fillTemplates(assume_sorting=false) {
|
||||
let sorting;
|
||||
if (assume_sorting)
|
||||
sorting = sorting_options[types.template.id][0][0];
|
||||
else
|
||||
sorting = document.querySelector('#sort-input').value;
|
||||
fillLibrary(`/api/templates?api_key=${api_key}&sort_by=${sorting}`, types.template);
|
||||
};
|
||||
|
||||
//
|
||||
// Library search
|
||||
//
|
||||
function searchLibrary() {
|
||||
const query = document.querySelector('#search-input').value,
|
||||
tab = document.getElementById(
|
||||
document.querySelector('.tab-selector > button[data-selected="true"]').dataset.target
|
||||
)
|
||||
const sorting = document.querySelector('#sort-input').value;
|
||||
let url;
|
||||
if (tab === types.reminder)
|
||||
url = `${url_prefix}/api/reminders/search?api_key=${api_key}&query=${query}&sort_by=${sorting}`;
|
||||
else if (tab === types.static_reminder)
|
||||
url = `${url_prefix}/api/staticreminders/search?api_key=${api_key}&query=${query}&sort_by=${sorting}`;
|
||||
else if (tab === types.template)
|
||||
url = `${url_prefix}/api/templates/search?api_key=${api_key}&query=${query}&sort_by=${sorting}`;
|
||||
else return;
|
||||
|
||||
fillLibrary(url, tab);
|
||||
};
|
||||
|
||||
function clearSearchLibrary() {
|
||||
document.querySelector('#search-input').value = '';
|
||||
const tab = document.getElementById(
|
||||
document.querySelector('.tab-selector > button[data-selected="true"]').dataset.target
|
||||
)
|
||||
if (tab === types.reminder)
|
||||
fillReminders();
|
||||
else if (tab === types.static_reminder)
|
||||
fillStaticReminders();
|
||||
else if (tab === types.template)
|
||||
fillTemplates();
|
||||
else return;
|
||||
};
|
||||
|
||||
//
|
||||
// Library sort
|
||||
//
|
||||
function fillSortOptions() {
|
||||
const tab = document.getElementById(
|
||||
document.querySelector('.tab-selector > button[data-selected="true"]').dataset.target
|
||||
)
|
||||
const sort_options = sorting_options[tab.id];
|
||||
|
||||
const select = document.getElementById('sort-input');
|
||||
select.innerHTML = '';
|
||||
sort_options.forEach(o => {
|
||||
const entry = document.createElement('option');
|
||||
entry.value = o[0]
|
||||
entry.innerText = o[1]
|
||||
select.appendChild(entry);
|
||||
});
|
||||
select.querySelector(':first-child').setAttribute('selected', '');
|
||||
};
|
||||
|
||||
function applySorting() {
|
||||
const query = document.querySelector('#search-input').value;
|
||||
if (query !== '') {
|
||||
searchLibrary();
|
||||
if (tab_type === Types.reminder)
|
||||
url = `${url_prefix}/api/reminders`;
|
||||
else if (tab_type === Types.static_reminder)
|
||||
url = `${url_prefix}/api/staticreminders`;
|
||||
else if (tab_type === Types.template)
|
||||
url = `${url_prefix}/api/templates`;
|
||||
else
|
||||
return;
|
||||
};
|
||||
|
||||
const sorting = document.getElementById('sort-input').value,
|
||||
tab = document.getElementById(
|
||||
document.querySelector('.tab-selector > button[data-selected="true"]').dataset.target
|
||||
)
|
||||
|
||||
let url;
|
||||
if (tab === types.reminder)
|
||||
url = `${url_prefix}/api/reminders?api_key=${api_key}&sort_by=${sorting}`;
|
||||
else if (tab === types.static_reminder)
|
||||
url = `${url_prefix}/api/staticreminders?api_key=${api_key}&sort_by=${sorting}`;
|
||||
else if (tab === types.template)
|
||||
url = `${url_prefix}/api/templates?api_key=${api_key}&sort_by=${sorting}`;
|
||||
else return;
|
||||
const sorting = getSorting(tab_type);
|
||||
const query = LibEls.search_bar.input.value;
|
||||
if (query)
|
||||
url = `${url}/search?api_key=${api_key}&sort_by=${sorting}&query=${query}`;
|
||||
else
|
||||
url = `${url}?api_key=${api_key}&sort_by=${sorting}`;
|
||||
|
||||
fetch(url)
|
||||
.then(response => {
|
||||
if (!response.ok) return Promise.reject(response.status);
|
||||
return response.json();
|
||||
})
|
||||
.then(json => fillTable(tab, json.result))
|
||||
.then(json => fillTable(tab_type, json.result))
|
||||
.catch(e => {
|
||||
if (e === 401)
|
||||
window.location.href = `${url_prefix}/`;
|
||||
@@ -225,25 +135,50 @@ function applySorting() {
|
||||
});
|
||||
};
|
||||
|
||||
function saveSorting() {
|
||||
const type = getActiveTab();
|
||||
const sorting_key = getSorting(type, key=true);
|
||||
const value = LibEls.search_bar.sort.value;
|
||||
setLocalStorage({[sorting_key]: value});
|
||||
};
|
||||
|
||||
function evaluateSizing() {
|
||||
const tab = getActiveTab();
|
||||
const entries = [...tab.querySelectorAll('button:not(.add-entry)')];
|
||||
entries.forEach(e => e.classList.remove('fit'));
|
||||
entries.forEach(e => {
|
||||
const title = e.querySelector('h2');
|
||||
if (title.clientHeight < title.scrollHeight)
|
||||
e.classList.add('expand');
|
||||
});
|
||||
entries.forEach(e => e.classList.add('fit'));
|
||||
};
|
||||
|
||||
// code run on load
|
||||
|
||||
document.querySelectorAll('.tab-selector > button').forEach(b => {
|
||||
b.addEventListener('click', e => showTab(b));
|
||||
Object.values(Types).forEach(t => fillLibrary(t));
|
||||
setInterval(() => fillLibrary(Types.reminder), 60000);
|
||||
|
||||
const week_days = getWeekDays(getLocalStorage('locale')['locale']);
|
||||
|
||||
NavButtons.home.onclick = e => showWindow("home");
|
||||
NavButtons.notification_services.onclick = e => showWindow("notification");
|
||||
NavButtons.settings.onclick = e => showWindow("settings");
|
||||
NavButtons.log_out.onclick = e => logout();
|
||||
|
||||
LibEls.search_bar.form.action = 'javascript:fillLibrary();'
|
||||
LibEls.search_bar.sort.value = getSorting(getActiveTab());
|
||||
LibEls.search_bar.sort.onchange = e => {
|
||||
saveSorting();
|
||||
fillLibrary();
|
||||
};
|
||||
LibEls.search_bar.clear.onclick = e => {
|
||||
LibEls.search_bar.input.value = '';
|
||||
fillLibrary();
|
||||
};
|
||||
|
||||
LibEls.tab_selector.querySelectorAll('input').forEach(r => r.onchange = e => {
|
||||
evaluateSizing();
|
||||
LibEls.search_bar.input.value = '';
|
||||
LibEls.search_bar.sort.value = getSorting(getActiveTab());
|
||||
});
|
||||
|
||||
document.getElementById('toggle-nav').addEventListener('click', e => toggleNav());
|
||||
|
||||
document.getElementById('home-button').addEventListener('click', e => hideWindow());
|
||||
document.getElementById('notification-services-button').addEventListener('click', e => showWindow('notification'));
|
||||
document.getElementById('settings-button').addEventListener('click', e => showWindow('settings'));
|
||||
document.getElementById('logout-button').addEventListener('click', e => logout());
|
||||
|
||||
fillSortOptions();
|
||||
fillReminders();
|
||||
fillStaticReminders(assume_sorting=true);
|
||||
fillTemplates(assume_sorting=true);
|
||||
setInterval(fillReminders, 60000);
|
||||
|
||||
document.querySelector('#search-form').setAttribute('action', 'javascript:searchLibrary();');
|
||||
document.querySelector('#clear-button').addEventListener('click', e => clearSearchLibrary());
|
||||
document.querySelector('#sort-input').addEventListener('change', e => applySorting());
|
||||
|
||||
@@ -1,37 +1,36 @@
|
||||
const login_inputs = {
|
||||
'username': document.querySelector('#login-form > input[type="text"]'),
|
||||
'password': document.querySelector('#login-form > input[type="password"]')
|
||||
};
|
||||
|
||||
const login_errors = {
|
||||
'username': document.getElementById('username-error'),
|
||||
'password': document.getElementById('password-error')
|
||||
};
|
||||
|
||||
const create_inputs = {
|
||||
'username': document.querySelector('#create-form > input[type="text"]'),
|
||||
'password': document.querySelector('#create-form > input[type="password"]')
|
||||
}
|
||||
|
||||
const create_errors = {
|
||||
'username_invalid': document.getElementById('new-username-error'),
|
||||
'username_taken': document.getElementById('taken-username-error'),
|
||||
};
|
||||
|
||||
function toggleWindow() {
|
||||
document.querySelector('main').classList.toggle('show-create');
|
||||
const forms = {
|
||||
'login': {
|
||||
'form': document.querySelector('#login-form'),
|
||||
'inputs': {
|
||||
'username': document.querySelector('#login-form input[type="text"]'),
|
||||
'password': document.querySelector('#login-form input[type="password"]')
|
||||
},
|
||||
'errors': {
|
||||
'username': document.querySelector("#username-error-container"),
|
||||
'password': document.querySelector("#password-error-container")
|
||||
}
|
||||
},
|
||||
'create': {
|
||||
'form': document.querySelector('#create-form'),
|
||||
'inputs': {
|
||||
'username': document.querySelector('#create-form input[type="text"]'),
|
||||
'password': document.querySelector('#create-form input[type="password"]')
|
||||
},
|
||||
'errors': {
|
||||
'username_invalid': document.querySelector('#new-username-error'),
|
||||
'username_taken': document.querySelector('#taken-username-error')
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
function login(data=null) {
|
||||
login_inputs.username.classList.remove('error-input');
|
||||
login_errors.username.classList.add('hidden');
|
||||
login_inputs.password.classList.remove('error-input');
|
||||
login_errors.password.classList.add('hidden');
|
||||
forms.login.errors.username.classList.remove('error-container');
|
||||
forms.login.errors.password.classList.remove('error-container');
|
||||
|
||||
if (data === null)
|
||||
data = {
|
||||
'username': login_inputs.username.value,
|
||||
'password': login_inputs.password.value
|
||||
'username': forms.login.inputs.username.value,
|
||||
'password': forms.login.inputs.password.value
|
||||
};
|
||||
|
||||
fetch(`${url_prefix}/api/auth/login`, {
|
||||
@@ -53,26 +52,23 @@ function login(data=null) {
|
||||
window.location.href = `${url_prefix}/reminders`;
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
login_inputs.password.classList.add('error-input');
|
||||
login_errors.password.classList.remove('hidden');
|
||||
} else if (e === 404) {
|
||||
login_inputs.username.classList.add('error-input');
|
||||
login_errors.username.classList.remove('hidden');
|
||||
} else {
|
||||
if (e === 404)
|
||||
forms.login.errors.username.classList.add('error-container');
|
||||
else if (e === 401)
|
||||
forms.login.errors.password.classList.add('error-container');
|
||||
else
|
||||
console.log(e);
|
||||
};
|
||||
});
|
||||
};
|
||||
|
||||
function create() {
|
||||
create_inputs.username.classList.remove('error-input');
|
||||
create_errors.username_invalid.classList.add('hidden');
|
||||
create_errors.username_taken.classList.add('hidden');
|
||||
forms.create.inputs.username.classList.remove('error-input');
|
||||
forms.create.errors.username_invalid.classList.add('hidden');
|
||||
forms.create.errors.username_taken.classList.add('hidden');
|
||||
|
||||
const data = {
|
||||
'username': create_inputs.username.value,
|
||||
'password': create_inputs.password.value
|
||||
'username': forms.create.inputs.username.value,
|
||||
'password': forms.create.inputs.password.value
|
||||
};
|
||||
fetch(`${url_prefix}/api/user/add`, {
|
||||
'method': 'POST',
|
||||
@@ -86,11 +82,11 @@ function create() {
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 'UsernameInvalid') {
|
||||
create_inputs.username.classList.add('error-input');
|
||||
create_errors.username_invalid.classList.remove('hidden');
|
||||
forms.create.inputs.username.classList.add('error-input');
|
||||
forms.create.errors.username_invalid.classList.remove('hidden');
|
||||
} else if (e === 'UsernameTaken') {
|
||||
create_inputs.username.classList.add('error-input');
|
||||
create_errors.username_taken.classList.remove('hidden');
|
||||
forms.create.inputs.username.classList.add('error-input');
|
||||
forms.create.errors.username_taken.classList.remove('hidden');
|
||||
} else {
|
||||
console.log(e);
|
||||
};
|
||||
@@ -98,7 +94,7 @@ function create() {
|
||||
};
|
||||
|
||||
function checkLogin() {
|
||||
fetch(`${url_prefix}/api/auth/status?api_key=${JSON.parse(localStorage.getItem('MIND')).api_key}`)
|
||||
fetch(`${url_prefix}/api/auth/status?api_key=${api_key}`)
|
||||
.then(response => {
|
||||
if (!response.ok) return Promise.reject(response.status);
|
||||
return response.json();
|
||||
@@ -129,13 +125,15 @@ function checkAllowNewAccounts() {
|
||||
// code run on load
|
||||
|
||||
if (localStorage.getItem('MIND') === null)
|
||||
localStorage.setItem('MIND', JSON.stringify({'api_key': null, 'locale': 'en-GB', 'default_service': null}))
|
||||
localStorage.setItem('MIND', JSON.stringify(
|
||||
{'api_key': null, 'locale': 'en-GB', 'default_service': null}
|
||||
))
|
||||
|
||||
const url_prefix = document.getElementById('url_prefix').dataset.value;
|
||||
const api_key = JSON.parse(localStorage.getItem('MIND')).api_key;
|
||||
|
||||
checkLogin();
|
||||
checkAllowNewAccounts();
|
||||
|
||||
document.getElementById('login-form').setAttribute('action', 'javascript:login();');
|
||||
document.getElementById('create-form').setAttribute('action', 'javascript:create();');
|
||||
document.querySelectorAll('.switch-button').forEach(e => e.addEventListener('click', e => toggleWindow()));
|
||||
forms.login.form.action = 'javascript:login();';
|
||||
forms.create.form.action = 'javascript:create();';
|
||||
|
||||
@@ -1,121 +1,118 @@
|
||||
function fillNotificationSelection() {
|
||||
const NotiEls = {
|
||||
services_list: document.querySelector('#services-list'),
|
||||
service_list: document.querySelector('#service-list'),
|
||||
default_service_input: document.querySelector('#default-service-input'),
|
||||
service_selection: document.querySelector('.notification-service-selection'),
|
||||
notification_service_row: document.querySelector('.element-storage .notification-service-row'),
|
||||
add_service_window: document.querySelector('#add-service-window'),
|
||||
triggers: {
|
||||
add_service: document.querySelector('#add-service-toggle'),
|
||||
service_list: document.querySelector('#service-list-toggle')
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// Fill lists and tables
|
||||
//
|
||||
function fillNotificationTable(json) {
|
||||
NotiEls.services_list.innerHTML = '';
|
||||
json.result.forEach(service => {
|
||||
const entry = NotiEls.notification_service_row.cloneNode(true);
|
||||
entry.dataset.id = service.id;
|
||||
|
||||
entry.querySelector('.title-column input').value = service.title;
|
||||
|
||||
const url_input = entry.querySelector('.url-column input');
|
||||
url_input.value = service.url;
|
||||
url_input.onkeydown = e => {
|
||||
if (e.key === 'Enter')
|
||||
saveService(service.id);
|
||||
};
|
||||
|
||||
entry.querySelector('button[data-type="edit"]').onclick = e =>
|
||||
document.querySelectorAll(`tr[data-id="${service.id}"] input`).forEach(
|
||||
e => e.removeAttribute('readonly')
|
||||
);
|
||||
|
||||
entry.querySelector('button[data-type="save"]').onclick = e =>
|
||||
saveService(service.id);
|
||||
|
||||
entry.querySelector('button[data-type="delete"]').onclick = e =>
|
||||
deleteService(service.id);
|
||||
|
||||
NotiEls.services_list.appendChild(entry);
|
||||
});
|
||||
};
|
||||
|
||||
function fillNotificationSelection(json) {
|
||||
// Default service setting
|
||||
NotiEls.default_service_input.innerHTML = '';
|
||||
const default_service = getLocalStorage('default_service')['default_service'];
|
||||
json.result.forEach(service => {
|
||||
const entry = document.createElement('option');
|
||||
entry.value = service.id;
|
||||
entry.innerText = service.title;
|
||||
if (default_service === service.id)
|
||||
entry.setAttribute('selected', '');
|
||||
NotiEls.default_service_input.appendChild(entry);
|
||||
});
|
||||
|
||||
if (!NotiEls.default_service_input.querySelector(`option[value="${default_service}"]`))
|
||||
setLocalStorage({'default_service':
|
||||
parseInt(NotiEls.default_service_input.querySelector('option')?.value)
|
||||
|| null
|
||||
});
|
||||
|
||||
// Selection when managing (static)reminders/templates
|
||||
NotiEls.service_selection.innerHTML = '';
|
||||
json.result.forEach(service => {
|
||||
const entry = document.createElement('div');
|
||||
|
||||
const select = document.createElement('input');
|
||||
select.dataset.id = service.id;
|
||||
select.type = 'checkbox';
|
||||
entry.appendChild(select);
|
||||
|
||||
const title = document.createElement('p');
|
||||
title.innerText = service.title;
|
||||
entry.appendChild(title);
|
||||
|
||||
NotiEls.service_selection.appendChild(entry);
|
||||
});
|
||||
if (json.result.length > 0)
|
||||
NotiEls.service_selection.querySelector(':first-child input').checked = true;
|
||||
};
|
||||
|
||||
function setNoNotificationServiceMsg(json) {
|
||||
if (json.result.length > 0) {
|
||||
LibEls.tab_container.querySelectorAll('.add-entry').forEach(ae => {
|
||||
ae.classList.remove('error', 'error-icon');
|
||||
if (ae.id === 'add-reminder')
|
||||
ae.onclick = e => showAdd(Types.reminder);
|
||||
else if (ae.id === 'add-static-reminder')
|
||||
ae.onclick = e => showAdd(Types.static_reminder);
|
||||
else if (ae.id === 'add-template')
|
||||
ae.onclick = e => showAdd(Types.template);
|
||||
});
|
||||
|
||||
} else {
|
||||
LibEls.tab_container.querySelectorAll('.add-entry').forEach(ae => {
|
||||
ae.classList.add('error', 'error-icon');
|
||||
ae.onclick = e => showWindow('notification');
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
function fillNotificationServices() {
|
||||
fetch(`${url_prefix}/api/notificationservices?api_key=${api_key}`)
|
||||
.then(response => {
|
||||
if (!response.ok) return Promise.reject(response.status);
|
||||
return response.json();
|
||||
})
|
||||
.then(json => {
|
||||
if (json.result.length) {
|
||||
document.getElementById('add-reminder').classList.remove('error', 'error-icon');
|
||||
|
||||
const default_select = document.querySelector('#default-service-input');
|
||||
default_select.innerHTML = '';
|
||||
let default_service = getLocalStorage('default_service')['default_service'];
|
||||
json.result.forEach(service => {
|
||||
const entry = document.createElement('option');
|
||||
entry.value = service.id;
|
||||
entry.innerText = service.title;
|
||||
if (default_service === service.id)
|
||||
entry.setAttribute('selected', '');
|
||||
default_select.appendChild(entry);
|
||||
});
|
||||
if (!document.querySelector(`#default-service-input > option[value="${default_service}"]`))
|
||||
setLocalStorage({'default_service':
|
||||
parseInt(document.querySelector('#default-service-input > option')?.value)
|
||||
|| null
|
||||
});
|
||||
default_service = getLocalStorage('default_service')['default_service'];
|
||||
|
||||
inputs.notification_service.innerHTML = '';
|
||||
json.result.forEach(service => {
|
||||
const entry = document.createElement('div');
|
||||
|
||||
const select = document.createElement('input');
|
||||
select.dataset.id = service.id;
|
||||
select.type = 'checkbox';
|
||||
entry.appendChild(select);
|
||||
|
||||
const title = document.createElement('p');
|
||||
title.innerText = service.title;
|
||||
entry.appendChild(title);
|
||||
|
||||
inputs.notification_service.appendChild(entry);
|
||||
});
|
||||
inputs.notification_service.querySelector(':first-child input').checked = true;
|
||||
|
||||
const table = document.getElementById('services-list');
|
||||
table.innerHTML = '';
|
||||
json.result.forEach(service => {
|
||||
const entry = document.createElement('tr');
|
||||
entry.dataset.id = service.id;
|
||||
|
||||
const title_container = document.createElement('td');
|
||||
title_container.classList.add('title-column');
|
||||
const title = document.createElement('input');
|
||||
title.setAttribute('readonly', '');
|
||||
title.setAttribute('type', 'text');
|
||||
title.value = service.title;
|
||||
title_container.appendChild(title);
|
||||
entry.appendChild(title_container);
|
||||
|
||||
const url_container = document.createElement('td');
|
||||
url_container.classList.add('url-column');
|
||||
const url = document.createElement('input');
|
||||
url.setAttribute('readonly', '');
|
||||
url.setAttribute('type', 'text');
|
||||
url.value = service.url;
|
||||
url.addEventListener('keydown', e => {
|
||||
if (e.key === 'Enter')
|
||||
saveService(service.id);
|
||||
});
|
||||
url_container.appendChild(url);
|
||||
entry.appendChild(url_container);
|
||||
|
||||
const actions = document.createElement('td');
|
||||
actions.classList.add('action-column');
|
||||
entry.appendChild(actions);
|
||||
|
||||
const edit_button = document.createElement('button');
|
||||
edit_button.dataset.type = 'edit';
|
||||
edit_button.addEventListener('click', e => editService(service.id));
|
||||
edit_button.title = 'Edit';
|
||||
edit_button.setAttribute('aria-label', 'Edit');
|
||||
edit_button.innerHTML = icons.edit;
|
||||
actions.appendChild(edit_button);
|
||||
|
||||
const save_button = document.createElement('button');
|
||||
save_button.dataset.type = 'save';
|
||||
save_button.addEventListener('click', e => saveService(service.id));
|
||||
save_button.title = 'Save Edits';
|
||||
save_button.setAttribute('aria-label', 'Save Edits');
|
||||
save_button.innerHTML = icons.save;
|
||||
actions.appendChild(save_button);
|
||||
|
||||
const delete_button = document.createElement('button');
|
||||
delete_button.dataset.type = 'delete';
|
||||
delete_button.addEventListener('click', e => deleteService(service.id));
|
||||
delete_button.title = 'Delete';
|
||||
delete_button.setAttribute('aria-label', 'Delete');
|
||||
delete_button.innerHTML = icons.delete;
|
||||
actions.appendChild(delete_button);
|
||||
|
||||
table.appendChild(entry);
|
||||
});
|
||||
} else {
|
||||
document.getElementById('add-reminder').classList.add('error', 'error-icon');
|
||||
|
||||
inputs.notification_service.innerHTML = '';
|
||||
|
||||
const default_select = document.querySelector('#default-service-input');
|
||||
default_select.innerHTML = '';
|
||||
|
||||
const default_service = getLocalStorage('default_service')['default_service'];
|
||||
if (!document.querySelector(`#default-service-input > option[value="${default_service}"]`))
|
||||
setLocalStorage({'default_service':
|
||||
parseInt(document.querySelector('#default-service-input > option')?.value)
|
||||
|| null
|
||||
});
|
||||
};
|
||||
fillNotificationTable(json);
|
||||
fillNotificationSelection(json);
|
||||
setNoNotificationServiceMsg(json);
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401)
|
||||
@@ -125,11 +122,9 @@ function fillNotificationSelection() {
|
||||
});
|
||||
};
|
||||
|
||||
function editService(id) {
|
||||
document.querySelectorAll(`tr[data-id="${id}"] input`).forEach(e => e.removeAttribute('readonly'));
|
||||
document.querySelector(`tr[data-id="${id}"]`).classList.add('edit');
|
||||
};
|
||||
|
||||
//
|
||||
// Actions for table
|
||||
//
|
||||
function saveService(id) {
|
||||
const row = document.querySelector(`tr[data-id="${id}"]`);
|
||||
const save_button = row.querySelector('button[data-type="save"]');
|
||||
@@ -144,8 +139,8 @@ function saveService(id) {
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) return Promise.reject(response.status);
|
||||
|
||||
fillNotificationSelection();
|
||||
|
||||
fillNotificationServices();
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401)
|
||||
@@ -153,7 +148,6 @@ function saveService(id) {
|
||||
else if (e === 400) {
|
||||
save_button.classList.add('error-icon');
|
||||
save_button.title = 'Invalid Apprise URL';
|
||||
save_button.setAttribute('aria-label', 'Invalid Apprise URL');
|
||||
} else
|
||||
console.log(e);
|
||||
});
|
||||
@@ -169,9 +163,7 @@ function deleteService(id) {
|
||||
if (json.error !== null) return Promise.reject(json);
|
||||
|
||||
row.remove();
|
||||
fillNotificationSelection();
|
||||
if (document.querySelectorAll('#services-list > tr').length === 0)
|
||||
document.getElementById('add-reminder').classList.add('error', 'error-icon');
|
||||
fillNotificationServices();
|
||||
})
|
||||
.catch(e => {
|
||||
if (e.error === 'ApiKeyExpired' || e.error === 'ApiKeyInvalid')
|
||||
@@ -185,83 +177,31 @@ function deleteService(id) {
|
||||
});
|
||||
};
|
||||
|
||||
function testService() {
|
||||
const test_button = document.querySelector('#test-service');
|
||||
|
||||
// Check regexes for input's
|
||||
[...document.querySelectorAll('#add-service-window > input:not([data-regex=""])[data-regex]')]
|
||||
.forEach(el => el.classList.remove('error-input'));
|
||||
|
||||
const faulty_inputs =
|
||||
[...document.querySelectorAll('#add-service-window > input:not([data-regex=""])[data-regex]')]
|
||||
.filter(el => !new RegExp
|
||||
(
|
||||
el.dataset.regex.split(',').slice(0, el.dataset.regex.split(',').length-1).join(','),
|
||||
el.dataset.regex.split(',')[el.dataset.regex.split(',').length-1]
|
||||
).test(el.value)
|
||||
);
|
||||
if (faulty_inputs.length > 0) {
|
||||
faulty_inputs.forEach(el => el.classList.add('error-input'));
|
||||
//
|
||||
// Adding a service
|
||||
//
|
||||
function showServiceList(e) {
|
||||
if (!e.target.checked)
|
||||
return;
|
||||
};
|
||||
|
||||
const data = {
|
||||
'url': buildAppriseURL()
|
||||
};
|
||||
if (!data.url) {
|
||||
test_button.classList.add('error-input');
|
||||
test_button.title = 'Required field missing';
|
||||
NotiEls.triggers.add_service.checked = false;
|
||||
|
||||
if (notification_services !== null)
|
||||
return;
|
||||
};
|
||||
fetch(`${url_prefix}/api/notificationservices/test?api_key=${api_key}`, {
|
||||
'method': 'POST',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) return Promise.reject(response.status);
|
||||
|
||||
test_button.classList.remove('error-input');
|
||||
test_button.title = '';
|
||||
test_button.classList.add('show-sent');
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401)
|
||||
window.location.href = `${url_prefix}/`;
|
||||
else if (e === 400) {
|
||||
test_button.classList.add('error-input');
|
||||
test_button.title = 'Invalid Apprise URL';
|
||||
} else
|
||||
console.log(e);
|
||||
|
||||
fetch(`${url_prefix}/api/notificationservices/available?api_key=${api_key}`)
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
notification_services = json.result;
|
||||
json.result.forEach((result, index) => {
|
||||
const entry = document.createElement('button');
|
||||
entry.innerText = result.name;
|
||||
entry.onclick = e => showAddServiceWindow(index);
|
||||
NotiEls.service_list.appendChild(entry);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
function toggleAddService() {
|
||||
const cont = document.querySelector('.overflow-container');
|
||||
if (cont.classList.contains('show-add')) {
|
||||
// Hide add
|
||||
cont.classList.remove('show-add');
|
||||
hideAddServiceWindow();
|
||||
} else {
|
||||
// Show add
|
||||
if (notification_services === null) {
|
||||
fetch(`${url_prefix}/api/notificationservices/available?api_key=${api_key}`)
|
||||
.then(response => response.json())
|
||||
.then(json => {
|
||||
notification_services = json.result;
|
||||
const table = document.querySelector('#service-list');
|
||||
json.result.forEach((result, index) => {
|
||||
const entry = document.createElement('button');
|
||||
entry.innerText = result.name;
|
||||
entry.addEventListener('click', e => showAddServiceWindow(index));
|
||||
table.appendChild(entry);
|
||||
});
|
||||
});
|
||||
};
|
||||
cont.classList.add('show-add');
|
||||
};
|
||||
};
|
||||
|
||||
function createTitle() {
|
||||
const service_title = document.createElement('input');
|
||||
service_title.id = 'service-title';
|
||||
@@ -354,25 +294,25 @@ function createEntriesList(token) {
|
||||
add_row.classList.add('add-row', 'hidden');
|
||||
const add_input = document.createElement('input');
|
||||
add_input.type = 'text';
|
||||
add_input.addEventListener('keydown', e => {
|
||||
add_input.onkeydown = e => {
|
||||
if (e.key === "Enter") {
|
||||
e.preventDefault();
|
||||
e.stopImmediatePropagation();
|
||||
addEntry(entries_list);
|
||||
};
|
||||
});
|
||||
};
|
||||
add_row.appendChild(add_input);
|
||||
const add_entry_button = document.createElement('button');
|
||||
add_entry_button.type = 'button';
|
||||
add_entry_button.innerText = 'Add';
|
||||
add_entry_button.addEventListener('click', e => addEntry(entries_list));
|
||||
add_entry_button.onclick = e => addEntry(entries_list);
|
||||
add_row.appendChild(add_entry_button);
|
||||
entries_list.appendChild(add_row);
|
||||
|
||||
const add_button = document.createElement('button');
|
||||
add_button.type = 'button';
|
||||
add_button.innerHTML = icons.add;
|
||||
add_button.addEventListener('click', e => toggleAddRow(add_row));
|
||||
add_button.innerHTML = Icons.add;
|
||||
add_button.onclick = e => toggleAddRow(add_row);
|
||||
entries_list.appendChild(add_button);
|
||||
|
||||
return entries_list;
|
||||
@@ -398,7 +338,7 @@ function addEntry(entries_list) {
|
||||
};
|
||||
|
||||
function showAddServiceWindow(index) {
|
||||
const window = document.getElementById('add-service-window');
|
||||
const window = NotiEls.add_service_window;
|
||||
window.innerHTML = '';
|
||||
window.dataset.index = index;
|
||||
|
||||
@@ -423,10 +363,10 @@ function showAddServiceWindow(index) {
|
||||
const show_args = document.createElement('button');
|
||||
show_args.type = 'button';
|
||||
show_args.innerText = 'Show Advanced Settings';
|
||||
show_args.addEventListener('click', e => {
|
||||
show_args.onclick = e => {
|
||||
window.querySelectorAll('[data-is_arg="true"]').forEach(el => el.classList.toggle('hidden'));
|
||||
show_args.innerText = show_args.innerText === 'Show Advanced Settings' ? 'Hide Advanced Settings' : 'Show Advanced Settings';
|
||||
});
|
||||
};
|
||||
window.appendChild(show_args);
|
||||
};
|
||||
|
||||
@@ -474,7 +414,9 @@ function showAddServiceWindow(index) {
|
||||
});
|
||||
|
||||
if (vars[1] === 'args' && vars[0].length > 0)
|
||||
window.querySelectorAll('[data-is_arg="true"]').forEach(el => el.classList.toggle('hidden'));
|
||||
window.querySelectorAll('[data-is_arg="true"]').forEach(
|
||||
el => el.classList.toggle('hidden')
|
||||
);
|
||||
})
|
||||
|
||||
// Bottom options
|
||||
@@ -484,13 +426,13 @@ function showAddServiceWindow(index) {
|
||||
const cancel = document.createElement('button');
|
||||
cancel.type = 'button';
|
||||
cancel.innerText = 'Cancel';
|
||||
cancel.addEventListener('click', e => toggleAddService());
|
||||
cancel.onclick = e => NotiEls.triggers.add_service.checked = false;
|
||||
options.appendChild(cancel);
|
||||
|
||||
const test = document.createElement('button');
|
||||
test.id = 'test-service';
|
||||
test.type = 'button';
|
||||
test.addEventListener('click', e => testService());
|
||||
test.onclick = e => testService();
|
||||
options.appendChild(test);
|
||||
const test_text = document.createElement('div');
|
||||
test_text.innerText = 'Test';
|
||||
@@ -504,17 +446,13 @@ function showAddServiceWindow(index) {
|
||||
add.innerText = 'Add';
|
||||
options.appendChild(add);
|
||||
window.appendChild(options);
|
||||
|
||||
document.getElementById('add-service-container').classList.add('show-add-window');
|
||||
};
|
||||
|
||||
function hideAddServiceWindow() {
|
||||
document.getElementById('add-service-container').classList.remove('show-add-window');
|
||||
NotiEls.triggers.add_service.checked = true;
|
||||
};
|
||||
|
||||
function buildAppriseURL() {
|
||||
const data = notification_services[document.querySelector('#add-service-window').dataset.index];
|
||||
const inputs = document.querySelectorAll('#add-service-window > [data-map][data-is_arg="false"]');
|
||||
const data = notification_services[NotiEls.add_service_window.dataset.index];
|
||||
const inputs = NotiEls.add_service_window.querySelectorAll('[data-map][data-is_arg="false"]');
|
||||
const values = {};
|
||||
|
||||
// Gather all values and format
|
||||
@@ -555,7 +493,7 @@ function buildAppriseURL() {
|
||||
template = template.replace(`{${key}}`, value);
|
||||
|
||||
// Add args
|
||||
const args = [...document.querySelectorAll('#add-service-window > [data-map][data-is_arg="true"]')]
|
||||
const args = [...NotiEls.add_service_window.querySelectorAll('[data-map][data-is_arg="true"]')]
|
||||
.map(el => {
|
||||
if (['INPUT', 'SELECT'].includes(el.nodeName) && el.value && el.value !== el.dataset.default)
|
||||
return `${el.dataset.map}=${el.value}`;
|
||||
@@ -587,15 +525,66 @@ function buildAppriseURL() {
|
||||
return template;
|
||||
};
|
||||
|
||||
function addService() {
|
||||
const add_button = document.querySelector('#add-service-window > .options > button[type="submit"]');
|
||||
|
||||
function testService() {
|
||||
const test_button = document.querySelector('#test-service');
|
||||
|
||||
// Check regexes for input's
|
||||
[...document.querySelectorAll('#add-service-window > input:not([data-regex=""])[data-regex]')]
|
||||
[...NotiEls.add_service_window.querySelectorAll('input:not([data-regex=""])[data-regex]')]
|
||||
.forEach(el => el.classList.remove('error-input'));
|
||||
|
||||
const faulty_inputs =
|
||||
[...document.querySelectorAll('#add-service-window > input:not([data-regex=""])[data-regex]')]
|
||||
[...NotiEls.add_service_window.querySelectorAll('input:not([data-regex=""])[data-regex]')]
|
||||
.filter(el => !new RegExp
|
||||
(
|
||||
el.dataset.regex.split(',').slice(0, el.dataset.regex.split(',').length-1).join(','),
|
||||
el.dataset.regex.split(',')[el.dataset.regex.split(',').length-1]
|
||||
).test(el.value)
|
||||
);
|
||||
if (faulty_inputs.length > 0) {
|
||||
faulty_inputs.forEach(el => el.classList.add('error-input'));
|
||||
return;
|
||||
};
|
||||
|
||||
const data = {
|
||||
'url': buildAppriseURL()
|
||||
};
|
||||
if (!data.url) {
|
||||
test_button.classList.add('error-input');
|
||||
test_button.title = 'Required field missing';
|
||||
return;
|
||||
};
|
||||
fetch(`${url_prefix}/api/notificationservices/test?api_key=${api_key}`, {
|
||||
'method': 'POST',
|
||||
'headers': {'Content-Type': 'application/json'},
|
||||
'body': JSON.stringify(data)
|
||||
})
|
||||
.then(response => {
|
||||
if (!response.ok) return Promise.reject(response.status);
|
||||
|
||||
test_button.classList.remove('error-input');
|
||||
test_button.title = '';
|
||||
test_button.classList.add('show-sent');
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401)
|
||||
window.location.href = `${url_prefix}/`;
|
||||
else if (e === 400) {
|
||||
test_button.classList.add('error-input');
|
||||
test_button.title = 'Invalid Apprise URL';
|
||||
} else
|
||||
console.log(e);
|
||||
});
|
||||
};
|
||||
|
||||
function addService() {
|
||||
const add_button = NotiEls.add_service_window.querySelector('.options > button[type="submit"]');
|
||||
|
||||
// Check regexes for input's
|
||||
[...NotiEls.add_service_window.querySelectorAll('input:not([data-regex=""])[data-regex]')]
|
||||
.forEach(el => el.classList.remove('error-input'));
|
||||
|
||||
const faulty_inputs =
|
||||
[...NotiEls.add_service_window.querySelectorAll('input:not([data-regex=""])[data-regex]')]
|
||||
.filter(el =>
|
||||
!(
|
||||
(!el.required && el.value === '')
|
||||
@@ -631,8 +620,9 @@ function addService() {
|
||||
add_button.classList.remove('error-input');
|
||||
add_button.title = '';
|
||||
|
||||
toggleAddService();
|
||||
fillNotificationSelection();
|
||||
NotiEls.triggers.service_list.checked = false;
|
||||
NotiEls.triggers.add_service.checked = false;
|
||||
fillNotificationServices();
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401)
|
||||
@@ -647,9 +637,9 @@ function addService() {
|
||||
|
||||
// code run on load
|
||||
|
||||
fillNotificationSelection();
|
||||
fillNotificationServices();
|
||||
|
||||
let notification_services = null;
|
||||
|
||||
document.getElementById('add-service-button').addEventListener('click', e => toggleAddService());
|
||||
document.getElementById('add-service-window').setAttribute('action', 'javascript:addService();');
|
||||
NotiEls.triggers.service_list.onchange = showServiceList;
|
||||
NotiEls.add_service_window.action = 'javascript:addService();';
|
||||
|
||||
@@ -1,14 +1,24 @@
|
||||
const SettingsEls = {
|
||||
locale_input: document.querySelector('#locale-input'),
|
||||
default_service_input: document.querySelector('#default-service-input'),
|
||||
change_password_form: document.querySelector('#change-password-form'),
|
||||
delete_account_button: document.querySelector('#delete-account-button')
|
||||
};
|
||||
|
||||
function loadSettings() {
|
||||
document.getElementById('locale-input').value = getLocalStorage('locale')['locale'];
|
||||
// Default Service is handled by notification.fillNotificationSelection()
|
||||
document.getElementById('locale-input').value =
|
||||
getLocalStorage('locale')['locale'];
|
||||
};
|
||||
|
||||
function updateLocale(e) {
|
||||
setLocalStorage({'locale': e.target.value});
|
||||
window.location.reload();
|
||||
fillLibrary(Types.reminder);
|
||||
};
|
||||
|
||||
function updateDefaultService(e) {
|
||||
setLocalStorage({'default_service': parseInt(e.target.value)});
|
||||
// Add window is handled by show.showAdd()
|
||||
};
|
||||
|
||||
function changePassword() {
|
||||
@@ -45,7 +55,7 @@ function deleteAccount() {
|
||||
|
||||
loadSettings();
|
||||
|
||||
document.getElementById('locale-input').addEventListener('change', updateLocale);
|
||||
document.querySelector('#default-service-input').addEventListener('change', updateDefaultService);
|
||||
document.getElementById('change-password-form').setAttribute('action', 'javascript:changePassword()');
|
||||
document.getElementById('delete-account-button').addEventListener('click', e => deleteAccount());
|
||||
SettingsEls.locale_input.onchange = updateLocale;
|
||||
SettingsEls.default_service_input.onchange = updateDefaultService;
|
||||
SettingsEls.change_password_form.action = 'javascript:changePassword();';
|
||||
SettingsEls.delete_account_button.onclick = e => deleteAccount();
|
||||
|
||||
@@ -4,32 +4,37 @@ function showAdd(type) {
|
||||
inputs.title.value = '';
|
||||
inputs.text.value = '';
|
||||
inputs.time.value = '';
|
||||
inputs.notification_service.querySelectorAll('input[type="checkbox"]').forEach(c => c.checked = false);
|
||||
inputs.notification_service.querySelector(`input[type="checkbox"][data-id="${default_service}"]`).checked = true;
|
||||
document.querySelectorAll('.weekday-bar > input[type="checkbox"]').forEach(el => el.checked = false);
|
||||
inputs.notification_service.querySelectorAll('input[type="checkbox"]')
|
||||
.forEach(c => c.checked = false);
|
||||
inputs.notification_service.querySelector(
|
||||
`input[type="checkbox"][data-id="${default_service}"]`
|
||||
).checked = true;
|
||||
document.querySelectorAll('.weekday-bar > input[type="checkbox"]')
|
||||
.forEach(el => el.checked = false);
|
||||
toggleNormal();
|
||||
toggleColor(true);
|
||||
selectColor(colors[0]);
|
||||
inputs.color_toggle.checked = false;
|
||||
document.getElementById('test-reminder').classList.remove('show-sent');
|
||||
|
||||
const cl = document.getElementById('info').classList;
|
||||
cl.forEach(c => {
|
||||
if (info_classes.includes(c)) cl.remove(c)
|
||||
if (InfoClasses.includes(c)) cl.remove(c)
|
||||
});
|
||||
document.querySelector('.options > button[type="submit"]').innerText = 'Add';
|
||||
if (type === types.reminder) {
|
||||
|
||||
document.querySelector('#test-reminder > div:first-child').innerText = 'Test';
|
||||
const title = document.querySelector('#info h2');
|
||||
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';
|
||||
title.innerText = 'Add a reminder';
|
||||
inputs.time.setAttribute('required', '');
|
||||
} else if (type === types.template) {
|
||||
} 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';
|
||||
title.innerText = 'Add a template';
|
||||
inputs.time.removeAttribute('required');
|
||||
} else if (type === types.static_reminder) {
|
||||
} 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';
|
||||
title.innerText = 'Add a static reminder';
|
||||
inputs.time.removeAttribute('required');
|
||||
} else
|
||||
return;
|
||||
@@ -38,14 +43,14 @@ function showAdd(type) {
|
||||
|
||||
function showEdit(id, type) {
|
||||
let url;
|
||||
if (type === types.reminder) {
|
||||
if (type === Types.reminder) {
|
||||
url = `${url_prefix}/api/reminders/${id}?api_key=${api_key}`;
|
||||
inputs.time.setAttribute('required', '');
|
||||
} else if (type === types.template) {
|
||||
} else if (type === Types.template) {
|
||||
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) {
|
||||
} 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');
|
||||
@@ -59,26 +64,27 @@ function showEdit(id, type) {
|
||||
})
|
||||
.then(json => {
|
||||
document.getElementById('info').dataset.id = id;
|
||||
if (json.result.color !== null) {
|
||||
if (inputs.color.classList.contains('hidden')) {
|
||||
toggleColor();
|
||||
};
|
||||
selectColor(json.result['color']);
|
||||
};
|
||||
|
||||
inputs.color_toggle.checked = false;
|
||||
selectColor(json.result.color || colors[0]);
|
||||
inputs.title.value = json.result.title;
|
||||
|
||||
if (type === types.reminder) {
|
||||
if (type === Types.reminder) {
|
||||
var trigger_date = new Date(
|
||||
(json.result.time + new Date(json.result.time * 1000).getTimezoneOffset() * -60) * 1000
|
||||
(json.result.time
|
||||
+ new Date(json.result.time * 1000).getTimezoneOffset()
|
||||
* -60
|
||||
) * 1000
|
||||
);
|
||||
inputs.time.value = trigger_date.toLocaleString('en-CA').slice(0,10) + 'T' + trigger_date.toTimeString().slice(0,5);
|
||||
inputs.time.value =
|
||||
trigger_date.toLocaleString('en-CA').slice(0,10)
|
||||
+ 'T'
|
||||
+ trigger_date.toTimeString().slice(0,5);
|
||||
};
|
||||
inputs.notification_service.querySelectorAll('input[type="checkbox"]').forEach(
|
||||
c => c.checked = json.result.notification_services.includes(parseInt(c.dataset.id))
|
||||
);
|
||||
|
||||
if (type == types.reminder) {
|
||||
if (type == Types.reminder) {
|
||||
if (json.result.repeat_interval !== null) {
|
||||
toggleRepeated();
|
||||
type_buttons.repeat_interval.value = json.result.repeat_interval;
|
||||
@@ -106,32 +112,23 @@ function showEdit(id, type) {
|
||||
|
||||
const cl = document.getElementById('info').classList;
|
||||
cl.forEach(c => {
|
||||
if (info_classes.includes(c)) cl.remove(c)
|
||||
if (InfoClasses.includes(c)) cl.remove(c)
|
||||
});
|
||||
document.querySelector('.options > button[type="submit"]').innerText = 'Save';
|
||||
if (type === types.reminder) {
|
||||
const title = document.querySelector('#info h2');
|
||||
const test_text = document.querySelector('#test-reminder > div:first-child');
|
||||
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) {
|
||||
title.innerText = 'Edit a reminder';
|
||||
test_text.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) {
|
||||
title.innerText = 'Edit a template';
|
||||
test_text.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';
|
||||
title.innerText = 'Edit a static reminder';
|
||||
test_text.innerText = 'Trigger';
|
||||
} else
|
||||
return;
|
||||
};
|
||||
|
||||
// code run on load
|
||||
|
||||
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));
|
||||
|
||||
@@ -26,9 +26,12 @@ function loadTemplateSelection() {
|
||||
function applyTemplate() {
|
||||
if (inputs.template.value === '0') {
|
||||
inputs.title.value = '';
|
||||
inputs.notification_service.querySelectorAll('input[type="checkbox"]:checked').forEach(c => c.checked = false)
|
||||
inputs.notification_service.querySelectorAll(
|
||||
'input[type="checkbox"]:checked'
|
||||
).forEach(c => c.checked = false)
|
||||
inputs.text.value = '';
|
||||
toggleColor(true);
|
||||
selectColor(colors[0]);
|
||||
|
||||
} else {
|
||||
fetch(`${url_prefix}/api/templates/${inputs.template.value}?api_key=${api_key}`)
|
||||
.then(response => {
|
||||
@@ -41,12 +44,7 @@ function applyTemplate() {
|
||||
c => c.checked = json.result.notification_services.includes(parseInt(c.dataset.id))
|
||||
);
|
||||
inputs.text.value = json.result.text;
|
||||
if (json.result.color !== null) {
|
||||
if (inputs.color.classList.contains('hidden'))
|
||||
toggleColor();
|
||||
selectColor(json.result.color);
|
||||
} else
|
||||
toggleColor(true);
|
||||
selectColor(json.result.color || colors[0]);
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401)
|
||||
|
||||
@@ -1,57 +1,54 @@
|
||||
const colors = ["#3c3c3c", "#49191e", "#171a42", "#083b06", "#3b3506", "#300e40"];
|
||||
|
||||
const inputs = {
|
||||
'template': document.getElementById('template-selection'),
|
||||
'title': document.getElementById('title-input'),
|
||||
'time': document.getElementById('time-input'),
|
||||
'notification_service': document.querySelector('.notification-service-list'),
|
||||
'text': document.getElementById('text-input'),
|
||||
'color': document.querySelector('.color-list')
|
||||
'template': document.querySelector('#template-selection'),
|
||||
'color_toggle': document.querySelector('#color-toggle'),
|
||||
'color_button': document.querySelector('#color-button'),
|
||||
'color': document.querySelector('.color-list'),
|
||||
'title': document.querySelector('#title-input'),
|
||||
'time': document.querySelector('#time-input'),
|
||||
'notification_service': document.querySelector('.notification-service-selection'),
|
||||
'text': document.querySelector('#text-input'),
|
||||
};
|
||||
|
||||
const type_buttons = {
|
||||
'normal_button': document.getElementById('normal-button'),
|
||||
'repeat_button': document.getElementById('repeat-button'),
|
||||
'weekday_button': document.getElementById('weekday-button'),
|
||||
'normal_button': document.querySelector('#normal-button'),
|
||||
'repeat_button': document.querySelector('#repeat-button'),
|
||||
'weekday_button': document.querySelector('#weekday-button'),
|
||||
|
||||
'repeat_bar': document.querySelector('.repeat-bar'),
|
||||
'repeat_interval': document.getElementById('repeat-interval'),
|
||||
'repeat_quantity': document.getElementById('repeat-quantity'),
|
||||
'repeat_interval': document.querySelector('#repeat-interval'),
|
||||
'repeat_quantity': document.querySelector('#repeat-quantity'),
|
||||
|
||||
'weekday_bar': document.querySelector('.weekday-bar')
|
||||
};
|
||||
|
||||
function loadColor() {
|
||||
colors.forEach(color => {
|
||||
const entry = document.createElement('button');
|
||||
entry.dataset.color = color;
|
||||
entry.title = color;
|
||||
entry.type = 'button';
|
||||
entry.style.setProperty('--color', color);
|
||||
entry.addEventListener('click', e => selectColor(color))
|
||||
inputs.color.appendChild(entry);
|
||||
function fillColors() {
|
||||
colors.forEach((color, idx) => {
|
||||
const entry_toggle = document.createElement('label');
|
||||
entry_toggle.title = color;
|
||||
entry_toggle.style.setProperty('--color', color);
|
||||
inputs.color.appendChild(entry_toggle);
|
||||
|
||||
const entry = document.createElement('input');
|
||||
entry.type = 'radio';
|
||||
entry.name = 'color_selection';
|
||||
entry.value = color;
|
||||
entry.checked = idx === 0;
|
||||
entry.classList.add('hidden');
|
||||
entry.onchange = e => {
|
||||
if (e.target === entry)
|
||||
inputs.color_button.style.setProperty('--color', color);
|
||||
};
|
||||
entry_toggle.appendChild(entry);
|
||||
|
||||
if (idx === 0)
|
||||
inputs.color_button.style.setProperty('--color', color);
|
||||
});
|
||||
};
|
||||
|
||||
function selectColor(color_code) {
|
||||
inputs.color.querySelector(`button[data-color="${color_code}"]`).dataset.selected = 'true';
|
||||
inputs.color.querySelectorAll(`button:not([data-color="${color_code}"])`).forEach(b => b.dataset.selected = 'false');
|
||||
return;
|
||||
};
|
||||
|
||||
function toggleColor(hide=false) {
|
||||
selectColor(colors[0])
|
||||
if (!hide)
|
||||
inputs.color.classList.toggle('hidden');
|
||||
else
|
||||
inputs.color.classList.add('hidden');
|
||||
};
|
||||
|
||||
function toggleNotificationService(hide=false) {
|
||||
if (!hide)
|
||||
inputs.notification_service.classList.toggle('hidden');
|
||||
else
|
||||
inputs.notification_service.classList.add('hidden');
|
||||
inputs.color.querySelector(`label[title="${color_code}"]`).click();
|
||||
};
|
||||
|
||||
function toggleNormal() {
|
||||
@@ -64,7 +61,8 @@ function toggleNormal() {
|
||||
type_buttons.repeat_interval.value = '';
|
||||
|
||||
type_buttons.weekday_bar.classList.add('hidden');
|
||||
type_buttons.weekday_bar.querySelectorAll('input[type="checkbox"]').forEach(el => el.checked = false);
|
||||
type_buttons.weekday_bar.querySelectorAll('input[type="checkbox"]')
|
||||
.forEach(el => el.checked = false);
|
||||
};
|
||||
|
||||
function toggleRepeated() {
|
||||
@@ -76,7 +74,8 @@ function toggleRepeated() {
|
||||
type_buttons.repeat_interval.setAttribute('required', '');
|
||||
|
||||
type_buttons.weekday_bar.classList.add('hidden');
|
||||
type_buttons.weekday_bar.querySelectorAll('input[type="checkbox"]').forEach(el => el.checked = false);
|
||||
type_buttons.weekday_bar.querySelectorAll('input[type="checkbox"]')
|
||||
.forEach(el => el.checked = false);
|
||||
};
|
||||
|
||||
function toggleWeekDay() {
|
||||
@@ -115,7 +114,7 @@ function testReminder() {
|
||||
};
|
||||
|
||||
const ns = [...
|
||||
document.querySelectorAll('.notification-service-list input[type="checkbox"]:checked')
|
||||
document.querySelectorAll('.notification-service-selection input[type="checkbox"]:checked')
|
||||
].map(c => parseInt(c.dataset.id))
|
||||
if (!ns.length) {
|
||||
input.classList.add('error-input');
|
||||
@@ -168,16 +167,16 @@ function deleteInfo() {
|
||||
|
||||
if (cl.contains('show-edit-reminder')) {
|
||||
// Delete reminder
|
||||
fillReminders();
|
||||
fillLibrary(Types.reminder);
|
||||
} else if (cl.contains('show-edit-template')) {
|
||||
// Delete template
|
||||
fillTemplates();
|
||||
fillLibrary(Types.template);
|
||||
loadTemplateSelection();
|
||||
} else if (cl.contains('show-edit-static-reminder')) {
|
||||
// Delete static reminder
|
||||
fillStaticReminders();
|
||||
fillLibrary(Types.static_reminder);
|
||||
};
|
||||
hideWindow();
|
||||
showWindow("home");
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401)
|
||||
@@ -202,14 +201,13 @@ function submitInfo() {
|
||||
const data = {
|
||||
'title': inputs.title.value,
|
||||
'notification_services': [...
|
||||
document.querySelectorAll('.notification-service-list input[type="checkbox"]:checked')
|
||||
document.querySelectorAll('.notification-service-selection input[type="checkbox"]:checked')
|
||||
].map(c => parseInt(c.dataset.id)),
|
||||
'text': inputs.text.value,
|
||||
'color': null
|
||||
};
|
||||
if (!inputs.color.classList.contains('hidden')) {
|
||||
data['color'] = inputs.color.querySelector('button[data-selected="true"]').dataset.color;
|
||||
'color': inputs.color.querySelector('input:checked').value
|
||||
};
|
||||
if (data.color === colors[0])
|
||||
data.color = null;
|
||||
|
||||
if (data.notification_services.length === 0) {
|
||||
inputs.notification_service.classList.add('error-input');
|
||||
@@ -221,7 +219,10 @@ function submitInfo() {
|
||||
const cl = document.getElementById('info').classList;
|
||||
if (cl.contains('show-add-reminder')) {
|
||||
// Add reminder
|
||||
data['time'] = (new Date(inputs.time.value) / 1000) + (new Date(inputs.time.value).getTimezoneOffset() * 60)
|
||||
data['time'] =
|
||||
(new Date(inputs.time.value) / 1000)
|
||||
+ (new Date(inputs.time.value).getTimezoneOffset() * 60);
|
||||
|
||||
if (type_buttons.repeat_button.dataset.selected === 'true') {
|
||||
data['repeat_quantity'] = type_buttons.repeat_quantity.value;
|
||||
data['repeat_interval'] = parseInt(type_buttons.repeat_interval.value)
|
||||
@@ -242,7 +243,7 @@ function submitInfo() {
|
||||
|
||||
fetch_data.url = `${url_prefix}/api/reminders?api_key=${api_key}`;
|
||||
fetch_data.method = 'POST';
|
||||
fetch_data.call_back = fillReminders;
|
||||
fetch_data.call_back = () => fillLibrary(Types.reminder);
|
||||
|
||||
} else if (cl.contains('show-add-template')) {
|
||||
// Add template
|
||||
@@ -250,18 +251,21 @@ function submitInfo() {
|
||||
fetch_data.method = 'POST';
|
||||
fetch_data.call_back = () => {
|
||||
loadTemplateSelection();
|
||||
fillTemplates();
|
||||
fillLibrary(Types.template);
|
||||
};
|
||||
|
||||
} 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';
|
||||
fetch_data.call_back = fillStaticReminders;
|
||||
fetch_data.call_back = () => fillLibrary(Types.static_reminder);
|
||||
|
||||
} else if (cl.contains('show-edit-reminder')) {
|
||||
// Edit reminder
|
||||
data['time'] = (new Date(inputs.time.value) / 1000) + (new Date(inputs.time.value).getTimezoneOffset() * 60)
|
||||
data['time'] =
|
||||
(new Date(inputs.time.value) / 1000)
|
||||
+ (new Date(inputs.time.value).getTimezoneOffset() * 60);
|
||||
|
||||
if (type_buttons.repeat_button.dataset.selected === 'true') {
|
||||
data['repeat_quantity'] = type_buttons.repeat_quantity.value;
|
||||
data['repeat_interval'] = parseInt(type_buttons.repeat_interval.value)
|
||||
@@ -282,7 +286,7 @@ function submitInfo() {
|
||||
|
||||
fetch_data.url = `${url_prefix}/api/reminders/${e_id}?api_key=${api_key}`;
|
||||
fetch_data.method = 'PUT';
|
||||
fetch_data.call_back = fillReminders;
|
||||
fetch_data.call_back = () => fillLibrary(Types.reminder);
|
||||
|
||||
} else if (cl.contains('show-edit-template')) {
|
||||
// Edit template
|
||||
@@ -290,14 +294,14 @@ function submitInfo() {
|
||||
fetch_data.method = 'PUT';
|
||||
fetch_data.call_back = () => {
|
||||
loadTemplateSelection();
|
||||
fillTemplates();
|
||||
fillLibrary(Types.template);
|
||||
};
|
||||
|
||||
} 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';
|
||||
fetch_data.call_back = fillStaticReminders;
|
||||
fetch_data.call_back = () => fillLibrary(Types.static_reminder);
|
||||
|
||||
} else return;
|
||||
|
||||
@@ -310,7 +314,7 @@ function submitInfo() {
|
||||
if (!response.ok) return Promise.reject(response.status);
|
||||
|
||||
fetch_data.call_back()
|
||||
hideWindow();
|
||||
showWindow("home");
|
||||
})
|
||||
.catch(e => {
|
||||
if (e === 401) {
|
||||
@@ -325,15 +329,13 @@ function submitInfo() {
|
||||
|
||||
// code run on load
|
||||
|
||||
loadColor();
|
||||
fillColors();
|
||||
|
||||
document.getElementById('template-selection').addEventListener('change', e => applyTemplate());
|
||||
document.getElementById('color-toggle').addEventListener('click', e => toggleColor());
|
||||
document.getElementById('toggle-notification-service-list').addEventListener('click', e => toggleNotificationService());
|
||||
document.getElementById('normal-button').addEventListener('click', e => toggleNormal());
|
||||
document.getElementById('repeat-button').addEventListener('click', e => toggleRepeated());
|
||||
document.getElementById('weekday-button').addEventListener('click', e => toggleWeekDay());
|
||||
document.getElementById('close-info').addEventListener('click', e => hideWindow());
|
||||
document.getElementById('delete-info').addEventListener('click', e => deleteInfo());
|
||||
document.getElementById('test-reminder').addEventListener('click', e => testReminder());
|
||||
document.getElementById('info-form').setAttribute('action', 'javascript:submitInfo();');
|
||||
document.querySelector('#template-selection').onchange = e => applyTemplate();
|
||||
document.querySelector('#normal-button').onclick = e => toggleNormal();
|
||||
document.querySelector('#repeat-button').onclick = e => toggleRepeated();
|
||||
document.querySelector('#weekday-button').onclick = e => toggleWeekDay();
|
||||
document.querySelector('#close-info').onclick = e => showWindow("home");
|
||||
document.querySelector('#delete-info').onclick = e => deleteInfo();
|
||||
document.querySelector('#test-reminder').onclick = e => testReminder();
|
||||
document.querySelector('#info-form').action = 'javascript:submitInfo();';
|
||||
|
||||
@@ -39,7 +39,7 @@
|
||||
<div class="form-container">
|
||||
<form id="settings-form">
|
||||
<h2>Authentication</h2>
|
||||
<div class="table-container">
|
||||
<div class="settings-table-container">
|
||||
<table class="settings-table">
|
||||
<tbody>
|
||||
<tr>
|
||||
@@ -86,24 +86,34 @@
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div class="table-container">
|
||||
<div class="user-table-container">
|
||||
<form id="add-user-form"></form>
|
||||
<table id="user-table">
|
||||
<thead>
|
||||
<th>User</th>
|
||||
<th></th>
|
||||
<th>Actions</th>
|
||||
</thead>
|
||||
<tbody id="add-user-row" class="hidden">
|
||||
<form id="add-user-form">
|
||||
<tr>
|
||||
<td>
|
||||
<input type="text" id="new-username-input" placeholder="Username">
|
||||
<input type="password" id="new-password-input" placeholder="Password">
|
||||
</td>
|
||||
<td>
|
||||
<button type="submit">Add</button>
|
||||
</td>
|
||||
</tr>
|
||||
</form>
|
||||
<tr>
|
||||
<td>
|
||||
<input type="text" id="new-username-input" form="add-user-form" placeholder="Username" required>
|
||||
</td>
|
||||
<td>
|
||||
<input type="password" id="new-password-input" form="add-user-form" placeholder="Password" required>
|
||||
</td>
|
||||
<td>
|
||||
<button type="submit" form="add-user-form">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
<tbody id="user-list">
|
||||
</tbody>
|
||||
|
||||
@@ -19,18 +19,25 @@
|
||||
<img src="{{ url_for('static', filename='img/favicon.svg') }}" alt="">
|
||||
</header>
|
||||
<main>
|
||||
<input type="checkbox" id="form-switch" class="hidden">
|
||||
|
||||
<div class="form-container">
|
||||
<form id="login-form">
|
||||
<h2>Login</h2>
|
||||
|
||||
<noscript>Javascript is disabled. The web-ui of MIND does not work with JavaScript disabled.</noscript>
|
||||
|
||||
<input type="text" autocomplete="username" placeholder="Username" required autofocus>
|
||||
<p class="error hidden" id="username-error">*Username not found</p>
|
||||
<div id="username-error-container">
|
||||
<input type="text" autocomplete="username" placeholder="Username" required autofocus>
|
||||
<p class="hidden">*Username not found</p>
|
||||
</div>
|
||||
|
||||
<input type="password" autocomplete="current-password" placeholder="Password" required>
|
||||
<p class="error hidden" id="password-error">*Password incorrect</p>
|
||||
<div id="password-error-container">
|
||||
<input type="password" autocomplete="current-password" placeholder="Password" required>
|
||||
<p class="hidden">*Password incorrect</p>
|
||||
</div>
|
||||
|
||||
<button type="button" class="switch-button">Or create an account</button>
|
||||
<label for="form-switch" class="switch-button">Or create an account</label>
|
||||
<button type="submit">Login</button>
|
||||
</form>
|
||||
</div>
|
||||
@@ -44,7 +51,7 @@
|
||||
|
||||
<input type="password" autocomplete="new-password" placeholder="Password" required>
|
||||
|
||||
<button type="button" class="switch-button">Or log into an account</button>
|
||||
<label for="form-switch" class="switch-button">Or log into an account</label>
|
||||
<button type="submit">Create</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
@@ -7,8 +7,8 @@
|
||||
|
||||
<link rel="shortcut icon" href="{{ url_for('static', filename='img/favicon.svg') }}" type="image/x-icon">
|
||||
|
||||
<link rel="stylesheet" href="/static/css/general.css">
|
||||
<link rel="stylesheet" href="/static/css/page_not_found.css">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/general.css') }}">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/page_not_found.css') }}">
|
||||
|
||||
<title>Page Not Found - MIND</title>
|
||||
</head>
|
||||
|
||||
@@ -24,9 +24,56 @@
|
||||
<title>Reminders - MIND</title>
|
||||
</head>
|
||||
<body>
|
||||
<input type="checkbox" class="hidden" id="wide-toggle">
|
||||
<div class="hidden element-storage">
|
||||
<table>
|
||||
<tbody>
|
||||
<tr class="notification-service-row" data-id="">
|
||||
<td class="title-column">
|
||||
<input type="text" readonly>
|
||||
</td>
|
||||
<td class="url-column">
|
||||
<input type="text" readonly>
|
||||
</td>
|
||||
<td class="action-column">
|
||||
<button data-type="edit" title="Edit">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M22.94,1.06a3.626,3.626,0,0,0-5.124,0L0,18.876V24H5.124L22.94,6.184A3.627,3.627,0,0,0,22.94,1.06ZM4.3,22H2V19.7L15.31,6.4l2.3,2.3ZM21.526,4.77,19.019,7.277l-2.295-2.3L19.23,2.474a1.624,1.624,0,0,1,2.3,2.3Z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
<button data-type="save" title="Save Edits">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M12,10a4,4,0,1,0,4,4A4,4,0,0,0,12,10Zm0,6a2,2,0,1,1,2-2A2,2,0,0,1,12,16Z"></path>
|
||||
<path d="M22.536,4.122,19.878,1.464A4.966,4.966,0,0,0,16.343,0H5A5.006,5.006,0,0,0,0,5V19a5.006,5.006,0,0,0,5,5H19a5.006,5.006,0,0,0,5-5V7.657A4.966,4.966,0,0,0,22.536,4.122ZM17,2.08V3a3,3,0,0,1-3,3H10A3,3,0,0,1,7,3V2h9.343A2.953,2.953,0,0,1,17,2.08ZM22,19a3,3,0,0,1-3,3H5a3,3,0,0,1-3-3V5A3,3,0,0,1,5,2V3a5.006,5.006,0,0,0,5,5h4a4.991,4.991,0,0,0,4.962-4.624l2.16,2.16A3.02,3.02,0,0,1,22,7.657Z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
<button data-type="delete" title="Delete">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M22,4H17V2a2,2,0,0,0-2-2H9A2,2,0,0,0,7,2V4H2V6H4V21a3,3,0,0,0,3,3H17a3,3,0,0,0,3-3V6h2ZM9,2h6V4H9Zm9,19a1,1,0,0,1-1,1H7a1,1,0,0,1-1-1V6H18Z"></path>
|
||||
<rect x="9" y="10" width="2" height="8"></rect>
|
||||
<rect x="13" y="10" width="2" height="8"></rect>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
<header>
|
||||
<div>
|
||||
<button id="toggle-nav">
|
||||
<input type="checkbox" id="nav-switch" class="hidden">
|
||||
<label for="nav-switch" id="toggle-nav">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<rect y="11" width="24" height="2" rx="1"></rect>
|
||||
@@ -34,7 +81,7 @@
|
||||
<rect y="18" width="24" height="2" rx="1"></rect>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</label>
|
||||
<img src="{{ url_for('static', filename='img/favicon.svg') }}" alt="">
|
||||
</div>
|
||||
</header>
|
||||
@@ -79,9 +126,12 @@
|
||||
<main class="window-container">
|
||||
<div id="home">
|
||||
<div class="tab-selector">
|
||||
<button data-target="reminder-tab" data-selected="true">Reminders</button>
|
||||
<button data-target="static-reminder-tab">Static Reminders</button>
|
||||
<button data-target="template-tab">Templates</button>
|
||||
<input type="radio" name="tab-selector" id="reminder-tab-selector" checked>
|
||||
<label for="reminder-tab-selector">Reminders</label>
|
||||
<input type="radio" name="tab-selector" id="static-tab-selector">
|
||||
<label for="static-tab-selector">Static Reminders</label>
|
||||
<input type="radio" name="tab-selector" id="template-tab-selector">
|
||||
<label for="template-tab-selector">Templates</label>
|
||||
</div>
|
||||
<div class="search-container">
|
||||
<noscript>Javascript is disabled. The web-ui of MIND does not work with JavaScript disabled.</noscript>
|
||||
@@ -106,251 +156,286 @@
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
<select id="sort-input"></select>
|
||||
<select id="sort-input">
|
||||
<option value="time">Time</option>
|
||||
<option value="time_reversed">Time Reversed</option>
|
||||
<option value="title">Title</option>
|
||||
<option value="title_reversed">Title Reversed</option>
|
||||
<option value="date_added">Date Added</option>
|
||||
<option value="date_added_reversed">Date Added Reversed</option>
|
||||
</select>
|
||||
<label id="wide-button" for="wide-toggle" aria-hidden="true" title="Toggle wide list">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="512" height="512">
|
||||
<path d="M23.312,10.012c-.29-.327-.574-.637-.771-.834L19.713,6.3a1,1,0,0,0-1.426,1.4l2.834,2.885c.108.108.244.255.389.414H2.555c.146-.16.284-.308.4-.42L5.779,7.7A1,1,0,0,0,4.353,6.3L1.53,9.172c-.2.2-.487.513-.777.84A2.99,2.99,0,0,0,0,11.994v.012a3,3,0,0,0,.754,1.983c.289.326.573.636.769.833L4.353,17.7a1,1,0,0,0,1.426-1.4L2.944,13.414c-.108-.108-.244-.255-.389-.414H21.51c-.145.16-.283.308-.4.42L18.287,16.3a1,1,0,1,0,1.426,1.4l2.822-2.873c.2-.2.486-.513.777-.84A3,3,0,0,0,23.312,10.012Z"/>
|
||||
</svg>
|
||||
</label>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
<div id="reminder-tab">
|
||||
<button class="entry add-entry" id="add-reminder" aria-label="Add reminder" title="Add reminder">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<div class="tab-container">
|
||||
<div id="reminder-tab">
|
||||
<button class="entry add-entry" id="add-reminder" title="Add reminder">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<p>Add a notification service first!</p>
|
||||
</button>
|
||||
</div>
|
||||
<div id="static-reminder-tab" class="hidden">
|
||||
<button class="entry add-entry" id="add-static-reminder" aria-label="Add static reminder" title="Add static reminder">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<div id="template-tab" class="hidden">
|
||||
<button class="entry add-entry" id="add-template" aria-label="Add template" title="Add template">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="info" class="hidden">
|
||||
<h2></h2>
|
||||
<div class="form-container">
|
||||
<form id="info-form">
|
||||
<div class="sub-inputs">
|
||||
<select id="template-selection">
|
||||
<option value="0" selected>No template</option>
|
||||
</select>
|
||||
<button type="button" id="color-toggle">Color</button>
|
||||
</div>
|
||||
<div class="color-list hidden"></div>
|
||||
<input type="text" id="title-input" placeholder="Title" required>
|
||||
<div class="sub-inputs">
|
||||
<input type="datetime-local" id="time-input" required>
|
||||
<button type="button" id="toggle-notification-service-list">Notification Services</button>
|
||||
</div>
|
||||
<div class="notification-service-list hidden"></div>
|
||||
<div class="repeat-options sub-inputs">
|
||||
<button type="button" id="normal-button" data-selected="true">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 507.506 507.506" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M163.865 436.934a54.228 54.228 0 0 1-38.4-15.915L9.369 304.966c-12.492-12.496-12.492-32.752 0-45.248 12.496-12.492 32.752-12.492 45.248 0l109.248 109.248L452.889 79.942c12.496-12.492 32.752-12.492 45.248 0 12.492 12.496 12.492 32.752 0 45.248L202.265 421.019a54.228 54.228 0 0 1-38.4 15.915z"></path>
|
||||
<path d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
Normal
|
||||
</button>
|
||||
<button type="button" id="repeat-button" data-selected="false">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 507.506 507.506" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M163.865 436.934a54.228 54.228 0 0 1-38.4-15.915L9.369 304.966c-12.492-12.496-12.492-32.752 0-45.248 12.496-12.492 32.752-12.492 45.248 0l109.248 109.248L452.889 79.942c12.496-12.492 32.752-12.492 45.248 0 12.492 12.496 12.492 32.752 0 45.248L202.265 421.019a54.228 54.228 0 0 1-38.4 15.915z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
Repeated
|
||||
</button>
|
||||
<button type="button" id="weekday-button" data-selected="false">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 507.506 507.506" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M163.865 436.934a54.228 54.228 0 0 1-38.4-15.915L9.369 304.966c-12.492-12.496-12.492-32.752 0-45.248 12.496-12.492 32.752-12.492 45.248 0l109.248 109.248L452.889 79.942c12.496-12.492 32.752-12.492 45.248 0 12.492 12.496 12.492 32.752 0 45.248L202.265 421.019a54.228 54.228 0 0 1-38.4 15.915z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
Week Days
|
||||
</button>
|
||||
</div>
|
||||
<div class="repeat-bar hidden">
|
||||
<p>Repeat every </p>
|
||||
<input type="number" id="repeat-interval" placeholder="interval" min="1" step="1" oninput="validity.valid || (value='');">
|
||||
<select id="repeat-quantity">
|
||||
<option value="minutes">Minute(s)</option>
|
||||
<option value="hours">Hour(s)</option>
|
||||
<option value="days" selected>Day(s)</option>
|
||||
<option value="weeks">Week(s)</option>
|
||||
<option value="months">Month(s)</option>
|
||||
<option value="years">Year(s)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="weekday-bar hidden">
|
||||
<p>Mo</p>
|
||||
<input type="checkbox">
|
||||
<p>Tu</p>
|
||||
<input type="checkbox">
|
||||
<p>We</p>
|
||||
<input type="checkbox">
|
||||
<p>Thu</p>
|
||||
<input type="checkbox">
|
||||
<p>Fr</p>
|
||||
<input type="checkbox">
|
||||
<p>Sa</p>
|
||||
<input type="checkbox">
|
||||
<p>Su</p>
|
||||
<input type="checkbox">
|
||||
</div>
|
||||
<textarea id="text-input" cols="30" rows="10" placeholder="Text (optional)"></textarea>
|
||||
<div class="options">
|
||||
<button type="button" id="close-info">Cancel</button>
|
||||
<button type="button" id="test-reminder">
|
||||
<div>Test</div>
|
||||
<div>Sent</div>
|
||||
</button>
|
||||
<button type="button" id="delete-info">Delete</button>
|
||||
<button type="submit">Add</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="notification" class="hidden">
|
||||
<h2>Notification Services</h2>
|
||||
<p>Setup your notification providers here</p>
|
||||
<div class="table-container">
|
||||
<button id="add-service-button" title="Add notification service" aria-label="Add notification service">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</button>
|
||||
<div class="overflow-container">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="title-column">Title</th>
|
||||
<th class="url-column">Apprise URL</th>
|
||||
<th title="Actions" aria-label="Actions" class="action-column">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M11.24,24a2.262,2.262,0,0,1-.948-.212,2.18,2.18,0,0,1-1.2-2.622L10.653,16H6.975A3,3,0,0,1,4.1,12.131l3.024-10A2.983,2.983,0,0,1,10,0h3.693a2.6,2.6,0,0,1,2.433,3.511L14.443,8H17a3,3,0,0,1,2.483,4.684l-6.4,10.3A2.2,2.2,0,0,1,11.24,24ZM10,2a1,1,0,0,0-.958.71l-3.024,10A1,1,0,0,0,6.975,14H12a1,1,0,0,1,.957,1.29L11.01,21.732a.183.183,0,0,0,.121.241A.188.188,0,0,0,11.4,21.9l6.4-10.3a1,1,0,0,0,.078-1.063A.979.979,0,0,0,17,10H13a1,1,0,0,1-.937-1.351l2.19-5.84A.6.6,0,0,0,13.693,2Z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="services-list">
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add-service-container">
|
||||
<div id="service-list">
|
||||
</div>
|
||||
<form id="add-service-window"></form>
|
||||
</div>
|
||||
</svg>
|
||||
<p>Add a notification service first!</p>
|
||||
</button>
|
||||
</div>
|
||||
<div id="static-reminder-tab">
|
||||
<button class="entry add-entry" id="add-static-reminder" title="Add static reminder">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<p>Add a notification service first!</p>
|
||||
</button>
|
||||
</div>
|
||||
<div id="template-tab">
|
||||
<button class="entry add-entry" id="add-template" title="Add template">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
<p>Add a notification service first!</p>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="settings" class="hidden">
|
||||
<h2>Settings</h2>
|
||||
<div class="settings-container">
|
||||
<h3>Locale</h3>
|
||||
<select id="locale-input">
|
||||
<option value="ar-SA">ar-SA</option>
|
||||
<option value="bn-BD">bn-BD</option>
|
||||
<option value="bn-IN">bn-IN</option>
|
||||
<option value="cs-CZ">cs-CZ</option>
|
||||
<option value="da-DK">da-DK</option>
|
||||
<option value="de-AT">de-AT</option>
|
||||
<option value="de-CH">de-CH</option>
|
||||
<option value="de-DE">de-DE</option>
|
||||
<option value="el-GR">el-GR</option>
|
||||
<option value="en-AU">en-AU</option>
|
||||
<option value="en-CA">en-CA</option>
|
||||
<option value="en-GB" selected>en-GB</option>
|
||||
<option value="en-IE">en-IE</option>
|
||||
<option value="en-IN">en-IN</option>
|
||||
<option value="en-NZ">en-NZ</option>
|
||||
<option value="en-US">en-US</option>
|
||||
<option value="en-ZA">en-ZA</option>
|
||||
<option value="es-AR">es-AR</option>
|
||||
<option value="es-CL">es-CL</option>
|
||||
<option value="es-CO">es-CO</option>
|
||||
<option value="es-ES">es-ES</option>
|
||||
<option value="es-MX">es-MX</option>
|
||||
<option value="es-US">es-US</option>
|
||||
<option value="fi-FI">fi-FI</option>
|
||||
<option value="fr-BE">fr-BE</option>
|
||||
<option value="fr-CA">fr-CA</option>
|
||||
<option value="fr-CH">fr-CH</option>
|
||||
<option value="fr-FR">fr-FR</option>
|
||||
<option value="he-IL">he-IL</option>
|
||||
<option value="hi-IN">hi-IN</option>
|
||||
<option value="hu-HU">hu-HU</option>
|
||||
<option value="id-ID">id-ID</option>
|
||||
<option value="it-CH">it-CH</option>
|
||||
<option value="it-IT">it-IT</option>
|
||||
<option value="ja-JP">ja-JP</option>
|
||||
<option value="ko-KR">ko-KR</option>
|
||||
<option value="nl-BE">nl-BE</option>
|
||||
<option value="nl-NL">nl-NL</option>
|
||||
<option value="no-NO">no-NO</option>
|
||||
<option value="pl-PL">pl-PL</option>
|
||||
<option value="pt-BR">pt-BR</option>
|
||||
<option value="pt-PT">pt-PT</option>
|
||||
<option value="ro-RO">ro-RO</option>
|
||||
<option value="ru-RU">ru-RU</option>
|
||||
<option value="sk-SK">sk-SK</option>
|
||||
<option value="sv-SE">sv-SE</option>
|
||||
<option value="ta-IN">ta-IN</option>
|
||||
<option value="ta-LK">ta-LK</option>
|
||||
<option value="th-TH">th-TH</option>
|
||||
<option value="tr-TR">tr-TR</option>
|
||||
<option value="zh-CN">zh-CN</option>
|
||||
<option value="zh-HK">zh-HK</option>
|
||||
<option value="zh-TW">zh-TW</option>
|
||||
</select>
|
||||
|
||||
<h3>Default Notification Service</h3>
|
||||
<select id="default-service-input"></select>
|
||||
|
||||
<h3>Change Password</h3>
|
||||
<form id="change-password-form">
|
||||
<input type="password" id="password-input" autocomplete="new-password" required>
|
||||
<button type="submit">Change</button>
|
||||
</form>
|
||||
|
||||
<h3>Delete Account</h3>
|
||||
<button id="delete-account-button">Delete Account</button>
|
||||
<div class="extra-window-container">
|
||||
<div id="info">
|
||||
<h2></h2>
|
||||
<div class="form-container">
|
||||
<form id="info-form">
|
||||
<input type="checkbox" id="color-toggle" class="hidden">
|
||||
<input type="checkbox" id="notification-service-selection-toggle" class="hidden">
|
||||
<div class="sub-inputs">
|
||||
<select id="template-selection">
|
||||
<option value="0" selected>No template</option>
|
||||
</select>
|
||||
<label for="color-toggle" id="color-button" class="as-button">Color</label>
|
||||
</div>
|
||||
<div class="color-list"></div>
|
||||
<input type="text" id="title-input" placeholder="Title" required>
|
||||
<div class="sub-inputs">
|
||||
<input type="datetime-local" id="time-input" required>
|
||||
<label for="notification-service-selection-toggle" id="notification-service-selection-button">Notification Services</label>
|
||||
</div>
|
||||
<div class="notification-service-selection"></div>
|
||||
<div class="repeat-options sub-inputs">
|
||||
<button type="button" id="normal-button" data-selected="true">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 507.506 507.506" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M163.865 436.934a54.228 54.228 0 0 1-38.4-15.915L9.369 304.966c-12.492-12.496-12.492-32.752 0-45.248 12.496-12.492 32.752-12.492 45.248 0l109.248 109.248L452.889 79.942c12.496-12.492 32.752-12.492 45.248 0 12.492 12.496 12.492 32.752 0 45.248L202.265 421.019a54.228 54.228 0 0 1-38.4 15.915z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
Normal
|
||||
</button>
|
||||
<button type="button" id="repeat-button" data-selected="false">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 507.506 507.506" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M163.865 436.934a54.228 54.228 0 0 1-38.4-15.915L9.369 304.966c-12.492-12.496-12.492-32.752 0-45.248 12.496-12.492 32.752-12.492 45.248 0l109.248 109.248L452.889 79.942c12.496-12.492 32.752-12.492 45.248 0 12.492 12.496 12.492 32.752 0 45.248L202.265 421.019a54.228 54.228 0 0 1-38.4 15.915z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
Repeated
|
||||
</button>
|
||||
<button type="button" id="weekday-button" data-selected="false">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 507.506 507.506" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M163.865 436.934a54.228 54.228 0 0 1-38.4-15.915L9.369 304.966c-12.492-12.496-12.492-32.752 0-45.248 12.496-12.492 32.752-12.492 45.248 0l109.248 109.248L452.889 79.942c12.496-12.492 32.752-12.492 45.248 0 12.492 12.496 12.492 32.752 0 45.248L202.265 421.019a54.228 54.228 0 0 1-38.4 15.915z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
Week Days
|
||||
</button>
|
||||
</div>
|
||||
<div class="repeat-bar hidden">
|
||||
<p>Repeat every </p>
|
||||
<input type="number" id="repeat-interval" placeholder="interval" min="1" step="1" oninput="validity.valid || (value='');">
|
||||
<select id="repeat-quantity">
|
||||
<option value="minutes">Minute(s)</option>
|
||||
<option value="hours">Hour(s)</option>
|
||||
<option value="days" selected>Day(s)</option>
|
||||
<option value="weeks">Week(s)</option>
|
||||
<option value="months">Month(s)</option>
|
||||
<option value="years">Year(s)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="weekday-bar hidden">
|
||||
<p>Mo</p>
|
||||
<input type="checkbox">
|
||||
<p>Tu</p>
|
||||
<input type="checkbox">
|
||||
<p>We</p>
|
||||
<input type="checkbox">
|
||||
<p>Thu</p>
|
||||
<input type="checkbox">
|
||||
<p>Fr</p>
|
||||
<input type="checkbox">
|
||||
<p>Sa</p>
|
||||
<input type="checkbox">
|
||||
<p>Su</p>
|
||||
<input type="checkbox">
|
||||
</div>
|
||||
<textarea id="text-input" cols="30" rows="10" placeholder="Text (optional)"></textarea>
|
||||
<div class="options">
|
||||
<button type="button" id="close-info">Cancel</button>
|
||||
<button type="button" id="test-reminder">
|
||||
<div>Test</div>
|
||||
<div>Sent</div>
|
||||
</button>
|
||||
<button type="button" id="delete-info">Delete</button>
|
||||
<button type="submit">Add</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
<h2>Contact and Donation</h2>
|
||||
<div class="contact-list">
|
||||
<a href="https://ko-fi.com/casvt">Donate to MIND</a>
|
||||
<a href="https://casvt.github.io/MIND/">Documentation</a>
|
||||
<a href="https://github.com/Casvt/MIND/issues">Report an issue</a>
|
||||
<a href="https://discord.gg/nMNdgG7vsE">Discord server</a>
|
||||
|
||||
|
||||
<div id="notification">
|
||||
<h2>Notification Services</h2>
|
||||
<p>Setup your notification providers here</p>
|
||||
<div class="table-container">
|
||||
<label
|
||||
id="add-service-button"
|
||||
title="Toggle adding notification service"
|
||||
for="service-list-toggle"
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<g>
|
||||
<path d="M480,224H288V32c0-17.673-14.327-32-32-32s-32,14.327-32,32v192H32c-17.673,0-32,14.327-32,32s14.327,32,32,32h192v192 c0,17.673,14.327,32,32,32s32-14.327,32-32V288h192c17.673,0,32-14.327,32-32S497.673,224,480,224z"></path>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
||||
</label>
|
||||
<div class="overflow-container">
|
||||
<input type="checkbox" id="service-list-toggle" class="hidden">
|
||||
<input type="checkbox" id="add-service-toggle" class="hidden">
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
<th class="title-column">Title</th>
|
||||
<th class="url-column">Apprise URL</th>
|
||||
<th title="Actions" aria-label="Actions" class="action-column">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:svgjs="http://svgjs.com/svgjs" width="256" height="256" x="0" y="0" viewBox="0 0 24 24" style="enable-background:new 0 0 512 512" xml:space="preserve">
|
||||
<g>
|
||||
<path d="M11.24,24a2.262,2.262,0,0,1-.948-.212,2.18,2.18,0,0,1-1.2-2.622L10.653,16H6.975A3,3,0,0,1,4.1,12.131l3.024-10A2.983,2.983,0,0,1,10,0h3.693a2.6,2.6,0,0,1,2.433,3.511L14.443,8H17a3,3,0,0,1,2.483,4.684l-6.4,10.3A2.2,2.2,0,0,1,11.24,24ZM10,2a1,1,0,0,0-.958.71l-3.024,10A1,1,0,0,0,6.975,14H12a1,1,0,0,1,.957,1.29L11.01,21.732a.183.183,0,0,0,.121.241A.188.188,0,0,0,11.4,21.9l6.4-10.3a1,1,0,0,0,.078-1.063A.979.979,0,0,0,17,10H13a1,1,0,0,1-.937-1.351l2.19-5.84A.6.6,0,0,0,13.693,2Z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody id="services-list">
|
||||
</tbody>
|
||||
</table>
|
||||
<div id="add-service-container">
|
||||
<div id="service-list">
|
||||
</div>
|
||||
<form id="add-service-window"></form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div id="settings">
|
||||
<h2>Settings</h2>
|
||||
<table id="settings-table">
|
||||
<tr>
|
||||
<td><label for="locale-input">Locale</label></td>
|
||||
<td>
|
||||
<select id="locale-input">
|
||||
<option value="ar-SA">ar-SA</option>
|
||||
<option value="bn-BD">bn-BD</option>
|
||||
<option value="bn-IN">bn-IN</option>
|
||||
<option value="cs-CZ">cs-CZ</option>
|
||||
<option value="da-DK">da-DK</option>
|
||||
<option value="de-AT">de-AT</option>
|
||||
<option value="de-CH">de-CH</option>
|
||||
<option value="de-DE">de-DE</option>
|
||||
<option value="el-GR">el-GR</option>
|
||||
<option value="en-AU">en-AU</option>
|
||||
<option value="en-CA">en-CA</option>
|
||||
<option value="en-GB" selected>en-GB</option>
|
||||
<option value="en-IE">en-IE</option>
|
||||
<option value="en-IN">en-IN</option>
|
||||
<option value="en-NZ">en-NZ</option>
|
||||
<option value="en-US">en-US</option>
|
||||
<option value="en-ZA">en-ZA</option>
|
||||
<option value="es-AR">es-AR</option>
|
||||
<option value="es-CL">es-CL</option>
|
||||
<option value="es-CO">es-CO</option>
|
||||
<option value="es-ES">es-ES</option>
|
||||
<option value="es-MX">es-MX</option>
|
||||
<option value="es-US">es-US</option>
|
||||
<option value="fi-FI">fi-FI</option>
|
||||
<option value="fr-BE">fr-BE</option>
|
||||
<option value="fr-CA">fr-CA</option>
|
||||
<option value="fr-CH">fr-CH</option>
|
||||
<option value="fr-FR">fr-FR</option>
|
||||
<option value="he-IL">he-IL</option>
|
||||
<option value="hi-IN">hi-IN</option>
|
||||
<option value="hu-HU">hu-HU</option>
|
||||
<option value="id-ID">id-ID</option>
|
||||
<option value="it-CH">it-CH</option>
|
||||
<option value="it-IT">it-IT</option>
|
||||
<option value="ja-JP">ja-JP</option>
|
||||
<option value="ko-KR">ko-KR</option>
|
||||
<option value="nl-BE">nl-BE</option>
|
||||
<option value="nl-NL">nl-NL</option>
|
||||
<option value="no-NO">no-NO</option>
|
||||
<option value="pl-PL">pl-PL</option>
|
||||
<option value="pt-BR">pt-BR</option>
|
||||
<option value="pt-PT">pt-PT</option>
|
||||
<option value="ro-RO">ro-RO</option>
|
||||
<option value="ru-RU">ru-RU</option>
|
||||
<option value="sk-SK">sk-SK</option>
|
||||
<option value="sv-SE">sv-SE</option>
|
||||
<option value="ta-IN">ta-IN</option>
|
||||
<option value="ta-LK">ta-LK</option>
|
||||
<option value="th-TH">th-TH</option>
|
||||
<option value="tr-TR">tr-TR</option>
|
||||
<option value="zh-CN">zh-CN</option>
|
||||
<option value="zh-HK">zh-HK</option>
|
||||
<option value="zh-TW">zh-TW</option>
|
||||
</select>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="default-service-input">Default Notification Service</label></td>
|
||||
<td><select id="default-service-input"></select></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="password-input">Change Password</label></td>
|
||||
<td>
|
||||
<form id="change-password-form">
|
||||
<input type="password" id="password-input" autocomplete="new-password" required>
|
||||
<button type="submit">Change</button>
|
||||
</form>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><label for="delete-account-button">Delete Account</label></td>
|
||||
<td><button id="delete-account-button">Delete Account</button></td>
|
||||
</tr>
|
||||
</table>
|
||||
<h2>Contact and Donation</h2>
|
||||
<div class="contact-list">
|
||||
<a href="https://ko-fi.com/casvt">Donate to MIND</a>
|
||||
<a href="https://casvt.github.io/MIND/">Documentation</a>
|
||||
<a href="https://github.com/Casvt/MIND/issues">Report an issue</a>
|
||||
<a href="https://discord.gg/nMNdgG7vsE">Discord server</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
Reference in New Issue
Block a user