merge upstream
All checks were successful
mnemo-build-and-publish / gractwo-mnemo-build (push) Successful in 32s
All checks were successful
mnemo-build-and-publish / gractwo-mnemo-build (push) Successful in 32s
This commit is contained in:
1
src/web/icons/circle-minus.svg
Normal file
1
src/web/icons/circle-minus.svg
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-circle-minus-icon lucide-circle-minus"><circle cx="12" cy="12" r="10"/><path d="M8 12h8"/></svg>
|
||||||
|
After Width: | Height: | Size: 299 B |
@@ -1,6 +1,7 @@
|
|||||||
// Below icons sourced from https://lucide.dev
|
// Below icons sourced from https://lucide.dev
|
||||||
pub const ARROW_RIGHT: &str = include_str!("arrow-right.svg");
|
pub const ARROW_RIGHT: &str = include_str!("arrow-right.svg");
|
||||||
pub const CALENDAR_1: &str = include_str!("calendar-1.svg");
|
pub const CALENDAR_1: &str = include_str!("calendar-1.svg");
|
||||||
|
pub const CIRCLE_MINUS: &str = include_str!("circle-minus.svg");
|
||||||
pub const CLIPBOARD_CLOCK: &str = include_str!("clipboard-clock.svg");
|
pub const CLIPBOARD_CLOCK: &str = include_str!("clipboard-clock.svg");
|
||||||
pub const CLOCK: &str = include_str!("clock.svg");
|
pub const CLOCK: &str = include_str!("clock.svg");
|
||||||
pub const CONTACT: &str = include_str!("contact.svg");
|
pub const CONTACT: &str = include_str!("contact.svg");
|
||||||
|
|||||||
@@ -1,13 +1,12 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
Json,
|
|
||||||
extract::Request,
|
extract::Request,
|
||||||
http::HeaderMap,
|
http::HeaderMap,
|
||||||
response::{IntoResponse, Response},
|
response::{IntoResponse, Redirect, Response},
|
||||||
};
|
};
|
||||||
use axum_extra::extract::Form;
|
use axum_extra::extract::Form;
|
||||||
use chrono::{TimeZone, Utc};
|
use chrono::{TimeZone, Utc};
|
||||||
use chrono_tz::Europe::Warsaw;
|
use chrono_tz::Europe::Warsaw;
|
||||||
use maud::{PreEscaped, html};
|
use maud::{Markup, PreEscaped, html};
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
@@ -24,6 +23,8 @@ use crate::{
|
|||||||
web::{components::nav::nav, icons, pages::base},
|
web::{components::nav::nav, icons, pages::base},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const LINE_ADD_RM_SCRIPT: &str = include_str!("line-add-rm.js");
|
||||||
|
|
||||||
pub async fn page(req: Request) -> Result<Response, CompositeError> {
|
pub async fn page(req: Request) -> Result<Response, CompositeError> {
|
||||||
let u = User::authenticate(req.headers())?;
|
let u = User::authenticate(req.headers())?;
|
||||||
let conn = database::conn()?;
|
let conn = database::conn()?;
|
||||||
@@ -43,30 +44,20 @@ pub async fn page(req: Request) -> Result<Response, CompositeError> {
|
|||||||
}
|
}
|
||||||
form method="post" action="/quotes/add-form"
|
form method="post" action="/quotes/add-form"
|
||||||
class="border border-neutral-200/25 bg-neutral-200/5 rounded-md p-4 flex flex-col" {
|
class="border border-neutral-200/25 bg-neutral-200/5 rounded-md p-4 flex flex-col" {
|
||||||
@for i in 1..=2 {
|
div quotelines class="flex flex-col" {
|
||||||
div class="flex justify-between gap-4" {
|
@for i in 1..=3 {(maker_line_row(i==1, &names))}
|
||||||
div class="flex flex-col flex-1" {
|
}
|
||||||
label class="w-full" {
|
template quotelinetemplate {
|
||||||
p class="mb-1" {(format!("Quote Line #{i}"))}
|
(maker_line_row(false, &names))
|
||||||
input type="text" name="quoteline" placeholder="They said..." autocomplete="off"
|
}
|
||||||
class="px-2 py-1 w-full mb-2 bg-neutral-950/50 rounded border border-neutral-200/25";
|
div class="flex flex-row gap-2" {
|
||||||
|
hr class="border-neutral-200/25 flex-1 my-4";
|
||||||
|
button addlinebtn type="button"
|
||||||
|
class="w-fit text-neutral-400 hover:text-neutral-300 cursor-pointer" {
|
||||||
|
"Add line"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
div class="flex flex-col" {
|
script {(PreEscaped(LINE_ADD_RM_SCRIPT))}
|
||||||
label {
|
|
||||||
p class="mb-1" {(format!("Quote Author #{i}"))}
|
|
||||||
select name="quoteauthor" autocomplete="off"
|
|
||||||
class="px-2 py-1.5 w-full mb-2 bg-neutral-950/50 rounded border border-neutral-200/25"{
|
|
||||||
option {"--"}
|
|
||||||
@for name in &names {
|
|
||||||
option value=(name.id.to_string()) {(name.name)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
hr class="border-neutral-200/25 my-4";
|
|
||||||
div class="flex gap-4 justify-between" {
|
div class="flex gap-4 justify-between" {
|
||||||
div class="flex flex-col flex-1" {
|
div class="flex flex-col flex-1" {
|
||||||
label class="w-full"{
|
label class="w-full"{
|
||||||
@@ -104,6 +95,35 @@ pub async fn page(req: Request) -> Result<Response, CompositeError> {
|
|||||||
.into_response())
|
.into_response())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn maker_line_row(rm_disabled: bool, names: &[Name]) -> Markup {
|
||||||
|
html!(
|
||||||
|
div quoteline class="flex gap-4" {
|
||||||
|
div class="flex flex-col flex-1" {
|
||||||
|
label class="w-full" {
|
||||||
|
p class="mb-1" {"Quote line"}
|
||||||
|
input type="text" name="quoteline" placeholder="They said..." autocomplete="off" required
|
||||||
|
class="px-2 py-1 w-full mb-2 bg-neutral-950/50 rounded border border-neutral-200/25";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div class="flex flex-col ml-auto" {
|
||||||
|
label {
|
||||||
|
p class="mb-1" {"Attribution"}
|
||||||
|
select name="quoteauthor" autocomplete="off" required
|
||||||
|
class="px-2 py-1.5 w-full mb-2 bg-neutral-950/50 rounded border border-neutral-200/25"{
|
||||||
|
option value="" {"--"}
|
||||||
|
@for name in names {
|
||||||
|
option value=(name.id.to_string()) {(name.name)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button rmlinebtn disabled?[rm_disabled] type="button" class="h-fit mt-auto mb-2 p-1 bg-neutral-200/5 hover:bg-neutral-200/15 rounded border border-neutral-200/25 hover:border-neutral-200/45 cursor-pointer disabled:cursor-not-allowed disabled:opacity-[.5]" {
|
||||||
|
(PreEscaped(icons::CIRCLE_MINUS))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Deserialize, Debug)]
|
#[derive(Deserialize, Debug)]
|
||||||
pub struct IncomingQuote {
|
pub struct IncomingQuote {
|
||||||
#[serde(rename = "quoteline")]
|
#[serde(rename = "quoteline")]
|
||||||
@@ -149,5 +169,5 @@ pub async fn form(
|
|||||||
LogEntry::new(&tx, u, LogAction::CreateQuote { id: q.id })?;
|
LogEntry::new(&tx, u, LogAction::CreateQuote { id: q.id })?;
|
||||||
tx.commit()?;
|
tx.commit()?;
|
||||||
|
|
||||||
Ok(Json(q).into_response())
|
Ok(Redirect::to("/dashboard").into_response())
|
||||||
}
|
}
|
||||||
|
|||||||
20
src/web/pages/quotes/line-add-rm.js
Normal file
20
src/web/pages/quotes/line-add-rm.js
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
document.addEventListener("DOMContentLoaded", () => {
|
||||||
|
const container = document.querySelector("[quotelines]");
|
||||||
|
const template = document.querySelector("[quotelinetemplate]");
|
||||||
|
const addButton = document.querySelector("[addlinebtn]");
|
||||||
|
|
||||||
|
addButton.addEventListener("click", () => {
|
||||||
|
const clone = template.content.cloneNode(true);
|
||||||
|
container.appendChild(clone);
|
||||||
|
});
|
||||||
|
|
||||||
|
container.addEventListener("click", (e) => {
|
||||||
|
const rmBtn = e.target.closest("[rmlinebtn]");
|
||||||
|
if (rmBtn && !rmBtn.disabled) {
|
||||||
|
const line = rmBtn.closest("[quoteline]");
|
||||||
|
if (line && container.querySelectorAll("[quoteline]").length > 1) {
|
||||||
|
line.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user