mirror of
https://github.com/we-promise/sure.git
synced 2026-05-30 15:59:02 +00:00
Add require_beta_features! to GoalsController and GoalPledgesController, hide the Goals nav item for non-beta users, and tag index/show headers with the Beta pill marker. Update controller tests to enable the preference in setup and assert the redirect for users without access.
216 lines
10 KiB
Plaintext
216 lines
10 KiB
Plaintext
<% intro_mode = Current.user&.ui_layout_intro? %>
|
|
<% home_path = intro_mode ? chats_path : root_path %>
|
|
<% mobile_nav_items = if intro_mode
|
|
[
|
|
{ name: t(".nav.home"), path: chats_path, icon: "home", icon_custom: false, active: page_active?(chats_path) },
|
|
{ name: "Intro", path: intro_path, icon: "sparkles", icon_custom: false, active: page_active?(intro_path) }
|
|
]
|
|
else
|
|
[
|
|
{ name: t(".nav.home"), path: root_path, icon: "pie-chart", icon_custom: false, active: page_active?(root_path) },
|
|
{ name: t(".nav.transactions"), path: transactions_path, icon: "credit-card", icon_custom: false, active: page_active?(transactions_path) },
|
|
{ name: t(".nav.reports"), path: reports_path, icon: "chart-bar", icon_custom: false, active: page_active?(reports_path) },
|
|
{ name: t(".nav.budgets"), path: budgets_path, icon: "map", icon_custom: false, active: page_active?(budgets_path) },
|
|
(beta_features_enabled? ? { name: t(".nav.goals"), path: goals_path, icon: "piggy-bank", icon_custom: false, active: page_active?(goals_path) } : nil),
|
|
{ name: t(".nav.assistant"), path: chats_path, icon: "icon-assistant", icon_custom: true, active: page_active?(chats_path), mobile_only: true }
|
|
].compact
|
|
end %>
|
|
|
|
<% desktop_nav_items = mobile_nav_items.reject { |item| item[:mobile_only] } %>
|
|
<% expanded_sidebar_class = "w-full" %>
|
|
<% collapsed_sidebar_class = "w-0 overflow-hidden" %>
|
|
|
|
<%= render "layouts/shared/htmldoc" do %>
|
|
<div
|
|
class="flex flex-col lg:flex-row h-full bg-surface"
|
|
data-controller="app-layout privacy-mode"
|
|
data-app-layout-expanded-sidebar-class="<%= expanded_sidebar_class %>"
|
|
data-app-layout-collapsed-sidebar-class="<%= collapsed_sidebar_class %>"
|
|
data-app-layout-user-id-value="<%= Current.user.id %>">
|
|
<%= link_to t("layouts.application.skip_to_main"), "#main",
|
|
class: "sr-only focus:not-sr-only focus:fixed focus:top-2 focus:left-2 focus:z-50 focus:px-3 focus:py-2 focus:rounded-lg focus:bg-container focus:text-primary focus:shadow-border-xs" %>
|
|
|
|
<div
|
|
class="hidden fixed inset-0 bg-surface z-20 h-full w-full pt-[calc(env(safe-area-inset-top)+0.75rem)] pr-3 pb-[calc(env(safe-area-inset-bottom)+0.75rem)] pl-3 overflow-y-auto transition-all duration-300"
|
|
data-app-layout-target="mobileSidebar">
|
|
<% unless intro_mode %>
|
|
<div class="mb-2">
|
|
<%= icon("x", as_button: true, data: { action: "app-layout#closeMobileSidebar" }) %>
|
|
</div>
|
|
<% end %>
|
|
|
|
<%= render(
|
|
"accounts/account_sidebar_tabs",
|
|
family: Current.family,
|
|
active_tab: @account_group_tab,
|
|
mobile: true
|
|
) %>
|
|
</div>
|
|
|
|
<%# MOBILE - Top nav %>
|
|
<nav class="lg:hidden flex justify-between items-center p-3 pt-[calc(env(safe-area-inset-top)+0.75rem)]">
|
|
<% if intro_mode %>
|
|
<% else %>
|
|
<%= icon("panel-left", as_button: true, data: { action: "app-layout#openMobileSidebar"}) %>
|
|
<% end %>
|
|
|
|
<%= link_to home_path, class: "block" do %>
|
|
<%= image_tag "logomark-color.svg", class: "w-9 h-9 mx-auto" %>
|
|
<% end %>
|
|
|
|
<div class="flex items-center gap-1">
|
|
<button type="button"
|
|
class="inline-flex items-center justify-center w-8 h-8 rounded-lg text-secondary hover:text-primary transition-colors"
|
|
data-action="click->privacy-mode#toggle"
|
|
data-privacy-mode-target="toggle"
|
|
title="<%= t("layouts.application.privacy_mode") %>"
|
|
aria-label="<%= t("layouts.application.privacy_mode") %>"
|
|
aria-pressed="false">
|
|
<%= icon("eye-off", size: "sm", data: { privacy_mode_target: "iconOff" }) %>
|
|
<%= icon("eye", size: "sm", class: "hidden", data: { privacy_mode_target: "iconOn" }) %>
|
|
</button>
|
|
<%= render "users/user_menu", user: Current.user, placement: "bottom-end", offset: 12, intro_mode: intro_mode %>
|
|
</div>
|
|
</nav>
|
|
|
|
<%# DESKTOP - Left navbar %>
|
|
<div class="hidden lg:block border-r border-divider">
|
|
<nav class="h-full flex flex-col shrink-0 w-[84px] py-4">
|
|
<div class="pl-2 mb-3">
|
|
<%= link_to home_path, class: "block" do %>
|
|
<%= image_tag "logomark-color.svg", class: "w-9 h-9 mx-auto" %>
|
|
<% end %>
|
|
</div>
|
|
|
|
<ul class="space-y-0.5">
|
|
<% desktop_nav_items.reject { |item| item[:mobile_only] }.each do |nav_item| %>
|
|
<li>
|
|
<%= render "layouts/shared/nav_item", **nav_item %>
|
|
</li>
|
|
<% end %>
|
|
</ul>
|
|
|
|
<div class="pl-2 mt-auto mx-auto flex flex-col gap-2">
|
|
<%= render DS::Link.new(
|
|
variant: "icon",
|
|
icon: "message-circle-question",
|
|
href: "https://discord.gg/36ZGBsxYEK",
|
|
target: "_blank"
|
|
) %>
|
|
|
|
<%= render "users/user_menu", user: Current.user, intro_mode: intro_mode %>
|
|
</div>
|
|
</nav>
|
|
</div>
|
|
|
|
<%# DESKTOP - Left sidebar %>
|
|
<%= tag.div class: class_names(
|
|
"hidden lg:block py-4 shrink-0 max-w-[320px] transition-all duration-300 border-divider",
|
|
Current.user.show_sidebar? ? [expanded_sidebar_class, "border-r"] : [collapsed_sidebar_class, "border-r-0"],
|
|
),
|
|
inert: !Current.user.show_sidebar?,
|
|
data: { app_layout_target: "leftSidebar" } do %>
|
|
<div class="px-4 h-full flex flex-col overflow-y-auto">
|
|
<% if content_for?(:sidebar) %>
|
|
<%= yield :sidebar %>
|
|
<% else %>
|
|
<div class="grow">
|
|
<%= render "accounts/account_sidebar_tabs", family: Current.family, active_tab: @account_group_tab %>
|
|
</div>
|
|
|
|
<% if Current.family.trialing? && !self_hosted? %>
|
|
<div class="px-4 py-3 space-y-4 bg-container shadow-border-xs rounded-xl">
|
|
<div class="flex items-start justify-between">
|
|
<div>
|
|
<p class="text-sm font-medium text-primary"><%= t("layouts.trial.open_demo") %></p>
|
|
<p class="text-sm text-secondary"><%= t("layouts.trial.data_deleted_in_days", days: Current.family.days_left_in_trial) %></p>
|
|
</div>
|
|
|
|
<%= render DS::Link.new(
|
|
text: t("layouts.trial.contribute"),
|
|
href: upgrade_subscription_path,
|
|
) %>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-0.5 h-1.5">
|
|
<div class="h-full bg-warning rounded-full" style="width: <%= Current.family.percentage_of_trial_completed %>%"></div>
|
|
<div class="h-full bg-surface-inset rounded-full" style="width: <%= Current.family.percentage_of_trial_remaining %>%"></div>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
<% end %>
|
|
</div>
|
|
<% end %>
|
|
|
|
<%# SHARED - Main content %>
|
|
<%= tag.main id: "main", class: class_names("grow overflow-y-auto px-3 lg:px-10 w-full mx-auto pb-[calc(5rem+env(safe-area-inset-bottom))] lg:pb-0"), data: { app_layout_target: "content", viewport_target: "content" } do %>
|
|
<% unless intro_mode %>
|
|
<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" }) %>
|
|
|
|
<% if content_for?(:breadcrumbs) %>
|
|
<%= yield :breadcrumbs %>
|
|
<% else %>
|
|
<%= render "layouts/shared/breadcrumbs", breadcrumbs: @breadcrumbs %>
|
|
<% end %>
|
|
</div>
|
|
|
|
<div class="flex items-center gap-2">
|
|
<button type="button"
|
|
class="inline-flex items-center justify-center w-8 h-8 rounded-lg text-secondary hover:text-primary hover:bg-surface-hover transition-colors"
|
|
data-action="click->privacy-mode#toggle"
|
|
data-privacy-mode-target="toggle"
|
|
title="<%= t("layouts.application.privacy_mode") %>"
|
|
aria-label="<%= t("layouts.application.privacy_mode") %>"
|
|
aria-pressed="false">
|
|
<%= icon("eye-off", size: "sm", data: { privacy_mode_target: "iconOff" }) %>
|
|
<%= icon("eye", size: "sm", class: "hidden", data: { privacy_mode_target: "iconOn" }) %>
|
|
</button>
|
|
|
|
<%= icon("panel-right", as_button: true, data: { action: "app-layout#toggleRightSidebar" }) %>
|
|
</div>
|
|
</div>
|
|
<% end %>
|
|
|
|
<% if content_for?(:page_header) %>
|
|
<%= yield :page_header %>
|
|
<% end %>
|
|
|
|
<%= yield %>
|
|
<% end %>
|
|
|
|
<%# DESKTOP - Right sidebar %>
|
|
<%= tag.div class: class_names(
|
|
"hidden lg:block h-full shrink-0 max-w-[400px] transition-all duration-300 border-divider",
|
|
Current.user.show_ai_sidebar? ? [expanded_sidebar_class, "border-l"] : [collapsed_sidebar_class, "border-l-0"],
|
|
),
|
|
inert: !Current.user.show_ai_sidebar?,
|
|
data: { app_layout_target: "rightSidebar" } do %>
|
|
<%= tag.div id: "chat-container", class: "relative h-full px-4 overflow-y-auto", data: { controller: "chat hotkey", turbo_permanent: true } do %>
|
|
<div class="flex flex-col h-full justify-between shrink-0">
|
|
<%= turbo_frame_tag chat_frame, src: chat_view_path(@chat), loading: "lazy", class: "h-full" do %>
|
|
<div class="flex justify-center items-center h-full">
|
|
<%= icon("loader-circle", class: "animate-spin") %>
|
|
</div>
|
|
<% end %>
|
|
</div>
|
|
|
|
<% unless Current.user.ai_enabled? %>
|
|
<div class="absolute backdrop-blur-lg inset-0 h-full w-full flex flex-col justify-center items-center px-4">
|
|
<%= render "chats/ai_consent" %>
|
|
</div>
|
|
<% end %>
|
|
<% end %>
|
|
<% end %>
|
|
|
|
<%# MOBILE - Bottom Nav %>
|
|
<%= tag.nav class: "lg:hidden fixed bottom-0 left-0 right-0 bg-surface z-10 border-t border-tertiary flex justify-around pb-[env(safe-area-inset-bottom)]",
|
|
data: { viewport_target: "bottomNav" } do %>
|
|
<% mobile_nav_items.each do |nav_item| %>
|
|
<%= render "layouts/shared/nav_item", **nav_item %>
|
|
<% end %>
|
|
<% end %>
|
|
</div>
|
|
<% end %>
|