Compare commits

..

4 commits

Author SHA1 Message Date
8068bf2ad4
bin/rails tailwindcss:upgrade 2025-02-11 20:25:35 -05:00
9f8d3bcc06
Prepare to run upgrader 2025-02-11 20:23:19 -05:00
3b47ce3db5
Bump tailwind 2025-02-11 20:19:20 -05:00
9f06cf6a7e
Use new node version 2025-02-11 20:14:40 -05:00
55 changed files with 544 additions and 2271 deletions

View file

@ -8,7 +8,7 @@ jobs:
runs-on: cth-ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: https://github.com/ruby/setup-ruby@a4effe49ee8ee5b8b5091268c473a4628afb5651
- uses: https://github.com/ruby/setup-ruby@bfefad842bb982ff05b233bcbc1571d97a87e69f
- uses: actions/cache@v4
with:
path: vendor/bundle

2
.gitignore vendored
View file

@ -36,3 +36,5 @@
/app/assets/builds/*
!/app/assets/builds/.keep
/node_modules

1
.node-version Normal file
View file

@ -0,0 +1 @@
22.14.0

View file

@ -1 +1 @@
3.4.4
3.4.1

View file

@ -7,7 +7,7 @@
# For a containerized dev environment, see Dev Containers: https://guides.rubyonrails.org/getting_started_with_devcontainer.html
# Make sure RUBY_VERSION matches the Ruby version in .ruby-version
ARG RUBY_VERSION
ARG RUBY_VERSION=3.4.1
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base
# Rails app lives here
@ -29,11 +29,11 @@ FROM base AS build
# Install packages needed to build gems
RUN apt-get update -qq && \
apt-get install --no-install-recommends -y build-essential git pkg-config libyaml-dev && \
apt-get install --no-install-recommends -y build-essential git pkg-config && \
rm -rf /var/lib/apt/lists /var/cache/apt/archives
# Install application gems
COPY Gemfile Gemfile.lock .ruby-version ./
COPY Gemfile Gemfile.lock ./
RUN bundle install && \
rm -rf ~/.bundle/ "${BUNDLE_PATH}"/ruby/*/cache "${BUNDLE_PATH}"/ruby/*/bundler/gems/*/.git && \
bundle exec bootsnap precompile --gemfile

View file

@ -51,9 +51,6 @@ group :development, :test do
# Omakase Ruby styling [https://github.com/rails/rubocop-rails-omakase/]
gem "rubocop-rails-omakase", require: false
# Audit bundle for known vulnerabilities
gem "bundler-audit", require: false
end
group :development do
@ -64,7 +61,7 @@ end
group :test do
# Use system testing [https://guides.rubyonrails.org/testing.html#system-testing]
gem "capybara"
gem "cuprite"
gem "selenium-webdriver"
end
gem "tailwindcss-rails", "~> 3.3.1"
gem "tailwindcss-rails"

View file

