diff --git a/app/controllers/pages_controller.rb b/app/controllers/pages_controller.rb index 07a7d10f0e1..8f8961f43c2 100644 --- a/app/controllers/pages_controller.rb +++ b/app/controllers/pages_controller.rb @@ -4,6 +4,10 @@ class PagesController < ApplicationController skip_authentication only: :redis_configuration_error def dashboard + if Current.user&.ui_layout_intro? + redirect_to chats_path and return + end + @balance_sheet = Current.family.balance_sheet @accounts = Current.family.accounts.visible.with_attached_logo @@ -45,6 +49,10 @@ def dashboard @breadcrumbs = [ [ "Home", root_path ], [ "Dashboard", nil ] ] end + def intro + @breadcrumbs = [ [ "Home", chats_path ], [ "Intro", nil ] ] + end + def changelog @release_notes = github_provider.fetch_latest_release_notes diff --git a/app/controllers/settings/profiles_controller.rb b/app/controllers/settings/profiles_controller.rb index 2839b60e0cd..6e8dbbf0e15 100644 --- a/app/controllers/settings/profiles_controller.rb +++ b/app/controllers/settings/profiles_controller.rb @@ -1,5 +1,5 @@ class Settings::ProfilesController < ApplicationController - layout "settings" + layout :layout_for_settings_profile def show @user = Current.user @@ -36,4 +36,10 @@ def destroy redirect_to settings_profile_path end + + private + + def layout_for_settings_profile + Current.user&.ui_layout_intro? ? "application" : "settings" + end end diff --git a/app/models/assistant/configurable.rb b/app/models/assistant/configurable.rb index 1da95d14b87..d0285f14f96 100644 --- a/app/models/assistant/configurable.rb +++ b/app/models/assistant/configurable.rb @@ -6,13 +6,51 @@ def config_for(chat) preferred_currency = Money::Currency.new(chat.user.family.currency) preferred_date_format = chat.user.family.date_format - { - instructions: default_instructions(preferred_currency, preferred_date_format), - functions: default_functions - } + if chat.user.ui_layout_intro? + { + instructions: intro_instructions(preferred_currency, preferred_date_format), + functions: [] + } + else + { + instructions: default_instructions(preferred_currency, preferred_date_format), + functions: default_functions + } + end end private + def intro_instructions(preferred_currency, preferred_date_format) + <<~PROMPT + ## Your identity + + You are Sure, a warm and curious financial guide welcoming a new household to the Sure personal finance application. + + ## Your purpose + + Host an introductory conversation that helps you understand the user's stage of life, financial responsibilities, and near-term priorities so future guidance feels personal and relevant. + + ## Conversation approach + + - Ask one thoughtful question at a time and tailor follow-ups based on what the user shares. + - Reflect key details back to the user to confirm understanding. + - Keep responses concise, friendly, and free of filler phrases. + - If the user requests detailed analytics, let them know the dashboard experience will cover it soon and guide them back to sharing context. + + ## Information to uncover + + - Household composition and stage of life milestones (education, career, retirement, dependents, caregiving, etc.). + - Primary financial goals, concerns, and timelines. + - Notable upcoming events or obligations. + + ## Formatting guidelines + + - Use markdown for any lists or emphasis. + - When money or timeframes are discussed, format currency with #{preferred_currency.symbol} (#{preferred_currency.iso_code}) and dates using #{preferred_date_format}. + - Do not call external tools or functions. + PROMPT + end + def default_functions [ Assistant::Function::GetTransactions, diff --git a/app/models/user.rb b/app/models/user.rb index a3ca25adad5..569528c8308 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -23,6 +23,9 @@ class User < ApplicationRecord normalizes :first_name, :last_name, with: ->(value) { value.strip.presence } enum :role, { member: "member", admin: "admin", super_admin: "super_admin" }, validate: true + enum :ui_layout, { dashboard: "dashboard", intro: "intro" }, validate: true, prefix: true + + before_validation :apply_ui_layout_defaults, on: :create has_one_attached :profile_image do |attachable| attachable.variant :thumbnail, resize_to_fill: [ 300, 300 ], convert: :webp, saver: { quality: 80 } @@ -96,6 +99,11 @@ def ai_enabled? ai_enabled && ai_available? end + def self.default_ui_layout + layout = Rails.application.config.x.ui&.default_layout || "dashboard" + layout.in?(%w[intro dashboard]) ? layout : "dashboard" + end + # Deactivation validate :can_deactivate, if: -> { active_changed? && !active } after_update_commit :purge_later, if: -> { saved_change_to_active?(from: true, to: false) } @@ -170,6 +178,16 @@ def account_order end private + def apply_ui_layout_defaults + self.ui_layout = (ui_layout.presence || self.class.default_ui_layout) + + if ui_layout_intro? + self.show_sidebar = false + self.show_ai_sidebar = false + self.ai_enabled = true + end + end + def ensure_valid_profile_image return unless profile_image.attached? diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 02854276b29..4b6db6804da 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,9 +1,18 @@ -<% mobile_nav_items = [ - { name: "Home", path: root_path, icon: "pie-chart", icon_custom: false, active: page_active?(root_path) }, - { name: "Transactions", path: transactions_path, icon: "credit-card", icon_custom: false, active: page_active?(transactions_path) }, - { name: "Budgets", path: budgets_path, icon: "map", icon_custom: false, active: page_active?(budgets_path) }, - { name: "Assistant", path: chats_path, icon: "icon-assistant", icon_custom: true, active: page_active?(chats_path), mobile_only: true } -] %> +<% intro_mode = Current.user&.ui_layout_intro? %> +<% home_path = intro_mode ? chats_path : root_path %> +<% mobile_nav_items = if intro_mode + [ + { name: "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: "Home", path: root_path, icon: "pie-chart", icon_custom: false, active: page_active?(root_path) }, + { name: "Transactions", path: transactions_path, icon: "credit-card", icon_custom: false, active: page_active?(transactions_path) }, + { name: "Budgets", path: budgets_path, icon: "map", icon_custom: false, active: page_active?(budgets_path) }, + { name: "Assistant", path: chats_path, icon: "icon-assistant", icon_custom: true, active: page_active?(chats_path), mobile_only: true } + ] +end %> <% desktop_nav_items = mobile_nav_items.reject { |item| item[:mobile_only] } %> <% expanded_sidebar_class = "w-full" %> @@ -19,9 +28,11 @@ @@ -112,18 +126,20 @@ <%# 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 %> -