commit 7693ea7bc8f48443067705442e1e9d841a1b80f6
parent adc217b5c27101a6fdedaa28e7c4f1f2591bfb10
Author: triesap <tyson@radroots.org>
Date: Mon, 20 Apr 2026 00:10:44 +0000
today: route follow-ons into shared execution screens
Diffstat:
6 files changed, 464 insertions(+), 268 deletions(-)
diff --git a/crates/launchers/desktop/src/runtime.rs b/crates/launchers/desktop/src/runtime.rs
@@ -206,6 +206,14 @@ impl DesktopAppRuntime {
self.lock_state_mut().open_orders()
}
+ pub fn open_orders_fulfillment_window(
+ &self,
+ fulfillment_window_id: FulfillmentWindowId,
+ ) -> Result<bool, AppSqliteError> {
+ self.lock_state_mut()
+ .open_orders_fulfillment_window(fulfillment_window_id)
+ }
+
pub fn open_order_detail(&self, order_id: OrderId) -> Result<bool, AppSqliteError> {
self.lock_state_mut().open_order_detail(order_id)
}
@@ -772,7 +780,25 @@ impl DesktopAppRuntimeState {
return Ok(false);
}
- let query_changed = self.replace_orders_query(OrdersScreenQueryState::default())?;
+ self.open_orders_query(OrdersScreenQueryState::default())
+ }
+
+ fn open_orders_fulfillment_window(
+ &mut self,
+ fulfillment_window_id: FulfillmentWindowId,
+ ) -> Result<bool, AppSqliteError> {
+ if !self.has_saved_farm() {
+ return Ok(false);
+ }
+
+ self.open_orders_query(OrdersScreenQueryState {
+ filter: OrdersFilter::All,
+ fulfillment_window_id: Some(fulfillment_window_id),
+ })
+ }
+
+ fn open_orders_query(&mut self, query: OrdersScreenQueryState) -> Result<bool, AppSqliteError> {
+ let query_changed = self.replace_orders_query(query)?;
let detail_changed = self
.state_store
.apply_in_memory(AppStateCommand::replace_order_detail(None));
@@ -2677,6 +2703,87 @@ mod tests {
}
#[test]
+ fn runtime_open_orders_fulfillment_window_filters_the_queue_to_one_window() {
+ let runtime = memory_runtime();
+ let (_, farm_id) = provision_ready_farmer_account(&runtime);
+ let (fulfillment_window_id, order_id) = seed_order_workspace(&runtime, farm_id);
+ let other_fulfillment_window_id = FulfillmentWindowId::new();
+ let other_order_id = OrderId::new();
+ let sql = format!(
+ "insert into fulfillment_windows (
+ id,
+ farm_id,
+ starts_at,
+ ends_at,
+ capacity_limit,
+ created_at,
+ updated_at,
+ pickup_location_id,
+ label,
+ order_cutoff_at
+ )
+ select
+ '{other_fulfillment_window_id}',
+ farm_id,
+ '2099-04-19T16:00:00Z',
+ '2099-04-19T18:00:00Z',
+ capacity_limit,
+ '2099-04-19T16:00:00Z',
+ '2099-04-19T16:00:00Z',
+ pickup_location_id,
+ 'Saturday pickup',
+ '2099-04-18T18:00:00Z'
+ from fulfillment_windows
+ where id = '{fulfillment_window_id}' and farm_id = '{farm_id}';
+ insert into orders (
+ id,
+ farm_id,
+ fulfillment_window_id,
+ order_number,
+ customer_display_name,
+ status,
+ updated_at
+ ) values (
+ '{other_order_id}',
+ '{farm_id}',
+ '{other_fulfillment_window_id}',
+ 'R-101',
+ 'Robin',
+ 'scheduled',
+ '2026-04-17T11:00:00Z'
+ )"
+ );
+ runtime
+ .lock_state()
+ .sqlite_store
+ .as_ref()
+ .expect("sqlite store")
+ .connection()
+ .execute_batch(&sql)
+ .expect("second orders workspace should seed");
+
+ assert!(
+ runtime
+ .open_orders_fulfillment_window(fulfillment_window_id)
+ .expect("orders window follow-on should route")
+ );
+ let summary = runtime.summary();
+
+ assert_eq!(
+ summary.shell_projection.selected_section,
+ ShellSection::Farmer(FarmerSection::Orders)
+ );
+ assert_eq!(summary.orders_projection.query.filter, OrdersFilter::All);
+ assert_eq!(
+ summary.orders_projection.query.fulfillment_window_id,
+ Some(fulfillment_window_id)
+ );
+ assert_eq!(summary.orders_projection.list.rows.len(), 1);
+ assert_eq!(summary.orders_projection.list.rows[0].order_id, order_id);
+ assert!(summary.orders_projection.detail.is_none());
+ }
+
+ #[test]
fn runtime_order_actions_refresh_repository_backed_orders_projection() {
let runtime = memory_runtime();
let (_, farm_id) = provision_ready_farmer_account(&runtime);
diff --git a/crates/launchers/desktop/src/source_guards.rs b/crates/launchers/desktop/src/source_guards.rs
@@ -66,6 +66,8 @@ const ALLOWED_WINDOW_LITERALS: &[&str] = &[
"home-nav-today",
"home-orders-scroll",
"home-pack-day-scroll",
+ "home-today-open-pack-day",
+ "home-today-order-open",
"home-signer-back",
"home-signer-source-input",
"home-today-open-orders",
@@ -203,6 +205,7 @@ const REQUIRED_WINDOW_COPY_KEYS: &[&str] = &[
"AppTextKey::HomeNavProducts",
"AppTextKey::HomeNavOrders",
"AppTextKey::HomeTodayOpenInOrdersAction",
+ "AppTextKey::HomeTodayOpenInPackDayAction",
"AppTextKey::OrdersTitle",
"AppTextKey::OrdersFiltersTitle",
"AppTextKey::OrdersSummaryTotal",
diff --git a/crates/launchers/desktop/src/window.rs b/crates/launchers/desktop/src/window.rs
@@ -952,6 +952,33 @@ impl HomeView {
}
}
+ fn open_orders_fulfillment_window(
+ &mut self,
+ fulfillment_window_id: FulfillmentWindowId,
+ cx: &mut Context<Self>,
+ ) {
+ match self
+ .runtime
+ .open_orders_fulfillment_window(fulfillment_window_id)
+ {
+ Ok(true) => {
+ self.products_stock_editor = None;
+ self.product_editor_form = None;
+ cx.notify();
+ }
+ Ok(false) => {}
+ Err(runtime_error) => {
+ error!(
+ target: "orders",
+ event = "orders.route_failed",
+ error = %runtime_error,
+ fulfillment_window_id = %fulfillment_window_id,
+ "failed to route into orders view"
+ );
+ }
+ }
+ }
+
fn open_pack_day(
&mut self,
fulfillment_window_id: Option<FulfillmentWindowId>,
@@ -975,6 +1002,33 @@ impl HomeView {
}
}
+ fn open_today_next_window(
+ &mut self,
+ fulfillment_window_id: Option<FulfillmentWindowId>,
+ cx: &mut Context<Self>,
+ ) {
+ let Some(fulfillment_window_id) = fulfillment_window_id else {
+ return;
+ };
+
+ match self.runtime.open_pack_day(Some(fulfillment_window_id)) {
+ Ok(true) => {
+ self.products_stock_editor = None;
+ self.product_editor_form = None;
+ cx.notify();
+ }
+ Ok(false) => self.open_orders_fulfillment_window(fulfillment_window_id, cx),
+ Err(runtime_error) => {
+ error!(
+ target: "pack_day",
+ event = "pack_day.route_failed",
+ error = %runtime_error,
+ "failed to route into pack day view"
+ );
+ }
+ }
+ }
+
fn select_orders_filter(&mut self, filter: OrdersFilter, cx: &mut Context<Self>) {
match self.runtime.select_orders_filter(filter) {
Ok(true) => cx.notify(),
@@ -1358,6 +1412,262 @@ impl HomeView {
cx.notify();
}
+ fn render_today_content(
+ &mut self,
+ runtime: &DesktopAppRuntimeSummary,
+ cx: &mut Context<Self>,
+ ) -> AnyElement {
+ let projection = &runtime.today_projection;
+ let home_status = home_status_presentation(runtime);
+ let setup_onboarding = farm_setup_onboarding_card_spec(runtime.home_route);
+ let farm_state = farmer_home_farm_state(runtime);
+ let next_fulfillment_window_id = projection
+ .next_fulfillment_window
+ .as_ref()
+ .map(|window| window.fulfillment_window_id);
+ let mut sections = Vec::<AnyElement>::new();
+
+ if let Some(summary) = projection.summary.as_ref() {
+ sections.push(home_summary_card(summary).into_any_element());
+ }
+
+ if let Some(issue) = runtime.startup_issue.as_ref() {
+ sections.push(
+ home_card(
+ app_shared_text(AppTextKey::MetadataStartupIssue),
+ home_body_text(issue.clone()),
+ )
+ .into_any_element(),
+ );
+ }
+
+ if let Some(farm_setup_form) = self.farm_setup_form.as_ref() {
+ sections.push(
+ home_farm_setup_form_card(
+ farm_setup_form,
+ cx.listener(|this, checked: &bool, _, cx| {
+ this.toggle_farm_order_method(FarmOrderMethod::Pickup, *checked, cx)
+ }),
+ cx.listener(|this, checked: &bool, _, cx| {
+ this.toggle_farm_order_method(FarmOrderMethod::Delivery, *checked, cx)
+ }),
+ cx.listener(|this, checked: &bool, _, cx| {
+ this.toggle_farm_order_method(FarmOrderMethod::Shipping, *checked, cx)
+ }),
+ cx.listener(|this, _, _, cx| this.finish_farm_setup(cx)),
+ cx,
+ )
+ .into_any_element(),
+ );
+ } else if let Some(spec) = setup_onboarding {
+ sections.push(
+ home_farm_setup_onboarding_card(
+ spec,
+ cx.listener(|this, _, window, cx| this.open_farm_setup(window, cx)),
+ cx,
+ )
+ .into_any_element(),
+ );
+ } else if projection.needs_setup() {
+ sections.push(
+ home_setup_card(
+ projection,
+ matches!(farm_state, FarmerHomeFarmState::IncompleteFarm).then_some(
+ action_button_primary(
+ "home-farm-setup-continue",
+ app_shared_text(AppTextKey::HomeFarmSetupContinueAction),
+ cx.listener(|this, _, window, cx| this.open_farm_setup(window, cx)),
+ cx,
+ )
+ .into_any_element(),
+ ),
+ )
+ .into_any_element(),
+ );
+ }
+
+ if let Some(saved_farm_summary_card) = home_saved_farm_summary_card(runtime) {
+ sections.push(saved_farm_summary_card);
+ }
+
+ if let Some(next_window) = projection.next_fulfillment_window.as_ref() {
+ sections.push(
+ home_next_fulfillment_window_card(
+ next_window,
+ Some(
+ action_button_compact(
+ "home-today-open-pack-day",
+ app_shared_text(AppTextKey::HomeTodayOpenInPackDayAction),
+ cx.listener(move |this, _, _, cx| {
+ this.open_today_next_window(next_fulfillment_window_id, cx)
+ }),
+ cx,
+ )
+ .into_any_element(),
+ ),
+ )
+ .into_any_element(),
+ );
+ }
+
+ if !projection.orders_needing_action.is_empty() {
+ sections.push(
+ home_list_card(
+ AppTextKey::HomeTodayOrdersNeedingAction,
+ projection
+ .orders_needing_action
+ .iter()
+ .enumerate()
+ .map(|(index, order)| {
+ home_order_row(
+ index,
+ order,
+ cx.listener({
+ let order_id = order.order_id;
+ move |this, _, _, cx| this.open_order_detail(order_id, cx)
+ }),
+ cx,
+ )
+ })
+ .collect::<Vec<_>>(),
+ Some(
+ action_button_compact(
+ "home-today-open-orders",
+ app_shared_text(AppTextKey::HomeTodayOpenInOrdersAction),
+ cx.listener(|this, _, _, cx| this.open_orders(cx)),
+ cx,
+ )
+ .into_any_element(),
+ ),
+ )
+ .into_any_element(),
+ );
+ }
+
+ if !projection.low_stock_products.is_empty() {
+ sections.push(
+ home_list_card(
+ AppTextKey::HomeTodayLowStock,
+ projection
+ .low_stock_products
+ .iter()
+ .map(home_low_stock_row)
+ .collect::<Vec<_>>(),
+ Some(
+ action_button_compact(
+ "home-today-open-products-low-stock",
+ app_shared_text(AppTextKey::HomeTodayOpenInProductsAction),
+ cx.listener(|this, _, _, cx| {
+ this.open_products_filter(ProductsFilter::NeedAttention, cx)
+ }),
+ cx,
+ )
+ .into_any_element(),
+ ),
+ )
+ .into_any_element(),
+ );
+ }
+
+ if !projection.draft_products.is_empty() {
+ sections.push(
+ home_list_card(
+ AppTextKey::HomeTodayDraftProducts,
+ projection
+ .draft_products
+ .iter()
+ .map(home_draft_row)
+ .collect::<Vec<_>>(),
+ Some(
+ action_button_compact(
+ "home-today-open-products-drafts",
+ app_shared_text(AppTextKey::HomeTodayOpenInProductsAction),
+ cx.listener(|this, _, _, cx| {
+ this.open_products_filter(ProductsFilter::Drafts, cx)
+ }),
+ cx,
+ )
+ .into_any_element(),
+ ),
+ )
+ .into_any_element(),
+ );
+ }
+
+ if runtime.startup_issue.is_none() && runtime.startup_gate == AppStartupGate::SetupRequired
+ {
+ sections.push(
+ home_empty_state_card(
+ AppTextKey::HomeTodayEmptySetupTitle,
+ AppTextKey::HomeTodayEmptySetupBody,
+ )
+ .into_any_element(),
+ );
+ } else if runtime.startup_issue.is_none()
+ && farm_state == FarmerHomeFarmState::NoFarm
+ && setup_onboarding.is_none()
+ {
+ sections.push(
+ home_empty_state_card(
+ AppTextKey::HomeTodayEmptyNoFarmTitle,
+ AppTextKey::HomeTodayEmptyNoFarmBody,
+ )
+ .into_any_element(),
+ );
+ } else if runtime.startup_issue.is_none()
+ && farm_state == FarmerHomeFarmState::ConfiguredFarm
+ && !projection.needs_setup()
+ && projection.next_fulfillment_window.is_none()
+ && !projection.has_attention_items()
+ {
+ sections.push(
+ home_empty_state_card(
+ AppTextKey::HomeTodayEmptyQuietTitle,
+ AppTextKey::HomeTodayEmptyQuietBody,
+ )
+ .into_any_element(),
+ );
+ }
+
+ div()
+ .w_full()
+ .max_w(px(APP_UI_THEME.shells.home_card_max_width_px))
+ .mx_auto()
+ .flex()
+ .flex_col()
+ .gap(px(APP_UI_THEME.shells.home_stack_gap_px))
+ .child(
+ div()
+ .w_full()
+ .flex()
+ .flex_col()
+ .gap(px(4.0))
+ .child(
+ div()
+ .text_size(px(APP_UI_THEME.foundation.typography.body_text_px * 2.0))
+ .font_weight(gpui::FontWeight::BOLD)
+ .text_color(rgb(APP_UI_THEME.foundation.text.primary))
+ .child(app_shared_text(AppTextKey::HomeTodayTitle)),
+ )
+ .child(
+ div()
+ .text_size(px(APP_UI_THEME.foundation.typography.body_text_px))
+ .font_weight(gpui::FontWeight::MEDIUM)
+ .line_height(relative(1.2))
+ .text_color(rgb(APP_UI_THEME.foundation.text.primary))
+ .when_some(home_saved_farm(runtime), |this, farm| {
+ this.child(farm.display_name.clone())
+ })
+ .when(home_saved_farm(runtime).is_none(), |this| {
+ this.child(app_shared_text(home_status.label_key))
+ }),
+ )
+ .child(home_status_row(&home_status)),
+ )
+ .children(sections)
+ .into_any_element()
+ }
+
fn render_farmer_workspace(
&mut self,
runtime: &DesktopAppRuntimeSummary,
@@ -1378,35 +1688,7 @@ impl HomeView {
| FarmerSection::Products
| FarmerSection::Orders
| FarmerSection::PackDay
- | FarmerSection::Farm => home_today_content(
- runtime,
- self.farm_setup_form.as_ref().map(|form| {
- home_farm_setup_form_card(
- form,
- cx.listener(|this, checked: &bool, _, cx| {
- this.toggle_farm_order_method(FarmOrderMethod::Pickup, *checked, cx)
- }),
- cx.listener(|this, checked: &bool, _, cx| {
- this.toggle_farm_order_method(FarmOrderMethod::Delivery, *checked, cx)
- }),
- cx.listener(|this, checked: &bool, _, cx| {
- this.toggle_farm_order_method(FarmOrderMethod::Shipping, *checked, cx)
- }),
- cx.listener(|this, _, _, cx| this.finish_farm_setup(cx)),
- cx,
- )
- .into_any_element()
- }),
- cx.listener(|this, _, window, cx| this.open_farm_setup(window, cx)),
- cx.listener(|this, _, window, cx| this.open_farm_setup(window, cx)),
- cx.listener(|this, _, _, cx| this.open_orders(cx)),
- cx.listener(|this, _, _, cx| {
- this.open_products_filter(ProductsFilter::NeedAttention, cx)
- }),
- cx.listener(|this, _, _, cx| this.open_products_filter(ProductsFilter::Drafts, cx)),
- cx,
- )
- .into_any_element(),
+ | FarmerSection::Farm => self.render_today_content(runtime, cx),
};
app_split_shell(
@@ -5065,209 +5347,6 @@ fn holding_home_sidebar(runtime: &DesktopAppRuntimeSummary) -> impl IntoElement
)
}
-fn home_today_content(
- runtime: &DesktopAppRuntimeSummary,
- farm_setup_form: Option<AnyElement>,
- on_start_farm_setup: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
- on_continue_farm_setup: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
- on_open_orders: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
- on_open_low_stock_products: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
- on_open_draft_products: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
- cx: &App,
-) -> impl IntoElement {
- let projection = &runtime.today_projection;
- let home_status = home_status_presentation(runtime);
- let setup_onboarding = farm_setup_onboarding_card_spec(runtime.home_route);
- let farm_state = farmer_home_farm_state(runtime);
- let mut sections = Vec::<AnyElement>::new();
-
- if let Some(summary) = projection.summary.as_ref() {
- sections.push(home_summary_card(summary).into_any_element());
- }
-
- if let Some(issue) = runtime.startup_issue.as_ref() {
- sections.push(
- home_card(
- app_shared_text(AppTextKey::MetadataStartupIssue),
- home_body_text(issue.clone()),
- )
- .into_any_element(),
- );
- }
-
- if let Some(farm_setup_form) = farm_setup_form {
- sections.push(farm_setup_form);
- } else if let Some(spec) = setup_onboarding {
- sections.push(
- home_farm_setup_onboarding_card(spec, on_start_farm_setup, cx).into_any_element(),
- );
- } else if projection.needs_setup() {
- sections.push(
- home_setup_card(
- projection,
- matches!(farm_state, FarmerHomeFarmState::IncompleteFarm).then_some(
- action_button_primary(
- "home-farm-setup-continue",
- app_shared_text(AppTextKey::HomeFarmSetupContinueAction),
- on_continue_farm_setup,
- cx,
- )
- .into_any_element(),
- ),
- )
- .into_any_element(),
- );
- }
-
- if let Some(saved_farm_summary_card) = home_saved_farm_summary_card(runtime) {
- sections.push(saved_farm_summary_card);
- }
-
- if let Some(next_window) = projection.next_fulfillment_window.as_ref() {
- sections.push(home_next_fulfillment_window_card(next_window).into_any_element());
- }
-
- if !projection.orders_needing_action.is_empty() {
- sections.push(
- home_list_card(
- AppTextKey::HomeTodayOrdersNeedingAction,
- projection
- .orders_needing_action
- .iter()
- .map(home_order_row)
- .collect::<Vec<_>>(),
- Some(
- action_button_compact(
- "home-today-open-orders",
- app_shared_text(AppTextKey::HomeTodayOpenInOrdersAction),
- on_open_orders,
- cx,
- )
- .into_any_element(),
- ),
- )
- .into_any_element(),
- );
- }
-
- if !projection.low_stock_products.is_empty() {
- sections.push(
- home_list_card(
- AppTextKey::HomeTodayLowStock,
- projection
- .low_stock_products
- .iter()
- .map(home_low_stock_row)
- .collect::<Vec<_>>(),
- Some(
- action_button_compact(
- "home-today-open-products-low-stock",
- app_shared_text(AppTextKey::HomeTodayOpenInProductsAction),
- on_open_low_stock_products,
- cx,
- )
- .into_any_element(),
- ),
- )
- .into_any_element(),
- );
- }
-
- if !projection.draft_products.is_empty() {
- sections.push(
- home_list_card(
- AppTextKey::HomeTodayDraftProducts,
- projection
- .draft_products
- .iter()
- .map(home_draft_row)
- .collect::<Vec<_>>(),
- Some(
- action_button_compact(
- "home-today-open-products-drafts",
- app_shared_text(AppTextKey::HomeTodayOpenInProductsAction),
- on_open_draft_products,
- cx,
- )
- .into_any_element(),
- ),
- )
- .into_any_element(),
- );
- }
-
- if runtime.startup_issue.is_none() && runtime.startup_gate == AppStartupGate::SetupRequired {
- sections.push(
- home_empty_state_card(
- AppTextKey::HomeTodayEmptySetupTitle,
- AppTextKey::HomeTodayEmptySetupBody,
- )
- .into_any_element(),
- );
- } else if runtime.startup_issue.is_none()
- && farm_state == FarmerHomeFarmState::NoFarm
- && setup_onboarding.is_none()
- {
- sections.push(
- home_empty_state_card(
- AppTextKey::HomeTodayEmptyNoFarmTitle,
- AppTextKey::HomeTodayEmptyNoFarmBody,
- )
- .into_any_element(),
- );
- } else if runtime.startup_issue.is_none()
- && farm_state == FarmerHomeFarmState::ConfiguredFarm
- && !projection.needs_setup()
- && projection.next_fulfillment_window.is_none()
- && !projection.has_attention_items()
- {
- sections.push(
- home_empty_state_card(
- AppTextKey::HomeTodayEmptyQuietTitle,
- AppTextKey::HomeTodayEmptyQuietBody,
- )
- .into_any_element(),
- );
- }
-
- div()
- .w_full()
- .max_w(px(APP_UI_THEME.shells.home_card_max_width_px))
- .mx_auto()
- .flex()
- .flex_col()
- .gap(px(APP_UI_THEME.shells.home_stack_gap_px))
- .child(
- div()
- .w_full()
- .flex()
- .flex_col()
- .gap(px(4.0))
- .child(
- div()
- .text_size(px(APP_UI_THEME.foundation.typography.body_text_px * 2.0))
- .font_weight(gpui::FontWeight::BOLD)
- .text_color(rgb(APP_UI_THEME.foundation.text.primary))
- .child(app_shared_text(AppTextKey::HomeTodayTitle)),
- )
- .child(
- div()
- .text_size(px(APP_UI_THEME.foundation.typography.body_text_px))
- .font_weight(gpui::FontWeight::MEDIUM)
- .line_height(relative(1.2))
- .text_color(rgb(APP_UI_THEME.foundation.text.primary))
- .when_some(home_saved_farm(runtime), |this, farm| {
- this.child(farm.display_name.clone())
- })
- .when(home_saved_farm(runtime).is_none(), |this| {
- this.child(app_shared_text(home_status.label_key))
- }),
- )
- .child(home_status_row(&home_status)),
- )
- .children(sections)
-}
-
fn selected_farmer_section(runtime: &DesktopAppRuntimeSummary) -> FarmerSection {
match runtime.shell_projection.selected_section {
ShellSection::Farmer(section) => section,
@@ -7149,19 +7228,28 @@ fn home_setup_card(
)
}
-fn home_next_fulfillment_window_card(next_window: &FulfillmentWindowSummary) -> impl IntoElement {
+fn home_next_fulfillment_window_card(
+ next_window: &FulfillmentWindowSummary,
+ action: Option<AnyElement>,
+) -> impl IntoElement {
home_card(
app_shared_text(AppTextKey::HomeTodayNextFulfillmentWindow),
- label_value_list(vec![
- LabelValueRow::new(
- app_shared_text(AppTextKey::HomeTodayWindowStartsLabel),
- next_window.starts_at.clone(),
- ),
- LabelValueRow::new(
- app_shared_text(AppTextKey::HomeTodayWindowEndsLabel),
- next_window.ends_at.clone(),
- ),
- ]),
+ div()
+ .w_full()
+ .flex()
+ .flex_col()
+ .gap(px(APP_UI_THEME.shells.home_stack_gap_px))
+ .child(label_value_list(vec![
+ LabelValueRow::new(
+ app_shared_text(AppTextKey::HomeTodayWindowStartsLabel),
+ next_window.starts_at.clone(),
+ ),
+ LabelValueRow::new(
+ app_shared_text(AppTextKey::HomeTodayWindowEndsLabel),
+ next_window.ends_at.clone(),
+ ),
+ ]))
+ .when_some(action, |this, action| this.child(div().child(action))),
)
}
@@ -7216,34 +7304,26 @@ fn order_optional_text(value: Option<&str>) -> SharedString {
.unwrap_or_else(|| app_shared_text(AppTextKey::ValueNone))
}
-fn home_order_row(order: &OrderListRow) -> AnyElement {
+fn home_order_row(
+ index: usize,
+ order: &OrderListRow,
+ on_open: impl Fn(&ClickEvent, &mut Window, &mut App) + 'static,
+ cx: &App,
+) -> AnyElement {
div()
.w_full()
.min_w_0()
.flex()
.items_center()
- .justify_between()
.gap(px(APP_UI_THEME.shells.home_stack_gap_px))
- .child(
- div()
- .min_w_0()
- .flex()
- .flex_col()
- .gap(px(2.0))
- .child(
- div()
- .text_size(px(APP_UI_THEME.foundation.typography.body_text_px))
- .font_weight(gpui::FontWeight::SEMIBOLD)
- .text_color(rgb(APP_UI_THEME.foundation.text.primary))
- .child(order.order_number.clone()),
- )
- .child(
- div()
- .text_size(px(APP_UI_THEME.foundation.typography.utility_title_text_px))
- .text_color(rgb(APP_UI_THEME.foundation.text.secondary))
- .child(order.customer_display_name.clone()),
- ),
- )
+ .child(list_row_button(
+ ("home-today-order-open", index),
+ order.order_number.clone(),
+ Some(SharedString::from(order.customer_display_name.clone())),
+ false,
+ on_open,
+ cx,
+ ))
.child(status_indicator(
APP_UI_THEME.components.app_status_indicator.attention,
))
diff --git a/crates/shared/i18n/src/keys.rs b/crates/shared/i18n/src/keys.rs
@@ -35,6 +35,7 @@ define_app_text_keys! {
HomeTodayLowStock => "home.today.low_stock",
HomeTodayDraftProducts => "home.today.draft_products",
HomeTodayOpenInOrdersAction => "home.today.open_in_orders.action",
+ HomeTodayOpenInPackDayAction => "home.today.open_in_pack_day.action",
HomeTodayOpenInProductsAction => "home.today.open_in_products.action",
HomeTodaySetupChecklist => "home.today.setup_checklist",
HomeTodayNextFulfillmentWindow => "home.today.next_fulfillment_window",
diff --git a/crates/shared/i18n/src/lib.rs b/crates/shared/i18n/src/lib.rs
@@ -172,7 +172,11 @@ mod tests {
assert_eq!(app_text(AppTextKey::HomeNavOrders), "Orders");
assert_eq!(
app_text(AppTextKey::HomeTodayOpenInOrdersAction),
- "Open in Orders"
+ "View all"
+ );
+ assert_eq!(
+ app_text(AppTextKey::HomeTodayOpenInPackDayAction),
+ "Open pack day"
);
assert_eq!(app_text(AppTextKey::OrdersTitle), "Orders");
assert_eq!(
diff --git a/i18n/locales/en/messages.json b/i18n/locales/en/messages.json
@@ -13,7 +13,8 @@
"home.today.orders_needing_action": "Orders needing action",
"home.today.low_stock": "Low stock",
"home.today.draft_products": "Draft products",
- "home.today.open_in_orders.action": "Open in Orders",
+ "home.today.open_in_orders.action": "View all",
+ "home.today.open_in_pack_day.action": "Open pack day",
"home.today.open_in_products.action": "Open in Products",
"home.today.setup_checklist": "Setup checklist",
"home.today.next_fulfillment_window": "Next fulfillment window",