@ -1,6 +1,6 @@
GIT
remote: https://github.com/rails/rails.git
revision: 82e9029bbf63a33b69f007927979c5564a6afe9e
revision: 57c24948eb5cc9e5f9a4cecb6f2060f53e2246e1
branch: main
specs:
actioncable (8.1.0.alpha)
@ -34,7 +34,6 @@ GIT
rails-html-sanitizer (~> 1.6)
useragent (~> 0.16)
actiontext (8.1.0.alpha)
action_text-trix (~> 2.1.15)
actionpack (= 8.1.0.alpha)
activerecord (= 8.1.0.alpha)
activestorage (= 8.1.0.alpha)
@ -101,24 +100,19 @@ GIT
GEM
remote: https://rubygems.org/
specs:
action_text-trix (2.1.15)
railties
addressable (2.8.7)
public_suffix (>= 2.0.2, < 7.0)
ast (2.4.3)
base64 (0.3.0)
ast (2.4.2)
base64 (0.2.0)
bcrypt (3.1.20)
benchmark (0.4.1)
bigdecimal (3.2.2)
benchmark (0.4.0)
bigdecimal (3.1.9)
bindex (0.8.1)
bootsnap (1.18.6)
bootsnap (1.18.4)
msgpack (~> 1.2)
brakeman (7.0.2)
brakeman (7.0.0)
racc
builder (3.3.0)
bundler-audit (0.9.2)
bundler (>= 1.2.0, < 3)
thor (~> 1.0)
capybara (3.40.0)
addressable
matrix
@ -129,26 +123,16 @@ GEM
regexp_parser (>= 1.5, < 3.0)
xpath (~> 3.2)
concurrent-ruby (1.3.5)
connection_pool (2.5.3)
connection_pool (2.5.0)
crass (1.0.6)
cuprite (0.17)
capybara (~> 3.0)
ferrum (~> 0.17.0)
date (3.4.1)
debug (1.10.0)
irb (~> 1.10)
reline (>= 0.3.8)
drb (2.2.3)
erb (5.0.1)
drb (2.2.1)
erubi (1.13.1)
et-orbi (1.2.11)
tzinfo
ferrum (0.17.1)
addressable (~> 2.5)
base64 (~> 0.2)
concurrent-ruby (~> 1.1)
webrick (~> 1.7)
websocket-driver (~> 0.7)
fugit (1.11.1)
et-orbi (~> 1, >= 1.2.11)
raabro (~> 1.4)
@ -161,18 +145,17 @@ GEM
activesupport (>= 6.0.0)
railties (>= 6.0.0)
io-console (0.8.0)
irb (1.15.2)
irb (1.15.1)
pp (>= 0.6.0)
rdoc (>= 4.0.0)
reline (>= 0.4.2)
jbuilder (2.13.0)
actionview (>= 5.0.0)
activesupport (>= 5.0.0)
json (2.12.2)
language_server-protocol (3.17.0.5)
lint_roller (1.1.0)
logger (1.7.0)
loofah (2.24.1)
json (2.10.1)
language_server-protocol (3.17.0.4)
logger (1.6.5)
loofah (2.24.0)
crass (~> 1.0.2)
nokogiri (>= 1.12.0)
mail (2.8.1)
@ -183,9 +166,9 @@ GEM
marcel (1.0.4)
matrix (0.4.2)
mini_mime (1.1.5)
minitest (5.25.5)
minitest (5.25.4)
msgpack (1.8.0)
net-imap (0.5.8)
net-imap (0.5.6)
date
net-protocol
net-pop (0.1.2)
@ -195,42 +178,41 @@ GEM
net-smtp (0.5.1)
net-protocol
nio4r (2.7.4)
nokogiri (1.18.8-aarch64-linux-gnu)
nokogiri (1.18.2-aarch64-linux-gnu)
racc (~> 1.4)
nokogiri (1.18.8-arm64-darwin)
nokogiri (1.18.2-arm64-darwin)
racc (~> 1.4)
nokogiri (1.18.8-x86_64-linux-gnu)
nokogiri (1.18.2-x86_64-linux-gnu)
racc (~> 1.4)
parallel (1.27.0)
parser (3.3.8.0)
parallel (1.26.3)
parser (3.3.7.1)
ast (~> 2.4.1)
racc
pp (0.6.2)
prettyprint
prettyprint (0.2.0)
prism (1.4.0)
propshaft (1.1.0)
actionpack (>= 7.0.0)
activesupport (>= 7.0.0)
rack
railties (>= 7.0.0)
psych (5.2.6)
psych (5.2.3)
date
stringio
public_suffix (6.0.2)
public_suffix (6.0.1)
puma (6.6.0)
nio4r (~> 2.0)
raabro (1.4.0)
racc (1.8.1)
rack (3.1.16)
rack-session (2.1.1)
rack (3.1.9)
rack-session (2.1.0)
base64 (>= 0.1.0)
rack (>= 3.0.0)
rack-test (2.2.0)
rack (>= 1.3)
rackup (2.2.1)
rack (>= 3)
rails-dom-testing (2.3.0)
rails-dom-testing (2.2.0)
activesupport (>= 5.0.0)
minitest
nokogiri (>= 1.6)
@ -238,44 +220,51 @@ GEM
loofah (~> 2.21)
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
rainbow (3.1.1)
rake (13.3.0)
rdoc (6.14.0)
erb
rake (13.2.1)
rdoc (6.12.0)
psych (>= 4.0.0)
regexp_parser (2.10.0)
reline (0.6.1)
reline (0.6.0)
io-console (~> 0.5)
rubocop (1.76.1)
rexml (3.4.0)
rubocop (1.71.2)
json (~> 2.3)
language_server-protocol (~> 3.17.0.2)
lint_roller (~> 1.1.0)
language_server-protocol (>= 3.17.0)
parallel (~> 1.10)
parser (>= 3.3.0.2)
rainbow (>= 2.2.2, < 4.0)
regexp_parser (>= 2.9.3, < 3.0)
rubocop-ast (>= 1.45.0, < 2.0)
rubocop-ast (>= 1.38.0, < 2.0)
ruby-progressbar (~> 1.7)
unicode-display_width (>= 2.4.0, < 4.0)
rubocop-ast (1.45.1)
parser (>= 3.3.7.2)
prism (~> 1.4)
rubocop-performance (1.25.0)
lint_roller (~> 1.1)
rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.38.0, < 2.0)
rubocop-rails (2.32.0)
rubocop-ast (1.38.0)
parser (>= 3.3.1.0)
rubocop-minitest (0.36.0)
rubocop (>= 1.61, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-performance (1.23.1)
rubocop (>= 1.48.1, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rails (2.29.1)
activesupport (>= 4.2.0)
lint_roller (~> 1.1)
rack (>= 1.1)
rubocop (>= 1.75.0, < 2.0)
rubocop-ast (>= 1.44.0, < 2.0)
rubocop-rails-omakase (1.1.0)
rubocop (>= 1.72)
rubocop-performance (>= 1.24)
rubocop-rails (>= 2.30)
rubocop (>= 1.52.0, < 2.0)
rubocop-ast (>= 1.31.1, < 2.0)
rubocop-rails-omakase (1.0.0)
rubocop
rubocop-minitest
rubocop-performance
rubocop-rails
ruby-progressbar (1.13.0)
rubyzip (2.4.1)
securerandom (0.4.1)
solid_cable (3.0.8)
selenium-webdriver (4.28.0)
base64 (~> 0.2)
logger (~> 1.4)
rexml (~> 3.2, >= 3.2.5)
rubyzip (>= 1.2.2, < 3.0)
websocket (~> 1.0)
solid_cable (3.0.7)
actioncable (>= 7.2)
activejob (>= 7.2)
activerecord (>= 7.2)
@ -284,50 +273,50 @@ GEM
activejob (>= 7.2)
activerecord (>= 7.2)
railties (>= 7.2)
solid_queue (1.1.5)
solid_queue (1.1.3)
activejob (>= 7.1)
activerecord (>= 7.1)
concurrent-ruby (>= 1.3.1)
fugit (~> 1.11.0)
railties (>= 7.1)
thor (~> 1.3.1)
sqlite3 (2.7.0-aarch64-linux-gnu)
sqlite3 (2.7.0-arm64-darwin)
sqlite3 (2.7.0-x86_64-linux-gnu)
sqlite3 (2.5.0-aarch64-linux-gnu)
sqlite3 (2.5.0-arm64-darwin)
sqlite3 (2.5.0-x86_64-linux-gnu)
stimulus-rails (1.3.4)
railties (>= 6.0.0)
stringio (3.1.7)
tailwindcss-rails (3.3.2)
stringio (3.1.2)
tailwindcss-rails (4.0.0)
railties (>= 7.0.0)
tailwindcss-ruby (~> 3.0)
tailwindcss-ruby (3.4.17-aarch64-linux)
tailwindcss-ruby (3.4.17-arm64-darwin)
tailwindcss-ruby (3.4.17-x86_64-linux)
tailwindcss-ruby (~> 4.0)
tailwindcss-ruby (4.0.6-aarch64-linux-gnu)
tailwindcss-ruby (4.0.6-arm64-darwin)
tailwindcss-ruby (4.0.6-x86_64-linux-gnu)
thor (1.3.2)
timeout (0.4.3)
turbo-rails (2.0.16)
actionpack (>= 7.1.0)
railties (>= 7.1.0)
turbo-rails (2.0.11)
actionpack (>= 6.0.0)
railties (>= 6.0.0)
tzinfo (2.0.6)
concurrent-ruby (~> 1.0)
unicode-display_width (3.1.4)
unicode-emoji (~> 4.0, >= 4.0.4)
unicode-emoji (4.0.4)
uri (1.0.3)
uri (1.0.2)
useragent (0.16.11)
web-console (4.2.1)
actionview (>= 6.0.0)
activemodel (>= 6.0.0)
bindex (>= 0.4.0)
railties (>= 6.0.0)
webrick (1.9.1)
websocket-driver (0.8.0)
websocket (1.2.11)
websocket-driver (0.7.7)
base64
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
xpath (3.2.0)
nokogiri (~> 1.8)
zeitwerk (2.7.3)
zeitwerk (2.7.1)
PLATFORMS
aarch64-linux
@ -339,9 +328,7 @@ DEPENDENCIES
bcrypt
bootsnap
brakeman
bundler-audit
capybara
cuprite
debug
importmap-rails
jbuilder
@ -349,18 +336,19 @@ DEPENDENCIES
puma (>= 5.0)
rails!
rubocop-rails-omakase
selenium-webdriver
solid_cable
solid_cache
solid_queue
sqlite3 (>= 1.4)
stimulus-rails
tailwindcss-rails (~> 3.3.1)
tailwindcss-rails
turbo-rails
tzinfo-data
web-console
RUBY VERSION
ruby 3.4.4p34
ruby 3.4.1p0
BUNDLED WITH
2.6.2

View file

@ -1,30 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg width="200" height="48" viewBox="0 0 200 48" fill="none" xmlns="http://www.w3.org/2000/svg">
<!-- Piggy bank icon -->
<g>
<!-- Piggy body -->
<ellipse cx="24" cy="26" rx="14" ry="10" fill="#F472B6" stroke="#DB2777" stroke-width="1.5"/>
<!-- Piggy snout -->
<ellipse cx="34" cy="26" rx="4" ry="3" fill="#F9A8D4" stroke="#DB2777" stroke-width="1"/>
<!-- Nostrils -->
<circle cx="33" cy="26" r="0.5" fill="#DB2777"/>
<circle cx="35" cy="26" r="0.5" fill="#DB2777"/>
<!-- Eye -->
<circle cx="28" cy="22" r="1.5" fill="#DB2777"/>
<!-- Ear -->
<path d="M20 18 L18 16 L20 20" fill="#F472B6" stroke="#DB2777" stroke-width="1" stroke-linejoin="round"/>
<!-- Legs -->
<rect x="16" y="32" width="3" height="4" rx="1" fill="#F472B6" stroke="#DB2777" stroke-width="1"/>
<rect x="29" y="32" width="3" height="4" rx="1" fill="#F472B6" stroke="#DB2777" stroke-width="1"/>
<!-- Coin slot -->
<rect x="22" y="16" width="8" height="1.5" rx="0.5" fill="#DB2777"/>
</g>
<!-- Text -->
<text x="48" y="28" font-family="Arial, Helvetica, sans-serif" font-size="22" fill="#ffffff" font-weight="600" dominant-baseline="middle">
Family
</text>
<text x="118" y="28" font-family="Arial, Helvetica, sans-serif" font-size="22" fill="#F472B6" font-weight="600" dominant-baseline="middle">
Funds
</text>
</svg>

Before

(image error) Size: 1.4 KiB

View file

@ -1,19 +0,0 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
.msg-notice {
@apply bg-green-300 text-green-900;
}
.msg-alert {
@apply bg-red-300 text-red-900;
}
/*
@layer components {
.btn-primary {
@apply py-2 px-4 bg-blue-200;
}
}
*/

View file

@ -0,0 +1,39 @@
@import 'tailwindcss';
@plugin '@tailwindcss/forms';
@plugin '@tailwindcss/aspect-ratio';
@plugin '@tailwindcss/typography';
/*
The default border color has changed to `currentColor` in Tailwind CSS v4,
so we've added these compatibility styles to make sure everything still
looks the same as it did with Tailwind CSS v3.
If we ever want to remove these styles, we need to add an explicit border
color utility to any element that depends on these defaults.
*/
@layer base {
*,
::after,
::before,
::backdrop,
::file-selector-button {
border-color: var(--color-gray-200, currentColor);
}
}
.msg-notice {
@apply bg-green-300 text-green-900;
}
.msg-alert {
@apply bg-red-300 text-red-900;
}
/*
@layer components {
.btn-primary {
@apply py-2 px-4 bg-blue-200;
}
}
*/

View file

@ -1,149 +1,48 @@
<div id="<%= dom_id credit_card_bill %>" class="bg-white rounded-lg shadow-sm border border-gray-200 p-6 space-y-6">
<!-- Bill Header -->
<div class="border-b border-gray-200 pb-6">
<h2 class="text-2xl font-semibold text-gray-900 mb-4"><%= credit_card_bill.description %></h2>
<div id="<%= dom_id credit_card_bill %>">
<p class="my-5">
<strong class="block font-medium mb-1">Description:</strong>
<%= credit_card_bill.description %>
</p>
<!-- Financial Summary Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-4">
<div class="bg-gradient-to-br from-blue-50 to-blue-100 rounded-lg p-4">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/>
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-blue-700">Total Bill</p>
<p class="text-xl font-bold text-blue-900"><%= number_to_currency(credit_card_bill.amount) %></p>
</div>
</div>
</div>
<p class="my-5">
<strong class="block font-medium mb-1">Amount:</strong>
<%= credit_card_bill.amount %>
</p>
<div class="bg-gradient-to-br from-green-50 to-green-100 rounded-lg p-4">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-green-700">Accounted For</p>
<p class="text-xl font-bold text-green-900"><%= number_to_currency(Expense.credit_card_monthly_total) %></p>
</div>
</div>
</div>
<p class="my-5">
<strong class="block font-medium mb-1">Accounted For:</strong>
<%= Expense.credit_card_monthly_total %>
</p>
<div class="bg-gradient-to-br from-<%= credit_card_bill.unpaid > 0 ? 'red' : 'gray' %>-50 to-<%= credit_card_bill.unpaid > 0 ? 'red' : 'gray' %>-100 rounded-lg p-4">
<div class="flex items-center">
<div class="flex-shrink-0">
<% if credit_card_bill.unpaid > 0 %>
<svg class="h-8 w-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
<% else %>
<svg class="h-8 w-8 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M5 13l4 4L19 7"/>
</svg>
<% end %>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-<%= credit_card_bill.unpaid > 0 ? 'red' : 'gray' %>-700">Unpaid Balance</p>
<p class="text-xl font-bold text-<%= credit_card_bill.unpaid > 0 ? 'red' : 'gray' %>-900"><%= number_to_currency(credit_card_bill.unpaid) %></p>
</div>
</div>
</div>
</div>
</div>
<p class="my-5">
<strong class="block font-medium mb-1">Unpaid:</strong>
<%= credit_card_bill.unpaid %>
</p>
<!-- Member Burden Breakdown -->
<div>
<div class="flex items-center justify-between mb-4">
<h3 class="text-lg font-medium text-gray-900">Member Burden Breakdown</h3>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-gray-100 text-gray-800">
Based on unpaid amount: <%= number_to_currency(credit_card_bill.unpaid) %>
</span>
</div>
<!-- Desktop Table -->
<div class="hidden sm:block overflow-x-auto">
<table class="min-w-full bg-gray-50 rounded-lg overflow-hidden">
<thead class="bg-gray-100">
<tr>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Member</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Burden %</th>
<th scope="col" class="px-4 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount Owed</th>
<p class="my-5">
<table class="min-w-full mb-8">
<thead class="border-b">
<tr>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Member</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Burden Percent</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Burden Amount</th>
</tr>
</thead>
<tbody>
<% Member.all.each do |member| %>
<tr class="even:bg-gray-50 border-b">
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= member.name %></td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= member.burden_percent * 100 %>%</td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= number_to_currency(member.burden_amount(total_amount: credit_card_bill.unpaid)) %></td>
</tr>
</thead>
<tbody class="divide-y divide-gray-200">
<% Member.all.each do |member| %>
<tr class="hover:bg-gray-100 transition-colors">
<td class="px-4 py-3">
<div class="flex items-center">
<div class="h-8 w-8 rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center">
<span class="text-white text-sm font-medium"><%= member.name.first.upcase %></span>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900"><%= member.name %></p>
</div>
</div>
</td>
<td class="px-4 py-3">
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
<%= (member.burden_percent * 100).round(1) %>%
</span>
</td>
<td class="px-4 py-3">
<span class="text-sm font-mono font-medium text-gray-900">
<%= number_to_currency(member.burden_amount(total_amount: credit_card_bill.unpaid)) %>
</span>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
</tbody>
</table>
</p>
<!-- Mobile Cards -->
<div class="sm:hidden space-y-3">
<% Member.all.each do |member| %>
<div class="bg-gray-50 rounded-lg p-4">
<div class="flex items-center justify-between mb-2">
<div class="flex items-center">
<div class="h-8 w-8 rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center">
<span class="text-white text-sm font-medium"><%= member.name.first.upcase %></span>
</div>
<p class="ml-3 text-sm font-medium text-gray-900"><%= member.name %></p>
</div>
<span class="inline-flex items-center px-2.5 py-0.5 rounded-full text-xs font-medium bg-blue-100 text-blue-800">
<%= (member.burden_percent * 100).round(1) %>%
</span>
</div>
<div class="text-right">
<p class="text-lg font-mono font-semibold text-gray-900">
<%= number_to_currency(member.burden_amount(total_amount: credit_card_bill.unpaid)) %>
</p>
</div>
</div>
<% end %>
</div>
</div>
<!-- Action Buttons -->
<% if action_name != "show" %>
<div class="border-t border-gray-200 pt-6 flex flex-wrap gap-3">
<%= link_to credit_card_bill, class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
View details
<% end %>
<%= link_to edit_credit_card_bill_path(credit_card_bill), class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit bill
<% end %>
</div>
<%= link_to "Show this credit card bill", credit_card_bill, class: "rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<%= link_to 'Edit this credit card bill', edit_credit_card_bill_path(credit_card_bill), class: "rounded-lg py-3 ml-2 px-5 bg-gray-100 inline-block font-medium" %>
<hr class="mt-6">
<% end %>
</div>

View file

@ -1,75 +1,26 @@
<%= form_with(model: credit_card_bill, class: "space-y-6") do |form| %>
<% if credit_card_bill.errors.any? %>
<div id="error_explanation" class="bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-lg">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-red-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium"><%= pluralize(credit_card_bill.errors.count, "error") %> prohibited this credit card bill from being saved:</h3>
<div class="mt-2 text-sm">
<ul class="list-disc list-inside space-y-1">
<% credit_card_bill.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
</div>
</div>
<%= form_with(model: credit_card_bill, class: "contents") do |form| %>
<% if credit_card_bill.errors.any? %> <div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
<h2><%= pluralize(credit_card_bill.errors.count, "error") %> prohibited this credit_card_bill from being saved:</h2>
<ul>
<% credit_card_bill.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-4 sm:p-6 lg:p-8">
<div class="flex items-center mb-6">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/>
</svg>
</div>
<div class="ml-4">
<h3 class="text-lg font-medium text-gray-900">Credit Card Bill Information</h3>
<p class="text-sm text-gray-500">Enter the details for this credit card statement</p>
</div>
</div>
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
<div class="lg:col-span-2">
<%= form.label :description, class: "block text-sm font-medium text-gray-700" %>
<%= form.text_field :description, class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm", placeholder: "e.g., Chase Sapphire - January 2024" %>
<p class="mt-1 text-sm text-gray-500">A description to identify this credit card bill</p>
</div>
<div>
<%= form.label :amount, class: "block text-sm font-medium text-gray-700" %>
<div class="mt-1 relative rounded-md shadow-sm">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span class="text-gray-500 sm:text-sm">$</span>
</div>
<%= form.text_field :amount, class: "pl-7 pr-3 py-2 block w-full border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm", placeholder: "0.00" %>
</div>
<p class="mt-1 text-sm text-gray-500">Total amount on the credit card statement</p>
</div>
<div class="flex items-center">
<div class="bg-gray-50 rounded-lg p-4 w-full">
<div class="flex items-center">
<svg class="h-6 w-6 text-green-600 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 7h6m0 10v-3m-3 3h.01M9 17h.01M9 14h.01M12 14h.01M15 11h.01M12 11h.01M9 11h.01M7 21h10a2 2 0 002-2V5a2 2 0 00-2-2H7a2 2 0 00-2 2v14a2 2 0 002 2z"/>
</svg>
<div>
<p class="text-sm font-medium text-gray-700">Auto-calculated unpaid amount</p>
<p class="text-xs text-gray-500">Based on total minus accounted expenses</p>
</div>
</div>
</div>
</div>
</div>
<div class="my-5">
<%= form.label :description %>
<%= form.text_field :description, class: "block shadow-sm rounded-md border border-gray-200 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<div class="flex justify-end gap-3">
<%= link_to "Cancel", credit_card_bills_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500" %>
<%= form.submit class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer" %>
<div class="my-5">
<%= form.label :amount %>
<%= form.text_field :amount, class: "block shadow-sm rounded-md border border-gray-200 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<div class="inline">
<%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
</div>
<% end %>

View file

@ -1,39 +1,8 @@
<div class="mx-auto max-w-4xl px-4 py-8">
<div class="mb-8">
<nav class="flex" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to credit_card_bills_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/>
</svg>
Credit Card Bills
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<%= link_to @credit_card_bill.description, @credit_card_bill, class: "ml-1 text-sm font-medium text-gray-700 hover:text-blue-600 md:ml-2" %>
</div>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">Edit</span>
</div>
</li>
</ol>
</nav>
</div>
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900">Editing credit card bill</h1>
<p class="mt-2 text-sm text-gray-600">Update the details for this credit card bill.</p>
</div>
<div class="mx-auto md:w-2/3 w-full">
<h1 class="font-bold text-4xl">Editing credit card bill</h1>
<%= render "form", credit_card_bill: @credit_card_bill %>
<%= link_to "Show this credit card bill", @credit_card_bill, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<%= link_to "Back to credit card bills", credit_card_bills_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>

View file

@ -1,155 +1,14 @@
<div class="w-full px-4 py-8">
<div class="w-full">
<% if notice.present? %>
<div class="mb-6">
<p class="py-3 px-4 bg-green-50 text-green-800 font-medium rounded-lg inline-flex items-center" id="notice">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<%= notice %>
</p>
</div>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
<% end %>
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-8 gap-4">
<div>
<h1 class="font-bold text-3xl sm:text-4xl text-gray-900">Credit Card Bills</h1>
<p class="mt-2 text-sm text-gray-600">Track and manage your credit card expenses and member burdens</p>
</div>
<%= link_to new_credit_card_bill_path, class: "rounded-lg py-2.5 px-4 sm:py-3 sm:px-5 bg-blue-600 hover:bg-blue-700 text-white font-medium transition-colors inline-flex items-center justify-center sm:justify-start" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
New credit card bill
<% end %>
<div class="flex justify-between items-center">
<h1 class="font-bold text-4xl">Credit card bills</h1>
<%= link_to 'New credit card bill', new_credit_card_bill_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %>
</div>
<% if @credit_card_bills.any? %>
<!-- Summary Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">Total Bills</p>
<p class="text-2xl font-semibold text-gray-900"><%= @credit_card_bills.count %></p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1"/>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">Total Amount</p>
<p class="text-2xl font-semibold text-gray-900"><%= number_to_currency(@credit_card_bills.sum(:amount)) %></p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-red-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">Unbudgeted</p>
<p class="text-2xl font-semibold text-gray-900"><%= number_to_currency(@credit_card_bills.sum(&:unpaid)) %></p>
</div>
</div>
</div>
</div>
<!-- Desktop Table View -->
<div class="hidden lg:block overflow-x-auto bg-white rounded-lg shadow">
<table class="min-w-full">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Total Amount</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Unbudgeted</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<% @credit_card_bills.each do |bill| %>
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900"><%= bill.description %></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-gray-900 font-mono"><%= number_to_currency(bill.amount) %></span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-gray-900 font-mono"><%= number_to_currency(bill.unpaid) %></span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<%= link_to "View", bill, class: "text-blue-600 hover:text-blue-900 mr-3" %>
<%= link_to "Edit", edit_credit_card_bill_path(bill), class: "text-indigo-600 hover:text-indigo-900" %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<!-- Mobile Card View -->
<div class="lg:hidden space-y-4">
<% @credit_card_bills.each do |bill| %>
<div class="bg-white rounded-lg shadow p-4 hover:shadow-md transition-shadow">
<div class="flex justify-between items-start mb-3">
<h3 class="text-lg font-medium text-gray-900"><%= bill.description %></h3>
<div class="flex gap-2">
<%= link_to bill, class: "text-blue-600 hover:text-blue-800" do %>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
<% end %>
<%= link_to edit_credit_card_bill_path(bill), class: "text-indigo-600 hover:text-indigo-800" do %>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
<% end %>
</div>
</div>
<div class="grid grid-cols-2 gap-3">
<div class="flex justify-between items-center">
<span class="text-sm text-gray-500">Total Amount</span>
<span class="text-sm font-mono font-medium"><%= number_to_currency(bill.amount) %></span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm text-gray-500">Unbudgeted</span>
<span class="text-sm font-mono font-medium text-gray-900"><%= number_to_currency(bill.unpaid) %></span>
</div>
</div>
</div>
<% end %>
</div>
<% else %>
<div class="text-center py-12 bg-white rounded-lg shadow">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/>
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">No credit card bills</h3>
<p class="mt-1 text-sm text-gray-500">Get started by creating a new credit card bill.</p>
<div class="mt-6">
<%= link_to new_credit_card_bill_path, class: "inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
New credit card bill
<% end %>
</div>
</div>
<% end %>
<div id="credit_card_bills" class="min-w-full">
<%= render @credit_card_bills %>
</div>
</div>

View file

@ -1,31 +1,7 @@
<div class="mx-auto max-w-4xl px-4 py-8">
<div class="mb-8">
<nav class="flex" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to credit_card_bills_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/>
</svg>
Credit Card Bills
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">New credit card bill</span>
</div>
</li>
</ol>
</nav>
</div>
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900">New credit card bill</h1>
<p class="mt-2 text-sm text-gray-600">Add a new credit card bill to track member expenses and burden distribution.</p>
</div>
<div class="mx-auto md:w-2/3 w-full">
<h1 class="font-bold text-4xl">New credit card bill</h1>
<%= render "form", credit_card_bill: @credit_card_bill %>
<%= link_to 'Back to credit card bills', credit_card_bills_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>

View file

@ -1,60 +1,15 @@
<div class="mx-auto max-w-6xl px-4 py-8">
<% if notice.present? %>
<div class="mb-6">
<p class="py-3 px-4 bg-green-50 text-green-800 font-medium rounded-lg inline-flex items-center" id="notice">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<%= notice %>
</p>
<div class="mx-auto md:w-2/3 w-full flex">
<div class="mx-auto">
<% if notice.present? %>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
<% end %>
<%= render @credit_card_bill %>
<%= link_to 'Edit this credit card bill', edit_credit_card_bill_path(@credit_card_bill), class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<div class="inline-block ml-2">
<%= button_to 'Destroy this credit card bill', credit_card_bill_path(@credit_card_bill), method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
</div>
<% end %>
<div class="mb-6">
<nav class="flex" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to credit_card_bills_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M3 10h18M7 15h1m4 0h1m-7 4h12a3 3 0 003-3V8a3 3 0 00-3-3H6a3 3 0 00-3 3v8a3 3 0 003 3z"/>
</svg>
Credit Card Bills
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2"><%= @credit_card_bill.description %></span>
</div>
</li>
</ol>
</nav>
</div>
<%= render @credit_card_bill %>
<div class="mt-8 flex flex-wrap gap-3">
<%= link_to edit_credit_card_bill_path(@credit_card_bill), class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit credit card bill
<% end %>
<%= button_to credit_card_bill_path(@credit_card_bill), method: :delete, form: { data: { turbo_confirm: "Are you sure you want to delete this credit card bill?" } }, class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
Delete credit card bill
<% end %>
<%= link_to credit_card_bills_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
</svg>
Back to credit card bills
<% end %>
<%= link_to 'Back to credit card bills', credit_card_bills_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>
</div>

View file

@ -1,110 +1,70 @@
<div class="flex flex-wrap">
<div class="m-4 lg:m-8">
<div class="overflow-x-auto mb-8">
<table class="min-w-full">
<thead class="border-b bg-gray-50">
<tr>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left">Member</th>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left">Burden Percent</th>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left">Burden Amount</th>
<div class="m-8">
<table class="min-w-full mb-8">
<thead class="border-b">
<tr>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Member</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Burden Percent</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Burden Amount</th>
</tr>
</thead>
<tbody>
<% @members.each do |member| %>
<tr class="even:bg-gray-50 border-b">
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= member.name %></td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= member.burden_percent * 100 %>%</td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= number_to_currency(member.burden_amount) %></td>
</tr>
</thead>
<tbody>
<% @members.each do |member| %>
<tr class="even:bg-gray-50 border-b hover:bg-gray-100">
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<div class="font-medium"><%= member.name %></div>
</td>
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800">
<%= member.burden_percent * 100 %>%
</span>
</td>
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<span class="font-mono"><%= number_to_currency(member.burden_amount) %></span>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
</tbody>
</table>
<div class="overflow-x-auto">
<table class="min-w-full">
<thead class="border-b bg-gray-50">
<tr>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left">Member</th>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left">For</th>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left">Amount</th>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left">Included</th>
<table class="min-w-full">
<thead class="border-b">
<tr>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Member</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">For</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Amount</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Included</th>
</tr>
</thead>
<tbody>
<% @incomes.each do |income| %>
<tr class="even:bg-gray-50 border-b">
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= income.member.name %></td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= income.description %></td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= number_to_currency(income.amount) %></td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= income.included %></td>
</tr>
</thead>
<tbody>
<% @incomes.each do |income| %>
<tr class="even:bg-gray-50 border-b hover:bg-gray-100">
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<div class="font-medium"><%= income.member.name %></div>
</td>
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<%= income.description %>
</td>
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<span class="font-mono"><%= number_to_currency(income.amount) %></span>
</td>
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<% if income.included %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">yes</span>
<% else %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-red-100 text-red-800">no</span>
<% end %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
</tbody>
</table>
</div>
<div class="m-4 lg:m-8">
<div class="overflow-x-auto">
<table class="min-w-full">
<thead class="border-b bg-gray-50">
<tr>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left">Bill</th>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left">Payment</th>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left">Monthly</th>
<th scope="col" class="text-xs sm:text-sm font-medium text-gray-900 px-3 sm:px-6 py-2 sm:py-4 text-left"></th>
<div class="m-8">
<table class="min-w-full">
<thead class="border-b">
<tr>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Bill</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Payment</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Period</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Montly</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">credit_card</th>
<th scope="col" class="text-sm font-medium text-gray-900 px-6 py-4 text-left">Estimated</th>
</tr>
</thead>
<tbody>
<% @expenses.each do |expense| %>
<tr class="even:bg-gray-50 border-b">
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= expense.description %></td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= number_to_currency(expense.payment) %></td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= expense.period %></td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= number_to_currency(expense.monthly) %></td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= expense.credit_card %></td>
<td class="text-sm text-gray-900 font-light px-6 py-4 whitespace-nowrap"><%= expense.estimated %></td>
</tr>
</thead>
<tbody>
<% @expenses.each do |expense| %>
<tr class="even:bg-gray-50 border-b hover:bg-gray-100">
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<div class="font-medium"><%= expense.description %></div>
</td>
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<span class="font-mono"><%= number_to_currency(expense.payment) %></span>
</td>
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<span class="font-mono"><%= number_to_currency(expense.monthly) %></span>
</td>
<td class="text-xs sm:text-sm text-gray-900 font-light px-3 sm:px-6 py-2 sm:py-4">
<div class="flex flex-wrap gap-1">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-800">
<%= expense.period.downcase %>
</span>
<% if expense.credit_card %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800">credit card</span>
<% end %>
<% if expense.estimated %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800">estimated</span>
<% end %>
</div>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<% end %>
</tbody>
</table>
</div>
</div>

View file

@ -1,80 +1,32 @@
<div id="<%= dom_id expense %>" class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<div class="mb-6">
<h2 class="text-2xl font-semibold text-gray-900 mb-4"><%= expense.description %></h2>
<div id="<%= dom_id expense %>">
<p class="my-5">
<strong class="block font-medium mb-1">Description:</strong>
<%= expense.description %>
</p>
<div class="grid grid-cols-1 md:grid-cols-2 gap-4">
<div class="bg-gray-50 rounded-lg p-4">
<p class="text-sm text-gray-600 mb-1">Payment Amount</p>
<p class="text-xl font-mono font-semibold text-gray-900"><%= number_to_currency(expense.payment) %></p>
</div>
<p class="my-5">
<strong class="block font-medium mb-1">Payment:</strong>
<%= expense.payment %>
</p>
<div class="bg-gray-50 rounded-lg p-4">
<p class="text-sm text-gray-600 mb-1">Monthly Amount</p>
<p class="text-xl font-mono font-semibold text-gray-900"><%= number_to_currency(expense.monthly) %></p>
</div>
</div>
</div>
<p class="my-5">
<strong class="block font-medium mb-1">Period:</strong>
<%= expense.period %>
</p>
<div class="border-t border-gray-200 pt-4">
<p class="text-sm text-gray-600 mb-3">Details</p>
<div class="flex flex-wrap gap-2">
<span class="inline-flex items-center px-3 py-1 rounded-md text-sm font-medium bg-purple-100 text-purple-800">
<svg class="w-4 h-4 mr-1.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm1-12a1 1 0 10-2 0v4a1 1 0 00.293.707l2.828 2.829a1 1 0 101.415-1.415L11 9.586V6z" clip-rule="evenodd"/>
</svg>
<%= expense.period.downcase %>
</span>
<p class="my-5">
<strong class="block font-medium mb-1">Credit card:</strong>
<%= expense.credit_card %>
</p>
<% if expense.credit_card %>
<span class="inline-flex items-center px-3 py-1 rounded-md text-sm font-medium bg-blue-100 text-blue-800">
<svg class="w-4 h-4 mr-1.5" fill="currentColor" viewBox="0 0 20 20">
<path d="M4 4a2 2 0 00-2 2v1h16V6a2 2 0 00-2-2H4z"/>
<path fill-rule="evenodd" d="M18 9H2v5a2 2 0 002 2h12a2 2 0 002-2V9zM4 13a1 1 0 011-1h1a1 1 0 110 2H5a1 1 0 01-1-1zm5-1a1 1 0 100 2h1a1 1 0 100-2H9z" clip-rule="evenodd"/>
</svg>
credit card
</span>
<% else %>
<span class="inline-flex items-center px-3 py-1 rounded-md text-sm font-medium bg-gray-100 text-gray-700">
<svg class="w-4 h-4 mr-1.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4 4a2 2 0 00-2 2v4a2 2 0 002 2V6h10a2 2 0 00-2-2H4zm2 6a2 2 0 012-2h8a2 2 0 012 2v4a2 2 0 01-2 2H8a2 2 0 01-2-2v-4zm6 4a2 2 0 100-4 2 2 0 000 4z" clip-rule="evenodd"/>
</svg>
bank account
</span>
<% end %>
<% if expense.estimated %>
<span class="inline-flex items-center px-3 py-1 rounded-md text-sm font-medium bg-yellow-100 text-yellow-800">
<svg class="w-4 h-4 mr-1.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z" clip-rule="evenodd"/>
</svg>
estimated
</span>
<% else %>
<span class="inline-flex items-center px-3 py-1 rounded-md text-sm font-medium bg-green-100 text-green-800">
<svg class="w-4 h-4 mr-1.5" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
actual
</span>
<% end %>
</div>
</div>
<p class="my-5">
<strong class="block font-medium mb-1">Estimated:</strong>
<%= expense.estimated %>
</p>
<% if action_name != "show" %>
<div class="mt-6 pt-6 border-t border-gray-200 flex gap-3">
<%= link_to expense, class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
View details
<% end %>
<%= link_to edit_expense_path(expense), class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-indigo-500" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit expense
<% end %>
</div>
<%= link_to "Show this expense", expense, class: "rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<%= link_to 'Edit this expense', edit_expense_path(expense), class: "rounded-lg py-3 ml-2 px-5 bg-gray-100 inline-block font-medium" %>
<hr class="mt-6">
<% end %>
</div>

View file

@ -1,103 +1,47 @@
<%= form_with(model: expense, class: "space-y-6") do |form| %>
<%= form_with(model: expense, class: "contents") do |form| %>
<% if expense.errors.any? %>
<div id="error_explanation" class="bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-lg">
<div class="flex">
<div class="flex-shrink-0">
<svg class="h-5 w-5 text-red-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zM8.707 7.293a1 1 0 00-1.414 1.414L8.586 10l-1.293 1.293a1 1 0 101.414 1.414L10 11.414l1.293 1.293a1 1 0 001.414-1.414L11.414 10l1.293-1.293a1 1 0 00-1.414-1.414L10 8.586 8.707 7.293z" clip-rule="evenodd"/>
</svg>
</div>
<div class="ml-3">
<h3 class="text-sm font-medium"><%= pluralize(expense.errors.count, "error") %> prohibited this expense from being saved:</h3>
<div class="mt-2 text-sm">
<ul class="list-disc list-inside space-y-1">
<% expense.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
</div>
</div>
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
<h2><%= pluralize(expense.errors.count, "error") %> prohibited this expense from being saved:</h2>
<ul>
<% expense.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-4 sm:p-6 lg:p-8">
<h3 class="text-lg font-medium text-gray-900 mb-6">Expense Information</h3>
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
<div class="lg:col-span-2">
<%= form.label :description, class: "block text-sm font-medium text-gray-700" %>
<%= form.text_field :description, class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm", placeholder: "e.g., Netflix subscription" %>
<p class="mt-1 text-sm text-gray-500">A brief description of the expense</p>
</div>
<div>
<%= form.label :payment, class: "block text-sm font-medium text-gray-700" %>
<div class="mt-1 relative rounded-md shadow-sm">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span class="text-gray-500 sm:text-sm">$</span>
</div>
<%= form.text_field :payment, class: "pl-7 pr-3 py-2 block w-full border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm", placeholder: "0.00" %>
</div>
<p class="mt-1 text-sm text-gray-500">The payment amount for this billing period</p>
</div>
<div>
<%= form.label :period, class: "block text-sm font-medium text-gray-700" %>
<%=
form.select :period,
options_for_select(expense_periods, expense.period),
{},
class: "mt-1 block w-full px-3 py-2 border border-gray-300 rounded-md shadow-sm focus:outline-none focus:ring-1 focus:ring-blue-500 focus:border-blue-500 sm:text-sm"
%>
<p class="mt-1 text-sm text-gray-500">How often this expense is billed</p>
</div>
</div>
<div class="my-5">
<%= form.label :description %>
<%= form.text_field :description, class: "block shadow-sm rounded-md border border-gray-200 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-4 sm:p-6 lg:p-8">
<h3 class="text-lg font-medium text-gray-900 mb-6">Payment Details</h3>
<div class="space-y-4">
<div class="flex items-center justify-between">
<div class="flex-grow">
<label for="expense_credit_card" class="text-sm font-medium text-gray-700">Credit Card Payment</label>
<p class="text-sm text-gray-500">This expense is paid by credit card</p>
</div>
<div class="flex-shrink-0 ml-4">
<%= form.check_box :credit_card, class: "toggle-checkbox sr-only" %>
<label for="expense_credit_card" class="toggle-label relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 bg-gray-200">
<span class="toggle-switch pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out translate-x-0"></span>
</label>
</div>
</div>
<div class="flex items-center justify-between">
<div class="flex-grow">
<label for="expense_estimated" class="text-sm font-medium text-gray-700">Estimated Amount</label>
<p class="text-sm text-gray-500">This amount is an estimate</p>
</div>
<div class="flex-shrink-0 ml-4">
<%= form.check_box :estimated, class: "toggle-checkbox sr-only" %>
<label for="expense_estimated" class="toggle-label relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus:outline-none focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 bg-gray-200">
<span class="toggle-switch pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out translate-x-0"></span>
</label>
</div>
</div>
</div>
<div class="my-5">
<%= form.label :payment %>
<%= form.text_field :payment, class: "block shadow-sm rounded-md border border-gray-200 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<style>
.toggle-checkbox:checked + .toggle-label {
background-color: rgb(59, 130, 246);
}
.toggle-checkbox:checked + .toggle-label .toggle-switch {
transform: translateX(1.25rem);
}
</style>
<div class="my-5">
<%= form.label :period %>
<%=
form.select :period,
options_for_select(expense_periods, expense.period),
{},
class: "block shadow-sm rounded-md border border-gray-200 outline-hidden px-3 py-2 mt-2 w-full"
%>
</div>
<div class="flex justify-end gap-3">
<%= link_to "Cancel", expenses_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500" %>
<%= form.submit class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer" %>
<div class="my-5">
<%= form.label :credit_card %>
<%= form.check_box :credit_card, class: "block mt-2 h-5 w-5" %>
</div>
<div class="my-5">
<%= form.label :estimated %>
<%= form.check_box :estimated, class: "block mt-2 h-5 w-5" %>
</div>
<div class="inline">
<%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
</div>
<% end %>

View file

@ -1,39 +1,8 @@
<div class="mx-auto max-w-4xl px-4 py-8">
<div class="mb-8">
<nav class="flex" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to expenses_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
</svg>
Expenses
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<%= link_to @expense.description, @expense, class: "ml-1 text-sm font-medium text-gray-700 hover:text-blue-600 md:ml-2" %>
</div>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">Edit</span>
</div>
</li>
</ol>
</nav>
</div>
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900">Editing expense</h1>
<p class="mt-2 text-sm text-gray-600">Update the details for this expense.</p>
</div>
<div class="mx-auto md:w-2/3 w-full">
<h1 class="font-bold text-4xl">Editing expense</h1>
<%= render "form", expense: @expense %>
<%= link_to "Show this expense", @expense, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<%= link_to "Back to expenses", expenses_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>

View file

@ -1,130 +1,14 @@
<div class="w-full px-4 py-8">
<div class="w-full">
<% if notice.present? %>
<div class="mb-6">
<p class="py-3 px-4 bg-green-50 text-green-800 font-medium rounded-lg inline-flex items-center" id="notice">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<%= notice %>
</p>
</div>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
<% end %>
<div class="flex justify-between items-center mb-8">
<h1 class="font-bold text-3xl sm:text-4xl text-gray-900">Expenses</h1>
<%= link_to new_expense_path, class: "rounded-lg py-2.5 px-4 sm:py-3 sm:px-5 bg-blue-600 hover:bg-blue-700 text-white font-medium transition-colors flex items-center" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
New expense
<% end %>
<div class="flex justify-between items-center">
<h1 class="font-bold text-4xl">Expenses</h1>
<%= link_to 'New expense', new_expense_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %>
</div>
<% if @expenses.any? %>
<div class="hidden lg:block overflow-x-auto bg-white rounded-lg shadow">
<table class="min-w-full">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Payment</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Monthly</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Details</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<% @expenses.each do |expense| %>
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900"><%= expense.description %></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-gray-900 font-mono"><%= number_to_currency(expense.payment) %></span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-gray-900 font-mono"><%= number_to_currency(expense.monthly) %></span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex gap-1">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-800">
<%= expense.period.downcase %>
</span>
<% if expense.credit_card %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800">credit card</span>
<% end %>
<% if expense.estimated %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800">estimated</span>
<% end %>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<%= link_to "View", expense, class: "text-blue-600 hover:text-blue-900 mr-3" %>
<%= link_to "Edit", edit_expense_path(expense), class: "text-indigo-600 hover:text-indigo-900" %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<div class="lg:hidden space-y-4">
<% @expenses.each do |expense| %>
<div class="bg-white rounded-lg shadow p-4 hover:shadow-md transition-shadow">
<div class="flex justify-between items-start mb-3">
<h3 class="text-lg font-medium text-gray-900"><%= expense.description %></h3>
<div class="flex gap-2">
<%= link_to expense, class: "text-blue-600 hover:text-blue-800" do %>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
<% end %>
<%= link_to edit_expense_path(expense), class: "text-indigo-600 hover:text-indigo-800" do %>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
<% end %>
</div>
</div>
<div class="grid grid-cols-2 gap-2 mb-3">
<div>
<p class="text-xs text-gray-500">Payment</p>
<p class="text-sm font-mono font-medium"><%= number_to_currency(expense.payment) %></p>
</div>
<div>
<p class="text-xs text-gray-500">Monthly</p>
<p class="text-sm font-mono font-medium"><%= number_to_currency(expense.monthly) %></p>
</div>
</div>
<div class="flex flex-wrap gap-1">
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-purple-100 text-purple-800">
<%= expense.period.downcase %>
</span>
<% if expense.credit_card %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-blue-100 text-blue-800">credit card</span>
<% end %>
<% if expense.estimated %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-yellow-100 text-yellow-800">estimated</span>
<% end %>
</div>
</div>
<% end %>
</div>
<% else %>
<div class="text-center py-12 bg-white rounded-lg shadow">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 5H7a2 2 0 00-2 2v12a2 2 0 002 2h10a2 2 0 002-2V7a2 2 0 00-2-2h-2M9 5a2 2 0 002 2h2a2 2 0 002-2M9 5a2 2 0 012-2h2a2 2 0 012 2"/>
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">No expenses</h3>
<p class="mt-1 text-sm text-gray-500">Get started by creating a new expense.</p>
<div class="mt-6">
<%= link_to new_expense_path, class: "inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
New expense
<% end %>
</div>
</div>
<% end %>
<div id="expenses" class="min-w-full">
<%= render @expenses %>
</div>
</div>

View file

@ -1,31 +1,7 @@
<div class="mx-auto max-w-4xl px-4 py-8">
<div class="mb-8">
<nav class="flex" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to expenses_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
</svg>
Expenses
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">New expense</span>
</div>
</li>
</ol>
</nav>
</div>
<div class="mb-8">
<h1 class="text-3xl font-bold text-gray-900">New expense</h1>
<p class="mt-2 text-sm text-gray-600">Add a new recurring expense to track your spending.</p>
</div>
<div class="mx-auto md:w-2/3 w-full">
<h1 class="font-bold text-4xl">New expense</h1>
<%= render "form", expense: @expense %>
<%= link_to 'Back to expenses', expenses_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>

View file

@ -1,60 +1,15 @@
<div class="mx-auto max-w-4xl px-4 py-8">
<% if notice.present? %>
<div class="mb-6">
<p class="py-3 px-4 bg-green-50 text-green-800 font-medium rounded-lg inline-flex items-center" id="notice">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<%= notice %>
</p>
<div class="mx-auto md:w-2/3 w-full flex">
<div class="mx-auto">
<% if notice.present? %>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
<% end %>
<%= render @expense %>
<%= link_to 'Edit this expense', edit_expense_path(@expense), class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<div class="inline-block ml-2">
<%= button_to 'Destroy this expense', expense_path(@expense), method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
</div>
<% end %>
<div class="mb-6">
<nav class="flex" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to expenses_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
</svg>
Expenses
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2"><%= @expense.description %></span>
</div>
</li>
</ol>
</nav>
</div>
<%= render @expense %>
<div class="mt-8 flex flex-wrap gap-3">
<%= link_to edit_expense_path(@expense), class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit expense
<% end %>
<%= button_to expense_path(@expense), method: :delete, form: { data: { turbo_confirm: "Are you sure you want to delete this expense?" } }, class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-red-600 hover:bg-red-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-red-500" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
Delete expense
<% end %>
<%= link_to expenses_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-gray-500" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
</svg>
Back to expenses
<% end %>
<%= link_to 'Back to expenses', expenses_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>
</div>

View file

@ -1,13 +1,9 @@
<%= form_with(model: income, class: "space-y-8") do |form| %>
<%= form_with(model: income, class: "contents") do |form| %>
<% if income.errors.any? %>
<div id="error_explanation" class="bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-lg">
<div class="flex items-center mb-2">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
<h3 class="text-sm font-medium"><%= pluralize(income.errors.count, "error") %> prohibited this income from being saved:</h3>
</div>
<ul class="list-disc list-inside text-sm space-y-1">
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
<h2><%= pluralize(income.errors.count, "error") %> prohibited this income from being saved:</h2>
<ul>
<% income.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
@ -15,74 +11,32 @@
</div>
<% end %>
<!-- Basic Information -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h3 class="text-lg font-medium text-gray-900 mb-6">Basic Information</h3>
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
<div class="md:col-span-2">
<%= form.label :description, class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= form.text_field :description,
class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm",
placeholder: "Enter income description..." %>
</div>
<div>
<%= form.label :amount, class: "block text-sm font-medium text-gray-700 mb-2" %>
<div class="relative">
<div class="absolute inset-y-0 left-0 pl-3 flex items-center pointer-events-none">
<span class="text-gray-500 sm:text-sm">$</span>
</div>
<%= form.number_field :amount,
class: "block w-full pl-7 pr-3 py-2 rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm font-mono",
placeholder: "0.00",
step: "0.01",
min: "0" %>
</div>
</div>
<div>
<%= form.label :member_id, "Member", class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= form.select :member_id,
options_for_select(members, income.member_id),
{ prompt: "Select a member..." },
class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm" %>
</div>
</div>
<div class="my-5">
<%= form.label :description %>
<%= form.text_field :description, class: "block shadow-sm rounded-md border border-gray-200 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<!-- Budget Settings -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h3 class="text-lg font-medium text-gray-900 mb-6">Budget Settings</h3>
<div class="flex items-center justify-between">
<div class="flex-1">
<%= form.label :included, "Include in Budget", class: "block text-sm font-medium text-gray-700" %>
<p class="text-sm text-gray-500 mt-1">Whether this income should be included in budget calculations</p>
</div>
<div class="ml-6">
<%= form.check_box :included, class: "toggle-checkbox sr-only" %>
<label for="income_included" class="toggle-label relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus-within:outline-none focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 bg-gray-200">
<span class="toggle-switch pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out translate-x-0"></span>
</label>
</div>
</div>
<div class="my-5">
<%= form.label :included %>
<%= form.check_box :included, class: "block mt-2 h-5 w-5" %>
</div>
<!-- Form Actions -->
<div class="flex justify-end space-x-3 pt-6 border-t border-gray-200">
<%= link_to incomes_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" do %>
Cancel
<% end %>
<%= form.submit class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer" %>
<div class="my-5">
<%= form.label :amount %>
<%= form.text_field :amount, class: "block shadow-sm rounded-md border border-gray-200 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<style>
.toggle-checkbox:checked + .toggle-label {
background-color: #3B82F6;
}
.toggle-checkbox:checked + .toggle-label .toggle-switch {
transform: translateX(1.25rem);
}
</style>
<div class="my-5">
<%= form.label :member_id %>
<%=
form.select :member_id,
options_for_select(members, income.member_id),
{},
class: "block shadow-sm rounded-md border border-gray-200 outline-hidden px-3 py-2 mt-2 w-full"
%>
</div>
<div class="inline">
<%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
</div>
<% end %>

View file

@ -1,49 +1,27 @@
<div id="<%= dom_id income %>" class="bg-white rounded-lg shadow hover:shadow-md transition-shadow">
<div class="p-6">
<div class="flex justify-between items-start mb-4">
<h3 class="text-lg font-semibold text-gray-900"><%= income.description %></h3>
<% if income.included %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">included</span>
<% else %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800">excluded</span>
<% end %>
</div>
<div id="<%= dom_id income %>">
<p class="my-5">
<strong class="block font-medium mb-1">Description:</strong>
<%= income.description %>
</p>
<div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-6">
<div class="flex items-center">
<div class="h-10 w-10 rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center">
<span class="text-white text-sm font-medium"><%= income.member.name.first.upcase %></span>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900"><%= income.member.name %></p>
<p class="text-xs text-gray-500">Member</p>
</div>
</div>
<p class="my-5">
<strong class="block font-medium mb-1">Included:</strong>
<%= income.included %>
</p>
<div class="flex items-center justify-end sm:justify-start">
<div class="text-right sm:text-left">
<p class="text-2xl font-bold text-gray-900 font-mono"><%= number_to_currency(income.amount) %></p>
<p class="text-xs text-gray-500">Amount</p>
</div>
</div>
</div>
<p class="my-5">
<strong class="block font-medium mb-1">Amount:</strong>
<%= income.amount %>
</p>
<% if action_name != "show" %>
<div class="flex gap-2 pt-4 border-t border-gray-200">
<%= link_to income, class: "flex-1 inline-flex items-center justify-center px-4 py-2 text-sm font-medium text-blue-600 bg-blue-50 hover:bg-blue-100 rounded-md transition-colors" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
View
<% end %>
<%= link_to edit_income_path(income), class: "flex-1 inline-flex items-center justify-center px-4 py-2 text-sm font-medium text-indigo-600 bg-indigo-50 hover:bg-indigo-100 rounded-md transition-colors" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit
<% end %>
</div>
<% end %>
</div>
<p class="my-5">
<strong class="block font-medium mb-1">Member:</strong>
<%= income.member_id %>
</p>
<% if action_name != "show" %>
<%= link_to "Show this income", income, class: "rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<%= link_to 'Edit this income', edit_income_path(income), class: "rounded-lg py-3 ml-2 px-5 bg-gray-100 inline-block font-medium" %>
<hr class="mt-6">
<% end %>
</div>

View file

@ -1,42 +1,8 @@
<div class="w-full px-4 py-8">
<!-- Breadcrumb -->
<nav class="flex mb-6" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to incomes_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
</svg>
Incomes
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<%= link_to @income, class: "ml-1 text-sm font-medium text-gray-700 hover:text-blue-600 md:ml-2" do %>
<%= @income.description %>
<% end %>
</div>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">Edit</span>
</div>
</li>
</ol>
</nav>
<div class="mx-auto md:w-2/3 w-full">
<h1 class="font-bold text-4xl">Editing income</h1>
<div class="max-w-4xl mx-auto">
<div class="mb-8">
<h1 class="font-bold text-3xl sm:text-4xl text-gray-900">Edit Income</h1>
<p class="mt-2 text-sm text-gray-600">Update the details for "<%= @income.description %>"</p>
</div>
<%= render "form", income: @income %>
<%= render "form", income: @income %>
</div>
<%= link_to "Show this income", @income, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<%= link_to "Back to incomes", incomes_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>

View file

@ -1,193 +1,14 @@
<div class="w-full px-4 py-8">
<div class="w-full">
<% if notice.present? %>
<div class="mb-6">
<p class="py-3 px-4 bg-green-50 text-green-800 font-medium rounded-lg inline-flex items-center" id="notice">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<%= notice %>
</p>
</div>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
<% end %>
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-8 gap-4">
<div>
<h1 class="font-bold text-3xl sm:text-4xl text-gray-900">Incomes</h1>
<p class="mt-2 text-sm text-gray-600">Track member income sources and budget allocations</p>
</div>
<%= link_to new_income_path, class: "rounded-lg py-2.5 px-4 sm:py-3 sm:px-5 bg-blue-600 hover:bg-blue-700 text-white font-medium transition-colors inline-flex items-center justify-center sm:justify-start" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
New income
<% end %>
<div class="flex justify-between items-center">
<h1 class="font-bold text-4xl">Incomes</h1>
<%= link_to 'New income', new_income_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %>
</div>
<% if @incomes.any? %>
<!-- Summary Cards -->
<div class="grid grid-cols-1 md:grid-cols-4 gap-6 mb-8">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">Total Sources</p>
<p class="text-2xl font-semibold text-gray-900"><%= @incomes.count %></p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 8c-1.657 0-3 .895-3 2s1.343 2 3 2 3 .895 3 2-1.343 2-3 2m0-8c1.11 0 2.08.402 2.599 1M12 8V7m0 1v8m0 0v1m0-1c-1.11 0-2.08-.402-2.599-1"/>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">Total Income</p>
<p class="text-2xl font-semibold text-gray-900"><%= number_to_currency(@incomes.sum(:amount)) %></p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-indigo-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">Included</p>
<p class="text-2xl font-semibold text-gray-900"><%= number_to_currency(@incomes.where(included: true).sum(:amount)) %></p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L5.636 5.636"/>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">Excluded</p>
<p class="text-2xl font-semibold text-gray-900"><%= number_to_currency(@incomes.where(included: false).sum(:amount)) %></p>
</div>
</div>
</div>
</div>
<!-- Desktop Table View -->
<div class="hidden lg:block overflow-x-auto bg-white rounded-lg shadow">
<table class="min-w-full">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Description</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Member</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Amount</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Status</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<% @incomes.each do |income| %>
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap">
<div class="text-sm font-medium text-gray-900"><%= income.description %></div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="h-8 w-8 rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center">
<span class="text-white text-sm font-medium"><%= income.member.name.first.upcase %></span>
</div>
<div class="ml-3">
<p class="text-sm font-medium text-gray-900"><%= income.member.name %></p>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span class="text-sm text-gray-900 font-mono"><%= number_to_currency(income.amount) %></span>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<% if income.included %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">included</span>
<% else %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800">excluded</span>
<% end %>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<%= link_to "View", income, class: "text-blue-600 hover:text-blue-900 mr-3" %>
<%= link_to "Edit", edit_income_path(income), class: "text-indigo-600 hover:text-indigo-900" %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<!-- Mobile Card View -->
<div class="lg:hidden space-y-4">
<% @incomes.each do |income| %>
<div class="bg-white rounded-lg shadow p-4 hover:shadow-md transition-shadow">
<div class="flex justify-between items-start mb-3">
<h3 class="text-lg font-medium text-gray-900"><%= income.description %></h3>
<div class="flex gap-2">
<%= link_to income, class: "text-blue-600 hover:text-blue-800" do %>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
<% end %>
<%= link_to edit_income_path(income), class: "text-indigo-600 hover:text-indigo-800" do %>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
<% end %>
</div>
</div>
<div class="grid grid-cols-1 gap-3">
<div class="flex items-center justify-between">
<div class="flex items-center">
<div class="h-8 w-8 rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center">
<span class="text-white text-sm font-medium"><%= income.member.name.first.upcase %></span>
</div>
<span class="ml-3 text-sm font-medium text-gray-900"><%= income.member.name %></span>
</div>
<span class="text-lg font-mono font-semibold text-gray-900"><%= number_to_currency(income.amount) %></span>
</div>
<div class="flex justify-between items-center">
<span class="text-sm text-gray-500">Budget Status</span>
<% if income.included %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">included</span>
<% else %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800">excluded</span>
<% end %>
</div>
</div>
</div>
<% end %>
</div>
<% else %>
<div class="text-center py-12 bg-white rounded-lg shadow">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 19v-6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2a2 2 0 002-2zm0 0V9a2 2 0 012-2h2a2 2 0 012 2v10m-6 0a2 2 0 002 2h2a2 2 0 002-2m0 0V5a2 2 0 012-2h2a2 2 0 012 2v14a2 2 0 01-2 2h-2a2 2 0 01-2-2z"/>
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">No income sources</h3>
<p class="mt-1 text-sm text-gray-500">Get started by adding your first income source.</p>
<div class="mt-6">
<%= link_to new_income_path, class: "inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
New income
<% end %>
</div>
</div>
<% end %>
<div id="incomes" class="min-w-full">
<%= render @incomes %>
</div>
</div>

View file

@ -1,32 +1,7 @@
<div class="w-full px-4 py-8">
<!-- Breadcrumb -->
<nav class="flex mb-6" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to incomes_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
</svg>
Incomes
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">New Income</span>
</div>
</li>
</ol>
</nav>
<div class="mx-auto md:w-2/3 w-full">
<h1 class="font-bold text-4xl">New income</h1>
<div class="max-w-4xl mx-auto">
<div class="mb-8">
<h1 class="font-bold text-3xl sm:text-4xl text-gray-900">New Income</h1>
<p class="mt-2 text-sm text-gray-600">Add a new income source to your budget</p>
</div>
<%= render "form", income: @income %>
<%= render "form", income: @income %>
</div>
<%= link_to 'Back to incomes', incomes_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>

View file

@ -1,110 +1,15 @@
<div class="w-full px-4 py-8">
<% if notice.present? %>
<div class="mb-6">
<p class="py-3 px-4 bg-green-50 text-green-800 font-medium rounded-lg inline-flex items-center" id="notice">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<%= notice %>
</p>
</div>
<% end %>
<!-- Breadcrumb -->
<nav class="flex mb-6" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to incomes_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
</svg>
Incomes
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2"><%= @income.description %></span>
</div>
</li>
</ol>
</nav>
<div class="max-w-4xl mx-auto">
<div class="mb-8">
<div class="flex justify-between items-start mb-4">
<div>
<h1 class="font-bold text-3xl sm:text-4xl text-gray-900"><%= @income.description %></h1>
<p class="mt-2 text-sm text-gray-600">Income source details and budget allocation</p>
</div>
<% if @income.included %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
included
</span>
<% else %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
excluded
</span>
<% end %>
</div>
</div>
<!-- Income Details Card -->
<div class="bg-white rounded-lg shadow-lg overflow-hidden mb-8">
<div class="px-6 py-8">
<div class="grid grid-cols-1 md:grid-cols-2 gap-8">
<!-- Member Info -->
<div class="flex items-center space-x-4">
<div class="h-16 w-16 rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center">
<span class="text-white text-xl font-medium"><%= @income.member.name.first.upcase %></span>
</div>
<div>
<h3 class="text-lg font-semibold text-gray-900">Member</h3>
<p class="text-gray-600"><%= @income.member.name %></p>
</div>
</div>
<!-- Amount -->
<div class="text-center md:text-right">
<h3 class="text-lg font-semibold text-gray-900 mb-2">Amount</h3>
<p class="text-4xl font-bold text-gray-900 font-mono"><%= number_to_currency(@income.amount) %></p>
</div>
</div>
</div>
</div>
<!-- Actions -->
<div class="flex flex-wrap gap-3">
<%= link_to edit_income_path(@income), class: "inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit Income
<% end %>
<%= button_to income_path(@income), method: :delete,
class: "inline-flex items-center px-4 py-2 bg-red-600 hover:bg-red-700 text-white font-medium rounded-lg transition-colors",
confirm: "Are you sure you want to delete this income source?" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
Delete
<% end %>
<%= link_to incomes_path, class: "inline-flex items-center px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium rounded-lg transition-colors" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
</svg>
Back to Incomes
<% end %>
<div class="mx-auto md:w-2/3 w-full flex">
<div class="mx-auto">
<% if notice.present? %>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
<% end %>
<%= render @income %>
<%= link_to 'Edit this income', edit_income_path(@income), class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<div class="inline-block ml-2">
<%= button_to 'Destroy this income', income_path(@income), method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
</div>
<%= link_to 'Back to incomes', incomes_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>
</div>

View file

@ -5,71 +5,27 @@
<meta name="viewport" content="width=device-width,initial-scale=1">
<%= csrf_meta_tags %>
<%= csp_meta_tag %>
<%= stylesheet_link_tag "tailwind", "inter-font", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "tailwind", "data-turbo-track": "reload" %>
<%= stylesheet_link_tag "application", "data-turbo-track": "reload" %>
<%= javascript_importmap_tags %>
</head>
<body>
<nav class="w-full py-4 px-4 md:px-6 bg-gray-900">
<input type="checkbox" id="mobile-menu" class="peer hidden" />
<div class="flex items-center justify-between">
<div class="flex items-center flex-shrink-0">
<%= link_to root_path, class: "mr-4 md:mr-8" do %>
<%= image_tag "logo.svg", alt: "Family Funds Logo", class: "h-8" %>
<% end %>
</div>
<!-- Desktop menu -->
<div class="hidden lg:flex items-center flex-grow justify-center">
<ul class="flex space-x-6">
<% if Current.user.registered? %>
<li><%= link_to "Dashboard", root_path, class: "text-white hover:text-gray-300" %></li>
<li><%= link_to "Expenses", expenses_path, class: "text-white hover:text-gray-300" %></li>
<li><%= link_to "Credit Card Bills", credit_card_bills_path, class: "text-white hover:text-gray-300" %></li>
<li><%= link_to "Incomes", incomes_path, class: "text-white hover:text-gray-300" %></li>
<li><%= link_to "Members", members_path, class: "text-white hover:text-gray-300" %></li>
<% else %>
<li><%= link_to "Sign up", new_user_path, class: "text-white hover:text-gray-300" %></li>
<li><%= link_to "Log in", new_session_path, class: "text-white hover:text-gray-300" %></li>
<% end %>
</ul>
</div>
<div class="flex items-center space-x-4 flex-shrink-0">
<!-- Hamburger menu button -->
<label for="mobile-menu"
class="lg:hidden text-white hover:text-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-300 rounded-lg p-2 cursor-pointer"
>
<svg class="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M4 6h16M4 12h16M4 18h16"></path>
</svg>
</label>
<% if Current.user.registered? %>
<%= render 'shared/user_dropdown' %>
<% end %>
</div>
</div>
<!-- Mobile menu -->
<div class="lg:hidden mt-4 hidden peer-checked:block">
<div class="border-t border-gray-700 pt-4">
<ul class="space-y-2">
<% if Current.user.registered? %>
<li><%= link_to "Dashboard", root_path, class: "block text-white hover:text-gray-300 hover:bg-gray-800 px-3 py-2 rounded-md text-base font-medium" %></li>
<li><%= link_to "Expenses", expenses_path, class: "block text-white hover:text-gray-300 hover:bg-gray-800 px-3 py-2 rounded-md text-base font-medium" %></li>
<li><%= link_to "Credit Card Bills", credit_card_bills_path, class: "block text-white hover:text-gray-300 hover:bg-gray-800 px-3 py-2 rounded-md text-base font-medium" %></li>
<li><%= link_to "Incomes", incomes_path, class: "block text-white hover:text-gray-300 hover:bg-gray-800 px-3 py-2 rounded-md text-base font-medium" %></li>
<li><%= link_to "Members", members_path, class: "block text-white hover:text-gray-300 hover:bg-gray-800 px-3 py-2 rounded-md text-base font-medium" %></li>
<% else %>
<li><%= link_to "Sign up", new_user_path, class: "block text-white hover:text-gray-300 hover:bg-gray-800 px-3 py-2 rounded-md text-base font-medium" %></li>
<li><%= link_to "Log in", new_session_path, class: "block text-white hover:text-gray-300 hover:bg-gray-800 px-3 py-2 rounded-md text-base font-medium" %></li>
<% end %>
</ul>
</div>
</div>
<nav class="w-100 p-6 bg-gray-900 flex items-center justify-between flex-wrap">
<ul class="flex">
<% if Current.user.registered? %>
<li class="mr-6"><%= link_to "Dashboard", root_path, class: "text-white" %></li>
<li class="mr-6"><%= link_to "Expenses", expenses_path, class: "text-white" %></li>
<li class="mr-6"><%= link_to "Credit Card Bills", credit_card_bills_path, class: "text-white" %></li>
<li class="mr-6"><%= link_to "Incomes", incomes_path, class: "text-white" %></li>
<li class="mr-6"><%= link_to "Members", members_path, class: "text-white" %></li>
<li class="mr-6"><%= link_to "Log out", session_path, data: {turbo_method: :delete}, class: "text-white" %></li>
<% else %>
<li class="mr-6"><%= link_to "Sign up", new_user_path, class: "text-white" %></li>
<li class="mr-6"><%= link_to "Log in", new_session_path, class: "text-white" %></li>
<% end %>
</ul>
</nav>
<% if flash.any? %>

View file

@ -1,13 +1,9 @@
<%= form_with(model: member, class: "space-y-8") do |form| %>
<%= form_with(model: member, class: "contents") do |form| %>
<% if member.errors.any? %>
<div id="error_explanation" class="bg-red-50 border border-red-200 text-red-600 px-4 py-3 rounded-lg">
<div class="flex items-center mb-2">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7 4a1 1 0 11-2 0 1 1 0 012 0zm-1-9a1 1 0 00-1 1v4a1 1 0 102 0V6a1 1 0 00-1-1z" clip-rule="evenodd"/>
</svg>
<h3 class="text-sm font-medium"><%= pluralize(member.errors.count, "error") %> prohibited this member from being saved:</h3>
</div>
<ul class="list-disc list-inside text-sm space-y-1">
<div id="error_explanation" class="bg-red-50 text-red-500 px-3 py-2 font-medium rounded-lg mt-3">
<h2><%= pluralize(member.errors.count, "error") %> prohibited this member from being saved:</h2>
<ul>
<% member.errors.each do |error| %>
<li><%= error.full_message %></li>
<% end %>
@ -15,52 +11,17 @@
</div>
<% end %>
<!-- Basic Information -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h3 class="text-lg font-medium text-gray-900 mb-6">Basic Information</h3>
<div class="grid grid-cols-1 gap-6">
<div>
<%= form.label :name, class: "block text-sm font-medium text-gray-700 mb-2" %>
<%= form.text_field :name,
class: "block w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm",
placeholder: "Enter member name..." %>
</div>
</div>
<div class="my-5">
<%= form.label :name %>
<%= form.text_field :name, class: "block shadow-sm rounded-md border border-gray-200 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<!-- Budget Settings -->
<div class="bg-white rounded-lg shadow-sm border border-gray-200 p-6">
<h3 class="text-lg font-medium text-gray-900 mb-6">Budget Role</h3>
<div class="flex items-center justify-between">
<div class="flex-1">
<%= form.label :pays, "Contributing Member", class: "block text-sm font-medium text-gray-700" %>
<p class="text-sm text-gray-500 mt-1">Whether this member contributes financially to the household budget</p>
</div>
<div class="ml-6">
<%= form.check_box :pays, class: "toggle-checkbox sr-only" %>
<label for="member_pays" class="toggle-label relative inline-flex h-6 w-11 flex-shrink-0 cursor-pointer rounded-full border-2 border-transparent transition-colors duration-200 ease-in-out focus-within:outline-none focus-within:ring-2 focus-within:ring-blue-500 focus-within:ring-offset-2 bg-gray-200">
<span class="toggle-switch pointer-events-none inline-block h-5 w-5 transform rounded-full bg-white shadow ring-0 transition duration-200 ease-in-out translate-x-0"></span>
</label>
</div>
</div>
<div class="my-5">
<%= form.label :pays %>
<%= form.check_box :pays, class: "block mt-2 h-5 w-5" %>
</div>
<!-- Form Actions -->
<div class="flex justify-end space-x-3 pt-6 border-t border-gray-200">
<%= link_to members_path, class: "inline-flex items-center px-4 py-2 border border-gray-300 rounded-md shadow-sm text-sm font-medium text-gray-700 bg-white hover:bg-gray-50 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500" do %>
Cancel
<% end %>
<%= form.submit class: "inline-flex items-center px-4 py-2 border border-transparent rounded-md shadow-sm text-sm font-medium text-white bg-blue-600 hover:bg-blue-700 focus:outline-none focus:ring-2 focus:ring-offset-2 focus:ring-blue-500 cursor-pointer" %>
<div class="inline">
<%= form.submit class: "rounded-lg py-3 px-5 bg-blue-600 text-white inline-block font-medium cursor-pointer" %>
</div>
<style>
.toggle-checkbox:checked + .toggle-label {
background-color: #3B82F6;
}
.toggle-checkbox:checked + .toggle-label .toggle-switch {
transform: translateX(1.25rem);
}
</style>
<% end %>

View file

@ -1,48 +1,17 @@
<div id="<%= dom_id member %>" class="bg-white rounded-lg shadow hover:shadow-md transition-shadow">
<div class="p-6">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<div class="h-12 w-12 rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center">
<span class="text-white text-lg font-medium"><%= member.name.first.upcase %></span>
</div>
<div class="ml-4">
<h3 class="text-lg font-semibold text-gray-900"><%= member.name %></h3>
<p class="text-sm text-gray-500">Household Member</p>
</div>
</div>
<% if member.pays %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
contributing
</span>
<% else %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
non-contributing
</span>
<% end %>
</div>
<div id="<%= dom_id member %>">
<p class="my-5">
<strong class="block font-medium mb-1">Name:</strong>
<%= member.name %>
</p>
<% if action_name != "show" %>
<div class="flex gap-2 pt-4 border-t border-gray-200">
<%= link_to member, class: "flex-1 inline-flex items-center justify-center px-4 py-2 text-sm font-medium text-blue-600 bg-blue-50 hover:bg-blue-100 rounded-md transition-colors" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
View
<% end %>
<%= link_to edit_member_path(member), class: "flex-1 inline-flex items-center justify-center px-4 py-2 text-sm font-medium text-indigo-600 bg-indigo-50 hover:bg-indigo-100 rounded-md transition-colors" do %>
<svg class="w-4 h-4 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit
<% end %>
</div>
<% end %>
</div>
<p class="my-5">
<strong class="block font-medium mb-1">Pays:</strong>
<%= member.pays %>
</p>
<% if action_name != "show" %>
<%= link_to "Show this member", member, class: "rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<%= link_to 'Edit this member', edit_member_path(member), class: "rounded-lg py-3 ml-2 px-5 bg-gray-100 inline-block font-medium" %>
<hr class="mt-6">
<% end %>
</div>

View file

@ -1,42 +1,8 @@
<div class="w-full px-4 py-8">
<!-- Breadcrumb -->
<nav class="flex mb-6" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to members_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
</svg>
Members
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<%= link_to @member, class: "ml-1 text-sm font-medium text-gray-700 hover:text-blue-600 md:ml-2" do %>
<%= @member.name %>
<% end %>
</div>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">Edit</span>
</div>
</li>
</ol>
</nav>
<div class="mx-auto md:w-2/3 w-full">
<h1 class="font-bold text-4xl">Editing member</h1>
<div class="max-w-4xl mx-auto">
<div class="mb-8">
<h1 class="font-bold text-3xl sm:text-4xl text-gray-900">Edit Member</h1>
<p class="mt-2 text-sm text-gray-600">Update the details for "<%= @member.name %>"</p>
</div>
<%= render "form", member: @member %>
<%= render "form", member: @member %>
</div>
<%= link_to "Show this member", @member, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<%= link_to "Back to members", members_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>

View file

@ -1,164 +1,14 @@
<div class="w-full px-4 py-8">
<div class="w-full">
<% if notice.present? %>
<div class="mb-6">
<p class="py-3 px-4 bg-green-50 text-green-800 font-medium rounded-lg inline-flex items-center" id="notice">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<%= notice %>
</p>
</div>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
<% end %>
<div class="flex flex-col sm:flex-row sm:justify-between sm:items-center mb-8 gap-4">
<div>
<h1 class="font-bold text-3xl sm:text-4xl text-gray-900">Members</h1>
<p class="mt-2 text-sm text-gray-600">Manage household members and their budget roles</p>
</div>
<%= link_to new_member_path, class: "rounded-lg py-2.5 px-4 sm:py-3 sm:px-5 bg-blue-600 hover:bg-blue-700 text-white font-medium transition-colors inline-flex items-center justify-center sm:justify-start" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
New member
<% end %>
<div class="flex justify-between items-center">
<h1 class="font-bold text-4xl">Members</h1>
<%= link_to 'New member', new_member_path, class: "rounded-lg py-3 px-5 bg-blue-600 text-white block font-medium" %>
</div>
<% if @members.any? %>
<!-- Summary Cards -->
<div class="grid grid-cols-1 md:grid-cols-3 gap-6 mb-8">
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-blue-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z"/>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">Total Members</p>
<p class="text-2xl font-semibold text-gray-900"><%= @members.count %></p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-green-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M9 12l2 2 4-4m6 2a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">Contributing</p>
<p class="text-2xl font-semibold text-gray-900"><%= @members.where(pays: true).count %></p>
</div>
</div>
</div>
<div class="bg-white rounded-lg shadow p-6">
<div class="flex items-center">
<div class="flex-shrink-0">
<svg class="h-8 w-8 text-gray-600" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M18.364 18.364A9 9 0 005.636 5.636m12.728 12.728L5.636 5.636m12.728 12.728L5.636 5.636"/>
</svg>
</div>
<div class="ml-4">
<p class="text-sm font-medium text-gray-500">Non-Contributing</p>
<p class="text-2xl font-semibold text-gray-900"><%= @members.where(pays: false).count %></p>
</div>
</div>
</div>
</div>
<!-- Desktop Table View -->
<div class="hidden lg:block overflow-x-auto bg-white rounded-lg shadow">
<table class="min-w-full">
<thead class="bg-gray-50 border-b border-gray-200">
<tr>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Member</th>
<th scope="col" class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">Role</th>
<th scope="col" class="px-6 py-3 text-right text-xs font-medium text-gray-500 uppercase tracking-wider">Actions</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<% @members.each do |member| %>
<tr class="hover:bg-gray-50 transition-colors">
<td class="px-6 py-4 whitespace-nowrap">
<div class="flex items-center">
<div class="h-10 w-10 rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center">
<span class="text-white text-sm font-medium"><%= member.name.first.upcase %></span>
</div>
<div class="ml-4">
<div class="text-sm font-medium text-gray-900"><%= member.name %></div>
</div>
</div>
</td>
<td class="px-6 py-4 whitespace-nowrap">
<% if member.pays %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">contributing</span>
<% else %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800">non-contributing</span>
<% end %>
</td>
<td class="px-6 py-4 whitespace-nowrap text-right text-sm font-medium">
<%= link_to "View", member, class: "text-blue-600 hover:text-blue-900 mr-3" %>
<%= link_to "Edit", edit_member_path(member), class: "text-indigo-600 hover:text-indigo-900" %>
</td>
</tr>
<% end %>
</tbody>
</table>
</div>
<!-- Mobile Card View -->
<div class="lg:hidden space-y-4">
<% @members.each do |member| %>
<div class="bg-white rounded-lg shadow p-4 hover:shadow-md transition-shadow">
<div class="flex justify-between items-start mb-3">
<div class="flex items-center">
<div class="h-12 w-12 rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center">
<span class="text-white text-lg font-medium"><%= member.name.first.upcase %></span>
</div>
<div class="ml-3">
<h3 class="text-lg font-medium text-gray-900"><%= member.name %></h3>
<% if member.pays %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-green-100 text-green-800">contributing</span>
<% else %>
<span class="inline-flex items-center px-2 py-0.5 rounded text-xs font-medium bg-gray-100 text-gray-800">non-contributing</span>
<% end %>
</div>
</div>
<div class="flex gap-2">
<%= link_to member, class: "text-blue-600 hover:text-blue-800" do %>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/>
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"/>
</svg>
<% end %>
<%= link_to edit_member_path(member), class: "text-indigo-600 hover:text-indigo-800" do %>
<svg class="w-5 h-5" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
<% end %>
</div>
</div>
</div>
<% end %>
</div>
<% else %>
<div class="text-center py-12 bg-white rounded-lg shadow">
<svg class="mx-auto h-12 w-12 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m13.5-9a2.5 2.5 0 11-5 0 2.5 2.5 0 015 0z"/>
</svg>
<h3 class="mt-2 text-sm font-medium text-gray-900">No members</h3>
<p class="mt-1 text-sm text-gray-500">Get started by adding your first household member.</p>
<div class="mt-6">
<%= link_to new_member_path, class: "inline-flex items-center px-4 py-2 border border-transparent shadow-sm text-sm font-medium rounded-md text-white bg-blue-600 hover:bg-blue-700" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4v16m8-8H4"/>
</svg>
New member
<% end %>
</div>
</div>
<% end %>
<div id="members" class="min-w-full">
<%= render @members %>
</div>
</div>

View file

@ -1,32 +1,7 @@
<div class="w-full px-4 py-8">
<!-- Breadcrumb -->
<nav class="flex mb-6" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to members_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
</svg>
Members
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2">New Member</span>
</div>
</li>
</ol>
</nav>
<div class="mx-auto md:w-2/3 w-full">
<h1 class="font-bold text-4xl">New member</h1>
<div class="max-w-4xl mx-auto">
<div class="mb-8">
<h1 class="font-bold text-3xl sm:text-4xl text-gray-900">New Member</h1>
<p class="mt-2 text-sm text-gray-600">Add a new household member and set their budget role</p>
</div>
<%= render "form", member: @member %>
<%= render "form", member: @member %>
</div>
<%= link_to 'Back to members', members_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>

View file

@ -1,118 +1,15 @@
<div class="w-full px-4 py-8">
<% if notice.present? %>
<div class="mb-6">
<p class="py-3 px-4 bg-green-50 text-green-800 font-medium rounded-lg inline-flex items-center" id="notice">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M10 18a8 8 0 100-16 8 8 0 000 16zm3.707-9.293a1 1 0 00-1.414-1.414L9 10.586 7.707 9.293a1 1 0 00-1.414 1.414l2 2a1 1 0 001.414 0l4-4z" clip-rule="evenodd"/>
</svg>
<%= notice %>
</p>
</div>
<% end %>
<!-- Breadcrumb -->
<nav class="flex mb-6" aria-label="Breadcrumb">
<ol class="inline-flex items-center space-x-1 md:space-x-3">
<li class="inline-flex items-center">
<%= link_to members_path, class: "inline-flex items-center text-sm font-medium text-gray-700 hover:text-blue-600" do %>
<svg class="w-4 h-4 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path d="M10.707 2.293a1 1 0 00-1.414 0l-7 7a1 1 0 001.414 1.414L4 10.414V17a1 1 0 001 1h2a1 1 0 001-1v-2a1 1 0 011-1h2a1 1 0 011 1v2a1 1 0 001 1h2a1 1 0 001-1v-6.586l.293.293a1 1 0 001.414-1.414l-7-7z"/>
</svg>
Members
<% end %>
</li>
<li>
<div class="flex items-center">
<svg class="w-6 h-6 text-gray-400" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z" clip-rule="evenodd"/>
</svg>
<span class="ml-1 text-sm font-medium text-gray-500 md:ml-2"><%= @member.name %></span>
</div>
</li>
</ol>
</nav>
<div class="max-w-4xl mx-auto">
<div class="mb-8">
<div class="flex justify-between items-start mb-4">
<div>
<h1 class="font-bold text-3xl sm:text-4xl text-gray-900"><%= @member.name %></h1>
<p class="mt-2 text-sm text-gray-600">Household member details and budget role</p>
</div>
<% if @member.pays %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-green-100 text-green-800">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
contributing
</span>
<% else %>
<span class="inline-flex items-center px-3 py-1 rounded-full text-sm font-medium bg-gray-100 text-gray-800">
<svg class="w-4 h-4 mr-1" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
non-contributing
</span>
<% end %>
</div>
</div>
<!-- Member Details Card -->
<div class="bg-white rounded-lg shadow-lg overflow-hidden mb-8">
<div class="px-6 py-8">
<div class="flex items-center justify-center">
<div class="text-center">
<div class="h-24 w-24 mx-auto rounded-full bg-gradient-to-r from-blue-500 to-purple-600 flex items-center justify-center mb-4">
<span class="text-white text-3xl font-medium"><%= @member.name.first.upcase %></span>
</div>
<h3 class="text-2xl font-semibold text-gray-900 mb-2"><%= @member.name %></h3>
<p class="text-gray-600 mb-4">Household Member</p>
<div class="inline-flex items-center">
<% if @member.pays %>
<div class="flex items-center text-green-600">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M16.707 5.293a1 1 0 010 1.414l-8 8a1 1 0 01-1.414 0l-4-4a1 1 0 011.414-1.414L8 12.586l7.293-7.293a1 1 0 011.414 0z" clip-rule="evenodd"/>
</svg>
<span class="font-medium">Contributing to budget</span>
</div>
<% else %>
<div class="flex items-center text-gray-600">
<svg class="w-5 h-5 mr-2" fill="currentColor" viewBox="0 0 20 20">
<path fill-rule="evenodd" d="M4.293 4.293a1 1 0 011.414 0L10 8.586l4.293-4.293a1 1 0 111.414 1.414L11.414 10l4.293 4.293a1 1 0 01-1.414 1.414L10 11.414l-4.293 4.293a1 1 0 01-1.414-1.414L8.586 10 4.293 5.707a1 1 0 010-1.414z" clip-rule="evenodd"/>
</svg>
<span class="font-medium">Not contributing to budget</span>
</div>
<% end %>
</div>
</div>
</div>
</div>
</div>
<!-- Actions -->
<div class="flex flex-wrap gap-3">
<%= link_to edit_member_path(@member), class: "inline-flex items-center px-4 py-2 bg-blue-600 hover:bg-blue-700 text-white font-medium rounded-lg transition-colors" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z"/>
</svg>
Edit Member
<% end %>
<%= button_to member_path(@member), method: :delete,
class: "inline-flex items-center px-4 py-2 bg-red-600 hover:bg-red-700 text-white font-medium rounded-lg transition-colors",
confirm: "Are you sure you want to delete this member?" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16"/>
</svg>
Delete
<% end %>
<%= link_to members_path, class: "inline-flex items-center px-4 py-2 bg-gray-100 hover:bg-gray-200 text-gray-700 font-medium rounded-lg transition-colors" do %>
<svg class="w-5 h-5 mr-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M10 19l-7-7m0 0l7-7m-7 7h18"/>
</svg>
Back to Members
<% end %>
<div class="mx-auto md:w-2/3 w-full flex">
<div class="mx-auto">
<% if notice.present? %>
<p class="py-2 px-3 bg-green-50 mb-5 text-green-500 font-medium rounded-lg inline-block" id="notice"><%= notice %></p>
<% end %>
<%= render @member %>
<%= link_to 'Edit this member', edit_member_path(@member), class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
<div class="inline-block ml-2">
<%= button_to 'Destroy this member', member_path(@member), method: :delete, class: "mt-2 rounded-lg py-3 px-5 bg-gray-100 font-medium" %>
</div>
<%= link_to 'Back to members', members_path, class: "ml-2 rounded-lg py-3 px-5 bg-gray-100 inline-block font-medium" %>
</div>
</div>

View file

@ -13,12 +13,12 @@
<div class="my-5">
<%= form.label :email %>
<%= form.text_field :email, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
<%= form.text_field :email, class: "block shadow-sm rounded-md border border-gray-400 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<div class="my-5">
<%= form.label :password %>
<%= form.password_field :password, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
<%= form.password_field :password, class: "block shadow-sm rounded-md border border-gray-400 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<div class="inline">

View file

@ -1,39 +0,0 @@
<div class="relative group">
<input type="checkbox" id="user-dropdown" class="peer hidden" />
<label for="user-dropdown"
class="flex items-center space-x-2 text-white hover:text-gray-300 focus:outline-none focus:ring-2 focus:ring-gray-300 rounded-lg p-2 cursor-pointer"
>
<div class="w-8 h-8 bg-blue-500 rounded-full flex items-center justify-center text-white font-semibold text-sm flex-shrink-0">
<%= Current.user.email.first.upcase %>
</div>
<span class="hidden xl:block font-medium truncate max-w-48"><%= Current.user.email %></span>
<svg class="w-4 h-4 transform transition-transform duration-200 flex-shrink-0 peer-checked:rotate-180" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M19 9l-7 7-7-7"></path>
</svg>
</label>
<!-- Invisible overlay to close dropdown when clicking outside -->
<label for="user-dropdown" class="fixed inset-0 z-40 hidden peer-checked:block cursor-default"></label>
<div
class="absolute right-0 mt-2 w-48 bg-white rounded-lg shadow-lg border border-gray-200 z-50 hidden peer-checked:block"
>
<div class="py-1">
<%= link_to user_path(Current.user), class: "flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" do %>
<svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z"></path>
</svg>
Profile
<% end %>
<hr class="my-1 border-gray-200">
<%= link_to session_path,
data: {turbo_method: :delete},
class: "flex items-center px-4 py-2 text-sm text-gray-700 hover:bg-gray-100" do %>
<svg class="w-4 h-4 mr-3" fill="none" stroke="currentColor" viewBox="0 0 24 24">
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 16l4-4m0 0l-4-4m4 4H7m6 4v1a3 3 0 01-3 3H6a3 3 0 01-3-3V7a3 3 0 013-3h4a3 3 0 013 3v1"></path>
</svg>
Log out
<% end %>
</div>
</div>
</div>

View file

@ -13,17 +13,17 @@
<div class="my-5">
<%= form.label :email %>
<%= form.text_field :email, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
<%= form.text_field :email, class: "block shadow-sm rounded-md border border-gray-400 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<div class="my-5">
<%= form.label :password %>
<%= form.password_field :password, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
<%= form.password_field :password, class: "block shadow-sm rounded-md border border-gray-400 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<div class="my-5">
<%= form.label :password_confirmation %>
<%= form.password_field :password_confirmation, class: "block shadow rounded-md border border-gray-400 outline-none px-3 py-2 mt-2 w-full" %>
<%= form.password_field :password_confirmation, class: "block shadow-sm rounded-md border border-gray-400 outline-hidden px-3 py-2 mt-2 w-full" %>
</div>
<div class="inline">

View file

@ -1,6 +0,0 @@
#!/usr/bin/env ruby
require_relative "../config/boot"
require "bundler/audit/cli"
ARGV.concat %w[ --config config/bundler-audit.yml ] if ARGV.empty? || ARGV.include?("check")
Bundler::Audit::CLI.start

6
bin/ci
View file

@ -1,6 +0,0 @@
#!/usr/bin/env ruby
require_relative "../config/boot"
require "active_support/continuous_integration"
CI = ActiveSupport::ContinuousIntegration
require_relative "../config/ci.rb"

View file

@ -22,7 +22,6 @@ FileUtils.chdir APP_ROOT do
puts "\n== Preparing database =="
system! "bin/rails db:prepare"
system! "bin/rails db:reset" if ARGV.include?("--reset")
puts "\n== Removing old logs and tempfiles =="
system! "bin/rails log:clear tmp:clear"

View file

@ -9,7 +9,7 @@ Bundler.require(*Rails.groups)
module FamilyBudget
class Application < Rails::Application
# Initialize configuration defaults for originally generated Rails version.
config.load_defaults 8.1
config.load_defaults 8.0
# Please, add to the `ignore` list any other `lib` subdirectories that do
# not contain `.rb` files, or that should not be reloaded or eager loaded.

View file

@ -1,5 +0,0 @@
# Audit all gems listed in the Gemfile for known security problems by running bin/bundler-audit.
# CVEs that are not relevant to the application can be enumerated on the ignore list below.
ignore:
- CVE-THAT-DOES-NOT-APPLY

View file

@ -1,23 +0,0 @@
# Run using bin/ci
CI.run do
step "Setup", "bin/setup --skip-server"
step "Style: Ruby", "bin/rubocop"
step "Security: Gem audit", "bin/bundler-audit"
step "Security: Importmap vulnerability audit", "bin/importmap audit"
step "Security: Brakeman code analysis", "bin/brakeman --quiet --no-pager --exit-on-warn --exit-on-error"
step "Tests: Rails", "bin/rails test"
step "Tests: System", "bin/rails test:system"
step "Tests: Seeds", "env RAILS_ENV=test bin/rails db:seed:replant"
# Optional: set a green GitHub commit status to unblock PR merge.
# Requires the `gh` CLI and and `gh extension install basecamp/gh-signoff`.
# if success?
# step "Signoff: All systems go. Ready for merge and deploy.", "gh signoff"
# else
# failure "Signoff: CI failed. Do not merge or deploy.", "Fix the issues and try again."
# end
end

View file

@ -59,7 +59,7 @@ Rails.application.configure do
# Set host to be used by links generated in mailer templates.
config.action_mailer.default_url_options = { host: "example.com" }
# Specify outgoing SMTP server. Remember to add smtp/* credentials via bin/rails credentials:edit.
# Specify outgoing SMTP server. Remember to add smtp/* credentials via rails credentials:edit.
# config.action_mailer.smtp_settings = {
# user_name: Rails.application.credentials.dig(:smtp, :user_name),
# password: Rails.application.credentials.dig(:smtp, :password),

View file

@ -20,10 +20,6 @@
# config.content_security_policy_nonce_generator = ->(request) { request.session.id.to_s }
# config.content_security_policy_nonce_directives = %w(script-src style-src)
#
# # Automatically add `nonce` to `javascript_tag`, `javascript_include_tag`, and `stylesheet_link_tag`
# # if the corresponding directives are specified in `content_security_policy_nonce_directives`.
# # config.content_security_policy_nonce_auto = true
#
# # Report violations without enforcing the policy.
# # config.content_security_policy_report_only = true
# end

View file

@ -1,16 +0,0 @@
# Be sure to restart your server when you modify this file.
# Avoid CORS issues when API is called from the frontend app.
# Handle Cross-Origin Resource Sharing (CORS) in order to accept cross-origin Ajax requests.
# Read more: https://github.com/cyu/rack-cors
# Rails.application.config.middleware.insert_before 0, Rack::Cors do
# allow do
# origins "example.com"
#
# resource "*",
# headers: :any,
# methods: [:get, :post, :put, :patch, :delete, :options, :head]
# end
# end

View file

@ -8,21 +8,3 @@
#
# Read the Guide for Upgrading Ruby on Rails for more info on each option.
# https://guides.rubyonrails.org/upgrading_ruby_on_rails.html
###
# Skips escaping HTML entities and line separators. When set to `false`, the
# JSON renderer no longer escapes these to improve performance.
#
# Example:
# class PostsController < ApplicationController
# def index
# render json: { key: "\u2028\u2029<>&" }
# end
# end
#
# Renders `{"key":"\u2028\u2029\u003c\u003e\u0026"}` with the previous default, but `{"key":"<>&"}` with the config
# set to `false`.
#
# Applications that want to keep the escaping behavior can set the config to `true`.
#++
# Rails.configuration.action_controller.escape_json_responses = false

View file

@ -1,8 +1,9 @@
require "test_helper"
require "capybara/cuprite"
class ApplicationSystemTestCase < ActionDispatch::SystemTestCase
driven_by :cuprite, options: { headless: ENV["VISIBLE_SYSTEM_TESTS"].blank? }
driven_by :selenium,
using: ENV["VISIBLE_SYSTEM_TESTS"].present? ? :chrome : :headless_chrome,
screen_size: [ 1400, 1400 ]
def login(email, password = "secret")
visit new_session_url

View file

@ -8,7 +8,7 @@ class CreditCardBillsTest < ApplicationSystemTestCase
test "visiting the index" do
visit credit_card_bills_url
assert_selector "h1", text: "Credit Card Bills"
assert_selector "h1", text: "Credit card bills"
end
test "should create credit card bill" do
@ -25,7 +25,7 @@ class CreditCardBillsTest < ApplicationSystemTestCase
test "should update Credit card bill" do
visit credit_card_bill_url(@credit_card_bill)
click_on "Edit credit card bill", match: :first
click_on "Edit this credit card bill", match: :first
fill_in "Amount", with: @credit_card_bill.amount
fill_in "Description", with: @credit_card_bill.description
@ -37,7 +37,7 @@ class CreditCardBillsTest < ApplicationSystemTestCase
test "should destroy Credit card bill" do
visit credit_card_bill_url(@credit_card_bill)
accept_prompt { click_on "Delete credit card bill", match: :first }
click_on "Destroy this credit card bill", match: :first
assert_text "Credit card bill was successfully destroyed"
end

View file

@ -15,9 +15,7 @@ class ExpensesTest < ApplicationSystemTestCase
visit expenses_url
click_on "New expense"
if @expense.credit_card
find("label.toggle-label[for='expense_credit_card']").click
end
check "Credit card" if @expense.credit_card
fill_in "Description", with: @expense.description
check "Estimated" if @expense.estimated
fill_in "Payment", with: @expense.payment
@ -30,11 +28,9 @@ class ExpensesTest < ApplicationSystemTestCase
test "should update Expense" do
visit expense_url(@expense)
click_on "Edit expense", match: :first
click_on "Edit this expense", match: :first
if @expense.credit_card
find("label.toggle-label[for='expense_credit_card']").click
end
check "Credit card" if @expense.credit_card
fill_in "Description", with: @expense.description
check "Estimated" if @expense.estimated
fill_in "Payment", with: @expense.payment
@ -47,7 +43,7 @@ class ExpensesTest < ApplicationSystemTestCase
test "should destroy Expense" do
visit expense_url(@expense)
accept_prompt { click_on "Delete expense", match: :first }
click_on "Destroy this expense", match: :first
assert_text "Expense was successfully destroyed"
end

View file

@ -17,9 +17,7 @@ class IncomesTest < ApplicationSystemTestCase
fill_in "Amount", with: @income.amount
fill_in "Description", with: @income.description
if @income.included
find("label.toggle-label[for='income_included']").click
end
check "Included" if @income.included
select @income.member.name, from: "Member"
click_on "Create Income"
@ -29,13 +27,11 @@ class IncomesTest < ApplicationSystemTestCase
test "should update Income" do
visit income_url(@income)
click_on "Edit Income", match: :first
click_on "Edit this income", match: :first
fill_in "Amount", with: @income.amount
fill_in "Description", with: @income.description
if @income.included
find("label.toggle-label[for='income_included']").click
end
check "Included" if @income.included
select @income.member.name, from: "Member"
click_on "Update Income"
@ -45,7 +41,7 @@ class IncomesTest < ApplicationSystemTestCase
test "should destroy Income" do
visit income_url(@income)
click_on "Delete", match: :first
click_on "Destroy this income", match: :first
assert_text "Income was successfully destroyed"
end

View file

@ -25,7 +25,7 @@ class MembersTest < ApplicationSystemTestCase
test "should update Member" do
visit member_url(@member)
click_on "Edit Member", match: :first
click_on "Edit this member", match: :first
fill_in "Name", with: @member.name
check "Pays" if @member.pays
@ -37,7 +37,7 @@ class MembersTest < ApplicationSystemTestCase
test "should destroy Member" do
visit member_url(@member)
click_on "Delete", match: :first
click_on "Destroy this member", match: :first
assert_text "Member was successfully destroyed"
end

View file

@ -15,8 +15,6 @@ class SessionsTest < ApplicationSystemTestCase
login(@user.email)
visit root_url
# Open the user dropdown by clicking the checkbox label
find('label[for="user-dropdown"]').click
click_on "Log out", match: :first
assert_text "Session was successfully destroyed"