fix: keep nav bar sticky at top (#943)

* fix: keep nav bar sticky at top

* fix: sticky on settings page

* fix: keep padding in settings page

* fix: make all settings page title sticky

* fix: make buttons sticky with title

* fix: set header bar min height

* fix: mobile responsive

* fix: reduce header bar
This commit is contained in:
Clayton
2026-02-10 16:37:42 -06:00
committed by GitHub
parent 8fcd2912cb
commit 17e2971603
12 changed files with 164 additions and 167 deletions

View File

@@ -1,25 +1,21 @@
<header class="flex justify-between items-center text-primary font-medium">
<h1 class="text-xl"><%= t(".accounts") %></h1>
<div class="flex items-center gap-5">
<div class="flex items-center gap-2">
<%= icon(
"refresh-cw",
as_button: true,
size: "sm",
href: sync_all_accounts_path,
disabled: Current.family.syncing?,
frame: :_top
) %>
<%= render DS::Link.new(
text: "New account",
href: new_account_path(return_to: accounts_path),
variant: "primary",
icon: "plus",
frame: :modal
<%= content_for :page_title, t(".accounts") %>
<%= content_for :page_actions do %>
<%= icon(
"refresh-cw",
as_button: true,
size: "sm",
href: sync_all_accounts_path,
disabled: Current.family.syncing?,
frame: :_top
) %>
</div>
</div>
</header>
<%= render DS::Link.new(
text: "New account",
href: new_account_path(return_to: accounts_path),
variant: "primary",
icon: "plus",
frame: :modal
) %>
<% end %>
<% if @manual_accounts.empty? && @plaid_items.empty? && @simplefin_items.empty? && @lunchflow_items.empty? && @enable_banking_items.empty? && @coinstats_items.empty? && @coinbase_items.empty? && @mercury_items.empty? && @snaptrade_items.empty? && @indexa_capital_items.empty? %>
<%= render "empty" %>

View File

@@ -1,26 +1,23 @@
<header class="flex items-center justify-between">
<h1 class="text-primary text-xl font-medium"><%= t(".categories") %></h1>
<%= content_for :page_title, t(".categories") %>
<%= content_for :page_actions do %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(
variant: "button",
text: "Delete all",
href: destroy_all_categories_path,
method: :delete,
icon: "trash-2",
confirm: CustomConfirm.for_resource_deletion("all categories", high_severity: true)) %>
<% end %>
<div class="flex items-center gap-2">
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(
variant: "button",
text: "Delete all",
href: destroy_all_categories_path,
method: :delete,
icon: "trash-2",
confirm: CustomConfirm.for_resource_deletion("all categories", high_severity: true)) %>
<% end %>
<%= render DS::Link.new(
text: t(".new"),
variant: "primary",
icon: "plus",
href: new_category_path,
frame: :modal
) %>
</div>
</header>
<%= render DS::Link.new(
text: t(".new"),
variant: "primary",
icon: "plus",
href: new_category_path,
frame: :modal
) %>
<% end %>
<div class="bg-container rounded-xl shadow-border-xs p-4">
<% if @categories.any? %>

View File

@@ -1,3 +1,5 @@
<%= content_for :page_title, t(".title") %>
<%= settings_section title: t(".title") do %>
<div class="space-y-4">
<% has_processing = @exports.any? { |e| e.pending? || e.processing? } %>

View File

@@ -1,21 +1,18 @@
<header class="flex items-center justify-between">
<h1 class="text-primary text-xl font-medium"><%= t(".title") %></h1>
<div class="flex items-center gap-2">
<%= render DS::Link.new(
text: t(".merge"),
variant: "outline",
href: merge_family_merchants_path,
frame: :modal
) %>
<%= render DS::Link.new(
text: t(".new"),
variant: "primary",
href: new_family_merchant_path,
frame: :modal
) %>
</div>
</header>
<%= content_for :page_title, t(".title") %>
<%= content_for :page_actions do %>
<%= render DS::Link.new(
text: t(".merge"),
variant: "outline",
href: merge_family_merchants_path,
frame: :modal
) %>
<%= render DS::Link.new(
text: t(".new"),
variant: "primary",
href: new_family_merchant_path,
frame: :modal
) %>
<% end %>
<div class="bg-container rounded-xl shadow-border-xs p-4 space-y-6">
<section class="space-y-3">

View File

@@ -1,3 +1,5 @@
<%= content_for :page_title, t(".title") %>
<%= settings_section title: t(".title") do %>
<div class="space-y-4">
<div class="rounded-xl bg-container-inset space-y-1 p-1">

View File

@@ -125,9 +125,9 @@ end %>
<% end %>
<%# SHARED - Main content %>
<%= tag.main class: class_names("grow overflow-y-auto px-3 lg:px-10 py-4 w-full mx-auto max-w-5xl"), data: { app_layout_target: "content" } do %>
<%= tag.main class: class_names("grow overflow-y-auto px-3 lg:px-10 pt-0 pb-4 w-full mx-auto max-w-5xl"), data: { app_layout_target: "content" } do %>
<% unless intro_mode %>
<div class="hidden lg:flex gap-2 items-center justify-between mb-6">
<div class="hidden lg:flex gap-2 items-center justify-between mb-6 sticky top-0 z-10 -mx-3 lg:-mx-10 px-3 lg:px-10 py-4 bg-surface border-b border-tertiary">
<div class="flex items-center gap-2">
<%= icon("panel-left", as_button: true, data: { action: "app-layout#toggleLeftSidebar" }) %>

View File

@@ -6,25 +6,37 @@
<main class="grow flex h-full">
<div class="relative max-w-4xl mx-auto flex flex-col w-full h-full">
<div class="grow space-y-4 overflow-y-auto px-3 md:px-10 pt-2 md:py-4 pb-20 overscroll-contain [-webkit-overflow-scrolling:touch]">
<% if content_for?(:breadcrumbs) %>
<%= yield :breadcrumbs %>
<% else %>
<%= render "layouts/shared/breadcrumbs", breadcrumbs: @breadcrumbs %>
<% end %>
<div class="grow flex flex-col overflow-y-auto overflow-x-hidden overscroll-contain [-webkit-overflow-scrolling:touch]">
<div class="sticky top-0 z-10 px-3 md:px-10 pt-1.5 md:pt-3 pb-3 bg-surface border-b border-tertiary shrink-0">
<% if content_for?(:breadcrumbs) %>
<%= yield :breadcrumbs %>
<% else %>
<%= render "layouts/shared/breadcrumbs", breadcrumbs: @breadcrumbs %>
<% end %>
<% if content_for?(:page_title) %>
<h1 class="text-primary text-3xl md:text-xl font-medium">
<%= content_for :page_title %>
</h1>
<% end %>
<div class="flex items-center justify-between gap-4 mt-1.5 min-h-9">
<% if content_for?(:page_title) %>
<h1 class="text-primary text-xl font-medium">
<%= content_for :page_title %>
</h1>
<% else %>
<div></div>
<% end %>
<% if content_for?(:page_actions) %>
<div class="flex items-center gap-2 shrink-0">
<%= yield :page_actions %>
</div>
<% end %>
</div>
</div>
<%= yield %>
<%= settings_nav_footer_mobile %>
</div>
<div class="my-4">
<%= settings_nav_footer %>
<div class="grow space-y-4 px-3 md:px-10 pb-20 pt-4">
<%= yield %>
<%= settings_nav_footer_mobile %>
<div class="my-4">
<%= settings_nav_footer %>
</div>
</div>
</div>
</div>
</main>

View File

@@ -1,28 +1,26 @@
<%= content_for :page_title, t("recurring_transactions.title") %>
<%= content_for :page_actions do %>
<% unless @family.recurring_transactions_disabled? %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(
variant: "button",
text: t("recurring_transactions.cleanup_stale"),
href: cleanup_recurring_transactions_path,
method: :post,
icon: "trash-2") %>
<% end %>
<%= render DS::Link.new(
text: t("recurring_transactions.identify_patterns"),
icon: "search",
variant: "outline",
href: identify_recurring_transactions_path,
method: :post
) %>
<% end %>
<% end %>
<div class="space-y-4 flex flex-col">
<header class="flex justify-between items-center text-primary font-medium">
<h1 class="text-xl"><%= t("recurring_transactions.title") %></h1>
<div class="flex items-center gap-2">
<% unless @family.recurring_transactions_disabled? %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(
variant: "button",
text: t("recurring_transactions.cleanup_stale"),
href: cleanup_recurring_transactions_path,
method: :post,
icon: "trash-2") %>
<% end %>
<%= render DS::Link.new(
text: t("recurring_transactions.identify_patterns"),
icon: "search",
variant: "outline",
href: identify_recurring_transactions_path,
method: :post
) %>
<% end %>
</div>
</header>
<div class="bg-container rounded-xl shadow-border-xs p-4">
<div class="flex items-center justify-between">
<div>

View File

@@ -1,44 +1,43 @@
<header class="flex items-center justify-between">
<h1 class="text-primary text-xl font-medium">Rules</h1>
<div class="flex items-center gap-2">
<% if @rules.any? %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(
variant: "button",
text: t("rules.clear_ai_cache.button"),
href: clear_ai_cache_rules_path,
icon: "refresh-cw",
method: :post,
confirm: CustomConfirm.new(
title: t("rules.clear_ai_cache.confirm_title"),
body: t("rules.clear_ai_cache.confirm_body"),
btn_text: t("rules.clear_ai_cache.confirm_button")
)) %>
<% menu.with_item(
variant: "button",
text: "Delete all rules",
href: destroy_all_rules_path,
icon: "trash-2",
method: :delete,
confirm: CustomConfirm.for_resource_deletion("all rules", high_severity: true)) %>
<% end %>
<%= render DS::Link.new(
text: t("rules.apply_all.button"),
variant: "secondary",
href: confirm_all_rules_path,
icon: "play",
frame: :modal
) %>
<%= content_for :page_title, "Rules" %>
<%= content_for :page_actions do %>
<% if @rules.any? %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(
variant: "button",
text: t("rules.clear_ai_cache.button"),
href: clear_ai_cache_rules_path,
icon: "refresh-cw",
method: :post,
confirm: CustomConfirm.new(
title: t("rules.clear_ai_cache.confirm_title"),
body: t("rules.clear_ai_cache.confirm_body"),
btn_text: t("rules.clear_ai_cache.confirm_button")
)) %>
<% menu.with_item(
variant: "button",
text: "Delete all rules",
href: destroy_all_rules_path,
icon: "trash-2",
method: :delete,
confirm: CustomConfirm.for_resource_deletion("all rules", high_severity: true)) %>
<% end %>
<%= render DS::Link.new(
text: "New rule",
variant: "primary",
href: new_rule_path(resource_type: "transaction"),
icon: "plus",
text: t("rules.apply_all.button"),
variant: "secondary",
href: confirm_all_rules_path,
icon: "play",
frame: :modal
) %>
</div>
</header>
<% end %>
<%= render DS::Link.new(
text: "New rule",
variant: "primary",
href: new_rule_path(resource_type: "transaction"),
icon: "plus",
frame: :modal
) %>
<% end %>
<% if self_hosted? %>
<div class="flex items-center gap-2 mb-2 py-4">
<%= icon("circle-alert", size: "sm") %>

View File

@@ -1,7 +1,5 @@
<% if @newly_created && @plain_key %>
<header class="flex items-center justify-between">
<h1 class="text-primary text-xl font-medium">API Key Created Successfully</h1>
</header>
<%= content_for :page_title, "API Key Created Successfully" %>
<div class="bg-container rounded-xl shadow-border-xs p-4">
<div class="space-y-4">
@@ -55,14 +53,14 @@
</div>
</div>
<% elsif @current_api_key %>
<header class="flex items-center justify-between">
<h1 class="text-primary text-xl font-medium">Your API Key</h1>
<%= content_for :page_title, "Your API Key" %>
<%= content_for :page_actions do %>
<%= render DS::Link.new(
text: "Create New Key",
href: new_settings_api_key_path(regenerate: true),
variant: "secondary"
) %>
</header>
<% end %>
<div class="bg-container rounded-xl shadow-border-xs p-4">
<div class="space-y-4">
@@ -147,14 +145,14 @@
</div>
</div>
<% else %>
<header class="flex items-center justify-between">
<h1 class="text-primary text-xl font-medium"><%= t(".no_api_key.title") %></h1>
<%= content_for :page_title, t(".no_api_key.title") %>
<%= content_for :page_actions do %>
<%= render DS::Link.new(
text: t(".no_api_key.create_api_key"),
href: new_settings_api_key_path,
variant: "primary"
) %>
</header>
<% end %>
<div class="bg-container rounded-xl shadow-border-xs p-4">
<div class="space-y-4">

View File

@@ -1,7 +1,8 @@
<%= content_for :page_title, "LLM Usage & Costs" %>
<div class="bg-container rounded-xl shadow-border-xs p-4">
<div class="mb-6">
<h1 class="text-2xl font-semibold text-primary">LLM Usage & Costs</h1>
<p class="text-sm text-secondary mt-1">Track your AI usage and estimated costs</p>
<p class="text-sm text-secondary">Track your AI usage and estimated costs</p>
</div>
<!-- Date Range Filter -->

View File

@@ -1,7 +1,5 @@
<header class="flex items-center justify-between">
<h1 class="text-primary text-xl font-medium"><%= t(".tags") %></h1>
<div class="flex items-center gap-2">
<%= content_for :page_title, t(".tags") %>
<%= content_for :page_actions do %>
<%= render DS::Menu.new do |menu| %>
<% menu.with_item(
variant: "button",
@@ -12,17 +10,14 @@
confirm: CustomConfirm.for_resource_deletion("all tags", high_severity: true)) %>
<% end %>
<%= render DS::Link.new(
text: t(".new"),
variant: "primary",
href: new_tag_path,
icon: "plus",
frame: :modal
) %>
</div>
</header>
<%= render DS::Link.new(
text: t(".new"),
variant: "primary",
href: new_tag_path,
icon: "plus",
frame: :modal
) %>
<% end %>
<div class="bg-container rounded-xl shadow-border-xs p-4">
<% if @tags.any? %>