1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-08-02 20:15:22 +02:00

More composable forms (#989)

* Make forms more composable, opt-in to form builder

* Remove unused method

* Simpler money input controls

* Add in new form styling to imports

* Lint fixes

* Small tweak of multi select styles
This commit is contained in:
Zach Gollwitzer 2024-07-16 14:08:24 -04:00 committed by GitHub
parent 47523f64c2
commit e51806b98b
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
50 changed files with 268 additions and 488 deletions

View file

@ -9,7 +9,6 @@
<%= turbo_frame_tag "bulk_transaction_edit_drawer" %>
<%= form_with url: mark_transfers_transactions_path,
builder: ActionView::Helpers::FormBuilder,
scope: "bulk_update",
data: {
turbo_frame: "_top",
@ -36,7 +35,7 @@
<%= lucide_icon "pencil-line", class: "w-5 group-hover:text-white" %>
<% end %>
<%= form_with url: bulk_delete_transactions_path, builder: ActionView::Helpers::FormBuilder, data: { turbo_confirm: true, turbo_frame: "_top" } do %>
<%= form_with url: bulk_delete_transactions_path, data: { turbo_confirm: true, turbo_frame: "_top" } do %>
<button type="button" data-bulk-select-scope-param="bulk_delete" data-action="bulk-select#submitBulkRequest" class="p-1.5 group hover:bg-gray-700 flex items-center justify-center rounded-md" title="Delete">
<%= lucide_icon "trash-2", class: "w-5 group-hover:text-white" %>
</button>

View file

@ -27,30 +27,28 @@
</summary>
<div class="pb-6">
<%= form_with model: [account, entry], url: account_entry_path(account, entry), html: { data: { controller: "auto-submit-form" } } do |f| %>
<div class="space-y-2">
<%= f.text_field :name, label: t(".name_label"), "data-auto-submit-form-target": "auto" %>
<% unless entry.marked_as_transfer? %>
<div class="flex space-x-2">
<div>
<%= f.select :nature, [["Expense", "expense"], ["Income", "income"]], { label: t(".nature"), selected: entry.amount.negative? ? "income" : "expense" }, "data-auto-submit-form-target": "auto" %>
</div>
<div class="flex-grow">
<%= f.number_field :amount, value: entry.amount.abs, label: t(".amount"), step: "0.01", "data-auto-submit-form-target": "auto", "data-autosubmit-trigger-event": "change" %>
</div>
<%= styled_form_with model: [account, entry], url: account_entry_path(account, entry), class: "space-y-2", data: { controller: "auto-submit-form" } do |f| %>
<%= f.text_field :name, label: t(".name_label"), "data-auto-submit-form-target": "auto" %>
<% unless entry.marked_as_transfer? %>
<div class="flex space-x-2">
<div>
<%= f.select :nature, [["Expense", "expense"], ["Income", "income"]], { label: t(".nature"), selected: entry.amount.negative? ? "income" : "expense" }, "data-auto-submit-form-target": "auto" %>
</div>
<% end %>
<%= f.date_field :date, label: t(".date_label"), max: Date.current, "data-auto-submit-form-target": "auto" %>
<div class="flex-grow">
<%= f.number_field :amount, value: entry.amount.abs, label: t(".amount"), step: "0.01", "data-auto-submit-form-target": "auto", "data-autosubmit-trigger-event": "change" %>
</div>
</div>
<% end %>
<%= f.date_field :date, label: t(".date_label"), max: Date.current, "data-auto-submit-form-target": "auto" %>
<%= f.fields_for :entryable do |ef| %>
<% unless entry.marked_as_transfer? %>
<%= ef.collection_select :category_id, selectable_categories, :id, :name, { prompt: t(".category_placeholder"), label: t(".category_label"), class: "text-gray-400" }, "data-auto-submit-form-target": "auto" %>
<%= ef.collection_select :merchant_id, selectable_merchants, :id, :name, { prompt: t(".merchant_placeholder"), label: t(".merchant_label"), class: "text-gray-400" }, "data-auto-submit-form-target": "auto" %>
<% end %>
<%= f.fields_for :entryable do |ef| %>
<% unless entry.marked_as_transfer? %>
<%= ef.collection_select :category_id, Current.family.categories.alphabetically, :id, :name, { prompt: t(".category_placeholder"), label: t(".category_label"), class: "text-gray-400" }, "data-auto-submit-form-target": "auto" %>
<%= ef.collection_select :merchant_id, Current.family.merchants.alphabetically, :id, :name, { prompt: t(".merchant_placeholder"), label: t(".merchant_label"), class: "text-gray-400" }, "data-auto-submit-form-target": "auto" %>
<% end %>
<% end %>
<%= f.collection_select :account_id, selectable_accounts, :id, :name, { prompt: t(".account_placeholder"), label: t(".account_label"), class: "text-gray-500" }, { class: "form-field__input cursor-not-allowed text-gray-400", disabled: "disabled" } %>
</div>
<%= f.collection_select :account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".account_placeholder"), label: t(".account_label"), class: "text-gray-500" }, { class: "form-field__input cursor-not-allowed text-gray-400", disabled: "disabled" } %>
<% end %>
</div>
</details>
@ -61,12 +59,12 @@
<%= lucide_icon "chevron-down", class: "group-open:transform group-open:rotate-180 text-gray-500 w-5" %>
</summary>
<div class="pb-6 space-y-2">
<%= form_with model: [account, entry], url: account_entry_path(account, entry), html: { data: { controller: "auto-submit-form" } } do |f| %>
<div class="pb-6">
<%= styled_form_with model: [account, entry], url: account_entry_path(account, entry), class: "space-y-2", data: { controller: "auto-submit-form" } do |f| %>
<%= f.fields_for :entryable do |ef| %>
<%= ef.select :tag_ids,
options_for_select(selectable_tags, transaction.tag_ids),
options_for_select(Current.family.tags.alphabetically.pluck(:name, :id), transaction.tag_ids),
{
multiple: true,
label: t(".tags_label"),
@ -86,7 +84,7 @@
</summary>
<div class="pb-6">
<%= form_with model: [account, entry], url: account_entry_path(account, entry), html: { class: "p-3 space-y-3", data: { controller: "auto-submit-form" } } do |f| %>
<%= styled_form_with model: [account, entry], url: account_entry_path(account, entry), class: "p-3 space-y-3", data: { controller: "auto-submit-form" } do |f| %>
<%= f.fields_for :entryable do |ef| %>
<div class="flex cursor-pointer items-center gap-2 justify-between">
<div class="text-sm space-y-1">

View file

@ -31,7 +31,7 @@
<% if unconfirmed_transfer?(entry) %>
<% if editable %>
<%= form_with url: unmark_transfers_transactions_path, builder: ActionView::Helpers::FormBuilder, class: "flex items-center", data: {
<%= form_with url: unmark_transfers_transactions_path, class: "flex items-center", data: {
turbo_confirm: {
title: t(".remove_transfer"),
body: t(".remove_transfer_body"),

View file

@ -1,8 +1,7 @@
<%# locals: (entry:) %>
<%= form_with model: [entry.account, entry],
data: { turbo_frame: "_top" },
url: entry.new_record? ? account_entries_path(entry.account) : account_entry_path(entry.account, entry),
builder: ActionView::Helpers::FormBuilder do |f| %>
url: entry.new_record? ? account_entries_path(entry.account) : account_entry_path(entry.account, entry) do |f| %>
<div class="grid grid-cols-10 p-4 items-center">
<div class="col-span-7 flex items-center gap-4">
<div class="w-8 h-8 rounded-full p-1.5 flex items-center justify-center bg-gray-500/5">

View file

@ -1,4 +1,4 @@
<%= form_with model: transfer, data: { turbo_frame: "_top" } do |f| %>
<%= styled_form_with model: transfer, class: "space-y-4", data: { turbo_frame: "_top" } do |f| %>
<section>
<fieldset class="bg-gray-50 rounded-lg p-1 grid grid-flow-col justify-stretch gap-x-2">
<%= link_to new_transaction_path(nature: "expense"), data: { turbo_frame: :modal }, class: "flex px-4 py-1 rounded-lg items-center space-x-2 justify-center text-gray-400" do %>
@ -22,7 +22,8 @@
<%= f.text_field :name, value: transfer.name, label: t(".description"), placeholder: t(".description_placeholder"), required: true %>
<%= f.collection_select :from_account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".select_account"), label: t(".from") }, required: true %>
<%= f.collection_select :to_account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".select_account"), label: t(".to") }, required: true %>
<%= f.money_field :amount_money, label: t(".amount"), required: true %>
<%= money_field f, :amount_money, label: t(".amount"), required: true %>
<%= f.hidden_field :currency, value: Current.family.currency %>
<%= f.date_field :date, value: transfer.date, label: t(".date"), required: true, max: Date.current %>
</section>

