add todos
This commit is contained in:
32
.sqlx/query-17dfc8ae16d077ed71976012315376e1df403cb81ef173cb8811a5481186db7a.json
generated
Normal file
32
.sqlx/query-17dfc8ae16d077ed71976012315376e1df403cb81ef173cb8811a5481186db7a.json
generated
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "\n SELECT\n todo.id AS id,\n todo.description AS description,\n todo.done AS done\n FROM trip_todos AS todo\n INNER JOIN trips\n ON trips.id = todo.trip_id\n WHERE \n trips.id = $1\n AND trips.user_id = $2\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "done",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 2
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "17dfc8ae16d077ed71976012315376e1df403cb81ef173cb8811a5481186db7a"
|
||||||
|
}
|
||||||
32
.sqlx/query-a9e75a36e019bb54ff06443a2ce98c788f88be295b6171f2f36e08c91109e380.json
generated
Normal file
32
.sqlx/query-a9e75a36e019bb54ff06443a2ce98c788f88be295b6171f2f36e08c91109e380.json
generated
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "\n SELECT\n todo.id AS id,\n todo.description AS description,\n todo.done AS done\n FROM trip_todos AS todo\n INNER JOIN trips\n ON trips.id = todo.trip_id\n WHERE \n trips.id = $1\n AND todo.id = $2\n AND trips.user_id = $3\n ",
|
||||||
|
"describe": {
|
||||||
|
"columns": [
|
||||||
|
{
|
||||||
|
"name": "id",
|
||||||
|
"ordinal": 0,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "description",
|
||||||
|
"ordinal": 1,
|
||||||
|
"type_info": "Text"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "done",
|
||||||
|
"ordinal": 2,
|
||||||
|
"type_info": "Bool"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 3
|
||||||
|
},
|
||||||
|
"nullable": [
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"hash": "a9e75a36e019bb54ff06443a2ce98c788f88be295b6171f2f36e08c91109e380"
|
||||||
|
}
|
||||||
12
.sqlx/query-d29d72b5c9dbf34d672aa271823b3b87f29e85ccdf67dc40f9ce372d5db9727d.json
generated
Normal file
12
.sqlx/query-d29d72b5c9dbf34d672aa271823b3b87f29e85ccdf67dc40f9ce372d5db9727d.json
generated
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"db_name": "SQLite",
|
||||||
|
"query": "\n UPDATE trip_todos\n SET done = ?\n WHERE trip_id = ?\n AND id = ?\n AND EXISTS(SELECT 1 FROM trips WHERE id = ? AND user_id = ?)",
|
||||||
|
"describe": {
|
||||||
|
"columns": [],
|
||||||
|
"parameters": {
|
||||||
|
"Right": 5
|
||||||
|
},
|
||||||
|
"nullable": []
|
||||||
|
},
|
||||||
|
"hash": "d29d72b5c9dbf34d672aa271823b3b87f29e85ccdf67dc40f9ce372d5db9727d"
|
||||||
|
}
|
||||||
@@ -1 +1,9 @@
|
|||||||
-- Add migration script here
|
-- Add migration script here
|
||||||
|
CREATE TABLE "trip_todos" (
|
||||||
|
id VARCHAR(36) NOT NULL,
|
||||||
|
trip_id VARCHAR(36) NOT NULL,
|
||||||
|
description TEXT NOT NULL,
|
||||||
|
done BOOLEAN NOT NULL,
|
||||||
|
PRIMARY KEY (id),
|
||||||
|
FOREIGN KEY(trip_id) REFERENCES "trips" (id)
|
||||||
|
)
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
use super::Error;
|
use super::Error;
|
||||||
use crate::{sqlite, Context};
|
use crate::{sqlite, Context};
|
||||||
|
|
||||||
use futures::{TryFutureExt, TryStreamExt};
|
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
pub struct Inventory {
|
pub struct Inventory {
|
||||||
|
|||||||
@@ -8,11 +8,12 @@ use super::{
|
|||||||
|
|
||||||
use crate::{sqlite, Context};
|
use crate::{sqlite, Context};
|
||||||
|
|
||||||
use futures::{TryFutureExt, TryStreamExt};
|
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
use time;
|
use time;
|
||||||
use uuid::Uuid;
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
pub mod todos;
|
||||||
|
|
||||||
// #[macro_use]
|
// #[macro_use]
|
||||||
// mod macros {
|
// mod macros {
|
||||||
// macro_rules! build_state_query {
|
// macro_rules! build_state_query {
|
||||||
@@ -491,6 +492,7 @@ impl TryFrom<DbTripRow> for Trip {
|
|||||||
temp_min: row.temp_min,
|
temp_min: row.temp_min,
|
||||||
temp_max: row.temp_max,
|
temp_max: row.temp_max,
|
||||||
comment: row.comment,
|
comment: row.comment,
|
||||||
|
todos: None,
|
||||||
types: None,
|
types: None,
|
||||||
categories: None,
|
categories: None,
|
||||||
})
|
})
|
||||||
@@ -508,8 +510,9 @@ pub struct Trip {
|
|||||||
pub temp_min: Option<i64>,
|
pub temp_min: Option<i64>,
|
||||||
pub temp_max: Option<i64>,
|
pub temp_max: Option<i64>,
|
||||||
pub comment: Option<String>,
|
pub comment: Option<String>,
|
||||||
pub(crate) types: Option<Vec<TripType>>,
|
pub todos: Option<Vec<todos::Todo>>,
|
||||||
pub(crate) categories: Option<Vec<TripCategory>>,
|
pub types: Option<Vec<TripType>>,
|
||||||
|
pub categories: Option<Vec<TripCategory>>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)]
|
||||||
@@ -990,7 +993,12 @@ impl Trip {
|
|||||||
pub fn categories(&self) -> &Vec<TripCategory> {
|
pub fn categories(&self) -> &Vec<TripCategory> {
|
||||||
self.categories
|
self.categories
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.expect("you need to call load_trips_types()")
|
.expect("you need to call load_categories()")
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub fn todos(&self) -> &Vec<todos::Todo> {
|
||||||
|
self.todos.as_ref().expect("you need to call load_todos()")
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
@@ -1009,6 +1017,12 @@ impl Trip {
|
|||||||
.sum::<i64>()
|
.sum::<i64>()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub async fn load_todos(&mut self, ctx: &Context, pool: &sqlite::Pool) -> Result<(), Error> {
|
||||||
|
self.todos = Some(todos::Todo::load(ctx, pool, self.id).await?);
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
#[tracing::instrument]
|
#[tracing::instrument]
|
||||||
pub async fn load_trips_types(
|
pub async fn load_trips_types(
|
||||||
&mut self,
|
&mut self,
|
||||||
176
src/models/trips/todos.rs
Normal file
176
src/models/trips/todos.rs
Normal file
@@ -0,0 +1,176 @@
|
|||||||
|
use uuid::Uuid;
|
||||||
|
|
||||||
|
use crate::{
|
||||||
|
models::{Error, QueryError},
|
||||||
|
sqlite, Context,
|
||||||
|
};
|
||||||
|
|
||||||
|
#[derive(Debug, PartialEq, Eq)]
|
||||||
|
pub enum State {
|
||||||
|
Todo,
|
||||||
|
Done,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<bool> for State {
|
||||||
|
fn from(done: bool) -> Self {
|
||||||
|
if done {
|
||||||
|
Self::Done
|
||||||
|
} else {
|
||||||
|
Self::Todo
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<State> for bool {
|
||||||
|
fn from(value: State) -> Self {
|
||||||
|
match value {
|
||||||
|
State::Todo => false,
|
||||||
|
State::Done => true,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct Todo {
|
||||||
|
pub id: Uuid,
|
||||||
|
pub description: String,
|
||||||
|
pub state: State,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct TodoRow {
|
||||||
|
id: String,
|
||||||
|
description: String,
|
||||||
|
done: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl TryFrom<TodoRow> for Todo {
|
||||||
|
type Error = Error;
|
||||||
|
|
||||||
|
fn try_from(row: TodoRow) -> Result<Self, Self::Error> {
|
||||||
|
Ok(Todo {
|
||||||
|
id: Uuid::try_parse(&row.id)?,
|
||||||
|
description: row.description,
|
||||||
|
state: row.done.into(),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Todo {
|
||||||
|
pub fn is_done(&self) -> bool {
|
||||||
|
self.state == State::Done
|
||||||
|
}
|
||||||
|
|
||||||
|
pub async fn load(
|
||||||
|
ctx: &Context,
|
||||||
|
pool: &sqlite::Pool,
|
||||||
|
trip_id: Uuid,
|
||||||
|
) -> Result<Vec<Self>, Error> {
|
||||||
|
let trip_id_param = trip_id.to_string();
|
||||||
|
let user_id = ctx.user.id.to_string();
|
||||||
|
|
||||||
|
let todos: Vec<Todo> = crate::query_all!(
|
||||||
|
&sqlite::QueryClassification {
|
||||||
|
query_type: sqlite::QueryType::Select,
|
||||||
|
component: sqlite::Component::Todo,
|
||||||
|
},
|
||||||
|
pool,
|
||||||
|
TodoRow,
|
||||||
|
Todo,
|
||||||
|
r#"
|
||||||
|
SELECT
|
||||||
|
todo.id AS id,
|
||||||
|
todo.description AS description,
|
||||||
|
todo.done AS done
|
||||||
|
FROM trip_todos AS todo
|
||||||
|
INNER JOIN trips
|
||||||
|
ON trips.id = todo.trip_id
|
||||||
|
WHERE
|
||||||
|
trips.id = $1
|
||||||
|
AND trips.user_id = $2
|
||||||
|
"#,
|
||||||
|
trip_id_param,
|
||||||
|
user_id,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(todos)
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub async fn find(
|
||||||
|
ctx: &Context,
|
||||||
|
pool: &sqlite::Pool,
|
||||||
|
trip_id: Uuid,
|
||||||
|
todo_id: Uuid,
|
||||||
|
) -> Result<Option<Self>, Error> {
|
||||||
|
let trip_id_param = trip_id.to_string();
|
||||||
|
let todo_id_param = todo_id.to_string();
|
||||||
|
let user_id = ctx.user.id.to_string();
|
||||||
|
crate::query_one!(
|
||||||
|
&sqlite::QueryClassification {
|
||||||
|
query_type: sqlite::QueryType::Select,
|
||||||
|
component: sqlite::Component::Todo,
|
||||||
|
},
|
||||||
|
pool,
|
||||||
|
TodoRow,
|
||||||
|
Self,
|
||||||
|
r#"
|
||||||
|
SELECT
|
||||||
|
todo.id AS id,
|
||||||
|
todo.description AS description,
|
||||||
|
todo.done AS done
|
||||||
|
FROM trip_todos AS todo
|
||||||
|
INNER JOIN trips
|
||||||
|
ON trips.id = todo.trip_id
|
||||||
|
WHERE
|
||||||
|
trips.id = $1
|
||||||
|
AND todo.id = $2
|
||||||
|
AND trips.user_id = $3
|
||||||
|
"#,
|
||||||
|
trip_id_param,
|
||||||
|
todo_id_param,
|
||||||
|
user_id,
|
||||||
|
)
|
||||||
|
.await
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub async fn set_state(
|
||||||
|
ctx: &Context,
|
||||||
|
pool: &sqlite::Pool,
|
||||||
|
trip_id: Uuid,
|
||||||
|
todo_id: Uuid,
|
||||||
|
state: State,
|
||||||
|
) -> Result<(), Error> {
|
||||||
|
let user_id = ctx.user.id.to_string();
|
||||||
|
let trip_id_param = trip_id.to_string();
|
||||||
|
let todo_id_param = todo_id.to_string();
|
||||||
|
let done = state == State::Done;
|
||||||
|
|
||||||
|
let result = crate::execute!(
|
||||||
|
&sqlite::QueryClassification {
|
||||||
|
query_type: sqlite::QueryType::Update,
|
||||||
|
component: sqlite::Component::Trips,
|
||||||
|
},
|
||||||
|
pool,
|
||||||
|
r#"
|
||||||
|
UPDATE trip_todos
|
||||||
|
SET done = ?
|
||||||
|
WHERE trip_id = ?
|
||||||
|
AND id = ?
|
||||||
|
AND EXISTS(SELECT 1 FROM trips WHERE id = ? AND user_id = ?)"#,
|
||||||
|
done,
|
||||||
|
trip_id_param,
|
||||||
|
todo_id_param,
|
||||||
|
trip_id_param,
|
||||||
|
user_id
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
(result.rows_affected() != 0).then_some(()).ok_or_else(|| {
|
||||||
|
Error::Query(QueryError::NotFound {
|
||||||
|
description: format!("todo {todo_id} not found for trip {trip_id}"),
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -139,6 +139,14 @@ pub fn router(state: AppState) -> Router {
|
|||||||
.route(
|
.route(
|
||||||
"/:id/items/:id/unready",
|
"/:id/items/:id/unready",
|
||||||
get(trip_item_set_unready).post(trip_item_set_unready_htmx),
|
get(trip_item_set_unready).post(trip_item_set_unready_htmx),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/:id/todo/:id/done",
|
||||||
|
get(trip_todo_done).post(trip_todo_done_htmx),
|
||||||
|
)
|
||||||
|
.route(
|
||||||
|
"/:id/todo/:id/undone",
|
||||||
|
get(trip_todo_undone).post(trip_todo_undone_htmx),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
.nest(
|
.nest(
|
||||||
|
|||||||
@@ -437,6 +437,8 @@ pub async fn trip(
|
|||||||
|
|
||||||
trip.load_trips_types(&ctx, &state.database_pool).await?;
|
trip.load_trips_types(&ctx, &state.database_pool).await?;
|
||||||
|
|
||||||
|
trip.load_todos(&ctx, &state.database_pool).await?;
|
||||||
|
|
||||||
trip.sync_trip_items_with_inventory(&ctx, &state.database_pool)
|
trip.sync_trip_items_with_inventory(&ctx, &state.database_pool)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
@@ -1238,3 +1240,97 @@ pub async fn trip_item_packagelist_set_unready_htmx(
|
|||||||
trip_id, &item,
|
trip_id, &item,
|
||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub async fn trip_todo_done_htmx(
|
||||||
|
Extension(current_user): Extension<models::user::User>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path((trip_id, todo_id)): Path<(Uuid, Uuid)>,
|
||||||
|
) -> Result<impl IntoResponse, Error> {
|
||||||
|
let ctx = Context::build(current_user);
|
||||||
|
models::trips::todos::Todo::set_state(
|
||||||
|
&ctx,
|
||||||
|
&state.database_pool,
|
||||||
|
trip_id,
|
||||||
|
todo_id,
|
||||||
|
models::trips::todos::State::Done,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let todo_item = models::trips::todos::Todo::find(&ctx, &state.database_pool, trip_id, todo_id)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::Request(RequestError::NotFound {
|
||||||
|
message: format!("todo with id {todo_id} not found"),
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(view::trip::TripTodo::build(&trip_id, &todo_item))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub async fn trip_todo_done(
|
||||||
|
Extension(current_user): Extension<models::user::User>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path((trip_id, todo_id)): Path<(Uuid, Uuid)>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
) -> Result<impl IntoResponse, Error> {
|
||||||
|
let ctx = Context::build(current_user);
|
||||||
|
models::trips::todos::Todo::set_state(
|
||||||
|
&ctx,
|
||||||
|
&state.database_pool,
|
||||||
|
trip_id,
|
||||||
|
todo_id,
|
||||||
|
models::trips::todos::State::Done,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Redirect::to(get_referer(&headers)?))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub async fn trip_todo_undone_htmx(
|
||||||
|
Extension(current_user): Extension<models::user::User>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path((trip_id, todo_id)): Path<(Uuid, Uuid)>,
|
||||||
|
) -> Result<impl IntoResponse, Error> {
|
||||||
|
let ctx = Context::build(current_user);
|
||||||
|
models::trips::todos::Todo::set_state(
|
||||||
|
&ctx,
|
||||||
|
&state.database_pool,
|
||||||
|
trip_id,
|
||||||
|
todo_id,
|
||||||
|
models::trips::todos::State::Todo,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
let todo_item = models::trips::todos::Todo::find(&ctx, &state.database_pool, trip_id, todo_id)
|
||||||
|
.await?
|
||||||
|
.ok_or_else(|| {
|
||||||
|
Error::Request(RequestError::NotFound {
|
||||||
|
message: format!("todo with id {todo_id} not found"),
|
||||||
|
})
|
||||||
|
})?;
|
||||||
|
|
||||||
|
Ok(view::trip::TripTodo::build(&trip_id, &todo_item))
|
||||||
|
}
|
||||||
|
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub async fn trip_todo_undone(
|
||||||
|
Extension(current_user): Extension<models::user::User>,
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Path((trip_id, todo_id)): Path<(Uuid, Uuid)>,
|
||||||
|
headers: HeaderMap,
|
||||||
|
) -> Result<impl IntoResponse, Error> {
|
||||||
|
let ctx = Context::build(current_user);
|
||||||
|
models::trips::todos::Todo::set_state(
|
||||||
|
&ctx,
|
||||||
|
&state.database_pool,
|
||||||
|
trip_id,
|
||||||
|
todo_id,
|
||||||
|
models::trips::todos::State::Todo,
|
||||||
|
)
|
||||||
|
.await?;
|
||||||
|
|
||||||
|
Ok(Redirect::to(get_referer(&headers)?))
|
||||||
|
}
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ pub enum Component {
|
|||||||
Inventory,
|
Inventory,
|
||||||
User,
|
User,
|
||||||
Trips,
|
Trips,
|
||||||
|
Todo,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Component {
|
impl fmt::Display for Component {
|
||||||
@@ -100,6 +101,7 @@ impl fmt::Display for Component {
|
|||||||
Self::Inventory => "inventory",
|
Self::Inventory => "inventory",
|
||||||
Self::User => "user",
|
Self::User => "user",
|
||||||
Self::Trips => "trips",
|
Self::Trips => "trips",
|
||||||
|
Self::Todo => "todo",
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -167,6 +169,7 @@ macro_rules! query_all {
|
|||||||
( $class:expr, $pool:expr, $struct_row:path, $struct_into:path, $query:expr, $( $args:tt )* ) => {
|
( $class:expr, $pool:expr, $struct_row:path, $struct_into:path, $query:expr, $( $args:tt )* ) => {
|
||||||
{
|
{
|
||||||
use tracing::Instrument as _;
|
use tracing::Instrument as _;
|
||||||
|
use futures::TryStreamExt as _;
|
||||||
async {
|
async {
|
||||||
$crate::sqlite::sqlx_query($class, $query, &[]);
|
$crate::sqlite::sqlx_query($class, $query, &[]);
|
||||||
let result: Result<Vec<$struct_into>, Error> = sqlx::query_as!(
|
let result: Result<Vec<$struct_into>, Error> = sqlx::query_as!(
|
||||||
@@ -293,6 +296,7 @@ macro_rules! execute_returning {
|
|||||||
( $class:expr, $pool:expr, $query:expr, $t:path, $fn:expr, $( $args:tt )*) => {
|
( $class:expr, $pool:expr, $query:expr, $t:path, $fn:expr, $( $args:tt )*) => {
|
||||||
{
|
{
|
||||||
use tracing::Instrument as _;
|
use tracing::Instrument as _;
|
||||||
|
use futures::TryFutureExt as _;
|
||||||
async {
|
async {
|
||||||
$crate::sqlite::sqlx_query($class, $query, &[]);
|
$crate::sqlite::sqlx_query($class, $query, &[]);
|
||||||
let result: Result<$t, Error> = sqlx::query!(
|
let result: Result<$t, Error> = sqlx::query!(
|
||||||
@@ -317,6 +321,7 @@ macro_rules! execute_returning_uuid {
|
|||||||
( $class:expr, $pool:expr, $query:expr, $( $args:tt )*) => {
|
( $class:expr, $pool:expr, $query:expr, $( $args:tt )*) => {
|
||||||
{
|
{
|
||||||
use tracing::Instrument as _;
|
use tracing::Instrument as _;
|
||||||
|
use futures::TryFutureExt as _;
|
||||||
async {
|
async {
|
||||||
$crate::sqlite::sqlx_query($class, $query, &[]);
|
$crate::sqlite::sqlx_query($class, $query, &[]);
|
||||||
let result: Result<Uuid, Error> = sqlx::query!(
|
let result: Result<Uuid, Error> = sqlx::query!(
|
||||||
|
|||||||
@@ -370,6 +370,7 @@ impl Trip {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
(TripInfo::build(trip_edit_attribute, trip))
|
(TripInfo::build(trip_edit_attribute, trip))
|
||||||
|
(TripTodoList::build(trip))
|
||||||
(TripComment::build(trip))
|
(TripComment::build(trip))
|
||||||
(TripItems::build(active_category, trip))
|
(TripItems::build(active_category, trip))
|
||||||
}
|
}
|
||||||
@@ -794,6 +795,113 @@ impl TripInfo {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct TripTodo;
|
||||||
|
|
||||||
|
impl TripTodo {
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub fn build(trip_id: &Uuid, todo: &models::trips::todos::Todo) -> Markup {
|
||||||
|
let done = todo.is_done();
|
||||||
|
html!(
|
||||||
|
li
|
||||||
|
."flex"
|
||||||
|
."flex-row"
|
||||||
|
."justify-start"
|
||||||
|
."items-stretch"
|
||||||
|
."bg-green-50"[done]
|
||||||
|
."bg-red-50"[!done]
|
||||||
|
."hover:bg-white"[!done]
|
||||||
|
."h-full"
|
||||||
|
{
|
||||||
|
@if done {
|
||||||
|
a
|
||||||
|
."flex"
|
||||||
|
."flex-row"
|
||||||
|
."aspect-square"
|
||||||
|
href={
|
||||||
|
"/trips/" (trip_id)
|
||||||
|
"/todo/" (todo.id)
|
||||||
|
"/undone"
|
||||||
|
}
|
||||||
|
hx-post={
|
||||||
|
"/trips/" (trip_id)
|
||||||
|
"/todo/" (todo.id)
|
||||||
|
"/undone"
|
||||||
|
}
|
||||||
|
hx-target="closest li"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
{
|
||||||
|
span
|
||||||
|
."mdi"
|
||||||
|
."m-auto"
|
||||||
|
."text-xl"
|
||||||
|
."mdi-check"
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
} @else {
|
||||||
|
a
|
||||||
|
."flex"
|
||||||
|
."flex-row"
|
||||||
|
."aspect-square"
|
||||||
|
href={
|
||||||
|
"/trips/" (trip_id)
|
||||||
|
"/todo/" (todo.id)
|
||||||
|
"/done"
|
||||||
|
}
|
||||||
|
hx-post={
|
||||||
|
"/trips/" (trip_id)
|
||||||
|
"/todo/" (todo.id)
|
||||||
|
"/done"
|
||||||
|
}
|
||||||
|
hx-target="closest li"
|
||||||
|
hx-swap="outerHTML"
|
||||||
|
{
|
||||||
|
span
|
||||||
|
."mdi"
|
||||||
|
."m-auto"
|
||||||
|
."text-xl"
|
||||||
|
."mdi-checkbox-blank-outline"
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
span
|
||||||
|
."p-2"
|
||||||
|
{
|
||||||
|
(todo.description)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub struct TripTodoList;
|
||||||
|
|
||||||
|
impl TripTodoList {
|
||||||
|
#[tracing::instrument]
|
||||||
|
pub fn build(trip: &models::trips::Trip) -> Markup {
|
||||||
|
let todos = trip.todos();
|
||||||
|
html!(
|
||||||
|
div {
|
||||||
|
h1 ."text-xl" ."mb-5" { "Todos" }
|
||||||
|
|
||||||
|
@if todos.is_empty() {
|
||||||
|
p { "no todos" }
|
||||||
|
|
||||||
|
} @else {
|
||||||
|
|
||||||
|
ul
|
||||||
|
."flex"
|
||||||
|
."flex-col"
|
||||||
|
{
|
||||||
|
@for todo in trip.todos() {
|
||||||
|
(TripTodo::build(&trip.id, &todo))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub struct TripComment;
|
pub struct TripComment;
|
||||||
|
|
||||||
impl TripComment {
|
impl TripComment {
|
||||||
|
|||||||
Reference in New Issue
Block a user