Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
d930122
Big UI update
jcostd Mar 26, 2026
63c0eae
Updated Gems
Mar 27, 2026
e8f9270
Updated business logic
jcostd Mar 27, 2026
7e4470d
Updated UI
jcostd Mar 29, 2026
3a5ee0c
Updated UI
jcostd Mar 30, 2026
7ab6657
Updated UI
jcostd Mar 30, 2026
9ea4c32
Updated UI
jcostd Mar 30, 2026
2df0dbe
Updated UI
jcostd Mar 30, 2026
02a0358
Updated UI and Refresh logic
jcostd Mar 30, 2026
1137d9e
Fixed safari glitch
jcostd Mar 30, 2026
9e05918
Updated UI and refactoring
jcostd Mar 31, 2026
8e8a4e7
Merge branch 'refactor/pos-sales-flow' of github.com:jcostd/active-co…
jcostd Mar 31, 2026
229ff74
Updated UI
jcostd Apr 1, 2026
70e3ebc
Updated UI
jcostd Apr 1, 2026
1b9a404
Updated UI
jcostd Apr 1, 2026
0cf8d98
Updated Schema
jcostd Apr 1, 2026
f26b190
Updated Controllers
jcostd Apr 1, 2026
b95536d
Updated UI and refactored Pos Draft Builder
jcostd Apr 10, 2026
7f6cccc
Updated UI
jcostd Apr 11, 2026
9033c99
Updated UI
jcostd Apr 11, 2026
fd11e81
Updated UI
jcostd Apr 11, 2026
f69d45c
Updated POS
jcostd Apr 11, 2026
3b9e7be
Updated KIOSK ui
jcostd Apr 12, 2026
8f2012d
Updated KIOSK UI
jcostd Apr 12, 2026
4114d1b
Updated Product groups in POS terminal
jcostd Apr 13, 2026
56ab42d
Updated Kiosk UI
jcostd Apr 13, 2026
8d144db
Big UI update
jcostd Apr 17, 2026
56be9fb
baby fix
jcostd Apr 18, 2026
445850d
Updated gems and UI
jcostd Apr 21, 2026
054d537
Rubocop refactor
jcostd Apr 21, 2026
8a374f5
Updated gems and product
jcostd Apr 27, 2026
b26dfcb
Big UI and Filter update
jcostd Apr 29, 2026
a655080
Big Update
jcostd Apr 30, 2026
8c70345
Updated Pos flow
jcostd Apr 30, 2026
6130202
release: v1.0.0 - Core subscription logic and access management
jcostd Apr 30, 2026
bce6301
Merge branch 'main' into refactor/pos-sales-flow
jcostd Apr 30, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,3 +40,4 @@
# Ignore key files for decrypting credentials and more.
/config/credentials/*.key

TODO.org
2 changes: 1 addition & 1 deletion .ruby-version
Original file line number Diff line number Diff line change
@@ -1 +1 @@
4.0.2
4.0.3
6 changes: 3 additions & 3 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@
# check=error=true

# This Dockerfile is designed for production, not development. Use with Kamal or build'n'run by hand:
# docker build -t active_core_minimal .
# docker run -d -p 80:80 -e RAILS_MASTER_KEY=<value from config/master.key> --name active_core_minimal active_core_minimal
# docker build -t active-core .
# docker run -d -p 80:80 -e RAILS_MASTER_KEY=<value from config/master.key> --name active-core active-core

# 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=4.0.2
ARG RUBY_VERSION=4.0.3
FROM docker.io/library/ruby:$RUBY_VERSION-slim AS base

# Rails app lives here
Expand Down
156 changes: 73 additions & 83 deletions Gemfile.lock

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions app/assets/images/icons/access.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion app/assets/images/icons/contact_page.svg

This file was deleted.

1 change: 1 addition & 0 deletions app/assets/images/icons/login.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/assets/images/icons/monitoring.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion app/assets/images/icons/other.svg

This file was deleted.

1 change: 0 additions & 1 deletion app/assets/images/icons/person_add.svg

This file was deleted.

1 change: 1 addition & 0 deletions app/assets/images/icons/person_search.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions app/assets/images/icons/shopping_bag.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 0 additions & 1 deletion app/assets/images/icons/sticky_note_2.svg

This file was deleted.

1 change: 0 additions & 1 deletion app/assets/images/icons/view.svg

This file was deleted.

2 changes: 1 addition & 1 deletion app/assets/tailwind/application.css
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,5 @@
@source not "./daisyui{,*}.mjs";

@plugin "./daisyui.mjs" {
themes: all;
themes: corporate --default, business --prefersdark, light, dark, dim;
}
25 changes: 24 additions & 1 deletion app/controllers/access_logs_controller.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,33 @@
class AccessLogsController < ApplicationController
include Filterable

before_action :require_admin
before_action :set_access_log, only: [ :destroy ]

def index
@total_accesses = AccessLog.count
@pagy, @access_logs = pagy(
AccessLog
.apply_filters(filter_params)
.includes(:member, :discipline, :checkin_by_user)
)
end

def new
@access_log = AccessLog.new
end

def create
def destroy
@access_log.destroy
turbo_refresh_or_redirect_to access_logs_path, status: :see_other, notice: "Accesso annullato con successo."
end

private
def set_access_log
@access_log = AccessLog.find(params[:id])
end

def filter_params
params.permit(:query, :sort, :status, :discipline_id)
end
end
17 changes: 16 additions & 1 deletion app/controllers/application_controller.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
class ApplicationController < ActionController::Base
include Themable, Authentication
include Themable, Localizable, Authentication

include Pagy::Method

Expand All @@ -8,4 +8,19 @@ class ApplicationController < ActionController::Base

# Changes to the importmap will invalidate the etag for HTML responses
stale_when_importmap_changes

private

def turbo_refresh_or_redirect_to(fallback_path, options = {})
respond_to do |format|
format.turbo_stream do
flash[:notice] = options[:notice] if options[:notice]
flash[:alert] = options[:alert] if options[:alert]

render turbo_stream: turbo_stream.refresh(request_id: nil)
end

format.html { redirect_to fallback_path, **options }
end
end
end
16 changes: 16 additions & 0 deletions app/controllers/concerns/filterable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Filterable
extend ActiveSupport::Concern

included do
helper_method :filtering?, :active_filters
end

private
def filtering?
active_filters.any?
end

def active_filters
request.query_parameters.except(:sort, :commit, :page).reject { |_, v| v.blank? }
end
end
31 changes: 0 additions & 31 deletions app/controllers/concerns/filterable_actions.rb

This file was deleted.

14 changes: 14 additions & 0 deletions app/controllers/concerns/localizable.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
module Localizable
extend ActiveSupport::Concern

included do
around_action :switch_locale
end

private
def switch_locale(&action)
locale = current_user&.locale || I18n.default_locale

I18n.with_locale(locale, &action)
end
end
44 changes: 44 additions & 0 deletions app/controllers/concerns/safe_date_parsing.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
module SafeDateParsing
extend ActiveSupport::Concern

included do
helper_method :parse_month_param, :parse_date_param
end

private
def parse_month_param(month_string, fallback: Date.current)
return fallback if month_string.blank?

normalized_string = month_string.to_s.strip

unless normalized_string.match?(/\A\d{4}-\d{2}\z/)
Rails.logger.warn("[SafeDateParsing] Formato mese non valido o manomesso: #{normalized_string.inspect}")
return fallback
end

begin
Date.strptime("#{normalized_string}-01", "%Y-%m-%d")
rescue Date::Error => e
Rails.logger.warn("[SafeDateParsing] Errore logico (Date::Error) per il mese #{normalized_string}: #{e.message}")
fallback
end
end

def parse_date_param(date_string, fallback: Date.current)
return fallback if date_string.blank?

normalized_string = date_string.to_s.strip

unless normalized_string.match?(/\A\d{4}-\d{2}-\d{2}\z/)
Rails.logger.warn("[SafeDateParsing] Formato data non valido o manomesso: #{normalized_string.inspect}")
return fallback
end

begin
Date.strptime(normalized_string, "%Y-%m-%d")
rescue Date::Error => e
Rails.logger.warn("[SafeDateParsing] Errore logico (Date::Error) per la data #{normalized_string}: #{e.message}")
fallback
end
end
end
8 changes: 3 additions & 5 deletions app/controllers/concerns/themable.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,11 @@ module Themable
extend ActiveSupport::Concern

included do
before_action :load_theme
helper_method :current_theme
end

private
def load_theme
return @theme = current_user.theme_or_default if current_user

@theme = "light"
def current_theme
current_user&.theme || "corporate"
end
end
21 changes: 13 additions & 8 deletions app/controllers/dashboard_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@ class DashboardController < ApplicationController
def index
@daily_cash = DailyCash.current

@recent_sales = Sale.kept
.includes(:member, :product, :user)
.order(created_at: :desc)
.limit(10)

@expiring_count = Subscription.kept
.where(end_date: Date.current..7.days.from_now)
.count
@today_accesses_count = AccessLog.where(entered_at: Time.current.beginning_of_day..Time.current.end_of_day).count

@expiring_subscriptions = Subscription.kept
.includes(:member)
.where(end_date: Date.current..7.days.from_now)
.order(end_date: :asc)
.limit(5)

@expiring_count = Subscription.kept.where(end_date: Date.current..7.days.from_now).count

@recent_accesses = AccessLog.includes(:member, :discipline)
.order(entered_at: :desc)
.limit(5)
end
end
20 changes: 12 additions & 8 deletions app/controllers/disciplines/members_controller.rb
Original file line number Diff line number Diff line change
@@ -1,20 +1,24 @@
class Disciplines::MembersController < ApplicationController
include Filterable

before_action :set_discipline

def index
product_ids = @discipline.product_ids
@products = @discipline.products.kept

@query = @discipline.recent_subscriptions
.apply_filters(filter_params)
.includes(:product, member: [ :subscriptions ])

all_subs = Subscription.kept
.where(product_id: product_ids)
.where("end_date >= ?", 30.days.ago)
.includes(:member, :product)
@subscriptions = all_subs.group_by(&:member_id)
.map { |_, subs| subs.max_by(&:end_date) }
.sort_by(&:end_date)
@pagy, @subscriptions = pagy(@query)
end

private
def set_discipline
@discipline = Discipline.find(params[:discipline_id])
end

def filter_params
params.permit(:query, :sort, :state, :product_id, :membership_status, :med_cert)
end
end
33 changes: 21 additions & 12 deletions app/controllers/disciplines_controller.rb
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
class DisciplinesController < ApplicationController
include Filterable

before_action :set_discipline, only: [ :show, :edit, :update, :destroy ]

layout "modal", only: [ :new, :create, :edit, :update ]

def index
@pagy, @disciplines = pagy(Discipline.kept.order(:name))
@total_active_disciplines = Discipline.kept.count
@pagy, @disciplines = pagy(
Discipline
.apply_filters(filter_params)
.includes(:products)
)
end

def show
Expand All @@ -14,37 +23,33 @@ def new
requires_medical_certificate: true,
requires_membership: true
)

render layout: "modal"
end

def create
@discipline = Discipline.new(discipline_params)

if @discipline.save
redirect_to disciplines_path, notice: t(".created", default: "Disciplina creata con successo.")
turbo_refresh_or_redirect_to disciplines_path, notice: "Disciplina creata con successo."
else
render :new, layout: "modal", status: :unprocessable_entity
render :new, status: :unprocessable_entity
end
end

def edit
render layout: "modal"
end
def edit; end

def update
if @discipline.update(discipline_params)
redirect_to disciplines_path, notice: t(".updated", default: "Disciplina aggiornata.")
turbo_refresh_or_redirect_to disciplines_path, notice: "Disciplina aggiornata."
else
render :edit, layout: "modal", status: :unprocessable_entity
render :edit, status: :unprocessable_entity
end
end

def destroy
if @discipline.discard!
redirect_to disciplines_path, notice: t(".discarded", default: "Disciplina archiviata.")
turbo_refresh_or_redirect_to disciplines_path, notice: "Disciplina archiviata."
else
redirect_to disciplines_path, alert: t(".error", default: "Impossibile archiviare.")
turbo_refresh_or_redirect_to disciplines_path, alert: "Impossibile archiviare."
end
end

Expand All @@ -56,4 +61,8 @@ def set_discipline
def discipline_params
params.require(:discipline).permit(:name, :requires_medical_certificate, :requires_membership)
end

def filter_params
params.permit(:query, :sort)
end
end
Loading
Loading