diff --git a/lib/src/v2.rs b/lib/src/v2.rs index 3d2ad78..e8e4a3d 100644 --- a/lib/src/v2.rs +++ b/lib/src/v2.rs @@ -82,6 +82,7 @@ pub mod trees { /// Whether the item has been "deleted", e.g. it shoudn't be shown in the view // FIXME: this field is currently not used + #[serde(default)] pub deleted: bool, } diff --git a/server/example.env b/server/example.env index 8d9c900..b354251 100644 --- a/server/example.env +++ b/server/example.env @@ -9,7 +9,7 @@ DB_PATH=./database #ROCKET_PORT=8000 #ROCKET_WORKERS=[number of cpus * 2] #ROCKET_LOG="normal" -#ROCKET_SECRET_KEY=[randomly generated at launch] +#ROCKET_SECRET_KEY=[random string, 44 or 88 for base64, 64 for hex] #ROCKET_LIMITS="{ forms = 32768 }" ROCKET_TEMPLATE_DIR="templates" ## =============================== ## diff --git a/server/src/database/v2.rs b/server/src/database/v2.rs index cec8fc8..067507d 100644 --- a/server/src/database/v2.rs +++ b/server/src/database/v2.rs @@ -71,5 +71,10 @@ pub mod trees { }) .collect::, _>>()??) } + + pub fn put(tree: &sled::Tree, key: &K, val: &V) -> Result<(), StatusJson> { + tree.insert(serialize(key)?, serialize(val)?)?; + Ok(()) + } } } diff --git a/server/src/main.rs b/server/src/main.rs index 83808a3..8db9648 100644 --- a/server/src/main.rs +++ b/server/src/main.rs @@ -78,6 +78,8 @@ async fn main() -> io::Result<()> { routes::pages::bump_session, routes::pages::stats::single_stats, routes::pages::stats::all_stats, + routes::pages::dailies::dailies, + routes::pages::dailies::new_daily, routes::pages::weeks::weeks, ], ) diff --git a/server/src/routes/pages/dailies.rs b/server/src/routes/pages/dailies.rs index 43ef9bd..83ae4af 100644 --- a/server/src/routes/pages/dailies.rs +++ b/server/src/routes/pages/dailies.rs @@ -1,17 +1,58 @@ -use rocket::{get, response::content::RawHtml, State}; +use rocket::{ + form::Form, + get, post, + response::{content::RawHtml, Redirect}, + uri, FromForm, State, +}; use rocket_dyn_templates::Template; use serde::{Deserialize, Serialize}; +use uuid::Uuid; use crate::{ - auth::Authorized, database::latest::trees::daily::V as Daily, status_json::StatusJson, + auth::Authorized, + database::latest::trees::daily::{self, V as Daily}, + status_json::StatusJson, }; -#[get("/")] +#[derive(FromForm)] +pub struct NewDaily { + name: String, + unit: String, + unit_count: u32, +} + +#[post("/dailies/new", data = "")] +pub fn new_daily( + _auth: Authorized, + db: &State, + daily: Form, +) -> Result { + let daily = daily.into_inner(); + let daily = Daily { + name: daily.name, + unit: serde_json::from_str(&format!("\"{}\"", daily.unit)).unwrap(), // TODO + unit_count: daily.unit_count, + deleted: false, + }; + + let dailies_tree = db.open_tree(daily::NAME)?; + daily::put(&dailies_tree, &Uuid::new_v4(), &daily)?; + + Ok(Redirect::to(uri!(dailies))) +} + +#[get("/dailies")] pub fn dailies(_auth: Authorized, db: &State) -> Result, StatusJson> { - #[derive(Debug, Serialize, Deserialize)] + #[derive(Default, Debug, Serialize, Deserialize)] struct TemplateContext { dailies: Vec, } - todo!() + let dailies_tree = db.open_tree(daily::NAME)?; + let mut ctx = TemplateContext::default(); + + ctx.dailies = daily::get_all(&dailies_tree)?.into_values().collect(); + ctx.dailies.sort_by(|a, b| a.name.cmp(&b.name)); + + Ok(RawHtml(Template::render("dailies", &ctx))) } diff --git a/server/static/styles/common.css b/server/static/styles/common.css index c9fcf42..6b7b998 100644 --- a/server/static/styles/common.css +++ b/server/static/styles/common.css @@ -233,6 +233,88 @@ ul.striped_list > li:nth-child(odd) ul li:nth-child(odd) { background-color:#30 display: none; } +.dailies_list { + max-width: 40rem; + margin: auto; + display: flex; + flex-direction: column; +} + +.dailies_entry { + color: wheat; + font-size: 1.5em; + background: #3a3743; + margin-bottom: 1rem; + padding: 1rem; + border-radius: 0.35rem; + border: .2rem solid #45374f; + display: flex; + cursor: pointer; + + transition: background-color 0.1s; +} + +.dailies_entry:hover { + background-color: #4c4858; +} + +.dailies_entry:active { + background-color: #312e38; +} + +.dailies_entry > span { + flex-grow: 1 +} + +.dailies_checkbox input[type=checkbox] { + visibility:hidden; + display:none +} +.dailies_checkbox *, +.dailies_checkbox :after, +.dailies_checkbox :before { + box-sizing:border-box +} +.dailies_checkbox .container { + cursor:pointer; + user-select:none; + font-size:25px; + display:block; + position:relative +} +.dailies_checkbox .checkmark { + --spread:10px; + background:#000; + border-radius:50px; + width:1.3em; + height:1.3em; + transition:all .7s; + position:relative; + top:0; + left:0 +} +.dailies_checkbox .container input:checked~.checkmark { + box-shadow:-5px -5px var(--spread)0px #5b51d8,0 -5px var(--spread)0px #833ab4,5px -5px var(--spread)0px #e1306c,5px 0 var(--spread)0px #fd1d1d,5px 5px var(--spread)0px #f77737,0 5px var(--spread)0px #fcaf45,-5px 5px var(--spread)0px #ffdc80; + background:#000 +} +.dailies_checkbox .checkmark:after { + content:""; + display:none; + position:absolute +} +.dailies_checkbox .container input:checked~.checkmark:after { + display:block +} +.dailies_checkbox .container .checkmark:after { + border:.15em solid wheat; + border-width:0 .15em .15em 0; + width:.25em; + height:.5em; + top:.34em; + left:.5em; + transform:rotate(45deg) +} + .life_calendar { display: flex; flex-direction: row; diff --git a/server/templates/dailies.html.hbs b/server/templates/dailies.html.hbs index ca1868d..05be885 100644 --- a/server/templates/dailies.html.hbs +++ b/server/templates/dailies.html.hbs @@ -3,12 +3,35 @@ {{> head}} {{> header}} -

Dailies

-
- {{#each categories}} - {{>category_entry}} - {{/each}} +
+

Dailies

+ {{#each dailies}} + + {{/each}} + +
+ Namn + + 1 gång per + + +
+ +