View file

@ -17,7 +17,6 @@
<%= form_with model: account,
namespace: account.id,
builder: ActionView::Helpers::FormBuilder,
data: { controller: "auto-submit-form", turbo_frame: "_top" } do |form| %>
<div class="relative inline-block select-none">
<%= form.check_box :is_active, { class: "sr-only peer", data: { "auto-submit-form-target": "auto" } } %>

View file

@ -5,9 +5,9 @@
<%= lucide_icon "x", class: "w-5 h-5 text-gray-500", data: { action: "click->modal#close" } %>
</header>
<%= form_with model: @account, data: { turbo_frame: "_top" } do |f| %>
<%= styled_form_with model: @account, class: "space-y-4", data: { turbo_frame: "_top" } do |f| %>
<%= f.text_field :name, label: t(".name") %>
<%= f.money_field :balance_money, label: t(".balance"), readonly_currency: true %>
<%= money_with_currency_field f, :balance_money, label: t(".balance"), disable_currency: true %>
<div class="relative">
<%= f.collection_select :institution_id, Current.family.institutions.alphabetically, :id, :name, { include_blank: t(".ungrouped"), label: t(".institution") } %>

View file

@ -1,5 +1,5 @@
<turbo-frame id="account-list" target="_top">
<%= turbo_frame_tag "account-list" do %>
<% account_groups(period: @period).each do |group| %>
<%= render "accounts/account_list", group: group %>
<% end %>
</turbo-frame>
<% end %>

