Skip to content

Commit fa5ea73

Browse files
committed
fix various gotcha
1 parent e587f4a commit fa5ea73

10 files changed

Lines changed: 168 additions & 71 deletions

File tree

Gemfile.lock

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ GEM
8383
bcrypt_pbkdf (1.1.2)
8484
bigdecimal (3.3.1)
8585
bindex (0.8.1)
86-
bootsnap (1.24.5)
86+
bootsnap (1.24.6)
8787
msgpack (~> 1.2)
8888
brakeman (8.0.4)
8989
racc
@@ -144,7 +144,7 @@ GEM
144144
jbuilder (2.15.1)
145145
actionview (>= 7.0.0)
146146
activesupport (>= 7.0.0)
147-
json (2.19.7)
147+
json (2.19.8)
148148
kamal (2.11.0)
149149
activesupport (>= 7.0)
150150
base64 (~> 0.2)
@@ -497,7 +497,7 @@ CHECKSUMS
497497
bcrypt_pbkdf (1.1.2) sha256=c2414c23ce66869b3eb9f643d6a3374d8322dfb5078125c82792304c10b94cf6
498498
bigdecimal (3.3.1) sha256=eaa01e228be54c4f9f53bf3cc34fe3d5e845c31963e7fcc5bedb05a4e7d52218
499499
bindex (0.8.1) sha256=7b1ecc9dc539ed8bccfc8cb4d2732046227b09d6f37582ff12e50a5047ceb17e
500-
bootsnap (1.24.5) sha256=36b677448524d279b470469aabd5dff4a980e3fa4931a0df68da4a500eb1b6c4
500+
bootsnap (1.24.6) sha256=c60bab88c70332290f0a2636a288f675299eb4f804a02a3c085b42eca9da164a
501501
brakeman (8.0.4) sha256=7bf921fa9638544835df9aa7b3e720a9a72c0267f34f92135955edd80d4dcf6f
502502
builder (3.3.0) sha256=497918d2f9dca528fdca4b88d84e4ef4387256d984b8154e9d5d3fe5a9c8835f
503503
bundler (4.0.12) sha256=7f8b757d28dfb636e7b24fba2344ac6dd13b5b24f4b46d62573d483f211825ac
@@ -529,7 +529,7 @@ CHECKSUMS
529529
io-console (0.8.2) sha256=d6e3ae7a7cc7574f4b8893b4fca2162e57a825b223a177b7afa236c5ef9814cc
530530
irb (1.18.0) sha256=de9454a0703a54704b9811a5ef31a60c86949fbf4013fcf244fabc7c775248e3
531531
jbuilder (2.15.1) sha256=2430bec28fb0cebacb5875b1009cf9d8bc3c303ccb810c4c8b062a4b51457637
532-
json (2.19.7) sha256=fe432c8639f6efff69f9d73b518a3705d9581ab93156f981ea72806e1e5bcc3e
532+
json (2.19.8) sha256=6354310fd76ef69b87d5bd1f38b40d730613baf90b6803d2d0a48f618d32dfaa
533533
kamal (2.11.0) sha256=1408864425e0dec7e0a14d712a3b13f614e9f3a425b7661d3f9d287a51d7dd75
534534
language_server-protocol (3.17.0.5) sha256=fd1e39a51a28bf3eec959379985a72e296e9f9acfce46f6a79d31ca8760803cc
535535
lint_roller (1.1.0) sha256=2c0c845b632a7d172cb849cc90c1bce937a28c5c8ccccb50dfd46a485003cc87

app/controllers/dashboard_controller.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,10 @@ def index
1515
@recent_accesses = AccessLog.includes(:member, :discipline)
1616
.order(entered_at: :desc)
1717
.limit(5)
18+
19+
@recent_sales = Sale.kept
20+
.includes(:member, subscription: :product)
21+
.order(created_at: :desc)
22+
.limit(5)
1823
end
1924
end

app/controllers/kiosk/access_logs_controller.rb

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,21 +7,27 @@ def create
77
@access_log = @discipline.access_logs.build(member: @member, checkin_by_user: current_user)
88

