mirror of
https://github.com/we-promise/sure.git
synced 2026-04-19 12:04:08 +00:00
First cut of a simplified "intro" UI layout (#265)
* First cut of a simplified "intro" UI layout * Linter * Add guest role and intro-only access * Fix guest role UI defaults (#940) Use enum predicate to avoid missing role helper. * Remove legacy user role mapping (#941) Drop the unused user role references in role normalization and SSO role mapping forms to avoid implying a role that never existed. Refs: #0 * Remove role normalization (#942) Remove role normalization Roles are now stored directly without legacy mappings. * Revert role mapping logic * Remove `normalize_role_settings` * Remove unnecessary migration * Make `member` the default * Broken `.erb` --------- Signed-off-by: Juan José Mata <juanjo.mata@gmail.com>
This commit is contained in:
@@ -6,13 +6,51 @@ module Assistant::Configurable
|
||||
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,
|
||||
|
||||
@@ -171,7 +171,7 @@ class Demo::Generator
|
||||
onboarded_at: onboarded ? Time.current : nil
|
||||
)
|
||||
|
||||
# Member user
|
||||
# Family member user
|
||||
family.users.create!(
|
||||
email: "partner_#{email}",
|
||||
first_name: "Eve",
|
||||
|
||||
@@ -11,7 +11,7 @@ class Invitation < ApplicationRecord
|
||||
end
|
||||
|
||||
validates :email, presence: true, format: { with: URI::MailTo::EMAIL_REGEXP }
|
||||
validates :role, presence: true, inclusion: { in: %w[admin member] }
|
||||
validates :role, presence: true, inclusion: { in: %w[admin member guest] }
|
||||
validates :token, presence: true, uniqueness: true
|
||||
validates_uniqueness_of :email, scope: :family_id, message: "has already been invited to this family"
|
||||
validate :inviter_is_admin
|
||||
@@ -32,7 +32,7 @@ class Invitation < ApplicationRecord
|
||||
return false unless emails_match?(user)
|
||||
|
||||
transaction do
|
||||
user.update!(family_id: family_id, role: role)
|
||||
user.update!(family_id: family_id, role: role.to_s)
|
||||
update!(accepted_at: Time.current)
|
||||
end
|
||||
true
|
||||
|
||||
@@ -104,11 +104,12 @@ class SsoProvider < ApplicationRecord
|
||||
end
|
||||
|
||||
def validate_default_role_setting
|
||||
default_role = settings&.dig("default_role")
|
||||
default_role = settings&.dig("default_role") || settings&.dig(:default_role)
|
||||
default_role = default_role.to_s
|
||||
return if default_role.blank?
|
||||
|
||||
unless User.roles.key?(default_role)
|
||||
errors.add(:settings, "default_role must be member, admin, or super_admin")
|
||||
errors.add(:settings, "default_role must be guest, member, admin, or super_admin")
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -50,7 +50,11 @@ 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 :role, { guest: "guest", 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
|
||||
before_validation :apply_role_based_ui_defaults
|
||||
|
||||
# Returns the appropriate role for a new user creating a family.
|
||||
# The very first user of an instance becomes super_admin; subsequent users
|
||||
@@ -139,6 +143,11 @@ class User < ApplicationRecord
|
||||
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
|
||||
|
||||
# SSO-only users have OIDC identities but no local password.
|
||||
# They cannot use password reset or local login.
|
||||
def sso_only?
|
||||
@@ -307,6 +316,39 @@ class User < ApplicationRecord
|
||||
end
|
||||
|
||||
private
|
||||
def apply_ui_layout_defaults
|
||||
self.ui_layout = (ui_layout.presence || self.class.default_ui_layout)
|
||||
end
|
||||
|
||||
def apply_role_based_ui_defaults
|
||||
if ui_layout_intro?
|
||||
if guest?
|
||||
self.show_sidebar = false
|
||||
self.show_ai_sidebar = false
|
||||
self.ai_enabled = true
|
||||
else
|
||||
self.ui_layout = "dashboard"
|
||||
end
|
||||
elsif guest?
|
||||
self.ui_layout = "intro"
|
||||
self.show_sidebar = false
|
||||
self.show_ai_sidebar = false
|
||||
self.ai_enabled = true
|
||||
end
|
||||
|
||||
if leaving_guest_role?
|
||||
self.show_sidebar = true unless show_sidebar
|
||||
self.show_ai_sidebar = true unless show_ai_sidebar
|
||||
end
|
||||
end
|
||||
|
||||
def leaving_guest_role?
|
||||
return false unless will_save_change_to_role?
|
||||
|
||||
previous_role, new_role = role_change_to_be_saved
|
||||
previous_role == "guest" && new_role != "guest"
|
||||
end
|
||||
|
||||
def skip_password_validation?
|
||||
skip_password_validation == true
|
||||
end
|
||||
|
||||
Reference in New Issue
Block a user