From 1a1b28559c9522e925bb4c0a02a9389b0c66d5bc Mon Sep 17 00:00:00 2001 From: Jon Seager Date: Tue, 10 Feb 2026 18:28:38 +0000 Subject: [PATCH] feat: add edit page route handlers, template structs, and edit templates Add edit page handlers for all 7 entities (roaster, roast, bag, brew, cafe, cup, gear) with authentication, data pre-loading, and image URL resolution. Register edit routes in app router. Add corresponding template structs and HTML templates with pre-populated forms. --- src/application/routes/app/bags.rs | 37 ++- src/application/routes/app/brews.rs | 57 ++++- src/application/routes/app/cafes.rs | 35 ++- src/application/routes/app/cups.rs | 41 ++- src/application/routes/app/gear.rs | 31 ++- src/application/routes/app/mod.rs | 7 + src/application/routes/app/roasters.rs | 33 ++- src/application/routes/app/roasts.rs | 47 +++- src/presentation/web/templates.rs | 120 +++++++++ templates/pages/edit_bag.html | 99 +++++++ templates/pages/edit_brew.html | 341 +++++++++++++++++++++++++ templates/pages/edit_cafe.html | 142 ++++++++++ templates/pages/edit_cup.html | 96 +++++++ templates/pages/edit_gear.html | 88 +++++++ templates/pages/edit_roast.html | 164 ++++++++++++ templates/pages/edit_roaster.html | 106 ++++++++ 16 files changed, 1437 insertions(+), 7 deletions(-) create mode 100644 templates/pages/edit_bag.html create mode 100644 templates/pages/edit_brew.html create mode 100644 templates/pages/edit_cafe.html create mode 100644 templates/pages/edit_cup.html create mode 100644 templates/pages/edit_gear.html create mode 100644 templates/pages/edit_roast.html create mode 100644 templates/pages/edit_roaster.html diff --git a/src/application/routes/app/bags.rs b/src/application/routes/app/bags.rs index 6b507e4..579a8f1 100644 --- a/src/application/routes/app/bags.rs +++ b/src/application/routes/app/bags.rs @@ -3,12 +3,14 @@ use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use tower_cookies::Cookies; +use crate::application::auth::AuthenticatedUser; use crate::application::errors::map_app_error; use crate::application::routes::api::images::resolve_image_url; use crate::application::routes::render_html; +use crate::application::routes::support::load_roast_options; use crate::application::state::AppState; use crate::domain::ids::BagId; -use crate::presentation::web::templates::BagDetailTemplate; +use crate::presentation::web::templates::{BagDetailTemplate, BagEditTemplate}; use crate::presentation::web::views::BagDetailView; #[tracing::instrument(skip(state, cookies))] @@ -55,3 +57,36 @@ pub(crate) async fn bag_detail_page( render_html(template).map(IntoResponse::into_response) } + +#[tracing::instrument(skip(state, _auth_user))] +pub(crate) async fn bag_edit_page( + State(state): State, + _auth_user: AuthenticatedUser, + Path(id): Path, +) -> Result { + let bag = state + .bag_repo + .get_with_roast(id) + .await + .map_err(|e| map_app_error(e.into()))?; + + let roast_options = load_roast_options(&state).await.map_err(map_app_error)?; + + let template = BagEditTemplate { + nav_active: "", + is_authenticated: true, + version_info: &crate::VERSION_INFO, + id: bag.bag.id.to_string(), + roast_id: bag.bag.roast_id.to_string(), + roast_label: format!("{} ({})", bag.roast_name, bag.roaster_name), + roast_date: bag + .bag + .roast_date + .map(|d| d.to_string()) + .unwrap_or_default(), + amount: bag.bag.amount, + roast_options, + }; + + render_html(template).map(IntoResponse::into_response) +} diff --git a/src/application/routes/app/brews.rs b/src/application/routes/app/brews.rs index 49532e4..b7f3597 100644 --- a/src/application/routes/app/brews.rs +++ b/src/application/routes/app/brews.rs @@ -3,12 +3,14 @@ use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use tower_cookies::Cookies; +use crate::application::auth::AuthenticatedUser; use crate::application::errors::map_app_error; +use crate::application::routes::api::brews::load_brew_form_data; use crate::application::routes::api::images::resolve_image_url; use crate::application::routes::render_html; use crate::application::state::AppState; use crate::domain::ids::BrewId; -use crate::presentation::web::templates::BrewDetailTemplate; +use crate::presentation::web::templates::{BrewDetailTemplate, BrewEditTemplate}; use crate::presentation::web::views::BrewDetailView; #[tracing::instrument(skip(state, cookies))] @@ -63,3 +65,56 @@ pub(crate) async fn brew_detail_page( render_html(template).map(IntoResponse::into_response) } + +#[tracing::instrument(skip(state, _auth_user))] +pub(crate) async fn brew_edit_page( + State(state): State, + _auth_user: AuthenticatedUser, + Path(id): Path, +) -> Result { + let brew = state + .brew_repo + .get_with_details(id) + .await + .map_err(|e| map_app_error(e.into()))?; + + let form_data = load_brew_form_data(&state).await.map_err(map_app_error)?; + + let image_url = resolve_image_url(&state, "brew", i64::from(id)).await; + + let template = BrewEditTemplate { + nav_active: "", + is_authenticated: true, + version_info: &crate::VERSION_INFO, + id: brew.brew.id.to_string(), + bag_id: brew.brew.bag_id.to_string(), + bag_label: format!("{} ({})", brew.roast_name, brew.roaster_name), + coffee_weight: brew.brew.coffee_weight, + grinder_id: brew.brew.grinder_id.to_string(), + grind_setting: brew.brew.grind_setting, + brewer_id: brew.brew.brewer_id.to_string(), + filter_paper_id: brew + .brew + .filter_paper_id + .map(|id| id.to_string()) + .unwrap_or_default(), + water_volume: brew.brew.water_volume, + water_temp: brew.brew.water_temp, + brew_time: brew.brew.brew_time.unwrap_or(0), + quick_notes: brew + .brew + .quick_notes + .iter() + .map(|n| n.form_value()) + .collect::>() + .join(","), + bag_options: form_data.bag_options, + grinder_options: form_data.grinder_options, + brewer_options: form_data.brewer_options, + filter_paper_options: form_data.filter_paper_options, + quick_note_options: form_data.quick_note_options, + image_url, + }; + + render_html(template).map(IntoResponse::into_response) +} diff --git a/src/application/routes/app/cafes.rs b/src/application/routes/app/cafes.rs index 44fbd92..2806c0d 100644 --- a/src/application/routes/app/cafes.rs +++ b/src/application/routes/app/cafes.rs @@ -3,11 +3,13 @@ use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use tower_cookies::Cookies; +use crate::application::auth::AuthenticatedUser; use crate::application::errors::map_app_error; use crate::application::routes::api::images::resolve_image_url; use crate::application::routes::render_html; use crate::application::state::AppState; -use crate::presentation::web::templates::CafeDetailTemplate; +use crate::domain::ids::CafeId; +use crate::presentation::web::templates::{CafeDetailTemplate, CafeEditTemplate}; use crate::presentation::web::views::CafeDetailView; #[tracing::instrument(skip(state, cookies))] @@ -41,3 +43,34 @@ pub(crate) async fn cafe_detail_page( render_html(template).map(IntoResponse::into_response) } + +#[tracing::instrument(skip(state, _auth_user))] +pub(crate) async fn cafe_edit_page( + State(state): State, + _auth_user: AuthenticatedUser, + Path(id): Path, +) -> Result { + let cafe = state + .cafe_repo + .get(id) + .await + .map_err(|e| map_app_error(e.into()))?; + + let image_url = resolve_image_url(&state, "cafe", i64::from(id)).await; + + let template = CafeEditTemplate { + nav_active: "", + is_authenticated: true, + version_info: &crate::VERSION_INFO, + id: cafe.id.to_string(), + name: cafe.name, + city: cafe.city, + country: cafe.country, + latitude: cafe.latitude, + longitude: cafe.longitude, + website: cafe.website.unwrap_or_default(), + image_url, + }; + + render_html(template).map(IntoResponse::into_response) +} diff --git a/src/application/routes/app/cups.rs b/src/application/routes/app/cups.rs index c786d8c..2d4e73f 100644 --- a/src/application/routes/app/cups.rs +++ b/src/application/routes/app/cups.rs @@ -3,12 +3,14 @@ use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use tower_cookies::Cookies; +use crate::application::auth::AuthenticatedUser; use crate::application::errors::map_app_error; use crate::application::routes::api::images::resolve_image_url; use crate::application::routes::render_html; +use crate::application::routes::support::{load_cafe_options, load_roast_options}; use crate::application::state::AppState; use crate::domain::ids::CupId; -use crate::presentation::web::templates::CupDetailTemplate; +use crate::presentation::web::templates::{CupDetailTemplate, CupEditTemplate}; use crate::presentation::web::views::CupDetailView; #[tracing::instrument(skip(state, cookies))] @@ -70,3 +72,40 @@ pub(crate) async fn cup_detail_page( render_html(template).map(IntoResponse::into_response) } + +#[tracing::instrument(skip(state, _auth_user))] +pub(crate) async fn cup_edit_page( + State(state): State, + _auth_user: AuthenticatedUser, + Path(id): Path, +) -> Result { + let cup = state + .cup_repo + .get_with_details(id) + .await + .map_err(|e| map_app_error(e.into()))?; + + let (roast_options, cafe_options) = + tokio::try_join!(async { load_roast_options(&state).await }, async { + load_cafe_options(&state).await + },) + .map_err(map_app_error)?; + + let image_url = resolve_image_url(&state, "cup", i64::from(id)).await; + + let template = CupEditTemplate { + nav_active: "", + is_authenticated: true, + version_info: &crate::VERSION_INFO, + id: cup.cup.id.to_string(), + roast_id: cup.cup.roast_id.to_string(), + roast_label: format!("{} ({})", cup.roast_name, cup.roaster_name), + cafe_id: cup.cup.cafe_id.to_string(), + cafe_label: format!("{}, {}", cup.cafe_name, cup.cafe_city), + roast_options, + cafe_options, + image_url, + }; + + render_html(template).map(IntoResponse::into_response) +} diff --git a/src/application/routes/app/gear.rs b/src/application/routes/app/gear.rs index 8e85eea..808aa38 100644 --- a/src/application/routes/app/gear.rs +++ b/src/application/routes/app/gear.rs @@ -3,12 +3,13 @@ use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use tower_cookies::Cookies; +use crate::application::auth::AuthenticatedUser; use crate::application::errors::map_app_error; use crate::application::routes::api::images::resolve_image_url; use crate::application::routes::render_html; use crate::application::state::AppState; use crate::domain::ids::GearId; -use crate::presentation::web::templates::GearDetailTemplate; +use crate::presentation::web::templates::{GearDetailTemplate, GearEditTemplate}; use crate::presentation::web::views::GearDetailView; #[tracing::instrument(skip(state, cookies))] @@ -41,3 +42,31 @@ pub(crate) async fn gear_detail_page( render_html(template).map(IntoResponse::into_response) } + +#[tracing::instrument(skip(state, _auth_user))] +pub(crate) async fn gear_edit_page( + State(state): State, + _auth_user: AuthenticatedUser, + Path(id): Path, +) -> Result { + let gear = state + .gear_repo + .get(id) + .await + .map_err(|e| map_app_error(e.into()))?; + + let image_url = resolve_image_url(&state, "gear", i64::from(id)).await; + + let template = GearEditTemplate { + nav_active: "", + is_authenticated: true, + version_info: &crate::VERSION_INFO, + id: gear.id.to_string(), + category: gear.category.display_label().to_string(), + make: gear.make, + model: gear.model, + image_url, + }; + + render_html(template).map(IntoResponse::into_response) +} diff --git a/src/application/routes/app/mod.rs b/src/application/routes/app/mod.rs index 7b9f21f..a70a4fd 100644 --- a/src/application/routes/app/mod.rs +++ b/src/application/routes/app/mod.rs @@ -35,15 +35,22 @@ pub(super) fn router() -> axum::Router { .route("/timeline", get(timeline::timeline_page)) .route("/stats", get(stats::stats_page)) .route("/bags/{id}", get(bags::bag_detail_page)) + .route("/bags/{id}/edit", get(bags::bag_edit_page)) .route("/brews/{id}", get(brews::brew_detail_page)) + .route("/brews/{id}/edit", get(brews::brew_edit_page)) .route("/cafes/{slug}", get(cafes::cafe_detail_page)) + .route("/cafes/{id}/edit", get(cafes::cafe_edit_page)) .route("/cups/{id}", get(cups::cup_detail_page)) + .route("/cups/{id}/edit", get(cups::cup_edit_page)) .route("/gear/{id}", get(gear::gear_detail_page)) + .route("/gear/{id}/edit", get(gear::gear_edit_page)) .route("/roasters/{slug}", get(roasters::roaster_detail_page)) + .route("/roasters/{id}/edit", get(roasters::roaster_edit_page)) .route( "/roasters/{roaster_slug}/roasts/{roast_slug}", get(roasts::roast_detail_page), ) + .route("/roasts/{id}/edit", get(roasts::roast_edit_page)) .route("/static/css/styles.css", get(styles)) .route("/static/js/webauthn.js", get(webauthn_js)) .route( diff --git a/src/application/routes/app/roasters.rs b/src/application/routes/app/roasters.rs index 56a7a7c..26eba92 100644 --- a/src/application/routes/app/roasters.rs +++ b/src/application/routes/app/roasters.rs @@ -3,11 +3,13 @@ use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use tower_cookies::Cookies; +use crate::application::auth::AuthenticatedUser; use crate::application::errors::map_app_error; use crate::application::routes::api::images::resolve_image_url; use crate::application::routes::render_html; use crate::application::state::AppState; -use crate::presentation::web::templates::RoasterDetailTemplate; +use crate::domain::ids::RoasterId; +use crate::presentation::web::templates::{RoasterDetailTemplate, RoasterEditTemplate}; use crate::presentation::web::views::RoasterDetailView; #[tracing::instrument(skip(state, cookies))] @@ -41,3 +43,32 @@ pub(crate) async fn roaster_detail_page( render_html(template).map(IntoResponse::into_response) } + +#[tracing::instrument(skip(state, _auth_user))] +pub(crate) async fn roaster_edit_page( + State(state): State, + _auth_user: AuthenticatedUser, + Path(id): Path, +) -> Result { + let roaster = state + .roaster_repo + .get(id) + .await + .map_err(|e| map_app_error(e.into()))?; + + let image_url = resolve_image_url(&state, "roaster", i64::from(id)).await; + + let template = RoasterEditTemplate { + nav_active: "", + is_authenticated: true, + version_info: &crate::VERSION_INFO, + id: roaster.id.to_string(), + name: roaster.name, + country: roaster.country, + city: roaster.city.unwrap_or_default(), + homepage: roaster.homepage.unwrap_or_default(), + image_url, + }; + + render_html(template).map(IntoResponse::into_response) +} diff --git a/src/application/routes/app/roasts.rs b/src/application/routes/app/roasts.rs index ffe99e9..508401e 100644 --- a/src/application/routes/app/roasts.rs +++ b/src/application/routes/app/roasts.rs @@ -3,11 +3,14 @@ use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; use tower_cookies::Cookies; +use crate::application::auth::AuthenticatedUser; use crate::application::errors::map_app_error; use crate::application::routes::api::images::resolve_image_url; use crate::application::routes::render_html; +use crate::application::routes::support::load_roaster_options; use crate::application::state::AppState; -use crate::presentation::web::templates::RoastDetailTemplate; +use crate::domain::ids::RoastId; +use crate::presentation::web::templates::{RoastDetailTemplate, RoastEditTemplate}; use crate::presentation::web::views::RoastDetailView; #[tracing::instrument(skip(state, cookies))] @@ -48,3 +51,45 @@ pub(crate) async fn roast_detail_page( render_html(template).map(IntoResponse::into_response) } + +#[tracing::instrument(skip(state, _auth_user))] +pub(crate) async fn roast_edit_page( + State(state): State, + _auth_user: AuthenticatedUser, + Path(id): Path, +) -> Result { + let roast = state + .roast_repo + .get(id) + .await + .map_err(|e| map_app_error(e.into()))?; + + let roaster = state + .roaster_repo + .get(roast.roaster_id) + .await + .map_err(|e| map_app_error(e.into()))?; + + let roaster_options = load_roaster_options(&state).await.map_err(map_app_error)?; + + let image_url = resolve_image_url(&state, "roast", i64::from(id)).await; + + let template = RoastEditTemplate { + nav_active: "", + is_authenticated: true, + version_info: &crate::VERSION_INFO, + id: roast.id.to_string(), + roaster_id: roast.roaster_id.to_string(), + roaster_name: roaster.name, + name: roast.name, + origin: roast.origin.unwrap_or_default(), + region: roast.region.unwrap_or_default(), + producer: roast.producer.unwrap_or_default(), + process: roast.process.unwrap_or_default(), + tasting_notes: roast.tasting_notes.join(", "), + roaster_options, + image_url, + }; + + render_html(template).map(IntoResponse::into_response) +} diff --git a/src/presentation/web/templates.rs b/src/presentation/web/templates.rs index 2a72088..c3e2d01 100644 --- a/src/presentation/web/templates.rs +++ b/src/presentation/web/templates.rs @@ -303,6 +303,126 @@ pub struct GearDetailTemplate { pub edit_url: String, } +// ── Edit page templates ── + +#[derive(Template)] +#[template(path = "pages/edit_roaster.html")] +pub struct RoasterEditTemplate { + pub nav_active: &'static str, + pub is_authenticated: bool, + pub version_info: &'static crate::VersionInfo, + pub id: String, + pub name: String, + pub country: String, + pub city: String, + pub homepage: String, + pub image_url: Option, +} + +#[derive(Template)] +#[template(path = "pages/edit_roast.html")] +pub struct RoastEditTemplate { + pub nav_active: &'static str, + pub is_authenticated: bool, + pub version_info: &'static crate::VersionInfo, + pub id: String, + pub roaster_id: String, + pub roaster_name: String, + pub name: String, + pub origin: String, + pub region: String, + pub producer: String, + pub process: String, + pub tasting_notes: String, + pub roaster_options: Vec, + pub image_url: Option, +} + +#[derive(Template)] +#[template(path = "pages/edit_bag.html")] +pub struct BagEditTemplate { + pub nav_active: &'static str, + pub is_authenticated: bool, + pub version_info: &'static crate::VersionInfo, + pub id: String, + pub roast_id: String, + pub roast_label: String, + pub roast_date: String, + pub amount: f64, + pub roast_options: Vec, +} + +#[derive(Template)] +#[template(path = "pages/edit_brew.html")] +pub struct BrewEditTemplate { + pub nav_active: &'static str, + pub is_authenticated: bool, + pub version_info: &'static crate::VersionInfo, + pub id: String, + pub bag_id: String, + pub bag_label: String, + pub coffee_weight: f64, + pub grinder_id: String, + pub grind_setting: f64, + pub brewer_id: String, + pub filter_paper_id: String, + pub water_volume: i32, + pub water_temp: f64, + pub brew_time: i32, + pub quick_notes: String, + pub bag_options: Vec, + pub grinder_options: Vec, + pub brewer_options: Vec, + pub filter_paper_options: Vec, + pub quick_note_options: Vec, + pub image_url: Option, +} + +#[derive(Template)] +#[template(path = "pages/edit_cafe.html")] +pub struct CafeEditTemplate { + pub nav_active: &'static str, + pub is_authenticated: bool, + pub version_info: &'static crate::VersionInfo, + pub id: String, + pub name: String, + pub city: String, + pub country: String, + pub latitude: f64, + pub longitude: f64, + pub website: String, + pub image_url: Option, +} + +#[derive(Template)] +#[template(path = "pages/edit_cup.html")] +pub struct CupEditTemplate { + pub nav_active: &'static str, + pub is_authenticated: bool, + pub version_info: &'static crate::VersionInfo, + pub id: String, + pub roast_id: String, + pub roast_label: String, + pub cafe_id: String, + pub cafe_label: String, + pub roast_options: Vec, + pub cafe_options: Vec, + pub image_url: Option, +} + +#[derive(Template)] +#[template(path = "pages/edit_gear.html")] +pub struct GearEditTemplate { + pub nav_active: &'static str, + pub is_authenticated: bool, + pub version_info: &'static crate::VersionInfo, + pub id: String, + pub category: String, + pub make: String, + pub model: String, + pub image_url: Option, +} + #[derive(Template)] #[template(path = "partials/image_upload.html")] pub struct ImageUploadTemplate<'a> { diff --git a/templates/pages/edit_bag.html b/templates/pages/edit_bag.html new file mode 100644 index 0000000..ddb43e9 --- /dev/null +++ b/templates/pages/edit_bag.html @@ -0,0 +1,99 @@ +{% extends "base.html" %} +{% import "partials/icons.html" as icons %} +{% block title %}Brewlog · Edit Bag{% endblock %} + +{% block content %} +
+