99
if @access_log.save
10-
if @access_log.status == "ok"
10+
case @access_log.status
11+
when "ok"
1112
flash[:success] = "Check-in registrato per #{@member.first_name}"
12-
else
13-
flash[:warning] = "Check-in forzato per #{@member.first_name} (Verificare anagrafica)"
13+
when "warning"
14+
flash[:info] = "Check-in registrato. Nota per #{@member.first_name}: verificare certificato."
15+
when "error"
16+
flash[:error] = "ATTENZIONE: Check-in FORZATO per #{@member.first_name}. Abbonamento o Quota assente!"
1417
end
1518
else
16-
flash[:error] = @access_log.errors.full_messages.to_sentence
19+
flash[:error] = "Impossibile registrare il check-in: " + @access_log.errors.full_messages.to_sentence
1720
end
1821

1922
redirect_to kiosk_discipline_path(@discipline)
2023
end
2124

2225
def destroy
26+
member_name = @access_log.member.first_name
2327
@access_log.destroy
24-
redirect_to kiosk_discipline_path(@discipline), notice: "Check-in annullato per #{@access_log.member.first_name}"
28+
29+
flash[:success] = "Check-in annullato per #{member_name}"
30+
redirect_to kiosk_discipline_path(@discipline)
2531
end
2632

2733
private

app/controllers/kiosk/disciplines_controller.rb

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,14 @@ def show
1313
.order(entered_at: :desc)
1414

1515
@pending_members = Member.kept
16-
.with_active_subscription_for(@discipline)
16+
.joins(:subscriptions)
17+
.merge(Subscription.kept.for_discipline(@discipline))
18+
.where(subscriptions: {
19+
end_date: 2.months.ago.beginning_of_month..,
20+
start_date: ..1.month.from_now.end_of_month
21+
})
1722
.without_recent_checkin_for(@discipline)
23+
.distinct
1824
.order(:first_name, :last_name)
1925
end
2026
end

app/models/member.rb

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -86,16 +86,16 @@ def valid_subscription_for(discipline)
8686
end
8787

8888
def relevant_subscriptions(date = Date.current)
89-
if subscriptions.loaded?
90-
subscriptions
91-
.select { |s| s.kept? && s.end_date && s.end_date >= (date - 30.days) }
92-
.sort_by { |s| s.end_date || Date.new(1970) }
93-
.reverse
94-
else
95-
subscriptions.kept
96-
.where(subscriptions: { end_date: (date - 30.days).. })
97-
.order(subscriptions: { end_date: :desc })
98-
end
89+
subs = subscriptions.loaded? ? subscriptions.select(&:kept?) : subscriptions.kept.to_a
90+
91+
subs
92+
.select { |s| s.end_date && s.end_date >= (date - 30.days) }
93+
.group_by(&:product_id)
94+
.map { |_, product_subs|
95+
product_subs.max_by(&:end_date)
96+
}
97+
.sort_by(&:end_date)
98+
.reverse
9999
end
100100

101101
def relevant_subscriptions_from_loaded(date = Date.current)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
<%# locals: (sale:) %>
2+
3+
<li class="list-row group">
4+
<div class="shrink-0 self-start sm:self-center mt-1 sm:mt-0">
5+
<%= link_to sale.member, class: "block hover:opacity-80 transition-opacity focus:outline-none focus:ring-2 focus:ring-primary rounded-box" do %>
6+
<%= ui_avatar(sale.member, size: "size-10", text_size: "text-md") %>
7+
<% end %>
8+
</div>
9+
10+
<div class="flex-1 min-w-0">
11+
<div class="font-semibold group-hover:text-primary transition-colors truncate">
12+
<%= link_to sale.member.full_name, sale.member, class: "block hover:opacity-80 transition-opacity focus:outline-none focus:ring-2 focus:ring-primary rounded-box" %>
13+
</div>
14+
<div class="text-[10px] text-base-content/60 uppercase tracking-wide truncate">
15+
<%# Mostriamo il nome del prodotto se è un abbonamento, altrimenti una dicitura generica %>
16+
<%= sale.subscription&.product&.name || 'Vendita / Quota' %>
17+
</div>
18+
</div>
19+
20+
<div class="flex flex-col items-end shrink-0 gap-0.5">
21+
<div class="badge badge-success badge-sm badge-soft font-mono font-bold text-sm">
22+
<%= format_money(sale.amount) %>
23+
</div>
24+
<span class="text-[10px] font-mono opacity-50 hidden sm:inline-block">
25+
<%= sale.created_at.strftime("%H:%M") %>
26+
</span>
27+
</div>
28+
</li>

app/views/dashboard/index.html.erb

