2023-11-03 14:55:24 +00:00
|
|
|
use axum::{
|
|
|
|
extract::{Path, State},
|
|
|
|
response::{
|
|
|
|
sse::{Event, KeepAlive},
|
|
|
|
IntoResponse, Sse,
|
|
|
|
},
|
|
|
|
routing::{get, post},
|
|
|
|
Form,
|
|
|
|
};
|
|
|
|
use cm_lib::{
|
2023-11-05 19:32:59 +00:00
|
|
|
hx_request::HxRequest,
|
2023-11-03 14:55:24 +00:00
|
|
|
models::{NewDrink, Shift},
|
|
|
|
schema::shifts,
|
|
|
|
};
|
2023-09-26 09:18:12 +00:00
|
|
|
use diesel::{ExpressionMethods, OptionalExtension, QueryDsl, SelectableHelper};
|
|
|
|
use diesel_async::{scoped_futures::ScopedFutureExt, AsyncConnection, RunQueryDsl};
|
2023-11-03 14:55:24 +00:00
|
|
|
use futures_util::Stream;
|
2023-09-30 21:51:11 +00:00
|
|
|
use serde::Deserialize;
|
2023-11-03 14:55:24 +00:00
|
|
|
use tokio_stream::{wrappers::errors::BroadcastStreamRecvError, StreamExt as _};
|
2023-09-26 09:18:12 +00:00
|
|
|
|
|
|
|
use crate::AppState;
|
2023-11-05 19:38:50 +00:00
|
|
|
use cm_lib::render;
|
2023-09-26 09:18:12 +00:00
|
|
|
|
|
|
|
pub(crate) fn router() -> axum::Router<AppState> {
|
|
|
|
axum::Router::new()
|
2023-09-30 21:51:11 +00:00
|
|
|
.route("/drinks", post(add_drink))
|
2023-09-26 09:18:12 +00:00
|
|
|
.route("/shifts/open", post(open_shift))
|
2023-09-27 00:00:01 +00:00
|
|
|
.route("/shifts/:id/close", post(close_shift))
|
2023-11-03 14:55:24 +00:00
|
|
|
.route("/ada/updates", get(ada_subscribe))
|
2023-09-26 09:18:12 +00:00
|
|
|
}
|
|
|
|
|
2023-11-05 19:32:59 +00:00
|
|
|
async fn open_shift(State(state): State<AppState>, HxRequest(hx): HxRequest) -> impl IntoResponse {
|
2023-09-26 09:18:12 +00:00
|
|
|
let shift = {
|
|
|
|
let mut conn = state.connection.get().await.unwrap();
|
|
|
|
conn.transaction(|conn| {
|
|
|
|
use cm_lib::schema::shifts::dsl::*;
|
|
|
|
|
|
|
|
async move {
|
|
|
|
diesel::insert_into(shifts)
|
|
|
|
.default_values()
|
|
|
|
.execute(conn)
|
|
|
|
.await?;
|
|
|
|
|
|
|
|
shifts
|
|
|
|
.order(id.desc())
|
|
|
|
.select(Shift::as_select())
|
|
|
|
.first(conn)
|
|
|
|
.await
|
|
|
|
}
|
|
|
|
.scope_boxed()
|
|
|
|
})
|
|
|
|
.await
|
|
|
|
.optional()
|
|
|
|
.unwrap()
|
|
|
|
};
|
|
|
|
|
2023-11-05 19:32:59 +00:00
|
|
|
render!(crate::templates::home_html, shift, !hx)
|
2023-09-26 09:18:12 +00:00
|
|
|
}
|
|
|
|
|
2023-11-05 19:32:59 +00:00
|
|
|
async fn close_shift(
|
|
|
|
State(state): State<AppState>,
|
|
|
|
Path(id): Path<u32>,
|
|
|
|
HxRequest(hx): HxRequest,
|
|
|
|
) -> impl IntoResponse {
|
2023-09-27 00:00:01 +00:00
|
|
|
let mut conn = state.connection.get().await.unwrap();
|
|
|
|
diesel::update(shifts::table.filter(shifts::id.eq(id)))
|
|
|
|
.set(shifts::end.eq(Some(chrono::Utc::now().naive_local())))
|
|
|
|
.execute(&mut conn)
|
2023-09-26 09:18:12 +00:00
|
|
|
.await
|
|
|
|
.unwrap();
|
|
|
|
|
2023-11-05 19:32:59 +00:00
|
|
|
render!(crate::templates::home_html, None, !hx)
|
2023-09-26 09:18:12 +00:00
|
|
|
}
|
2023-09-30 21:51:11 +00:00
|
|
|
|
|
|
|
#[derive(Deserialize, Debug)]
|
|
|
|
struct DrinkForm {
|
|
|
|
shift_id: u32,
|
|
|
|
price: u32,
|
|
|
|
quantity: u32,
|
|
|
|
}
|
|
|
|
|
2023-11-02 22:26:47 +00:00
|
|
|
impl From<DrinkForm> for NewDrink {
|
|
|
|
fn from(value: DrinkForm) -> Self {
|
2023-09-30 21:51:11 +00:00
|
|
|
NewDrink {
|
2023-11-02 22:26:47 +00:00
|
|
|
price: value.price,
|
|
|
|
quantity: value.quantity,
|
|
|
|
shift: value.shift_id,
|
2023-09-30 21:51:11 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
async fn add_drink(
|
|
|
|
State(state): State<AppState>,
|
2023-11-05 19:32:59 +00:00
|
|
|
HxRequest(hx): HxRequest,
|
2023-09-30 21:51:11 +00:00
|
|
|
Form(form): Form<DrinkForm>,
|
|
|
|
) -> impl IntoResponse {
|
|
|
|
let mut conn = state.connection.get().await.unwrap();
|
|
|
|
|
2023-10-19 10:16:16 +00:00
|
|
|
let open_shift: Option<Shift> = {
|
|
|
|
use cm_lib::schema::shifts::dsl::*;
|
|
|
|
|
|
|
|
shifts
|
|
|
|
.filter(end.is_null())
|
|
|
|
.select(Shift::as_select())
|
|
|
|
.first(&mut conn)
|
|
|
|
.await
|
|
|
|
.optional()
|
|
|
|
.expect("Query failed: No open shifts found")
|
|
|
|
};
|
|
|
|
let open_shift = open_shift.unwrap();
|
2023-09-30 21:51:11 +00:00
|
|
|
|
|
|
|
async {
|
|
|
|
use cm_lib::schema::drinks::dsl::*;
|
|
|
|
|
|
|
|
diesel::insert_into(drinks)
|
|
|
|
.values::<NewDrink>(form.into())
|
|
|
|
.execute(&mut conn)
|
|
|
|
.await
|
|
|
|
.unwrap()
|
|
|
|
}
|
|
|
|
.await;
|
|
|
|
|
2023-10-19 10:16:16 +00:00
|
|
|
let mut headers = axum::http::HeaderMap::new();
|
|
|
|
headers.insert("HX-Push-Url", "/".parse().unwrap());
|
|
|
|
(
|
|
|
|
headers,
|
2023-11-05 19:32:59 +00:00
|
|
|
render!(crate::templates::home_html, Some(open_shift), !hx),
|
2023-10-19 10:16:16 +00:00
|
|
|
)
|
2023-09-30 21:51:11 +00:00
|
|
|
}
|
2023-11-03 14:55:24 +00:00
|
|
|
|
|
|
|
async fn ada_subscribe(
|
|
|
|
State(state): State<AppState>,
|
|
|
|
) -> Sse<impl Stream<Item = Result<Event, BroadcastStreamRecvError>>> {
|
2023-11-04 08:59:58 +00:00
|
|
|
let stream =
|
|
|
|
tokio_stream::wrappers::BroadcastStream::new(state.sse_handler.ada_sender.subscribe())
|
|
|
|
.map(|r| r.map(|s| Event::default().event("ada").data(s)));
|
2023-11-03 14:55:24 +00:00
|
|
|
|
|
|
|
Sse::new(stream).keep_alive(KeepAlive::default())
|
|
|
|
}
|