diff options
author | Joris | 2021-10-10 19:27:22 +0200 |
---|---|---|
committer | Joris | 2021-10-10 21:21:34 +0200 |
commit | 6e695bf7a0253b4f6d1db78fa4310616d8a1357f (patch) | |
tree | a5b9f30b74a6914336993f93504df3b42fccb933 | |
parent | 8ccd762bfc3d7da2716749d709cf5cc216882a23 (diff) |
Search by name, cost and user
-rw-r--r-- | assets/main.css | 10 | ||||
-rw-r--r-- | src/controller/payments.rs | 11 | ||||
-rw-r--r-- | src/db/payments.rs | 142 | ||||
-rw-r--r-- | src/queries.rs | 28 | ||||
-rw-r--r-- | src/templates.rs | 4 | ||||
-rw-r--r-- | templates/category/table.html | 2 | ||||
-rw-r--r-- | templates/payment/create.html | 3 | ||||
-rw-r--r-- | templates/payment/table.html | 91 | ||||
-rw-r--r-- | templates/payment/table/search.html | 68 | ||||
-rw-r--r-- | templates/payment/update.html | 9 |
10 files changed, 227 insertions, 141 deletions
diff --git a/assets/main.css b/assets/main.css index 6a0be98..e69a0b2 100644 --- a/assets/main.css +++ b/assets/main.css @@ -210,6 +210,11 @@ body { text-align: right; } +.g-Table__NoResults { + margin: var(--size-camel) 0 var(--size-lion); + text-align: center; +} + /* Paging */ .g-Paging { @@ -330,11 +335,6 @@ body { /* Payment */ -.g-Payments__NoResults { - margin-top: var(--size-camel); - text-align: center; -} - .g-Payments__Header { display: flex; justify-content: space-between; diff --git a/src/controller/payments.rs b/src/controller/payments.rs index 883f9e1..66ec056 100644 --- a/src/controller/payments.rs +++ b/src/controller/payments.rs @@ -22,6 +22,7 @@ pub async fn table( let payments = db::payments::list_for_table(&wallet.pool, &query, PER_PAGE).await; let max_page = (count.count as f32 / PER_PAGE as f32).ceil() as i64; + let users = db::users::list(&wallet.pool).await; let categories = db::categories::list(&wallet.pool).await; let mut context = Context::new(); @@ -33,6 +34,7 @@ pub async fn table( context.insert("query", &query); context.insert("count", &count.count); context.insert("total_cost", &count.total_cost); + context.insert("users", &users); context.insert("categories", &categories); utils::template( @@ -98,9 +100,11 @@ pub async fn create( let page = (row - 1) / PER_PAGE + 1; let query = queries::Payments { page: Some(page), - search: None, + name: None, + cost: None, frequency: Some(create_payment.frequency), highlight: Some(id), + user: None, category: None, }; utils::redirect(&format!( @@ -176,11 +180,14 @@ pub async fn update( let row = db::payments::get_row(&wallet.pool, id, frequency).await; let page = (row - 1) / PER_PAGE + 1; + // TODO: keep name, cost, user and category when updating a line let query = queries::Payments { page: Some(page), - search: None, + name: None, + cost: None, frequency: Some(frequency), highlight: Some(id), + user: None, category: None, }; utils::redirect(&format!("/{}", queries::payments_url(query))) diff --git a/src/db/payments.rs b/src/db/payments.rs index 3b85dab..25d1cbb 100644 --- a/src/db/payments.rs +++ b/src/db/payments.rs @@ -23,8 +23,6 @@ pub async fn count( pool: &SqlitePool, payment_query: &queries::Payments, ) -> Count { - let search = payment_query.search.clone().unwrap_or("".to_string()); - let query = format!( r#" SELECT @@ -39,18 +37,26 @@ INNER JOIN WHERE payments.deleted_at IS NULL AND payments.frequency = ? - {} - {} + {} {} {} {} "#, - search_query(search.clone()), + name_query(payment_query.name.clone()), + cost_query(payment_query.cost.clone()), + user_query(payment_query.user), category_query(payment_query.category) ); let res = bind_category( - bind_search( - sqlx::query_as::<_, Count>(&query) - .bind(payment_query.frequency.unwrap_or(Frequency::Punctual)), - search, + bind_user( + bind_cost( + bind_name( + sqlx::query_as::<_, Count>(&query).bind( + payment_query.frequency.unwrap_or(Frequency::Punctual), + ), + payment_query.name.clone(), + ), + payment_query.cost.clone(), + ), + payment_query.user, ), payment_query.category, ) @@ -75,7 +81,6 @@ pub async fn list_for_table( per_page: i64, ) -> Vec<payment::Table> { let offset = (payment_query.page.unwrap_or(1) - 1) * per_page; - let search = payment_query.search.clone().unwrap_or("".to_string()); let query = format!( r#" @@ -97,22 +102,30 @@ INNER JOIN WHERE payments.deleted_at IS NULL AND payments.frequency = ? - {} - {} + {} {} {} {} ORDER BY payments.date DESC LIMIT ? OFFSET ? "#, - search_query(search.clone()), + name_query(payment_query.name.clone()), + cost_query(payment_query.cost.clone()), + user_query(payment_query.user), category_query(payment_query.category) ); let res = bind_category( - bind_search( - sqlx::query_as::<_, payment::Table>(&query) - .bind(payment_query.frequency.unwrap_or(Frequency::Punctual)), - search, + bind_user( + bind_cost( + bind_name( + sqlx::query_as::<_, payment::Table>(&query).bind( + payment_query.frequency.unwrap_or(Frequency::Punctual), + ), + payment_query.name.clone(), + ), + payment_query.cost.clone(), + ), + payment_query.user, ), payment_query.category, ) @@ -130,45 +143,78 @@ OFFSET ? } } -fn search_query(search: String) -> String { - let payments_name = utils::format_key_for_search("payments.name"); - let users_name = utils::format_key_for_search("users.name"); - - search - .split_ascii_whitespace() - .map(|_| { - format!( - r#" -AND ( - {} LIKE ? - OR payments.cost LIKE ? - OR {} LIKE ? - OR strftime('%d/%m/%Y', date) LIKE ? -) - "#, - payments_name, users_name - ) - }) - .collect::<Vec<String>>() - .join(" ") +fn name_query(name: Option<String>) -> String { + if name.map_or_else(|| false, |str| !str.is_empty()) { + format!( + "AND {} LIKE ?", + utils::format_key_for_search("payments.name") + ) + } else { + "".to_string() + } +} + +fn bind_name<'a, Row: FromRow<'a, SqliteRow>>( + query: sqlx::query::QueryAs<'a, Sqlite, Row, SqliteArguments<'a>>, + name: Option<String>, +) -> sqlx::query::QueryAs<'a, Sqlite, Row, SqliteArguments<'a>> { + match name { + Some(str) => { + if str.is_empty() { + query + } else { + query.bind(text::format_search(&str)) + } + } + _ => query, + } +} + +fn cost_query(cost: Option<String>) -> String { + if cost.map_or_else(|| false, |str| !str.is_empty()) { + "AND payments.cost = ?".to_string() + } else { + "".to_string() + } +} + +fn bind_cost<'a, Row: FromRow<'a, SqliteRow>>( + query: sqlx::query::QueryAs<'a, Sqlite, Row, SqliteArguments<'a>>, + cost: Option<String>, +) -> sqlx::query::QueryAs<'a, Sqlite, Row, SqliteArguments<'a>> { + match cost { + Some(str) => { + if str.is_empty() { + query + } else { + query.bind(str) + } + } + _ => query, + } } -fn bind_search<'a, Row: FromRow<'a, SqliteRow>>( +fn user_query(user: Option<i64>) -> String { + if user.is_some() { + "AND payments.user_id = ?".to_string() + } else { + "".to_string() + } +} + +fn bind_user<'a, Row: FromRow<'a, SqliteRow>>( query: sqlx::query::QueryAs<'a, Sqlite, Row, SqliteArguments<'a>>, - search: String, + user: Option<i64>, ) -> sqlx::query::QueryAs<'a, Sqlite, Row, SqliteArguments<'a>> { - search.split_ascii_whitespace().fold(query, |q, word| { - let s = format!("%{}%", text::format_search(&word.to_string())); - q.bind(s.clone()) - .bind(s.clone()) - .bind(s.clone()) - .bind(s.clone()) - }) + match user { + Some(id) => query.bind(id), + _ => query, + } } fn category_query(category: Option<i64>) -> String { if category.is_some() { - "AND category_id = ?".to_string() + "AND payments.category_id = ?".to_string() } else { "".to_string() } diff --git a/src/queries.rs b/src/queries.rs index df57bd8..f10c7a1 100644 --- a/src/queries.rs +++ b/src/queries.rs @@ -4,9 +4,11 @@ use serde::{Deserialize, Serialize}; #[derive(Deserialize, Serialize, Clone)] pub struct Payments { pub page: Option<i64>, - pub search: Option<String>, + pub name: Option<String>, + pub cost: Option<String>, pub frequency: Option<Frequency>, pub highlight: Option<i64>, + pub user: Option<i64>, pub category: Option<i64>, } @@ -18,15 +20,6 @@ pub fn payments_url(q: Payments) -> String { Some(p) => params.push(format!("page={}", p)), }; - match q.search { - Some(s) => { - if !s.is_empty() { - params.push(format!("search={}", s)); - } - } - _ => (), - }; - match q.frequency { Some(Frequency::Monthly) => { params.push("frequency=Monthly".to_string()) @@ -39,6 +32,21 @@ pub fn payments_url(q: Payments) -> String { _ => (), }; + match q.name { + Some(str) => params.push(format!("name={}", str)), + _ => (), + }; + + match q.cost { + Some(n) => params.push(format!("cost={}", n)), + _ => (), + }; + + match q.user { + Some(id) => params.push(format!("user={}", id)), + _ => (), + }; + match q.category { Some(id) => params.push(format!("category={}", id)), _ => (), diff --git a/src/templates.rs b/src/templates.rs index 89b0ed8..2c8e72a 100644 --- a/src/templates.rs +++ b/src/templates.rs @@ -33,9 +33,11 @@ pub fn get() -> Tera { fn payments_params(args: &HashMap<String, Value>) -> Result<Value> { let q = json!({ "page": args.get("page"), - "search": args.get("search"), + "name": args.get("name"), + "cost": args.get("cost"), "frequency": args.get("frequency"), "highlight": args.get("highlight"), + "user": args.get("user"), "category": args.get("category"), }); diff --git a/templates/category/table.html b/templates/category/table.html index ad42258..e05c84b 100644 --- a/templates/category/table.html +++ b/templates/category/table.html @@ -9,7 +9,7 @@ <section class="g-Section"> {% if not categories %} - <div class="g-Payments__NoResults"> + <div class="g-Table__NoResults"> Il n’y a aucune catégorie. </div> {% endif %} diff --git a/templates/payment/create.html b/templates/payment/create.html index 8defad3..4fc3245 100644 --- a/templates/payment/create.html +++ b/templates/payment/create.html @@ -12,7 +12,8 @@ class="g-Link g-Media__Large" href="/{{ payments_params( page=query.page, - search=query.search, + name=query.name, + cost=query.cost, frequency=query.frequency ) }}" > diff --git a/templates/payment/table.html b/templates/payment/table.html index da15b22..c187f17 100644 --- a/templates/payment/table.html +++ b/templates/payment/table.html @@ -9,84 +9,34 @@ {% block main %} <aside class="g-Aside"> - <form action="/" method="GET" class="g-Payments__Filters"> - <label class="g-Form__Label"> - Fréquence - <select name="frequency" class="g-Form__Select"> - <option value="Punctual" {% if query.frequency == "Punctual" %} selected {% endif %}> - Ponctuelle - </option> - <option value="Monthly" {% if query.frequency == "Monthly" %} selected {% endif %}> - Mensuelle - </option> - </select> - </label> - - <label class="g-Form__Label"> - Recherche - <input - type="search" - name="search" - class="g-Form__Input" - value="{{ query.search }}" - /> - </label> - - <label class="g-Form__Label"> - Catégorie - <select name="category" class="g-Form__Select"> - <option selected disabled hidden></option> - {% for category in categories %} - <option - value="{{ category.id }}" - style="color: {{ category.color }}" - {% if category.id == query.category %} selected {% endif %} - > - {{ category.name }} - </option> - {% endfor %} - </select> - </label> - - <input type="submit" class="g-Button__Validate" value="Rechercher"> - </form> + {% include "payment/table/search.html" %} </aside> <section class="g-Section"> - {% if not payments %} - <div class="g-Payments__NoResults"> + {% if not payments %} + <div class="g-Table__NoResults"> Aucun paiement ne correspond à votre recherche. </div> - - <a - class="g-Button__Validate g-Payments__New" - href="/payment{{ payments_params( - page=query.page, - search=query.search, - frequency=query.frequency - ) }}" - > - Nouveau - </a> - {% else %} - <div class="g-Paragraph"> {{ count | numeric }} paiement{{ count | pluralize }} comptabilisant {{ total_cost | euros() }}. </div> + {% endif %} - <a - class="g-Paragraph g-Button__Validate g-Payments__New" - href="/payment{{ payments_params( - page=query.page, - search=query.search, - frequency=query.frequency - ) }}" - > - Ajouter un paiement - </a> - + <a + class="g-Paragraph g-Button__Validate" + href="/payment{{ payments_params( + page=query.page, + name=query.name, + cost=query.cost, + frequency=query.frequency + ) }}" + > + Ajouter un paiement + </a> + + {% if payments %} <div class="g-Table"> <div class="g-Table__Row g-Table__Row--Header"> <span class="g-Table__Cell">Nom</span> @@ -102,7 +52,8 @@ class="g-Table__Row {% if query.highlight == payment.id %} g-Table__Row--Highlight {% endif %}" href="/payment/{{ payment.id }}{{ payments_params( page=query.page, - search=query.search, + name=query.name, + cost=query.cost, frequency=query.frequency ) }}" > @@ -131,14 +82,14 @@ {{ paging::paging( url="/" ~ payments_params( - search=query.search, + name=query.name, + cost=query.cost, frequency=query.frequency, category=query.category ), page=page, max_page=max_page ) }} - {% endif %} </section> diff --git a/templates/payment/table/search.html b/templates/payment/table/search.html new file mode 100644 index 0000000..8805cbb --- /dev/null +++ b/templates/payment/table/search.html @@ -0,0 +1,68 @@ +<form action="/" method="GET" class="g-Payments__Filters"> + + <label class="g-Form__Label"> + Fréquence + <select name="frequency" class="g-Form__Select"> + <option value="Punctual" {% if query.frequency == "Punctual" %} selected {% endif %}> + Ponctuelle + </option> + <option value="Monthly" {% if query.frequency == "Monthly" %} selected {% endif %}> + Mensuelle + </option> + </select> + </label> + + <label class="g-Form__Label"> + Nom + <input + type="search" + name="name" + class="g-Form__Input" + value="{{ query.name }}" + /> + </label> + + <label class="g-Form__Label"> + Coût + <input + type="number" + name="cost" + class="g-Form__Input" + value="{{ query.cost }}" + /> + </label> + + <label class="g-Form__Label"> + Personne + <select name="user" class="g-Form__Select"> + <option selected disabled hidden></option> + {% for user in users %} + <option + value="{{ user.id }}" + {% if user.id == query.user %} selected {% endif %} + > + {{ user.name }} + </option> + {% endfor %} + </select> + </label> + + <label class="g-Form__Label"> + Catégorie + <select name="category" class="g-Form__Select"> + <option selected disabled hidden></option> + {% for category in categories %} + <option + value="{{ category.id }}" + style="color: {{ category.color }}" + {% if category.id == query.category %} selected {% endif %} + > + {{ category.name }} + </option> + {% endfor %} + </select> + </label> + + <input type="submit" class="g-Button__Validate" value="Rechercher"> + +</form> diff --git a/templates/payment/update.html b/templates/payment/update.html index 4e244f4..002117e 100644 --- a/templates/payment/update.html +++ b/templates/payment/update.html @@ -12,7 +12,8 @@ class="g-Link g-Media__Large" href="/{{ payments_params( page=query.page, - search=query.search, + name=query.name, + cost=query.cost, frequency=query.frequency ) }}" > @@ -34,7 +35,8 @@ class="g-Form" action="/payment/{{ payment.id }}/update{{ payments_params( page=query.page, - search=query.search, + name=query.name, + cost=query.cost, frequency=query.frequency, highlight=query.highlight ) }}" @@ -126,7 +128,8 @@ class="g-Form" action="/payment/{{ payment.id }}/delete{{ payments_params( page=query.page, - search=query.search, + name=query.name, + cost=query.cost, frequency=query.frequency, highlight=query.highlight ) }}" |