Lines changed: 42 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -47,40 +47,38 @@
4747

4848
</div>
4949

50-
<%# RIGA 2: I FEED OPERATIVI %>
51-
<div class="grid grid-cols-1 lg:grid-cols-3 gap-6">
50+
<%# RIGA 2: FEED COMMERCIALE (Pagamenti e Scadenze) %>
51+
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mb-6">
5252

53-
<%# COLONNA 1: Timeline Ingressi (Card) %>
54-
<div class="card bg-base-100 shadow-sm lg:col-span-2">
53+
<%# COLONNA: Ultimi Pagamenti %>
54+
<div class="card bg-base-100 shadow-sm border border-base-200">
5555
<div class="card-body p-4 md:p-6">
5656
<div class="flex justify-between items-center mb-2">
5757
<h2 class="card-title text-base flex items-center gap-2">
58-
<%= icon("history", classes: "size-5 opacity-50") %> Registro Accessi
58+
<%= icon("payments", classes: "size-5 text-success") %> Ultimi Pagamenti
5959
</h2>
60-
61-
<% if current_user.admin? %>
62-
<%= link_to "Vedi tutti", access_logs_path, class: "text-xs font-semibold link link-hover text-base-content/60" %>
63-
<% end %>
60+
<% if current_user.admin? %>
61+
<%= link_to "Vedi tutti", sales_path, class: "text-xs font-semibold link link-hover text-base-content/60" %>
62+
<% end %>
6463
</div>
6564

66-
<% if @recent_accesses.any? %>
67-
<%# Componente LIST nativo di DaisyUI %>
65+
<% if @recent_sales.any? %>
6866
<ul class="list bg-base-100/50 rounded-box">
69-
<%= render partial: "dashboard/recent_access", collection: @recent_accesses, as: :log, cached: true %>
67+
<%= render partial: "dashboard/recent_sale", collection: @recent_sales, as: :sale, cached: true %>
7068
</ul>
7169
<% else %>
7270
<div class="flex items-center justify-center py-10 text-base-content/40 text-sm">
73-
Nessun ingresso registrato oggi.
71+
Nessun pagamento registrato.
7472
</div>
7573
<% end %>
7674
</div>
7775
</div>
7876

79-
<%# COLONNA 2: Scadenze (Card) %>
80-
<div class="card bg-base-100 shadow-sm">
77+
<%# COLONNA: Scadenze (Rinnovi da fare) %>
78+
<div class="card bg-base-100 shadow-sm border border-base-200">
8179
<div class="card-body p-4 md:p-6">
8280
<h2 class="card-title text-base flex items-center gap-2 mb-2">
83-
<%= icon("event", classes: "size-6 text-warning") %> In Scadenza
81+
<%= icon("event", classes: "size-6 text-warning") %> Prossime Scadenza
8482
</h2>
8583

8684
<% if @expiring_subscriptions.any? %>
@@ -90,13 +88,39 @@
9088

9189
<% if @expiring_count > 5 %>
9290
<div class="mt-4 text-center">
93-
<%= link_to "Mostra altre (#{@expiring_count - 5})", subscriptions_path(filter: 'expiring'), class: "btn btn-ghost btn-sm text-xs font-semibold" %>
91+
<%= link_to "Mostra tutte le scadenze (#{@expiring_count})", subscriptions_path(filter: 'expiring'), class: "btn btn-ghost btn-sm text-xs font-semibold" %>
9492
</div>
9593
<% end %>
9694
<% else %>
9795
<div class="flex flex-col items-center justify-center py-8 text-sm text-base-content/40 gap-3">
9896
<%= icon("success", classes: "size-10 opacity-20") %>
99-
<span>Nessuna urgenza.</span>
97+
<span>Nessun abbonamento in scadenza.</span>
98+
</div>
99+
<% end %>
100+
</div>
101+
</div>
102+
103+
<%# RIGA 3: FEED OPERATIVO (Accessi) - Prende tutta la larghezza %>
104+
<div class="card bg-base-100 shadow-sm border border-base-200 lg:col-span-2">
105+
<div class="card-body p-4 md:p-6">
106+
<div class="flex justify-between items-center mb-2">
107+
<h2 class="card-title text-base flex items-center gap-2">
108+
<%= icon("history", classes: "size-5 opacity-50") %> Registro Accessi Recenti
109+
</h2>
110+
<% if current_user.admin? %>
111+
<%= link_to "Vedi registro completo", access_logs_path, class: "text-xs font-semibold link link-hover text-base-content/60" %>
112+
<% end %>
113+
</div>
114+
115+
<% if @recent_accesses.any? %>
116+
<%# Qui potresti anche valutare una griglia interna se gli accessi sono tanti,
117+
ma la lista DaisyUI che abbiamo creato è molto pulita %>
118+
<ul class="list bg-base-100/50 rounded-box">
119+
<%= render partial: "dashboard/recent_access", collection: @recent_accesses, as: :log, cached: true %>
120+
</ul>
121+
<% else %>
122+
<div class="flex items-center justify-center py-10 text-base-content/40 text-sm">
123+
Nessun ingresso registrato oggi.
100124
</div>
101125
<% end %>
102126
</div>