View file

@ -73,13 +73,13 @@
<% end %>
<span>Add <%= @account.accountable.model_name.human.downcase %></span>
</div>
<%= form_with model: @account, url: accounts_path, scope: :account, html: { class: "m-5 mt-1 flex flex-col justify-between grow", data: { turbo: false } } do |f| %>
<%= styled_form_with model: @account, url: accounts_path, scope: :account, class: "m-5 mt-1 flex flex-col justify-between grow", data: { turbo: false } do |f| %>
<div class="space-y-4 grow">
<%= f.hidden_field :accountable_type %>
<%= f.text_field :name, placeholder: t(".name.placeholder"), required: "required", label: t(".name.label"), autofocus: true %>
<%= f.collection_select :institution_id, Current.family.institutions.alphabetically, :id, :name, { include_blank: t(".ungrouped"), label: t(".institution") } %>
<%= render "accounts/accountables/#{permitted_accountable_partial(@account.accountable_type)}", f: f %>
<%= f.money_field :balance_money, label: t(".balance"), required: "required" %>
<%= money_with_currency_field f, :balance_money, label: t(".balance"), required: "required" %>
<div>
<%= check_box_tag :add_start_values, class: "maybe-checkbox maybe-checkbox--light peer mb-1" %>

View file

@ -69,8 +69,8 @@
<%= tag.span period_label(@period), class: "text-gray-500" %>
</div>
</div>
<%= form_with url: account_path(@account), method: :get, class: "flex items-center gap-4", data: { controller: "auto-submit-form" } do %>
<%= render partial: "shared/period_select", locals: { value: @period.name } %>
<%= form_with url: account_path(@account), method: :get, data: { controller: "auto-submit-form" } do |form| %>
<%= period_select form: form, selected: @period.name %>
<% end %>
</div>
<div class="h-96 flex items-center justify-center text-2xl font-bold">

View file

@ -10,10 +10,10 @@
<div class="space-y-2 grow">
<%= render partial: "shared/value_heading", locals: {
label: "Assets",
period: @period,
value: Current.family.assets,
trend: @asset_series.trend
} %>
period: @period,
value: Current.family.assets,
trend: @asset_series.trend
} %>
</div>
<div
id="assetsChart"
@ -26,11 +26,11 @@
<div class="space-y-2 grow">
<%= render partial: "shared/value_heading", locals: {
label: "Liabilities",
period: @period,
size: "md",
value: Current.family.liabilities,
trend: @liability_series.trend
} %>
period: @period,
size: "md",
value: Current.family.liabilities,
trend: @liability_series.trend
} %>
</div>
<div
id="liabilitiesChart"
@ -48,8 +48,8 @@
<%= lucide_icon("plus", class: "w-5 h-5 text-gray-500") %>
<p><%= t(".new") %></p>
<% end %>
<%= form_with url: summary_accounts_path, method: :get, class: "flex items-center gap-4", data: { controller: "auto-submit-form" } do %>
<%= render partial: "shared/period_select", locals: { value: @period.name } %>
<%= form_with url: summary_accounts_path, method: :get, data: { controller: "auto-submit-form" } do |form| %>
<%= period_select form: form, selected: @period.name %>
<% end %>
</div>
</div>
@ -64,8 +64,8 @@
<%= lucide_icon("plus", class: "w-5 h-5 text-gray-500") %>
<p><%= t(".new") %></p>
<% end %>
<%= form_with url: summary_accounts_path, method: :get, class: "flex items-center gap-4", data: { controller: "auto-submit-form" } do %>
<%= render partial: "shared/period_select", locals: { value: @period.name } %>
<%= form_with url: summary_accounts_path, method: :get, data: { controller: "auto-submit-form" } do |form| %>
<%= period_select form: form, selected: @period.name %>
<% end %>
</div>
</div>

