feat: drinks
This commit is contained in:
parent
1c35f2f81f
commit
20b214e262
13 changed files with 157 additions and 7 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -125,6 +125,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"async-trait",
|
"async-trait",
|
||||||
"axum-core",
|
"axum-core",
|
||||||
|
"axum-macros",
|
||||||
"bitflags 1.3.2",
|
"bitflags 1.3.2",
|
||||||
"bytes",
|
"bytes",
|
||||||
"futures-util",
|
"futures-util",
|
||||||
|
@ -166,6 +167,18 @@ dependencies = [
|
||||||
"tower-service",
|
"tower-service",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "axum-macros"
|
||||||
|
version = "0.3.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cdca6a10ecad987bda04e95606ef85a5417dcaac1a78455242d72e031e2b6b62"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn 2.0.37",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "backtrace"
|
name = "backtrace"
|
||||||
version = "0.3.69"
|
version = "0.3.69"
|
||||||
|
|
|
@ -10,7 +10,7 @@ path = "src/lib/mod.rs"
|
||||||
|
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
axum = "0.6.20"
|
axum = { version = "0.6.20", features = ["macros"] }
|
||||||
serde = { version = "1.0", features = ["derive"] }
|
serde = { version = "1.0", features = ["derive"] }
|
||||||
serde_json = "1.0.68"
|
serde_json = "1.0.68"
|
||||||
tokio = { version = "1.32.0", features = ["full"] }
|
tokio = { version = "1.32.0", features = ["full"] }
|
||||||
|
|
2
migrations/2023-09-30-201752_create_drinks/down.sql
Normal file
2
migrations/2023-09-30-201752_create_drinks/down.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
-- This file should undo anything in `up.sql`
|
||||||
|
DROP TABLE drinks
|
10
migrations/2023-09-30-201752_create_drinks/up.sql
Normal file
10
migrations/2023-09-30-201752_create_drinks/up.sql
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
-- Your SQL goes here
|
||||||
|
CREATE TABLE drinks
|
||||||
|
(
|
||||||
|
id INT UNSIGNED PRIMARY KEY AUTO_INCREMENT,
|
||||||
|
price INT UNSIGNED NOT NULL,
|
||||||
|
quantity INT UNSIGNED NOT NULL,
|
||||||
|
shift INT UNSIGNED NOT NULL,
|
||||||
|
time DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL,
|
||||||
|
FOREIGN KEY (shift) REFERENCES shifts(id) ON DELETE CASCADE
|
||||||
|
)
|
44
src/api.rs
44
src/api.rs
|
@ -1,15 +1,18 @@
|
||||||
use axum::extract::Path;
|
use axum::extract::Path;
|
||||||
|
use axum::Form;
|
||||||
use axum::{extract::State, response::IntoResponse, routing::post};
|
use axum::{extract::State, response::IntoResponse, routing::post};
|
||||||
use cm_lib::models::Shift;
|
use cm_lib::models::{NewDrink, Shift};
|
||||||
use cm_lib::schema::shifts;
|
use cm_lib::schema::shifts;
|
||||||
use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper};
|
use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper};
|
||||||
use diesel_async::{scoped_futures::ScopedFutureExt, AsyncConnection, RunQueryDsl};
|
use diesel_async::{scoped_futures::ScopedFutureExt, AsyncConnection, RunQueryDsl};
|
||||||
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::axum_ructe::render;
|
use crate::axum_ructe::render;
|
||||||
use crate::AppState;
|
use crate::AppState;
|
||||||
|
|
||||||
pub(crate) fn router() -> axum::Router<AppState> {
|
pub(crate) fn router() -> axum::Router<AppState> {
|
||||||
axum::Router::new()
|
axum::Router::new()
|
||||||
|
.route("/drinks", post(add_drink))
|
||||||
.route("/shifts/open", post(open_shift))
|
.route("/shifts/open", post(open_shift))
|
||||||
.route("/shifts/:id/close", post(close_shift))
|
.route("/shifts/:id/close", post(close_shift))
|
||||||
}
|
}
|
||||||
|
@ -52,3 +55,42 @@ async fn close_shift(State(state): State<AppState>, Path(id): Path<u32>) -> impl
|
||||||
|
|
||||||
render!(crate::templates::home_html, None)
|
render!(crate::templates::home_html, None)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Deserialize, Debug)]
|
||||||
|
struct DrinkForm {
|
||||||
|
shift_id: u32,
|
||||||
|
price: u32,
|
||||||
|
quantity: u32,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Into<NewDrink> for DrinkForm {
|
||||||
|
fn into(self) -> NewDrink {
|
||||||
|
NewDrink {
|
||||||
|
price: self.price,
|
||||||
|
quantity: self.quantity,
|
||||||
|
shift: self.shift_id,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn add_drink(
|
||||||
|
State(state): State<AppState>,
|
||||||
|
Form(form): Form<DrinkForm>,
|
||||||
|
) -> impl IntoResponse {
|
||||||
|
let mut conn = state.connection.get().await.unwrap();
|
||||||
|
|
||||||
|
tracing::debug!("{form:?}");
|
||||||
|
|
||||||
|
async {
|
||||||
|
use cm_lib::schema::drinks::dsl::*;
|
||||||
|
|
||||||
|
diesel::insert_into(drinks)
|
||||||
|
.values::<NewDrink>(form.into())
|
||||||
|
.execute(&mut conn)
|
||||||
|
.await
|
||||||
|
.unwrap()
|
||||||
|
}
|
||||||
|
.await;
|
||||||
|
|
||||||
|
axum::response::Redirect::to("/")
|
||||||
|
}
|
||||||
|
|
|
@ -8,3 +8,23 @@ pub struct Shift {
|
||||||
pub start: chrono::NaiveDateTime,
|
pub start: chrono::NaiveDateTime,
|
||||||
pub end: Option<chrono::NaiveDateTime>,
|
pub end: Option<chrono::NaiveDateTime>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Queryable, Selectable, Debug)]
|
||||||
|
#[diesel(table_name = crate::schema::drinks)]
|
||||||
|
#[diesel(check_for_backend(diesel::mysql::Mysql))]
|
||||||
|
pub struct Drink {
|
||||||
|
pub id: u32,
|
||||||
|
pub price: u32,
|
||||||
|
pub quantity: u32,
|
||||||
|
pub shift: u32,
|
||||||
|
pub time: chrono::NaiveDateTime,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Insertable, Debug)]
|
||||||
|
#[diesel(table_name = crate::schema::drinks)]
|
||||||
|
#[diesel(check_for_backend(diesel::mysql::Mysql))]
|
||||||
|
pub struct NewDrink {
|
||||||
|
pub price: u32,
|
||||||
|
pub quantity: u32,
|
||||||
|
pub shift: u32,
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,15 @@
|
||||||
// @generated automatically by Diesel CLI.
|
// @generated automatically by Diesel CLI.
|
||||||
|
|
||||||
|
diesel::table! {
|
||||||
|
drinks (id) {
|
||||||
|
id -> Unsigned<Integer>,
|
||||||
|
price -> Unsigned<Integer>,
|
||||||
|
quantity -> Unsigned<Integer>,
|
||||||
|
shift -> Unsigned<Integer>,
|
||||||
|
time -> Datetime,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
diesel::table! {
|
diesel::table! {
|
||||||
shifts (id) {
|
shifts (id) {
|
||||||
id -> Unsigned<Integer>,
|
id -> Unsigned<Integer>,
|
||||||
|
@ -7,3 +17,10 @@ diesel::table! {
|
||||||
end -> Nullable<Datetime>,
|
end -> Nullable<Datetime>,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
diesel::joinable!(drinks -> shifts (shift));
|
||||||
|
|
||||||
|
diesel::allow_tables_to_appear_in_same_query!(
|
||||||
|
drinks,
|
||||||
|
shifts,
|
||||||
|
);
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
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 axum::{extract::State, response::IntoResponse, routing::get, Router};
|
||||||
|
@ -59,6 +60,7 @@ 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("/shifts/:id/drinks", get(drinks))
|
||||||
.with_state(state);
|
.with_state(state);
|
||||||
|
|
||||||
// run our app with hyper
|
// run our app with hyper
|
||||||
|
@ -90,3 +92,7 @@ async fn root(State(state): State<AppState>) -> impl IntoResponse {
|
||||||
|
|
||||||
render!(templates::home_html, open_shift)
|
render!(templates::home_html, open_shift)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn drinks(Path(id): Path<u32>) -> impl IntoResponse {
|
||||||
|
render!(templates::drinks_html, id)
|
||||||
|
}
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
@use cm_lib::models::Shift;
|
@use cm_lib::models::Shift;
|
||||||
|
|
||||||
@(open_shift: Shift)
|
@(open_shift: &Shift)
|
||||||
|
|
||||||
<button hx-post="/api/shifts/@open_shift.id/close" hx-swap="outerHTML" type="button">
|
<button hx-post="/api/shifts/@open_shift.id/close" hx-swap="outerHTML" hx-target="body" type="button">
|
||||||
Close Shift
|
Close Shift
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
@()
|
@()
|
||||||
|
|
||||||
<button hx-post="/api/shifts/open" hx-swap="outerHTML" type="button">
|
<button hx-post="/api/shifts/open" hx-swap="outerHTML" hx-target="body" type="button">
|
||||||
Open Shift
|
Open Shift
|
||||||
</button>
|
</button>
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
@use super::open_shift_button_html;
|
@use super::open_shift_button_html;
|
||||||
@use cm_lib::models::Shift;
|
@use cm_lib::models::Shift;
|
||||||
|
|
||||||
@(open_shift: Option<Shift>)
|
@(open_shift: Option<&Shift>)
|
||||||
|
|
||||||
@if open_shift.is_none() {
|
@if open_shift.is_none() {
|
||||||
@:open_shift_button_html()
|
@:open_shift_button_html()
|
||||||
|
|
33
templates/drinks.rs.html
Normal file
33
templates/drinks.rs.html
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
@use super::base_html;
|
||||||
|
|
||||||
|
@(shift_id: u32)
|
||||||
|
|
||||||
|
@:base_html({
|
||||||
|
|
||||||
|
<form action="/api/drinks" method="post">
|
||||||
|
<input required type="radio" id="two" name="price" value="2" />
|
||||||
|
<label for="two">$2</label>
|
||||||
|
|
||||||
|
<input type="radio" id="three" name="price" value="3" />
|
||||||
|
<label for="three">$3</label>
|
||||||
|
|
||||||
|
<input type="radio" id="five" name="price" value="5" />
|
||||||
|
<label for="five">$5</label>
|
||||||
|
|
||||||
|
<input type="radio" id="eight" name="price" value="8" />
|
||||||
|
<label for="eight">$8</label>
|
||||||
|
|
||||||
|
<input type="radio" id="fifteen" name="price" value="15" />
|
||||||
|
<label for="fifteen">$15</label>
|
||||||
|
|
||||||
|
<label for="quantity">Quantity: </label>
|
||||||
|
<input type="number" id="quantity" name="quantity" value="1" />
|
||||||
|
|
||||||
|
<input type="hidden" id="shift_id" name="shift_id" value="@shift_id" />
|
||||||
|
|
||||||
|
<button type="submit">
|
||||||
|
submit
|
||||||
|
</button>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
})
|
|
@ -8,8 +8,15 @@
|
||||||
|
|
||||||
<main>
|
<main>
|
||||||
<div>
|
<div>
|
||||||
@:shift_button_html(open_shift)
|
@:shift_button_html(open_shift.as_ref())
|
||||||
</div>
|
</div>
|
||||||
|
@if open_shift.is_some() {
|
||||||
|
<div hx-boost="true">
|
||||||
|
<a href="/shifts/@open_shift.unwrap().id/drinks">
|
||||||
|
<button>Drinks</button>
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
Loading…
Reference in a new issue