removed remainder of old personal dashboard code. fixed big lighthouse issues.
This commit is contained in:
parent
85d27c5bba
commit
0f05018cab
@ -24,10 +24,11 @@ CREATE TABLE reminder_template (
|
||||
`embed_author` VARCHAR(256) NOT NULL DEFAULT '',
|
||||
`embed_author_url` VARCHAR(512),
|
||||
`embed_color` INT UNSIGNED NOT NULL DEFAULT 0x0,
|
||||
`embed_fields` JSON,
|
||||
|
||||
PRIMARY KEY (id),
|
||||
|
||||
FOREIGN KEY (`guild_id`) REFERENCES channels (`id`) ON DELETE CASCADE
|
||||
FOREIGN KEY (`guild_id`) REFERENCES guilds (`id`) ON DELETE CASCADE
|
||||
);
|
||||
|
||||
ALTER TABLE reminders ADD COLUMN embed_fields JSON;
|
||||
|
@ -11,7 +11,8 @@ use std::{collections::HashMap, env};
|
||||
use oauth2::{basic::BasicClient, AuthUrl, ClientId, ClientSecret, RedirectUrl, TokenUrl};
|
||||
use rocket::{
|
||||
fs::FileServer,
|
||||
serde::json::{json, Json, Value as JsonValue},
|
||||
serde::json::{json, Value as JsonValue},
|
||||
shield::Shield,
|
||||
tokio::sync::broadcast::Sender,
|
||||
};
|
||||
use rocket_dyn_templates::Template;
|
||||
@ -119,12 +120,15 @@ pub async fn initialize(
|
||||
.mount(
|
||||
"/dashboard",
|
||||
routes![
|
||||
routes::dashboard::dashboard,
|
||||
routes::dashboard::dashboard_home,
|
||||
routes::dashboard::user::get_user_info,
|
||||
routes::dashboard::user::update_user_info,
|
||||
routes::dashboard::user::get_user_guilds,
|
||||
routes::dashboard::guild::get_guild_channels,
|
||||
routes::dashboard::guild::get_guild_roles,
|
||||
routes::dashboard::guild::get_reminder_templates,
|
||||
routes::dashboard::guild::create_reminder_template,
|
||||
routes::dashboard::guild::create_reminder,
|
||||
routes::dashboard::guild::get_reminders,
|
||||
routes::dashboard::guild::edit_reminder,
|
||||
|
@ -24,8 +24,8 @@ use crate::{
|
||||
MAX_URL_LENGTH, MAX_USERNAME_LENGTH, MIN_INTERVAL,
|
||||
},
|
||||
routes::dashboard::{
|
||||
create_database_channel, generate_uid, name_default, DeleteReminder, PatchReminder,
|
||||
Reminder,
|
||||
create_database_channel, generate_uid, name_default, template_name_default, DeleteReminder,
|
||||
PatchReminder, Reminder, ReminderTemplate,
|
||||
},
|
||||
};
|
||||
|
||||
@ -131,6 +131,135 @@ pub async fn get_guild_roles(id: u64, cookies: &CookieJar<'_>, ctx: &State<Conte
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/api/guild/<id>/templates")]
|
||||
pub async fn get_reminder_templates(
|
||||
id: u64,
|
||||
cookies: &CookieJar<'_>,
|
||||
ctx: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonValue {
|
||||
check_authorization!(cookies, ctx.inner(), id);
|
||||
|
||||
match sqlx::query_as_unchecked!(
|
||||
ReminderTemplate,
|
||||
"SELECT * FROM reminder_template WHERE guild_id = (SELECT id FROM guilds WHERE guild = ?)",
|
||||
id
|
||||
)
|
||||
.fetch_all(pool.inner())
|
||||
.await
|
||||
{
|
||||
Ok(templates) => {
|
||||
json!(templates)
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Could not fetch templates from {}: {:?}", id, e);
|
||||
|
||||
json!({"error": "Could not get templates"})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/api/guild/<id>/templates", data = "<reminder_template>")]
|
||||
pub async fn create_reminder_template(
|
||||
id: u64,
|
||||
reminder_template: Json<ReminderTemplate>,
|
||||
cookies: &CookieJar<'_>,
|
||||
ctx: &State<Context>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonValue {
|
||||
check_authorization!(cookies, ctx.inner(), id);
|
||||
|
||||
// validate lengths
|
||||
check_length!(MAX_CONTENT_LENGTH, reminder_template.content);
|
||||
check_length!(MAX_EMBED_DESCRIPTION_LENGTH, reminder_template.embed_description);
|
||||
check_length!(MAX_EMBED_TITLE_LENGTH, reminder_template.embed_title);
|
||||
check_length!(MAX_EMBED_AUTHOR_LENGTH, reminder_template.embed_author);
|
||||
check_length!(MAX_EMBED_FOOTER_LENGTH, reminder_template.embed_footer);
|
||||
check_length_opt!(MAX_EMBED_FIELDS, reminder_template.embed_fields);
|
||||
if let Some(fields) = &reminder_template.embed_fields {
|
||||
for field in &fields.0 {
|
||||
check_length!(MAX_EMBED_FIELD_VALUE_LENGTH, field.value);
|
||||
check_length!(MAX_EMBED_FIELD_TITLE_LENGTH, field.title);
|
||||
}
|
||||
}
|
||||
check_length_opt!(MAX_USERNAME_LENGTH, reminder_template.username);
|
||||
check_length_opt!(
|
||||
MAX_URL_LENGTH,
|
||||
reminder_template.embed_footer_url,
|
||||
reminder_template.embed_thumbnail_url,
|
||||
reminder_template.embed_author_url,
|
||||
reminder_template.embed_image_url,
|
||||
reminder_template.avatar
|
||||
);
|
||||
|
||||
// validate urls
|
||||
check_url_opt!(
|
||||
reminder_template.embed_footer_url,
|
||||
reminder_template.embed_thumbnail_url,
|
||||
reminder_template.embed_author_url,
|
||||
reminder_template.embed_image_url,
|
||||
reminder_template.avatar
|
||||
);
|
||||
|
||||
let name = if reminder_template.name.is_empty() {
|
||||
template_name_default()
|
||||
} else {
|
||||
reminder_template.name.clone()
|
||||
};
|
||||
|
||||
match sqlx::query!(
|
||||
"INSERT INTO reminder_template
|
||||
(guild_id,
|
||||
name,
|
||||
attachment,
|
||||
attachment_name,
|
||||
avatar,
|
||||
content,
|
||||
embed_author,
|
||||
embed_author_url,
|
||||
embed_color,
|
||||
embed_description,
|
||||
embed_footer,
|
||||
embed_footer_url,
|
||||
embed_image_url,
|
||||
embed_thumbnail_url,
|
||||
embed_title,
|
||||
embed_fields,
|
||||
tts,
|
||||
username
|
||||
) VALUES ((SELECT id FROM guilds WHERE guild = ?), ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
|
||||
id, name,
|
||||
reminder_template.attachment,
|
||||
reminder_template.attachment_name,
|
||||
reminder_template.avatar,
|
||||
reminder_template.content,
|
||||
reminder_template.embed_author,
|
||||
reminder_template.embed_author_url,
|
||||
reminder_template.embed_color,
|
||||
reminder_template.embed_description,
|
||||
reminder_template.embed_footer,
|
||||
reminder_template.embed_footer_url,
|
||||
reminder_template.embed_image_url,
|
||||
reminder_template.embed_thumbnail_url,
|
||||
reminder_template.embed_title,
|
||||
reminder_template.embed_fields,
|
||||
reminder_template.tts,
|
||||
reminder_template.username,
|
||||
)
|
||||
.fetch_all(pool.inner())
|
||||
.await
|
||||
{
|
||||
Ok(_) => {
|
||||
json!({})
|
||||
}
|
||||
Err(e) => {
|
||||
warn!("Could not fetch templates from {}: {:?}", id, e);
|
||||
|
||||
json!({"error": "Could not get templates"})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[post("/api/guild/<id>/reminders", data = "<reminder>")]
|
||||
pub async fn create_reminder(
|
||||
id: u64,
|
||||
@ -550,9 +679,8 @@ pub async fn edit_reminder(
|
||||
}
|
||||
}
|
||||
|
||||
#[delete("/api/guild/<id>/reminders", data = "<reminder>")]
|
||||
#[delete("/api/guild/<_>/reminders", data = "<reminder>")]
|
||||
pub async fn delete_reminder(
|
||||
id: u64,
|
||||
reminder: Json<DeleteReminder>,
|
||||
pool: &State<Pool<MySql>>,
|
||||
) -> JsonValue {
|
||||
|
@ -22,10 +22,44 @@ fn name_default() -> String {
|
||||
"Reminder".to_string()
|
||||
}
|
||||
|
||||
fn template_name_default() -> String {
|
||||
"Template".to_string()
|
||||
}
|
||||
|
||||
fn channel_default() -> u64 {
|
||||
0
|
||||
}
|
||||
|
||||
fn id_default() -> u32 {
|
||||
0
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct ReminderTemplate {
|
||||
#[serde(default = "id_default")]
|
||||
id: u32,
|
||||
#[serde(default = "id_default")]
|
||||
guild_id: u32,
|
||||
#[serde(default = "template_name_default")]
|
||||
name: String,
|
||||
attachment: Option<Vec<u8>>,
|
||||
attachment_name: Option<String>,
|
||||
avatar: Option<String>,
|
||||
content: String,
|
||||
embed_author: String,
|
||||
embed_author_url: Option<String>,
|
||||
embed_color: u32,
|
||||
embed_description: String,
|
||||
embed_footer: String,
|
||||
embed_footer_url: Option<String>,
|
||||
embed_image_url: Option<String>,
|
||||
embed_thumbnail_url: Option<String>,
|
||||
embed_title: String,
|
||||
embed_fields: Option<Json<Vec<EmbedField>>>,
|
||||
tts: bool,
|
||||
username: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Serialize, Deserialize)]
|
||||
pub struct EmbedField {
|
||||
title: String,
|
||||
@ -241,3 +275,13 @@ pub async fn dashboard_home(cookies: &CookieJar<'_>) -> Result<Template, Redirec
|
||||
Err(Redirect::to("/login/discord"))
|
||||
}
|
||||
}
|
||||
|
||||
#[get("/<_>")]
|
||||
pub async fn dashboard(cookies: &CookieJar<'_>) -> Result<Template, Redirect> {
|
||||
if cookies.get_private("userid").is_some() {
|
||||
let map: HashMap<&str, String> = HashMap::new();
|
||||
Ok(Template::render("dashboard", &map))
|
||||
} else {
|
||||
Err(Redirect::to("/login/discord"))
|
||||
}
|
||||
}
|
||||
|
@ -3,52 +3,61 @@
|
||||
font-style: italic;
|
||||
font-weight: 300;
|
||||
src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightItalic'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKwdSBYKcSV-LCoeQqfX1RYOo3qPZZMkids18E.ttf) format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: italic;
|
||||
font-weight: 400;
|
||||
src: local('Source Sans Pro Italic'), local('SourceSansPro-Italic'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xK1dSBYKcSV-LCoeQqfX1RYOo3qPZ7nsDc.ttf) format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: italic;
|
||||
font-weight: 600;
|
||||
src: local('Source Sans Pro SemiBold Italic'), local('SourceSansPro-SemiBoldItalic'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKwdSBYKcSV-LCoeQqfX1RYOo3qPZY4lCds18E.ttf) format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 300;
|
||||
src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ik4zwlxdr.ttf) format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: local('Source Sans Pro Regular'), local('SourceSansPro-Regular'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xK3dSBYKcSV-LCoeQqfX1RYOo3qOK7g.ttf) format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 600;
|
||||
src: local('Source Sans Pro SemiBold'), local('SourceSansPro-SemiBold'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3i54rwlxdr.ttf) format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Source Sans Pro';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url(https://fonts.gstatic.com/s/sourcesanspro/v13/6xKydSBYKcSV-LCoeQqfX1RYOo3ig4vwlxdr.ttf) format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
src: url(https://fonts.gstatic.com/s/ubuntu/v15/4iCs6KVjbNBYlgo6eA.ttf) format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
@font-face {
|
||||
font-family: 'Ubuntu';
|
||||
font-style: normal;
|
||||
font-weight: 700;
|
||||
src: url(https://fonts.gstatic.com/s/ubuntu/v15/4iCv6KVjbNBYlgoCxCvTtw.ttf) format('truetype');
|
||||
font-display: swap;
|
||||
}
|
||||
|
@ -2,6 +2,10 @@
|
||||
font-family: "Ubuntu Bold", "Ubuntu", sans-serif;
|
||||
}
|
||||
|
||||
button {
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
/* override styles for when the div is collapsed */
|
||||
div.reminderContent.is-collapsed .column.discord-frame {
|
||||
display: none;
|
||||
@ -55,6 +59,11 @@ div.reminderContent.is-collapsed button.hide-box i {
|
||||
/* END */
|
||||
|
||||
/* dashboard styles */
|
||||
button.inline-btn {
|
||||
height: 100%;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
button.change-color {
|
||||
position: absolute;
|
||||
left: calc(-1rem - 40px);
|
||||
@ -88,7 +97,7 @@ div.interval-group > button {
|
||||
}
|
||||
|
||||
/* Interval inputs */
|
||||
div.interval-group > .interval-group-left > input {
|
||||
div.interval-group > .interval-group-left input {
|
||||
-webkit-appearance: none;
|
||||
border-style: none;
|
||||
background-color: #eee;
|
||||
@ -96,11 +105,11 @@ div.interval-group > .interval-group-left > input {
|
||||
font-family: monospace;
|
||||
}
|
||||
|
||||
div.interval-group > .interval-group-left > input.w2 {
|
||||
div.interval-group > .interval-group-left input.w2 {
|
||||
width: 3ch;
|
||||
}
|
||||
|
||||
div.interval-group > .interval-group-left > input.w3 {
|
||||
div.interval-group > .interval-group-left input.w3 {
|
||||
width: 6ch;
|
||||
}
|
||||
|
||||
@ -153,6 +162,15 @@ span.patreon-color {
|
||||
color: #f96854;
|
||||
}
|
||||
|
||||
p.pageTitle {
|
||||
margin-left: 12px;
|
||||
}
|
||||
|
||||
#welcome > div {
|
||||
height: 100%;
|
||||
padding-top: 30vh;
|
||||
}
|
||||
|
||||
div#pageNavbar {
|
||||
background-color: #363636;
|
||||
}
|
||||
@ -188,6 +206,17 @@ div.dashboard-sidebar {
|
||||
padding-right: 0;
|
||||
}
|
||||
|
||||
div.dashboard-sidebar:not(.mobile-sidebar) {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
div.dashboard-sidebar:not(.mobile-sidebar) .aside-footer {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
width: 226px;
|
||||
}
|
||||
|
||||
div.mobile-sidebar {
|
||||
z-index: 100;
|
||||
min-height: 100vh;
|
||||
@ -197,10 +226,24 @@ div.mobile-sidebar {
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
#expandAll {
|
||||
width: 60px;
|
||||
}
|
||||
|
||||
div.mobile-sidebar .aside-footer {
|
||||
margin-top: auto;
|
||||
}
|
||||
|
||||
div.mobile-sidebar.is-active {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
aside.menu {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
div.dashboard-frame {
|
||||
min-height: 100vh;
|
||||
margin-bottom: 0 !important;
|
||||
@ -475,3 +518,40 @@ textarea, input {
|
||||
height: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* loader */
|
||||
#loader {
|
||||
position: fixed;
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
width: 100vw;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
#loader .title {
|
||||
font-size: 6rem;
|
||||
}
|
||||
|
||||
/* END */
|
||||
|
||||
/* other stuff */
|
||||
|
||||
.half-rem {
|
||||
width: 0.5rem;
|
||||
}
|
||||
|
||||
.pad-left {
|
||||
width: 12px;
|
||||
}
|
||||
|
||||
#dead {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.colorpicker-container {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.create-reminder {
|
||||
margin: 0 12px 12px 12px;
|
||||
}
|
||||
|
@ -6,9 +6,19 @@ const $colorPickerInput = $colorPickerModal.querySelector("input");
|
||||
const $deleteReminderBtn = document.querySelector("#delete-reminder-confirm");
|
||||
const $reminderTemplate = document.querySelector("template#guildReminder");
|
||||
const $embedFieldTemplate = document.querySelector("template#embedFieldTemplate");
|
||||
const $createReminder = document.querySelector("#reminderCreator");
|
||||
const $createReminderBtn = $createReminder.querySelector("button#createReminder");
|
||||
const $createTemplateBtn = $createReminder.querySelector("button#createTemplate");
|
||||
const $loadTemplateBtn = document.querySelector("button#load-template");
|
||||
const $templateSelect = document.querySelector("select#templateSelect");
|
||||
|
||||
let channels;
|
||||
let roles;
|
||||
let channels = [];
|
||||
let roles = [];
|
||||
let templates = {};
|
||||
|
||||
function guildId() {
|
||||
return document.querySelector(".guildList a.is-active").dataset["guild"];
|
||||
}
|
||||
|
||||
function colorToInt(r, g, b) {
|
||||
return (r << 16) + (g << 8) + b;
|
||||
@ -77,6 +87,30 @@ function fetch_roles(guild_id) {
|
||||
});
|
||||
}
|
||||
|
||||
function fetch_templates(guild_id) {
|
||||
fetch(`/dashboard/api/guild/${guild_id}/templates`)
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
show_error(data.error);
|
||||
} else {
|
||||
templates = {};
|
||||
|
||||
const select = document.querySelector("#templateSelect");
|
||||
select.innerHTML = "";
|
||||
for (let template of data) {
|
||||
templates[template["id"]] = template;
|
||||
|
||||
let option = document.createElement("option");
|
||||
option.value = template["id"];
|
||||
option.textContent = template["name"];
|
||||
|
||||
select.appendChild(option);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async function fetch_channels(guild_id) {
|
||||
const event = new Event("channelsLoading");
|
||||
document.dispatchEvent(event);
|
||||
@ -121,7 +155,7 @@ async function fetch_reminders(guild_id) {
|
||||
newFrame.querySelector(".reminderContent").dataset["uid"] =
|
||||
reminder["uid"];
|
||||
|
||||
deserialize_reminder(reminder, newFrame);
|
||||
deserialize_reminder(reminder, newFrame, "load");
|
||||
|
||||
$reminderBox.appendChild(newFrame);
|
||||
|
||||
@ -137,8 +171,21 @@ async function fetch_reminders(guild_id) {
|
||||
});
|
||||
}
|
||||
|
||||
async function serialize_reminder(node) {
|
||||
let interval = get_interval(node);
|
||||
async function serialize_reminder(node, mode) {
|
||||
let interval, utc_time;
|
||||
|
||||
if (mode !== "template") {
|
||||
interval = get_interval(node);
|
||||
|
||||
utc_time = luxon.DateTime.fromISO(
|
||||
node.querySelector('input[name="time"]').value
|
||||
).setZone("UTC");
|
||||
if (utc_time.invalid) {
|
||||
return { error: "Time provided invalid." };
|
||||
} else {
|
||||
utc_time = utc_time.toFormat("yyyy-LL-dd'T'HH:mm:ss");
|
||||
}
|
||||
}
|
||||
|
||||
let rgb_color = window.getComputedStyle(
|
||||
node.querySelector("div.discord-embed")
|
||||
@ -146,21 +193,13 @@ async function serialize_reminder(node) {
|
||||
let rgb = rgb_color.match(/\d+/g);
|
||||
let color = colorToInt(parseInt(rgb[0]), parseInt(rgb[1]), parseInt(rgb[2]));
|
||||
|
||||
let utc_time = luxon.DateTime.fromISO(
|
||||
node.querySelector('input[name="time"]').value
|
||||
).setZone("UTC");
|
||||
|
||||
if (utc_time.invalid) {
|
||||
return { error: "Time provided invalid." };
|
||||
}
|
||||
|
||||
let fields = [
|
||||
...node.querySelectorAll("div.embed-multifield-box div.embed-field-box"),
|
||||
]
|
||||
.map((el) => {
|
||||
return {
|
||||
title: el.querySelector("textarea#embedFieldTitle").value,
|
||||
value: el.querySelector("textarea#embedFieldValue").value,
|
||||
title: el.querySelector("textarea.discord-field-title").value,
|
||||
value: el.querySelector("textarea.discord-field-value").value,
|
||||
inline: el.dataset["inlined"] === "1",
|
||||
};
|
||||
})
|
||||
@ -181,13 +220,21 @@ async function serialize_reminder(node) {
|
||||
attachment_name = file.name;
|
||||
}
|
||||
|
||||
const reminderContent = node.closest(".reminderContent");
|
||||
let uid = "";
|
||||
if (mode === "edit") {
|
||||
uid = node.closest(".reminderContent").dataset["uid"];
|
||||
}
|
||||
|
||||
let enabled = null;
|
||||
if (mode === "create") {
|
||||
enabled = true;
|
||||
}
|
||||
|
||||
return {
|
||||
// if we're creating a reminder, ignore this field
|
||||
uid: reminderContent !== null ? reminderContent.dataset["uid"] : "",
|
||||
uid: uid,
|
||||
// if we're editing a reminder, ignore this field
|
||||
enabled: reminderContent !== null ? null : true,
|
||||
enabled: enabled,
|
||||
restartable: false,
|
||||
attachment: attachment,
|
||||
attachment_name: attachment_name,
|
||||
@ -207,17 +254,17 @@ async function serialize_reminder(node) {
|
||||
embed_title: node.querySelector('textarea[name="embed_title"]').value,
|
||||
embed_fields: fields,
|
||||
expires: null,
|
||||
interval_seconds: interval.seconds,
|
||||
interval_months: interval.months,
|
||||
interval_seconds: mode !== "template" ? interval.seconds : null,
|
||||
interval_months: mode !== "template" ? interval.months : null,
|
||||
name: node.querySelector('input[name="name"]').value,
|
||||
pin: node.querySelector('input[name="pin"]').checked,
|
||||
tts: node.querySelector('input[name="tts"]').checked,
|
||||
username: node.querySelector('input[name="username"]').value,
|
||||
utc_time: utc_time.toFormat("yyyy-LL-dd'T'HH:mm:ss"),
|
||||
utc_time: utc_time,
|
||||
};
|
||||
}
|
||||
|
||||
function deserialize_reminder(reminder, frame) {
|
||||
function deserialize_reminder(reminder, frame, mode) {
|
||||
// populate channels
|
||||
set_channels(frame.querySelector("select.channel-selector"));
|
||||
|
||||
@ -256,16 +303,18 @@ function deserialize_reminder(reminder, frame) {
|
||||
.insertBefore(embed_field, lastChild);
|
||||
}
|
||||
|
||||
if (reminder["interval_seconds"] !== null) update_interval(frame);
|
||||
if (mode !== "template") {
|
||||
if (reminder["interval_seconds"]) update_interval(frame);
|
||||
|
||||
let $enableBtn = frame.querySelector(".disable-enable");
|
||||
$enableBtn.dataset["action"] = reminder["enabled"] ? "disable" : "enable";
|
||||
let $enableBtn = frame.querySelector(".disable-enable");
|
||||
$enableBtn.dataset["action"] = reminder["enabled"] ? "disable" : "enable";
|
||||
|
||||
let timeInput = frame.querySelector('input[name="time"]');
|
||||
let localTime = luxon.DateTime.fromISO(reminder["utc_time"], { zone: "UTC" }).setZone(
|
||||
timezone
|
||||
);
|
||||
timeInput.value = localTime.toFormat("yyyy-LL-dd'T'HH:mm:ss");
|
||||
let timeInput = frame.querySelector('input[name="time"]');
|
||||
let localTime = luxon.DateTime.fromISO(reminder["utc_time"], {
|
||||
zone: "UTC",
|
||||
}).setZone(timezone);
|
||||
timeInput.value = localTime.toFormat("yyyy-LL-dd'T'HH:mm:ss");
|
||||
}
|
||||
}
|
||||
|
||||
document.addEventListener("guildSwitched", async (e) => {
|
||||
@ -276,11 +325,11 @@ document.addEventListener("guildSwitched", async (e) => {
|
||||
);
|
||||
|
||||
switch_pane($anchor.dataset["pane"]);
|
||||
reset_guild_pane();
|
||||
$anchor.classList.add("is-active");
|
||||
|
||||
reset_guild_pane();
|
||||
|
||||
fetch_roles(e.detail.guild_id);
|
||||
fetch_templates(e.detail.guild_id);
|
||||
await fetch_channels(e.detail.guild_id);
|
||||
fetch_reminders(e.detail.guild_id);
|
||||
|
||||
@ -303,7 +352,7 @@ document.addEventListener("channelsLoaded", () => {
|
||||
});
|
||||
|
||||
document.addEventListener("remindersLoaded", (event) => {
|
||||
const guild = document.querySelector(".guildList a.is-active").dataset["guild"];
|
||||
const guild = guildId();
|
||||
|
||||
for (let reminder of event.detail) {
|
||||
let node = reminder.node;
|
||||
@ -351,13 +400,13 @@ document.addEventListener("remindersLoaded", (event) => {
|
||||
"fas fa-spinner fa-spin",
|
||||
];
|
||||
|
||||
let reminder = await serialize_reminder(node);
|
||||
let reminder = await serialize_reminder(node, "edit");
|
||||
if (reminder.error) {
|
||||
show_error(reminder.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let guild = document.querySelector(".guildList a.is-active").dataset["guild"];
|
||||
let guild = guildId();
|
||||
|
||||
fetch(`/dashboard/api/guild/${guild}/reminders`, {
|
||||
method: "PATCH",
|
||||
@ -381,7 +430,7 @@ document.addEventListener("remindersLoaded", (event) => {
|
||||
});
|
||||
|
||||
$deleteReminderBtn.addEventListener("click", () => {
|
||||
let guild = document.querySelector(".guildList a.is-active").dataset["guild"];
|
||||
let guild = guildId();
|
||||
|
||||
fetch(`/dashboard/api/guild/${guild}/reminders`, {
|
||||
method: "DELETE",
|
||||
@ -422,7 +471,7 @@ $colorPickerModal.querySelector("button.is-success").addEventListener("click", (
|
||||
$colorPickerModal.classList.remove("is-active");
|
||||
});
|
||||
|
||||
document.querySelectorAll("a.show-modal").forEach((element) => {
|
||||
document.querySelectorAll(".show-modal").forEach((element) => {
|
||||
element.addEventListener("click", (e) => {
|
||||
e.preventDefault();
|
||||
document.getElementById(element.dataset["modal"]).classList.toggle("is-active");
|
||||
@ -480,10 +529,15 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
);
|
||||
$anchor.dataset["guild"] = guild.id;
|
||||
$anchor.dataset["name"] = guild.name;
|
||||
$anchor.href = `/dashboard/${guild.id}?name=${guild.name}`;
|
||||
|
||||
$anchor.addEventListener("click", async (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
window.history.pushState(
|
||||
{},
|
||||
"",
|
||||
`/dashboard/${guild.id}?name=${guild.name}`
|
||||
);
|
||||
const event = new CustomEvent("guildSwitched", {
|
||||
detail: {
|
||||
guild_name: guild.name,
|
||||
@ -497,6 +551,21 @@ document.addEventListener("DOMContentLoaded", () => {
|
||||
element.append($clone);
|
||||
});
|
||||
}
|
||||
|
||||
const matches = window.location.href.match(/dashboard\/(\d+)/);
|
||||
if (matches) {
|
||||
let id = matches[1];
|
||||
let name =
|
||||
new URLSearchParams(window.location.search).get("name") || id;
|
||||
const event = new CustomEvent("guildSwitched", {
|
||||
detail: {
|
||||
guild_name: name,
|
||||
guild_id: id,
|
||||
},
|
||||
});
|
||||
|
||||
document.dispatchEvent(event);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
@ -530,34 +599,18 @@ function has_source(string) {
|
||||
}
|
||||
}
|
||||
|
||||
let $createReminder = document.querySelector("#reminderCreator");
|
||||
let $createBtn = $createReminder.querySelector("button#createReminder");
|
||||
$createReminderBtn.addEventListener("click", async () => {
|
||||
$createReminderBtn.querySelector("span.icon > i").classList = [
|
||||
"fas fa-spinner fa-spin",
|
||||
];
|
||||
|
||||
$createBtn.addEventListener("click", async () => {
|
||||
$createBtn.querySelector("span.icon > i").classList = ["fas fa-spinner fa-spin"];
|
||||
|
||||
let attachment = null;
|
||||
let attachment_name = null;
|
||||
|
||||
if ($createReminder.querySelector('input[name="attachment"]').files.length > 0) {
|
||||
let file = $createReminder.querySelector('input[name="attachment"]').files[0];
|
||||
|
||||
attachment = await new Promise((resolve) => {
|
||||
let fileReader = new FileReader();
|
||||
fileReader.onload = (e) => resolve(fileReader.result);
|
||||
fileReader.readAsDataURL(file);
|
||||
});
|
||||
attachment = attachment.split(",")[1];
|
||||
attachment_name = file.name;
|
||||
}
|
||||
|
||||
let reminder = await serialize_reminder($createReminder);
|
||||
let reminder = await serialize_reminder($createReminder, "create");
|
||||
if (reminder.error) {
|
||||
show_error(reminder.error);
|
||||
return;
|
||||
}
|
||||
|
||||
let guild = document.querySelector(".guildList a.is-active").dataset["guild"];
|
||||
let guild = guildId();
|
||||
|
||||
fetch(`/dashboard/api/guild/${guild}/reminders`, {
|
||||
method: "POST",
|
||||
@ -570,13 +623,17 @@ $createBtn.addEventListener("click", async () => {
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
show_error(data.error);
|
||||
|
||||
$createReminderBtn.querySelector("span.icon > i").classList = [
|
||||
"fas fa-sparkles",
|
||||
];
|
||||
} else {
|
||||
const $reminderBox = document.querySelector("div#guildReminders");
|
||||
let newFrame = $reminderTemplate.content.cloneNode(true);
|
||||
|
||||
newFrame.querySelector(".reminderContent").dataset["uid"] = data["uid"];
|
||||
|
||||
deserialize_reminder(data, newFrame);
|
||||
deserialize_reminder(data, newFrame, "load");
|
||||
|
||||
$reminderBox.appendChild(newFrame);
|
||||
|
||||
@ -587,16 +644,62 @@ $createBtn.addEventListener("click", async () => {
|
||||
detail: [data],
|
||||
})
|
||||
);
|
||||
|
||||
$createReminderBtn.querySelector("span.icon > i").classList = [
|
||||
"fas fa-check",
|
||||
];
|
||||
|
||||
window.setTimeout(() => {
|
||||
$createReminderBtn.querySelector("span.icon > i").classList = [
|
||||
"fas fa-sparkles",
|
||||
];
|
||||
}, 1500);
|
||||
}
|
||||
|
||||
$createBtn.querySelector("span.icon > i").classList = ["fas fa-check"];
|
||||
|
||||
window.setTimeout(() => {
|
||||
$createBtn.querySelector("span.icon > i").classList = ["fas fa-sparkles"];
|
||||
}, 1500);
|
||||
});
|
||||
});
|
||||
|
||||
$createTemplateBtn.addEventListener("click", async () => {
|
||||
let reminder = await serialize_reminder($createReminder, "template");
|
||||
let guild = guildId();
|
||||
|
||||
fetch(`/dashboard/api/guild/${guild}/templates`, {
|
||||
method: "POST",
|
||||
headers: {
|
||||
"Content-Type": "application/json",
|
||||
},
|
||||
body: JSON.stringify(reminder),
|
||||
})
|
||||
.then((response) => response.json())
|
||||
.then((data) => {
|
||||
if (data.error) {
|
||||
show_error(data.error);
|
||||
$createTemplateBtn.querySelector("span.icon > i").classList = [
|
||||
"fas fa-file-spreadsheet",
|
||||
];
|
||||
} else {
|
||||
fetch_templates(guildId());
|
||||
|
||||
$createTemplateBtn.querySelector("span.icon > i").classList = [
|
||||
"fas fa-check",
|
||||
];
|
||||
|
||||
window.setTimeout(() => {
|
||||
$createTemplateBtn.querySelector("span.icon > i").classList = [
|
||||
"fas fa-file-spreadsheet",
|
||||
];
|
||||
}, 1500);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
$loadTemplateBtn.addEventListener("click", (ev) => {
|
||||
deserialize_reminder(
|
||||
templates[parseInt($templateSelect.value)],
|
||||
$createReminder,
|
||||
"template"
|
||||
);
|
||||
});
|
||||
|
||||
document.querySelectorAll("textarea.autoresize").forEach((element) => {
|
||||
element.addEventListener("input", () => {
|
||||
element.style.height = "";
|
||||
|
@ -6,6 +6,7 @@
|
||||
<meta charset="UTF-8">
|
||||
<meta name="yandex-verification" content="bb77b8681eb64a90"/>
|
||||
<meta name="google-site-verification" content="7h7UVTeEe0AOzHiH3cFtsqMULYGN-zCZdMT_YCkW1Ho"/>
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; img-src *; font-src fonts.gstatic.com 'self'">
|
||||
|
||||
<!-- favicon -->
|
||||
<link rel="apple-touch-icon" sizes="180x180"
|
||||
@ -27,10 +28,7 @@
|
||||
<link rel="stylesheet" href="/static/css/style.css">
|
||||
<link rel="stylesheet" href="/static/css/dtsel.css">
|
||||
|
||||
<script src="/static/js/iro.js"></script>
|
||||
<script src="/static/js/dtsel.js"></script>
|
||||
<script src="/static/js/luxon.min.js"></script>
|
||||
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar is-spaced is-size-4 is-hidden-desktop dashboard-navbar" role="navigation"
|
||||
@ -54,10 +52,10 @@
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<div id="loader" class="is-hidden hero is-fullheight" style="position: fixed; background-color: rgba(255, 255, 255, 0.8); width: 100vw; z-index: 999;">
|
||||
<div id="loader" class="is-hidden hero is-fullheight">
|
||||
<div class="hero-body">
|
||||
<div class="container has-text-centered">
|
||||
<p class="title" style="font-size: 6rem; color: #8fb677">
|
||||
<p class="title">
|
||||
<i class="fas fa-cog fa-spin"></i>
|
||||
</p>
|
||||
<p class="subtitle">
|
||||
@ -68,7 +66,7 @@
|
||||
</div>
|
||||
|
||||
<!-- dead image used to check which other images are dead -->
|
||||
<img style="display: none;" src="" id="dead">
|
||||
<img src="" id="dead">
|
||||
|
||||
<div class="notification is-danger flash-message" id="errors">
|
||||
<span class="icon"><i class="far fa-exclamation-circle"></i></span> <span class="error-message"></span>
|
||||
@ -100,7 +98,7 @@
|
||||
<button class="delete close-modal" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div style="display: flex; justify-content: center">
|
||||
<div class="colorpicker-container">
|
||||
<div id="colorpicker"></div>
|
||||
</div>
|
||||
<input class="input" id="colorInput">
|
||||
@ -140,6 +138,33 @@
|
||||
<button class="modal-close is-large close-modal" aria-label="close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="chooseTemplateModal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
<header class="modal-card-head">
|
||||
<label class="modal-card-title" for="urlInput">Load Template</label>
|
||||
<button class="delete close-modal" aria-label="close"></button>
|
||||
</header>
|
||||
<section class="modal-card-body">
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-fullwidth">
|
||||
<select id="templateSelect">
|
||||
</select>
|
||||
</div>
|
||||
<div class="icon is-small is-left">
|
||||
<i class="fas fa-file-spreadsheet"></i>
|
||||
</div>
|
||||
</div>
|
||||
<br>
|
||||
<div class="has-text-centered">
|
||||
<button class="button is-success close-modal" id="load-template">Load Template</button>
|
||||
<button class="button is-danger" id="delete-template">Delete</button>
|
||||
</div>
|
||||
</section>
|
||||
</div>
|
||||
<button class="modal-close is-large close-modal" aria-label="close"></button>
|
||||
</div>
|
||||
|
||||
<div class="modal" id="dataManagerModal">
|
||||
<div class="modal-background"></div>
|
||||
<div class="modal-card">
|
||||
@ -179,7 +204,7 @@
|
||||
</div>
|
||||
|
||||
<div class="columns is-gapless dashboard-frame">
|
||||
<div class="column is-2 is-sidebar-menu dashboard-sidebar is-hidden-touch" style="display: flex; flex-direction: column;">
|
||||
<div class="column is-2 is-sidebar-menu dashboard-sidebar is-hidden-touch">
|
||||
<a href="/">
|
||||
<div class="brand">
|
||||
<img src="/static/img/logo_flat.webp" alt="Reminder bot logo"
|
||||
@ -192,14 +217,14 @@
|
||||
d="M0,192L60,170.7C120,149,240,107,360,96C480,85,600,107,720,138.7C840,171,960,213,1080,197.3C1200,181,1320,107,1380,69.3L1440,32L1440,0L1380,0C1320,0,1200,0,1080,0C960,0,840,0,720,0C600,0,480,0,360,0C240,0,120,0,60,0L0,0Z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<aside class="menu" style="display: flex; flex-direction: column; flex-grow: 1;">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">
|
||||
Servers
|
||||
</p>
|
||||
<ul class="menu-list guildList">
|
||||
|
||||
</ul>
|
||||
<div class="aside-footer" style="position: fixed; bottom: 0; width: 226px;">
|
||||
<div class="aside-footer">
|
||||
<p class="menu-label">
|
||||
Settings
|
||||
</p>
|
||||
@ -230,14 +255,14 @@
|
||||
d="M0,192L60,170.7C120,149,240,107,360,96C480,85,600,107,720,138.7C840,171,960,213,1080,197.3C1200,181,1320,107,1380,69.3L1440,32L1440,0L1380,0C1320,0,1200,0,1080,0C960,0,840,0,720,0C600,0,480,0,360,0C240,0,120,0,60,0L0,0Z"></path>
|
||||
</g>
|
||||
</svg>
|
||||
<aside class="menu" style="display: flex; flex-direction: column; flex-grow: 1;">
|
||||
<aside class="menu">
|
||||
<p class="menu-label">
|
||||
Servers
|
||||
</p>
|
||||
<ul class="menu-list guildList">
|
||||
|
||||
</ul>
|
||||
<div class="aside-footer" style="margin-top: auto;">
|
||||
<div class="aside-footer">
|
||||
<p class="menu-label">
|
||||
Settings
|
||||
</p>
|
||||
@ -257,17 +282,14 @@
|
||||
|
||||
<!-- main content -->
|
||||
<div class="column is-main-content">
|
||||
<p class="title pageTitle" style="margin-left: 12px;"></p>
|
||||
<p class="title pageTitle"></p>
|
||||
<section id="welcome">
|
||||
<div class="has-text-centered" style="height: 100%; padding-top: 30vh;">
|
||||
<div class="has-text-centered">
|
||||
<p class="title">Welcome!</p>
|
||||
<p class="subtitle is-hidden-touch">Select an option from the side to get started</p>
|
||||
<p class="subtitle is-hidden-desktop">Press the <span class="icon"><i class="fal fa-bars"></i></span> to get started</p>
|
||||
</div>
|
||||
</section>
|
||||
<section id="personal" class="is-hidden">
|
||||
{% include "reminder_dashboard/reminder_dashboard_personal" %}
|
||||
</section>
|
||||
<section id="guild" class="is-hidden">
|
||||
{% include "reminder_dashboard/reminder_dashboard" %}
|
||||
</section>
|
||||
@ -294,20 +316,26 @@
|
||||
|
||||
<template id="embedFieldTemplate">
|
||||
<div data-inlined="1" class="embed-field-box">
|
||||
<label class="is-sr-only" for="embedFieldTitle">Field Title</label>
|
||||
<div style="display: flex;">
|
||||
<textarea class="discord-field-title field-input message-input autoresize"
|
||||
placeholder="Field Title..." rows="1"
|
||||
maxlength="256" id="embedFieldTitle" name="embed_field_title[]"></textarea>
|
||||
<button class="button is-small inline-btn" style="height: 100%; padding: 5px;"><i class="fas fa-arrows-h"></i></button>
|
||||
<div class="is-flex">
|
||||
<label>
|
||||
<span class="is-sr-only">Field Title</span>
|
||||
<textarea class="discord-field-title field-input message-input autoresize"
|
||||
placeholder="Field Title..." rows="1"
|
||||
maxlength="256" name="embed_field_title[]"></textarea>
|
||||
</label>
|
||||
<button class="button is-small inline-btn">
|
||||
<span class="is-sr-only">Toggle field inline</span><i class="fas fa-arrows-h"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<label class="is-sr-only" for="embedFieldValue">Field Value</label>
|
||||
<textarea
|
||||
class="discord-field-value field-input message-input autoresize"
|
||||
placeholder="Field Value..."
|
||||
maxlength="1024" id="embedFieldValue" name="embed_field_value[]"
|
||||
rows="1"></textarea>
|
||||
<label>
|
||||
<span class="is-sr-only">Field Value</span>
|
||||
<textarea
|
||||
class="discord-field-value field-input message-input autoresize"
|
||||
placeholder="Field Value..."
|
||||
maxlength="1024" name="embed_field_value[]"
|
||||
rows="1"></textarea>
|
||||
</label>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@ -323,13 +351,12 @@
|
||||
{% include "reminder_dashboard/guild_reminder" %}
|
||||
</template>
|
||||
|
||||
<template id="personalReminder">
|
||||
{% include "reminder_dashboard/personal_reminder" %}
|
||||
</template>
|
||||
<script src="/static/js/iro.js"></script>
|
||||
<script src="/static/js/dtsel.js"></script>
|
||||
|
||||
<script src="/static/js/interval.js"></script>
|
||||
<script src="/static/js/timezone.js"></script>
|
||||
<script src="/static/js/main.js"></script>
|
||||
<script src="/static/js/timezone.js" defer></script>
|
||||
<script src="/static/js/main.js" defer></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
@ -4,33 +4,33 @@
|
||||
<figure class="media-left">
|
||||
<p class="image is-32x32 customizable">
|
||||
<a>
|
||||
<img class="is-rounded discord-avatar" src="/static/img/bg.webp">
|
||||
<img class="is-rounded discord-avatar" src="/static/img/bg.webp" alt="Image for discord avatar">
|
||||
</a>
|
||||
</p>
|
||||
</figure>
|
||||
<div class="media-content">
|
||||
<div class="content">
|
||||
<div class="discord-message-header">
|
||||
<label class="is-sr-only" for="reminderUsername">Username Override</label>
|
||||
<label class="is-sr-only">Username Override</label>
|
||||
<input class="discord-username message-input" placeholder="Username Override"
|
||||
maxlength="32" id="reminderUsername" name="username">
|
||||
maxlength="32" name="username">
|
||||
</div>
|
||||
<label class="is-sr-only" for="messageContent">Message</label>
|
||||
<label class="is-sr-only">Message</label>
|
||||
<textarea class="message-input autoresize discord-content"
|
||||
placeholder="Message Content..."
|
||||
maxlength="2000" id="messageContent" name="content" rows="1"></textarea>
|
||||
maxlength="2000" name="content" rows="1"></textarea>
|
||||
|
||||
<div class="discord-embed">
|
||||
<div class="embed-body">
|
||||
<button class="change-color button is-rounded is-small">
|
||||
<i class="fas fa-eye-dropper"></i>
|
||||
<span class="is-sr-only">Choose embed color</span><i class="fas fa-eye-dropper"></i>
|
||||
</button>
|
||||
<div class="a">
|
||||
<div class="embed-author-box">
|
||||
<div class="a">
|
||||
<p class="image is-24x24 customizable">
|
||||
<a>
|
||||
<img class="is-rounded embed_author_url" src="/static/img/bg.webp">
|
||||
<img class="is-rounded embed_author_url" src="/static/img/bg.webp" alt="Image for embed author">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
@ -38,40 +38,42 @@
|
||||
<div class="b">
|
||||
<label class="is-sr-only" for="embedAuthor">Embed Author</label>
|
||||
<textarea
|
||||
class="discord-embed-author message-input autoresize"
|
||||
class="discord-embed-author message-input autoresize"
|
||||
placeholder="Embed Author..." rows="1" maxlength="256"
|
||||
id="embedAuthor" name="embed_author"></textarea>
|
||||
name="embed_author"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="is-sr-only" for="embedTitle">Embed Title</label>
|
||||
<textarea class="discord-title message-input autoresize"
|
||||
placeholder="Embed Title..."
|
||||
maxlength="256" id="embedTitle" rows="1"
|
||||
maxlength="256" rows="1"
|
||||
name="embed_title"></textarea>
|
||||
<br>
|
||||
<label class="is-sr-only" for="embedDescription">Embed Description</label>
|
||||
<textarea class="discord-description message-input autoresize "
|
||||
placeholder="Embed Description..."
|
||||
maxlength="4096" id="embedDescription" name="embed_description"
|
||||
maxlength="4096" name="embed_description"
|
||||
rows="1"></textarea>
|
||||
<br>
|
||||
|
||||
<div class="embed-multifield-box">
|
||||
<div data-inlined="1" class="embed-field-box">
|
||||
<label class="is-sr-only" for="embedFieldTitle">Field Title</label>
|
||||
<div style="display: flex;">
|
||||
<div class="is-flex">
|
||||
<textarea class="discord-field-title field-input message-input autoresize"
|
||||
placeholder="Field Title..." rows="1"
|
||||
maxlength="256" id="embedFieldTitle" name="embed_field_title[]"></textarea>
|
||||
<button class="button is-small inline-btn" style="height: 100%; padding: 5px;"><i class="fas fa-arrows-h"></i></button>
|
||||
maxlength="256" name="embed_field_title[]"></textarea>
|
||||
<button class="button is-small inline-btn">
|
||||
<span class="is-sr-only">Toggle field inline</span><i class="fas fa-arrows-h"></i>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<label class="is-sr-only" for="embedFieldValue">Field Value</label>
|
||||
<textarea
|
||||
class="discord-field-value field-input message-input autoresize "
|
||||
placeholder="Field Value..."
|
||||
maxlength="1024" id="embedFieldValue" name="embed_field_value[]"
|
||||
maxlength="1024" name="embed_field_value[]"
|
||||
rows="1"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
@ -102,7 +104,7 @@
|
||||
<label class="is-sr-only" for="embedFooter">Embed Footer text</label>
|
||||
<textarea class="discord-embed-footer message-input autoresize "
|
||||
placeholder="Embed Footer..."
|
||||
maxlength="2048" id="embedFooter" name="embed_footer" rows="1"></textarea>
|
||||
maxlength="2048" name="embed_footer" rows="1"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -121,7 +123,7 @@
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
<button class="button is-rounded hide-box">
|
||||
<i class="fas fa-chevron-down"></i>
|
||||
<span class="is-sr-only">Hide reminder</span><i class="fas fa-chevron-down"></i>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
@ -132,20 +134,21 @@
|
||||
</div>
|
||||
<div class="control has-icons-left">
|
||||
<div class="select">
|
||||
<select id="channelOption" name="channel" class="channel-selector">
|
||||
<select name="channel" class="channel-selector">
|
||||
</select>
|
||||
</div>
|
||||
<div class="icon is-small is-left">
|
||||
<i class="fas fa-hashtag"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="collapses">
|
||||
<div class="field">
|
||||
<label class="label">Time</label>
|
||||
<div class="control">
|
||||
<input class="input" type="datetime-local" step="1" name="time">
|
||||
<label class="label">
|
||||
Time
|
||||
<input class="input" type="datetime-local" step="1" name="time">
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -154,13 +157,28 @@
|
||||
<div class="control intervalSelector">
|
||||
<div class="input interval-group">
|
||||
<div class="interval-group-left">
|
||||
<input class="w2" type="text" pattern="\d*" name="interval_months" maxlength="2" placeholder=""> <span style="width: 0.5rem"></span> months, <span style="width: 0.5rem"></span>
|
||||
<input class="w3" type="text" pattern="\d*" name="interval_days" maxlength="4" placeholder=""> <span style="width: 0.5rem"></span> days, <span style="width: 0.5rem"></span>
|
||||
<input class="w2" type="text" pattern="\d*" name="interval_hours" maxlength="2" placeholder="HH">:
|
||||
<input class="w2" type="text" pattern="\d*" name="interval_minutes" maxlength="2" placeholder="MM">:
|
||||
<input class="w2" type="text" pattern="\d*" name="interval_seconds" maxlength="2" placeholder="SS">
|
||||
<label>
|
||||
<span class="is-sr-only">Interval months</span>
|
||||
<input class="w2" type="text" pattern="\d*" name="interval_months" maxlength="2" placeholder=""> <span class="half-rem"></span> months, <span class="half-rem"></span>
|
||||
</label>
|
||||
<label>
|
||||
<span class="is-sr-only">Interval days</span>
|
||||
<input class="w3" type="text" pattern="\d*" name="interval_days" maxlength="4" placeholder=""> <span class="half-rem"></span> days, <span class="half-rem"></span>
|
||||
</label>
|
||||
<label>
|
||||
<span class="is-sr-only">Interval hours</span>
|
||||
<input class="w2" type="text" pattern="\d*" name="interval_hours" maxlength="2" placeholder="HH">:
|
||||
</label>
|
||||
<label>
|
||||
<span class="is-sr-only">Interval minutes</span>
|
||||
<input class="w2" type="text" pattern="\d*" name="interval_minutes" maxlength="2" placeholder="MM">:
|
||||
</label>
|
||||
<label>
|
||||
<span class="is-sr-only">Interval seconds</span>
|
||||
<input class="w2" type="text" pattern="\d*" name="interval_seconds" maxlength="2" placeholder="SS">
|
||||
</label>
|
||||
</div>
|
||||
<button class="clear"><span class="icon"><i class="fas fa-trash"></i></span></button>
|
||||
<button class="clear"><span class="is-sr-only">Clear interval</span><span class="icon"><i class="fas fa-trash"></i></span></button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@ -193,13 +211,13 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<span style="width: 12px;"></span>
|
||||
<span class="pad-left"></span>
|
||||
{% if creating %}
|
||||
<button class="button is-outlined">
|
||||
<button class="button is-outlined show-modal" data-modal="chooseTemplateModal">
|
||||
Load Template
|
||||
</button>
|
||||
<button class="button is-success is-outlined" id="createTemplate">
|
||||
Create Template
|
||||
<span>Create Template</span> <span class="icon"><i class="fas fa-file-spreadsheet"></i></span>
|
||||
</button>
|
||||
<button class="button is-success" id="createReminder">
|
||||
<span>Create Reminder</span> <span class="icon"><i class="fas fa-sparkles"></i></span>
|
||||
@ -209,6 +227,7 @@
|
||||
<span>Save</span> <span class="icon"><i class="fas fa-save"></i></span>
|
||||
</button>
|
||||
<button class="button is-warning disable-enable">
|
||||
<span class="is-sr-only">Text content filled by CSS</span>
|
||||
</button>
|
||||
<button class="button is-danger delete-reminder">
|
||||
Delete
|
||||
|
@ -1,186 +0,0 @@
|
||||
<div class="discord-frame">
|
||||
<article class="media">
|
||||
<figure class="media-left">
|
||||
<p class="image is-32x32">
|
||||
<img class="is-rounded" src="/static/img/icon.png" alt="reminder bot icon">
|
||||
</p>
|
||||
</figure>
|
||||
<div class="media-content">
|
||||
<div class="content">
|
||||
<div class="is-hidden-touch">
|
||||
<div class="discord-message-header">
|
||||
Reminder Bot -
|
||||
<label class="is-sr-only" for="reminderDate">Reminder Date</label>
|
||||
<input class="time-input date" placeholder="YYYY/MM/DD"
|
||||
id="reminderDate" name="date">
|
||||
|
||||
<label class="is-sr-only" for="reminderTime">Reminder Time</label>
|
||||
<input class="time-input time" placeholder="HH:MM:SS"
|
||||
id="reminderTime" name="time">
|
||||
</div>
|
||||
</div>
|
||||
<div class="is-hidden-desktop">
|
||||
<label class="is-sr-only" for="reminderDate">Reminder Date</label>
|
||||
<input class="time-input date" placeholder="YYYY/MM/DD"
|
||||
id="reminderDate" name="date">
|
||||
|
||||
<label class="is-sr-only" for="reminderTime">Reminder Time</label>
|
||||
<input class="time-input time" placeholder="HH:MM:SS"
|
||||
id="reminderTime" name="time">
|
||||
|
||||
<div class="discord-message-header">
|
||||
Reminder Bot
|
||||
</div>
|
||||
</div>
|
||||
<label class="is-sr-only" for="messageContent">Message</label>
|
||||
<textarea class="message-input autoresize discord-content preview-mode"
|
||||
placeholder="Message Content..."
|
||||
maxlength="2000" id="messageContent" name="content" rows="1"></textarea>
|
||||
|
||||
<div class="discord-embed">
|
||||
<div class="embed-body">
|
||||
<div class="a">
|
||||
<div class="embed-author-box">
|
||||
<div class="a">
|
||||
<p class="image is-24x24 customizable">
|
||||
<a>
|
||||
<img class="is-rounded preview-mode" src="">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="b">
|
||||
<label class="is-sr-only" for="embedAuthor">Embed Author</label>
|
||||
<textarea
|
||||
class="discord-embed-author message-input preview-mode autoresize"
|
||||
placeholder="Embed Author..." rows="1" maxlength="256"
|
||||
id="embedAuthor" name="embed_author"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<label class="is-sr-only" for="embedTitle">Embed Title</label>
|
||||
<textarea class="discord-title message-input preview-mode autoresize"
|
||||
placeholder="Embed Title..."
|
||||
maxlength="256" id="embedTitle" rows="1"
|
||||
name="embed_title"></textarea>
|
||||
<br>
|
||||
<label class="is-sr-only" for="embedDescription">Embed Description</label>
|
||||
<textarea class="discord-description message-input autoresize preview-mode"
|
||||
placeholder="Embed Description..."
|
||||
maxlength="2048" id="embedDescription" name="embed_description"
|
||||
rows="1"></textarea>
|
||||
<br>
|
||||
|
||||
<div class="embed-multifield-box">
|
||||
<div class="embed-field-box">
|
||||
<label class="is-sr-only" for="embedFieldTitle">Field Title</label>
|
||||
<textarea
|
||||
class="discord-field-title field-input message-input autoresize preview-mode"
|
||||
placeholder="Field Title..." rows="1"
|
||||
maxlength="256" id="embedFieldTitle"
|
||||
name="embed_field_title[]"></textarea>
|
||||
|
||||
<label class="is-sr-only" for="embedFieldValue">Field Value</label>
|
||||
<textarea
|
||||
class="discord-field-value field-input message-input autoresize preview-mode"
|
||||
placeholder="Field Value..."
|
||||
maxlength="1024" id="embedFieldValue" name="embed_field_value[]"
|
||||
rows="1"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="b">
|
||||
<p class="image thumbnail customizable">
|
||||
<a>
|
||||
<img class="preview-mode" src="">
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="image is-400x300 customizable">
|
||||
<a>
|
||||
<img class="preview-mode" src="">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<div class="embed-footer-box">
|
||||
<p class="image is-20x20 customizable">
|
||||
<a>
|
||||
<img class="is-rounded preview-mode" src="">
|
||||
</a>
|
||||
</p>
|
||||
|
||||
<label class="is-sr-only" for="embedAuthor">Embed Author</label>
|
||||
<textarea class="discord-embed-footer message-input autoresize preview-mode"
|
||||
placeholder="Embed Footer..."
|
||||
maxlength="2048" id="embedAuthor" name="embed_author" rows="1"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<nav class="level is-mobile">
|
||||
<div class="level-left">
|
||||
<a class="level-item icon-toggle tts-toggle" title="Enable TTS">
|
||||
<p>
|
||||
TTS <span class="icon is-small"><i class="far fa-comment-lines"></i></span>
|
||||
</p>
|
||||
</a>
|
||||
<a class="level-item icon-toggle autopin-toggle" title="Enable Autopin">
|
||||
<p>
|
||||
Pin <span class="icon is-small"><i class="far fa-thumbtack"></i></span>
|
||||
</p>
|
||||
</a>
|
||||
<span style="width: 12px;"></span>
|
||||
<a class="level-item set-color">
|
||||
<p>
|
||||
Set Embed Color <span class="icon is-small"><i
|
||||
class="far fa-eye-dropper"></i></span>
|
||||
</p>
|
||||
</a>
|
||||
<a class="level-item file-upload">
|
||||
<div class="file">
|
||||
<input class="file-input" type="file" name="attachment">
|
||||
<p>
|
||||
Attach File
|
||||
<span class="icon is-small">
|
||||
<i class="far fa-file-upload"></i>
|
||||
</span>
|
||||
</p>
|
||||
</div>
|
||||
</a>
|
||||
<a class="level-item set-interval">
|
||||
<p>
|
||||
Set Interval <span class="icon is-small"><i class="far fa-repeat"></i></span>
|
||||
</p>
|
||||
</a>
|
||||
<span style="width: 12px;"></span>
|
||||
<a class="level-item preview-toggle" title="Preview Message">
|
||||
<p>
|
||||
Preview Mode <span class="icon is-small"><i class="far fa-eye"></i></span>
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
<nav class="level is-mobile">
|
||||
<div class="level-left">
|
||||
<a class="level-item create-reminder" title="Create Reminder">
|
||||
<p>
|
||||
Create <span class="icon is-small"><i class="far fa-calendar-plus"></i></span>
|
||||
</p>
|
||||
</a>
|
||||
<a class="level-item icon-toggle disable-reminder" title="Disable/enable Reminder">
|
||||
<p>
|
||||
Disable <span class="icon is-small"><i class="far fa-comment-slash"></i></span>
|
||||
</p>
|
||||
</a>
|
||||
<a class="level-item delete-reminder" title="Delete Reminder">
|
||||
<p>
|
||||
Delete <span class="icon is-small"><i class="far fa-trash"></i></span>
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</nav>
|
||||
</div>
|
||||
</article>
|
||||
</div>
|
@ -1,52 +1,50 @@
|
||||
<div style="margin: 0 12px 12px 12px;">
|
||||
<div class="create-reminder">
|
||||
<strong>Create Reminder</strong>
|
||||
<div id="reminderCreator">
|
||||
{% set creating = true %}
|
||||
{% include "reminder_dashboard/guild_reminder" %}
|
||||
{% set creating = false %}
|
||||
</div>
|
||||
<br>
|
||||
<div class="create-reminder">
|
||||
<strong>Create Reminder</strong>
|
||||
<div id="reminderCreator">
|
||||
{% set creating = true %}
|
||||
{% include "reminder_dashboard/guild_reminder" %}
|
||||
{% set creating = false %}
|
||||
</div>
|
||||
<br>
|
||||
|
||||
<div class="field">
|
||||
<div class="columns is-mobile">
|
||||
<div class="column">
|
||||
<strong>Reminders</strong>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small">
|
||||
<select id="orderBy">
|
||||
<option value="time" selected>Time</option>
|
||||
<option value="name">Name</option>
|
||||
<option value="channel">Channel</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="icon is-small is-left">
|
||||
<i class="fas fa-sort-amount-down"></i>
|
||||
</div>
|
||||
<div class="field">
|
||||
<div class="columns is-mobile">
|
||||
<div class="column">
|
||||
<strong>Reminders</strong>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small">
|
||||
<select id="orderBy">
|
||||
<option value="time" selected>Time</option>
|
||||
<option value="name">Name</option>
|
||||
<option value="channel">Channel</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="icon is-small is-left">
|
||||
<i class="fas fa-sort-amount-down"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small">
|
||||
<select id="expandAll" style="width: 60px">
|
||||
<option value="" selected></option>
|
||||
<option value="expand">Expand All</option>
|
||||
<option value="collapse">Collapse All</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="icon is-small is-left">
|
||||
<i class="fas fa-expand-arrows"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div class="column is-narrow">
|
||||
<div class="control has-icons-left">
|
||||
<div class="select is-small">
|
||||
<select id="expandAll">
|
||||
<option value="" selected></option>
|
||||
<option value="expand">Expand All</option>
|
||||
<option value="collapse">Collapse All</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="icon is-small is-left">
|
||||
<i class="fas fa-expand-arrows"></i>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="guildReminders">
|
||||
<div id="guildReminders">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
@ -1,23 +0,0 @@
|
||||
<div style="margin: 0 12px 12px 12px;">
|
||||
<div class="create-reminder">
|
||||
<p>
|
||||
<strong>Message Designer</strong>
|
||||
</p>
|
||||
{% include "reminder_dashboard/personal_reminder" %}
|
||||
<p style="font-size: 0.8rem;">
|
||||
Most fields are optional. Use 'Preview Mode' to see how the reminder will appear in Discord.
|
||||
Scaling is not exact.
|
||||
</p>
|
||||
|
||||
<div class="field">
|
||||
<p class="control">
|
||||
<a class="button is-success">
|
||||
Create
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
<div id="personalReminders">
|
||||
|
||||
</div>
|
||||
</div>
|
Loading…
Reference in New Issue
Block a user