1
0
Fork 0
mirror of https://github.com/maybe-finance/maybe.git synced 2025-07-23 15:19:38 +02:00

Implement dark mode (#2078)

* User theme settings

* Initial rough pass on colors

* More progress on dark mode
This commit is contained in:
Josh Pigford 2025-04-11 09:28:00 -05:00 committed by GitHub
parent 52d170e36c
commit 88a6373e84
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
98 changed files with 580 additions and 196 deletions

View file

@ -15,7 +15,7 @@ The codebase uses TailwindCSS v4.x (the newest version) with a custom design sys
- Always start by referencing [maybe-design-system.css](mdc:app/assets/tailwind/maybe-design-system.css) to see the base primitives, functional tokens, and component tokens we use in the codebase - Always start by referencing [maybe-design-system.css](mdc:app/assets/tailwind/maybe-design-system.css) to see the base primitives, functional tokens, and component tokens we use in the codebase
- Always prefer using the functional "tokens" defined in @maybe-design-system.css when possible. - Always prefer using the functional "tokens" defined in @maybe-design-system.css when possible.
- Example 1: use `text-primary` rather than `text-gray-900` - Example 1: use `text-primary` rather than `text-primary`
- Example 2: use `bg-container` rather than `bg-white` - Example 2: use `bg-container` rather than `bg-white`
- Example 3: use `border border-primary` rather than `border border-gray-200` - Example 3: use `border border-primary` rather than `border border-gray-200`
- Never create new styles in [maybe-design-system.css](mdc:app/assets/tailwind/maybe-design-system.css) or [application.css](mdc:app/assets/tailwind/application.css) without explicitly receiving permission to do so - Never create new styles in [maybe-design-system.css](mdc:app/assets/tailwind/maybe-design-system.css) or [application.css](mdc:app/assets/tailwind/application.css) without explicitly receiving permission to do so

View file

@ -5,6 +5,8 @@
One-off styling (3rd party overrides, etc.) should be done in the application.css file. One-off styling (3rd party overrides, etc.) should be done in the application.css file.
*/ */
@custom-variant theme-dark (&:where([data-theme=dark], [data-theme=dark] *));
@theme { @theme {
/* Font families */ /* Font families */
--font-sans: 'Geist', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif; --font-sans: 'Geist', system-ui, -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', Arial, sans-serif;
@ -241,78 +243,235 @@
/* Design system color utilities */ /* Design system color utilities */
@utility text-primary { @utility text-primary {
@apply text-gray-900; @apply text-gray-900;
@variant theme-dark {
@apply text-white;
}
} }
@utility text-secondary { @utility text-secondary {
@apply text-gray-500; @apply text-gray-500;
@variant theme-dark {
@apply text-gray-400;
}
} }
@utility text-subdued { @utility text-subdued {
@apply text-gray-400; @apply text-gray-400;
@variant theme-dark {
@apply text-gray-600;
}
} }
@utility text-link { @utility text-link {
@apply text-blue-600; @apply text-blue-600;
@variant theme-dark {
@apply text-blue-500;
}
} }
@utility bg-surface { @utility bg-surface {
@apply bg-gray-50; @apply bg-gray-50;
@variant theme-dark {
@apply bg-black;
}
} }
@utility bg-surface-hover { @utility bg-surface-hover {
@apply bg-gray-100; @apply bg-gray-100;
@variant theme-dark {
@apply bg-gray-800;
}
} }
@utility bg-surface-inset { @utility bg-surface-inset {
@apply bg-gray-100; @apply bg-gray-100;
@variant theme-dark {
@apply bg-gray-900;
}
} }
@utility bg-surface-inset-hover { @utility bg-surface-inset-hover {
@apply bg-gray-200; @apply bg-gray-200;
@variant theme-dark {
@apply bg-gray-800;
}
} }
@utility bg-container { @utility bg-container {
@apply bg-white; @apply bg-white;
@variant theme-dark {
@apply bg-gray-900;
}
} }
@utility bg-container-hover { @utility bg-container-hover {
@apply bg-gray-50; @apply bg-gray-50;
@variant theme-dark {
@apply bg-gray-800;
}
} }
@utility bg-container-inset { @utility bg-container-inset {
@apply bg-gray-50; @apply bg-gray-50;
@variant theme-dark {
@apply bg-gray-800;
}
} }
@utility bg-container-inset-hover { @utility bg-container-inset-hover {
@apply bg-gray-100; @apply bg-gray-100;
@variant theme-dark {
@apply bg-gray-700;
}
} }
@utility bg-inverse { @utility bg-inverse {
@apply bg-gray-800; @apply bg-gray-800;
@variant theme-dark {
@apply bg-white;
}
} }
@utility bg-inverse-hover { @utility bg-inverse-hover {
@apply bg-gray-700; @apply bg-gray-700;
@variant theme-dark {
@apply bg-gray-100;
}
} }
@utility bg-overlay { @utility bg-overlay {
@apply bg-alpha-black-200; background-color: rgba(var(--color-gray-100), 0.5);
@variant theme-dark {
background-color: var(--color-alpha-black-900);
}
} }
@utility border-primary { @utility border-primary {
@apply border-alpha-black-300; @apply border-alpha-black-300;
@variant theme-dark {
@apply border-alpha-white-400;
}
} }
@utility border-secondary { @utility border-secondary {
@apply border-alpha-black-200; @apply border-alpha-black-200;
@variant theme-dark {
@apply border-alpha-white-300;
}
} }
@utility border-tertiary { @utility border-tertiary {
@apply border-alpha-black-100; @apply border-alpha-black-100;
@variant theme-dark {
@apply border-alpha-white-200;
}
} }
@utility border-subdued { @utility border-subdued {
@apply border-alpha-black-50; @apply border-alpha-black-50;
@variant theme-dark {
@apply border-alpha-white-100;
}
}
@utility border-solid {
@apply border-black;
@variant theme-dark {
@apply border-white;
}
}
@utility border-destructive {
@apply border-red-500;
@variant theme-dark {
@apply border-red-400;
}
}
/* Foreground Colors */
@utility fg-gray {
@apply text-gray-500;
@variant theme-dark {
@apply text-gray-400;
}
}
@utility fg-contrast {
@apply text-gray-400;
@variant theme-dark {
@apply text-gray-500;
}
}
@utility fg-inverse {
@apply text-white;
@variant theme-dark {
@apply text-gray-900;
}
}
@utility fg-primary {
@apply text-gray-900;
@variant theme-dark {
@apply text-white;
}
}
@utility fg-primary-variant {
@apply text-gray-800;
@variant theme-dark {
@apply text-gray-50;
}
}
@utility fg-secondary {
@apply text-gray-50;
@variant theme-dark {
@apply text-gray-700;
}
}
@utility fg-secondary-variant {
@apply text-gray-100;
@variant theme-dark {
@apply text-gray-600;
}
}
@utility fg-subdued {
@apply text-gray-400;
@variant theme-dark {
@apply text-gray-500;
}
} }
@layer base { @layer base {
@ -331,6 +490,15 @@
details>summary { details>summary {
@apply list-none; @apply list-none;
} }
input[type='radio'] {
@apply border-gray-300 text-indigo-600 focus:ring-indigo-600; /* Default light mode */
@variant theme-dark {
/* Dark mode radio button base and checked styles */
@apply border-gray-600 bg-gray-700 checked:bg-blue-500 focus:ring-blue-500 focus:ring-offset-gray-800;
}
}
} }
@layer components { @layer components {
@ -341,31 +509,63 @@
} }
.btn--primary { .btn--primary {
@apply bg-gray-900 text-white hover:bg-gray-700 disabled:bg-gray-50 disabled:hover:bg-gray-50 disabled:text-gray-400; @apply button-bg-primary text-white disabled:text-gray-400;
@apply hover:button-bg-primary-hover;
@apply disabled:button-bg-disabled disabled:hover:button-bg-disabled;
@variant theme-dark {
@apply button-bg-primary fg-primary;
@apply hover:button-bg-primary-hover;
@apply disabled:button-bg-disabled disabled:hover:button-bg-disabled;
}
} }
.btn--secondary { .btn--secondary {
@apply bg-gray-50 hover:bg-gray-100 text-gray-900; @apply button-bg-secondary text-primary;
@apply hover:button-bg-secondary-hover;
@variant theme-dark {
@apply button-bg-secondary text-white;
@apply hover:button-bg-secondary-hover;
}
} }
.btn--outline { .btn--outline {
@apply border border-alpha-black-200 text-gray-900 hover:bg-gray-50 disabled:bg-gray-50 disabled:hover:bg-gray-50 disabled:text-gray-400; @apply border border-alpha-black-200 text-primary disabled:button-bg-disabled disabled:hover:button-bg-disabled disabled:text-gray-400;
@apply hover:button-bg-outline-hover;
@variant theme-dark {
@apply border-alpha-white-300 text-white disabled:button-bg-disabled disabled:hover:button-bg-disabled disabled:text-gray-600;
@apply hover:button-bg-outline-hover;
}
} }
.btn--ghost { .btn--ghost {
@apply border border-transparent text-gray-900 hover:bg-gray-100; @apply border border-transparent text-primary hover:button-bg-ghost-hover;
@variant theme-dark {
@apply fg-primary hover:button-bg-ghost-hover;
}
} }
.btn--destructive { .btn--destructive {
@apply bg-red-500 text-white hover:bg-red-600 disabled:bg-red-50 disabled:hover:bg-red-50 disabled:text-red-400; @apply button-bg-destructive text-white hover:button-bg-destructive-hover disabled:button-bg-disabled disabled:hover:button-bg-disabled disabled:text-red-400;
@variant theme-dark {
@apply button-bg-destructive text-white hover:button-bg-destructive-hover disabled:button-bg-disabled disabled:hover:button-bg-disabled;
}
} }
/* Forms */ /* Forms */
.form-field { .form-field {
@apply flex flex-col gap-1 relative px-3 py-2 rounded-md border bg-white border-alpha-black-100 shadow-xs w-full; @apply flex flex-col gap-1 relative px-3 py-2 rounded-md border bg-container border-secondary shadow-xs w-full;
@apply focus-within:border-gray-900 focus-within:shadow-none focus-within:ring-4 focus-within:ring-gray-100; @apply focus-within:border-secondary focus-within:shadow-none focus-within:ring-4 focus-within:ring-alpha-black-200;
@apply transition-all duration-300; @apply transition-all duration-300;
@variant theme-dark {
@apply focus-within:ring-alpha-white-300;
}
/* Add styles for multiple select within form fields */ /* Add styles for multiple select within form fields */
select[multiple] { select[multiple] {
@apply py-2 pr-2 space-y-0.5 overflow-y-auto; @apply py-2 pr-2 space-y-0.5 overflow-y-auto;
@ -375,25 +575,25 @@
} }
option:checked { option:checked {
@apply after:content-['\2713'] bg-white after:text-gray-500 after:ml-2; @apply after:content-['\2713'] bg-container-inset after:text-gray-500 after:ml-2;
} }
option:active, option:active,
option:focus { option:focus {
@apply bg-white; @apply bg-container-inset;
} }
} }
} }
.form-field__label { .form-field__label {
@apply block text-xs text-gray-500 peer-disabled:text-gray-400; @apply block text-xs text-secondary peer-disabled:text-subdued;
} }
.form-field__input { .form-field__input {
@apply border-none bg-transparent text-sm opacity-100 w-full p-0; @apply border-none bg-transparent text-sm opacity-100 w-full p-0;
@apply focus:opacity-100 focus:outline-hidden focus:ring-0; @apply focus:opacity-100 focus:outline-hidden focus:ring-0;
@apply placeholder-shown:opacity-50; @apply placeholder-shown:opacity-50;
@apply disabled:text-gray-400; @apply disabled:text-subdued;
@apply text-ellipsis overflow-hidden whitespace-nowrap; @apply text-ellipsis overflow-hidden whitespace-nowrap;
@apply transition-opacity duration-300; @apply transition-opacity duration-300;
@ -403,11 +603,11 @@
} }
.form-field__radio { .form-field__radio {
@apply text-gray-900; @apply text-primary;
} }
.form-field__submit { .form-field__submit {
@apply cursor-pointer rounded-lg bg-black p-3 text-center text-white hover:bg-gray-700; @apply cursor-pointer rounded-lg bg-surface p-3 text-center text-white hover:bg-surface-hover;
} }
/* Checkboxes */ /* Checkboxes */
@ -456,3 +656,108 @@
@apply hidden absolute; @apply hidden absolute;
} }
} }
@layer utilities {
/* Specific override for strong tags in prose under dark mode */
.prose:where([data-theme=dark], [data-theme=dark] *) strong {
color: theme(colors.white) !important;
}
}
/* Button Backgrounds */
@utility button-bg-primary {
@apply bg-gray-900; /* Maps to fg-primary light */
@variant theme-dark {
@apply bg-white; /* Maps to fg-primary dark */
}
}
@utility button-bg-primary-hover {
@apply bg-gray-800; /* Maps to fg-primary-variant light */
@variant theme-dark {
@apply bg-gray-50; /* Maps to fg-primary-variant dark */
}
}
@utility button-bg-secondary {
@apply bg-gray-50; /* Maps to fg-secondary light */
@variant theme-dark {
@apply bg-gray-700; /* Maps to fg-secondary dark */
}
}
@utility button-bg-secondary-hover {
@apply bg-gray-100; /* Maps to fg-secondary-variant light */
@variant theme-dark {
@apply bg-gray-600; /* Maps to fg-secondary-variant dark */
}
}
@utility button-bg-disabled {
@apply bg-gray-50;
@variant theme-dark {
@apply bg-gray-700;
}
}
@utility button-bg-destructive {
@apply bg-red-500;
@variant theme-dark {
@apply bg-red-400;
}
}
@utility button-bg-destructive-hover {
@apply bg-red-600;
@variant theme-dark {
@apply bg-red-500;
}
}
@utility button-bg-ghost-hover {
@apply bg-gray-50;
@variant theme-dark {
@apply bg-gray-800;
}
}
@utility button-bg-outline-hover {
@apply bg-gray-100;
@variant theme-dark {
@apply bg-gray-700;
}
}
/* Tab Styles */
@utility tab-item-active {
@apply bg-white;
@variant theme-dark {
@apply bg-gray-700;
}
}
@utility tab-item-hover {
@apply bg-gray-200;
@variant theme-dark {
@apply bg-gray-800;
}
}
@utility tab-bg-group {
@apply bg-gray-50;
@variant theme-dark {
@apply bg-alpha-black-700;
}
}

View file

@ -74,7 +74,7 @@ class UsersController < ApplicationController
def user_params def user_params
params.require(:user).permit( params.require(:user).permit(
:first_name, :last_name, :email, :profile_image, :redirect_to, :delete_profile_image, :onboarded_at, :show_sidebar, :default_period, :show_ai_sidebar, :ai_enabled, :first_name, :last_name, :email, :profile_image, :redirect_to, :delete_profile_image, :onboarded_at, :show_sidebar, :default_period, :show_ai_sidebar, :ai_enabled, :theme,
family_attributes: [ :name, :currency, :country, :locale, :date_format, :timezone, :id, :data_enrichment_enabled ] family_attributes: [ :name, :currency, :country, :locale, :date_format, :timezone, :id, :data_enrichment_enabled ]
) )
end end

View file

@ -17,7 +17,7 @@ module FormsHelper
end end
end end
def period_select(form:, selected:, classes: "border border-secondary rounded-lg text-sm pr-7 cursor-pointer text-primary focus:outline-hidden focus:ring-0") def period_select(form:, selected:, classes: "border border-secondary bg-container-inset rounded-lg text-sm pr-7 cursor-pointer text-primary focus:outline-hidden focus:ring-0")
periods_for_select = Period.all.map { |period| [ period.label_short, period.key ] } periods_for_select = Period.all.map { |period| [ period.label_short, period.key ] }
form.select(:period, periods_for_select, { selected: selected.key }, class: classes, data: { "auto-submit-form-target": "auto" }) form.select(:period, periods_for_select, { selected: selected.key }, class: classes, data: { "auto-submit-form-target": "auto" })
@ -30,7 +30,7 @@ end
private private
def radio_tab_contents(label:, icon:) def radio_tab_contents(label:, icon:)
tag.div(class: "flex px-4 py-1 rounded-lg items-center space-x-2 justify-center text-subdued group-has-checked:bg-white group-has-checked:text-gray-800 group-has-checked:shadow-sm") do tag.div(class: "flex px-4 py-1 rounded-lg items-center space-x-2 justify-center text-subdued group-has-checked:bg-container group-has-checked:text-gray-800 group-has-checked:shadow-sm") do
concat lucide_icon(icon, class: "w-5 h-5") concat lucide_icon(icon, class: "w-5 h-5")
concat tag.span(label, class: "group-has-checked:font-semibold") concat tag.span(label, class: "group-has-checked:font-semibold")
end end

