feat: add edit button to all detail pages
Add edit_button and edit_delete_buttons macros to detail_cards.html. All 7 entity detail pages now show an Edit button next to Delete when authenticated. Each detail template struct receives a pre-computed edit_url from the route handler.
This commit is contained in:
parent
1c029f52f4
commit
94088f1f4b
16 changed files with 51 additions and 6 deletions
|
|
@ -46,6 +46,7 @@ pub(crate) async fn bag_detail_page(
|
||||||
is_authenticated,
|
is_authenticated,
|
||||||
version_info: &crate::VERSION_INFO,
|
version_info: &crate::VERSION_INFO,
|
||||||
base_url: crate::base_url(),
|
base_url: crate::base_url(),
|
||||||
|
edit_url: format!("/bags/{id}/edit"),
|
||||||
bag: view,
|
bag: view,
|
||||||
roaster_slug: roaster.slug.clone(),
|
roaster_slug: roaster.slug.clone(),
|
||||||
roast_slug: roast.slug.clone(),
|
roast_slug: roast.slug.clone(),
|
||||||
|
|
|
||||||
|
|
@ -54,6 +54,7 @@ pub(crate) async fn brew_detail_page(
|
||||||
is_authenticated,
|
is_authenticated,
|
||||||
version_info: &crate::VERSION_INFO,
|
version_info: &crate::VERSION_INFO,
|
||||||
base_url: crate::base_url(),
|
base_url: crate::base_url(),
|
||||||
|
edit_url: format!("/brews/{id}/edit"),
|
||||||
brew: view,
|
brew: view,
|
||||||
roaster_slug: roaster.slug.clone(),
|
roaster_slug: roaster.slug.clone(),
|
||||||
roast_slug: roast.slug.clone(),
|
roast_slug: roast.slug.clone(),
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ pub(crate) async fn cafe_detail_page(
|
||||||
.map_err(|e| map_app_error(e.into()))?;
|
.map_err(|e| map_app_error(e.into()))?;
|
||||||
|
|
||||||
let image_url = resolve_image_url(&state, "cafe", i64::from(cafe.id)).await;
|
let image_url = resolve_image_url(&state, "cafe", i64::from(cafe.id)).await;
|
||||||
|
let edit_url = format!("/cafes/{}/edit", cafe.id);
|
||||||
|
|
||||||
let view = CafeDetailView::from_domain(cafe);
|
let view = CafeDetailView::from_domain(cafe);
|
||||||
|
|
||||||
|
|
@ -33,6 +34,7 @@ pub(crate) async fn cafe_detail_page(
|
||||||
is_authenticated,
|
is_authenticated,
|
||||||
version_info: &crate::VERSION_INFO,
|
version_info: &crate::VERSION_INFO,
|
||||||
base_url: crate::base_url(),
|
base_url: crate::base_url(),
|
||||||
|
edit_url,
|
||||||
cafe: view,
|
cafe: view,
|
||||||
image_url,
|
image_url,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,7 @@ pub(crate) async fn cup_detail_page(
|
||||||
is_authenticated,
|
is_authenticated,
|
||||||
version_info: &crate::VERSION_INFO,
|
version_info: &crate::VERSION_INFO,
|
||||||
base_url: crate::base_url(),
|
base_url: crate::base_url(),
|
||||||
|
edit_url: format!("/cups/{id}/edit"),
|
||||||
cup: view,
|
cup: view,
|
||||||
roaster_slug: roaster.slug.clone(),
|
roaster_slug: roaster.slug.clone(),
|
||||||
roast_slug: roast.slug.clone(),
|
roast_slug: roast.slug.clone(),
|
||||||
|
|
|
||||||
|
|
@ -34,6 +34,7 @@ pub(crate) async fn gear_detail_page(
|
||||||
is_authenticated,
|
is_authenticated,
|
||||||
version_info: &crate::VERSION_INFO,
|
version_info: &crate::VERSION_INFO,
|
||||||
base_url: crate::base_url(),
|
base_url: crate::base_url(),
|
||||||
|
edit_url: format!("/gear/{id}/edit"),
|
||||||
gear: view,
|
gear: view,
|
||||||
image_url,
|
image_url,
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,7 @@ pub(crate) async fn roaster_detail_page(
|
||||||
.map_err(|e| map_app_error(e.into()))?;
|
.map_err(|e| map_app_error(e.into()))?;
|
||||||
|
|
||||||
let image_url = resolve_image_url(&state, "roaster", i64::from(roaster.id)).await;
|
let image_url = resolve_image_url(&state, "roaster", i64::from(roaster.id)).await;
|
||||||
|
let edit_url = format!("/roasters/{}/edit", roaster.id);
|
||||||
|
|
||||||
let view = RoasterDetailView::from_domain(roaster);
|
let view = RoasterDetailView::from_domain(roaster);
|
||||||
|
|
||||||
|
|
@ -35,6 +36,7 @@ pub(crate) async fn roaster_detail_page(
|
||||||
base_url: crate::base_url(),
|
base_url: crate::base_url(),
|
||||||
roaster: view,
|
roaster: view,
|
||||||
image_url,
|
image_url,
|
||||||
|
edit_url,
|
||||||
};
|
};
|
||||||
|
|
||||||
render_html(template).map(IntoResponse::into_response)
|
render_html(template).map(IntoResponse::into_response)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,7 @@ pub(crate) async fn roast_detail_page(
|
||||||
.map_err(|e| map_app_error(e.into()))?;
|
.map_err(|e| map_app_error(e.into()))?;
|
||||||
|
|
||||||
let image_url = resolve_image_url(&state, "roast", i64::from(roast.id)).await;
|
let image_url = resolve_image_url(&state, "roast", i64::from(roast.id)).await;
|
||||||
|
let edit_url = format!("/roasts/{}/edit", roast.id);
|
||||||
|
|
||||||
let view = RoastDetailView::from_parts(roast, &roaster);
|
let view = RoastDetailView::from_parts(roast, &roaster);
|
||||||
|
|
||||||
|
|
@ -42,6 +43,7 @@ pub(crate) async fn roast_detail_page(
|
||||||
roast: view,
|
roast: view,
|
||||||
roaster_slug,
|
roaster_slug,
|
||||||
image_url,
|
image_url,
|
||||||
|
edit_url,
|
||||||
};
|
};
|
||||||
|
|
||||||
render_html(template).map(IntoResponse::into_response)
|
render_html(template).map(IntoResponse::into_response)
|
||||||
|
|
|
||||||
|
|
@ -222,6 +222,7 @@ pub struct BagDetailTemplate {
|
||||||
pub roaster_slug: String,
|
pub roaster_slug: String,
|
||||||
pub roast_slug: String,
|
pub roast_slug: String,
|
||||||
pub image_url: Option<String>,
|
pub image_url: Option<String>,
|
||||||
|
pub edit_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
|
|
@ -235,6 +236,7 @@ pub struct BrewDetailTemplate {
|
||||||
pub roaster_slug: String,
|
pub roaster_slug: String,
|
||||||
pub roast_slug: String,
|
pub roast_slug: String,
|
||||||
pub image_url: Option<String>,
|
pub image_url: Option<String>,
|
||||||
|
pub edit_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
|
|
@ -249,6 +251,7 @@ pub struct CupDetailTemplate {
|
||||||
pub roast_slug: String,
|
pub roast_slug: String,
|
||||||
pub cafe_slug: String,
|
pub cafe_slug: String,
|
||||||
pub image_url: Option<String>,
|
pub image_url: Option<String>,
|
||||||
|
pub edit_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
|
|
@ -261,6 +264,7 @@ pub struct RoastDetailTemplate {
|
||||||
pub roast: RoastDetailView,
|
pub roast: RoastDetailView,
|
||||||
pub roaster_slug: String,
|
pub roaster_slug: String,
|
||||||
pub image_url: Option<String>,
|
pub image_url: Option<String>,
|
||||||
|
pub edit_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
|
|
@ -272,6 +276,7 @@ pub struct RoasterDetailTemplate {
|
||||||
pub base_url: &'static str,
|
pub base_url: &'static str,
|
||||||
pub roaster: RoasterDetailView,
|
pub roaster: RoasterDetailView,
|
||||||
pub image_url: Option<String>,
|
pub image_url: Option<String>,
|
||||||
|
pub edit_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
|
|
@ -283,6 +288,7 @@ pub struct CafeDetailTemplate {
|
||||||
pub base_url: &'static str,
|
pub base_url: &'static str,
|
||||||
pub cafe: CafeDetailView,
|
pub cafe: CafeDetailView,
|
||||||
pub image_url: Option<String>,
|
pub image_url: Option<String>,
|
||||||
|
pub edit_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
|
|
@ -294,6 +300,7 @@ pub struct GearDetailTemplate {
|
||||||
pub base_url: &'static str,
|
pub base_url: &'static str,
|
||||||
pub gear: GearDetailView,
|
pub gear: GearDetailView,
|
||||||
pub image_url: Option<String>,
|
pub image_url: Option<String>,
|
||||||
|
pub edit_url: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Template)]
|
#[derive(Template)]
|
||||||
|
|
|
||||||
|
|
@ -114,6 +114,7 @@
|
||||||
<div
|
<div
|
||||||
class="rounded-lg border bg-surface p-5 md:col-span-2 flex items-center gap-2"
|
class="rounded-lg border bg-surface p-5 md:col-span-2 flex items-center gap-2"
|
||||||
>
|
>
|
||||||
|
{{ detail::edit_button(edit_url) }}
|
||||||
{% if !bag.closed %}
|
{% if !bag.closed %}
|
||||||
<button
|
<button
|
||||||
type="button"
|
type="button"
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if is_authenticated %}
|
{% if is_authenticated %}
|
||||||
{{ detail::delete_button("brew", "/api/v1/brews", brew.id) }}
|
{{ detail::edit_delete_buttons(edit_url, "brew", "/api/v1/brews", brew.id) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -65,6 +65,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if is_authenticated %}
|
{% if is_authenticated %}
|
||||||
{{ detail::delete_button("cafe", "/api/v1/cafes", cafe.id) }}
|
{{ detail::edit_delete_buttons(edit_url, "cafe", "/api/v1/cafes", cafe.id) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -88,6 +88,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if is_authenticated %}
|
{% if is_authenticated %}
|
||||||
{{ detail::delete_button("cup", "/api/v1/cups", cup.id) }}
|
{{ detail::edit_delete_buttons(edit_url, "cup", "/api/v1/cups", cup.id) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -52,6 +52,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if is_authenticated %}
|
{% if is_authenticated %}
|
||||||
{{ detail::delete_button("gear", "/api/v1/gear", gear.id) }}
|
{{ detail::edit_delete_buttons(edit_url, "gear", "/api/v1/gear", gear.id) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -40,6 +40,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if is_authenticated %}
|
{% if is_authenticated %}
|
||||||
{{ detail::delete_button("roast", "/api/v1/roasts", roast.id) }}
|
{{ detail::edit_delete_buttons(edit_url, "roast", "/api/v1/roasts", roast.id) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -72,6 +72,6 @@
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{% if is_authenticated %}
|
{% if is_authenticated %}
|
||||||
{{ detail::delete_button("roaster", "/api/v1/roasters", roaster.id) }}
|
{{ detail::edit_delete_buttons(edit_url, "roaster", "/api/v1/roasters", roaster.id) }}
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
|
||||||
|
|
@ -125,6 +125,15 @@
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro edit_button(edit_url) %}
|
||||||
|
<a
|
||||||
|
href="{{ edit_url }}"
|
||||||
|
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-accent transition hover:bg-surface-alt"
|
||||||
|
>
|
||||||
|
{{ icons::pencil("h-4 w-4") }} Edit
|
||||||
|
</a>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro delete_button(entity_label, api_path, id) %}
|
{% macro delete_button(entity_label, api_path, id) %}
|
||||||
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
||||||
<button
|
<button
|
||||||
|
|
@ -137,6 +146,24 @@
|
||||||
</div>
|
</div>
|
||||||
{% endmacro %}
|
{% endmacro %}
|
||||||
|
|
||||||
|
{% macro edit_delete_buttons(edit_url, entity_label, api_path, id) %}
|
||||||
|
<div class="rounded-lg border bg-surface p-5 flex items-center gap-2">
|
||||||
|
<a
|
||||||
|
href="{{ edit_url }}"
|
||||||
|
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-accent transition hover:bg-surface-alt"
|
||||||
|
>
|
||||||
|
{{ icons::pencil("h-4 w-4") }} Edit
|
||||||
|
</a>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="inline-flex items-center gap-2 rounded-md border px-4 py-2 text-sm font-medium text-error transition hover:bg-surface-alt"
|
||||||
|
data-on:click="confirm('Delete this {{ entity_label }}? This cannot be undone.') && @delete('{{ api_path }}/{{ id }}')"
|
||||||
|
>
|
||||||
|
{{ icons::delete("h-4 w-4") }} Delete
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{% endmacro %}
|
||||||
|
|
||||||
{% macro roaster_card(name, country, country_flag, city, homepage, roaster_slug) %}
|
{% macro roaster_card(name, country, country_flag, city, homepage, roaster_slug) %}
|
||||||
<div class="rounded-lg border bg-surface p-5">
|
<div class="rounded-lg border bg-surface p-5">
|
||||||
<h2 class="text-lg font-semibold text-text mb-4">Roaster</h2>
|
<h2 class="text-lg font-semibold text-text mb-4">Roaster</h2>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue