mirror of
https://github.com/maybe-finance/maybe.git
synced 2025-08-05 13:35:21 +02:00
CSV Transaction Imports (#708)
Introduces a basic CSV import module for bulk-importing account transactions. Changes include: - User can load a CSV - User can configure the column mappings for a CSV - Imported CSV shows invalid cells - User can clean up their data directly in the UI - User can see a preview of the import rows and confirm import - Layout refactor + Import nav stepper - System test stability improvements
This commit is contained in:
parent
3d9ff3ad2a
commit
45ae4a9737
71 changed files with 1657 additions and 117 deletions
|
@ -1,52 +1,39 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="h-full bg-gray-25">
|
||||
<html class="h-full">
|
||||
<head>
|
||||
<title><%= content_for(:title) || "Maybe" %></title>
|
||||
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
|
||||
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
|
||||
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
||||
|
||||
<%= javascript_importmap_tags %>
|
||||
<%= hotwire_livereload_tags if Rails.env.development? %>
|
||||
<%= turbo_refreshes_with method: :morph, scroll: :preserve %>
|
||||
|
||||
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="Maybe">
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#ffffff">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
|
||||
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
||||
<%= javascript_importmap_tags %>
|
||||
<%= hotwire_livereload_tags if Rails.env.development? %>
|
||||
<%= turbo_refreshes_with method: :morph, scroll: :preserve %>
|
||||
|
||||
<%= yield :head %>
|
||||
</head>
|
||||
|
||||
<body class="h-full">
|
||||
<div id="notification-tray" class="fixed z-50 space-y-1 top-6 right-6"></div>
|
||||
<%= safe_join(flash.map { |type, message| notification(message, type: type) }) %>
|
||||
<div class="flex h-full">
|
||||
<div class="p-6 pb-20 w-[368px] shrink-0 h-full overflow-y-auto">
|
||||
<% if content_for?(:sidebar) %>
|
||||
<%= yield :sidebar %>
|
||||
<% else %>
|
||||
<%= render "layouts/sidebar" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<main class="grow px-20 pt-6 pb-32 h-full overflow-y-auto">
|
||||
<%= yield %>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<%= content_for?(:content) ? yield(:content) : yield %>
|
||||
|
||||
<%= turbo_frame_tag "modal" %>
|
||||
<%= render "shared/confirm_modal" %>
|
||||
<%= render "shared/upgrade_notification" %>
|
||||
|
||||
<% if self_hosted? %>
|
||||
<div class="flex items-center py-0.5 px-0.5 gap-1 fixed bottom-2 right-2 shadow-xs border border-alpha-black-50 rounded-md bg-white">
|
||||
<p class="text-xs text-gray-500 pl-2">Self-hosted Maybe: <%= Maybe.version.to_release_tag %></p>
|
||||
<%= link_to settings_hosting_path, class: "flex gap-1 items-center hover:bg-gray-50 rounded-md p-2" do %>
|
||||
<%= lucide_icon("settings", class: "w-4 h-4 text-gray-500 shrink-0") %>
|
||||
<% end %>
|
||||
</div>
|
||||
<%= render "shared/app_version" %>
|
||||
<% end %>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
@ -1,61 +1,31 @@
|
|||
<!DOCTYPE html>
|
||||
<html class="h-full bg-white">
|
||||
<head>
|
||||
<title>Maybe</title>
|
||||
<meta name="viewport" content="viewport-fit=cover, width=device-width, initial-scale=1.0, minimum-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<meta name="apple-mobile-web-app-capable" content="yes">
|
||||
<meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
|
||||
<meta name="apple-mobile-web-app-title" content="Maybe">
|
||||
<%= content_for :content do %>
|
||||
<div class="flex flex-col justify-center min-h-full px-6 py-12">
|
||||
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<%= render "shared/logo" %>
|
||||
|
||||
<link rel="apple-touch-icon" sizes="180x180" href="/apple-touch-icon.png">
|
||||
<link rel="icon" type="image/png" sizes="32x32" href="/favicon-32x32.png">
|
||||
<link rel="icon" type="image/png" sizes="16x16" href="/favicon-16x16.png">
|
||||
<link rel="manifest" href="/site.webmanifest">
|
||||
<link rel="mask-icon" href="/safari-pinned-tab.svg" color="#ffffff">
|
||||
<meta name="msapplication-TileColor" content="#ffffff">
|
||||
<meta name="theme-color" content="#ffffff">
|
||||
<h2 class="mt-6 text-3xl font-semibold tracking-tight text-center font-display">
|
||||
<%= content_for?(:header_title) ? yield(:header_title).html_safe : t(".your_account") %>
|
||||
</h2>
|
||||
|
||||
<%= csrf_meta_tags %>
|
||||
<%= csp_meta_tag %>
|
||||
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
|
||||
|
||||
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
|
||||
<%= javascript_importmap_tags %>
|
||||
|
||||
<%= hotwire_livereload_tags if Rails.env.development? %>
|
||||
<%= turbo_refreshes_with method: :morph, scroll: :preserve %>
|
||||
</head>
|
||||
|
||||
<body class="h-full">
|
||||
<div class="flex flex-col justify-center min-h-full px-6 py-12">
|
||||
|
||||
<div class="sm:mx-auto sm:w-full sm:max-w-md">
|
||||
<%= render "shared/logo" %>
|
||||
|
||||
<h2 class="mt-6 text-3xl font-semibold tracking-tight text-center font-display">
|
||||
<%= content_for?(:header_title) ? yield(:header_title).html_safe : t(".your_account") %>
|
||||
</h2>
|
||||
|
||||
<% if controller_name == "sessions" %>
|
||||
<% if controller_name == "sessions" %>
|
||||
<p class="mt-2 text-sm text-center text-gray-600">
|
||||
<%= t(".or") %> <%= link_to t(".sign_up"), new_registration_path, class: "font-medium text-gray-600 hover:text-gray-400 transition" %>
|
||||
</p>
|
||||
<% elsif controller_name == "registrations" %>
|
||||
<% elsif controller_name == "registrations" %>
|
||||
<p class="mt-2 text-sm text-center text-gray-600">
|
||||
<%= t(".or") %> <%= link_to t(".sign_in"), new_session_path, class: "font-medium text-gray-600 hover:text-gray-400 transition" %>
|
||||
</p>
|
||||
<% end %>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-lg">
|
||||
<%= yield %>
|
||||
</div>
|
||||
|
||||
<div class="p-8 mt-2 text-center">
|
||||
<p class="mt-6 text-sm text-black"><%= link_to t(".privacy_policy"), "/privacy", class: "font-medium text-gray-600 hover:text-gray-400 transition" %> • <%= link_to t(".terms_of_service"), "/terms", class: "font-medium text-gray-600 hover:text-gray-400 transition" %></p>
|
||||
</div>
|
||||
|
||||
<% end %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
||||
<div class="mt-8 sm:mx-auto sm:w-full sm:max-w-lg">
|
||||
<%= yield %>
|
||||
</div>
|
||||
|
||||
<div class="p-8 mt-2 text-center">
|
||||
<p class="mt-6 text-sm text-black"><%= link_to t(".privacy_policy"), "/privacy", class: "font-medium text-gray-600 hover:text-gray-400 transition" %> • <%= link_to t(".terms_of_service"), "/terms", class: "font-medium text-gray-600 hover:text-gray-400 transition" %></p>
|
||||
</div>
|
||||
</div>
|
||||
<% end %>
|
||||
|
||||
<%= render template: "layouts/application" %>
|
||||
|
|
34
app/views/layouts/imports.html.erb
Normal file
34
app/views/layouts/imports.html.erb
Normal file
|
@ -0,0 +1,34 @@
|
|||
<%= content_for :content do %>
|
||||
<div class="flex items-center justify-between p-8">
|
||||
<%= link_to root_path do %>
|
||||
<%= image_tag "logo.svg", alt: "Maybe", class: "h-[22px]" %>
|
||||
<% end %>
|
||||
<nav>
|
||||
<div>
|
||||
<ul class="flex items-center gap-2">
|
||||
<% nav_steps(@import).each_with_index do |step, idx| %>
|
||||
<li class="group flex items-center gap-2">
|
||||
<% if step[:path].present? %>
|
||||
<%= link_to step[:path], class: "flex items-center gap-3" do %>
|
||||
<%= render partial: "nav_step", locals: { step: step, step_idx: idx } %>
|
||||
<% end %>
|
||||
<% else %>
|
||||
<%= render partial: "nav_step", locals: { step: step, step_idx: idx } %>
|
||||
<% end %>
|
||||
<% if idx < nav_steps.size %>
|
||||
<div class="h-px bg-alpha-black-200 w-12 group-last:hidden"></div>
|
||||
<% end %>
|
||||
</li>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
</nav>
|
||||
<%= link_to content_for(:return_to_path) do %>
|
||||
<%= lucide_icon("x", class: "text-gray-500 w-5 h-5") %>
|
||||
<% end %>
|
||||
</div>
|
||||
|
||||
<%= yield %>
|
||||
<% end %>
|
||||
|
||||
<%= render template: "layouts/application" %>
|
18
app/views/layouts/with_sidebar.html.erb
Normal file
18
app/views/layouts/with_sidebar.html.erb
Normal file
|
@ -0,0 +1,18 @@
|
|||
<%= content_for :content do %>
|
||||
<div class="flex h-full bg-gray-25">
|
||||
<div class="p-6 pb-20 w-[368px] shrink-0 h-full overflow-y-auto">
|
||||
<% if content_for?(:sidebar) %>
|
||||
<%= yield :sidebar %>
|
||||
<% else %>
|
||||
<%= render "layouts/sidebar" %>
|
||||
<% end %>
|
||||
</div>
|
||||
<main class="grow px-20 pt-6 pb-32 h-full overflow-y-auto">
|
||||
<%= yield %>
|
||||
</main>
|
||||
</div>
|
||||
|
||||
<%= render "shared/upgrade_notification" %>
|
||||
<% end %>
|
||||
|
||||
<%= render template: "layouts/application" %>
|
Loading…
Add table
Add a link
Reference in a new issue