View file

@ -38,7 +38,7 @@ module MenusHelper
end end
def contextual_menu_content(&block) def contextual_menu_content(&block)
tag.div class: "min-w-[200px] p-1 z-50 shadow-border-xs bg-white rounded-lg hidden", tag.div class: "min-w-[200px] p-1 z-50 shadow-border-xs bg-container rounded-lg hidden",
data: { menu_target: "content" } do data: { menu_target: "content" } do
capture(&block) capture(&block)
end end

View file

@ -0,0 +1,73 @@
import { Controller } from "@hotwired/stimulus"
export default class extends Controller {
static values = { userPreference: String }
connect() {
this.applyTheme()
this.startSystemThemeListener()
}
disconnect() {
this.stopSystemThemeListener()
}
// Called automatically by Stimulus when the userPreferenceValue changes (e.g., after form submit/page reload)
userPreferenceValueChanged() {
this.applyTheme()
}
// Called when a theme radio button is clicked
updateTheme(event) {
const selectedTheme = event.currentTarget.value
if (selectedTheme === "system") {
this.setTheme(this.systemPrefersDark())
} else if (selectedTheme === "dark") {
this.setTheme(true)
} else {
this.setTheme(false)
}
}
// Applies theme based on the userPreferenceValue (from server)
applyTheme() {
if (this.userPreferenceValue === "system") {
this.setTheme(this.systemPrefersDark())
} else if (this.userPreferenceValue === "dark") {
this.setTheme(true)
} else {
this.setTheme(false)
}
}
// Sets or removes the data-theme attribute
setTheme(isDark) {
if (isDark) {
document.documentElement.setAttribute("data-theme", "dark")
} else {
document.documentElement.removeAttribute("data-theme")
}
}
systemPrefersDark() {
return window.matchMedia("(prefers-color-scheme: dark)").matches
}
handleSystemThemeChange = (event) => {
// Only apply system theme changes if the user preference is currently 'system'
if (this.userPreferenceValue === "system") {
this.setTheme(event.matches)
}
}
startSystemThemeListener() {
this.darkMediaQuery = window.matchMedia("(prefers-color-scheme: dark)")
this.darkMediaQuery.addEventListener("change", this.handleSystemThemeChange)
}
stopSystemThemeListener() {
if (this.darkMediaQuery) {
this.darkMediaQuery.removeEventListener("change", this.handleSystemThemeChange)
}
}
}

View file

@ -1,6 +1,6 @@
<%# locals: (date:, entries:, content:, totals: false) %> <%# locals: (date:, entries:, content:, totals: false) %>
<div id="entry-group-<%= date %>" class="bg-gray-25 rounded-xl p-1 w-full" data-bulk-select-target="group"> <div id="entry-group-<%= date %>" class="bg-container-inset rounded-xl p-1 w-full" data-bulk-select-target="group">
<div class="py-2 px-4 flex items-center justify-between font-medium text-xs text-secondary"> <div class="py-2 px-4 flex items-center justify-between font-medium text-xs text-secondary">
<div class="flex pl-0.5 items-center gap-4"> <div class="flex pl-0.5 items-center gap-4">
<%= check_box_tag "#{date}_entries_selection", <%= check_box_tag "#{date}_entries_selection",
@ -21,7 +21,7 @@
</div> </div>
<% end %> <% end %>
</div> </div>
<div class="bg-white shadow-border-xs rounded-md divide-y divide-alpha-black-50"> <div class="bg-container shadow-border-xs rounded-md divide-y divide-alpha-black-50">
<%= content %> <%= content %>
</div> </div>
</div> </div>

View file

@ -1,4 +1,4 @@
<div class="bg-white space-y-4 p-5 shadow-border-xs rounded-xl"> <div class="bg-container space-y-4 p-5 shadow-border-xs rounded-xl">
<div class="p-5 flex justify-center items-center"> <div class="p-5 flex justify-center items-center">
<%= tag.p t(".loading"), class: "text-secondary animate-pulse text-sm" %> <%= tag.p t(".loading"), class: "text-secondary animate-pulse text-sm" %>
</div> </div>

View file

@ -1,5 +1,5 @@
<%= turbo_frame_tag dom_id(@account, "holdings") do %> <%= turbo_frame_tag dom_id(@account, "holdings") do %>
<div class="bg-white space-y-4 p-5 rounded-xl shadow-border-xs"> <div class="bg-container space-y-4 p-5 rounded-xl shadow-border-xs">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<%= tag.h2 t(".holdings"), class: "font-medium text-lg" %> <%= tag.h2 t(".holdings"), class: "font-medium text-lg" %>
<%= link_to new_account_trade_path(account_id: @account.id), <%= link_to new_account_trade_path(account_id: @account.id),
@ -11,7 +11,7 @@
<% end %> <% end %>
</div> </div>
<div class="rounded-xl bg-gray-25 p-1"> <div class="rounded-xl bg-container-inset p-1">
<div class="grid grid-cols-12 items-center uppercase text-xs font-medium text-secondary px-4 py-2"> <div class="grid grid-cols-12 items-center uppercase text-xs font-medium text-secondary px-4 py-2">
<%= tag.p t(".name"), class: "col-span-4" %> <%= tag.p t(".name"), class: "col-span-4" %>
<%= tag.p t(".weight"), class: "col-span-2 justify-self-end" %> <%= tag.p t(".weight"), class: "col-span-2 justify-self-end" %>
@ -20,7 +20,7 @@
<%= tag.p t(".return"), class: "col-span-2 justify-self-end" %> <%= tag.p t(".return"), class: "col-span-2 justify-self-end" %>
</div> </div>
<div class="rounded-lg bg-white shadow-border-xs"> <div class="rounded-lg bg-container shadow-border-xs">
<%= render "account/holdings/cash", account: @account %> <%= render "account/holdings/cash", account: @account %>
<%= render "account/holdings/ruler" %> <%= render "account/holdings/ruler" %>

View file

@ -8,7 +8,7 @@
<fieldset class="bg-gray-50 rounded-lg p-1 grid grid-flow-col justify-stretch gap-x-2"> <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: :outflow, label: t(".expense"), icon: "minus-circle", checked: params[:nature] == "outflow" || params[:nature].nil? %> <%= radio_tab_tag form: f, name: :nature, value: :outflow, label: t(".expense"), icon: "minus-circle", checked: params[:nature] == "outflow" || params[:nature].nil? %>
<%= radio_tab_tag form: f, name: :nature, value: :inflow, label: t(".income"), icon: "plus-circle", checked: params[:nature] == "inflow" %> <%= radio_tab_tag form: f, name: :nature, value: :inflow, label: t(".income"), icon: "plus-circle", checked: params[:nature] == "inflow" %>
<%= link_to new_transfer_path, data: { turbo_frame: :modal }, class: "flex px-4 py-1 rounded-lg items-center space-x-2 justify-center text-subdued group-has-checked:bg-white group-has-checked:text-gray-800 group-has-checked:shadow-sm" do %> <%= link_to new_transfer_path, data: { turbo_frame: :modal }, class: "flex px-4 py-1 rounded-lg items-center space-x-2 justify-center text-subdued group-has-checked:bg-container group-has-checked:text-gray-800 group-has-checked:shadow-sm" do %>
<%= lucide_icon "arrow-right-left", class: "w-5 h-5" %> <%= lucide_icon "arrow-right-left", class: "w-5 h-5" %>
<%= tag.span t(".transfer") %> <%= tag.span t(".transfer") %>
<% end %> <% end %>

View file

@ -1,7 +1,7 @@
<%= turbo_frame_tag "bulk_transaction_edit_drawer" do %> <%= turbo_frame_tag "bulk_transaction_edit_drawer" do %>
<dialog data-controller="modal" <dialog data-controller="modal"
data-action="mousedown->modal#clickOutside" data-action="mousedown->modal#clickOutside"
class="bg-white shadow-border-xs rounded-2xl max-h-[calc(100vh-32px)] h-full max-w-[480px] w-full mt-4 mr-4 ml-auto"> class="bg-container shadow-border-xs rounded-2xl max-h-[calc(100vh-32px)] h-full max-w-[480px] w-full mt-4 mr-4 ml-auto">
<%= styled_form_with url: bulk_update_account_transactions_path, scope: "bulk_update", class: "h-full", data: { turbo_frame: "_top" } do |form| %> <%= styled_form_with url: bulk_update_account_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 gap-4"> <div class="flex h-full flex-col justify-between p-4 gap-4">
<div> <div>

View file

@ -1,5 +1,5 @@
<%= turbo_frame_tag dom_id(@account, "valuations") do %> <%= turbo_frame_tag dom_id(@account, "valuations") do %>
<div class="bg-white space-y-4 p-5 shadow-border-xs rounded-xl"> <div class="bg-container space-y-4 p-5 shadow-border-xs rounded-xl">
<div class="flex items-center justify-between"> <div class="flex items-center justify-between">
<%= tag.h2 t(".valuations"), class: "font-medium text-lg" %> <%= tag.h2 t(".valuations"), class: "font-medium text-lg" %>
<%= link_to new_account_valuation_path(@account), <%= link_to new_account_valuation_path(@account),
@ -10,7 +10,7 @@
<% end %> <% end %>
</div> </div>
<div class="rounded-xl bg-gray-25 p-1"> <div class="rounded-xl bg-container-inset p-1">
<div class="grid grid-cols-10 items-center uppercase text-xs font-medium text-secondary px-4 py-2"> <div class="grid grid-cols-10 items-center uppercase text-xs font-medium text-secondary px-4 py-2">
<%= tag.p t(".date"), class: "col-span-5" %> <%= tag.p t(".date"), class: "col-span-5" %>
<%= tag.p t(".value"), class: "col-span-2 justify-self-end" %> <%= tag.p t(".value"), class: "col-span-2 justify-self-end" %>
@ -18,7 +18,7 @@
<%= tag.div class: "col-span-1" %> <%= tag.div class: "col-span-1" %>
</div> </div>
<div class="rounded-lg bg-white shadow-border-xs"> <div class="rounded-lg bg-container shadow-border-xs">
<%= turbo_frame_tag dom_id(@account.entries.account_valuations.new) %> <%= turbo_frame_tag dom_id(@account.entries.account_valuations.new) %>
<% if @entries.any? %> <% if @entries.any? %>

View file

@ -24,7 +24,7 @@
class="space-y-3" class="space-y-3"
data-controller="tabs" data-controller="tabs"
data-tabs-local-storage-key-value="account-sidebar-tabs" data-tabs-local-storage-key-value="account-sidebar-tabs"
data-tabs-active-class="bg-white shadow-sm text-primary" data-tabs-active-class="bg-surface shadow-sm text-primary"
data-tabs-inactive-class="text-secondary" data-tabs-inactive-class="text-secondary"
data-tabs-default-tab-value="assets-tab"> data-tabs-default-tab-value="assets-tab">
<div class="bg-surface-inset rounded-lg p-1 flex"> <div class="bg-surface-inset rounded-lg p-1 flex">

View file

@ -11,7 +11,7 @@
<%= turbo_frame_tag "#{account_group.key}_sparkline", src: accountable_sparkline_path(account_group.key), loading: "lazy" do %> <%= turbo_frame_tag "#{account_group.key}_sparkline", src: accountable_sparkline_path(account_group.key), loading: "lazy" do %>
<div class="flex items-center w-8 h-4 ml-auto"> <div class="flex items-center w-8 h-4 ml-auto">
<div class="w-6 h-px bg-gray-200"></div> <div class="w-6 h-px bg-surface-inset"></div>
</div> </div>
<% end %> <% end %>
</div> </div>
@ -23,7 +23,7 @@
<%= render "accounts/logo", account: account, size: "sm", color: account_group.color %> <%= render "accounts/logo", account: account, size: "sm", color: account_group.color %>
<div class="min-w-0 grow"> <div class="min-w-0 grow">
<%= tag.p account.name, class: "text-sm font-medium mb-0.5 truncate" %> <%= tag.p account.name, class: "text-sm text-primary font-medium mb-0.5 truncate" %>
<%= tag.p account.subtype&.humanize.presence || account_group.name, class: "text-sm text-secondary truncate" %> <%= tag.p account.subtype&.humanize.presence || account_group.name, class: "text-sm text-secondary truncate" %>
</div> </div>
@ -32,7 +32,7 @@
<%= turbo_frame_tag dom_id(account, :sparkline), src: sparkline_account_path(account), loading: "lazy" do %> <%= turbo_frame_tag dom_id(account, :sparkline), src: sparkline_account_path(account), loading: "lazy" do %>
<div class="flex items-center w-8 h-5 ml-auto"> <div class="flex items-center w-8 h-5 ml-auto">
<div class="w-6 h-px bg-gray-200"></div> <div class="w-6 h-px bg-surface-inset"></div>
</div> </div>
<% end %> <% end %>
</div> </div>

View file

@ -1,6 +1,6 @@
<%# locals: (title:, content:) %> <%# locals: (title:, content:) %>
<div class="rounded-xl bg-white shadow-xs border border-alpha-black-25 p-4"> <div class="rounded-xl bg-container shadow-xs border border-alpha-black-25 p-4">
<h4 class="text-secondary text-sm"><%= title %></h4> <h4 class="text-secondary text-sm"><%= title %></h4>
<p class="text-xl font-medium text-primary"> <p class="text-xl font-medium text-primary">
<%= content %> <%= content %>

View file

@ -1,14 +1,14 @@
<%# locals: (accounts:) %> <%# locals: (accounts:) %>
<% accounts.group_by(&:accountable_type).sort_by { |group, _| group }.each do |group, accounts| %> <% accounts.group_by(&:accountable_type).sort_by { |group, _| group }.each do |group, accounts| %>
<div class="bg-gray-25 p-1 rounded-xl"> <div class="bg-container-inset p-1 rounded-xl">
<div class="flex items-center px-4 py-2 text-xs font-medium text-secondary"> <div class="flex items-center px-4 py-2 text-xs font-medium text-secondary">
<p><%= Accountable.from_type(group).display_name %></p> <p><%= Accountable.from_type(group).display_name %></p>
<span class="text-subdued mx-2">&middot;</span> <span class="text-subdued mx-2">&middot;</span>
<p><%= accounts.count %></p> <p><%= accounts.count %></p>
<p class="ml-auto"><%= totals_by_currency(collection: accounts, money_method: :balance_money) %></p> <p class="ml-auto"><%= totals_by_currency(collection: accounts, money_method: :balance_money) %></p>
</div> </div>
<div class="bg-white"> <div class="bg-container">
<% accounts.each do |account| %> <% accounts.each do |account| %>
<%= render account %> <%= render account %>
<% end %> <% end %>

View file

@ -1,6 +1,6 @@
<%# locals: (accounts:) %> <%# locals: (accounts:) %>
<details open class="group bg-white p-4 shadow-border-xs rounded-xl"> <details open class="group bg-container p-4 shadow-border-xs rounded-xl">
<summary class="flex items-center gap-2 focus-visible:outline-hidden"> <summary class="flex items-center gap-2 focus-visible:outline-hidden">
<%= lucide_icon "chevron-right", class: "group-open:transform group-open:rotate-90 text-secondary w-5" %> <%= lucide_icon "chevron-right", class: "group-open:transform group-open:rotate-90 text-secondary w-5" %>

View file

