Skip to content

Commit

Permalink
Complete import preview view functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
zachgoll committed May 13, 2024
1 parent 5b92d7f commit 567abd7
Show file tree
Hide file tree
Showing 14 changed files with 174 additions and 139 deletions.
12 changes: 9 additions & 3 deletions app/controllers/imports_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,9 +16,14 @@ def edit
end

def update
@import.update!(account_id: params[:import][:account_id])
account = Current.family.accounts.find(params[:import][:account_id])

redirect_to load_import_path(@import), notice: "Import updated"
if @import.update(account: account)
redirect_to load_import_path(@import), notice: "Import updated"
else
flash.now[:error] = "Could not update account"
render :edit, status: :unprocessable_entity
end
end

def create
Expand Down Expand Up @@ -50,6 +55,7 @@ def configure

def update_mappings
if @import.update(import_params)
@import.rows.insert_all(@import.rows_mapped)
redirect_to clean_import_path(@import), notice: "Mappings saved"
else
flash.now[:error] = @import.errors.full_messages.first
Expand All @@ -74,6 +80,6 @@ def set_import
end

def import_params
params.require(:import).permit(:raw_csv, column_mappings: [ :date, :merchant, :category, :amount ])
params.require(:import).permit(:raw_csv, column_mappings: [ :date, :name, :category, :amount ])
end
end
39 changes: 24 additions & 15 deletions app/models/import.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,14 @@ class Import < ApplicationRecord
belongs_to :account
has_many :rows, dependent: :destroy
validate :raw_csv_must_be_valid_csv, :column_mappings_must_contain_expected_fields
before_update :prevent_update_after_complete

enum :status, { pending: "pending", complete: "complete" }, validate: true

store_accessor :column_mappings, :date, :merchant, :category, :amount

scope :ordered, -> { order(:created_at) }

def complete?
# Interim placeholder
false
end
scope :complete, -> { where(status: "complete") }
scope :pending, -> { where(status: "pending") }

def confirm!
puts "confirmed"
Expand All @@ -21,10 +19,28 @@ def parsed_csv
CSV.parse(raw_csv || "", headers: true, header_converters: :symbol, converters: [ ->(str) { str.strip } ])
end

def rows_mapped
rows = []
parsed_csv.map do |row|
preview_row = {}
required_keys.each { |key| preview_row[key] = row[column_mappings[key].to_sym] }
rows << preview_row
end
rows
end

def rows_preview
rows_mapped.first(3).map do |row|
Import::Row.new \
import: self,
**row
end
end

def default_column_mappings
{
"date" => parsed_csv.headers[0] || "date",
"merchant" => parsed_csv.headers[1] || "merchant",
"name" => parsed_csv.headers[1] || "name",
"category" => parsed_csv.headers[2] || "category",
"amount" => parsed_csv.headers[3] || "amount"
}
Expand All @@ -33,7 +49,7 @@ def default_column_mappings
private

def required_keys
%w[date merchant category amount]
%w[date name category amount]
end

def column_mappings_must_contain_expected_fields
Expand All @@ -51,13 +67,6 @@ def column_mappings_must_contain_expected_fields
end
end

def prevent_update_after_complete
if complete?
errors.add(:base, "Update not allowed on a completed import.")
throw(:abort)
end
end

def raw_csv_must_be_valid_csv
return if raw_csv.nil?

Expand Down
113 changes: 65 additions & 48 deletions app/views/imports/_type_selector.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -15,59 +15,76 @@
<h3 class="uppercase text-gray-500 text-xs font-medium px-3 py-1.5"><%= t(".sources") %></h3>
<ul class="bg-white border border-alpha-black-25 rounded-lg shadow-xs">
<li>
<%= link_to new_import_path, class: "flex items-center gap-3 p-4 group cursor-pointer", data: { turbo: false } do %>
<div class="bg-indigo-500/5 rounded-md w-8 h-8 flex items-center justify-center">
<%= lucide_icon("file-spreadsheet", class: "w-5 h-5 text-indigo-500") %>
<% if Current.family.imports.pending.present? %>
<%= link_to edit_import_path(Current.family.imports.pending.ordered.first), class: "flex items-center gap-3 p-4 group cursor-pointer", data: { turbo: false } do %>
<div class="bg-orange-500/5 rounded-md w-8 h-8 flex items-center justify-center">
<%= lucide_icon("loader", class: "w-5 h-5 text-orange-500") %>
</div>
<span class="text-sm text-gray-900 group-hover:text-gray-700">
<%= t(".resume_latest_import") %>
</span>
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-500 ml-auto") %>
<% end %>

