commiting old work
This commit is contained in:
parent
087916a9bc
commit
f6bad75438
8 changed files with 190 additions and 12 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -1,2 +1,3 @@
|
||||||
/target
|
/target
|
||||||
.env
|
.env
|
||||||
|
Session.vim
|
||||||
|
|
|
@ -1,2 +1,3 @@
|
||||||
pub mod models;
|
pub mod models;
|
||||||
pub mod schema;
|
pub mod schema;
|
||||||
|
pub mod report;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
use diesel::prelude::*;
|
use diesel::prelude::*;
|
||||||
|
|
||||||
#[derive(Queryable, Selectable, Debug)]
|
#[derive(Identifiable, Queryable, Selectable, Debug)]
|
||||||
#[diesel(table_name = crate::schema::shifts)]
|
#[diesel(table_name = crate::schema::shifts)]
|
||||||
#[diesel(check_for_backend(diesel::mysql::Mysql))]
|
#[diesel(check_for_backend(diesel::mysql::Mysql))]
|
||||||
pub struct Shift {
|
pub struct Shift {
|
||||||
|
@ -9,7 +9,8 @@ pub struct Shift {
|
||||||
pub end: Option<chrono::NaiveDateTime>,
|
pub end: Option<chrono::NaiveDateTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Queryable, Selectable, Debug)]
|
#[derive(Identifiable, Queryable, Associations, PartialEq, Selectable, Debug)]
|
||||||
|
#[diesel(belongs_to(Shift, foreign_key = shift))]
|
||||||
#[diesel(table_name = crate::schema::drinks)]
|
#[diesel(table_name = crate::schema::drinks)]
|
||||||
#[diesel(check_for_backend(diesel::mysql::Mysql))]
|
#[diesel(check_for_backend(diesel::mysql::Mysql))]
|
||||||
pub struct Drink {
|
pub struct Drink {
|
||||||
|
|
40
src/lib/report.rs
Normal file
40
src/lib/report.rs
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
use crate::models::Drink;
|
||||||
|
use std::collections::HashMap;
|
||||||
|
|
||||||
|
pub struct DrinkReport {
|
||||||
|
pub summary: HashMap<u32, u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl DrinkReport {
|
||||||
|
pub fn into_sorted(self) -> impl Iterator<Item = (u32, u32)> {
|
||||||
|
let mut array: Vec<(u32, u32)> = self.summary.into_iter().collect();
|
||||||
|
|
||||||
|
array.sort_by(|(a, _), (b, _)| a.cmp(b));
|
||||||
|
|
||||||
|
array.into_iter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait GenerateDrinkReport {
|
||||||
|
fn generate_report(&self) -> DrinkReport;
|
||||||
|
}
|
||||||
|
|
||||||
|
impl GenerateDrinkReport for Vec<Drink> {
|
||||||
|
fn generate_report(&self) -> DrinkReport {
|
||||||
|
let mut summary: HashMap<u32, u32> = HashMap::new();
|
||||||
|
|
||||||
|
// init with all default drink prices
|
||||||
|
for price in [2u32, 3u32, 5u32, 8u32, 15u32].into_iter() {
|
||||||
|
summary.insert(price, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
for drink in self {
|
||||||
|
summary
|
||||||
|
.entry(drink.price)
|
||||||
|
.and_modify(|v| *v += drink.quantity)
|
||||||
|
.or_insert(drink.quantity);
|
||||||
|
}
|
||||||
|
|
||||||
|
DrinkReport { summary }
|
||||||
|
}
|
||||||
|
}
|
52
src/main.rs
52
src/main.rs
|
@ -1,22 +1,37 @@
|
||||||
mod api;
|
mod api;
|
||||||
mod axum_ructe;
|
mod axum_ructe;
|
||||||
|
|
||||||
use axum::extract::Path;
|
|
||||||
use axum_ructe::render;
|
use axum_ructe::render;
|
||||||
|
|
||||||
use axum::{extract::State, response::IntoResponse, routing::get, Router};
|
use cm_lib::{
|
||||||
use cm_lib::models::Shift;
|
models::{Drink, Shift},
|
||||||
use diesel::result::OptionalExtension;
|
report::GenerateDrinkReport,
|
||||||
use diesel::{ExpressionMethods, QueryDsl, SelectableHelper};
|
schema::shifts,
|
||||||
|
};
|
||||||
|
|
||||||
|
use axum::{
|
||||||
|
extract::{Path, State},
|
||||||
|
response::IntoResponse,
|
||||||
|
routing::get,
|
||||||
|
Router,
|
||||||
|
};
|
||||||
|
|
||||||
|
use diesel::{
|
||||||
|
result::OptionalExtension, BelongingToDsl, ExpressionMethods, QueryDsl, SelectableHelper,
|
||||||
|
};
|
||||||
|
|
||||||
use diesel_async::{
|
use diesel_async::{
|
||||||
pooled_connection::{deadpool::Pool, AsyncDieselConnectionManager},
|
pooled_connection::{deadpool::Pool, AsyncDieselConnectionManager},
|
||||||
AsyncMysqlConnection, RunQueryDsl,
|
AsyncMysqlConnection, RunQueryDsl,
|
||||||
};
|
};
|
||||||
|
|
||||||
use dotenvy::dotenv;
|
use dotenvy::dotenv;
|
||||||
use std::net::SocketAddr;
|
use std::net::SocketAddr;
|
||||||
use tower_http::services::{ServeDir, ServeFile};
|
use tower_http::services::{ServeDir, ServeFile};
|
||||||
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
use tracing_subscriber::{layer::SubscriberExt, util::SubscriberInitExt};
|
||||||
|
|
||||||
|
use crate::templates::shift_reports_html;
|
||||||
|
|
||||||
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
|
include!(concat!(env!("OUT_DIR"), "/templates.rs"));
|
||||||
|
|
||||||
async fn establish_connection() -> Pool<AsyncMysqlConnection> {
|
async fn establish_connection() -> Pool<AsyncMysqlConnection> {
|
||||||
|
@ -63,7 +78,9 @@ async fn main() {
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.nest("/api", api::router())
|
.nest("/api", api::router())
|
||||||
.route("/", get(root))
|
.route("/", get(root))
|
||||||
|
.route("/shift_reports", get(shift_reports))
|
||||||
.route("/shifts/:id/drinks", get(drinks))
|
.route("/shifts/:id/drinks", get(drinks))
|
||||||
|
.route("/shifts/:id/report", get(shift_report))
|
||||||
.fallback_service(fallback_handler)
|
.fallback_service(fallback_handler)
|
||||||
.with_state(state);
|
.with_state(state);
|
||||||
|
|
||||||
|
@ -100,3 +117,28 @@ async fn root(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
async fn drinks(Path(id): Path<u32>) -> impl IntoResponse {
|
async fn drinks(Path(id): Path<u32>) -> impl IntoResponse {
|
||||||
render!(templates::drinks_html, id)
|
render!(templates::drinks_html, id)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn shift_report(State(state): State<AppState>, Path(id): Path<u32>) -> impl IntoResponse {
|
||||||
|
let mut conn = state.connection.get().await.unwrap();
|
||||||
|
let shift: Shift = shifts::table.find(id).first(&mut conn).await.unwrap();
|
||||||
|
let drinks: Vec<Drink> = Drink::belonging_to(&shift).load(&mut conn).await.unwrap();
|
||||||
|
|
||||||
|
render!(
|
||||||
|
crate::templates::shift_report_html,
|
||||||
|
drinks.generate_report()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn shift_reports(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
|
let mut conn = state.connection.get().await.unwrap();
|
||||||
|
|
||||||
|
let mut shifts: Vec<Shift> = shifts::table
|
||||||
|
.select(Shift::as_select())
|
||||||
|
.load(&mut conn)
|
||||||
|
.await
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
shifts.sort_by(|a, b| b.start.cmp(&a.start));
|
||||||
|
|
||||||
|
render!(shift_reports_html, shifts)
|
||||||
|
}
|
||||||
|
|
|
@ -17,6 +17,11 @@
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
|
<div hx-boost="true">
|
||||||
|
<a href="/shift_reports">
|
||||||
|
<button>Shift Reports</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
41
templates/shift_report.rs.html
Normal file
41
templates/shift_report.rs.html
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
@use super::base_html;
|
||||||
|
@use cm_lib::report::DrinkReport;
|
||||||
|
|
||||||
|
@(drinks: DrinkReport)
|
||||||
|
|
||||||
|
|
||||||
|
@:base_html({
|
||||||
|
|
||||||
|
<div style="display: flex; gap: 2rem;">
|
||||||
|
<div hx-boost="true">
|
||||||
|
<a href="/">
|
||||||
|
<button>Return to Main Page</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
<div hx-boost="true">
|
||||||
|
<a href="/shift_reports">
|
||||||
|
<button>Return to Shift Reports</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Price</th>
|
||||||
|
<th>Quantity</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for (price, quantity) in drinks.into_sorted() {
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@price</td>
|
||||||
|
<td>@quantity</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
})
|
47
templates/shift_reports.rs.html
Normal file
47
templates/shift_reports.rs.html
Normal file
|
@ -0,0 +1,47 @@
|
||||||
|
@use super::base_html;
|
||||||
|
@use cm_lib::models::Shift;
|
||||||
|
|
||||||
|
@(shifts: Vec<Shift>)
|
||||||
|
|
||||||
|
@:base_html({
|
||||||
|
|
||||||
|
|
||||||
|
<div hx-boost="true">
|
||||||
|
<a href="/">
|
||||||
|
<button>Return to Main Page</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Shift ID</th>
|
||||||
|
<th>Shift Start</th>
|
||||||
|
<th>Shift End</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@for shift in shifts {
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>@shift.id</td>
|
||||||
|
<td>@shift.start.format("%Y-%m-%d %H:%M:%S")</td>
|
||||||
|
<td>
|
||||||
|
@if let Some(end) = shift.end {
|
||||||
|
@end.format("%Y-%m-%d %H:%M:%S")
|
||||||
|
} else {
|
||||||
|
Currently Open
|
||||||
|
}
|
||||||
|
</td>
|
||||||
|
<td hx-boost="true">
|
||||||
|
<a href="/shifts/@shift.id/report">
|
||||||
|
<button>View Shift</button>
|
||||||
|
</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
})
|
Loading…
Reference in a new issue