View file

@ -1,4 +1,4 @@
<%= form_with model: category, data: { turbo: false } do |form| %>
<%= styled_form_with model: category, data: { turbo: false } do |form| %>
<div class="flex flex-col space-y-4 w-96" data-controller="color-select" data-color-select-selection-value="<%= category.color %>">
<fieldset class="relative">
<span data-color-select-target="decoration" class="pointer-events-none absolute inset-y-3.5 left-3 flex items-center pl-1 block w-1 rounded-lg"></span>

View file

@ -11,22 +11,23 @@
</p>
</div>
<%= form_with url: category_deletions_path(@category),
data: {
turbo: false,
controller: "deletion",
deletion_dangerous_action_class: "form-field__submit bg-white text-red-600 border hover:bg-red-50",
deletion_safe_action_class: "form-field__submit border border-transparent",
deletion_submit_text_when_not_replacing_value: t(".delete_and_leave_uncategorized", category_name: @category.name),
deletion_submit_text_when_replacing_value: t(".delete_and_recategorize", category_name: @category.name) } do |f| %>
<%= styled_form_with url: category_deletions_path(@category),
class: "space-y-4",
data: {
turbo: false,
controller: "deletion",
deletion_dangerous_action_class: "form-field__submit bg-white text-red-600 border hover:bg-red-50",
deletion_safe_action_class: "form-field__submit border border-transparent",
deletion_submit_text_when_not_replacing_value: t(".delete_and_leave_uncategorized", category_name: @category.name),
deletion_submit_text_when_replacing_value: t(".delete_and_recategorize", category_name: @category.name) } do |f| %>
<%= f.collection_select :replacement_category_id,
Current.family.categories.alphabetically.without(@category),
:id, :name,
{ prompt: t(".replacement_category_prompt"), label: t(".category") },
:id, :name,
{ prompt: t(".replacement_category_prompt"), label: t(".category") },
{ data: { deletion_target: "replacementField", action: "deletion#updateSubmitButton" } } %>
<%= f.submit t(".delete_and_leave_uncategorized", category_name: @category.name),
class: "form-field__submit bg-white text-red-600 border hover:bg-red-50",
class: "form-field__submit bg-white text-red-600 border hover:bg-red-50",
data: { deletion_target: "submitButton" } %>
<% end %>
</article>

View file

@ -1,11 +1,9 @@
<%= form_with model: @import, url: load_import_path(@import) do |form| %>
<div>
<%= styled_form_with model: @import, url: load_import_path(@import), class: "space-y-4" do |form| %>
<%= form.text_area :raw_csv_str,
rows: 10,
required: true,
placeholder: "Paste your CSV file contents here",
class: "rounded-md w-full border text-sm border-alpha-black-100 bg-white placeholder:text-gray-400 p-4" %>
</div>
class: "rounded-md w-full border text-sm border-alpha-black-100 bg-white placeholder:text-gray-400" %>
<%= form.submit t(".next"), class: "px-4 py-2 mb-4 block w-full rounded-lg bg-gray-900 text-white text-sm font-medium", data: { turbo_confirm: (@import.raw_csv_str? ? { title: t(".confirm_title"), body: t(".confirm_body"), accept: t(".confirm_accept") } : nil) } %>
<% end %>

View file

@ -1,4 +1,4 @@
<%= form_with model: @import, url: upload_import_path(@import), class: "dropzone", data: { controller: "csv-upload" }, method: :patch, multipart: true do |form| %>
<%= styled_form_with model: @import, url: upload_import_path(@import), class: "dropzone space-y-4", data: { controller: "csv-upload" }, method: :patch, multipart: true do |form| %>
<div class="flex items-center justify-center w-full">
<label for="import_raw_csv_str" class="csv-drop-box flex flex-col items-center justify-center w-full h-64 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50" data-action="dragover->csv-upload#dragover dragleave->csv-upload#dragleave drop->csv-upload#drop">
<div class="flex flex-col items-center justify-center pt-5 pb-6">

View file

@ -1,4 +1,4 @@
<%= form_with model: @import do |form| %>
<%= styled_form_with model: @import do |form| %>
<div class="mb-4">
<%= form.collection_select :account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".select_account"), label: t(".account"), required: true } %>
</div>