app/views/kiosk/members/_card.html.erb

Lines changed: 32 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,46 @@
1-
<% cache [member, discipline] do %>
2-
<div id="<%= dom_id(member, :pending) %>" class="card bg-base-100 shadow-sm border border-base-200 hover:border-primary transition-colors">
1+
<%# locals: (member:, discipline:) %>
2+
<%
3+
policy = AccessPolicy.new(member: member, discipline: discipline).evaluate!
4+
sub = policy.subscription
5+
6+
# Definiamo i colori e i testi una volta sola
7+
if policy.status == :error
8+
bg_css, btn_css, text_css = "bg-error/5 border-error/50 hover:border-error", "btn-error", "text-error"
9+
icon_name, status_msg = "error", "Abbonam. Scaduto"
10+
elsif policy.status == :warning
11+
bg_css, btn_css, text_css = "bg-info/5 border-info/50 hover:border-info", "btn-info", "text-info"
12+
icon_name, status_msg = "warning", (!member.medical_certificate_valid? ? "Cert. Scaduto" : "In Scadenza")
13+
else
14+
bg_css, btn_css, text_css = "bg-base-100 border-base-200 hover:border-primary", "btn-primary", ""
15+
end
16+
%>
17+
18+
<% cache [member, discipline, policy.status, Date.current] do %>
19+
<div id="<%= dom_id(member, :pending) %>" class="card shadow-sm border transition-colors <%= bg_css %>">
320
<div class="card-body p-4 flex flex-row items-center justify-between gap-4">
421

522
<div class="flex items-center gap-4">
623
<%= ui_avatar(member, size: "size-12", text_size: "text-md") %>
724
<div>
825
<h4 class="font-bold text-lg leading-tight truncate max-w-[180px]"><%= member.full_name %></h4>
9-
<% if !member.medical_certificate_valid? %>
10-
<span class="text-[10px] uppercase font-bold text-error flex items-center gap-1">
11-
<%= icon("warning", classes: "size-3") %> Cert. Scaduto
12-
</span>
13-
<% end %>
26+
27+
<div class="flex flex-col gap-0.5 mt-1">
28+
<% if policy.status != :ok %>
29+
<span class="text-[10px] uppercase font-bold <%= text_css %> flex items-center gap-1">
30+
<%= icon(icon_name, classes: "size-3") %> <%= status_msg %>
31+
</span>
32+
<% elsif sub && !sub.unlimited_entries? %>
33+
<span class="text-[10px] uppercase font-bold text-success flex items-center gap-1">
34+
<%= icon("ticket", classes: "size-3") %> <%= sub.entries_remaining %> Ingressi
35+
</span>
36+
<% end %>
37+
</div>
1438
</div>
1539
</div>
1640

1741
<%= button_to kiosk_discipline_access_logs_path(discipline),
1842
params: { member_id: member.id },
19-
class: "btn btn-circle btn-primary btn-lg shadow-md",
43+
class: "btn btn-circle btn-lg shadow-md #{btn_css}",
2044
data: { turbo: true } do %>
2145
<%= icon("check", classes: "size-8") %>
2246
<% end %>

