aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJoris2021-10-10 19:27:22 +0200
committerJoris2021-10-10 21:21:34 +0200
commit6e695bf7a0253b4f6d1db78fa4310616d8a1357f (patch)
treea5b9f30b74a6914336993f93504df3b42fccb933
parent8ccd762bfc3d7da2716749d709cf5cc216882a23 (diff)
downloadbudget-6e695bf7a0253b4f6d1db78fa4310616d8a1357f.tar.gz
budget-6e695bf7a0253b4f6d1db78fa4310616d8a1357f.tar.bz2
budget-6e695bf7a0253b4f6d1db78fa4310616d8a1357f.zip
Search by name, cost and user
-rw-r--r--assets/main.css10
-rw-r--r--src/controller/payments.rs11
-rw-r--r--src/db/payments.rs142
-rw-r--r--src/queries.rs28
-rw-r--r--src/templates.rs4
-rw-r--r--templates/category/table.html2
-rw-r--r--templates/payment/create.html3
-rw-r--r--templates/payment/table.html91
-rw-r--r--templates/payment/table/search.html68
-rw-r--r--templates/payment/update.html9
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
) }}"