fix: remove superfluous /bags/:id/finish endpoint
This commit is contained in:
parent
7eab263071
commit
57260a71c2
4 changed files with 33 additions and 70 deletions
|
|
@ -201,20 +201,37 @@ pub(crate) async fn get_bag(
|
||||||
Ok(Json(bag))
|
Ok(Json(bag))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(state, _auth_user))]
|
#[tracing::instrument(skip(state, _auth_user, headers, query))]
|
||||||
pub(crate) async fn update_bag(
|
pub(crate) async fn update_bag(
|
||||||
State(state): State<AppState>,
|
State(state): State<AppState>,
|
||||||
_auth_user: AuthenticatedUser,
|
_auth_user: AuthenticatedUser,
|
||||||
|
headers: HeaderMap,
|
||||||
Path(id): Path<BagId>,
|
Path(id): Path<BagId>,
|
||||||
Json(payload): Json<UpdateBag>,
|
Query(query): Query<ListQuery>,
|
||||||
) -> Result<Json<Bag>, ApiError> {
|
Query(update_params): Query<UpdateBag>,
|
||||||
|
payload: Option<Json<UpdateBag>>,
|
||||||
|
) -> Result<Response, ApiError> {
|
||||||
|
let request = query.into_request::<BagSortKey>();
|
||||||
|
|
||||||
|
let body_update = payload.map(|Json(p)| p).unwrap_or(UpdateBag {
|
||||||
|
remaining: None,
|
||||||
|
closed: None,
|
||||||
|
finished_at: None,
|
||||||
|
});
|
||||||
|
|
||||||
|
let update = UpdateBag {
|
||||||
|
remaining: body_update.remaining.or(update_params.remaining),
|
||||||
|
closed: body_update.closed.or(update_params.closed),
|
||||||
|
finished_at: body_update.finished_at.or(update_params.finished_at),
|
||||||
|
};
|
||||||
|
|
||||||
let bag = state
|
let bag = state
|
||||||
.bag_repo
|
.bag_repo
|
||||||
.update(id, payload.clone())
|
.update(id, update.clone())
|
||||||
.await
|
.await
|
||||||
.map_err(AppError::from)?;
|
.map_err(AppError::from)?;
|
||||||
|
|
||||||
if let Some(true) = payload.closed {
|
if let Some(true) = update.closed {
|
||||||
// Fetch roast and roaster for timeline event
|
// Fetch roast and roaster for timeline event
|
||||||
if let Ok(roast) = state.roast_repo.get(bag.roast_id).await
|
if let Ok(roast) = state.roast_repo.get(bag.roast_id).await
|
||||||
&& let Ok(roaster) = state.roaster_repo.get(roast.roaster_id).await
|
&& let Ok(roaster) = state.roaster_repo.get(roast.roaster_id).await
|
||||||
|
|
@ -234,7 +251,13 @@ pub(crate) async fn update_bag(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Json(bag))
|
if is_datastar_request(&headers) {
|
||||||
|
render_bag_list_fragment(state, request, true)
|
||||||
|
.await
|
||||||
|
.map_err(ApiError::from)
|
||||||
|
} else {
|
||||||
|
Ok(Json(bag).into_response())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(state, _auth_user, headers, query))]
|
#[tracing::instrument(skip(state, _auth_user, headers, query))]
|
||||||
|
|
@ -257,68 +280,6 @@ pub(crate) async fn delete_bag(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[tracing::instrument(skip(state, _auth_user, headers, query))]
|
|
||||||
pub(crate) async fn finish_bag(
|
|
||||||
State(state): State<AppState>,
|
|
||||||
_auth_user: AuthenticatedUser,
|
|
||||||
headers: HeaderMap,
|
|
||||||
Path(id): Path<BagId>,
|
|
||||||
Query(query): Query<ListQuery>,
|
|
||||||
) -> Result<Response, StatusCode> {
|
|
||||||
let request = query.into_request::<BagSortKey>();
|
|
||||||
|
|
||||||
let bag = state
|
|
||||||
.bag_repo
|
|
||||||
.get(id)
|
|
||||||
.await
|
|
||||||
.map_err(|err| map_app_error(AppError::from(err)))?;
|
|
||||||
|
|
||||||
let update = UpdateBag {
|
|
||||||
remaining: Some(0.0),
|
|
||||||
closed: Some(true),
|
|
||||||
finished_at: Some(chrono::Utc::now().date_naive()),
|
|
||||||
};
|
|
||||||
|
|
||||||
let _ = state
|
|
||||||
.bag_repo
|
|
||||||
.update(id, update)
|
|
||||||
.await
|
|
||||||
.map_err(|err| map_app_error(AppError::from(err)))?;
|
|
||||||
|
|
||||||
// Add timeline event
|
|
||||||
if let Ok(roast) = state.roast_repo.get(bag.roast_id).await
|
|
||||||
&& let Ok(roaster) = state.roaster_repo.get(roast.roaster_id).await
|
|
||||||
{
|
|
||||||
let event = NewTimelineEvent {
|
|
||||||
entity_type: "bag".to_string(),
|
|
||||||
entity_id: bag.id.into_inner(),
|
|
||||||
occurred_at: chrono::Utc::now(),
|
|
||||||
title: roast.name.to_string(),
|
|
||||||
details: vec![
|
|
||||||
TimelineEventDetail {
|
|
||||||
label: "Roaster".to_string(),
|
|
||||||
value: roaster.name,
|
|
||||||
},
|
|
||||||
TimelineEventDetail {
|
|
||||||
label: "Amount".to_string(),
|
|
||||||
value: format!("{:.1}g", bag.amount),
|
|
||||||
},
|
|
||||||
],
|
|
||||||
tasting_notes: vec![],
|
|
||||||
};
|
|
||||||
let _ = state.timeline_repo.insert(event).await;
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_datastar_request(&headers) {
|
|
||||||
render_bag_list_fragment(state, request, true)
|
|
||||||
.await
|
|
||||||
.map_err(map_app_error)
|
|
||||||
} else {
|
|
||||||
// Fallback for non-datastar requests (though the UI uses datastar)
|
|
||||||
Ok(Redirect::to(BAG_PAGE_PATH).into_response())
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug, Deserialize)]
|
#[derive(Debug, Deserialize)]
|
||||||
pub struct BagsQuery {
|
pub struct BagsQuery {
|
||||||
pub roast_id: Option<RoastId>,
|
pub roast_id: Option<RoastId>,
|
||||||
|
|
|
||||||
|
|
@ -64,7 +64,6 @@ pub fn app_router(state: AppState) -> axum::Router {
|
||||||
get(roasts::roast_page),
|
get(roasts::roast_page),
|
||||||
)
|
)
|
||||||
.route("/bags", get(bags::bags_page))
|
.route("/bags", get(bags::bags_page))
|
||||||
.route("/bags/:id/finish", post(bags::finish_bag))
|
|
||||||
.route("/timeline", get(timeline::timeline_page))
|
.route("/timeline", get(timeline::timeline_page))
|
||||||
.route("/styles.css", get(styles))
|
.route("/styles.css", get(styles))
|
||||||
.route("/favicon.ico", get(favicon))
|
.route("/favicon.ico", get(favicon))
|
||||||
|
|
|
||||||
|
|
@ -118,6 +118,9 @@ pub fn render_fragment<T: Template>(
|
||||||
.map_err(|err| AppError::unexpected(format!("failed to render fragment: {err}")))?;
|
.map_err(|err| AppError::unexpected(format!("failed to render fragment: {err}")))?;
|
||||||
|
|
||||||
let mut response = Html(html).into_response();
|
let mut response = Html(html).into_response();
|
||||||
|
response
|
||||||
|
.headers_mut()
|
||||||
|
.insert(CONTENT_TYPE, HeaderValue::from_static("text/html"));
|
||||||
set_datastar_patch_headers(response.headers_mut(), selector);
|
set_datastar_patch_headers(response.headers_mut(), selector);
|
||||||
Ok(response)
|
Ok(response)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -78,7 +78,7 @@
|
||||||
<div class="pt-4 border-t border-stone-100 flex justify-end">
|
<div class="pt-4 border-t border-stone-100 flex justify-end">
|
||||||
<button
|
<button
|
||||||
class="text-sm text-stone-600 hover:text-stone-900 font-medium"
|
class="text-sm text-stone-600 hover:text-stone-900 font-medium"
|
||||||
data-on:click="@post('/bags/{{ bag.id }}/finish?{{ navigator.query() }}', {responseOverrides: {selector: '#bag-list', mode: 'replace'}})"
|
data-on:click="@put('/api/v1/bags/{{ bag.id }}?{{ navigator.query() }}&closed=true&remaining=0.0&finished_at=' + new Date().toISOString().split('T')[0], {responseOverrides: {selector: '#bag-list', mode: 'replace'}})"
|
||||||
>
|
>
|
||||||
Finish Bag
|
Finish Bag
|
||||||
</button>
|
</button>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue