Add "Reset account" followed by sample data preload (#163)

* Add reset with sample data option on profile settings

* No need for "member" user in preload

* Cleanup/shorten copy
This commit is contained in:
Juan José Mata
2025-09-25 11:43:23 +02:00
committed by GitHub
parent 3264a96249
commit dfd467ccb5
12 changed files with 138 additions and 10 deletions

View File

@@ -1,6 +1,6 @@
class UsersController < ApplicationController
before_action :set_user
before_action :ensure_admin, only: :reset
before_action :ensure_admin, only: %i[reset reset_with_sample_data]
def update
@user = Current.user
@@ -40,6 +40,11 @@ class UsersController < ApplicationController
redirect_to settings_profile_path, notice: t(".success")
end
def reset_with_sample_data
FamilyResetJob.perform_later(Current.family, load_sample_data_for_email: @user.email)
redirect_to settings_profile_path, notice: t(".success")
end
def destroy
if @user.deactivate
Current.session.destroy

View File

@@ -1,7 +1,7 @@
class FamilyResetJob < ApplicationJob
queue_as :low_priority
def perform(family)
def perform(family, load_sample_data_for_email: nil)
# Delete all family data except users
ActiveRecord::Base.transaction do
# Delete accounts and related data
@@ -12,7 +12,11 @@ class FamilyResetJob < ApplicationJob
family.plaid_items.destroy_all
family.imports.destroy_all
family.budgets.destroy_all
end
if load_sample_data_for_email.present?
Demo::Generator.new.generate_new_user_data_for!(family.reload, email: load_sample_data_for_email)
else
family.sync_later
end
end

View File

@@ -1,3 +1,5 @@
require "securerandom"
class Demo::Generator
# @param seed [Integer, String, nil] Seed value used to initialise the internal PRNG. If nil, the ENV variable DEMO_DATA_SEED will
# be honoured and default to a random seed when not present.
@@ -59,6 +61,25 @@ class Demo::Generator
end
end
def generate_new_user_data_for!(family, email:)
with_timing(__method__, max_seconds: 1000) do
family = family.reload
admin_user = ensure_admin_user!(family, email)
puts "📊 Creating sample financial data for #{family.name}..."
ActiveRecord::Base.transaction do
create_realistic_categories!(family)
create_realistic_accounts!(family)
create_realistic_transactions!(family)
generate_budget_auto_fill!(family)
end
family.sync_later
puts "✅ Sample data loaded successfully!"
end
end
# Generate comprehensive realistic demo data with multi-currency
def generate_default_data!(skip_clear: false, email: "user@example.com")
if skip_clear
@@ -118,6 +139,17 @@ class Demo::Generator
Demo::DataCleaner.new.destroy_everything!
end
def ensure_admin_user!(family, email)
user = family.users.find_by(email: email)
return user if user&.admin? || user&.super_admin?
raise ActiveRecord::RecordNotFound, "No admin user with email #{email} found in family ##{family.id}"
end
def partner_email_for(email)
"partner_#{email}"
end
def create_family_and_users!(family_name, email, onboarded:, subscribed:)
family = Family.create!(
name: family_name,

View File

@@ -137,9 +137,30 @@
href: reset_user_path(@user),
method: :delete,
confirm: CustomConfirm.new(
title: "Reset account?",
body: "This will delete all data associated with your account. Your user profile will remain active.",
btn_text: "Reset account",
title: t(".confirm_reset.title"),
body: t(".confirm_reset.body"),
btn_text: t(".reset_account"),
destructive: true,
high_severity: true
)
) %>
</div>
<div class="flex flex-col md:flex-row md:items-center md:justify-between gap-4">
<div class="w-full md:w-2/3">
<h3 class="font-medium text-primary"><%= t(".reset_account_with_sample_data") %></h3>
<p class="text-secondary text-sm"><%= t(".reset_account_with_sample_data_warning") %></p>
</div>
<%= render DS::Button.new(
text: t(".reset_account_with_sample_data"),
variant: "destructive",
href: reset_with_sample_data_user_path(@user),
method: :delete,
confirm: CustomConfirm.new(
title: t(".confirm_reset_with_sample_data.title"),
body: t(".confirm_reset_with_sample_data.body"),
btn_text: t(".reset_account_with_sample_data"),
destructive: true,
high_severity: true
)