@ -1,7 +1,7 @@
<%# locals: (account:) %> <%# locals: (account:) %>
<%= turbo_frame_tag dom_id(account, "entries") do %> <%= turbo_frame_tag dom_id(account, "entries") do %>
<div class="bg-white p-5 shadow-border-xs rounded-xl" data-controller="focus-record" data-focus-record-id-value="<%= @focused_record ? dom_id(@focused_record) : nil %>"> <div class="bg-container p-5 shadow-border-xs rounded-xl" data-controller="focus-record" data-focus-record-id-value="<%= @focused_record ? dom_id(@focused_record) : nil %>">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<%= tag.h2 t(".title"), class: "font-medium text-lg" %> <%= tag.h2 t(".title"), class: "font-medium text-lg" %>
<% unless @account.plaid_account_id.present? %> <% unless @account.plaid_account_id.present? %>
@ -10,7 +10,7 @@
<%= lucide_icon("plus", class: "w-4 h-4") %> <%= lucide_icon("plus", class: "w-4 h-4") %>
<%= tag.span t(".new") %> <%= tag.span t(".new") %>
</button> </button>
<div data-menu-target="content" class="z-10 hidden bg-white rounded-lg border border-alpha-black-25 shadow-xs p-1"> <div data-menu-target="content" class="z-10 hidden bg-container rounded-lg border border-alpha-black-25 shadow-xs p-1">
<%= link_to new_account_valuation_path(account_id: @account.id), data: { turbo_frame: :modal }, class: "block p-2 rounded-lg hover:bg-gray-50 flex items-center gap-2" do %> <%= link_to new_account_valuation_path(account_id: @account.id), data: { turbo_frame: :modal }, class: "block p-2 rounded-lg hover:bg-gray-50 flex items-center gap-2" do %>
<%= lucide_icon("circle-dollar-sign", class: "text-secondary w-5 h-5") %> <%= lucide_icon("circle-dollar-sign", class: "text-secondary w-5 h-5") %>
<%= tag.span t(".new_balance"), class: "text-sm" %> <%= tag.span t(".new_balance"), class: "text-sm" %>
@ -62,7 +62,7 @@
<%= render "account/entries/selection_bar" %> <%= render "account/entries/selection_bar" %>
</div> </div>
<div class="grid bg-gray-25 rounded-xl grid-cols-12 items-center uppercase text-xs font-medium text-secondary px-5 py-3 mb-4"> <div class="grid bg-container-inset rounded-xl grid-cols-12 items-center uppercase text-xs font-medium text-secondary px-5 py-3 mb-4">
<div class="pl-0.5 col-span-8 flex items-center gap-4"> <div class="pl-0.5 col-span-8 flex items-center gap-4">
<%= check_box_tag "selection_entry", <%= check_box_tag "selection_entry",
class: "checkbox checkbox--light", class: "checkbox checkbox--light",
@ -74,7 +74,7 @@
</div> </div>
<div> <div>
<div class="rounded-tl-lg rounded-tr-lg bg-white border-alpha-black-25 shadow-xs"> <div class="rounded-tl-lg rounded-tr-lg bg-container border-alpha-black-25 shadow-xs">
<div class="space-y-4"> <div class="space-y-4">
<% calculator = Account::BalanceTrendCalculator.for(@entries) %> <% calculator = Account::BalanceTrendCalculator.for(@entries) %>
<%= entries_by_date(@entries) do |entries| %> <%= entries_by_date(@entries) do |entries| %>
@ -85,7 +85,7 @@
</div> </div>
</div> </div>
<div class="p-4 bg-white rounded-bl-lg rounded-br-lg"> <div class="p-4 bg-container rounded-bl-lg rounded-br-lg">
<%= render "shared/pagination", pagy: @pagy %> <%= render "shared/pagination", pagy: @pagy %>
</div> </div>
</div> </div>

View file

@ -3,7 +3,7 @@
<% period = @period || Period.last_30_days %> <% period = @period || Period.last_30_days %>
<% default_value_title = account.asset? ? t(".balance") : t(".owed") %> <% default_value_title = account.asset? ? t(".balance") : t(".owed") %>
<div id="<%= dom_id(account, :chart) %>" class="bg-white shadow-xs rounded-xl border border-alpha-black-25 rounded-lg space-y-2"> <div id="<%= dom_id(account, :chart) %>" class="bg-container shadow-xs rounded-xl border border-alpha-black-25 rounded-lg space-y-2">
<div class="flex justify-between px-4 pt-4 mb-2"> <div class="flex justify-between px-4 pt-4 mb-2">
<div class="space-y-2"> <div class="space-y-2">
<div class="flex items-center gap-1"> <div class="flex items-center gap-1">

View file

@ -1,7 +1,7 @@
<%# locals: (account:) %> <%# locals: (account:) %>
<%= contextual_menu do %> <%= contextual_menu do %>
<div class="w-48 p-1 text-sm leading-6 text-primary bg-white shadow-lg shrink rounded-xl ring-1 ring-gray-900/5"> <div class="w-48 p-1 text-sm leading-6 text-primary bg-container shadow-lg shrink rounded-xl ring-1 ring-gray-900/5">
<% if account.plaid_account_id.present? %> <% if account.plaid_account_id.present? %>
<%= link_to accounts_path, <%= link_to accounts_path,
data: { turbo_frame: :_top }, data: { turbo_frame: :_top },

View file

@ -5,5 +5,5 @@
data: { turbo: false }, data: { turbo: false },
class: [ class: [
"px-2 py-1.5 rounded-md border border-transparent", "px-2 py-1.5 rounded-md border border-transparent",
"bg-white shadow-xs border-alpha-black-50": is_selected "bg-container shadow-xs border-alpha-black-50": is_selected
] %> ] %>

View file

@ -1,7 +1,7 @@
<%# locals: (budget_category:) %> <%# locals: (budget_category:) %>
<%= turbo_frame_tag dom_id(budget_category), class: "w-full" do %> <%= turbo_frame_tag dom_id(budget_category), class: "w-full" do %>
<%= link_to budget_budget_category_path(budget_category.budget, budget_category), class: "group w-full p-4 flex items-center gap-3 bg-white", data: { turbo_frame: "drawer" } do %> <%= link_to budget_budget_category_path(budget_category.budget, budget_category), class: "group w-full p-4 flex items-center gap-3 bg-container", data: { turbo_frame: "drawer" } do %>
<% if budget_category.initialized? %> <% if budget_category.initialized? %>
<div class="w-10 h-10 group-hover:scale-105 transition-all duration-300"> <div class="w-10 h-10 group-hover:scale-105 transition-all duration-300">

View file

@ -16,7 +16,7 @@
<div class="mx-auto max-w-lg"> <div class="mx-auto max-w-lg">
<% if @budget.family.categories.empty? %> <% if @budget.family.categories.empty? %>
<div class="bg-white shadow-border-xs rounded-lg p-4"> <div class="bg-container shadow-border-xs rounded-lg p-4">
<%= render "budget_categories/no_categories" %> <%= render "budget_categories/no_categories" %>
</div> </div>
<% else %> <% else %>

View file

@ -9,7 +9,7 @@
<p class="ml-auto">Amount</p> <p class="ml-auto">Amount</p>
</div> </div>
<div class="bg-white py-1 shadow-border-xs rounded-md"> <div class="bg-container py-1 shadow-border-xs rounded-md">
<% if budget.family.categories.expenses.empty? %> <% if budget.family.categories.expenses.empty? %>
<div class="py-8"> <div class="py-8">
<%= render "budget_categories/no_categories" %> <%= render "budget_categories/no_categories" %>

View file

@ -16,9 +16,9 @@
step[:is_complete] ? "text-green-600" : "text-secondary" step[:is_complete] ? "text-green-600" : "text-secondary"
end %> end %>
<% step_class = if is_current <% step_class = if is_current
"bg-gray-900 text-white" "bg-primary text-white"
else else
step[:is_complete] ? "bg-green-600/10 border-alpha-black-25" : "bg-gray-50" step[:is_complete] ? "bg-green-600/10 border-alpha-black-25" : "bg-container-inset"
end %> end %>
<%= link_to step[:path], class: "flex items-center gap-3" do %> <%= link_to step[:path], class: "flex items-center gap-3" do %>

View file

@ -1,7 +1,7 @@
<%# locals: (family:, year:) %> <%# locals: (family:, year:) %>
<%= turbo_frame_tag "budget_picker" do %> <%= turbo_frame_tag "budget_picker" do %>
<div class="bg-white shadow-border-xs p-3 rounded-xl space-y-4"> <div class="bg-container shadow-border-xs p-3 rounded-xl space-y-4">
<div class="flex items-center gap-2 justify-between"> <div class="flex items-center gap-2 justify-between">
<% last_month_of_previous_year = Date.new(year - 1, 12, 1) %> <% last_month_of_previous_year = Date.new(year - 1, 12, 1) %>

View file

@ -7,7 +7,7 @@
<div class="flex items-start gap-4"> <div class="flex items-start gap-4">
<div class="w-[300px] space-y-4"> <div class="w-[300px] space-y-4">
<div class="h-[300px] bg-white rounded-xl shadow-border-xs p-8"> <div class="h-[300px] bg-container rounded-xl shadow-border-xs p-8">
<% if @budget.available_to_allocate.negative? %> <% if @budget.available_to_allocate.negative? %>
<%= render "budgets/over_allocation_warning", budget: @budget %> <%= render "budgets/over_allocation_warning", budget: @budget %>
<% else %> <% else %>
@ -25,7 +25,7 @@
budget_path(@budget, tab: "budgeted"), budget_path(@budget, tab: "budgeted"),
class: class_names( class: class_names(
base_classes, base_classes,
"bg-white shadow-xs text-primary": selected_tab == "budgeted", "bg-container shadow-xs text-primary": selected_tab == "budgeted",
"text-secondary": selected_tab != "budgeted" "text-secondary": selected_tab != "budgeted"
) %> ) %>
@ -33,23 +33,23 @@
budget_path(@budget, tab: "actuals"), budget_path(@budget, tab: "actuals"),
class: class_names( class: class_names(
base_classes, base_classes,
"bg-white shadow-xs text-primary": selected_tab == "actuals", "bg-container shadow-xs text-primary": selected_tab == "actuals",
"text-secondary": selected_tab != "actuals" "text-secondary": selected_tab != "actuals"
) %> ) %>
</div> </div>
<div class="bg-white rounded-xl shadow-border-xs"> <div class="bg-container rounded-xl shadow-border-xs">
<%= render selected_tab == "budgeted" ? "budgets/budgeted_summary" : "budgets/actuals_summary", budget: @budget %> <%= render selected_tab == "budgeted" ? "budgets/budgeted_summary" : "budgets/actuals_summary", budget: @budget %>
</div> </div>
<% else %> <% else %>
<div class="bg-white rounded-xl shadow-border-xs"> <div class="bg-container rounded-xl shadow-border-xs">
<%= render "budgets/actuals_summary", budget: @budget %> <%= render "budgets/actuals_summary", budget: @budget %>
</div> </div>
<% end %> <% end %>
</div> </div>
</div> </div>
<div class="grow bg-white rounded-xl shadow-border-xs p-4"> <div class="grow bg-container rounded-xl shadow-border-xs p-4">
<div class="flex items-center justify-between mb-4"> <div class="flex items-center justify-between mb-4">
<h2 class="text-lg font-medium">Categories</h2> <h2 class="text-lg font-medium">Categories</h2>
@ -61,7 +61,7 @@
<% end %> <% end %>
</div> </div>
<div class="bg-gray-25 rounded-xl p-1"> <div class="bg-container-inset rounded-xl p-1">
<%= render "budgets/budget_categories", budget: @budget %> <%= render "budgets/budget_categories", budget: @budget %>
</div> </div>
</div> </div>

View file

@ -1,6 +1,6 @@
<%# locals: (category:) %> <%# locals: (category:) %>
<div id="<%= dom_id(category) %>" class="flex justify-between items-center px-4 pb-4 <%= "pt-4" unless category.subcategory? %> <%= "pb-4" unless category.subcategories.any? %> bg-white"> <div id="<%= dom_id(category) %>" class="flex justify-between items-center px-4 pb-4 <%= "pt-4" unless category.subcategory? %> <%= "pb-4" unless category.subcategories.any? %> bg-container">
<div class="flex w-full items-center gap-2.5"> <div class="flex w-full items-center gap-2.5">
<% if category.subcategory? %> <% if category.subcategory? %>
<%= lucide_icon "corner-down-right", class: "shrink-0 w-5 h-5 text-subdued ml-2" %> <%= lucide_icon "corner-down-right", class: "shrink-0 w-5 h-5 text-subdued ml-2" %>
@ -10,7 +10,7 @@
</div> </div>
<div class="justify-self-end"> <div class="justify-self-end">
<%= contextual_menu do %> <%= contextual_menu do %>
<div class="w-48 p-1 text-sm leading-6 text-primary bg-white shadow-lg shrink rounded-xl ring-1 ring-gray-900/5"> <div class="w-48 p-1 text-sm leading-6 text-primary bg-container shadow-lg shrink rounded-xl ring-1 ring-gray-900/5">
<%= contextual_menu_modal_action_item t(".edit"), edit_category_path(category) %> <%= contextual_menu_modal_action_item t(".edit"), edit_category_path(category) %>
<% if category.transactions.any? %> <% if category.transactions.any? %>

View file

@ -1,13 +1,13 @@
<%# locals: (title:, categories:) %> <%# locals: (title:, categories:) %>
<div class="rounded-xl bg-gray-25 space-y-1 p-1"> <div class="rounded-xl bg-container-inset space-y-1 p-1">
<div class="flex items-center gap-1.5 px-4 py-2 text-xs font-medium text-secondary uppercase"> <div class="flex items-center gap-1.5 px-4 py-2 text-xs font-medium text-secondary uppercase">
<p><%= title %></p> <p><%= title %></p>
<span class="text-subdued">&middot;</span> <span class="text-subdued">&middot;</span>
<p><%= categories.count %></p> <p><%= categories.count %></p>
</div> </div>
<div class="shadow-border-xs rounded-md bg-white"> <div class="shadow-border-xs rounded-md bg-container">
<div class="overflow-hidden rounded-md"> <div class="overflow-hidden rounded-md">
<% Category::Group.for(categories).each_with_index do |group, idx| %> <% Category::Group.for(categories).each_with_index do |group, idx| %>
<%= render group.category %> <%= render group.category %>

View file

@ -11,7 +11,7 @@
<%= icon("pen", size: "sm") %> <%= icon("pen", size: "sm") %>
</summary> </summary>
<div class=" absolute z-50 bg-white p-4 border border-alpha-black-25 rounded-2xl shadow-xs h-fit left-66 top-24"> <div class=" absolute z-50 bg-container p-4 border border-alpha-black-25 rounded-2xl shadow-xs h-fit left-66 top-24">
<div class="flex gap-2 flex-col mb-4" data-category-target="selection" style="<%= "display:none;" if @category.subcategory? %>"> <div class="flex gap-2 flex-col mb-4" data-category-target="selection" style="<%= "display:none;" if @category.subcategory? %>">
<div data-category-target="pickerSection"></div> <div data-category-target="pickerSection"></div>
<h4 class="text-gray-500 text-sm">Color</h4> <h4 class="text-gray-500 text-sm">Color</h4>

View file

@ -5,7 +5,7 @@
<%= render partial: "categories/badge", locals: { category: transaction.category } %> <%= render partial: "categories/badge", locals: { category: transaction.category } %>
</button> </button>
<div data-menu-target="content" class="absolute z-10 hidden w-screen mt-2 max-w-min cursor-default"> <div data-menu-target="content" class="absolute z-10 hidden w-screen mt-2 max-w-min cursor-default">
<div class="w-80 text-sm font-semibold leading-6 text-primary bg-white shadow-lg shrink rounded-xl ring-1 ring-gray-900/5"> <div class="w-80 text-sm font-semibold leading-6 text-primary bg-container shadow-lg shrink rounded-xl ring-1 ring-gray-900/5">
<%= turbo_frame_tag "category_dropdown", src: category_dropdown_path(category_id: transaction.category_id, transaction_id: transaction.id), loading: :lazy do %> <%= turbo_frame_tag "category_dropdown", src: category_dropdown_path(category_id: transaction.category_id, transaction_id: transaction.id), loading: :lazy do %>
<div class="p-6 flex items-center justify-center"> <div class="p-6 flex items-center justify-center">
<p class="text-sm text-secondary animate-pulse"><%= t(".loading") %></p> <p class="text-sm text-secondary animate-pulse"><%= t(".loading") %></p>

View file

@ -1,3 +1,3 @@
<div class="bg-white"> <div class="bg-container">
<div class="h-px bg-alpha-black-50 ml-4 mr-6"></div> <div class="h-px bg-alpha-black-50 ml-4 mr-6"></div>
</div> </div>

View file

@ -8,7 +8,7 @@
<% end %> <% end %>
</header> </header>
<div class="bg-white shadow-border-xs rounded-xl p-4"> <div class="bg-container shadow-border-xs rounded-xl p-4">
<% if @categories.any? %> <% if @categories.any? %>
<div class="space-y-4"> <div class="space-y-4">
<% if @categories.incomes.any? %> <% if @categories.incomes.any? %>