Edit Bag

+

Update bag details.

+
+ +
+
+
+ Roast* + + {% for roast in roast_options %} + + {% endfor %} + +
+
+ + +
+ +
+ +
+
+
+{% endblock %} diff --git a/templates/pages/edit_brew.html b/templates/pages/edit_brew.html new file mode 100644 index 0000000..3186979 --- /dev/null +++ b/templates/pages/edit_brew.html @@ -0,0 +1,341 @@ +{% extends "base.html" %} +{% import "partials/icons.html" as icons %} +{% import "partials/image_section.html" as img %} +{% block title %}Brewlog · Edit Brew{% endblock %} + +{% block content %} +
+

Edit Brew

+

Update brew details.

+
+ +
+
+ +
+

Coffee

+
+
+ Bag* + + {% for bag in bag_options %} + + {% endfor %} + +
+
+ Weight (g)* +
+ + + +
+
+
+
+ + +
+

Grinder

+
+ +
+ Grind Setting* +
+ + + +
+
+
+
+ + +
+

Brewer

+
+ + +
+
+ + +
+

Recipe

+
+
+ Volume (ml)* +
+ + + +
+
+
+ Temp (°C)* +
+ + + +
+
+
+ Time (s) +
+ + + +
+
+
+
+ + +
+

Quick Notes

