feat: drinks
parent
1c35f2f81f
commit
20b214e262
|
@ -125,6 +125,7 @@ checksum = "3b829e4e32b91e643de6eafe82b1d90675f5874230191a4ffbc1b336dec4d6bf"
|
|||
dependencies = [
|
||||
"async-trait",
|
||||
"axum-core",
|
||||
"axum-macros",
|
||||
"bitflags 1.3.2",
|
||||
"bytes",
|
||||
"futures-util",
|
||||
|
@ -166,6 +167,18 @@ dependencies = [
|
|||
"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]]
|
||||
name = "backtrace"
|
||||
version = "0.3.69"
|
||||
|
|
|
@ -10,7 +10,7 @@ path = "src/lib/mod.rs"
|
|||
|
||||
|
||||
[dependencies]
|
||||
axum = "0.6.20"
|
||||
axum = { version = "0.6.20", features = ["macros"] }
|
||||
serde = { version = "1.0", features = ["derive"] }
|
||||
serde_json = "1.0.68"
|
||||
tokio = { version = "1.32.0", features = ["full"] }
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
-- This file should undo anything in `up.sql`
|
||||
DROP TABLE drinks
|
|
@ -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::Form;
|
||||
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 diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper};
|
||||
use diesel_async::{scoped_futures::ScopedFutureExt, AsyncConnection, RunQueryDsl};
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::axum_ructe::render;
|
||||
use crate::AppState;
|
||||
|
||||
pub(crate) fn router() -> axum::Router<AppState> {
|
||||
axum::Router::new()
|
||||
.route("/drinks", post(add_drink))
|
||||
.route("/shifts/open", post(open_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)
|
||||
}
|
||||
|
||||
#[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 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.
|
||||
|
||||
diesel::table! {
|
||||
drinks (id) {
|
||||
id -> Unsigned<Integer>,
|
||||
price -> Unsigned<Integer>,
|
||||
quantity -> Unsigned<Integer>,
|
||||
shift -> Unsigned<Integer>,
|
||||
time -> Datetime,
|
||||
}
|
||||
}
|
||||
|
||||
diesel::table! {
|
||||
shifts (id) {
|
||||
id -> Unsigned<Integer>,
|
||||
|
@ -7,3 +17,10 @@ diesel::table! {
|
|||
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 axum_ructe;
|
||||
|
||||
use axum::extract::Path;
|
||||
use axum_ructe::render;
|
||||
|
||||
use axum::{extract::State, response::IntoResponse, routing::get, Router};
|
||||
|
@ -59,6 +60,7 @@ async fn main() {
|
|||
let app = Router::new()
|
||||
.nest("/api", api::router())
|
||||
.route("/", get(root))
|
||||
.route("/shifts/:id/drinks", get(drinks))
|
||||
.with_state(state);
|
||||
|
||||
// run our app with hyper
|
||||
|
@ -90,3 +92,7 @@ async fn root(State(state): State<AppState>) -> impl IntoResponse {
|
|||
|
||||
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;
|
||||
|
||||
@(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
|
||||
</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
|
||||
</button>
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
@use super::open_shift_button_html;
|
||||
@use cm_lib::models::Shift;
|
||||
|
||||
@(open_shift: Option<Shift>)
|
||||
@(open_shift: Option<&Shift>)
|
||||
|
||||
@if open_shift.is_none() {
|
||||
@:open_shift_button_html()
|
||||
|
|
|
@ -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>
|
||||
<div>
|
||||
@:shift_button_html(open_shift)
|
||||
@:shift_button_html(open_shift.as_ref())
|
||||
</div>
|
||||
@if open_shift.is_some() {
|
||||
<div hx-boost="true">
|
||||
<a href="/shifts/@open_shift.unwrap().id/drinks">
|
||||
<button>Drinks</button>
|
||||
</a>
|
||||
</div>
|
||||
}
|
||||
</main>
|
||||
|
||||
})
|
||||
|
|
Loading…
Reference in New Issue