View file

@ -24,7 +24,6 @@
style="grid-template-columns: repeat(<%= @import.expected_fields.size %>, 1fr);">
<% row.fields.each_with_index do |value, col_index| %>
<%= form_with model: @import,
builder: ActionView::Helpers::FormBuilder,
url: clean_import_url(@import),
method: :patch,
data: { turbo: false, controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } do |form| %>

View file

@ -8,8 +8,7 @@
<p class="text-gray-500 text-sm"><%= t(".configure_description") %></p>
</div>
<%= form_with model: @import, url: configure_import_path(@import) do |form| %>
<div class="mb-4 space-y-4">
<%= styled_form_with model: @import, url: configure_import_path(@import), class: "space-y-4" do |form| %>
<%= form.fields_for :column_mappings do |mappings| %>
<% @import.expected_fields.each do |field| %>
<%= mappings.select field.key,
@ -18,7 +17,6 @@
include_blank: field.optional? ? t(".optional") : false %>
<% end %>
<% end %>
</div>
<%= form.submit t(".next"), class: "px-4 py-2 block w-full rounded-lg bg-gray-900 text-white text-sm font-medium cursor-pointer hover:bg-gray-700", data: { turbo_confirm: (@import.column_mappings? ? { title: t(".confirm_title"), body: t(".confirm_body"), accept: t(".confirm_accept") } : nil) } %>
<% end %>

View file

@ -1,5 +1,4 @@
<%= form_with model: institution, data: { turbo_frame: "_top", controller: "profile-image-preview" } do |f| %>
<%= styled_form_with model: institution, class: "space-y-4", data: { turbo_frame: "_top", controller: "profile-image-preview" } do |f| %>
<div class="flex justify-center items-center py-4">
<%= f.label :logo do %>
<div class="relative cursor-pointer hover:opacity-80 w-16 h-16 rounded-full bg-gray-50">

View file

@ -91,17 +91,18 @@
<%= t(".portfolio") %>
<% end %>
<span class="font-bold tracking-wide">&bull;</span>
<%= form_with url: list_accounts_path, method: :get, class: "flex items-center gap-4", data: { controller: "auto-submit-form", turbo_frame: "account-list" } do %>
<%= render partial: "shared/period_select", locals: { button_class: "flex items-center gap-1 w-full cursor-pointer font-bold tracking-wide" } %>
<%= form_with url: list_accounts_path, method: :get, data: { controller: "auto-submit-form", turbo_frame: "account-list" } do |form| %>
<%= period_select form: form, selected: "last_7_days", classes: "w-full border-none pl-2 pr-7 text-xs bg-transparent gap-1 cursor-pointer font-semibold tracking-wide focus:outline-none focus:ring-0" %>
<% end %>
</div>
<%= link_to new_account_path, id: "sidebar-new-account", class: "block hover:bg-gray-100 p-2 text-sm font-semibold text-gray-900 flex items-center rounded", title: t(".new_account"), data: { turbo_frame: "modal" } do %>
<%= link_to new_account_path, id: "sidebar-new-account", class: "block hover:bg-gray-100 font-semibold text-gray-900 flex items-center rounded", title: t(".new_account"), data: { turbo_frame: "modal" } do %>
<%= lucide_icon("plus", class: "w-5 h-5 text-gray-500") %>
<% end %>
</div>
<turbo-frame id="account-list" target="_top">
<%= turbo_frame_tag "account-list", target: "_top" do %>
<% account_groups.each do |group| %>
<%= render "accounts/account_list", group: group %>
<% end %>
</turbo-frame>
<% end %>
</div>

View file

@ -1,6 +1,6 @@
<% is_editing = @merchant.id.present? %>
<div data-controller="merchant-avatar">
<%= form_with model: @merchant, url: is_editing ? merchant_path(@merchant) : merchants_path, method: is_editing ? :patch : :post, scope: :merchant, data: { turbo: false } do |f| %>
<%= styled_form_with model: @merchant, url: is_editing ? merchant_path(@merchant) : merchants_path, method: is_editing ? :patch : :post, scope: :merchant, class: "space-y-4", data: { turbo: false } do |f| %>
<section class="space-y-4">
<div class="w-fit m-auto">
<%= render partial: "merchants/avatar", locals: { merchant: } %>

View file