+ +
+ + {{ img::deferred_upload("edit-brew-image", "Brew Image") }} + +
+ +
+
+
+{% endblock %} diff --git a/templates/pages/edit_cafe.html b/templates/pages/edit_cafe.html new file mode 100644 index 0000000..6112002 --- /dev/null +++ b/templates/pages/edit_cafe.html @@ -0,0 +1,142 @@ +{% extends "base.html" %} +{% import "partials/icons.html" as icons %} +{% import "partials/image_section.html" as img %} +{% block title %}Brewlog · Edit Cafe{% endblock %} + +{% block content %} +
+

Edit Cafe

+

Update cafe details.

+
+ +
+
+
+ + + + + + +
+ {{ img::deferred_upload("edit-cafe-image", "Cafe Image") }} + +
+ +
+
+
+{% endblock %} diff --git a/templates/pages/edit_cup.html b/templates/pages/edit_cup.html new file mode 100644 index 0000000..aacf872 --- /dev/null +++ b/templates/pages/edit_cup.html @@ -0,0 +1,96 @@ +{% extends "base.html" %} +{% import "partials/icons.html" as icons %} +{% import "partials/image_section.html" as img %} +{% block title %}Brewlog · Edit Cup{% endblock %} + +{% block content %} +
+

Edit Cup