View file

@ -4,7 +4,7 @@
data: { data: {
turbo: false, turbo: false,
controller: "deletion", controller: "deletion",
deletion_dangerous_action_class: "form-field__submit bg-white text-red-600 border hover:bg-red-50", deletion_dangerous_action_class: "form-field__submit bg-container text-red-600 border hover:bg-red-50",
deletion_safe_action_class: "form-field__submit border border-transparent", 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_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| %> deletion_submit_text_when_replacing_value: t(".delete_and_recategorize", category_name: @category.name) } do |f| %>
@ -15,7 +15,7 @@
{ data: { deletion_target: "replacementField", action: "deletion#updateSubmitButton" } } %> { data: { deletion_target: "replacementField", action: "deletion#updateSubmitButton" } } %>
<%= f.submit t(".delete_and_leave_uncategorized", category_name: @category.name), <%= 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-container text-red-600 border hover:bg-red-50",
data: { deletion_target: "submitButton" } %> data: { deletion_target: "submitButton" } %>
<% end %> <% end %>
<% end %> <% end %>

View file

@ -25,7 +25,7 @@
<% end %> <% end %>
<%= contextual_menu do %> <%= contextual_menu do %>
<div class="w-48 p-1 text-sm leading-6 text-primary bg-white shadow-lg shrink rounded-xl ring-1 ring-gray-900/5"> <div class="w-48 p-1 text-sm leading-6 text-primary bg-container shadow-lg shrink rounded-xl ring-1 ring-gray-900/5">
<%= link_to edit_category_path(category), <%= link_to edit_category_path(category),
class: "block w-full py-2 px-3 space-x-2 text-primary hover:bg-gray-50 flex items-center rounded-lg", class: "block w-full py-2 px-3 space-x-2 text-primary hover:bg-gray-50 flex items-center rounded-lg",
data: { turbo_frame: :modal } do %> data: { turbo_frame: :modal } do %>

View file

@ -1,7 +1,7 @@
<%= turbo_frame_tag "category_dropdown" do %> <%= turbo_frame_tag "category_dropdown" do %>
<div class="flex flex-col relative" data-controller="list-filter"> <div class="flex flex-col relative" data-controller="list-filter">
<div class="grow p-1.5"> <div class="grow p-1.5">
<div class="relative flex items-center bg-white border border-gray-200 rounded-lg"> <div class="relative flex items-center bg-container border border-gray-200 rounded-lg">
<input placeholder="<%= t(".search_placeholder") %>" autocomplete="nope" type="search" class="placeholder:text-sm placeholder:text-secondary font-normal h-10 relative pl-10 w-full border-none rounded-lg" data-list-filter-target="input" data-action="list-filter#filter"> <input placeholder="<%= t(".search_placeholder") %>" autocomplete="nope" type="search" class="placeholder:text-sm placeholder:text-secondary font-normal h-10 relative pl-10 w-full border-none rounded-lg" data-list-filter-target="input" data-action="list-filter#filter">
<%= lucide_icon("search", class: "w-5 h-5 text-secondary ml-2 absolute inset-0 transform top-1/2 -translate-y-1/2") %> <%= lucide_icon("search", class: "w-5 h-5 text-secondary ml-2 absolute inset-0 transform top-1/2 -translate-y-1/2") %>
</div> </div>

View file

@ -1,17 +1,17 @@
<div class="flex flex-col items-center justify-start h-full p-6 text-center"> <div class="flex flex-col items-center justify-start h-full p-6 text-center">
<div class="border border-gray-200 rounded-lg p-4 bg-white"> <div class="border border-gray-200 rounded-lg p-4 bg-container">
<div class="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4"> <div class="w-16 h-16 bg-surface-inset rounded-full flex items-center justify-center mx-auto mb-4">
<%= icon("sparkles") %> <%= icon("sparkles") %>
</div> </div>
<h3 class="text-lg font-medium text-gray-900 mb-2">Enable Personal Finance AI</h3> <h3 class="text-lg font-medium text-primary mb-2">Enable Personal Finance AI</h3>
<p class="text-gray-600 mb-6 text-sm"> <p class="text-gray-600 mb-6 text-sm">
<% if Current.user.ai_available? %> <% if Current.user.ai_available? %>
Our personal finance AI can help answer questions about your finances and provide insights based on your data. Our personal finance AI can help answer questions about your finances and provide insights based on your data.
To use this feature, you'll need to explicitly enable it. To use this feature, you'll need to explicitly enable it.
<% else %> <% else %>
To use the AI assistant, you need to set the <code class="bg-gray-100 px-1 py-0.5 rounded font-mono text-xs">OPENAI_ACCESS_TOKEN</code> To use the AI assistant, you need to set the <code class="bg-surface-inset px-1 py-0.5 rounded font-mono text-xs">OPENAI_ACCESS_TOKEN</code>
environment variable in your self-hosted instance. environment variable in your self-hosted instance.
<% end %> <% end %>
</p> </p>

View file

@ -5,7 +5,7 @@
<p>Hey <%= Current.user&.first_name || "there" %>! I'm an AI built by Maybe to help with your finances. I have access to the web and your account data.</p> <p>Hey <%= Current.user&.first_name || "there" %>! I'm an AI built by Maybe to help with your finances. I have access to the web and your account data.</p>
<p> <p>
You can use <span class="bg-white border border-gray-200 px-1.5 py-0.5 rounded font-mono text-xs">/</span> to access commands You can use <span class="bg-container border border-gray-200 px-1.5 py-0.5 rounded font-mono text-xs">/</span> to access commands
</p> </p>
<div class="space-y-3"> <div class="space-y-3">

View file

@ -1,5 +1,5 @@
<%= turbo_frame_tag dom_id(@chat, :title), class: "block" do %> <%= turbo_frame_tag dom_id(@chat, :title), class: "block" do %>
<% bg_class = params[:ctx] == "chat" ? "bg-white" : "bg-container-inset" %> <% bg_class = params[:ctx] == "chat" ? "bg-container" : "bg-container-inset" %>
<%= styled_form_with model: @chat, <%= styled_form_with model: @chat,
class: class_names("p-1 rounded-md font-medium text-primary w-full", bg_class), class: class_names("p-1 rounded-md font-medium text-primary w-full", bg_class),
data: { controller: "auto-submit-form", auto_submit_form_trigger_event_value: "blur" } do |f| %> data: { controller: "auto-submit-form", auto_submit_form_trigger_event_value: "blur" } do |f| %>

View file

@ -15,11 +15,11 @@
<%= render @chats %> <%= render @chats %>
</div> </div>
<% else %> <% else %>
<div class="text-center py-12 bg-white rounded-lg border border-gray-200"> <div class="text-center py-12 bg-container rounded-lg border border-gray-200">
<div class="w-16 h-16 bg-gray-100 rounded-full flex items-center justify-center mx-auto mb-4"> <div class="w-16 h-16 bg-surface-inset rounded-full flex items-center justify-center mx-auto mb-4">
<%= icon("message-square", size: "lg") %> <%= icon("message-square", size: "lg") %>
</div> </div>
<h3 class="text-lg font-medium text-gray-900 mb-1">No chats yet</h3> <h3 class="text-lg font-medium text-primary mb-1">No chats yet</h3>
<p class="text-gray-500 mb-4">Start a new conversation with the AI assistant</p> <p class="text-gray-500 mb-4">Start a new conversation with the AI assistant</p>
<%= link_to "Start a chat", new_chat_path, class: "inline-flex items-center gap-2 py-2 px-4 bg-gray-800 text-white rounded-lg text-sm font-medium" %> <%= link_to "Start a chat", new_chat_path, class: "inline-flex items-center gap-2 py-2 px-4 bg-gray-800 text-white rounded-lg text-sm font-medium" %>
</div> </div>

View file

@ -11,7 +11,7 @@
</div> </div>
<% if @import.cleaned? %> <% if @import.cleaned? %>
<div class="bg-white border border-tertiary rounded-lg p-3 flex items-center justify-between"> <div class="bg-container border border-tertiary rounded-lg p-3 flex items-center justify-between">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<%= lucide_icon "check-circle", class: "w-4 h-4 text-green-500" %> <%= lucide_icon "check-circle", class: "w-4 h-4 text-green-500" %>
<p class="text-green-500">Your data has been cleaned</p> <p class="text-green-500">Your data has been cleaned</p>
@ -20,7 +20,7 @@
<%= link_to "Next step", import_confirm_path(@import), class: "btn btn--primary" %> <%= link_to "Next step", import_confirm_path(@import), class: "btn btn--primary" %>
</div> </div>
<% else %> <% else %>
<div class="bg-white border border-tertiary rounded-lg p-3 flex items-center justify-between"> <div class="bg-container border border-tertiary rounded-lg p-3 flex items-center justify-between">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<%= lucide_icon "alert-triangle", class: "w-4 h-4 text-red-500" %> <%= lucide_icon "alert-triangle", class: "w-4 h-4 text-red-500" %>
<p class="text-red-500 text-sm"><%= t(".errors_notice") %></p> <p class="text-red-500 text-sm"><%= t(".errors_notice") %></p>
@ -28,22 +28,22 @@
<div class="flex justify-center"> <div class="flex justify-center">
<div class="bg-gray-50 rounded-lg inline-flex p-1 space-x-2 text-sm text-primary font-medium"> <div class="bg-gray-50 rounded-lg inline-flex p-1 space-x-2 text-sm text-primary font-medium">
<%= link_to "All rows", import_clean_path(@import, per_page: params[:per_page], view: "all"), class: "p-2 rounded-lg #{params[:view] != 'errors' ? 'bg-white' : ''}" %> <%= link_to "All rows", import_clean_path(@import, per_page: params[:per_page], view: "all"), class: "p-2 rounded-lg #{params[:view] != 'errors' ? 'bg-container' : ''}" %>
<%= link_to "Error rows", import_clean_path(@import, per_page: params[:per_page], view: "errors"), class: "p-2 rounded-lg #{params[:view] == 'errors' ? 'bg-white' : ''}" %> <%= link_to "Error rows", import_clean_path(@import, per_page: params[:per_page], view: "errors"), class: "p-2 rounded-lg #{params[:view] == 'errors' ? 'bg-container' : ''}" %>
</div> </div>
</div> </div>
</div> </div>
<% end %> <% end %>
<div class="pb-12"> <div class="pb-12">
<div class="bg-gray-25 rounded-xl p-1 mb-6"> <div class="bg-container-inset rounded-xl p-1 mb-6">
<div style="grid-template-columns: repeat(<%= @import.column_keys.count %>, 1fr)" class="grid items-center uppercase text-xs font-medium text-secondary py-3"> <div style="grid-template-columns: repeat(<%= @import.column_keys.count %>, 1fr)" class="grid items-center uppercase text-xs font-medium text-secondary py-3">
<% @import.column_keys.each do |key| %> <% @import.column_keys.each do |key| %>
<div class="px-5"><%= import_col_label(key) %></div> <div class="px-5"><%= import_col_label(key) %></div>
<% end %> <% end %>
</div> </div>
<div class="bg-white shadow-border-xs rounded-xl divide-y divide-alpha-black-200"> <div class="bg-container shadow-border-xs rounded-xl divide-y divide-alpha-black-200">
<% @rows.each do |row| %> <% @rows.each do |row| %>
<%= render "import/rows/form", row: row %> <%= render "import/rows/form", row: row %>
<% end %> <% end %>
@ -52,7 +52,7 @@
</div> </div>
<div class="fixed bottom-0 left-1/2 -translate-x-1/2 w-full p-12"> <div class="fixed bottom-0 left-1/2 -translate-x-1/2 w-full p-12">
<div class="shadow-border-xs rounded-lg p-3 max-w-2xl mx-auto bg-white"> <div class="shadow-border-xs rounded-lg p-3 max-w-2xl mx-auto bg-container">
<%= render "shared/pagination", pagy: @pagy %> <%= render "shared/pagination", pagy: @pagy %>
</div> </div>
</div> </div>

View file

@ -20,7 +20,7 @@
<% end %> <% end %>
<div class="space-y-4"> <div class="space-y-4">
<div class="bg-gray-25 rounded-xl p-1 space-y-1 w-[650px]"> <div class="bg-container-inset rounded-xl p-1 space-y-1 w-[650px]">
<div class="grid grid-cols-3 gap-2 text-xs font-medium text-secondary uppercase px-5 py-3"> <div class="grid grid-cols-3 gap-2 text-xs font-medium text-secondary uppercase px-5 py-3">
<p><%= t(".csv_mapping_label", mapping: mapping_label(mapping_class)) %></p> <p><%= t(".csv_mapping_label", mapping: mapping_label(mapping_class)) %></p>
<p><%= t(".maybe_mapping_label", mapping: mapping_label(mapping_class)) %></p> <p><%= t(".maybe_mapping_label", mapping: mapping_label(mapping_class)) %></p>
@ -29,7 +29,7 @@
<div class="shadow-border-xs rounded-md divide-y divide-alpha-black-100 text-sm"> <div class="shadow-border-xs rounded-md divide-y divide-alpha-black-100 text-sm">
<% mappings.sort_by(&:key).each do |mapping| %> <% mappings.sort_by(&:key).each do |mapping| %>
<div class="px-5 py-3 bg-white first:rounded-tl-xl first:rounded-tr-xl last:rounded-bl-xl last:rounded-br-xl"> <div class="px-5 py-3 bg-container first:rounded-tl-xl first:rounded-tr-xl last:rounded-bl-xl last:rounded-br-xl">
<%= render partial: "import/mappings/form", locals: { mapping: mapping } %> <%= render partial: "import/mappings/form", locals: { mapping: mapping } %>
</div> </div>
<% end %> <% end %>

View file

@ -11,9 +11,9 @@
<p class="text-secondary text-sm"><%= t(".description") %></p> <p class="text-secondary text-sm"><%= t(".description") %></p>
</div> </div>
<div data-controller="tabs" data-tabs-active-class="bg-white" data-tabs-default-tab-value="csv-paste-tab"> <div data-controller="tabs" data-tabs-active-class="bg-container" data-tabs-default-tab-value="csv-paste-tab">
<div class="flex justify-center mb-4"> <div class="flex justify-center mb-4">
<div class="bg-gray-50 rounded-lg inline-flex p-1 space-x-2 text-sm text-primary font-medium"> <div class="tab-item-active rounded-lg inline-flex p-1 space-x-2 text-sm text-primary font-medium">
<button type="button" data-id="csv-paste-tab" class="p-2 rounded-lg" data-tabs-target="btn" data-action="click->tabs#select">Copy & Paste</button> <button type="button" data-id="csv-paste-tab" class="p-2 rounded-lg" data-tabs-target="btn" data-action="click->tabs#select">Copy & Paste</button>
<button type="button" data-id="csv-upload-tab" class="p-2 rounded-lg" data-tabs-target="btn" data-action="click->tabs#select">Upload CSV</button> <button type="button" data-id="csv-upload-tab" class="p-2 rounded-lg" data-tabs-target="btn" data-action="click->tabs#select">Upload CSV</button>
</div> </div>
@ -35,7 +35,7 @@
placeholder: "Paste your CSV file contents here", placeholder: "Paste your CSV file contents here",
"data-auto-submit-form-target": "auto" %> "data-auto-submit-form-target": "auto" %>
<% else %> <% else %>
<label for="import_csv_file" class="flex flex-col items-center justify-center w-full h-56 border-2 border-gray-300 border-dashed rounded-lg cursor-pointer bg-gray-50"> <label for="import_csv_file" class="flex flex-col items-center justify-center w-full h-56 border-2 border-secondary border-dashed rounded-lg cursor-pointer bg-container-inset">
<div class="flex flex-col items-center justify-center pt-5 pb-6"> <div class="flex flex-col items-center justify-center pt-5 pb-6">
<%= form.file_field :csv_file, class: "ml-32", "data-auto-submit-form-target": "auto" %> <%= form.file_field :csv_file, class: "ml-32", "data-auto-submit-form-target": "auto" %>
</div> </div>

View file

@ -37,7 +37,7 @@
</div> </div>
<%= contextual_menu do %> <%= contextual_menu do %>
<div class="w-48 p-1 text-sm leading-6 text-primary bg-white shadow-lg shrink rounded-xl ring-1 ring-gray-900/5"> <div class="w-48 p-1 text-sm leading-6 text-primary bg-container shadow-lg shrink rounded-xl ring-1 ring-gray-900/5">
<%= link_to import_path(import), <%= link_to import_path(import),
class: "block w-full py-2 px-3 space-x-2 text-primary hover:bg-gray-50 flex items-center rounded-lg" do %> class: "block w-full py-2 px-3 space-x-2 text-primary hover:bg-gray-50 flex items-center rounded-lg" do %>
<%= lucide_icon "eye", class: "w-5 h-5 text-secondary" %> <%= lucide_icon "eye", class: "w-5 h-5 text-secondary" %>

View file

@ -19,9 +19,9 @@
step[:is_complete] ? "text-green-600" : "text-secondary" step[:is_complete] ? "text-green-600" : "text-secondary"
end %> end %>
<% step_class = if is_current <% step_class = if is_current
"bg-gray-900 text-white" "bg-primary text-white"
else else
step[:is_complete] ? "bg-green-600/10 border-alpha-black-25" : "bg-gray-50" step[:is_complete] ? "bg-green-600/10 border-alpha-black-25" : "bg-container-inset"
end %> end %>
<%= link_to step[:path], class: "flex items-center gap-3" do %> <%= link_to step[:path], class: "flex items-center gap-3" do %>

View file

@ -6,17 +6,17 @@
</div> </div>
<div class="mx-auto max-w-2xl space-y-4"> <div class="mx-auto max-w-2xl space-y-4">
<div class="bg-gray-25 rounded-xl p-1 space-y-1"> <div class="bg-container-inset rounded-xl p-1 space-y-1">
<div class="flex justify-between items-center text-xs font-medium text-secondary uppercase px-5 py-3"> <div class="flex justify-between items-center text-xs font-medium text-secondary uppercase px-5 py-3">
<p>item</p> <p>item</p>
<p class="justify-self-end">count</p> <p class="justify-self-end">count</p>
</div> </div>
<div class="bg-white shadow-border-xs rounded-lg text-sm"> <div class="bg-container shadow-border-xs rounded-lg text-sm">
<% import.dry_run.each do |key, count| %> <% import.dry_run.each do |key, count| %>
<% resource = dry_run_resource(key) %> <% resource = dry_run_resource(key) %>
<div class="flex items-center justify-between gap-2 bg-white px-5 py-3 rounded-tl-lg rounded-tr-lg"> <div class="flex items-center justify-between gap-2 bg-container px-5 py-3 rounded-tl-lg rounded-tr-lg">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="<%= resource.bg_class %> w-8 h-8 rounded-full flex justify-center items-center"> <div class="<%= resource.bg_class %> w-8 h-8 rounded-full flex justify-center items-center">
<%= lucide_icon resource.icon, class: "#{resource.text_class} w-5 h-5 shrink-0" %> <%= lucide_icon resource.icon, class: "#{resource.text_class} w-5 h-5 shrink-0" %>

View file

@ -1,10 +1,10 @@
<%# locals: (headers: [], rows: [], caption: nil) %> <%# locals: (headers: [], rows: [], caption: nil) %>
<div class="overflow-x-auto"> <div class="overflow-x-auto">
<div class="border border-secondary rounded-md shadow-border-xs text-sm bg-white w-full"> <div class="border border-secondary rounded-md shadow-border-xs text-sm bg-container w-full">
<div class="grid border-b border-b-alpha-black-200" style="grid-template-columns: repeat(<%= headers.length %>, minmax(0, 1fr))"> <div class="grid border-b border-b-alpha-black-200" style="grid-template-columns: repeat(<%= headers.length %>, minmax(0, 1fr))">
<% headers.each_with_index do |header, index| %> <% headers.each_with_index do |header, index| %>
<div class=" <div class="
bg-gray-25 px-3 py-2.5 font-medium whitespace-nowrap overflow-x-auto bg-container-inset px-3 py-2.5 font-medium whitespace-nowrap overflow-x-auto
first:rounded-tl-md last:rounded-tr-md first:rounded-tl-md last:rounded-tr-md
<%= "border-r border-r-alpha-black-200" unless index == headers.length - 1 %> <%= "border-r border-r-alpha-black-200" unless index == headers.length - 1 %>
"> ">
@ -29,7 +29,7 @@
<% end %> <% end %>
<% if caption %> <% if caption %>
<div class="px-3 py-2.5 text-center text-xs text-primary rounded-b-md italic bg-gray-25 overflow-x-auto"> <div class="px-3 py-2.5 text-center text-xs text-primary rounded-b-md italic bg-container-inset overflow-x-auto">
<%= caption %> <%= caption %>
</div> </div>
<% end %> <% end %>

View file

@ -7,14 +7,14 @@
<% end %> <% end %>
</div> </div>
<div class="bg-white shadow-border-xs rounded-xl p-4"> <div class="bg-container shadow-border-xs rounded-xl p-4">
<% if @imports.empty? %> <% if @imports.empty? %>
<%= render partial: "imports/empty" %> <%= render partial: "imports/empty" %>
<% else %> <% else %>
<div class="rounded-xl bg-gray-25 p-1"> <div class="rounded-xl bg-container-inset p-1">
<h2 class="uppercase px-4 py-2 text-secondary text-xs"><%= t(".imports") %> · <%= @imports.size %></h2> <h2 class="uppercase px-4 py-2 text-secondary text-xs"><%= t(".imports") %> · <%= @imports.size %></h2>
<div class="border border-alpha-black-100 rounded-lg bg-white shadow-xs"> <div class="border border-alpha-black-100 rounded-lg bg-container shadow-xs">
<%= render partial: "imports/import", collection: @imports.ordered %> <%= render partial: "imports/import", collection: @imports.ordered %>
</div> </div>
</div> </div>

View file

@ -11,9 +11,9 @@
<p class="text-secondary text-sm"><%= t(".description") %></p> <p class="text-secondary text-sm"><%= t(".description") %></p>
</div> </div>
<div class="rounded-xl bg-gray-25 p-1"> <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> <h3 class="uppercase text-secondary text-xs font-medium px-3 py-1.5"><%= t(".sources") %></h3>
<ul class="bg-white shadow-border-xs rounded-lg"> <ul class="bg-container shadow-border-xs rounded-lg">
<li> <li>
<% if @pending_import.present? && (params[:type].nil? || params[:type] == @pending_import.type) %> <% 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 %> <%= link_to import_path(@pending_import), class: "flex items-center justify-between p-4 group cursor-pointer", data: { turbo: false } do %>

View file

@ -3,7 +3,7 @@
<% if @invite_codes.present? %> <% if @invite_codes.present? %>
<%= render @invite_codes %> <%= render @invite_codes %>
<% else %> <% else %>
<div class="flex flex-col items-center w-full h-64 bg-white text-center justify-center"> <div class="flex flex-col items-center w-full h-64 bg-container text-center justify-center">
<%= lucide_icon "binary", class: "w-6 h-6 text-sm text-secondary" %> <%= lucide_icon "binary", class: "w-6 h-6 text-sm text-secondary" %>
<p class="text-base pt-4"><%= t(".no_invite_codes") %></p> <p class="text-base pt-4"><%= t(".no_invite_codes") %></p>
<p class="text-sm text-secondary pt-2 w-2/3"><%= t(".invite_code_description") %></p> <p class="text-sm text-secondary pt-2 w-2/3"><%= t(".invite_code_description") %></p>

View file

@ -1,7 +1,7 @@
<%= render "layouts/shared/htmldoc" do %> <%= render "layouts/shared/htmldoc" do %>
<% sidebar_config = app_sidebar_config(Current.user) %> <% sidebar_config = app_sidebar_config(Current.user) %>
<div class="flex flex-col lg:flex-row h-dvh lg:h-full bg-gray-50" <div class="flex flex-col lg:flex-row h-dvh lg:h-full bg-surface"
data-controller="sidebar" data-controller="sidebar"
data-sidebar-user-id-value="<%= Current.user.id %>" data-sidebar-user-id-value="<%= Current.user.id %>"
data-sidebar-config-value="<%= sidebar_config.to_json %>"> data-sidebar-config-value="<%= sidebar_config.to_json %>">
@ -12,7 +12,7 @@
</button> </button>
<%# Mobile only account sidebar groups %> <%# Mobile only account sidebar groups %>
<%= tag.div class: class_names("hidden bg-gray-50 z-20 absolute inset-0 h-dvh w-full p-4 overflow-y-auto transition-all duration-300"), <%= tag.div class: class_names("hidden bg-surface z-20 absolute inset-0 h-dvh w-full p-4 overflow-y-auto transition-all duration-300"),
data: { sidebar_target: "leftPanelMobile" } do %> data: { sidebar_target: "leftPanelMobile" } do %>
<div id="account-sidebar-tabs"> <div id="account-sidebar-tabs">
<div class="mb-4"> <div class="mb-4">
@ -108,7 +108,7 @@
<% end %> <% end %>
</div> </div>
<nav class="lg:hidden bg-white shrink-0 z-10 pb-2"> <nav class="lg:hidden bg-container shrink-0 z-10 pb-2">
<ul class="flex items-center justify-around gap-1"> <ul class="flex items-center justify-around gap-1">
<li> <li>
<%= render "layouts/sidebar/nav_item", name: "Home", path: root_path, icon_key: "pie-chart" %> <%= render "layouts/sidebar/nav_item", name: "Home", path: root_path, icon_key: "pie-chart" %>

View file

@ -1,6 +1,6 @@
<%= render "layouts/shared/htmldoc" do %> <%= render "layouts/shared/htmldoc" do %>
<div class="flex flex-col h-dvh"> <div class="flex flex-col h-dvh">
<div class="flex flex-col h-screen px-6 py-12 bg-gray-25"> <div class="flex flex-col h-screen px-6 py-12 bg-surface">
<div class="grow flex flex-col justify-center"> <div class="grow flex flex-col justify-center">
<div class="sm:mx-auto sm:w-full sm:max-w-md"> <div class="sm:mx-auto sm:w-full sm:max-w-md">
<div class="flex justify-center mb-6"> <div class="flex justify-center mb-6">

View file

@ -1,5 +1,5 @@
<%= render "layouts/shared/htmldoc" do %> <%= render "layouts/shared/htmldoc" do %>
<div class="flex flex-col h-dvh"> <div class="flex flex-col h-dvh bg-surface">
<header class="flex items-center justify-between p-8"> <header class="flex items-center justify-between p-8">
<%= link_to content_for(:previous_path) || imports_path do %> <%= link_to content_for(:previous_path) || imports_path do %>
<%= lucide_icon "arrow-left", class: "w-5 h-5 text-secondary" %> <%= lucide_icon "arrow-left", class: "w-5 h-5 text-secondary" %>

View file

@ -1,5 +1,5 @@
<%= render "layouts/shared/htmldoc" do %> <%= render "layouts/shared/htmldoc" do %>
<div class="flex h-full bg-gray-25"> <div class="flex h-full bg-surface">
<div class="p-4 w-96 shrink-0 h-full overflow-y-auto"> <div class="p-4 w-96 shrink-0 h-full overflow-y-auto">
<%= render "settings/settings_nav" %> <%= render "settings/settings_nav" %>
</div> </div>

View file

@ -2,7 +2,7 @@
<nav class="flex items-center gap-2 mb-6"> <nav class="flex items-center gap-2 mb-6">
<% if sidebar_toggle_enabled %> <% if sidebar_toggle_enabled %>
<button data-action="sidebar#toggleLeftPanel" class="hidden p-2 lg:inline-flex rounded-lg items-center justify-center hover:bg-gray-100 cursor-pointer"> <button data-action="sidebar#toggleLeftPanel" class="hidden p-2 lg:inline-flex rounded-lg items-center justify-center hover:bg-container-inset cursor-pointer">
<%= icon("panel-left", color: "gray") %> <%= icon("panel-left", color: "gray") %>
</button> </button>
<% end %> <% end %>
@ -16,7 +16,7 @@
<% if path.present? && index < breadcrumbs.size - 1 %> <% if path.present? && index < breadcrumbs.size - 1 %>
<%= link_to name, path, class: "text-sm text-gray-500 font-medium" %> <%= link_to name, path, class: "text-sm text-gray-500 font-medium" %>
<% elsif index == breadcrumbs.size - 1 %> <% elsif index == breadcrumbs.size - 1 %>
<span class="text-gray-900 font-medium text-sm"><%= name %></span> <span class="text-primary font-medium text-sm"><%= name %></span>
<% else %> <% else %>
<span class="text-sm text-gray-500 font-medium"><%= name %></span> <span class="text-sm text-gray-500 font-medium"><%= name %></span>
<% end %> <% end %>
@ -25,7 +25,7 @@
<% if sidebar_toggle_enabled %> <% if sidebar_toggle_enabled %>
<div class="ml-auto"> <div class="ml-auto">
<button data-action="sidebar#toggleRightPanel" class="p-2 hidden lg:inline-flex rounded-lg items-center justify-center hover:bg-gray-100 cursor-pointer" title="Toggle AI Assistant"> <button data-action="sidebar#toggleRightPanel" class="p-2 hidden lg:inline-flex rounded-lg items-center justify-center hover:bg-container-inset cursor-pointer" title="Toggle AI Assistant">
<%= icon("panel-right", color: "gray") %> <%= icon("panel-right", color: "gray") %>
</button> </button>
</div> </div>

View file

@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<html class="h-full text-primary overflow-hidden lg:overflow-auto font-sans <%= @os %>" lang="en"> <html class="h-full text-primary overflow-hidden lg:overflow-auto font-sans <%= @os %>" lang="en" data-controller="theme" data-theme-user-preference-value="<%= Current.user&.theme || 'system' %>">
<head> <head>
<%= render "layouts/shared/head" %> <%= render "layouts/shared/head" %>
<%= yield :head %> <%= yield :head %>

View file

@ -2,7 +2,7 @@
<header class="space-y-6"> <header class="space-y-6">
<% if local_assigns[:title].present? %> <% if local_assigns[:title].present? %>
<div class="space-y-1"> <div class="space-y-1">
<h1 class="text-3xl font-medium text-gray-900"><%= title %></h1> <h1 class="text-3xl font-medium text-primary"><%= title %></h1>
<% if local_assigns[:subtitle].present? %> <% if local_assigns[:subtitle].present? %>
<p class="text-gray-500"><%= subtitle %></p> <p class="text-gray-500"><%= subtitle %></p>
<% end %> <% end %>

View file

@ -2,9 +2,9 @@
<%= link_to path, class: "space-y-1 lg:py-1 group block" do %> <%= link_to path, class: "space-y-1 lg:py-1 group block" do %>
<div class="grow flex flex-col lg:flex-row gap-1 items-center"> <div class="grow flex flex-col lg:flex-row gap-1 items-center">
<%= tag.div class: class_names("w-4 h-1 lg:w-1 lg:h-4 rounded-bl-sm rounded-br-sm lg:rounded-tr-sm lg:rounded-br-sm lg:rounded-bl-none", "bg-gray-900" => page_active?(path)) %> <%= tag.div class: class_names("w-4 h-1 lg:w-1 lg:h-4 rounded-bl-sm rounded-br-sm lg:rounded-tr-sm lg:rounded-br-sm lg:rounded-bl-none", "bg-container-inset" => page_active?(path)) %>
<%= tag.div class: class_names("w-8 h-8 flex items-center justify-center mx-auto rounded-lg", page_active?(path) ? "bg-white shadow-xs text-primary" : "group-hover:bg-gray-100 text-secondary") do %> <%= tag.div class: class_names("w-8 h-8 flex items-center justify-center mx-auto rounded-lg", page_active?(path) ? "bg-container-inset shadow-xs text-primary" : "group-hover:bg-container-inset-hover text-secondary") do %>
<%= icon(icon_key) %> <%= icon(icon_key) %>
<% end %> <% end %>
</div> </div>

View file

@ -1,6 +1,6 @@
<%# locals: (merchant:) %> <%# locals: (merchant:) %>
<div class="flex justify-between items-center p-4 bg-white"> <div class="flex justify-between items-center p-4 bg-container">
<div class="flex w-full items-center gap-2.5"> <div class="flex w-full items-center gap-2.5">
<% if merchant.icon_url %> <% if merchant.icon_url %>
<div class="w-8 h-8 rounded-full flex justify-center items-center"> <div class="w-8 h-8 rounded-full flex justify-center items-center">
@ -16,7 +16,7 @@
</div> </div>
<div class="justify-self-end"> <div class="justify-self-end">
<%= contextual_menu do %> <%= contextual_menu do %>
<div class="w-48 p-1 text-sm leading-6 text-primary bg-white shadow-lg shrink rounded-xl ring-1 ring-gray-900/5"> <div class="w-48 p-1 text-sm leading-6 text-primary bg-container shadow-lg shrink rounded-xl ring-1 ring-gray-900/5">
<%= contextual_menu_modal_action_item t(".edit"), edit_merchant_path(merchant) %> <%= contextual_menu_modal_action_item t(".edit"), edit_merchant_path(merchant) %>
<%= contextual_menu_destructive_item t(".delete"), <%= contextual_menu_destructive_item t(".delete"),

View file

@ -1,3 +1,3 @@
<div class="bg-white"> <div class="bg-container">
<div class="h-px bg-alpha-black-50 ml-14 mr-6"></div> <div class="h-px bg-alpha-black-50 ml-14 mr-6"></div>
</div> </div>

View file

@ -7,16 +7,16 @@
<% end %> <% end %>
</header> </header>
<div class="bg-white shadow-border-xs rounded-xl p-4"> <div class="bg-container shadow-border-xs rounded-xl p-4">
<% if @merchants.any? %> <% if @merchants.any? %>
<div class="rounded-xl bg-gray-25 space-y-1 p-1"> <div class="rounded-xl bg-container-inset space-y-1 p-1">
<div class="flex items-center gap-1.5 px-4 py-2 text-xs font-medium text-secondary uppercase"> <div class="flex items-center gap-1.5 px-4 py-2 text-xs font-medium text-secondary uppercase">
<p><%= t(".title") %></p> <p><%= t(".title") %></p>
<span class="text-subdued">&middot;</span> <span class="text-subdued">&middot;</span>
<p><%= @merchants.count %></p> <p><%= @merchants.count %></p>
</div> </div>
<div class="border border-alpha-black-25 rounded-md bg-white shadow-border-xs"> <div class="border border-alpha-black-25 rounded-md bg-container shadow-border-xs">
<div class="overflow-hidden rounded-md"> <div class="overflow-hidden rounded-md">
<%= render partial: @merchants, spacer_template: "merchants/ruler" %> <%= render partial: @merchants, spacer_template: "merchants/ruler" %>
</div> </div>

View file

@ -4,14 +4,14 @@
<% model = chat && chat.persisted? ? [chat, Message.new] : Chat.new %> <% model = chat && chat.persisted? ? [chat, Message.new] : Chat.new %>
<%= form_with model: model, <%= form_with model: model,
class: "flex flex-col gap-2 bg-white px-2 py-1.5 rounded-lg shadow-border-xs", class: "flex flex-col gap-2 bg-container px-2 py-1.5 rounded-lg shadow-border-xs",
data: { chat_target: "form" } do |f| %> data: { chat_target: "form" } do |f| %>
<%# In the future, this will be a dropdown with different AI models %> <%# In the future, this will be a dropdown with different AI models %>
<%= f.hidden_field :ai_model, value: "gpt-4o" %> <%= f.hidden_field :ai_model, value: "gpt-4o" %>
<%= f.text_area :content, placeholder: "Ask anything ...", value: message_hint, <%= f.text_area :content, placeholder: "Ask anything ...", value: message_hint,
class: "w-full border-0 focus:ring-0 text-sm resize-none px-1", class: "w-full border-0 focus:ring-0 text-sm resize-none px-1 bg-transparent",
data: { chat_target: "input", action: "input->chat#autoResize keydown->chat#handleInputKeyDown" }, data: { chat_target: "input", action: "input->chat#autoResize keydown->chat#handleInputKeyDown" },
rows: 1 %> rows: 1 %>

View file

@ -9,7 +9,7 @@
</div> </div>
<div class="p-1 bg-alpha-black-25 mb-2 rounded-lg"> <div class="p-1 bg-alpha-black-25 mb-2 rounded-lg">
<div class="bg-white p-4 rounded-lg flex gap-8 shadow-border-xs"> <div class="bg-container p-4 rounded-lg flex gap-8 shadow-border-xs">
<div class="space-y-2"> <div class="space-y-2">
<%= tag.p t(".example"), class: "text-secondary text-sm" %> <%= tag.p t(".example"), class: "text-secondary text-sm" %>
<%= tag.p format_money(Money.new(2325.25, params[:currency] || @user.family.currency)), class: "text-primary font-medium text-2xl" %> <%= tag.p format_money(Money.new(2325.25, params[:currency] || @user.family.currency)), class: "text-primary font-medium text-2xl" %>

View file

@ -18,8 +18,8 @@
</div> </div>
<div class="flex justify-between items-center gap-4 mb-4"> <div class="flex justify-between items-center gap-4 mb-4">
<%= form.text_field :first_name, placeholder: t(".first_name"), label: t(".first_name"), container_class: "bg-white w-1/2", required: true %> <%= form.text_field :first_name, placeholder: t(".first_name"), label: t(".first_name"), container_class: "bg-container w-1/2", required: true %>
<%= form.text_field :last_name, placeholder: t(".last_name"), label: t(".last_name"), container_class: "bg-white w-1/2", required: true %> <%= form.text_field :last_name, placeholder: t(".last_name"), label: t(".last_name"), container_class: "bg-container w-1/2", required: true %>
</div> </div>
<% unless @invitation %> <% unless @invitation %>
<div class="space-y-4 mb-4"> <div class="space-y-4 mb-4">

View file

@ -1,6 +1,6 @@
<%= content_for :page_title, t(".title") %> <%= content_for :page_title, t(".title") %>
<div class="bg-white shadow-border-xs rounded-xl p-4 grow overflow-y-auto"> <div class="bg-container shadow-border-xs rounded-xl p-4 grow overflow-y-auto">
<div class="flex justify-between gap-4 mb-12 last:mb-0"> <div class="flex justify-between gap-4 mb-12 last:mb-0">
<div class="w-1/3"> <div class="w-1/3">
<div class="px-3 flex items-center gap-3"> <div class="px-3 flex items-center gap-3">

View file

@ -1,12 +1,12 @@
<% content_for :page_header do %> <% content_for :page_header do %>
<div class="space-y-1 mb-6"> <div class="space-y-1 mb-6">
<h1 class="text-3xl font-medium text-gray-900">Welcome back, <%= Current.user.first_name %></h1> <h1 class="text-3xl font-medium text-primary">Welcome back, <%= Current.user.first_name %></h1>
<p class="text-gray-500">Here's what's happening with your finances</p> <p class="text-gray-500">Here's what's happening with your finances</p>
</div> </div>
<% end %> <% end %>
<div class="w-full space-y-6 pb-24"> <div class="w-full space-y-6 pb-24">
<section class="bg-white py-4 rounded-xl shadow-border-xs"> <section class="bg-container py-4 rounded-xl shadow-border-xs">
<%= render partial: "pages/dashboard/net_worth_chart", locals: { series: @balance_sheet.net_worth_series(period: @period), period: @period } %> <%= render partial: "pages/dashboard/net_worth_chart", locals: { series: @balance_sheet.net_worth_series(period: @period), period: @period } %>
</section> </section>

View file

@ -2,7 +2,7 @@
<div class="space-y-4"> <div class="space-y-4">
<% balance_sheet.classification_groups.each do |classification_group| %> <% balance_sheet.classification_groups.each do |classification_group| %>
<div class="bg-white shadow-border-xs rounded-xl space-y-4 p-4"> <div class="bg-container shadow-border-xs rounded-xl space-y-4 p-4">
<h2 class="text-lg font-medium"><%= classification_group.display_name %></h2> <h2 class="text-lg font-medium"><%= classification_group.display_name %></h2>
<% if classification_group.account_groups.any? %> <% if classification_group.account_groups.any? %>
@ -36,10 +36,10 @@
</div> </div>
</div> </div>
<div class="shadow-border-xs rounded-lg bg-white"> <div class="shadow-border-xs rounded-lg bg-container">
<% classification_group.account_groups.each do |account_group| %> <% classification_group.account_groups.each do |account_group| %>
<details class="group rounded-lg open:bg-surface font-medium text-sm"> <details class="group rounded-lg open:bg-surface font-medium text-sm">
<summary class="cursor-pointer p-4 group-open:bg-surface bg-white rounded-lg flex items-center justify-between"> <summary class="cursor-pointer p-4 group-open:bg-surface bg-container rounded-lg flex items-center justify-between">
<div class="flex items-center gap-4"> <div class="flex items-center gap-4">
<%= lucide_icon("chevron-right", class: "group-open:rotate-90 text-secondary w-5 h-5") %> <%= lucide_icon("chevron-right", class: "group-open:rotate-90 text-secondary w-5 h-5") %>

View file

@ -30,7 +30,7 @@
<p class="text-base text-subdued">There <%= @invite_codes_count == 1 ? "is" : "are" %> <span class="text-white"><%= @invite_codes_count %> invite <%= "code".pluralize(@invite_codes_count) %></span> remaining.</p> <p class="text-base text-subdued">There <%= @invite_codes_count == 1 ? "is" : "are" %> <span class="text-white"><%= @invite_codes_count %> invite <%= "code".pluralize(@invite_codes_count) %></span> remaining.</p>
<div class="bg-gray-900 border border-gray-800 p-2 rounded-xl my-4"> <div class="bg-gray-900 border border-gray-800 p-2 rounded-xl my-4">
<p class="text-sm text-subdued mt-1 mb-3 sm:mb-4">Your invite code is <span class="font-mono text-white"><%= @invite_code.token %></span></p> <p class="text-sm text-subdued mt-1 mb-3 sm:mb-4">Your invite code is <span class="font-mono text-white"><%= @invite_code.token %></span></p>
<p><%= link_to "Sign up with this code", new_registration_path(invite: @invite_code.token), class: "block w-full bg-white text-black py-2 px-3 rounded-lg no-underline text-sm sm:text-base hover:bg-gray-200 transition duration-150" %></p> <p><%= link_to "Sign up with this code", new_registration_path(invite: @invite_code.token), class: "block w-full bg-container text-black py-2 px-3 rounded-lg no-underline text-sm sm:text-base hover:bg-gray-200 transition duration-150" %></p>
</div> </div>
<p class="text-sm text-subdued">You may need to refresh the page to get a new invite code if someone else claimed it before you.</p> <p class="text-sm text-subdued">You may need to refresh the page to get a new invite code if someone else claimed it before you.</p>
@ -43,7 +43,7 @@
</p> </p>
<% else %> <% else %>
<p class="text-base text-subdued mb-6 sm:mb-8">Sorry, there are <span class="text-white">no invite codes</span> remaining. Join our <%= link_to "Discord server", "https://link.maybe.co/discord", target: "_blank", class: "text-white hover:text-gray-300" %> to get notified when new invite codes are available.</p> <p class="text-base text-subdued mb-6 sm:mb-8">Sorry, there are <span class="text-white">no invite codes</span> remaining. Join our <%= link_to "Discord server", "https://link.maybe.co/discord", target: "_blank", class: "text-white hover:text-gray-300" %> to get notified when new invite codes are available.</p>
<p><%= link_to "Join Discord server", "https://link.maybe.co/discord", target: "_blank", class: "bg-white text-black px-3 py-2 rounded-md no-underline text-base hover:bg-gray-200 transition duration-150" %></p> <p><%= link_to "Join Discord server", "https://link.maybe.co/discord", target: "_blank", class: "bg-container text-black px-3 py-2 rounded-md no-underline text-base hover:bg-gray-200 transition duration-150" %></p>
<% end %> <% end %>
</div> </div>
</div> </div>

View file

@ -1,6 +1,6 @@
<%= content_for :page_title, "Feedback" %> <%= content_for :page_title, "Feedback" %>
<div class="bg-white shadow-border-xs rounded-xl p-4"> <div class="bg-container shadow-border-xs rounded-xl p-4">
<h2 class="text-lg font-medium text-primary mb-1">Leave feedback</h2> <h2 class="text-lg font-medium text-primary mb-1">Leave feedback</h2>
<p class="text-sm text-secondary mb-4">Let us know if you have any specific feedback. Feel free to include links to videos or screenshots.</p> <p class="text-sm text-secondary mb-4">Let us know if you have any specific feedback. Feel free to include links to videos or screenshots.</p>
<div class="flex gap-2"> <div class="flex gap-2">

View file

@ -1,7 +1,7 @@
<%# locals: (plaid_item:) %> <%# locals: (plaid_item:) %>
<%= tag.div id: dom_id(plaid_item) do %> <%= tag.div id: dom_id(plaid_item) do %>
<details open class="group bg-white p-4 shadow-border-xs rounded-xl"> <details open class="group bg-container p-4 shadow-border-xs rounded-xl">
<summary class="flex items-center justify-between gap-2 focus-visible:outline-hidden"> <summary class="flex items-center justify-between gap-2 focus-visible:outline-hidden">
<div class="flex items-center gap-2"> <div class="flex items-center gap-2">
<%= lucide_icon "chevron-right", class: "group-open:transform group-open:rotate-90 text-secondary w-5" %> <%= lucide_icon "chevron-right", class: "group-open:transform group-open:rotate-90 text-secondary w-5" %>
@ -96,7 +96,7 @@
<% end %> <% end %>
<%= contextual_menu do %> <%= contextual_menu do %>
<div class="w-48 p-1 text-sm leading-6 text-primary bg-white shadow-lg shrink rounded-xl ring-1 ring-gray-900/5"> <div class="w-48 p-1 text-sm leading-6 text-primary bg-container shadow-lg shrink rounded-xl ring-1 ring-gray-900/5">
<%= button_to plaid_item_path(plaid_item), <%= button_to plaid_item_path(plaid_item),
method: :delete, method: :delete,
class: "block w-full py-2 px-3 space-x-2 text-red-600 hover:bg-red-50 flex items-center rounded-lg", class: "block w-full py-2 px-3 space-x-2 text-red-600 hover:bg-red-50 flex items-center rounded-lg",

View file

@ -1,5 +1,5 @@
<%# locals: (title:, subtitle: nil, content:) %> <%# locals: (title:, subtitle: nil, content:) %>
<section class="bg-white shadow-border-xs rounded-xl p-4 space-y-4"> <section class="bg-container shadow-border-xs rounded-xl p-4 space-y-4">
<div> <div>
<h2 class="text-lg font-medium text-primary"><%= title %></h2> <h2 class="text-lg font-medium text-primary"><%= title %></h2>
<% if subtitle.present? %> <% if subtitle.present? %>

View file

@ -2,7 +2,7 @@
<%= link_to path, class: class_names( <%= link_to path, class: class_names(
"flex items-center gap-2 btn btn--ghost", "flex items-center gap-2 btn btn--ghost",
page_active?(path) ? "text-primary bg-white shadow-border-xs" : "text-secondary hover:bg-gray-100 border-transparent" page_active?(path) ? "text-primary bg-container shadow-border-xs" : "text-secondary hover:bg-gray-100 border-transparent"
), aria: { current: ("page" if page_active?(path)) } do %> ), aria: { current: ("page" if page_active?(path)) } do %>
<%= lucide_icon(icon, class: "w-5 h-5") if icon %> <%= lucide_icon(icon, class: "w-5 h-5") if icon %>
<%= name %> <%= name %>

View file

@ -1,5 +1,5 @@
<%# locals: path, direction, title %> <%# locals: path, direction, title %>
<%= link_to path, class: "w-full bg-white hover:bg-gray-50 rounded-xl border border-alpha-black-25 shadow-xs p-4 flex items-center justify-between" do %> <%= link_to path, class: "w-full bg-container hover:bg-container-inset rounded-xl border border-alpha-black-25 shadow-xs p-4 flex items-center justify-between" do %>
<% if direction == 'previous' %> <% if direction == 'previous' %>
<div class="w-5 h-5 text-secondary"> <div class="w-5 h-5 text-secondary">
<%= lucide_icon("arrow-left") %> <%= lucide_icon("arrow-left") %>

View file

@ -2,7 +2,7 @@
<%= settings_section title: t(".subscription_title"), subtitle: t(".subscription_subtitle") do %> <%= settings_section title: t(".subscription_title"), subtitle: t(".subscription_subtitle") do %>
<div class="space-y-4"> <div class="space-y-4">
<div class="p-3 shadow-border-xs bg-white rounded-lg flex justify-between items-center"> <div class="p-3 shadow-border-xs bg-container rounded-lg flex justify-between items-center">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-full bg-gray-25 flex justify-center items-center"> <div class="w-9 h-9 rounded-full bg-gray-25 flex justify-center items-center">
<%= lucide_icon "gem", class: "w-5 h-5 text-secondary" %> <%= lucide_icon "gem", class: "w-5 h-5 text-secondary" %>

View file

@ -47,26 +47,26 @@
<%= settings_section title: t(".theme_title"), subtitle: t(".theme_subtitle") do %> <%= settings_section title: t(".theme_title"), subtitle: t(".theme_subtitle") do %>
<div> <div>
<%= styled_form_with model: @user, class: "flex justify-between items-center" do |form| %> <%= styled_form_with model: @user, class: "flex justify-between items-center", data: { controller: "auto-submit-form" } do |form| %>
<%= form.hidden_field :redirect_to, value: "preferences" %> <%= form.hidden_field :redirect_to, value: "preferences" %>
<div class="text-center"> <div class="text-center">
<%= image_tag("light-mode-preview.png", alt: "Light Theme Preview", class: "h-44 mb-4") %> <%= image_tag("light-mode-preview.png", alt: "Light Theme Preview", class: "h-44 mb-4") %>
<div class="flex justify-center items-center gap-2"> <div class="flex justify-center items-center gap-2">
<%= form.radio_button :theme, t(".theme_light"), checked: true %> <%= form.radio_button :theme, "light", checked: @user.theme == "light", data: { auto_submit_form_target: "auto", action: "theme#updateTheme" } %>
<%= form.label :theme_light, t(".theme_light"), value: "light" %> <%= form.label :theme_light, t(".theme_light"), value: "light" %>
</div> </div>
</div> </div>
<div class="text-center"> <div class="text-center">
<%= image_tag("dark-mode-preview.png", alt: "Dark Theme Preview", class: "h-44 mb-4") %> <%= image_tag("dark-mode-preview.png", alt: "Dark Theme Preview", class: "h-44 mb-4") %>
<div class="flex justify-center items-center gap-2"> <div class="flex justify-center items-center gap-2">
<%= form.radio_button :theme, t(".theme_dark"), disabled: true, class: "cursor-not-allowed" %> <%= form.radio_button :theme, "dark", checked: @user.theme == "dark", data: { auto_submit_form_target: "auto", action: "theme#updateTheme" } %>
<%= form.label :theme_dark, t(".theme_dark"), value: "dark" %> <%= form.label :theme_dark, t(".theme_dark"), value: "dark" %>
</div> </div>
</div> </div>
<div class="text-center"> <div class="text-center">
<%= image_tag("system-mode-preview.png", alt: "System Theme Preview", class: "h-44 mb-4") %> <%= image_tag("system-mode-preview.png", alt: "System Theme Preview", class: "h-44 mb-4") %>
<div class="flex items-center gap-2 justify-center"> <div class="flex items-center gap-2 justify-center">
<%= form.radio_button :theme, t(".theme_system"), disabled: true, class: "cursor-not-allowed" %> <%= form.radio_button :theme, "system", checked: @user.theme == "system", data: { auto_submit_form_target: "auto", action: "theme#updateTheme" } %>
<%= form.label :theme_system, t(".theme_system"), value: "system" %> <%= form.label :theme_system, t(".theme_system"), value: "system" %>
</div> </div>
</div> </div>

View file

@ -33,12 +33,12 @@
"data-auto-submit-form-target": "auto" %> "data-auto-submit-form-target": "auto" %>
<% end %> <% end %>
<% end %> <% end %>
<div class="bg-gray-25 rounded-xl p-1"> <div class="bg-container-inset rounded-xl p-1">
<div class="px-4 py-2"> <div class="px-4 py-2">
<p class="uppercase text-xs text-secondary font-medium"><%= Current.family.name %> &middot; <%= Current.family.users.size %></p> <p class="uppercase text-xs text-secondary font-medium"><%= Current.family.name %> &middot; <%= Current.family.users.size %></p>
</div> </div>
<% @users.each do |user| %> <% @users.each do |user| %>
<div class="flex gap-2 items-center bg-white p-4 border border-alpha-black-25 rounded-lg"> <div class="flex gap-2 items-center bg-container p-4 border border-alpha-black-25 rounded-lg">
<div class="w-9 h-9 shrink-0"> <div class="w-9 h-9 shrink-0">
<%= render "settings/user_avatar", user: user %> <%= render "settings/user_avatar", user: user %>
</div> </div>
@ -65,7 +65,7 @@
<% end %> <% end %>
<% if @pending_invitations.any? %> <% if @pending_invitations.any? %>
<% @pending_invitations.each do |invitation| %> <% @pending_invitations.each do |invitation| %>
<div class="flex gap-2 items-center justify-between bg-white p-4 border border-alpha-black-25 rounded-lg"> <div class="flex gap-2 items-center justify-between bg-container p-4 border border-alpha-black-25 rounded-lg">
<div class="flex gap-2 items-center"> <div class="flex gap-2 items-center">
<div class="w-9 h-9 shrink-0"> <div class="w-9 h-9 shrink-0">
<div class="text-white w-full h-full bg-gray-400 rounded-full flex items-center justify-center text-lg uppercase"><%= invitation.email[0] %></div> <div class="text-white w-full h-full bg-gray-400 rounded-full flex items-center justify-center text-lg uppercase"><%= invitation.email[0] %></div>
@ -116,7 +116,7 @@
<% end %> <% end %>
<% if Current.user.admin? %> <% if Current.user.admin? %>
<%= link_to new_invitation_path, <%= link_to new_invitation_path,
class: "bg-gray-100 flex items-center justify-center gap-2 text-secondary mt-1 hover:bg-gray-200 rounded-lg px-4 py-2 w-full text-center", class: "bg-container-inset flex items-center justify-center gap-2 text-secondary mt-1 hover:bg-container-inset-hover rounded-lg px-4 py-2 w-full text-center",
data: { turbo_frame: :modal } do %> data: { turbo_frame: :modal } do %>
<%= lucide_icon("plus", class: "w-5 h-5 text-secondary") %> <%= lucide_icon("plus", class: "w-5 h-5 text-secondary") %>
<%= t(".invite_member") %> <%= t(".invite_member") %>

View file

@ -2,7 +2,7 @@
<%= settings_section title: t(".mfa_title"), subtitle: t(".mfa_description") do %> <%= settings_section title: t(".mfa_title"), subtitle: t(".mfa_description") do %>
<div class="space-y-4"> <div class="space-y-4">
<div class="p-3 shadow-border-xs bg-white rounded-lg flex justify-between items-center"> <div class="p-3 shadow-border-xs bg-container rounded-lg flex justify-between items-center">
<div class="flex items-center gap-3"> <div class="flex items-center gap-3">
<div class="w-9 h-9 rounded-full bg-gray-25 flex justify-center items-center"> <div class="w-9 h-9 rounded-full bg-gray-25 flex justify-center items-center">
<%= lucide_icon "shield-check", class: "w-5 h-5 text-secondary" %> <%= lucide_icon "shield-check", class: "w-5 h-5 text-secondary" %>

View file

@ -1,3 +1,3 @@
<div class="flex items-center py-0.5 px-0.5 gap-1 fixed right-1 top-1 shadow-xs border border-alpha-black-50 rounded-md bg-white"> <div class="flex items-center py-0.5 px-0.5 gap-1 fixed right-1 top-1 shadow-xs border border-alpha-black-50 rounded-md bg-container">
<p class="text-xs text-secondary pl-2">Version: <%= Maybe.version.to_release_tag %></p> <p class="text-xs text-secondary pl-2">Version: <%= Maybe.version.to_release_tag %></p>
</div> </div>

View file

@ -1,7 +1,7 @@
<%# locals: (content:, reload_on_close: false) %> <%# locals: (content:, reload_on_close: false) %>
<%= turbo_frame_tag "drawer" do %> <%= turbo_frame_tag "drawer" do %>
<dialog class="ml-auto bg-white shadow-border-xs rounded-2xl max-w-[480px] h-full w-full mt-4 mr-4 focus-visible:outline-hidden" <dialog class="ml-auto bg-container shadow-border-xs rounded-2xl max-w-[480px] h-full w-full mt-4 mr-4 focus-visible:outline-hidden"
data-controller="modal" data-controller="modal"
data-action="mousedown->modal#clickOutside" data-action="mousedown->modal#clickOutside"
data-modal-reload-on-close-value="<%= reload_on_close %>"> data-modal-reload-on-close-value="<%= reload_on_close %>">

View file

@ -1,6 +1,6 @@
<%# locals: (content:, classes:) -%> <%# locals: (content:, classes:) -%>
<%= turbo_frame_tag "modal" do %> <%= turbo_frame_tag "modal" do %>
<dialog class="m-auto bg-white shadow-border-xs rounded-2xl max-w-[580px] w-min-content h-fit overflow-visible <%= classes %>" data-controller="modal" data-action="mousedown->modal#clickOutside"> <dialog class="m-auto bg-container shadow-border-xs rounded-2xl max-w-[580px] w-min-content h-fit overflow-visible <%= classes %>" data-controller="modal" data-action="mousedown->modal#clickOutside">
<div class="flex flex-col"> <div class="flex flex-col">
<%= content %> <%= content %>
</div> </div>

View file

@ -3,7 +3,7 @@
<% type = type.to_sym %> <% type = type.to_sym %>
<% action = "animationend->element-removal#remove" if type == :notice %> <% action = "animationend->element-removal#remove" if type == :notice %>
<%= tag.div class: "flex gap-3 rounded-lg border bg-white p-4 group max-w-80 shadow-xs border-alpha-black-25", <%= tag.div class: "flex gap-3 rounded-lg border bg-container p-4 group max-w-80 shadow-xs border-alpha-black-25",
data: { data: {
controller: "element-removal", controller: "element-removal",
action: action action: action
@ -32,7 +32,7 @@
<circle class="origin-center -rotate-90 animate-[stroke-fill_2.2s_300ms_forwards]" stroke="#141414" stroke-opacity="0.4" r="7.2" cx="10" cy="10" stroke-dasharray="43.9822971503" stroke-dashoffset="43.9822971503" /> <circle class="origin-center -rotate-90 animate-[stroke-fill_2.2s_300ms_forwards]" stroke="#141414" stroke-opacity="0.4" r="7.2" cx="10" cy="10" stroke-dasharray="43.9822971503" stroke-dashoffset="43.9822971503" />
</svg> </svg>
<div class="absolute -top-2 -right-2"> <div class="absolute -top-2 -right-2">
<%= lucide_icon "x", class: "w-5 h-5 p-0.5 hidden group-hover:inline-block border border-alpha-black-50 border-solid rounded-lg bg-white text-subdued cursor-pointer", data: { action: "click->element-removal#remove" } %> <%= lucide_icon "x", class: "w-5 h-5 p-0.5 hidden group-hover:inline-block border border-alpha-black-50 border-solid rounded-lg bg-container text-subdued cursor-pointer", data: { action: "click->element-removal#remove" } %>
</div> </div>
</div> </div>
<% elsif type.to_sym == :alert %> <% elsif type.to_sym == :alert %>

View file

@ -6,27 +6,27 @@
<% if pagy.prev %> <% if pagy.prev %>
<%= link_to pagy_url_for(pagy, pagy.prev), <%= link_to pagy_url_for(pagy, pagy.prev),
data: { turbo_frame: :_top }, data: { turbo_frame: :_top },
class: "inline-flex items-center p-2 text-sm font-medium text-secondary hover:border-gray-300 hover:text-gray-700" do %> class: "inline-flex items-center p-2 text-sm font-medium text-secondary bg-container-inset hover:border-secondary hover:text-secondary" do %>
<%= lucide_icon("chevron-left", class: "w-5 h-5 text-secondary") %> <%= lucide_icon("chevron-left", class: "w-5 h-5 text-secondary") %>
<% end %> <% end %>
<% else %> <% else %>
<div class="inline-flex items-center p-2 text-sm font-medium hover:border-gray-300"> <div class="inline-flex items-center p-2 text-sm font-medium hover:border-secondary">
<%= lucide_icon("chevron-left", class: "w-5 h-5 text-gray-200") %> <%= lucide_icon("chevron-left", class: "w-5 h-5 text-secondary") %>
</div> </div>
<% end %> <% end %>
</div> </div>
<div class="rounded-xl p-1 bg-gray-25"> <div class="rounded-xl p-1 bg-container-inset">
<% pagy.series.each do |series_item| %> <% pagy.series.each do |series_item| %>
<% if series_item.is_a?(Integer) %> <% if series_item.is_a?(Integer) %>
<%= link_to pagy_url_for(pagy, series_item), <%= link_to pagy_url_for(pagy, series_item),
data: { turbo_frame: :_top }, data: { turbo_frame: :_top },
class: "rounded-md px-2 py-1 inline-flex items-center text-sm font-medium text-secondary hover:border-gray-300 hover:text-gray-700" do %> class: "rounded-md px-2 py-1 inline-flex items-center text-sm font-medium text-secondary hover:border-secondary hover:text-secondary" do %>
<%= series_item %> <%= series_item %>
<% end %> <% end %>
<% elsif series_item.is_a?(String) %> <% elsif series_item.is_a?(String) %>
<%= link_to pagy_url_for(pagy, series_item), <%= link_to pagy_url_for(pagy, series_item),
data: { turbo_frame: :_top }, data: { turbo_frame: :_top },
class: "rounded-md px-2 py-1 bg-white border border-alpha-black-25 shadow-xs inline-flex items-center text-sm font-medium text-primary" do %> class: "rounded-md px-2 py-1 bg-container border border-secondary shadow-xs inline-flex items-center text-sm font-medium text-primary" do %>
<%= series_item %> <%= series_item %>
<% end %> <% end %>
<% elsif series_item == :gap %> <% elsif series_item == :gap %>
@ -38,12 +38,12 @@
<% if pagy.next %> <% if pagy.next %>
<%= link_to pagy_url_for(pagy, pagy.next), <%= link_to pagy_url_for(pagy, pagy.next),
data: { turbo_frame: :_top }, data: { turbo_frame: :_top },
class: "inline-flex items-center p-2 text-sm font-medium text-secondary hover:border-gray-300 hover:text-gray-700" do %> class: "inline-flex items-center p-2 text-sm font-medium text-secondary hover:border-secondary hover:text-secondary" do %>
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-secondary") %> <%= lucide_icon("chevron-right", class: "w-5 h-5 text-secondary") %>
<% end %> <% end %>
<% else %> <% else %>
<div class="inline-flex items-center p-2 text-sm font-medium hover:border-gray-300"> <div class="inline-flex items-center p-2 text-sm font-medium hover:border-secondary">
<%= lucide_icon("chevron-right", class: "w-5 h-5 text-gray-200") %> <%= lucide_icon("chevron-right", class: "w-5 h-5 text-secondary") %>
</div> </div>
<% end %> <% end %>
</div> </div>
@ -52,6 +52,6 @@
<%= select_tag :per_page, <%= select_tag :per_page,
options_for_select(["10", "20", "30", "50"], pagy.limit), options_for_select(["10", "20", "30", "50"], pagy.limit),
data: { controller: "selectable-link" }, data: { controller: "selectable-link" },
class: "py-1.5 pr-8 text-sm text-primary font-medium border border-gray-200 rounded-lg focus:border-gray-900 focus:ring-gray-900 focus-visible:ring-gray-900" %> class: "py-1.5 pr-8 text-sm text-primary font-medium bg-container-inset border border-secondary rounded-lg focus:border-secondary focus:ring-secondary focus-visible:ring-secondary" %>
</div> </div>
</nav> </nav>

View file

@ -1,6 +1,6 @@
<div data-controller="modal" data-modal-open-value="true" class="h-full flex items-center justify-center bg-white/90" aria-labelledby="modal-title" role="dialog" aria-modal="true"> <div data-controller="modal" data-modal-open-value="true" class="h-full flex items-center justify-center bg-container/90" aria-labelledby="modal-title" role="dialog" aria-modal="true">
<div class="w-[400px] rounded-xl relative overflow-hidden"> <div class="w-[400px] rounded-xl relative overflow-hidden">
<div class="bg-white shadow-border-xs rounded-xl relative z-10"> <div class="bg-container shadow-border-xs rounded-xl relative z-10">
<div class="rounded-xl" style="background-image: url('<%= asset_path("maybe-plus-background.svg") %>'); background-size: cover; background-position: center top;"> <div class="rounded-xl" style="background-image: url('<%= asset_path("maybe-plus-background.svg") %>'); background-size: cover; background-position: center top;">
<div class="text-center rounded-xl" style="background-image: linear-gradient(to bottom, rgba(197,161,119,0.15) 0%, rgba(255,255,255,0.8) 30%, white 40%);"> <div class="text-center rounded-xl" style="background-image: linear-gradient(to bottom, rgba(197,161,119,0.15) 0%, rgba(255,255,255,0.8) 30%, white 40%);">
<div class="p-4 pt-2 rounded-xl"> <div class="p-4 pt-2 rounded-xl">

View file

@ -1,4 +1,4 @@
<%= tag.div id: "syncing-notice", class: "flex gap-3 rounded-lg border bg-white p-4 group w-full shadow-xs border-alpha-black-25" do %> <%= tag.div id: "syncing-notice", class: "flex gap-3 rounded-lg border bg-container p-4 group w-full shadow-xs border-alpha-black-25" do %>
<div class="h-5 w-5 shrink-0 p-px text-white"> <div class="h-5 w-5 shrink-0 p-px text-white">
<%= lucide_icon "loader", class: "w-5 h-5 text-secondary animate-pulse" %> <%= lucide_icon "loader", class: "w-5 h-5 text-secondary animate-pulse" %>
</div> </div>

View file

@ -4,7 +4,7 @@
data: { data: {
turbo: false, turbo: false,
controller: "deletion", controller: "deletion",
deletion_dangerous_action_class: "form-field__submit bg-white text-red-600 border hover:bg-red-50", deletion_dangerous_action_class: "form-field__submit bg-container text-red-600 border hover:bg-red-50",
deletion_safe_action_class: "form-field__submit border border-transparent", 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_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| %> deletion_submit_text_when_replacing_value: t(".delete_and_recategorize", tag_name: @tag.name) } do |f| %>
@ -15,7 +15,7 @@
{ data: { deletion_target: "replacementField", action: "deletion#updateSubmitButton" } } %> { data: { deletion_target: "replacementField", action: "deletion#updateSubmitButton" } } %>
<%= f.submit t(".delete_and_leave_uncategorized", tag_name: @tag.name), <%= f.submit t(".delete_and_leave_uncategorized", tag_name: @tag.name),
class: "form-field__submit bg-white text-red-600 border hover:bg-red-50", class: "form-field__submit bg-container text-red-600 border hover:bg-red-50",
data: { deletion_target: "submitButton" } %> data: { deletion_target: "submitButton" } %>
<% end %> <% end %>
<% end %> <% end %>

View file

@ -1,3 +1,3 @@
<div class="bg-white"> <div class="bg-container">
<div class="h-px bg-alpha-black-50 ml-4 mr-6"></div> <div class="h-px bg-alpha-black-50 ml-4 mr-6"></div>
</div> </div>

View file

@ -1,6 +1,6 @@
<%# locals: (tag:) %> <%# locals: (tag:) %>
<div id="<%= dom_id(tag) %>" class="flex justify-between items-center p-4 bg-white"> <div id="<%= dom_id(tag) %>" class="flex justify-between items-center p-4 bg-container">
<div class="flex w-full items-center gap-2.5"> <div class="flex w-full items-center gap-2.5">
<%= render partial: "shared/color_avatar", locals: { name: tag.name, color: tag.color } %> <%= render partial: "shared/color_avatar", locals: { name: tag.name, color: tag.color } %>
<p class="text-primary text-sm truncate"> <p class="text-primary text-sm truncate">
@ -9,7 +9,7 @@
</div> </div>
<div class="justify-self-end"> <div class="justify-self-end">
<%= contextual_menu do %> <%= contextual_menu do %>
<div class="w-48 p-1 text-sm leading-6 text-primary bg-white shadow-lg shrink rounded-xl ring-1 ring-gray-900/5"> <div class="w-48 p-1 text-sm leading-6 text-primary bg-container shadow-lg shrink rounded-xl ring-1 ring-gray-900/5">
<%= contextual_menu_modal_action_item t(".edit"), edit_tag_path(tag) %> <%= contextual_menu_modal_action_item t(".edit"), edit_tag_path(tag) %>
<% if tag.transactions.any? %> <% if tag.transactions.any? %>

View file

@ -7,16 +7,16 @@
<% end %> <% end %>
</header> </header>
<div class="bg-white shadow-border-xs rounded-xl p-4"> <div class="bg-container shadow-border-xs rounded-xl p-4">
<% if @tags.any? %> <% if @tags.any? %>
<div class="rounded-xl bg-gray-25 space-y-1 p-1"> <div class="rounded-xl bg-container-inset space-y-1 p-1">
<div class="flex items-center gap-1.5 px-4 py-2 text-xs font-medium text-secondary uppercase"> <div class="flex items-center gap-1.5 px-4 py-2 text-xs font-medium text-secondary uppercase">
<p><%= t(".tags") %></p> <p><%= t(".tags") %></p>
<span class="text-subdued">&middot;</span> <span class="text-subdued">&middot;</span>
<p><%= @tags.count %></p> <p><%= @tags.count %></p>
</div> </div>
<div class="border border-alpha-black-25 rounded-md bg-white shadow-border-xs"> <div class="border border-alpha-black-25 rounded-md bg-container shadow-border-xs">
<div class="overflow-hidden rounded-md"> <div class="overflow-hidden rounded-md">
<%= render partial: @tags, spacer_template: "tags/ruler" %> <%= render partial: @tags, spacer_template: "tags/ruler" %>
</div> </div>

View file

@ -1,5 +1,5 @@
<%# locals: (totals:) %> <%# locals: (totals:) %>
<div class="grid grid-cols-3 bg-white rounded-xl shadow-border-xs divide-x divide-alpha-black-100"> <div class="grid grid-cols-3 bg-container rounded-xl shadow-border-xs divide-x divide-alpha-black-100">
<div class="p-4 space-y-2"> <div class="p-4 space-y-2">
<p class="text-sm text-secondary">Total transactions</p> <p class="text-sm text-secondary">Total transactions</p>
<p class="text-primary font-medium text-xl" id="total-transactions"><%= totals.transactions_count.round(0) %></p> <p class="text-primary font-medium text-xl" id="total-transactions"><%= totals.transactions_count.round(0) %></p>

View file

@ -7,7 +7,7 @@
data-controller="bulk-select" data-controller="bulk-select"
data-bulk-select-singular-label-value="<%= t(".transaction") %>" data-bulk-select-singular-label-value="<%= t(".transaction") %>"
data-bulk-select-plural-label-value="<%= t(".transactions") %>" data-bulk-select-plural-label-value="<%= t(".transactions") %>"
class="flex flex-col bg-white rounded-xl shadow-border-xs p-4"> class="flex flex-col bg-container rounded-xl shadow-border-xs p-4">
<%= render "transactions/searches/search" %> <%= render "transactions/searches/search" %>
<div id="entry-selection-bar" data-bulk-select-target="selectionBar" class="flex justify-center hidden"> <div id="entry-selection-bar" data-bulk-select-target="selectionBar" class="flex justify-center hidden">
@ -16,7 +16,7 @@
<% if @pagy.count > 0 %> <% if @pagy.count > 0 %>
<div class="grow overflow-y-auto"> <div class="grow overflow-y-auto">
<div class="grid grid-cols-12 bg-gray-25 rounded-xl px-5 py-3 text-xs uppercase font-medium text-secondary items-center mb-4"> <div class="grid grid-cols-12 bg-container-inset rounded-xl px-5 py-3 text-xs uppercase font-medium text-secondary items-center mb-4">
<div class="pl-0.5 col-span-8 flex items-center gap-4"> <div class="pl-0.5 col-span-8 flex items-center gap-4">
<%= check_box_tag "selection_entry", <%= check_box_tag "selection_entry",
class: "checkbox checkbox--light", class: "checkbox checkbox--light",

View file

@ -7,7 +7,7 @@
<div class="flex gap-2 mb-4"> <div class="flex gap-2 mb-4">
<div class="grow"> <div class="grow">
<div class="flex items-center px-3 py-2 gap-2 border border-gray-200 rounded-lg focus-within:ring-gray-100 focus-within:border-gray-900"> <div class="flex items-center px-3 py-2 gap-2 border border-secondary rounded-lg focus-within:ring-secondary focus-within:border-secondary">
<%= lucide_icon("search", class: "w-5 h-5 text-secondary") %> <%= lucide_icon("search", class: "w-5 h-5 text-secondary") %>
<%= form.text_field :search, <%= form.text_field :search,
placeholder: "Search transactions by name", placeholder: "Search transactions by name",

View file

@ -6,11 +6,11 @@
data-controller="tabs" data-controller="tabs"
data-tabs-active-class="bg-gray-25 text-primary" data-tabs-active-class="bg-gray-25 text-primary"
data-tabs-default-tab-value="<%= get_default_transaction_search_filter[:key] %>" data-tabs-default-tab-value="<%= get_default_transaction_search_filter[:key] %>"
class="hidden absolute flex z-10 h-80 w-[540px] top-12 right-0 shadow-border-xs bg-white rounded-lg"> class="hidden absolute flex z-10 h-80 w-[540px] top-12 right-0 shadow-border-xs bg-container rounded-lg">
<div class="flex w-44 flex-col items-start p-3 text-sm font-medium text-secondary border-r border-r-alpha-black-100"> <div class="flex w-44 flex-col items-start p-3 text-sm font-medium text-secondary border-r border-r-alpha-black-100">
<% transaction_search_filters.each do |filter| %> <% transaction_search_filters.each do |filter| %>
<button <button
class="flex text-secondary hover:bg-gray-25 items-center gap-2 px-3 rounded-md py-2 w-full" class="flex text-secondary hover:bg-container-inset items-center gap-2 px-3 rounded-md py-2 w-full"
type="button" type="button"
data-id="<%= filter[:key] %>" data-id="<%= filter[:key] %>"
data-tabs-target="btn" data-tabs-target="btn"
@ -30,7 +30,7 @@
<% end %> <% end %>
</div> </div>
<div class="flex justify-between items-center gap-2 bg-white p-3"> <div class="flex justify-between items-center gap-2 bg-container p-3">
<div> <div>
<% if @q.present? %> <% if @q.present? %>
<%= link_to t(".clear_filters"), transactions_path(clear_filters: true), class: "btn btn--ghost" %> <%= link_to t(".clear_filters"), transactions_path(clear_filters: true), class: "btn btn--ghost" %>
@ -38,10 +38,10 @@
</div> </div>
<div> <div>
<%= button_tag type: "reset", data: { action: "menu#close" }, class: "py-2 px-3 bg-gray-50 rounded-lg text-sm text-primary font-medium" do %> <%= button_tag type: "reset", data: { action: "menu#close" }, class: "py-2 px-3 bg-container-inset rounded-lg text-sm text-primary font-medium" do %>
<%= t(".cancel") %> <%= t(".cancel") %>
<% end %> <% end %>
<%= form.submit t(".apply"), name: nil, class: "py-2 px-3 bg-gray-900 hover:bg-gray-700 rounded-lg text-sm text-white font-medium cursor-pointer" %> <%= form.submit t(".apply"), name: nil, class: "py-2 px-3 bg-primary hover:bg-primary-dark rounded-lg text-sm text-white font-medium cursor-pointer" %>
</div> </div>
</div> </div>
</div> </div>

View file

@ -18,7 +18,7 @@
<%= tag.span t(".income") %> <%= tag.span t(".income") %>
<% end %> <% end %>
<%= tag.div class: "flex px-4 py-1 rounded-lg items-center space-x-2 justify-center text-subdued bg-white text-gray-800 shadow-sm" do %> <%= tag.div class: "flex px-4 py-1 rounded-lg items-center space-x-2 justify-center text-subdued bg-container text-gray-800 shadow-sm" do %>
<%= lucide_icon "arrow-right-left", class: "w-5 h-5" %> <%= lucide_icon "arrow-right-left", class: "w-5 h-5" %>
<%= tag.span t(".transfer") %> <%= tag.span t(".transfer") %>
<% end %> <% end %>

View file

@ -1,5 +1,5 @@
<%# locals: (user_message:) %> <%# locals: (user_message:) %>
<div id="<%= dom_id(user_message) %>" class="bg-gray-100 px-3 py-2 rounded-lg max-w-[85%] w-fit ml-auto mb-6"> <div id="<%= dom_id(user_message) %>" class="bg-surface-inset px-3 py-2 rounded-lg max-w-[85%] w-fit ml-auto mb-6">
<div class="prose prose--ai-chat"><%= markdown(user_message.content) %></div> <div class="prose prose--ai-chat"><%= markdown(user_message.content) %></div>
</div> </div>

View file

@ -7,7 +7,7 @@
</div> </div>
</button> </button>
<div data-menu-target="content" class="hidden absolute w-[276px] z-10 divide-y divide-alpha-black-100 bg-white rounded-xl shadow-border-sm"> <div data-menu-target="content" class="hidden absolute w-[276px] z-10 divide-y divide-alpha-black-100 bg-container rounded-xl shadow-border-sm">
<div class="px-4 py-3 flex items-center gap-3"> <div class="px-4 py-3 flex items-center gap-3">
<div class="w-9 h-9 shrink-0"> <div class="w-9 h-9 shrink-0">
<%= render "settings/user_avatar", user: user, variant: :small, lazy: true %> <%= render "settings/user_avatar", user: user, variant: :small, lazy: true %>

View file

@ -25,7 +25,7 @@ en:
page_title: Preferences page_title: Preferences
theme_dark: Dark theme_dark: Dark
theme_light: Light theme_light: Light
theme_subtitle: Choose a preferred theme for the app (coming soon...) theme_subtitle: Choose a preferred theme for the app
theme_system: System theme_system: System
theme_title: Theme theme_title: Theme
timezone: Timezone timezone: Timezone

View file

@ -0,0 +1,5 @@
class AddThemeToUsers < ActiveRecord::Migration[7.2]
def change
add_column :users, :theme, :string, default: "system"
end
end

5
db/schema.rb generated
View file

@ -10,7 +10,7 @@
# #
# It's strongly recommended that you check this file into your version control system. # It's strongly recommended that you check this file into your version control system.
ActiveRecord::Schema[7.2].define(version: 2025_03_19_212839) do ActiveRecord::Schema[7.2].define(version: 2025_04_10_144939) do
# These are extensions that must be enabled in order to support this database # These are extensions that must be enabled in order to support this database
enable_extension "pgcrypto" enable_extension "pgcrypto"
enable_extension "plpgsql" enable_extension "plpgsql"
@ -101,7 +101,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_03_19_212839) do
t.decimal "balance", precision: 19, scale: 4 t.decimal "balance", precision: 19, scale: 4
t.string "currency" t.string "currency"
t.boolean "is_active", default: true, null: false t.boolean "is_active", default: true, null: false
t.virtual "classification", type: :string, as: "\nCASE\n WHEN ((accountable_type)::text = ANY ((ARRAY['Loan'::character varying, 'CreditCard'::character varying, 'OtherLiability'::character varying])::text[])) THEN 'liability'::text\n ELSE 'asset'::text\nEND", stored: true t.virtual "classification", type: :string, as: "\nCASE\n WHEN ((accountable_type)::text = ANY (ARRAY[('Loan'::character varying)::text, ('CreditCard'::character varying)::text, ('OtherLiability'::character varying)::text])) THEN 'liability'::text\n ELSE 'asset'::text\nEND", stored: true
t.uuid "import_id" t.uuid "import_id"
t.uuid "plaid_account_id" t.uuid "plaid_account_id"
t.boolean "scheduled_for_deletion", default: false t.boolean "scheduled_for_deletion", default: false
@ -614,6 +614,7 @@ ActiveRecord::Schema[7.2].define(version: 2025_03_19_212839) do
t.uuid "last_viewed_chat_id" t.uuid "last_viewed_chat_id"
t.boolean "show_ai_sidebar", default: true t.boolean "show_ai_sidebar", default: true
t.boolean "ai_enabled", default: false, null: false t.boolean "ai_enabled", default: false, null: false
t.string "theme", default: "system"
t.index ["email"], name: "index_users_on_email", unique: true t.index ["email"], name: "index_users_on_email", unique: true
t.index ["family_id"], name: "index_users_on_family_id" t.index ["family_id"], name: "index_users_on_family_id"
t.index ["last_viewed_chat_id"], name: "index_users_on_last_viewed_chat_id" t.index ["last_viewed_chat_id"], name: "index_users_on_last_viewed_chat_id"