mirror of
https://github.com/we-promise/sure.git
synced 2026-04-09 15:24:48 +00:00
* Add full import/export support for rules with versioned JSON schema This commit implements comprehensive import/export functionality for rules, allowing users to back up and restore their rule definitions. Key features: - Export rules to both CSV and NDJSON formats with versioned schema (v1) - Import rules from CSV with full support for nested conditions and actions - UUID to name mapping for categories and merchants for portability - Support for compound conditions with sub-conditions - Comprehensive test coverage for export and import functionality - UI integration for rules import in the imports interface Technical details: - Extended Family::DataExporter to generate rules.csv and include rules in all.ndjson - Created RuleImport model following the existing Import STI pattern - Added migration for rule-specific columns in import_rows table - Implemented serialization helpers to map UUIDs to human-readable names - Added i18n support for the new import option - Included versioning in NDJSON export to support future schema evolution The implementation ensures rules can be safely exported from one family and imported into another, even when category/merchant IDs differ, by mapping between names and IDs during export/import. * Fix AR migration version * Mention support for rules export * Rabbit suggestion * Fix tests * Missed schema.rb * Fix sample CSV download for rule import * Fix parsing in Rules import * Fix tests * Rule import message i18n * Export tag names, not UUIDs * Make sure tags are created if needed at import * Avoid test errors when running in parallel --------- Co-authored-by: Claude <noreply@anthropic.com>
147 lines
6.5 KiB
Plaintext
147 lines
6.5 KiB
Plaintext
<%= render DS::Dialog.new do |dialog| %>
|
|
<% dialog.with_header(title: t(".title"), subtitle: t(".description")) %>
|
|
|
|
<% dialog.with_body do %>
|
|
<div class="rounded-xl bg-container-inset p-1">
|
|
<h3 class="uppercase text-secondary text-xs font-medium px-3 py-1.5"><%= t(".sources") %></h3>
|
|
<ul class="bg-container shadow-border-xs rounded-lg">
|
|
<li>
|
|
<% if @pending_import.present? && (params[:type].nil? || params[:type] == @pending_import.type) %>
|
|
<%= link_to import_path(@pending_import), class: "flex items-center justify-between p-4 group cursor-pointer", data: { turbo: false } do %>
|
|
<div class="flex items-center gap-2">
|
|
<div class="bg-orange-500/5 rounded-md w-8 h-8 flex items-center justify-center">
|
|
<span class="text-orange-500">
|
|
<%= icon("loader", color: "current") %>
|
|
</span>
|
|
</div>
|
|
<span class="text-sm text-primary group-hover:text-secondary">
|
|
<%= t(".resume", type: @pending_import.type.titleize) %>
|
|
</span>
|
|
</div>
|
|
<%= icon("chevron-right") %>
|
|
<% end %>
|
|
|
|
<%= render "shared/ruler" %>
|
|
</li>
|
|
<% end %>
|
|
|
|
<% if Current.family.accounts.any? && (params[:type].nil? || params[:type] == "TransactionImport") %>
|
|
<li>
|
|
<%= button_to imports_path(import: { type: "TransactionImport" }), class: "flex items-center justify-between p-4 group cursor-pointer w-full", data: { turbo: false } do %>
|
|
<div class="flex items-center gap-2">
|
|
<div class="bg-indigo-500/5 rounded-md w-8 h-8 flex items-center justify-center">
|
|
<span class="text-indigo-500">
|
|
<%= icon("file-spreadsheet", color: "current") %>
|
|
</span>
|
|
</div>
|
|
<span class="text-sm text-primary group-hover:text-secondary">
|
|
<%= t(".import_transactions") %>
|
|
</span>
|
|
</div>
|
|
<%= icon("chevron-right") %>
|
|
<% end %>
|
|
|
|
<%= render "shared/ruler" %>
|
|
</li>
|
|
<% end %>
|
|
|
|
<% if Current.family.accounts.any? && (params[:type].nil? || params[:type] == "TradeImport") %>
|
|
<li>
|
|
<%= button_to imports_path(import: { type: "TradeImport" }), class: "flex items-center justify-between p-4 group cursor-pointer w-full", data: { turbo: false } do %>
|
|
<div class="flex items-center gap-2">
|
|
<div class="bg-yellow-500/5 rounded-md w-8 h-8 flex items-center justify-center">
|
|
<span class="text-yellow-500">
|
|
<%= icon("square-percent", color: "current") %>
|
|
</span>
|
|
</div>
|
|
<span class="text-sm text-primary group-hover:text-secondary">
|
|
<%= t(".import_portfolio") %>
|
|
</span>
|
|
</div>
|
|
<%= icon("chevron-right") %>
|
|
<% end %>
|
|
|
|
<%= render "shared/ruler" %>
|
|
</li>
|
|
<% end %>
|
|
|
|
<% if params[:type].nil? || params[:type] == "AccountImport" %>
|
|
<li>
|
|
<%= button_to imports_path(import: { type: "AccountImport" }), class: "flex items-center justify-between p-4 group cursor-pointer w-full", data: { turbo: false } do %>
|
|
<div class="flex items-center gap-2">
|
|
<div class="bg-violet-500/5 rounded-md w-8 h-8 flex items-center justify-center">
|
|
<span class="text-violet-500">
|
|
<%= icon("building", color: "current") %>
|
|
</span>
|
|
</div>
|
|
<span class="text-sm text-primary group-hover:text-secondary">
|
|
<%= t(".import_accounts") %>
|
|
</span>
|
|
</div>
|
|
<%= icon("chevron-right") %>
|
|
<% end %>
|
|
|
|
<%= render "shared/ruler" %>
|
|
</li>
|
|
<% end %>
|
|
|
|
<% if params[:type].nil? || params[:type] == "CategoryImport" %>
|
|
<li>
|
|
<%= button_to imports_path(import: { type: "CategoryImport" }), class: "flex items-center justify-between p-4 group cursor-pointer w-full", data: { turbo: false } do %>
|
|
<div class="flex items-center gap-2">
|
|
<div class="bg-blue-500/5 rounded-md w-8 h-8 flex items-center justify-center">
|
|
<span class="text-blue-500">
|
|
<%= icon("shapes", color: "current") %>
|
|
</span>
|
|
</div>
|
|
<span class="text-sm text-primary group-hover:text-secondary">
|
|
<%= t(".import_categories") %>
|
|
</span>
|
|
</div>
|
|
<%= icon("chevron-right") %>
|
|
<% end %>
|
|
|
|
<%= render "shared/ruler" %>
|
|
</li>
|
|
<% end %>
|
|
|
|
<% if params[:type].nil? || params[:type] == "RuleImport" %>
|
|
<li>
|
|
<%= button_to imports_path(import: { type: "RuleImport" }), class: "flex items-center justify-between p-4 group cursor-pointer w-full", data: { turbo: false } do %>
|
|
<div class="flex items-center gap-2">
|
|
<div class="bg-green-500/5 rounded-md w-8 h-8 flex items-center justify-center">
|
|
<span class="text-green-500">
|
|
<%= icon("workflow", color: "current") %>
|
|
</span>
|
|
</div>
|
|
<span class="text-sm text-primary group-hover:text-secondary">
|
|
<%= t(".import_rules") %>
|
|
</span>
|
|
</div>
|
|
<%= icon("chevron-right") %>
|
|
<% end %>
|
|
|
|
<%= render "shared/ruler" %>
|
|
</li>
|
|
<% end %>
|
|
|
|
<% if Current.family.accounts.any? && (params[:type].nil? || params[:type] == "MintImport" || params[:type] == "TransactionImport") %>
|
|
<li>
|
|
<%= button_to imports_path(import: { type: "MintImport" }), class: "flex items-center justify-between p-4 group w-full", data: { turbo: false } do %>
|
|
<div class="flex items-center gap-2">
|
|
<%= image_tag("mint-logo.jpeg", alt: "Mint logo", class: "w-8 h-8 rounded-md") %>
|
|
<span class="text-sm text-primary">
|
|
<%= t(".import_mint") %>
|
|
</span>
|
|
</div>
|
|
<%= icon("chevron-right") %>
|
|
<% end %>
|
|
|
|
<%= render "shared/ruler" %>
|
|
</li>
|
|
<% end %>
|
|
</ul>
|
|
</div>
|
|
<% end %>
|
|
<% end %>
|