<div class="pl-14 pr-3">
<div class="h-px bg-alpha-black-50"></div>
</div>
<span class="text-sm text-gray-900 group-hover:text-gray-700">
<%= t(".import_from_csv") %>
</span>
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-500 ml-auto") %>
</li>
<% end %>
<li>
<%= link_to new_import_path, class: "flex items-center gap-3 p-4 group cursor-pointer", data: { turbo: false } do %>
<div class="bg-indigo-500/5 rounded-md w-8 h-8 flex items-center justify-center">
<%= lucide_icon("file-spreadsheet", class: "w-5 h-5 text-indigo-500") %>
</div>
<span class="text-sm text-gray-900 group-hover:text-gray-700">
<%= t(".import_from_csv") %>
</span>
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-500 ml-auto") %>
<% end %>

<div class="pl-14 pr-3">
<div class="h-px bg-alpha-black-50"></div>
</div>
</li>
<li>
<div class="flex items-center gap-3 p-4 group cursor-not-allowed">
<%= image_tag("mint-logo.jpeg", alt: "Mint logo", class: "w-8 h-8 rounded-md") %>
<span class="text-sm text-gray-900 group-hover:text-gray-700">
<%= t(".import_from_mint") %>
</span>
<span class="bg-indigo-500/5 rounded-full px-1.5 py-0.5 border border-alpha-black-25 uppercase text-xs font-medium text-indigo-500"><%= t(".soon") %></span>
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-300 ml-auto") %>
</div>
<div class="pl-14 pr-3">
<div class="h-px bg-alpha-black-50"></div>
</div>
</li>
<li>
<div class="flex items-center gap-3 p-4 group cursor-not-allowed">
<%= image_tag("mint-logo.jpeg", alt: "Mint logo", class: "w-8 h-8 rounded-md") %>
<span class="text-sm text-gray-900 group-hover:text-gray-700">
<%= t(".import_from_mint") %>
</span>
<span class="bg-indigo-500/5 rounded-full px-1.5 py-0.5 border border-alpha-black-25 uppercase text-xs font-medium text-indigo-500"><%= t(".soon") %></span>
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-300 ml-auto") %>
</div>

<div class="pl-14 pr-3">
<div class="h-px bg-alpha-black-50"></div>
</div>
</li>
<li>
<div class="flex items-center gap-3 p-4 group cursor-not-allowed">
<%= image_tag("empower-logo.jpeg", alt: "Mint logo", class: "w-8 h-8 border border-alpha-black-100 rounded-md") %>
<span class="text-sm text-gray-900 group-hover:text-gray-700">
<%= t(".import_from_empower") %>
</span>
<span class="bg-indigo-500/5 rounded-full px-1.5 py-0.5 border border-alpha-black-25 uppercase text-xs font-medium text-indigo-500"><%= t(".soon") %></span>
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-300 ml-auto") %>
</div>
<div class="pl-14 pr-3">
<div class="h-px bg-alpha-black-50"></div>
</div>
</li>
<li>
<div class="flex items-center gap-3 p-4 group cursor-not-allowed">
<%= image_tag("empower-logo.jpeg", alt: "Mint logo", class: "w-8 h-8 border border-alpha-black-100 rounded-md") %>
<span class="text-sm text-gray-900 group-hover:text-gray-700">
<%= t(".import_from_empower") %>
</span>
<span class="bg-indigo-500/5 rounded-full px-1.5 py-0.5 border border-alpha-black-25 uppercase text-xs font-medium text-indigo-500"><%= t(".soon") %></span>
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-300 ml-auto") %>
</div>

