diff --git a/.cargo/config.toml b/.cargo/config.toml
deleted file mode 100644
index e51ab4f..0000000
--- a/.cargo/config.toml
+++ /dev/null
@@ -1,2 +0,0 @@
-[env]
-DATABASE_URL = "postgres://postgres:postgres@localhost:19522/postgres"
diff --git a/.gitignore b/.gitignore
index 68d3068..c907f58 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,6 +1,8 @@
/target
.shuttle-storage
Secrets*.toml
+.env
+.cargo
# ignore virtualenv
/bin
diff --git a/.sqlx/query-59183ce536917b910db8f480b490771a7440e56fa6f09a36a02f3fa79516f95b.json b/.sqlx/query-59183ce536917b910db8f480b490771a7440e56fa6f09a36a02f3fa79516f95b.json
new file mode 100644
index 0000000..4c2a909
--- /dev/null
+++ b/.sqlx/query-59183ce536917b910db8f480b490771a7440e56fa6f09a36a02f3fa79516f95b.json
@@ -0,0 +1,67 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT id, description, price, expense_type as \"expense_type: ExpenseType\", is_essencial, date\n FROM expenses WHERE date BETWEEN $1 AND $2 ORDER BY date ASC",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 1,
+ "name": "description",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 2,
+ "name": "price",
+ "type_info": "Float4"
+ },
+ {
+ "ordinal": 3,
+ "name": "expense_type: ExpenseType",
+ "type_info": {
+ "Custom": {
+ "name": "expense_type",
+ "kind": {
+ "Enum": [
+ "food",
+ "transport",
+ "health",
+ "education",
+ "entertainment",
+ "other"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "ordinal": 4,
+ "name": "is_essencial",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 5,
+ "name": "date",
+ "type_info": "Date"
+ }
+ ],
+ "parameters": {
+ "Left": [
+ "Date",
+ "Date"
+ ]
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ ]
+ },
+ "hash": "59183ce536917b910db8f480b490771a7440e56fa6f09a36a02f3fa79516f95b"
+}
diff --git a/.sqlx/query-c6a4023380d34cfa6892168ae56ef7c3951792638f4d6779394922fa18516e61.json b/.sqlx/query-c6a4023380d34cfa6892168ae56ef7c3951792638f4d6779394922fa18516e61.json
new file mode 100644
index 0000000..5976a57
--- /dev/null
+++ b/.sqlx/query-c6a4023380d34cfa6892168ae56ef7c3951792638f4d6779394922fa18516e61.json
@@ -0,0 +1,64 @@
+{
+ "db_name": "PostgreSQL",
+ "query": "SELECT id, description, price, expense_type as \"expense_type: ExpenseType\", is_essencial, date\n FROM expenses ORDER BY date ASC",
+ "describe": {
+ "columns": [
+ {
+ "ordinal": 0,
+ "name": "id",
+ "type_info": "Int4"
+ },
+ {
+ "ordinal": 1,
+ "name": "description",
+ "type_info": "Varchar"
+ },
+ {
+ "ordinal": 2,
+ "name": "price",
+ "type_info": "Float4"
+ },
+ {
+ "ordinal": 3,
+ "name": "expense_type: ExpenseType",
+ "type_info": {
+ "Custom": {
+ "name": "expense_type",
+ "kind": {
+ "Enum": [
+ "food",
+ "transport",
+ "health",
+ "education",
+ "entertainment",
+ "other"
+ ]
+ }
+ }
+ }
+ },
+ {
+ "ordinal": 4,
+ "name": "is_essencial",
+ "type_info": "Bool"
+ },
+ {
+ "ordinal": 5,
+ "name": "date",
+ "type_info": "Date"
+ }
+ ],
+ "parameters": {
+ "Left": []
+ },
+ "nullable": [
+ false,
+ false,
+ false,
+ false,
+ false,
+ false
+ ]
+ },
+ "hash": "c6a4023380d34cfa6892168ae56ef7c3951792638f4d6779394922fa18516e61"
+}
diff --git a/Cargo.lock b/Cargo.lock
index 2fb7b5f..792a8bf 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -2502,6 +2502,7 @@ dependencies = [
"atoi",
"byteorder",
"bytes",
+ "chrono",
"crc",
"crossbeam-queue",
"dotenvy",
@@ -2587,6 +2588,7 @@ dependencies = [
"bitflags 2.4.1",
"byteorder",
"bytes",
+ "chrono",
"crc",
"digest",
"dotenvy",
@@ -2628,6 +2630,7 @@ dependencies = [
"base64",
"bitflags 2.4.1",
"byteorder",
+ "chrono",
"crc",
"dotenvy",
"etcetera",
@@ -2664,6 +2667,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "210976b7d948c7ba9fced8ca835b11cbb2d677c59c79de41ac0d397e14547490"
dependencies = [
"atoi",
+ "chrono",
"flume",
"futures-channel",
"futures-core",
diff --git a/Cargo.toml b/Cargo.toml
index 57b55f9..77d84cf 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -12,7 +12,7 @@ serde = { version = "1.0.193", features = ["derive"] }
shuttle-axum = { version = "0.35.1", default-features = false, features = ["axum-0-7"] }
shuttle-runtime = { version = "0.35.1", default-features = false, features = ["colored"] }
shuttle-shared-db = { version = "0.35.1", features = ["postgres"] }
-sqlx = { version = "0.7.3", features = ["runtime-tokio-rustls", "postgres"] }
+sqlx = { version = "0.7.3", features = ["runtime-tokio-rustls", "postgres", "chrono"] }
strum = { version = "0.25.0", features = ["strum_macros", "derive"] }
tokio = { version = "1.35.1", features = ["full"] }
tower = "0.4.13"
diff --git a/migrations/1_schema.sql b/migrations/1_schema.sql
index 7753487..78a1471 100644
--- a/migrations/1_schema.sql
+++ b/migrations/1_schema.sql
@@ -11,5 +11,6 @@ CREATE TABLE IF NOT EXISTS expenses (
description varchar(255) NOT NULL,
price real NOT NULL,
expense_type expense_type NOT NULL,
- is_essencial boolean NOT NULL
+ is_essencial boolean NOT NULL,
+ date date NOT NULL DEFAULT CURRENT_DATE
);
diff --git a/src/constant.rs b/src/constant.rs
new file mode 100644
index 0000000..c33358e
--- /dev/null
+++ b/src/constant.rs
@@ -0,0 +1,60 @@
+macro_rules! TABLE_ROW {
+ () => {
+ "
+ {} |
+ {} |
+ {} |
+ {} |
+ {} |
+
+
+ |
+
"
+ };
+}
+
+macro_rules! EDITABLE_TABLE_ROW {
+ () => {
+ "
+ |
+ |
+ |
+ |
+ |
+
+
+
+ |
+
"
+ };
+}
+
+pub(crate) use EDITABLE_TABLE_ROW;
+pub(crate) use TABLE_ROW;
diff --git a/src/data.rs b/src/data.rs
index 0f9729e..f5def97 100644
--- a/src/data.rs
+++ b/src/data.rs
@@ -1,9 +1,10 @@
-use std::fmt::Display;
+use std::{fmt::Display, str::FromStr};
use chrono::Month;
+use serde::Deserialize;
use strum::EnumIter;
-#[derive(EnumIter, Debug, PartialEq, Clone)]
+#[derive(EnumIter, Debug, PartialEq, Clone, Deserialize)]
pub enum Months {
January,
February,
@@ -19,6 +20,28 @@ pub enum Months {
December,
}
+impl FromStr for Months {
+ type Err = String;
+
+ fn from_str(s: &str) -> Result {
+ match s {
+ "January" => Ok(Months::January),
+ "February" => Ok(Months::February),
+ "March" => Ok(Months::March),
+ "April" => Ok(Months::April),
+ "May" => Ok(Months::May),
+ "June" => Ok(Months::June),
+ "July" => Ok(Months::July),
+ "August" => Ok(Months::August),
+ "September" => Ok(Months::September),
+ "October" => Ok(Months::October),
+ "November" => Ok(Months::November),
+ "December" => Ok(Months::December),
+ _ => Err(format!("{} is not a valid month", s)),
+ }
+ }
+}
+
impl Display for Months {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
diff --git a/src/hypermedia_router.rs b/src/hypermedia_router.rs
index 7664050..4c6aaee 100644
--- a/src/hypermedia_router.rs
+++ b/src/hypermedia_router.rs
@@ -1,15 +1,17 @@
+use crate::{
+ constant::{EDITABLE_TABLE_ROW, TABLE_ROW},
+ schema::{Expense, ExpenseType, GetExpense},
+ util::{get_first_day_from_month, get_last_day_from_month},
+ AppState, ExpensesTemplate,
+};
use std::sync::Arc;
use askama_axum::IntoResponse;
-use axum::extract::Path;
-use axum::routing::get;
-use axum::Router;
-use axum::{extract::State, response::Html};
-
-use crate::{
- schema::Expense,
- util::{EDITABLE_TABLE_ROW, TABLE_ROW},
- AppState, ExpensesTemplate,
+use axum::{
+ extract::{Path, Query, State},
+ response::Html,
+ routing::get,
+ Router,
};
pub fn hypermedia_router() -> Router> {
@@ -26,15 +28,34 @@ pub async fn expenses_index() -> impl IntoResponse {
}
}
-pub async fn get_expenses(State(shared_state): State>) -> impl IntoResponse {
- // let expenses: Vec = sqlx::query_as!(Expense, "SELECT * FROM expenses")
- // .fetch_all(&shared_state.pool)
- // .await
- // .unwrap();
- let expenses: Vec = sqlx::query_as("SELECT * FROM expenses")
- .fetch_all(&shared_state.pool)
- .await
- .unwrap();
+pub async fn get_expenses(
+ State(shared_state): State>,
+ Query(get_expense_input): Query,
+) -> impl IntoResponse {
+ let expenses: Vec = match get_expense_input.month {
+ Some(month) => {
+ sqlx::query_as!(
+ Expense,
+ r#"SELECT id, description, price, expense_type as "expense_type: ExpenseType", is_essencial, date
+ FROM expenses WHERE date BETWEEN $1 AND $2 ORDER BY date ASC"#,
+ get_first_day_from_month(month.clone() as u32 + 1),
+ get_last_day_from_month(month as u32 + 1)
+ )
+ .fetch_all(&shared_state.pool)
+ .await
+ .unwrap()
+ }
+ None => {
+ sqlx::query_as!(
+ Expense,
+ r#"SELECT id, description, price, expense_type as "expense_type: ExpenseType", is_essencial, date
+ FROM expenses ORDER BY date ASC"#
+ )
+ .fetch_all(&shared_state.pool)
+ .await
+ .unwrap()
+ }
+ };
Html(
expenses
@@ -42,6 +63,7 @@ pub async fn get_expenses(State(shared_state): State>) -> impl Int
.map(|expense| {
format!(
TABLE_ROW!(),
+ expense.date,
expense.description,
expense.price,
expense.expense_type,
@@ -67,6 +89,7 @@ pub async fn edit_expense(
Html(format!(
EDITABLE_TABLE_ROW!(),
expense.id,
+ expense.date,
expense.description,
expense.price,
expense.is_essencial,
@@ -87,7 +110,12 @@ pub async fn get_expense(
Html(format!(
TABLE_ROW!(),
- expense.description, expense.price, expense.expense_type, expense.is_essencial, expense.id
+ expense.date,
+ expense.description,
+ expense.price,
+ expense.expense_type,
+ expense.is_essencial,
+ expense.id
))
}
@@ -103,6 +131,11 @@ pub async fn update_expense(
Html(format!(
TABLE_ROW!(),
- expense.description, expense.price, expense.expense_type, expense.is_essencial, expense.id
+ expense.date,
+ expense.description,
+ expense.price,
+ expense.expense_type,
+ expense.is_essencial,
+ expense.id
))
}
diff --git a/src/main.rs b/src/main.rs
index 29321a5..81f568e 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -1,3 +1,4 @@
+mod constant;
mod data;
mod data_router;
mod hypermedia_router;
diff --git a/src/schema.rs b/src/schema.rs
index 9015436..07779fe 100644
--- a/src/schema.rs
+++ b/src/schema.rs
@@ -1,5 +1,7 @@
+use crate::Months;
use std::{fmt::Display, str::FromStr};
+use chrono::NaiveDate;
use serde::{Deserialize, Serialize};
use sqlx::FromRow;
use strum::EnumIter;
@@ -51,4 +53,23 @@ pub struct Expense {
pub price: f32,
pub expense_type: ExpenseType,
pub is_essencial: bool,
+ pub date: NaiveDate,
+}
+
+#[derive(Deserialize, Debug)]
+pub struct GetExpense {
+ #[serde(deserialize_with = "empty_string_to_none")]
+ pub month: Option,
+}
+
+fn empty_string_to_none<'de, D>(deserializer: D) -> Result{{ month }}
{% endif %}
{% endfor %}
+ Entire year
@@ -25,6 +26,7 @@ Carol's Expenses
+ Date |
Description |
Price |
Category |