+

Update cup details.

+
+ +
+
+
+
+ Coffee* + + {% for roast in roast_options %} + + {% endfor %} + +
+
+ Cafe* + + {% for cafe in cafe_options %} + + {% endfor %} + +
+
+ {{ img::deferred_upload("edit-cup-image", "Cup Image") }} + +
+ +
+
+
+{% endblock %} diff --git a/templates/pages/edit_gear.html b/templates/pages/edit_gear.html new file mode 100644 index 0000000..2ff71ee --- /dev/null +++ b/templates/pages/edit_gear.html @@ -0,0 +1,88 @@ +{% extends "base.html" %} +{% import "partials/icons.html" as icons %} +{% import "partials/image_section.html" as img %} +{% block title %}Brewlog · Edit Gear{% endblock %} + +{% block content %} +
+

Edit Gear

+

Update gear details.

+
+ +
+
+
+
+ Category + {{ category }} +
+ + +
+ {{ img::deferred_upload("edit-gear-image", "Gear Image") }} + +
+ +
+
+
+{% endblock %} diff --git a/templates/pages/edit_roast.html b/templates/pages/edit_roast.html new file mode 100644 index 0000000..1eabd74 --- /dev/null +++ b/templates/pages/edit_roast.html @@ -0,0 +1,164 @@ +{% extends "base.html" %} +{% import "partials/icons.html" as icons %} +{% import "partials/image_section.html" as img %} +{% block title %}Brewlog · Edit Roast{% endblock %} + +{% block content %} +
+

Edit Roast

+

Update roast details.

+
+ +
+
+
+ Roaster* + + {% for roaster in roaster_options %} + + {% endfor %} + +
+
+ + + + + + +
+ {{ img::deferred_upload("edit-roast-image", "Roast Image") }} + +
+ +
+
+
+{% endblock %} diff --git a/templates/pages/edit_roaster.html b/templates/pages/edit_roaster.html new file mode 100644 index 0000000..a99d7ba --- /dev/null +++ b/templates/pages/edit_roaster.html @@ -0,0 +1,106 @@ +{% extends "base.html" %} +{% import "partials/icons.html" as icons %} +{% import "partials/image_section.html" as img %} +{% block title %}Brewlog · Edit Roaster{% endblock %} + +{% block content %} +
+

Edit Roaster

+

Update roaster details.

+
+ +
+
+
+ + + + +
+ {{ img::deferred_upload("edit-roaster-image", "Roaster Image") }} + +
+ +
+
+
+{% endblock %}