Add delete buttons to history page

This commit is contained in:
2020-10-31 00:23:01 +01:00
parent 24f3d69301
commit 68df64d5f9
10 changed files with 93 additions and 35 deletions

View File

@ -1,2 +1,5 @@
pub mod unversioned; pub mod unversioned;
pub mod v1; pub mod v1;
pub const SCHEMA_VERSION: unversioned::global::schema_version::V = 1;
pub use self::v1 as latest;

View File

@ -1,6 +1,6 @@
pub mod global { pub mod global {
pub mod schema_version { pub mod schema_version {
pub const K: &str = "SCHEMA_VERSION"; pub const K: &str = "SCHEMA_VERSION";
pub type V = (u32, u32, u32); pub type V = u32;
} }
} }

View File

@ -35,8 +35,13 @@ pub mod trees {
#[derive(Debug, Clone, Serialize, Deserialize)] #[derive(Debug, Clone, Serialize, Deserialize)]
pub struct V { pub struct V {
/// The UUID of the category to which this session belongs
pub category: trees::categories::K, pub category: trees::categories::K,
/// The time when this session was started
pub started: NaiveDateTime, pub started: NaiveDateTime,
/// The time when this session was ended
pub ended: NaiveDateTime, pub ended: NaiveDateTime,
} }
} }

View File

@ -4,6 +4,8 @@ mod handlebars_util;
mod routes; mod routes;
mod status_json; mod status_json;
use bincode::{deserialize, serialize};
use database::{unversioned::global::schema_version, SCHEMA_VERSION};
use dotenv::dotenv; use dotenv::dotenv;
use rocket_contrib::serve::StaticFiles; use rocket_contrib::serve::StaticFiles;
use rocket_contrib::templates::Template; use rocket_contrib::templates::Template;
@ -15,6 +17,21 @@ fn main() -> io::Result<()> {
let db_path = env::var("DB_PATH").expect("DB_PATH not set"); let db_path = env::var("DB_PATH").expect("DB_PATH not set");
let sled = sled::open(db_path)?; let sled = sled::open(db_path)?;
match sled.insert(
serialize(schema_version::K).unwrap(),
serialize(&SCHEMA_VERSION).unwrap(),
)? {
Some(prev_schema_version) => {
let prev_schema_version: schema_version::V = deserialize(&prev_schema_version).unwrap();
println!(
"Schema version: {}, previously: {}",
SCHEMA_VERSION, prev_schema_version
);
}
None => {
println!("Schema version: {}, previously: None", SCHEMA_VERSION);
}
}
let rocket = rocket::ignite() let rocket = rocket::ignite()
.attach(Template::custom(|engines| { .attach(Template::custom(|engines| {
@ -28,8 +45,9 @@ fn main() -> io::Result<()> {
routes::pages::index, routes::pages::index,
routes::pages::history, routes::pages::history,
routes::api::create_category, routes::api::create_category,
routes::api::activate_category, routes::api::start_session,
routes::api::deactivate_category, routes::api::end_session,
routes::api::delete_session,
], ],
); );

View File

@ -1,10 +1,12 @@
use crate::database::v1::trees::{categories, past_sessions}; use crate::database::latest::trees::{categories, past_sessions};
use crate::routes::pages;
use crate::status_json::StatusJson; use crate::status_json::StatusJson;
use bincode::{deserialize, serialize}; use bincode::{deserialize, serialize};
use chrono::{Duration, Utc}; use chrono::{Duration, Utc};
use rocket::http::Status; use rocket::http::Status;
use rocket::request::{Form, FromForm}; use rocket::request::{Form, FromForm};
use rocket::{post, State}; use rocket::response::Redirect;
use rocket::{post, uri, State};
use sled::Transactional; use sled::Transactional;
use uuid::Uuid; use uuid::Uuid;
@ -34,23 +36,23 @@ pub fn create_category(
Ok(Status::Ok.into()) Ok(Status::Ok.into())
} }
#[post("/set_category/<category_uuid>/active")] #[post("/category/<category_uuid>/start_session")]
pub fn activate_category( pub fn start_session(
category_uuid: String, category_uuid: String,
db: State<'_, sled::Db>, db: State<'_, sled::Db>,
) -> Result<StatusJson, StatusJson> { ) -> Result<StatusJson, StatusJson> {
toggle_category(category_uuid, true, db) toggle_category_session(category_uuid, true, db)
} }
#[post("/set_category/<category_uuid>/inactive")] #[post("/category/<category_uuid>/end_session")]
pub fn deactivate_category( pub fn end_session(
category_uuid: String, category_uuid: String,
db: State<'_, sled::Db>, db: State<'_, sled::Db>,
) -> Result<StatusJson, StatusJson> { ) -> Result<StatusJson, StatusJson> {
toggle_category(category_uuid, false, db) toggle_category_session(category_uuid, false, db)
} }
pub fn toggle_category( pub fn toggle_category_session(
category_uuid: String, category_uuid: String,
set_active: bool, set_active: bool,
db: State<'_, sled::Db>, db: State<'_, sled::Db>,
@ -101,3 +103,30 @@ pub fn toggle_category(
}, },
)??) )??)
} }
#[post("/session/<session_uuid>/delete")]
pub fn delete_session(
session_uuid: String,
db: State<'_, sled::Db>,
) -> Result<Redirect, StatusJson> {
let session_uuid = Uuid::parse_str(&session_uuid)?;
let session_uuid_s = sled::IVec::from(serialize(&session_uuid)?);
let past_sessions_tree = db.open_tree(past_sessions::NAME)?;
match past_sessions_tree.remove(session_uuid_s)? {
Some(_) => Ok(Redirect::to(uri!(pages::history))),
None => Err(Status::NotFound.into()),
}
// TODO: mark as deleted instead of removing
// Ok(past_sessions_tree.transaction(|tx_past_sessions| {
// match tx_past_sessions.get(&session_uuid_s)? {
// None => return Ok(Err(Status::NotFound)),
// Some(data) => {
// let mut past_session: past_sessions::V = deserialize(&data).unwrap();
// }
// }
// Ok(Ok(Redirect::to(uri!(pages::history))))
// })??)
}

