app

Local-first trade for farms and co-ops
git clone https://radroots.dev/git/app.git
Log | Files | Refs | README | LICENSE

commit ee0f4439b3e7a398a4ee84e44191ad43150033ec
parent 9f814f5fcdf7010b476624123c5d31da005f8bc8
Author: triesap <tyson@radroots.org>
Date:   Sun,  7 Jun 2026 15:51:24 -0700

ui: expand farm details scroll view

Diffstat:
Mcrates/desktop/src/source_guards.rs | 95+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/desktop/src/window.rs | 978++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
Mcrates/i18n/src/keys.rs | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcrates/i18n/src/lib.rs | 20++++++++++++++++++++
Mi18n/locales/en/messages.json | 93+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
5 files changed, 1227 insertions(+), 52 deletions(-)

diff --git a/crates/desktop/src/source_guards.rs b/crates/desktop/src/source_guards.rs @@ -56,6 +56,8 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[ "account-settings-save-draft", "account-farm-save", "account-farm-save-draft", + "account-farm-add-pickup-window", + "account-farm-profile-preview", "account-farm-view-profile", "account-scroll", "account-tabs", @@ -454,6 +456,99 @@ const REQUIRED_WINDOW_COPY_KEYS: &[&str] = &[ "AppTextKey::AccountFarmDetailsFarmTypeNurseryPlantFarm", "AppTextKey::AccountFarmDetailsFarmTypeMixedFarm", "AppTextKey::AccountFarmDetailsFarmTypeOther", + "AppTextKey::AccountFarmDetailsLocationTitle", + "AppTextKey::AccountFarmDetailsLocationIntro", + "AppTextKey::AccountFarmDetailsMapNotImplemented", + "AppTextKey::AccountFarmDetailsStreetAddressLabel", + "AppTextKey::AccountFarmDetailsStreetAddressValue", + "AppTextKey::AccountFarmDetailsCityLabel", + "AppTextKey::AccountFarmDetailsCityValue", + "AppTextKey::AccountFarmDetailsProvinceLabel", + "AppTextKey::AccountFarmDetailsProvinceBritishColumbia", + "AppTextKey::AccountFarmDetailsProvinceAlberta", + "AppTextKey::AccountFarmDetailsPostalCodeLabel", + "AppTextKey::AccountFarmDetailsPostalCodeValue", + "AppTextKey::AccountFarmDetailsCountryLabel", + "AppTextKey::AccountFarmDetailsCountryCanada", + "AppTextKey::AccountFarmDetailsCountryUnitedStates", + "AppTextKey::AccountFarmDetailsServiceAreaLabel", + "AppTextKey::AccountFarmDetailsServiceAreaValue", + "AppTextKey::AccountFarmDetailsServiceAreaHelper", + "AppTextKey::AccountFarmDetailsExactAddressPublicLabel", + "AppTextKey::AccountFarmDetailsExactAddressPublicHelper", + "AppTextKey::AccountFarmDetailsLocationPreviewTitle", + "AppTextKey::AccountFarmDetailsLocationPreviewHelper", + "AppTextKey::AccountFarmDetailsOperatingTitle", + "AppTextKey::AccountFarmDetailsOperatingIntro", + "AppTextKey::AccountFarmDetailsGrowingPracticesLabel", + "AppTextKey::AccountFarmDetailsGrowingPracticeRegenerative", + "AppTextKey::AccountFarmDetailsGrowingPracticeOrganic", + "AppTextKey::AccountFarmDetailsProductionMethodsLabel", + "AppTextKey::AccountFarmDetailsProductionMethodOrganicPractices", + "AppTextKey::AccountFarmDetailsProductionMethodNoSpray", + "AppTextKey::AccountFarmDetailsSeasonDatesLabel", + "AppTextKey::AccountFarmDetailsSeasonStartValue", + "AppTextKey::AccountFarmDetailsSeasonEndValue", + "AppTextKey::AccountFarmDetailsOrderDaysLabel", + "AppTextKey::AccountFarmDetailsOrderDaysSummaryValue", + "AppTextKey::AccountFarmDetailsDayMon", + "AppTextKey::AccountFarmDetailsDayTue", + "AppTextKey::AccountFarmDetailsDayWed", + "AppTextKey::AccountFarmDetailsDayThu", + "AppTextKey::AccountFarmDetailsDayFri", + "AppTextKey::AccountFarmDetailsDaySat", + "AppTextKey::AccountFarmDetailsDaySun", + "AppTextKey::AccountFarmDetailsAboutProductsLabel", + "AppTextKey::AccountFarmDetailsAboutProductsValue", + "AppTextKey::AccountFarmDetailsCertificationsTitle", + "AppTextKey::AccountFarmDetailsCertificationsHelper", + "AppTextKey::AccountFarmDetailsCertificationCertifiedOrganic", + "AppTextKey::AccountFarmDetailsCertificationNaturallyGrown", + "AppTextKey::AccountFarmDetailsCertificationSmallFamilyFarm", + "AppTextKey::AccountFarmDetailsCertificationDeliveryAvailable", + "AppTextKey::AccountFarmDetailsCustomerNoteTitle", + "AppTextKey::AccountFarmDetailsCustomerNoteHelper", + "AppTextKey::AccountFarmDetailsCustomerNoteValue", + "AppTextKey::AccountFarmDetailsProfilePreviewTitle", + "AppTextKey::AccountFarmDetailsGrowingPracticesSummaryLabel", + "AppTextKey::AccountFarmDetailsSeasonSummaryLabel", + "AppTextKey::AccountFarmDetailsOrderDaysSummaryLabel", + "AppTextKey::AccountFarmDetailsPickupFulfillmentTitle", + "AppTextKey::AccountFarmDetailsPickupFulfillmentIntro", + "AppTextKey::AccountFarmDetailsFulfillmentModeLabel", + "AppTextKey::AccountFarmDetailsFulfillmentPickupOnly", + "AppTextKey::AccountFarmDetailsFulfillmentDelivery", + "AppTextKey::AccountFarmDetailsFulfillmentBoth", + "AppTextKey::AccountFarmDetailsPrimaryPickupLocationLabel", + "AppTextKey::AccountFarmDetailsPrimaryPickupLocationTitleValue", + "AppTextKey::AccountFarmDetailsPrimaryPickupLocationAddressValue", + "AppTextKey::AccountFarmDetailsPickupInstructionsLabel", + "AppTextKey::AccountFarmDetailsPickupInstructionsValue", + "AppTextKey::AccountFarmDetailsPickupInstructionsHelper", + "AppTextKey::AccountFarmDetailsPickupWindowsLabel", + "AppTextKey::AccountFarmDetailsPickupWindowDayHeader", + "AppTextKey::AccountFarmDetailsPickupWindowStartHeader", + "AppTextKey::AccountFarmDetailsPickupWindowEndHeader", + "AppTextKey::AccountFarmDetailsPickupWindowWednesday", + "AppTextKey::AccountFarmDetailsPickupWindowSaturday", + "AppTextKey::AccountFarmDetailsPickupWindowWednesdayStart", + "AppTextKey::AccountFarmDetailsPickupWindowWednesdayEnd", + "AppTextKey::AccountFarmDetailsPickupWindowSaturdayStart", + "AppTextKey::AccountFarmDetailsPickupWindowSaturdayEnd", + "AppTextKey::AccountFarmDetailsAddPickupWindowAction", + "AppTextKey::AccountFarmDetailsOrderCutoffLabel", + "AppTextKey::AccountFarmDetailsOrderCutoffHelper", + "AppTextKey::AccountFarmDetailsOrderCutoffNoonValue", + "AppTextKey::AccountFarmDetailsDeliveryRadiusTitle", + "AppTextKey::AccountFarmDetailsDeliveryRadiusHelper", + "AppTextKey::AccountFarmDetailsDeliveryRadiusValue", + "AppTextKey::AccountFarmDetailsDeliveryRadiusUnit", + "AppTextKey::AccountFarmDetailsDeliveryRadiusNote", + "AppTextKey::AccountFarmDetailsCustomerExperienceTitle", + "AppTextKey::AccountFarmDetailsCustomerExperienceIntro", + "AppTextKey::AccountFarmDetailsCustomerExperiencePickupTitle", + "AppTextKey::AccountFarmDetailsCustomerExperienceDeliveryTitle", + "AppTextKey::AccountFarmDetailsCustomerExperienceDeliveryBody", "AppTextKey::AccountSettingsTitle", "AppTextKey::AccountSettingsNostrRelaysTitle", "AppTextKey::AccountSettingsNostrRelaysHelper", diff --git a/crates/desktop/src/window.rs b/crates/desktop/src/window.rs @@ -338,6 +338,21 @@ struct AccountFarmProfileFormState { established_year_input: Entity<InputState>, about_farm_input: Entity<InputState>, farm_type_select: Entity<AccountFarmProfileSelectState>, + street_address_input: Entity<InputState>, + city_input: Entity<InputState>, + postal_code_input: Entity<InputState>, + province_select: Entity<AccountFarmProfileSelectState>, + country_select: Entity<AccountFarmProfileSelectState>, + service_area_select: Entity<AccountFarmProfileSelectState>, + growing_practices_select: Entity<AccountFarmProfileSelectState>, + season_start_input: Entity<InputState>, + season_end_input: Entity<InputState>, + about_products_input: Entity<InputState>, + customer_note_input: Entity<InputState>, + primary_pickup_location_select: Entity<AccountFarmProfileSelectState>, + pickup_instructions_input: Entity<InputState>, + order_cutoff_select: Entity<AccountFarmProfileSelectState>, + delivery_radius_input: Entity<InputState>, } impl AccountFarmProfileFormState { @@ -403,6 +418,90 @@ impl AccountFarmProfileFormState { window, cx, ), + street_address_input: account_profile_input_state( + AppTextKey::AccountFarmDetailsStreetAddressValue, + window, + cx, + ), + city_input: account_profile_input_state( + AppTextKey::AccountFarmDetailsCityValue, + window, + cx, + ), + postal_code_input: account_profile_input_state( + AppTextKey::AccountFarmDetailsPostalCodeValue, + window, + cx, + ), + province_select: account_farm_profile_select_state( + &[ + AppTextKey::AccountFarmDetailsProvinceBritishColumbia, + AppTextKey::AccountFarmDetailsProvinceAlberta, + ], + window, + cx, + ), + country_select: account_farm_profile_select_state( + &[ + AppTextKey::AccountFarmDetailsCountryCanada, + AppTextKey::AccountFarmDetailsCountryUnitedStates, + ], + window, + cx, + ), + service_area_select: account_farm_profile_select_state( + &[AppTextKey::AccountFarmDetailsServiceAreaValue], + window, + cx, + ), + growing_practices_select: account_farm_profile_select_state( + &[ + AppTextKey::AccountFarmDetailsGrowingPracticeRegenerative, + AppTextKey::AccountFarmDetailsGrowingPracticeOrganic, + ], + window, + cx, + ), + season_start_input: account_profile_input_state( + AppTextKey::AccountFarmDetailsSeasonStartValue, + window, + cx, + ), + season_end_input: account_profile_input_state( + AppTextKey::AccountFarmDetailsSeasonEndValue, + window, + cx, + ), + about_products_input: account_profile_autogrow_input_state( + AppTextKey::AccountFarmDetailsAboutProductsValue, + window, + cx, + ), + customer_note_input: account_profile_autogrow_input_state( + AppTextKey::AccountFarmDetailsCustomerNoteValue, + window, + cx, + ), + primary_pickup_location_select: account_farm_profile_select_state( + &[AppTextKey::AccountFarmDetailsPrimaryPickupLocationTitleValue], + window, + cx, + ), + pickup_instructions_input: account_profile_autogrow_input_state( + AppTextKey::AccountFarmDetailsPickupInstructionsValue, + window, + cx, + ), + order_cutoff_select: account_farm_profile_select_state( + &[AppTextKey::AccountFarmDetailsOrderCutoffNoonValue], + window, + cx, + ), + delivery_radius_input: account_profile_input_state( + AppTextKey::AccountFarmDetailsDeliveryRadiusValue, + window, + cx, + ), } } @@ -440,6 +539,57 @@ impl AccountFarmProfileFormState { AppTextKey::AccountFarmDetailsAboutFarmValue, cx, ) || account_select_is_dirty(&self.farm_type_select, cx) + || account_input_is_dirty( + &self.street_address_input, + AppTextKey::AccountFarmDetailsStreetAddressValue, + cx, + ) + || account_input_is_dirty( + &self.city_input, + AppTextKey::AccountFarmDetailsCityValue, + cx, + ) + || account_input_is_dirty( + &self.postal_code_input, + AppTextKey::AccountFarmDetailsPostalCodeValue, + cx, + ) + || account_select_is_dirty(&self.province_select, cx) + || account_select_is_dirty(&self.country_select, cx) + || account_select_is_dirty(&self.service_area_select, cx) + || account_select_is_dirty(&self.growing_practices_select, cx) + || account_input_is_dirty( + &self.season_start_input, + AppTextKey::AccountFarmDetailsSeasonStartValue, + cx, + ) + || account_input_is_dirty( + &self.season_end_input, + AppTextKey::AccountFarmDetailsSeasonEndValue, + cx, + ) + || account_input_is_dirty( + &self.about_products_input, + AppTextKey::AccountFarmDetailsAboutProductsValue, + cx, + ) + || account_input_is_dirty( + &self.customer_note_input, + AppTextKey::AccountFarmDetailsCustomerNoteValue, + cx, + ) + || account_select_is_dirty(&self.primary_pickup_location_select, cx) + || account_input_is_dirty( + &self.pickup_instructions_input, + AppTextKey::AccountFarmDetailsPickupInstructionsValue, + cx, + ) + || account_select_is_dirty(&self.order_cutoff_select, cx) + || account_input_is_dirty( + &self.delivery_radius_input, + AppTextKey::AccountFarmDetailsDeliveryRadiusValue, + cx, + ) } } @@ -9598,37 +9748,45 @@ fn account_farm_profile_panel( cx, )), )) - .child( - div() + .child(account_farm_profile_section_row( + account_farm_profile_main_card(form, is_textarea_wrap_ready, cx), + app_stack_v(APP_UI_THEME.shells.home_stack_gap_px) .w_full() - .flex() - .items_start() - .gap(px(APP_UI_THEME.shells.home_stack_gap_px)) - .child( - div() - .flex_1() - .min_w_0() - .child(account_farm_profile_main_card( - form, - is_textarea_wrap_ready, - cx, - )), - ) - .child( - app_stack_v(APP_UI_THEME.shells.home_stack_gap_px) - .w(px(336.0)) - .min_w(px(300.0)) - .child(account_farm_profile_completeness_card()) - .child(account_farm_profile_summary_card(cx)), - ), - ) + .child(account_farm_profile_completeness_card()) + .child(account_farm_profile_summary_card(cx)), + )) + .child(account_farm_profile_section_row( + account_farm_location_card(form), + account_farm_location_preview_card(), + )) + .child(account_farm_profile_section_row( + account_farm_operating_card(form, is_textarea_wrap_ready, cx), + account_farm_profile_preview_card(cx), + )) + .child(account_farm_profile_section_row( + account_farm_fulfillment_card(form, is_textarea_wrap_ready, cx), + account_farm_customer_experience_card(), + )) +} + +fn account_farm_profile_section_row( + main: impl IntoElement, + rail: impl IntoElement, +) -> impl IntoElement { + div() + .w_full() + .flex() + .items_start() + .gap(px(APP_UI_THEME.shells.home_stack_gap_px)) + .child(div().flex_1().min_w_0().child(main)) + .child(div().w(px(336.0)).min_w(px(300.0)).child(rail)) } fn account_farm_profile_main_card( form: &AccountFarmProfileFormState, is_textarea_wrap_ready: bool, cx: &mut Context<HomeView>, -) -> impl IntoElement { +) -> impl IntoElement + use<> { account_farm_profile_card( app_stack_v(APP_UI_THEME.shells.home_stack_gap_px) .w_full() @@ -9694,57 +9852,545 @@ fn account_farm_profile_main_card( ) } -fn account_farm_profile_card(content: impl IntoElement) -> impl IntoElement { +fn account_farm_location_card(form: &AccountFarmProfileFormState) -> impl IntoElement { + account_farm_profile_card( + app_stack_v(APP_UI_THEME.shells.home_stack_gap_px) + .w_full() + .child(account_farm_profile_title_block( + AppTextKey::AccountFarmDetailsLocationTitle, + AppTextKey::AccountFarmDetailsLocationIntro, + )) + .child(account_farm_map_placeholder()) + .child(account_farm_profile_field_row( + account_profile_input_field( + AppTextKey::AccountFarmDetailsStreetAddressLabel, + &form.street_address_input, + ), + account_profile_input_field( + AppTextKey::AccountFarmDetailsCityLabel, + &form.city_input, + ), + )) + .child(account_farm_profile_field_row( + account_farm_profile_select_field( + AppTextKey::AccountFarmDetailsProvinceLabel, + &form.province_select, + ), + account_profile_input_field( + AppTextKey::AccountFarmDetailsPostalCodeLabel, + &form.postal_code_input, + ), + )) + .child(account_farm_profile_field_row( + account_farm_profile_select_field( + AppTextKey::AccountFarmDetailsCountryLabel, + &form.country_select, + ), + account_farm_profile_select_field( + AppTextKey::AccountFarmDetailsServiceAreaLabel, + &form.service_area_select, + ), + )) + .child(account_farm_profile_helper_text( + AppTextKey::AccountFarmDetailsServiceAreaHelper, + )) + .child(account_farm_toggle_preview_row( + AppTextKey::AccountFarmDetailsExactAddressPublicLabel, + AppTextKey::AccountFarmDetailsExactAddressPublicHelper, + )), + ) +} + +fn account_farm_map_placeholder() -> impl IntoElement { div() .w_full() + .h(px(220.0)) + .rounded(px(APP_UI_THEME.foundation.radii.large_px)) .border_1() .border_color(rgb(APP_UI_THEME.foundation.surfaces.divider)) - .rounded(px(APP_UI_THEME.foundation.radii.large_px)) - .bg(transparent_black()) + .bg(rgb(APP_UI_THEME.foundation.surfaces.card_background)) + .flex() + .items_center() + .justify_center() .child( div() - .w_full() - .p(px(APP_UI_THEME.shells.home_card_padding_px)) - .child(content), + .text_size(px(APP_UI_THEME.foundation.typography.body_text_px)) + .font_weight(gpui::FontWeight::MEDIUM) + .text_color(rgb(APP_UI_THEME.foundation.text.secondary)) + .child(app_shared_text( + AppTextKey::AccountFarmDetailsMapNotImplemented, + )), ) } -fn account_farm_profile_title_block( - title_key: AppTextKey, - body_key: AppTextKey, -) -> impl IntoElement { - app_stack_v(8.0) +fn account_farm_location_preview_card() -> impl IntoElement { + account_farm_profile_card( + app_stack_v(APP_UI_THEME.shells.home_stack_gap_px) + .w_full() + .child(account_farm_preview_title( + AppTextKey::AccountFarmDetailsLocationPreviewTitle, + )) + .child(account_farm_icon_value_row( + IconName::Map, + AppTextKey::AccountFarmDetailsFarmLocationValue, + AppTextKey::AccountFarmDetailsServiceAreaValue, + )) + .child(account_farm_service_area_preview()) + .child(account_farm_profile_helper_text( + AppTextKey::AccountFarmDetailsLocationPreviewHelper, + )), + ) +} + +fn account_farm_service_area_preview() -> impl IntoElement { + div() .w_full() + .h(px(132.0)) + .rounded(px(APP_UI_THEME.foundation.radii.medium_px)) + .bg(rgb(APP_UI_THEME.foundation.surfaces.card_background)) + .border_1() + .border_color(rgb(APP_UI_THEME.foundation.surfaces.divider)) + .flex() + .items_center() + .justify_center() .child( div() - .w_full() - .text_size(px(APP_UI_THEME.foundation.typography.body_text_px * 1.1)) - .font_weight(gpui::FontWeight::BOLD) - .text_color(rgb(APP_UI_THEME.foundation.text.primary)) - .child(app_shared_text(title_key)), - ) - .child( - div() - .w_full() - .line_height(relative(1.35)) .text_size(px(APP_UI_THEME.foundation.typography.body_text_px)) + .font_weight(gpui::FontWeight::MEDIUM) .text_color(rgb(APP_UI_THEME.foundation.text.secondary)) - .child(app_shared_text(body_key)), + .child(app_shared_text( + AppTextKey::AccountFarmDetailsMapNotImplemented, + )), ) } -fn account_farm_profile_field_row( - first: impl IntoElement, - second: impl IntoElement, +fn account_farm_operating_card( + form: &AccountFarmProfileFormState, + is_textarea_wrap_ready: bool, + cx: &mut Context<HomeView>, +) -> impl IntoElement + use<> { + account_farm_profile_card( + app_stack_v(APP_UI_THEME.shells.home_stack_gap_px) + .w_full() + .child(account_farm_profile_title_block( + AppTextKey::AccountFarmDetailsOperatingTitle, + AppTextKey::AccountFarmDetailsOperatingIntro, + )) + .child(account_farm_profile_field_row( + account_farm_profile_select_field( + AppTextKey::AccountFarmDetailsGrowingPracticesLabel, + &form.growing_practices_select, + ), + account_farm_static_chip_group( + AppTextKey::AccountFarmDetailsProductionMethodsLabel, + &[ + AppTextKey::AccountFarmDetailsProductionMethodOrganicPractices, + AppTextKey::AccountFarmDetailsProductionMethodNoSpray, + AppTextKey::AccountFarmDetailsGrowingPracticeRegenerative, + ], + 3, + ), + )) + .child(account_farm_profile_field_row( + account_farm_date_range_fields( + AppTextKey::AccountFarmDetailsSeasonDatesLabel, + &form.season_start_input, + &form.season_end_input, + ), + account_farm_day_chip_group(), + )) + .child(account_farm_profile_text_area_field( + AppTextKey::AccountFarmDetailsAboutProductsLabel, + &form.about_products_input, + is_textarea_wrap_ready, + cx, + )) + .child(account_farm_static_chip_group_with_helper( + AppTextKey::AccountFarmDetailsCertificationsTitle, + AppTextKey::AccountFarmDetailsCertificationsHelper, + &[ + AppTextKey::AccountFarmDetailsCertificationCertifiedOrganic, + AppTextKey::AccountFarmDetailsCertificationNaturallyGrown, + AppTextKey::AccountFarmDetailsCertificationSmallFamilyFarm, + AppTextKey::AccountFarmDetailsCertificationDeliveryAvailable, + ], + 3, + )) + .child(account_farm_profile_text_area_field_with_helper( + AppTextKey::AccountFarmDetailsCustomerNoteTitle, + AppTextKey::AccountFarmDetailsCustomerNoteHelper, + &form.customer_note_input, + is_textarea_wrap_ready, + cx, + )), + ) +} + +fn account_farm_profile_preview_card(cx: &mut Context<HomeView>) -> impl IntoElement + use<> { + account_farm_profile_card( + app_stack_v(APP_UI_THEME.shells.home_stack_gap_px) + .w_full() + .child(account_farm_preview_title( + AppTextKey::AccountFarmDetailsProfilePreviewTitle, + )) + .child(account_farm_icon_value_row( + IconName::Building2, + AppTextKey::AccountFarmDetailsFarmNameValue, + AppTextKey::AccountFarmDetailsFarmLocationValue, + )) + .child(account_farm_profile_summary_row( + AppTextKey::AccountFarmDetailsGrowingPracticesSummaryLabel, + AppTextKey::AccountFarmDetailsGrowingPracticeRegenerative, + )) + .child(account_farm_profile_summary_row( + AppTextKey::AccountFarmDetailsSeasonSummaryLabel, + AppTextKey::AccountFarmDetailsSeasonStartValue, + )) + .child(account_farm_profile_summary_row( + AppTextKey::AccountFarmDetailsOrderDaysSummaryLabel, + AppTextKey::AccountFarmDetailsOrderDaysSummaryValue, + )) + .child(account_farm_profile_summary_row( + AppTextKey::AccountFarmDetailsFarmTypeSummaryLabel, + AppTextKey::AccountFarmDetailsFarmTypeVegetableFarm, + )) + .child(action_button_full_width( + "account-farm-profile-preview", + app_shared_text(AppTextKey::AccountFarmDetailsViewFarmProfileAction), + |_, _, _| {}, + cx, + )), + ) +} + +fn account_farm_fulfillment_card( + form: &AccountFarmProfileFormState, + is_textarea_wrap_ready: bool, + cx: &mut Context<HomeView>, +) -> impl IntoElement + use<> { + account_farm_profile_card( + app_stack_v(APP_UI_THEME.shells.home_stack_gap_px) + .w_full() + .child(account_farm_profile_title_block( + AppTextKey::AccountFarmDetailsPickupFulfillmentTitle, + AppTextKey::AccountFarmDetailsPickupFulfillmentIntro, + )) + .child(account_farm_static_chip_group( + AppTextKey::AccountFarmDetailsFulfillmentModeLabel, + &[ + AppTextKey::AccountFarmDetailsFulfillmentPickupOnly, + AppTextKey::AccountFarmDetailsFulfillmentDelivery, + AppTextKey::AccountFarmDetailsFulfillmentBoth, + ], + 2, + )) + .child(account_farm_profile_labeled_control_with_helper( + AppTextKey::AccountFarmDetailsPrimaryPickupLocationLabel, + account_farm_profile_select_input(&form.primary_pickup_location_select), + Some(AppTextKey::AccountFarmDetailsPrimaryPickupLocationAddressValue), + )) + .child(account_farm_profile_text_area_field_with_helper( + AppTextKey::AccountFarmDetailsPickupInstructionsLabel, + AppTextKey::AccountFarmDetailsPickupInstructionsHelper, + &form.pickup_instructions_input, + is_textarea_wrap_ready, + cx, + )) + .child(account_farm_pickup_schedule_row(form, cx)) + .child(account_farm_delivery_radius_row( + &form.delivery_radius_input, + )), + ) +} + +fn account_farm_pickup_schedule_row( + form: &AccountFarmProfileFormState, + cx: &mut Context<HomeView>, ) -> impl IntoElement { div() .w_full() .flex() .items_start() .gap(px(APP_UI_THEME.shells.home_stack_gap_px)) - .child(div().flex_1().min_w_0().child(first)) - .child(div().flex_1().min_w_0().child(second)) -} + .child( + app_stack_v(8.0) + .flex_1() + .min_w_0() + .child(account_farm_field_label( + AppTextKey::AccountFarmDetailsPickupWindowsLabel, + )) + .child(account_farm_pickup_windows_table()) + .child(div().w(px(180.0)).child(action_button_compact( + "account-farm-add-pickup-window", + app_shared_text(AppTextKey::AccountFarmDetailsAddPickupWindowAction), + |_, _, _| {}, + cx, + ))), + ) + .child( + div() + .w(px(176.0)) + .child(account_farm_profile_labeled_control_with_helper( + AppTextKey::AccountFarmDetailsOrderCutoffLabel, + account_farm_profile_select_input(&form.order_cutoff_select), + Some(AppTextKey::AccountFarmDetailsOrderCutoffHelper), + )), + ) +} + +fn account_farm_pickup_windows_table() -> impl IntoElement { + app_stack_v(0.0) + .w_full() + .border_1() + .border_color(rgb(APP_UI_THEME.foundation.surfaces.divider)) + .rounded(px(APP_UI_THEME.foundation.radii.medium_px)) + .overflow_hidden() + .child(account_farm_pickup_window_table_row( + AppTextKey::AccountFarmDetailsPickupWindowDayHeader, + AppTextKey::AccountFarmDetailsPickupWindowStartHeader, + AppTextKey::AccountFarmDetailsPickupWindowEndHeader, + false, + )) + .child(account_farm_pickup_window_table_row( + AppTextKey::AccountFarmDetailsPickupWindowWednesday, + AppTextKey::AccountFarmDetailsPickupWindowWednesdayStart, + AppTextKey::AccountFarmDetailsPickupWindowWednesdayEnd, + true, + )) + .child(account_farm_pickup_window_table_row( + AppTextKey::AccountFarmDetailsPickupWindowSaturday, + AppTextKey::AccountFarmDetailsPickupWindowSaturdayStart, + AppTextKey::AccountFarmDetailsPickupWindowSaturdayEnd, + true, + )) +} + +fn account_farm_pickup_window_table_row( + day_key: AppTextKey, + start_key: AppTextKey, + end_key: AppTextKey, + action: bool, +) -> impl IntoElement { + let bg = if action { + APP_UI_THEME.foundation.surfaces.window_background + } else { + APP_UI_THEME.foundation.surfaces.card_background + }; + + div() + .w_full() + .bg(rgb(bg)) + .px(px(10.0)) + .py(px(7.0)) + .flex() + .items_center() + .gap(px(10.0)) + .child(account_farm_table_cell(day_key, 1.0)) + .child(account_farm_table_cell(start_key, 1.0)) + .child(account_farm_table_cell(end_key, 1.0)) + .when(action, |this| { + this.child( + div() + .flex_none() + .text_color(rgb(APP_UI_THEME.foundation.text.secondary)) + .child(Icon::new(IconName::Ellipsis).with_size(gpui_component::Size::Small)), + ) + }) +} + +fn account_farm_table_cell(label_key: AppTextKey, basis: f32) -> impl IntoElement { + div() + .flex_basis(relative(basis)) + .min_w_0() + .text_size(px(APP_UI_THEME.foundation.typography.settings_row_text_px)) + .text_color(rgb(APP_UI_THEME.foundation.text.primary)) + .child(app_shared_text(label_key)) +} + +fn account_farm_delivery_radius_row(input: &Entity<InputState>) -> impl IntoElement { + app_stack_v(8.0) + .w_full() + .pt(px(APP_UI_THEME.shells.home_stack_gap_px)) + .child(account_farm_profile_title_block( + AppTextKey::AccountFarmDetailsDeliveryRadiusTitle, + AppTextKey::AccountFarmDetailsDeliveryRadiusHelper, + )) + .child( + div() + .w_full() + .flex() + .items_center() + .gap(px(APP_UI_THEME.shells.home_stack_gap_px)) + .child(account_farm_slider_preview()) + .child(div().w(px(74.0)).child(account_form_text_input(input))) + .child( + div() + .text_size(px(APP_UI_THEME.foundation.typography.settings_row_text_px)) + .text_color(rgb(APP_UI_THEME.foundation.text.secondary)) + .child(app_shared_text( + AppTextKey::AccountFarmDetailsDeliveryRadiusUnit, + )), + ), + ) + .child(account_farm_profile_helper_text( + AppTextKey::AccountFarmDetailsDeliveryRadiusNote, + )) +} + +fn account_farm_slider_preview() -> impl IntoElement { + div() + .flex_1() + .min_w_0() + .h(px(4.0)) + .rounded(px(2.0)) + .bg(rgb(APP_UI_THEME.foundation.surfaces.divider)) + .child(div().w(relative(0.38)).h(px(4.0)).rounded(px(2.0)).bg(rgb( + APP_UI_THEME.components.app_button.primary_colors.background, + ))) +} + +fn account_farm_customer_experience_card() -> impl IntoElement { + account_farm_profile_card( + app_stack_v(APP_UI_THEME.shells.home_stack_gap_px) + .w_full() + .child(account_farm_preview_title( + AppTextKey::AccountFarmDetailsCustomerExperienceTitle, + )) + .child(account_farm_profile_helper_text( + AppTextKey::AccountFarmDetailsCustomerExperienceIntro, + )) + .child(account_farm_customer_experience_panel( + AppTextKey::AccountFarmDetailsCustomerExperiencePickupTitle, + AppTextKey::AccountFarmDetailsPrimaryPickupLocationTitleValue, + AppTextKey::AccountFarmDetailsPrimaryPickupLocationAddressValue, + )) + .child(account_farm_customer_experience_panel( + AppTextKey::AccountFarmDetailsCustomerExperienceDeliveryTitle, + AppTextKey::AccountFarmDetailsCustomerExperienceDeliveryBody, + AppTextKey::AccountFarmDetailsServiceAreaValue, + )), + ) +} + +fn account_farm_customer_experience_panel( + title_key: AppTextKey, + primary_key: AppTextKey, + secondary_key: AppTextKey, +) -> impl IntoElement { + div() + .w_full() + .rounded(px(APP_UI_THEME.foundation.radii.medium_px)) + .bg(rgb(APP_UI_THEME.foundation.surfaces.card_background)) + .p(px(10.0)) + .child( + app_stack_v(4.0) + .w_full() + .child( + div() + .text_size(px(APP_UI_THEME.foundation.typography.settings_row_text_px)) + .font_weight(gpui::FontWeight::BOLD) + .text_color(rgb(APP_UI_THEME.foundation.text.primary)) + .child(app_shared_text(title_key)), + ) + .child( + div() + .text_size(px(APP_UI_THEME.foundation.typography.settings_row_text_px)) + .text_color(rgb(APP_UI_THEME.foundation.text.primary)) + .child(app_shared_text(primary_key)), + ) + .child( + div() + .text_size(px(APP_UI_THEME.foundation.typography.settings_row_text_px)) + .line_height(relative(1.25)) + .text_color(rgb(APP_UI_THEME.foundation.text.secondary)) + .child(app_shared_text(secondary_key)), + ), + ) +} + +fn account_farm_profile_card(content: impl IntoElement) -> impl IntoElement { + div() + .w_full() + .border_1() + .border_color(rgb(APP_UI_THEME.foundation.surfaces.divider)) + .rounded(px(APP_UI_THEME.foundation.radii.large_px)) + .bg(transparent_black()) + .child( + div() + .w_full() + .p(px(APP_UI_THEME.shells.home_card_padding_px)) + .child(content), + ) +} + +fn account_farm_profile_title_block( + title_key: AppTextKey, + body_key: AppTextKey, +) -> impl IntoElement { + app_stack_v(8.0) + .w_full() + .child( + div() + .w_full() + .text_size(px(APP_UI_THEME.foundation.typography.body_text_px * 1.1)) + .font_weight(gpui::FontWeight::BOLD) + .text_color(rgb(APP_UI_THEME.foundation.text.primary)) + .child(app_shared_text(title_key)), + ) + .child( + div() + .w_full() + .line_height(relative(1.35)) + .text_size(px(APP_UI_THEME.foundation.typography.body_text_px)) + .text_color(rgb(APP_UI_THEME.foundation.text.secondary)) + .child(app_shared_text(body_key)), + ) +} + +fn account_farm_profile_field_row( + first: impl IntoElement, + second: impl IntoElement, +) -> impl IntoElement { + div() + .w_full() + .flex() + .items_start() + .gap(px(APP_UI_THEME.shells.home_stack_gap_px)) + .child(div().flex_1().min_w_0().child(first)) + .child(div().flex_1().min_w_0().child(second)) +} + +fn account_farm_field_label(label_key: AppTextKey) -> impl IntoElement { + div() + .w_full() + .text_size(px(APP_UI_THEME.foundation.typography.utility_title_text_px)) + .font_weight(gpui::FontWeight::SEMIBOLD) + .text_color(rgb(APP_UI_THEME.foundation.text.secondary)) + .child(app_shared_text(label_key)) +} + +fn account_farm_profile_helper_text(text_key: AppTextKey) -> impl IntoElement { + div() + .w_full() + .line_height(relative(1.3)) + .text_size(px(APP_UI_THEME.foundation.typography.settings_row_text_px)) + .text_color(rgb(APP_UI_THEME.foundation.text.secondary)) + .child(app_shared_text(text_key)) +} + +fn account_farm_profile_labeled_control_with_helper( + label_key: AppTextKey, + control: impl IntoElement, + helper_key: Option<AppTextKey>, +) -> impl IntoElement { + app_stack_v(6.0) + .w_full() + .child(account_farm_field_label(label_key)) + .child(control) + .when_some(helper_key, |this, helper_key| { + this.child(account_farm_profile_helper_text(helper_key)) + }) +} fn account_farm_profile_select_field( label_key: AppTextKey, @@ -9765,6 +10411,234 @@ fn account_farm_profile_text_area_field( ) } +fn account_farm_profile_text_area_field_with_helper( + label_key: AppTextKey, + helper_key: AppTextKey, + input: &Entity<InputState>, + is_wrap_ready: bool, + cx: &mut Context<HomeView>, +) -> impl IntoElement { + account_farm_profile_labeled_control_with_helper( + label_key, + account_form_text_area_input_with_wrapped_preview(input, is_wrap_ready, cx), + Some(helper_key), + ) +} + +fn account_farm_toggle_preview_row( + label_key: AppTextKey, + helper_key: AppTextKey, +) -> impl IntoElement { + div() + .w_full() + .flex() + .items_start() + .gap(px(10.0)) + .child( + div() + .flex_none() + .w(px(34.0)) + .h(px(20.0)) + .rounded(px(10.0)) + .bg(rgb(APP_UI_THEME.foundation.surfaces.divider)) + .p(px(2.0)) + .child( + div() + .size(px(16.0)) + .rounded(px(8.0)) + .bg(rgb(APP_UI_THEME.foundation.surfaces.window_background)), + ), + ) + .child( + app_stack_v(3.0) + .flex_1() + .min_w_0() + .child( + div() + .text_size(px(APP_UI_THEME.foundation.typography.settings_row_text_px)) + .font_weight(gpui::FontWeight::MEDIUM) + .text_color(rgb(APP_UI_THEME.foundation.text.primary)) + .child(app_shared_text(label_key)), + ) + .child(account_farm_profile_helper_text(helper_key)), + ) +} + +fn account_farm_preview_title(title_key: AppTextKey) -> impl IntoElement { + div() + .w_full() + .text_size(px(APP_UI_THEME.foundation.typography.body_text_px * 1.1)) + .font_weight(gpui::FontWeight::BOLD) + .text_color(rgb(APP_UI_THEME.foundation.text.primary)) + .child(app_shared_text(title_key)) +} + +fn account_farm_icon_value_row( + icon_name: IconName, + title_key: AppTextKey, + subtitle_key: AppTextKey, +) -> impl IntoElement { + div() + .w_full() + .flex() + .items_center() + .gap(px(APP_UI_THEME.shells.home_stack_gap_px)) + .child( + div() + .size(px(46.0)) + .rounded(px(23.0)) + .bg(rgb(0xA7F3B8)) + .flex() + .items_center() + .justify_center() + .child( + Icon::new(icon_name) + .with_size(gpui_component::Size::Size(px(24.0))) + .text_color(rgb(APP_UI_THEME.foundation.text.primary)), + ), + ) + .child( + app_stack_v(3.0) + .flex_1() + .min_w_0() + .child( + div() + .text_size(px(APP_UI_THEME.foundation.typography.body_text_px)) + .font_weight(gpui::FontWeight::BOLD) + .text_color(rgb(APP_UI_THEME.foundation.text.primary)) + .child(app_shared_text(title_key)), + ) + .child( + div() + .text_size(px(APP_UI_THEME.foundation.typography.settings_row_text_px)) + .text_color(rgb(APP_UI_THEME.foundation.text.secondary)) + .child(app_shared_text(subtitle_key)), + ), + ) +} + +fn account_farm_static_chip_group( + label_key: AppTextKey, + chips: &[AppTextKey], + selected_count: usize, +) -> impl IntoElement { + account_farm_profile_labeled_control_with_helper( + label_key, + account_farm_chip_wrap(chips, selected_count), + None, + ) +} + +fn account_farm_static_chip_group_with_helper( + label_key: AppTextKey, + helper_key: AppTextKey, + chips: &[AppTextKey], + selected_count: usize, +) -> impl IntoElement { + account_farm_profile_labeled_control_with_helper( + label_key, + account_farm_chip_wrap(chips, selected_count), + Some(helper_key), + ) +} + +fn account_farm_chip_wrap(chips: &[AppTextKey], selected_count: usize) -> impl IntoElement { + div().w_full().flex().flex_wrap().gap(px(8.0)).children( + chips + .iter() + .enumerate() + .map(|(index, key)| account_farm_chip(*key, index < selected_count).into_any_element()), + ) +} + +fn account_farm_chip(label_key: AppTextKey, selected: bool) -> impl IntoElement { + let border = if selected { + APP_UI_THEME.components.app_button.primary_colors.background + } else { + APP_UI_THEME.foundation.surfaces.divider + }; + let text = if selected { + APP_UI_THEME.components.app_button.primary_colors.background + } else { + APP_UI_THEME.foundation.text.primary + }; + + div() + .rounded(px(APP_UI_THEME.foundation.radii.medium_px)) + .border_1() + .border_color(rgb(border)) + .bg(rgb(APP_UI_THEME.foundation.surfaces.window_background)) + .px(px(10.0)) + .py(px(6.0)) + .flex() + .items_center() + .gap(px(6.0)) + .when(selected, |this| { + this.child( + Icon::new(IconName::Check) + .with_size(gpui_component::Size::Small) + .text_color(rgb(text)), + ) + }) + .child( + div() + .text_size(px(APP_UI_THEME.foundation.typography.settings_row_text_px)) + .font_weight(gpui::FontWeight::MEDIUM) + .text_color(rgb(text)) + .child(app_shared_text(label_key)), + ) +} + +fn account_farm_day_chip_group() -> impl IntoElement { + account_farm_static_chip_group( + AppTextKey::AccountFarmDetailsOrderDaysLabel, + &[ + AppTextKey::AccountFarmDetailsDayMon, + AppTextKey::AccountFarmDetailsDayTue, + AppTextKey::AccountFarmDetailsDayWed, + AppTextKey::AccountFarmDetailsDayThu, + AppTextKey::AccountFarmDetailsDayFri, + AppTextKey::AccountFarmDetailsDaySat, + AppTextKey::AccountFarmDetailsDaySun, + ], + 5, + ) +} + +fn account_farm_date_range_fields( + label_key: AppTextKey, + start_input: &Entity<InputState>, + end_input: &Entity<InputState>, +) -> impl IntoElement { + app_stack_v(6.0) + .w_full() + .child(account_farm_field_label(label_key)) + .child( + div() + .w_full() + .flex() + .items_center() + .gap(px(8.0)) + .child( + div() + .flex_1() + .min_w_0() + .child(account_form_text_input(start_input)), + ) + .child( + Icon::new(IconName::ArrowRight) + .with_size(gpui_component::Size::Small) + .text_color(rgb(APP_UI_THEME.foundation.text.secondary)), + ) + .child( + div() + .flex_1() + .min_w_0() + .child(account_form_text_input(end_input)), + ), + ) +} + fn account_farm_profile_completeness_card() -> impl IntoElement { account_farm_profile_card( app_stack_v(APP_UI_THEME.shells.home_stack_gap_px) diff --git a/crates/i18n/src/keys.rs b/crates/i18n/src/keys.rs @@ -108,6 +108,99 @@ define_app_text_keys! { AccountFarmDetailsFarmTypeNurseryPlantFarm => "account.farm_details.farm_type.nursery_plant_farm", AccountFarmDetailsFarmTypeMixedFarm => "account.farm_details.farm_type.mixed_farm", AccountFarmDetailsFarmTypeOther => "account.farm_details.farm_type.other", + AccountFarmDetailsLocationTitle => "account.farm_details.location.title", + AccountFarmDetailsLocationIntro => "account.farm_details.location.intro", + AccountFarmDetailsMapNotImplemented => "account.farm_details.map.not_implemented", + AccountFarmDetailsStreetAddressLabel => "account.farm_details.street_address.label", + AccountFarmDetailsStreetAddressValue => "account.farm_details.street_address.value", + AccountFarmDetailsCityLabel => "account.farm_details.city.label", + AccountFarmDetailsCityValue => "account.farm_details.city.value", + AccountFarmDetailsProvinceLabel => "account.farm_details.province.label", + AccountFarmDetailsProvinceBritishColumbia => "account.farm_details.province.british_columbia", + AccountFarmDetailsProvinceAlberta => "account.farm_details.province.alberta", + AccountFarmDetailsPostalCodeLabel => "account.farm_details.postal_code.label", + AccountFarmDetailsPostalCodeValue => "account.farm_details.postal_code.value", + AccountFarmDetailsCountryLabel => "account.farm_details.country.label", + AccountFarmDetailsCountryCanada => "account.farm_details.country.canada", + AccountFarmDetailsCountryUnitedStates => "account.farm_details.country.united_states", + AccountFarmDetailsServiceAreaLabel => "account.farm_details.service_area.label", + AccountFarmDetailsServiceAreaValue => "account.farm_details.service_area.value", + AccountFarmDetailsServiceAreaHelper => "account.farm_details.service_area.helper", + AccountFarmDetailsExactAddressPublicLabel => "account.farm_details.exact_address_public.label", + AccountFarmDetailsExactAddressPublicHelper => "account.farm_details.exact_address_public.helper", + AccountFarmDetailsLocationPreviewTitle => "account.farm_details.location_preview.title", + AccountFarmDetailsLocationPreviewHelper => "account.farm_details.location_preview.helper", + AccountFarmDetailsOperatingTitle => "account.farm_details.operating.title", + AccountFarmDetailsOperatingIntro => "account.farm_details.operating.intro", + AccountFarmDetailsGrowingPracticesLabel => "account.farm_details.growing_practices.label", + AccountFarmDetailsGrowingPracticeRegenerative => "account.farm_details.growing_practice.regenerative", + AccountFarmDetailsGrowingPracticeOrganic => "account.farm_details.growing_practice.organic", + AccountFarmDetailsProductionMethodsLabel => "account.farm_details.production_methods.label", + AccountFarmDetailsProductionMethodOrganicPractices => "account.farm_details.production_method.organic_practices", + AccountFarmDetailsProductionMethodNoSpray => "account.farm_details.production_method.no_spray", + AccountFarmDetailsSeasonDatesLabel => "account.farm_details.season_dates.label", + AccountFarmDetailsSeasonStartValue => "account.farm_details.season_start.value", + AccountFarmDetailsSeasonEndValue => "account.farm_details.season_end.value", + AccountFarmDetailsOrderDaysLabel => "account.farm_details.order_days.label", + AccountFarmDetailsOrderDaysSummaryValue => "account.farm_details.order_days.summary.value", + AccountFarmDetailsDayMon => "account.farm_details.day.mon", + AccountFarmDetailsDayTue => "account.farm_details.day.tue", + AccountFarmDetailsDayWed => "account.farm_details.day.wed", + AccountFarmDetailsDayThu => "account.farm_details.day.thu", + AccountFarmDetailsDayFri => "account.farm_details.day.fri", + AccountFarmDetailsDaySat => "account.farm_details.day.sat", + AccountFarmDetailsDaySun => "account.farm_details.day.sun", + AccountFarmDetailsAboutProductsLabel => "account.farm_details.about_products.label", + AccountFarmDetailsAboutProductsValue => "account.farm_details.about_products.value", + AccountFarmDetailsCertificationsTitle => "account.farm_details.certifications.title", + AccountFarmDetailsCertificationsHelper => "account.farm_details.certifications.helper", + AccountFarmDetailsCertificationCertifiedOrganic => "account.farm_details.certification.certified_organic", + AccountFarmDetailsCertificationNaturallyGrown => "account.farm_details.certification.naturally_grown", + AccountFarmDetailsCertificationSmallFamilyFarm => "account.farm_details.certification.small_family_farm", + AccountFarmDetailsCertificationDeliveryAvailable => "account.farm_details.certification.delivery_available", + AccountFarmDetailsCustomerNoteTitle => "account.farm_details.customer_note.title", + AccountFarmDetailsCustomerNoteHelper => "account.farm_details.customer_note.helper", + AccountFarmDetailsCustomerNoteValue => "account.farm_details.customer_note.value", + AccountFarmDetailsProfilePreviewTitle => "account.farm_details.profile_preview.title", + AccountFarmDetailsGrowingPracticesSummaryLabel => "account.farm_details.summary.growing_practices.label", + AccountFarmDetailsSeasonSummaryLabel => "account.farm_details.summary.season.label", + AccountFarmDetailsOrderDaysSummaryLabel => "account.farm_details.summary.order_days.label", + AccountFarmDetailsPickupFulfillmentTitle => "account.farm_details.pickup_fulfillment.title", + AccountFarmDetailsPickupFulfillmentIntro => "account.farm_details.pickup_fulfillment.intro", + AccountFarmDetailsFulfillmentModeLabel => "account.farm_details.fulfillment_mode.label", + AccountFarmDetailsFulfillmentPickupOnly => "account.farm_details.fulfillment.pickup_only", + AccountFarmDetailsFulfillmentDelivery => "account.farm_details.fulfillment.delivery", + AccountFarmDetailsFulfillmentBoth => "account.farm_details.fulfillment.both", + AccountFarmDetailsPrimaryPickupLocationLabel => "account.farm_details.primary_pickup_location.label", + AccountFarmDetailsPrimaryPickupLocationTitleValue => "account.farm_details.primary_pickup_location.title.value", + AccountFarmDetailsPrimaryPickupLocationAddressValue => "account.farm_details.primary_pickup_location.address.value", + AccountFarmDetailsPickupInstructionsLabel => "account.farm_details.pickup_instructions.label", + AccountFarmDetailsPickupInstructionsValue => "account.farm_details.pickup_instructions.value", + AccountFarmDetailsPickupInstructionsHelper => "account.farm_details.pickup_instructions.helper", + AccountFarmDetailsPickupWindowsLabel => "account.farm_details.pickup_windows.label", + AccountFarmDetailsPickupWindowDayHeader => "account.farm_details.pickup_window.day.header", + AccountFarmDetailsPickupWindowStartHeader => "account.farm_details.pickup_window.start.header", + AccountFarmDetailsPickupWindowEndHeader => "account.farm_details.pickup_window.end.header", + AccountFarmDetailsPickupWindowWednesday => "account.farm_details.pickup_window.wednesday", + AccountFarmDetailsPickupWindowSaturday => "account.farm_details.pickup_window.saturday", + AccountFarmDetailsPickupWindowWednesdayStart => "account.farm_details.pickup_window.wednesday.start", + AccountFarmDetailsPickupWindowWednesdayEnd => "account.farm_details.pickup_window.wednesday.end", + AccountFarmDetailsPickupWindowSaturdayStart => "account.farm_details.pickup_window.saturday.start", + AccountFarmDetailsPickupWindowSaturdayEnd => "account.farm_details.pickup_window.saturday.end", + AccountFarmDetailsAddPickupWindowAction => "account.farm_details.add_pickup_window.action", + AccountFarmDetailsOrderCutoffLabel => "account.farm_details.order_cutoff.label", + AccountFarmDetailsOrderCutoffHelper => "account.farm_details.order_cutoff.helper", + AccountFarmDetailsOrderCutoffNoonValue => "account.farm_details.order_cutoff.noon.value", + AccountFarmDetailsDeliveryRadiusTitle => "account.farm_details.delivery_radius.title", + AccountFarmDetailsDeliveryRadiusHelper => "account.farm_details.delivery_radius.helper", + AccountFarmDetailsDeliveryRadiusValue => "account.farm_details.delivery_radius.value", + AccountFarmDetailsDeliveryRadiusUnit => "account.farm_details.delivery_radius.unit", + AccountFarmDetailsDeliveryRadiusNote => "account.farm_details.delivery_radius.note", + AccountFarmDetailsCustomerExperienceTitle => "account.farm_details.customer_experience.title", + AccountFarmDetailsCustomerExperienceIntro => "account.farm_details.customer_experience.intro", + AccountFarmDetailsCustomerExperiencePickupTitle => "account.farm_details.customer_experience.pickup.title", + AccountFarmDetailsCustomerExperienceDeliveryTitle => "account.farm_details.customer_experience.delivery.title", + AccountFarmDetailsCustomerExperienceDeliveryBody => "account.farm_details.customer_experience.delivery.body", AccountSettingsTitle => "account.settings.title", AccountSettingsNostrRelaysTitle => "account.settings.nostr_relays.title", AccountSettingsNostrRelaysHelper => "account.settings.nostr_relays.helper", diff --git a/crates/i18n/src/lib.rs b/crates/i18n/src/lib.rs @@ -214,6 +214,26 @@ mod tests { app_text(AppTextKey::AccountFarmDetailsFarmTypeVegetableFarm), "Vegetable farm" ); + assert_eq!( + app_text(AppTextKey::AccountFarmDetailsLocationTitle), + "Location & service area" + ); + assert_eq!( + app_text(AppTextKey::AccountFarmDetailsMapNotImplemented), + "not implemented" + ); + assert_eq!( + app_text(AppTextKey::AccountFarmDetailsOperatingTitle), + "Operating details" + ); + assert_eq!( + app_text(AppTextKey::AccountFarmDetailsPickupFulfillmentTitle), + "Pickup & fulfilment" + ); + assert_eq!( + app_text(AppTextKey::AccountFarmDetailsOrderCutoffNoonValue), + "12:00 PM (Noon)" + ); assert_eq!(app_text(AppTextKey::AccountSettingsTitle), "Settings"); assert_eq!( app_text(AppTextKey::AccountSettingsNostrRelaysTitle), diff --git a/i18n/locales/en/messages.json b/i18n/locales/en/messages.json @@ -87,6 +87,99 @@ "account.farm_details.farm_type.nursery_plant_farm": "Nursery & plant farm", "account.farm_details.farm_type.mixed_farm": "Mixed farm", "account.farm_details.farm_type.other": "Other farm type", + "account.farm_details.location.title": "Location & service area", + "account.farm_details.location.intro": "Help customers understand where your farm operates and which local areas you serve.", + "account.farm_details.map.not_implemented": "not implemented", + "account.farm_details.street_address.label": "Street address *", + "account.farm_details.street_address.value": "1234 Beacon Hill Road", + "account.farm_details.city.label": "City *", + "account.farm_details.city.value": "Victoria", + "account.farm_details.province.label": "Province *", + "account.farm_details.province.british_columbia": "British Columbia", + "account.farm_details.province.alberta": "Alberta", + "account.farm_details.postal_code.label": "Postal code *", + "account.farm_details.postal_code.value": "V8N 1Z1", + "account.farm_details.country.label": "Country *", + "account.farm_details.country.canada": "Canada", + "account.farm_details.country.united_states": "United States", + "account.farm_details.service_area.label": "Service area *", + "account.farm_details.service_area.value": "25 km around Victoria, BC", + "account.farm_details.service_area.helper": "Customers within this area can discover your farm.", + "account.farm_details.exact_address_public.label": "Make exact address public", + "account.farm_details.exact_address_public.helper": "Customers see your service area unless pickup is enabled.", + "account.farm_details.location_preview.title": "Location preview", + "account.farm_details.location_preview.helper": "Customers will see your service area.", + "account.farm_details.operating.title": "Operating details", + "account.farm_details.operating.intro": "Share how you grow, when products are available, and the standards customers can expect.", + "account.farm_details.growing_practices.label": "Growing practices *", + "account.farm_details.growing_practice.regenerative": "Regenerative", + "account.farm_details.growing_practice.organic": "Organic practices", + "account.farm_details.production_methods.label": "Production methods", + "account.farm_details.production_method.organic_practices": "Organic practices", + "account.farm_details.production_method.no_spray": "No spray", + "account.farm_details.season_dates.label": "Typical season dates *", + "account.farm_details.season_start.value": "Mar 15", + "account.farm_details.season_end.value": "Nov 15", + "account.farm_details.order_days.label": "Customer availability / order days *", + "account.farm_details.order_days.summary.value": "Mon - Fri", + "account.farm_details.day.mon": "Mon", + "account.farm_details.day.tue": "Tue", + "account.farm_details.day.wed": "Wed", + "account.farm_details.day.thu": "Thu", + "account.farm_details.day.fri": "Fri", + "account.farm_details.day.sat": "Sat", + "account.farm_details.day.sun": "Sun", + "account.farm_details.about_products.label": "About your products *", + "account.farm_details.about_products.value": "We grow a diverse mix of seasonal vegetables and herbs with a focus on flavour, freshness, and soil health.", + "account.farm_details.certifications.title": "Certifications & trust signals", + "account.farm_details.certifications.helper": "Select the signals that accurately describe your farm.", + "account.farm_details.certification.certified_organic": "Certified organic", + "account.farm_details.certification.naturally_grown": "Naturally grown", + "account.farm_details.certification.small_family_farm": "Small family farm", + "account.farm_details.certification.delivery_available": "Delivery available", + "account.farm_details.customer_note.title": "What customers should know", + "account.farm_details.customer_note.helper": "Share anything important customers should know before ordering.", + "account.farm_details.customer_note.value": "We never use synthetic pesticides or herbicides. We prioritize soil health, biodiversity, and growing food the right way.", + "account.farm_details.profile_preview.title": "Profile preview", + "account.farm_details.summary.growing_practices.label": "Growing practices", + "account.farm_details.summary.season.label": "Season", + "account.farm_details.summary.order_days.label": "Order days", + "account.farm_details.pickup_fulfillment.title": "Pickup & fulfilment", + "account.farm_details.pickup_fulfillment.intro": "Set clear pickup and delivery expectations for customers.", + "account.farm_details.fulfillment_mode.label": "Fulfilment mode *", + "account.farm_details.fulfillment.pickup_only": "Pickup only", + "account.farm_details.fulfillment.delivery": "Delivery", + "account.farm_details.fulfillment.both": "Both", + "account.farm_details.primary_pickup_location.label": "Primary pickup location *", + "account.farm_details.primary_pickup_location.title.value": "Farmstand at Tyson's Farm", + "account.farm_details.primary_pickup_location.address.value": "1234 Meadow Lane, Victoria, BC V9B 2N1", + "account.farm_details.pickup_instructions.label": "Pickup instructions", + "account.farm_details.pickup_instructions.value": "Please park in the gravel lot and check in at the farmstand. We'll have your order ready for you.", + "account.farm_details.pickup_instructions.helper": "These instructions are shown after a customer places an order.", + "account.farm_details.pickup_windows.label": "Standard pickup windows *", + "account.farm_details.pickup_window.day.header": "Day", + "account.farm_details.pickup_window.start.header": "Start time", + "account.farm_details.pickup_window.end.header": "End time", + "account.farm_details.pickup_window.wednesday": "Wednesday", + "account.farm_details.pickup_window.saturday": "Saturday", + "account.farm_details.pickup_window.wednesday.start": "3:00 PM", + "account.farm_details.pickup_window.wednesday.end": "6:00 PM", + "account.farm_details.pickup_window.saturday.start": "9:00 AM", + "account.farm_details.pickup_window.saturday.end": "1:00 PM", + "account.farm_details.add_pickup_window.action": "Add pickup window", + "account.farm_details.order_cutoff.label": "Order cutoff time *", + "account.farm_details.order_cutoff.helper": "Orders placed before this time are prepared for the next pickup window.", + "account.farm_details.order_cutoff.noon.value": "12:00 PM (Noon)", + "account.farm_details.delivery_radius.title": "Optional delivery radius", + "account.farm_details.delivery_radius.helper": "Set how far you are willing to deliver. Leave at 0 km to hide delivery.", + "account.farm_details.delivery_radius.value": "25", + "account.farm_details.delivery_radius.unit": "km", + "account.farm_details.delivery_radius.note": "Customers outside this radius will not see delivery as an option.", + "account.farm_details.customer_experience.title": "Customer experience", + "account.farm_details.customer_experience.intro": "This is how customers will see pickup and delivery information.", + "account.farm_details.customer_experience.pickup.title": "Pickup", + "account.farm_details.customer_experience.delivery.title": "Delivery", + "account.farm_details.customer_experience.delivery.body": "Delivery available within 25 km from your farm.", "account.settings.title": "Settings", "account.settings.nostr_relays.title": "Nostr relays", "account.settings.nostr_relays.helper": "Manage the relays used to publish and read events.",