@ -26,8 +26,8 @@
trend: @net_worth_series.trend
} %>
</div>
<%= form_with url: root_path, method: :get, class: "flex items-center gap-4", data: { controller: "auto-submit-form" } do %>
<%= render partial: "shared/period_select", locals: { value: @period.name } %>
<%= form_with url: root_path, method: :get, class: "flex items-center gap-4", data: { controller: "auto-submit-form" } do |form| %>
<%= period_select form: form, selected: @period.name %>
<% end %>
</div>
<%= render partial: "pages/dashboard/net_worth_chart", locals: { series: @net_worth_series } %>

View file

@ -2,7 +2,7 @@
header_title t(".title")
%>
<%= form_with model: @user, url: password_reset_path(token: params[:token]), method: :patch, html: {class: "space-y-6"} do |form| %>
<%= styled_form_with model: @user, url: password_reset_path(token: params[:token]), method: :patch, class: "space-y-4" do |form| %>
<%= auth_messages form %>
<div class="relative border border-gray-100 bg-gray-25 rounded-xl focus-within:bg-white focus-within:shadow focus-within:opacity-100">

View file

@ -2,7 +2,7 @@
header_title t(".title")
%>
<%= form_with url: password_reset_path do |form| %>
<%= styled_form_with url: password_reset_path, class: "space-y-4" do |form| %>
<%= auth_messages form %>
<%= form.email_field :email, label: true, autofocus: false, autocomplete: "email", required: "required", placeholder: "you@example.com" %>

View file

@ -1,6 +1,6 @@
<h1><% t(".title") %></h1>
<%= form_with model: Current.user, url: password_path do |form| %>
<%= styled_form_with model: Current.user, url: password_path, class: "space-y-4" do |form| %>
<%= auth_messages form %>
<div>

View file

@ -1,7 +1,7 @@
<%
header_title t(".title")
%>
<%= form_with model: @user, url: registration_path do |form| %>
<%= styled_form_with model: @user, url: registration_path, class: "space-y-4" do |form| %>
<%= auth_messages form %>
<%= form.email_field :email, autofocus: false, autocomplete: "email", required: "required", placeholder: "you@example.com", label: true %>
<%= form.password_field :password, autocomplete: "new-password", required: "required", label: true %>

View file

@ -2,12 +2,12 @@
header_title t(".title")
%>
<%= form_with url: session_path do |form| %>
<%= styled_form_with url: session_path, class: "space-y-4" do |form| %>
<%= auth_messages form %>
<%= form.email_field :email, label: t(".email"), autofocus: false, autocomplete: "email", required: "required", placeholder: t(".email_placeholder") %>
<%= form.password_field :password, label: true, required: "required" %>
<%= form.password_field :password, label: t(".password"), required: "required" %>
<%= form.submit t(".submit") %>
<% end %>

View file

@ -4,7 +4,7 @@
<div class="space-y-4">
<h1 class="text-gray-900 text-xl font-medium mb-4"><%= t(".page_title") %></h1>
<%= settings_section title: t(".general_settings_title") do %>
<%= form_with model: Setting.new, url: settings_hosting_path, method: :patch, local: true, html: { class: "space-y-6", data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } } do |form| %>
<%= styled_form_with model: Setting.new, url: settings_hosting_path, method: :patch, local: true, class: "space-y-6", data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value" => "blur" } do |form| %>
<% if ENV["HOSTING_PLATFORM"] == "render" %>
<div>

View file

@ -5,16 +5,16 @@
<h1 class="text-gray-900 text-xl font-medium mb-4"><%= t(".page_title") %></h1>
<%= settings_section title: t(".general_title"), subtitle: t(".general_subtitle") do %>
<div>
<%= form_with model: Current.user, url: settings_preferences_path, html: { class: "space-y-4", data: { controller: "auto-submit-form" } } do |form| %>
<%= form.fields_for :family_attributes do |family_fields| %>
<%= family_fields.currency_select :currency, { selected: Current.family.currency, label: "Currency" }, { data: { auto_submit_form_target: "auto" } } %>
<%= styled_form_with model: Current.user, url: settings_preferences_path, class: "space-y-4", data: { controller: "auto-submit-form" } do |form| %>
<%= form.fields_for :family_attributes do |family_form| %>
<%= currency_select_full family_form, :currency, { label: "Currency", selected: Current.family.currency }, { data: { auto_submit_form_target: "auto" } } %>
<% end %>
<% end %>
</div>
<% end %>
<%= settings_section title: t(".theme_title"), subtitle: t(".theme_subtitle") do %>
<div>
<%= form_with model: Current.user, url: settings_preferences_path, local: true, html: { class: "flex justify-between items-center" } do |form| %>
<%= styled_form_with model: Current.user, url: settings_preferences_path, local: true, class: "flex justify-between items-center" do |form| %>
<div class="text-center">
<%= image_tag("light-mode-preview.png", alt: "Light Theme Preview", class: "h-44 mb-4") %>
<div class="flex justify-center items-center gap-2">

