diff --git a/rust/.gitignore b/rust/.gitignore index 4371c4e..bd04656 100644 --- a/rust/.gitignore +++ b/rust/.gitignore @@ -2,3 +2,4 @@ *.sqlite *.sqlite-wal *.sqlite-shm +*.sqlite-journal diff --git a/rust/sqlx-data.json b/rust/sqlx-data.json index 6022e6c..d427751 100644 --- a/rust/sqlx-data.json +++ b/rust/sqlx-data.json @@ -28,15 +28,71 @@ }, "query": "INSERT INTO trips_to_trips_types\n (trip_id, trip_type_id)\n VALUES\n (?, ?)\n " }, - "1320943d04e921a8e5f409737e466838b4ecf7e73ad0ade59ccd7664459a9c80": { + "16c1a8c70c0778528502dcf1067f5b23d8fc8aaa6d61385b79bac4224a78d703": { "describe": { - "columns": [], - "nullable": [], + "columns": [ + { + "name": "id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "picked", + "ordinal": 1, + "type_info": "Bool" + }, + { + "name": "packed", + "ordinal": 2, + "type_info": "Bool" + }, + { + "name": "ready", + "ordinal": 3, + "type_info": "Bool" + }, + { + "name": "new", + "ordinal": 4, + "type_info": "Bool" + }, + { + "name": "name", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "description", + "ordinal": 6, + "type_info": "Text" + }, + { + "name": "weight", + "ordinal": 7, + "type_info": "Int64" + }, + { + "name": "category_id", + "ordinal": 8, + "type_info": "Text" + } + ], + "nullable": [ + false, + false, + false, + false, + false, + false, + true, + false, + false + ], "parameters": { - "Right": 5 + "Right": 2 } }, - "query": "\n INSERT INTO trips_items\n (\n item_id,\n trip_id,\n pick,\n pack,\n new\n )\n VALUES (?, ?, ?, ?, ?)\n " + "query": "\n SELECT\n t_item.item_id AS id,\n t_item.pick AS picked,\n t_item.pack AS packed,\n t_item.ready AS ready,\n t_item.new AS new,\n i_item.name AS name,\n i_item.description AS description,\n i_item.weight AS weight,\n i_item.category_id AS category_id\n FROM trips_items AS t_item\n INNER JOIN inventory_items AS i_item\n ON i_item.id = t_item.item_id\n WHERE t_item.item_id = ?\n AND t_item.trip_id = ?\n " }, "1994305e1521fe1f5f927ad28e21c9cab8a25598b19e1c9038dae9092fe18f1f": { "describe": { @@ -130,6 +186,90 @@ }, "query": "UPDATE trips\n SET state = ?\n WHERE id = ?" }, + "343addb4024192688590b6b1228b9fdc554e2bbcc7e7300d8ca04cc4f23087d0": { + "describe": { + "columns": [ + { + "name": "category_id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "category_name", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "category_description", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "trip_id", + "ordinal": 3, + "type_info": "Text" + }, + { + "name": "item_id", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "item_name", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "item_description", + "ordinal": 6, + "type_info": "Text" + }, + { + "name": "item_weight", + "ordinal": 7, + "type_info": "Int64" + }, + { + "name": "item_is_picked", + "ordinal": 8, + "type_info": "Bool" + }, + { + "name": "item_is_packed", + "ordinal": 9, + "type_info": "Bool" + }, + { + "name": "item_is_ready", + "ordinal": 10, + "type_info": "Bool" + }, + { + "name": "item_is_new", + "ordinal": 11, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "parameters": { + "Right": 2 + } + }, + "query": "\n SELECT\n category.id as category_id,\n category.name as category_name,\n category.description AS category_description,\n inner.trip_id AS trip_id,\n inner.item_id AS item_id,\n inner.item_name AS item_name,\n inner.item_description AS item_description,\n inner.item_weight AS item_weight,\n inner.item_is_picked AS item_is_picked,\n inner.item_is_packed AS item_is_packed,\n inner.item_is_ready AS item_is_ready,\n inner.item_is_new AS item_is_new\n FROM inventory_items_categories AS category\n LEFT JOIN (\n SELECT\n trip.trip_id AS trip_id,\n category.id as category_id,\n category.name as category_name,\n category.description as category_description,\n item.id as item_id,\n item.name as item_name,\n item.description as item_description,\n item.weight as item_weight,\n trip.pick as item_is_picked,\n trip.pack as item_is_packed,\n trip.ready as item_is_ready,\n trip.new as item_is_new\n FROM trips_items as trip\n INNER JOIN inventory_items as item\n ON item.id = trip.item_id\n INNER JOIN inventory_items_categories as category\n ON category.id = item.category_id\n WHERE trip.trip_id = ?\n ) AS inner\n ON inner.category_id = category.id\n WHERE category.id = ?\n " + }, "452cb08b3b46bda9cb62d390d9f518d97626270a26465e55793b0a4b05432e50": { "describe": { "columns": [], @@ -364,6 +504,16 @@ }, "query": "DELETE FROM trips_to_trips_types AS ttt\n WHERE ttt.trip_id = ?\n AND ttt.trip_type_id = ?\n " }, + "86eb9fd598fbbb50b32ea7eae3151bfd8a83639c8b70301f5d5aeef19ae9462c": { + "describe": { + "columns": [], + "nullable": [], + "parameters": { + "Right": 6 + } + }, + "query": "\n INSERT INTO trips_items\n (\n item_id,\n trip_id,\n pick,\n pack,\n ready,\n new\n )\n VALUES (?, ?, ?, ?, ?, ?)\n " + }, "918fc9cf50097d4210b212255ef49335ebedbe81002ce9a418b4dab4fbb29aa3": { "describe": { "columns": [ @@ -458,6 +608,90 @@ }, "query": "SELECT\n id,\n name,\n CAST (date_start AS TEXT) date_start,\n CAST (date_end AS TEXT) date_end,\n state,\n location,\n temp_min,\n temp_max,\n comment\n FROM trips\n WHERE id = ?" }, + "a417bf136b8f79339a21fda10518c781d0d2bfc8c3fc99353d1956dfad9f5ac8": { + "describe": { + "columns": [ + { + "name": "category_id", + "ordinal": 0, + "type_info": "Text" + }, + { + "name": "category_name", + "ordinal": 1, + "type_info": "Text" + }, + { + "name": "category_description", + "ordinal": 2, + "type_info": "Text" + }, + { + "name": "trip_id", + "ordinal": 3, + "type_info": "Text" + }, + { + "name": "item_id", + "ordinal": 4, + "type_info": "Text" + }, + { + "name": "item_name", + "ordinal": 5, + "type_info": "Text" + }, + { + "name": "item_description", + "ordinal": 6, + "type_info": "Text" + }, + { + "name": "item_weight", + "ordinal": 7, + "type_info": "Int64" + }, + { + "name": "item_is_picked", + "ordinal": 8, + "type_info": "Bool" + }, + { + "name": "item_is_packed", + "ordinal": 9, + "type_info": "Bool" + }, + { + "name": "item_is_ready", + "ordinal": 10, + "type_info": "Bool" + }, + { + "name": "item_is_new", + "ordinal": 11, + "type_info": "Bool" + } + ], + "nullable": [ + false, + false, + true, + true, + true, + true, + true, + true, + true, + true, + true, + true + ], + "parameters": { + "Right": 1 + } + }, + "query": "\n SELECT\n category.id as category_id,\n category.name as category_name,\n category.description AS category_description,\n inner.trip_id AS trip_id,\n inner.item_id AS item_id,\n inner.item_name AS item_name,\n inner.item_description AS item_description,\n inner.item_weight AS item_weight,\n inner.item_is_picked AS item_is_picked,\n inner.item_is_packed AS item_is_packed,\n inner.item_is_ready AS item_is_ready,\n inner.item_is_new AS item_is_new\n FROM inventory_items_categories AS category\n LEFT JOIN (\n SELECT\n trip.trip_id AS trip_id,\n category.id as category_id,\n category.name as category_name,\n category.description as category_description,\n item.id as item_id,\n item.name as item_name,\n item.description as item_description,\n item.weight as item_weight,\n trip.pick as item_is_picked,\n trip.pack as item_is_packed,\n trip.ready as item_is_ready,\n trip.new as item_is_new\n FROM trips_items as trip\n INNER JOIN inventory_items as item\n ON item.id = trip.item_id\n INNER JOIN inventory_items_categories as category\n ON category.id = item.category_id\n WHERE trip.trip_id = ?\n ) AS inner\n ON inner.category_id = category.id\n " + }, "a5cdb8a6d5850326815efd460c0b42dda02a4ea32713ec89beceb38cd24321d5": { "describe": { "columns": [], @@ -546,84 +780,6 @@ }, "query": "\n SELECT\n CAST(IFNULL(SUM(i_item.weight), 0) AS INTEGER) AS total_weight\n FROM trips AS trip\n INNER JOIN trips_items AS t_item\n ON t_item.trip_id = trip.id\n INNER JOIN inventory_items AS i_item\n ON t_item.item_id = i_item.id\n WHERE\n trip.id = ?\n AND t_item.pick = true\n " }, - "d0562ad92782a6ad6080c0535749c4a0a28fa78a17698933bce670db057e2628": { - "describe": { - "columns": [ - { - "name": "category_id", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "category_name", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "category_description", - "ordinal": 2, - "type_info": "Text" - }, - { - "name": "trip_id", - "ordinal": 3, - "type_info": "Text" - }, - { - "name": "item_id", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "item_name", - "ordinal": 5, - "type_info": "Text" - }, - { - "name": "item_description", - "ordinal": 6, - "type_info": "Text" - }, - { - "name": "item_weight", - "ordinal": 7, - "type_info": "Int64" - }, - { - "name": "item_is_picked", - "ordinal": 8, - "type_info": "Bool" - }, - { - "name": "item_is_packed", - "ordinal": 9, - "type_info": "Bool" - }, - { - "name": "item_is_new", - "ordinal": 10, - "type_info": "Bool" - } - ], - "nullable": [ - false, - false, - true, - true, - true, - true, - true, - true, - true, - true, - true - ], - "parameters": { - "Right": 2 - } - }, - "query": "\n SELECT\n category.id as category_id,\n category.name as category_name,\n category.description AS category_description,\n inner.trip_id AS trip_id,\n inner.item_id AS item_id,\n inner.item_name AS item_name,\n inner.item_description AS item_description,\n inner.item_weight AS item_weight,\n inner.item_is_picked AS item_is_picked,\n inner.item_is_packed AS item_is_packed,\n inner.item_is_new AS item_is_new\n FROM inventory_items_categories AS category\n LEFT JOIN (\n SELECT\n trip.trip_id AS trip_id,\n category.id as category_id,\n category.name as category_name,\n category.description as category_description,\n item.id as item_id,\n item.name as item_name,\n item.description as item_description,\n item.weight as item_weight,\n trip.pick as item_is_picked,\n trip.pack as item_is_packed,\n trip.new as item_is_new\n FROM trips_items as trip\n INNER JOIN inventory_items as item\n ON item.id = trip.item_id\n INNER JOIN inventory_items_categories as category\n ON category.id = item.category_id\n WHERE trip.trip_id = ?\n ) AS inner\n ON inner.category_id = category.id\n WHERE category.id = ?\n " - }, "d7c6ae3c6e00c6c99b0bedee87ff237b01007e7001584c82ae896b91833b807b": { "describe": { "columns": [], @@ -644,144 +800,6 @@ }, "query": "INSERT INTO trips_types\n (id, name)\n VALUES\n (?, ?)" }, - "f24056f8d6e2d483185d71b036ae8a0a1943b8718e8255d826df76ac77ad6326": { - "describe": { - "columns": [ - { - "name": "category_id", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "category_name", - "ordinal": 1, - "type_info": "Text" - }, - { - "name": "category_description", - "ordinal": 2, - "type_info": "Text" - }, - { - "name": "trip_id", - "ordinal": 3, - "type_info": "Text" - }, - { - "name": "item_id", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "item_name", - "ordinal": 5, - "type_info": "Text" - }, - { - "name": "item_description", - "ordinal": 6, - "type_info": "Text" - }, - { - "name": "item_weight", - "ordinal": 7, - "type_info": "Int64" - }, - { - "name": "item_is_picked", - "ordinal": 8, - "type_info": "Bool" - }, - { - "name": "item_is_packed", - "ordinal": 9, - "type_info": "Bool" - }, - { - "name": "item_is_new", - "ordinal": 10, - "type_info": "Bool" - } - ], - "nullable": [ - false, - false, - true, - true, - true, - true, - true, - true, - true, - true, - true - ], - "parameters": { - "Right": 1 - } - }, - "query": "\n SELECT\n category.id as category_id,\n category.name as category_name,\n category.description AS category_description,\n inner.trip_id AS trip_id,\n inner.item_id AS item_id,\n inner.item_name AS item_name,\n inner.item_description AS item_description,\n inner.item_weight AS item_weight,\n inner.item_is_picked AS item_is_picked,\n inner.item_is_packed AS item_is_packed,\n inner.item_is_new AS item_is_new\n FROM inventory_items_categories AS category\n LEFT JOIN (\n SELECT\n trip.trip_id AS trip_id,\n category.id as category_id,\n category.name as category_name,\n category.description as category_description,\n item.id as item_id,\n item.name as item_name,\n item.description as item_description,\n item.weight as item_weight,\n trip.pick as item_is_picked,\n trip.pack as item_is_packed,\n trip.new as item_is_new\n FROM trips_items as trip\n INNER JOIN inventory_items as item\n ON item.id = trip.item_id\n INNER JOIN inventory_items_categories as category\n ON category.id = item.category_id\n WHERE trip.trip_id = ?\n ) AS inner\n ON inner.category_id = category.id\n " - }, - "f3fd58ae5e462c354d76fcfce1e86ffcdab80d507cc6599adebcdaea8bb2bc6f": { - "describe": { - "columns": [ - { - "name": "id", - "ordinal": 0, - "type_info": "Text" - }, - { - "name": "picked", - "ordinal": 1, - "type_info": "Bool" - }, - { - "name": "packed", - "ordinal": 2, - "type_info": "Bool" - }, - { - "name": "new", - "ordinal": 3, - "type_info": "Bool" - }, - { - "name": "name", - "ordinal": 4, - "type_info": "Text" - }, - { - "name": "description", - "ordinal": 5, - "type_info": "Text" - }, - { - "name": "weight", - "ordinal": 6, - "type_info": "Int64" - }, - { - "name": "category_id", - "ordinal": 7, - "type_info": "Text" - } - ], - "nullable": [ - false, - false, - false, - false, - false, - true, - false, - false - ], - "parameters": { - "Right": 2 - } - }, - "query": "\n SELECT\n t_item.item_id AS id,\n t_item.pick AS picked,\n t_item.pack AS packed,\n t_item.new AS new,\n i_item.name AS name,\n i_item.description AS description,\n i_item.weight AS weight,\n i_item.category_id AS category_id\n FROM trips_items AS t_item\n INNER JOIN inventory_items AS i_item\n ON i_item.id = t_item.item_id\n WHERE t_item.item_id = ?\n AND t_item.trip_id = ?\n " - }, "f9d080a5b8710c7d6a497bb1f5cf4839ad1589fd7d6a06d3faf1163d6981d8a0": { "describe": { "columns": [ diff --git a/rust/src/main.rs b/rust/src/main.rs index 4b91a76..0ecc249 100644 --- a/rust/src/main.rs +++ b/rust/src/main.rs @@ -230,6 +230,14 @@ async fn main() -> Result<(), StartError> { .route( "/:id/items/:id/unpack", get(trip_item_set_unpack).post(trip_item_set_unpack_htmx), + ) + .route( + "/:id/items/:id/ready", + get(trip_item_set_ready).post(trip_item_set_ready_htmx), + ) + .route( + "/:id/items/:id/unready", + get(trip_item_set_unready).post(trip_item_set_unready_htmx), ), ) .nest( @@ -856,6 +864,82 @@ async fn trip_item_set_unpack_htmx( Ok((headers, trip_row(&state, trip_id, item_id).await?)) } +async fn trip_item_set_ready( + State(state): State, + Path((trip_id, item_id)): Path<(Uuid, Uuid)>, + headers: HeaderMap, +) -> Result { + Ok::<_, Error>( + trip_item_set_state( + &state, + trip_id, + item_id, + models::trips::TripItemStateKey::Ready, + true, + ) + .await?, + ) + .map(|_| -> Result { Ok(Redirect::to(get_referer(&headers)?)) })? +} + +async fn trip_item_set_ready_htmx( + State(state): State, + Path((trip_id, item_id)): Path<(Uuid, Uuid)>, +) -> Result { + trip_item_set_state( + &state, + trip_id, + item_id, + models::trips::TripItemStateKey::Ready, + true, + ) + .await?; + let mut headers = HeaderMap::new(); + headers.insert::( + HtmxResponseHeaders::Trigger.into(), + HtmxEvents::TripItemEdited.into(), + ); + Ok((headers, trip_row(&state, trip_id, item_id).await?)) +} + +async fn trip_item_set_unready( + State(state): State, + Path((trip_id, item_id)): Path<(Uuid, Uuid)>, + headers: HeaderMap, +) -> Result { + Ok::<_, Error>( + trip_item_set_state( + &state, + trip_id, + item_id, + models::trips::TripItemStateKey::Ready, + false, + ) + .await?, + ) + .map(|_| -> Result { Ok(Redirect::to(get_referer(&headers)?)) })? +} + +async fn trip_item_set_unready_htmx( + State(state): State, + Path((trip_id, item_id)): Path<(Uuid, Uuid)>, +) -> Result { + trip_item_set_state( + &state, + trip_id, + item_id, + models::trips::TripItemStateKey::Ready, + false, + ) + .await?; + let mut headers = HeaderMap::new(); + headers.insert::( + HtmxResponseHeaders::Trigger.into(), + HtmxEvents::TripItemEdited.into(), + ); + Ok((headers, trip_row(&state, trip_id, item_id).await?)) +} + async fn trip_total_weight_htmx( State(state): State, Path(trip_id): Path, diff --git a/rust/src/models/trips.rs b/rust/src/models/trips.rs index 4a0d5fc..695f56a 100644 --- a/rust/src/models/trips.rs +++ b/rust/src/models/trips.rs @@ -91,6 +91,7 @@ impl std::convert::TryFrom<&str> for TripState { pub enum TripItemStateKey { Pick, Pack, + Ready, } impl fmt::Display for TripItemStateKey { @@ -101,6 +102,7 @@ impl fmt::Display for TripItemStateKey { match self { Self::Pick => "pick", Self::Pack => "pack", + Self::Ready => "ready", }, ) } @@ -146,6 +148,7 @@ impl TripCategory { inner.item_weight AS item_weight, inner.item_is_picked AS item_is_picked, inner.item_is_packed AS item_is_packed, + inner.item_is_ready AS item_is_ready, inner.item_is_new AS item_is_new FROM inventory_items_categories AS category LEFT JOIN ( @@ -160,6 +163,7 @@ impl TripCategory { item.weight as item_weight, trip.pick as item_is_picked, trip.pack as item_is_packed, + trip.ready as item_is_ready, trip.new as item_is_new FROM trips_items as trip INNER JOIN inventory_items as item @@ -209,6 +213,7 @@ impl TripCategory { }, picked: row.item_is_picked.unwrap(), packed: row.item_is_packed.unwrap(), + ready: row.item_is_ready.unwrap(), new: row.item_is_new.unwrap(), }; @@ -237,18 +242,20 @@ pub struct TripItem { pub item: inventory::Item, pub picked: bool, pub packed: bool, + pub ready: bool, pub new: bool, } -pub(crate) struct DbTripsItemsRow { - pub(crate) picked: bool, - pub(crate) packed: bool, - pub(crate) new: bool, - pub(crate) id: String, - pub(crate) name: String, - pub(crate) weight: i64, - pub(crate) description: Option, - pub(crate) category_id: String, +pub struct DbTripsItemsRow { + pub picked: bool, + pub packed: bool, + pub ready: bool, + pub new: bool, + pub id: String, + pub name: String, + pub weight: i64, + pub description: Option, + pub category_id: String, } impl TryFrom for TripItem { @@ -258,6 +265,7 @@ impl TryFrom for TripItem { Ok(TripItem { picked: row.picked, packed: row.packed, + ready: row.ready, new: row.new, item: inventory::Item { id: Uuid::try_parse(&row.id)?, @@ -285,6 +293,7 @@ impl TripItem { t_item.item_id AS id, t_item.pick AS picked, t_item.pack AS packed, + t_item.ready AS ready, t_item.new AS new, i_item.name AS name, i_item.description AS description, @@ -738,14 +747,16 @@ impl Trip { trip_id, pick, pack, + ready, new ) - VALUES (?, ?, ?, ?, ?) + VALUES (?, ?, ?, ?, ?, ?) ", item_id, trip_id, false, false, + false, mark_as_new, ) .execute(pool) @@ -775,6 +786,7 @@ impl Trip { inner.item_weight AS item_weight, inner.item_is_picked AS item_is_picked, inner.item_is_packed AS item_is_packed, + inner.item_is_ready AS item_is_ready, inner.item_is_new AS item_is_new FROM inventory_items_categories AS category LEFT JOIN ( @@ -789,6 +801,7 @@ impl Trip { item.weight as item_weight, trip.pick as item_is_picked, trip.pack as item_is_packed, + trip.ready as item_is_ready, trip.new as item_is_new FROM trips_items as trip INNER JOIN inventory_items as item @@ -832,6 +845,7 @@ impl Trip { }, picked: row.item_is_picked.unwrap(), packed: row.item_is_packed.unwrap(), + ready: row.item_is_ready.unwrap(), new: row.item_is_new.unwrap(), }; diff --git a/rust/src/view/trip/mod.rs b/rust/src/view/trip/mod.rs index da4868c..63930eb 100644 --- a/rust/src/view/trip/mod.rs +++ b/rust/src/view/trip/mod.rs @@ -1032,6 +1032,7 @@ impl TripItemList { { thead ."bg-gray-200" { tr ."h-10" { + th ."border" ."p-2" {} th ."border" ."p-2" {} th ."border" ."p-2" {} th ."border" ."p-2" ."w-1/2" { "Name" } @@ -1099,6 +1100,61 @@ impl TripItemListRow { } } } + td + ."border" + ."p-0" + { + @if item.picked { + a + href={ + "/trips/" (trip_id) + "/items/" (item.item.id) + "/" (if item.ready { "unready" } else { "ready" }) } + hx-post={ + "/trips/" (trip_id) + "/items/" (item.item.id) + "/" (if item.ready { "unready" } else { "ready" }) } + hx-target="closest tr" + hx-swap="outerHTML" + ."inline-block" + ."p-2" + ."m-0" + ."w-full" + ."justify-center" + ."content-center" + ."flex" + ."bg-green-200"[item.ready] + ."hover:bg-green-100"[!item.ready] + { + @if item.ready { + span + ."mdi" + ."mdi-wardrobe-outline" + ."text-2xl" + {} + } @else { + span + ."mdi" + ."mdi-map-marker-question-outline" + ."text-2xl" + {} + } + } + } @else { + div + ."flex" + ."justify-center" + ."items-center" + { + span + ."mdi" + ."mdi-wardrobe-outline" + ."text-2xl" + ."text-gray-300" + {} + } + } + } td ."border" ."p-0"