Name::get_all, quotes page stub, quote add UI work, icons
This commit is contained in:
@@ -157,6 +157,20 @@ impl Name {
|
|||||||
None => Err(PersonError::NoNameWithId(id)),
|
None => Err(PersonError::NoNameWithId(id)),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
pub fn get_all(conn: &Connection) -> Result<Vec<Name>, PersonError> {
|
||||||
|
Ok(conn
|
||||||
|
.prepare("SELECT id, is_primary, person_id, created_by, name FROM names")?
|
||||||
|
.query_map((), |r| {
|
||||||
|
Ok(Name {
|
||||||
|
id: r.get(0)?,
|
||||||
|
is_primary: r.get(1)?,
|
||||||
|
person_id: r.get(2)?,
|
||||||
|
created_by: r.get(3)?,
|
||||||
|
name: r.get(4)?,
|
||||||
|
})
|
||||||
|
})?
|
||||||
|
.collect::<Result<Vec<Name>, _>>()?)
|
||||||
|
}
|
||||||
pub fn set_primary(&mut self, conn: &Connection) -> Result<(), PersonError> {
|
pub fn set_primary(&mut self, conn: &Connection) -> Result<(), PersonError> {
|
||||||
if self.is_primary {
|
if self.is_primary {
|
||||||
return Err(PersonError::AlreadyPrimary);
|
return Err(PersonError::AlreadyPrimary);
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ use crate::{users::User, web::icons};
|
|||||||
// (SHOWTEXT, LINK, ICON, REQUIRES_LOG_IN)
|
// (SHOWTEXT, LINK, ICON, REQUIRES_LOG_IN)
|
||||||
const LINKS: &[(&str, &str, &str, bool)] = &[
|
const LINKS: &[(&str, &str, &str, bool)] = &[
|
||||||
("Dashboard", "/dashboard", icons::LAYOUT_DASHBOARD, false),
|
("Dashboard", "/dashboard", icons::LAYOUT_DASHBOARD, false),
|
||||||
("Quotes", "#quotes", icons::SCROLL_TEXT, false),
|
("Quotes", "/quotes", icons::SCROLL_TEXT, false),
|
||||||
("Photos", "#photos", icons::FILE_IMAGE, false),
|
("Photos", "#photos", icons::FILE_IMAGE, false),
|
||||||
("Persons", "/persons", icons::CONTACT, false),
|
("Persons", "/persons", icons::CONTACT, false),
|
||||||
("Tags", "/tags", icons::TAG, false),
|
("Tags", "/tags", icons::TAG, false),
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ pub const LAYOUT_DASHBOARD: &str = include_str!("layout-dashboard.svg");
|
|||||||
pub const LOG_OUT: &str = include_str!("log-out.svg");
|
pub const LOG_OUT: &str = include_str!("log-out.svg");
|
||||||
pub const MAP_PIN: &str = include_str!("map-pin.svg");
|
pub const MAP_PIN: &str = include_str!("map-pin.svg");
|
||||||
pub const PEN: &str = include_str!("pen.svg");
|
pub const PEN: &str = include_str!("pen.svg");
|
||||||
|
pub const PLUS: &str = include_str!("plus.svg");
|
||||||
pub const QUOTE: &str = include_str!("quote.svg");
|
pub const QUOTE: &str = include_str!("quote.svg");
|
||||||
pub const SCROLL_TEXT: &str = include_str!("scroll-text.svg");
|
pub const SCROLL_TEXT: &str = include_str!("scroll-text.svg");
|
||||||
pub const SERVER: &str = include_str!("server.svg");
|
pub const SERVER: &str = include_str!("server.svg");
|
||||||
|
|||||||
1
src/web/icons/plus.svg
Normal file
1
src/web/icons/plus.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-plus-icon lucide-plus"><path d="M5 12h14"/><path d="M12 5v14"/></svg>
|
||||||
|
After Width: | Height: | Size: 272 B |
@@ -9,6 +9,7 @@ pub mod index;
|
|||||||
pub mod login;
|
pub mod login;
|
||||||
pub mod logs;
|
pub mod logs;
|
||||||
pub mod persons;
|
pub mod persons;
|
||||||
|
pub mod quotes;
|
||||||
pub mod tags;
|
pub mod tags;
|
||||||
pub mod users;
|
pub mod users;
|
||||||
pub mod usersettings;
|
pub mod usersettings;
|
||||||
@@ -30,6 +31,8 @@ pub fn pages() -> Router {
|
|||||||
.route("/persons", get(persons::page))
|
.route("/persons", get(persons::page))
|
||||||
.route("/persons/create", post(persons::create))
|
.route("/persons/create", post(persons::create))
|
||||||
.route("/logs", get(logs::page))
|
.route("/logs", get(logs::page))
|
||||||
|
.route("/quotes", get(quotes::page))
|
||||||
|
.route("/quotes/add", get(quotes::add::page))
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn base(title: &str, inner: Markup) -> Markup {
|
pub fn base(title: &str, inner: Markup) -> Markup {
|
||||||
|
|||||||
43
src/web/pages/quotes.rs
Normal file
43
src/web/pages/quotes.rs
Normal file
@@ -0,0 +1,43 @@
|
|||||||
|
use axum::{
|
||||||
|
extract::Request,
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
use maud::{PreEscaped, html};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
error::CompositeError,
|
||||||
|
users::{User, auth::UserAuthenticate},
|
||||||
|
web::{components::nav::nav, icons, pages::base},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub mod add;
|
||||||
|
|
||||||
|
pub async fn page(req: Request) -> Result<Response, CompositeError> {
|
||||||
|
let u = User::authenticate(req.headers())?;
|
||||||
|
|
||||||
|
Ok(base(
|
||||||
|
"Persons | Mnemosyne",
|
||||||
|
html!(
|
||||||
|
(nav(u.as_ref(), req.uri().path()))
|
||||||
|
|
||||||
|
div class="max-w-4xl mx-auto px-2" {
|
||||||
|
div class="my-4 flex justify-between" {
|
||||||
|
p class="flex items-center gap-2" {
|
||||||
|
span class="text-neutral-500" {(PreEscaped(icons::SCROLL_TEXT))}
|
||||||
|
span class="text-2xl font-semibold font-lora" {"Quotes"}
|
||||||
|
}
|
||||||
|
@if let Some(_) = u {
|
||||||
|
a href="/quotes/add" class="group border rounded flex items-center gap-1 px-2 border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-400/5 hover:bg-neutral-400/10" {
|
||||||
|
span class="text-neutral-300 group-hover:text-neutral-200" {(PreEscaped(icons::PLUS))}
|
||||||
|
span class="text-neutral-300 group-hover:text-neutral-200" {"Add quote"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
input class="border w-full border-neutral-200/25 hover:border-neutral-200/45 bg-neutral-950/50 p-2 rounded"
|
||||||
|
placeholder="Search for quotes...";
|
||||||
|
div class="text-center p-4" {"Search not yet implemented."}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_response())
|
||||||
|
}
|
||||||
102
src/web/pages/quotes/add.rs
Normal file
102
src/web/pages/quotes/add.rs
Normal file
@@ -0,0 +1,102 @@
|
|||||||
|
use axum::{
|
||||||
|
extract::Request,
|
||||||
|
response::{IntoResponse, Response},
|
||||||
|
};
|
||||||
|
use maud::{PreEscaped, html};
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
database,
|
||||||
|
error::CompositeError,
|
||||||
|
persons::Name,
|
||||||
|
users::{User, auth::UserAuthenticate},
|
||||||
|
web::{components::nav::nav, icons, pages::base},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub async fn page(req: Request) -> Result<Response, CompositeError> {
|
||||||
|
let u = User::authenticate(req.headers())?;
|
||||||
|
let conn = database::conn()?;
|
||||||
|
let names = Name::get_all(&conn)?;
|
||||||
|
|
||||||
|
Ok(base(
|
||||||
|
"Persons | Mnemosyne",
|
||||||
|
html!(
|
||||||
|
(nav(u.as_ref(), req.uri().path()))
|
||||||
|
|
||||||
|
div class="max-w-4xl mx-auto px-2" {
|
||||||
|
div class="my-4 flex justify-between" {
|
||||||
|
p class="flex items-center gap-2" {
|
||||||
|
span class="text-neutral-500" {(PreEscaped(icons::SCROLL_TEXT))}
|
||||||
|
span class="text-2xl font-semibold font-lora" {"Quote Maker"}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div class="border border-neutral-200/25 bg-neutral-200/5 rounded-md p-4 flex flex-col" {
|
||||||
|
@for i in 1..=2 {
|
||||||
|
div class="flex justify-between gap-4" {
|
||||||
|
div class="flex flex-col flex-1" {
|
||||||
|
label class="w-full" {
|
||||||
|
p class="mb-1" {(format!("Quote Line #{i}"))}
|
||||||
|
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";
|
||||||
|
}
|
||||||
|
// label for=(format!("line-{i}")) class="mb-1" {(format!("Quote Line #{i}"))}
|
||||||
|
// input type="text" id=(format!("line-{i}")) name=(format!("line-{i}"))
|
||||||
|
// placeholder=(format!("They said...")) autocomplete="off"
|
||||||
|
// class="px-2 py-1 mb-2 bg-neutral-950/50 rounded border border-neutral-200/25";
|
||||||
|
}
|
||||||
|
div class="flex flex-col" {
|
||||||
|
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 {(name.name)}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// label for=(format!("who-{i}")) class="mb-1" {(format!("Quote Author #{i}"))}
|
||||||
|
// select id=(format!("line-{i}")) name=(format!("line-{i}")) autocomplete="off"
|
||||||
|
// class="px-2 py-1.5 mb-2 bg-neutral-950/50 rounded border border-neutral-200/25" {
|
||||||
|
// option {"--"}
|
||||||
|
// @for name in &names {
|
||||||
|
// option {(name.name)}
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
hr class="border-neutral-200/25 my-4";
|
||||||
|
div class="flex gap-4 justify-between" {
|
||||||
|
div class="flex flex-col flex-1" {
|
||||||
|
label class="w-full"{
|
||||||
|
p class="mb-1" {"Location"}
|
||||||
|
input type="text" name="location" autocomplete="off" placeholder="Right there!"
|
||||||
|
class="px-2 py-1 w-full mb-2 bg-neutral-950/50 rounded border border-neutral-200/25";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div class="flex flex-col flex-1" {
|
||||||
|
label class="w-full" {
|
||||||
|
p class="mb-1" {"Time of utterance"}
|
||||||
|
input type="text" name="time" autocomplete="off" placeholder="2026-04-05T01:14:05+02:00"
|
||||||
|
class="px-2 py-1 w-full mb-2 bg-neutral-950/50 rounded border border-neutral-200/25";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
div class="flex gap-4 justify-between" {
|
||||||
|
div class="flex flex-col flex-1" {
|
||||||
|
label class="w-full" {
|
||||||
|
p class="mb-1" {"Context"}
|
||||||
|
input type="text" name="context" autocomplete="off" placeholder="It was like this.."
|
||||||
|
class="px-2 py-1 w-full mb-2 bg-neutral-950/50 rounded border border-neutral-200/25";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button type="submit" class="border mt-auto mb-2 cursor-pointer rounded h-fit px-2 py-1 bg-neutral-200/5 border-neutral-200/25 hover:border-neutral-200/45 hover:bg-neutral-200/15" {
|
||||||
|
"Submit"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.into_response())
|
||||||
|
}
|
||||||
File diff suppressed because one or more lines are too long
Reference in New Issue
Block a user