View file

@ -5,7 +5,7 @@
<h1 class="text-gray-900 text-xl font-medium mb-4"><%= t(".page_title") %></h1>
<div class="space-y-4">
<%= settings_section title: t(".profile_title"), subtitle: t(".profile_subtitle") do %>
<%= form_with model: Current.user, url: settings_profile_path, html: {data: { controller: "profile-image-preview" }} do |form| %>
<%= styled_form_with model: Current.user, url: settings_profile_path, class: "space-y-4", data: { controller: "profile-image-preview" } do |form| %>
<div class="flex items-center gap-4">
<div class="relative flex justify-center items-center bg-gray-50 w-24 h-24 rounded-full border border-alpha-black-25">
<div data-profile-image-preview-target="imagePreview" class="h-full w-full flex justify-center items-center">
@ -43,7 +43,7 @@
<% end %>
<%= settings_section title: t(".household_title"), subtitle: t(".household_subtitle") do %>
<div class="space-y-4">
<%= form_with model: Current.user, url: settings_profile_path, html: { class: "space-y-4", data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value": "blur" } } do |form| %>
<%= styled_form_with model: Current.user, url: settings_profile_path, class: "space-y-4", data: { controller: "auto-submit-form", "auto-submit-form-trigger-event-value": "blur" } do |form| %>
<%= form.fields_for :family_attributes do |family_fields| %>
<%= family_fields.text_field :name, placeholder: t(".household_form_input_placeholder"), value: Current.family.name, label: t(".household_form_label"), disabled: !Current.user.admin?, "data-auto-submit-form-target": "auto" %>
<% end %>

View file

@ -0,0 +1,24 @@
<%# locals: (form:, money_method:, default_currency: "USD", disable_currency: false, hide_currency: false, label: nil) %>
<% fallback_label = t(".money-label") %>
<div class="form-field pr-0" data-controller="money-field">
<%= form.label label || fallback_label, { class: "form-field__label" } %>
<div class="flex items-center gap-1">
<div class="flex items-center grow gap-1">
<span class="text-gray-500 text-sm" data-money-field-target="symbol">$</span>
<%= money_field form, money_method, { inline: true, "data-money-field-target" => "amount", default_currency: default_currency } %>
</div>
<% unless hide_currency %>
<div>
<%= currency_select form, :currency, { inline: true }, {
class: "form-field__input text-right pr-8 disabled:text-gray-500",
disabled: disable_currency,
data: {
"money-field-target" => "currency",
action: "money-field#handleCurrencyChange"
}
} %>
</div>
<% end %>
</div>
</div>

View file

@ -1,22 +0,0 @@
<%# locals: (value: 'last_30_days', button_class: '') -%>
<% options = [["7D", "last_7_days"], ["1M", "last_30_days"], ["1Y", "last_365_days"], ["All", "all"]] %>
<div data-controller="select" data-select-active-class="bg-alpha-black-50" class="relative" data-select-selected-value="<%= value %>">
<%=
tag.button(
type: "button",
data: { "select-target": "button" },
class: button_class.presence || "flex items-center gap-1 w-full border border-alpha-black-100 shadow-xs rounded-lg text-sm p-2 cursor-pointer text-gray-900 text-sm"
) do
%>
<span data-select-target="buttonText"><%= options.find { |option| option[1] == value }[0] %></span>
<%= lucide_icon("chevron-down", class: "w-5 h-5 text-gray-500") %>
<% end %>
<input type="hidden" name="period" data-select-target="input" data-auto-submit-form-target="auto">
<ul data-select-target="list" class="hidden absolute z-10 top-[100%] right-0 border border-alpha-black-25 bg-white rounded-lg shadow-xs">
<% options.each do |label, value| %>
<li tabindex="0" data-select-target="option" data-action="click->select#selectOption" data-value="<%= value %>" class="text-sm text-gray-900 rounded-lg cursor-pointer hover:bg-alpha-black-50 px-5 py-1">
<%= label %>
</li>
<% end %>
</ul>
</div>

View file

@ -11,14 +11,15 @@
</p>
</div>
<%= form_with url: tag_deletions_path(@tag),
data: {
turbo: false,
controller: "deletion",
deletion_dangerous_action_class: "form-field__submit bg-white text-red-600 border hover:bg-red-50",
deletion_safe_action_class: "form-field__submit border border-transparent",
deletion_submit_text_when_not_replacing_value: t(".delete_and_leave_uncategorized", tag_name: @tag.name),
deletion_submit_text_when_replacing_value: t(".delete_and_recategorize", tag_name: @tag.name) } do |f| %>
<%= styled_form_with url: tag_deletions_path(@tag),
class: "space-y-4",
data: {
turbo: false,
controller: "deletion",
deletion_dangerous_action_class: "form-field__submit bg-white text-red-600 border hover:bg-red-50",
deletion_safe_action_class: "form-field__submit border border-transparent",
deletion_submit_text_when_not_replacing_value: t(".delete_and_leave_uncategorized", tag_name: @tag.name),
deletion_submit_text_when_replacing_value: t(".delete_and_recategorize", tag_name: @tag.name) } do |f| %>
<%= f.collection_select :replacement_tag_id,
Current.family.tags.alphabetically.without(@tag),
:id, :name,

View file

@ -1,4 +1,4 @@
<%= form_with model: tag, data: { turbo: false } do |form| %>
<%= styled_form_with model: tag, data: { turbo: false } do |form| %>
<div class="flex flex-col space-y-4 w-96" data-controller="color-select" data-color-select-selection-value="<%= tag.color %>">
<fieldset class="relative">
<span data-color-select-target="decoration" class="pointer-events-none absolute inset-y-3.5 left-3 flex items-center pl-1 block w-1 rounded-lg"></span>

View file

@ -1,4 +1,4 @@
<%= form_with model: @entry, url: transactions_path, data: { turbo_frame: "_top" } do |f| %>
<%= styled_form_with model: @entry, url: transactions_path, class: "space-y-4", data: { turbo_frame: "_top" } do |f| %>
<section>
<fieldset class="bg-gray-50 rounded-lg p-1 grid grid-flow-col justify-stretch gap-x-2">
<%= radio_tab_tag form: f, name: :nature, value: :expense, label: t(".expense"), icon: "minus-circle", checked: params[:nature] == "expense" || params[:nature].nil? %>
@ -13,7 +13,7 @@
<section class="space-y-2">
<%= f.text_field :name, label: t(".description"), placeholder: t(".description_placeholder"), required: true %>
<%= f.collection_select :account_id, Current.family.accounts.alphabetically, :id, :name, { prompt: t(".account_prompt"), label: t(".account") }, required: true %>
<%= f.money_field :amount_money, label: t(".amount"), required: true %>
<%= money_with_currency_field f, :amount_money, label: t(".amount"), required: true %>
<%= f.hidden_field :entryable_type, value: "Account::Transaction" %>
<%= f.fields_for :entryable do |ef| %>
<%= ef.collection_select :category_id, Current.family.categories.alphabetically, :id, :name, { prompt: t(".category_prompt"), label: t(".category") } %>

View file

@ -41,7 +41,6 @@
</div>
<div class="flex items-center gap-4">
<%= form_with url: transactions_path,
builder: ActionView::Helpers::FormBuilder,
method: :get,
class: "flex items-center gap-4",
data: { controller: "auto-submit-form" } do |f| %>

View file

@ -2,7 +2,7 @@
<dialog data-controller="modal"
data-action="click->modal#clickOutside"
class="bg-white border border-alpha-black-25 rounded-2xl max-h-[calc(100vh-32px)] max-w-[480px] w-full shadow-xs h-full mt-4 mr-4">
<%= form_with url: bulk_update_transactions_path, scope: "bulk_update", html: { class: "h-full" }, data: { turbo_frame: "_top" } do |form| %>
<%= styled_form_with url: bulk_update_transactions_path, scope: "bulk_update", class: "h-full", data: { turbo_frame: "_top" } do |form| %>
<div class="flex h-full flex-col justify-between p-4">
<div>
<div class="flex h-9 items-center justify-end">

View file

@ -5,11 +5,11 @@
data: { controller: "auto-submit-form" } do |form| %>
<div class="flex gap-2 mb-4">
<div class="grow">
<div class="relative flex items-center bg-white border border-gray-200 rounded-lg">
<div class="relative flex items-center bg-white border border-alpha-black-200 rounded-lg focus-within:border-alpha-black-500">
<%= form.text_field :search,
placeholder: "Search transactions by name",
value: @q[:search],
class: "placeholder:text-sm placeholder:text-gray-500 relative pl-10 w-full border-none rounded-lg",
class: "placeholder:text-sm placeholder:text-gray-500 relative pl-10 w-full border-none rounded-lg focus:outline-none focus:ring-0",
"data-auto-submit-form-target": "auto" %>
<%= lucide_icon("search", class: "w-5 h-5 text-gray-500 ml-2 absolute inset-0 transform top-1/2 -translate-y-1/2") %>
</div>