app/views/kiosk/members/_checked_in_card.html.erb

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,46 @@
11
<%# locals: (log:) %>
2-
3-
<% cache log do %>
4-
<%
5-
member = log.member
6-
is_warning = log.status != "ok"
7-
bg_class = is_warning ? "bg-warning/10 border-warning/50 text-warning-content" : "bg-success/10 border-success/50 text-success-content"
8-
%>
9-
10-
<div id="<%= dom_id(log) %>" class="card <%= bg_class %> shadow-sm border transition-all">
2+
<%
3+
member = log.member
4+
sub = log.subscription || member.relevant_subscriptions.find { |s| s.product.disciplines.include?(log.discipline) }
5+
6+
if log.error?
7+
bg_css, text_css = "bg-error/10 border-error/50", "text-error"
8+
icon_name, status_msg = "error", "Abbonam. Scaduto"
9+
elsif log.warning?
10+
bg_css, text_css = "bg-info/10 border-info/50", "text-info"
11+
icon_name, status_msg = "warning", (!member.medical_certificate_valid? ? "Cert. Scaduto" : "In Scadenza")
12+
else
13+
bg_css, text_css = "bg-success/5 border-success/20", ""
14+
end
15+
%>
16+
17+
<% cache [log, Date.current] do %>
18+
<div id="<%= dom_id(log) %>" class="card <%= bg_css %> shadow-sm border transition-all text-base-content">
1119
<div class="card-body p-4 flex flex-row items-center justify-between gap-4">
1220

1321
<div class="flex items-center gap-4">
1422
<%= ui_avatar(member, size: "size-12", text_size: "text-md") %>
1523

1624
<div>
17-
<h4 class="font-bold text-lg leading-tight truncate max-w-[180px] text-base-content"><%= member.full_name %></h4>
25+
<h4 class="font-bold text-lg leading-tight truncate max-w-[180px]"><%= member.full_name %></h4>
1826

1927
<div class="flex flex-col gap-0.5 mt-1">
20-
<span class="text-[10px] uppercase font-bold opacity-60 text-base-content flex items-center gap-1">
28+
<span class="text-[10px] uppercase font-bold opacity-60 flex items-center gap-1">
2129
<%= icon("clock", classes: "size-3") %>
2230
<%= format_datetime(log.entered_at, format: :short) %>
2331
</span>
2432

25-
<% if is_warning %>
26-
<span class="text-[10px] uppercase font-bold text-warning flex items-center gap-1">
27-
<%= icon("warning", classes: "size-3") %> Forzato
33+
<% if !log.ok? %>
34+
<span class="text-[10px] uppercase font-bold <%= text_css %> flex items-center gap-1 mt-0.5">
35+
<%= icon(icon_name, classes: "size-3") %> <%= status_msg %>
2836
</span>
2937
<% end %>
3038

31-
<% sub = member.valid_subscription_for(log.discipline) %>
32-
<% if sub && !sub.unlimited_entries? %>
33-
<span class="text-[10px] uppercase font-bold text-secondary flex items-center gap-1 mt-0.5">
34-
<%= icon("access", classes: "size-3") %> Entrate Rimanenti: <%= sub.entries_remaining %>
35-
</span>
36-
<% end %>
37-
39+
<% if sub && !sub.unlimited_entries? %>
40+
<span class="text-[10px] uppercase font-bold opacity-75 flex items-center gap-1 mt-0.5">
41+
<%= icon("access", classes: "size-3") %> Ingressi Rimanenti: <%= sub.entries_remaining %>
42+
</span>
43+
<% end %>
3844
</div>
3945
</div>
4046
</div>

app/views/subscriptions/_compact.html.erb

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<%# locals: (subscription:) %>
22
<% status = subscription.status %>
33

4-
<div class="badge border-none gap-1.5 font-semibold text-xs tracking-wide <%= subscription_badge_color(status.key) %>">
4+
<div class="badge border-none badge-soft gap-1.5 font-semibold text-xs tracking-wide <%= subscription_badge_color(status.key) %>">
55

66
<span class="truncate max-w-[120px]" title="<%= subscription.product.name %>">
77
<%= subscription.product.name %>
@@ -10,10 +10,8 @@
1010
<span class="flex items-center gap-1 opacity-90">
1111
<% if status.key == :pending_payment %>
1212
-<%= format_cents(subscription.agreed_price_cents - subscription.amount_paid) %>
13-
<% elsif status.key == :expired %>
14-
SCADUTO
1513
<% elsif subscription.respond_to?(:days_left) && subscription.days_left.present? %>
16-
-<%= subscription.days_left %>gg
14+
<%= subscription.days_left %>gg
1715
<% else %>
1816
<%= format_date(subscription.end_date, format: :short) %>
1917
<% end %>

0 commit comments

Comments
 (0)