<div class="pl-14 pr-3">
<div class="h-px bg-alpha-black-50"></div>
</div>
</li>
<li>
<div class="flex items-center gap-3 p-4 group cursor-not-allowed">
<%= image_tag("apple-logo.png", alt: "Mint logo", class: "w-8 h-8 rounded-md") %>
<span class="text-sm text-gray-900 group-hover:text-gray-700">
<%= t(".import_from_apple") %>
</span>
<span class="bg-indigo-500/5 rounded-full px-1.5 py-0.5 border border-alpha-black-25 uppercase text-xs font-medium text-indigo-500"><%= t(".soon") %></span>
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-300 ml-auto") %>
</div>
</li>
</ul>
<div class="pl-14 pr-3">
<div class="h-px bg-alpha-black-50"></div>
</div>
</li>
<li>
<div class="flex items-center gap-3 p-4 group cursor-not-allowed">
<%= image_tag("apple-logo.png", alt: "Mint logo", class: "w-8 h-8 rounded-md") %>
<span class="text-sm text-gray-900 group-hover:text-gray-700">
<%= t(".import_from_apple") %>
</span>
<span class="bg-indigo-500/5 rounded-full px-1.5 py-0.5 border border-alpha-black-25 uppercase text-xs font-medium text-indigo-500"><%= t(".soon") %></span>
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-300 ml-auto") %>
</div>
</li>
</ul>
</div>

</div>
2 changes: 1 addition & 1 deletion app/views/imports/configure.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<div class="mb-4">
<%= form.fields_for :column_mappings, OpenStruct.new(@import.column_mappings || @import.default_column_mappings) do |mappings| %>
<%= mappings.text_field :date, label: "Date" %>
<%= mappings.text_field :merchant, label: "Merchant" %>
<%= mappings.text_field :name, label: "Name" %>
<%= mappings.text_field :category, label: "Category" %>
<%= mappings.text_field :amount, label: "Amount" %>
<% end %>
Expand Down
7 changes: 7 additions & 0 deletions app/views/transactions/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,14 @@
<%= lucide_icon "tags", class: "w-5 h-5 text-gray-500" %>
<span class="text-black"><%= t(".edit_categories") %></span>
<% end %>
<%= link_to imports_path,
class: "block w-full py-2 px-3 space-x-2 text-gray-900 hover:bg-gray-50 flex items-center rounded-lg font-normal" do %>
<%= lucide_icon "hard-drive-upload", class: "w-5 h-5 text-gray-500" %>
<span class="text-black"><%= t(".edit_imports") %></span>
<% end %>
</div>

<% end %>
<%= link_to new_import_path(enable_type_selector: true), class: "rounded-lg bg-gray-50 border border-gray-200 flex items-center gap-1 justify-center px-3 py-2", data: { turbo_frame: "modal" } do %>
Expand Down
3 changes: 2 additions & 1 deletion config/locales/views/imports/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ en:
description: You can manually import transactions from CSVs or from other financial
apps like Mint, Empower (formerly Personal Capital) or Apple Card.
import_from_apple: Import from Apple Card
import_from_csv: Import from CSV
import_from_csv: New import from CSV
import_from_empower: Import from Empower
import_from_mint: Import from Mint
resume_latest_import: Resume latest import
soon: Soon
sources: Sources
1 change: 1 addition & 0 deletions config/locales/views/transaction/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ en:
transfer: Transfer
index:
edit_categories: Edit categories
edit_imports: Edit imports
import: Import
merchants:
create:
Expand Down
9 changes: 9 additions & 0 deletions db/migrate/20240513114739_add_status_to_import.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class AddStatusToImport < ActiveRecord::Migration[7.2]
def change
create_enum :import_status, %w[pending completed]

change_table :imports do |t|
t.enum :status, enum_type: :import_status, default: "pending"
end
end
end
4 changes: 3 additions & 1 deletion db/schema.rb

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 567abd7

Please sign in to comment.