View File

@ -1,4 +1,4 @@
use crate::database::v1::trees::{categories, past_sessions}; use crate::database::latest::trees::{categories, past_sessions};
use crate::status_json::StatusJson; use crate::status_json::StatusJson;
use bincode::deserialize; use bincode::deserialize;
use rocket::{get, State}; use rocket::{get, State};
@ -35,6 +35,7 @@ pub fn history(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
struct HistoryEntryContext { struct HistoryEntryContext {
category: categories::V, category: categories::V,
session: past_sessions::V, session: past_sessions::V,
session_id: Uuid,
duration: Duration, duration: Duration,
} }
@ -60,19 +61,17 @@ pub fn history(db: State<'_, sled::Db>) -> Result<Template, StatusJson> {
}) })
.collect::<Result<Result<_, _>, _>>()??; .collect::<Result<Result<_, _>, _>>()??;
let mut context = dbg!(TemplateContext { let mut context = TemplateContext {
entries: past_sessions entries: past_sessions
.into_iter() .into_iter()
.map(|(_, session)| { .map(|(session_id, session)| HistoryEntryContext {
let category = categories.get(&session.category).unwrap().clone(); duration: (session.ended - session.started).to_std().unwrap(),
HistoryEntryContext { category: categories.get(&session.category).unwrap().clone(),
duration: (session.ended - session.started).to_std().unwrap(), session,
category, session_id,
session,
}
}) })
.collect(), .collect(),
}); };
// Newest entries first // Newest entries first
context.entries.sort_by_key(|entry| entry.session.started); context.entries.sort_by_key(|entry| entry.session.started);

View File

@ -1,3 +1,5 @@
#![allow(dead_code)]
use duplicate::duplicate; use duplicate::duplicate;
use log::{info, warn}; use log::{info, warn};
use rocket::http::Status; use rocket::http::Status;

View File

@ -100,3 +100,6 @@ ul.striped_list > li:nth-of-type(odd) {
color: #9f2727; color: #9f2727;
} }
.history_entry_delete_button {
color: #aa0000;
}

View File

@ -17,13 +17,16 @@
<ul class="striped_list"> <ul class="striped_list">
{{#each entries}} {{#each entries}}
<li class="history_entry"> <li class="history_entry">
<span class="history_entry_category">{{this.category.name}}</span> <form action="/session/{{this.session_id}}/delete" method="post">
<span> for </span> <span class="history_entry_category">{{this.category.name}}</span>
<span class="history_entry_duration">{{pretty_seconds this.duration.secs}}</span> <span> under </span>
<span> from </span> <span class="history_entry_duration">{{pretty_seconds this.duration.secs}}</span>
<span class="history_entry_started">{{pretty_datetime this.session.started}}</span> <span> från </span>
<span> to </span> <span class="history_entry_started">{{pretty_datetime this.session.started}}</span>
<span class="history_entry_ended">{{pretty_datetime this.session.ended}}</span> <span> tills </span>
<span class="history_entry_ended">{{pretty_datetime this.session.ended}}</span>
<button type="submit" class="history_entry_delete_button">✕</button>
</form>
</li> </li>
{{/each}} {{/each}}
</ul> </ul>

View File

@ -20,10 +20,10 @@
// Get the corresponding route to activate/inactivate the category // Get the corresponding route to activate/inactivate the category
let url; let url;
if(active) { if(active) {
url = "/set_category/" + id + "/inactive"; url = "/category/" + id + "/end_session";
cl.remove(toggled_class); cl.remove(toggled_class);
} else { } else {
url = "/set_category/" + id + "/active"; url = "/category/" + id + "/start_session";
cl.add(toggled_class); cl.add(toggled_class);
} }
@ -42,10 +42,6 @@
<body> <body>
<h1 class="title">stl</h1> <h1 class="title">stl</h1>
{{#each categories}}
<form action="/toggle_category/{{this.0}}" method="post" id="{{this.0}}"></form>
{{/each}}
<ul class="striped_list"> <ul class="striped_list">
{{#each categories}} {{#each categories}}
<li class="category_entry"> <li class="category_entry">