From 86533981bcb236471ddbcd551f687e97655253b5 Mon Sep 17 00:00:00 2001 From: Rick Barrette Date: Fri, 7 Aug 2026 21:20:46 -0400 Subject: [PATCH] Refactor appointment model and views to use a single date field; update tooltip and calendar display for improved functionality --- .../customer_appointments_controller.rb | 6 +-- app/models/customer_appointment.rb | 22 ++++---- .../customer_appointments/_form.html.erb | 5 +- .../customer_appointments/_tooltip.html.erb | 2 +- app/views/customer_appointments/show.html.erb | 49 +---------------- .../{stylesheets => javascripts}/calendar.js | 0 assets/stylesheets/customer_appointments.css | 53 +++++++++++++++++-- .../001_create_customer_appointments.rb | 5 +- 8 files changed, 66 insertions(+), 76 deletions(-) rename assets/{stylesheets => javascripts}/calendar.js (100%) diff --git a/app/controllers/customer_appointments_controller.rb b/app/controllers/customer_appointments_controller.rb index acb80cf..fe31c79 100644 --- a/app/controllers/customer_appointments_controller.rb +++ b/app/controllers/customer_appointments_controller.rb @@ -41,8 +41,7 @@ class CustomerAppointmentsController < ApplicationController def new @appointment = CustomerAppointment.new @appointment.project = @project - @appointment.start_date = (params[:start_date].to_date + 9.hours) if params[:start_date].present? - @appointment.end_date = @appointment.start_date + 1.hour if @appointment.start_date.present? + @appointment.date = (params[:start_date].to_date + 9.hours) if params[:start_date].present? end def create @@ -89,8 +88,7 @@ class CustomerAppointmentsController < ApplicationController :description, :status, :user_id, - :start_date, - :end_date, + :date, :estimated_hours, :estimate_id, :duration diff --git a/app/models/customer_appointment.rb b/app/models/customer_appointment.rb index 908ec76..3209c6f 100644 --- a/app/models/customer_appointment.rb +++ b/app/models/customer_appointment.rb @@ -17,8 +17,7 @@ class CustomerAppointment < ActiveRecord::Base belongs_to :estimate, optional: true validates :subject, presence: true - validates :start_date, presence: true - validate :date_order + validates :date, presence: true STATUSES = %w[ Scheduled @@ -29,9 +28,15 @@ class CustomerAppointment < ActiveRecord::Base Cancelled ] + # Cast datetime to Date for Redmine Calendar compatibility + def start_date + date&.to_date + end + def due_date - return end_date&.to_date if end_date.present? - return start_date&.to_date + return nil unless date + # Accounts for multi-day duration spanning across dates + start_date + ([duration.to_i, 1].max - 1).days end def self.visible(user) @@ -40,15 +45,6 @@ class CustomerAppointment < ActiveRecord::Base private - def date_order - return if end_date.blank? - return if start_date.blank? - - if end_date < start_date - errors.add( :end_date, "cannot be before start date") - end - end - def log(msg) Rails.logger.info "[Event] #{msg}" end diff --git a/app/views/customer_appointments/_form.html.erb b/app/views/customer_appointments/_form.html.erb index 0bfc63f..5fb490f 100644 --- a/app/views/customer_appointments/_form.html.erb +++ b/app/views/customer_appointments/_form.html.erb @@ -24,10 +24,9 @@ <%= f.collection_select :user_id, User.active.sorted, :id, :name, { include_blank: true }, { label: false } %>

-

<%= f.datetime_local_field :start_date %>

-

<%= f.date_field :end_date, size: 10 %>

+

<%= f.datetime_local_field :date %>

-

<%= f.number_field :estimated_hours, step: 0.1, size: 6 %>

+

<%= f.number_field :estimated_hours, step: 1, size: 6 %>

<%= f.number_field :duration, step: 1, size: 6, label: :label_duration %>

<%= f.select :status, CustomerAppointment::STATUSES, label: :field_status %>

diff --git a/app/views/customer_appointments/_tooltip.html.erb b/app/views/customer_appointments/_tooltip.html.erb index 94d897d..b77350a 100644 --- a/app/views/customer_appointments/_tooltip.html.erb +++ b/app/views/customer_appointments/_tooltip.html.erb @@ -57,7 +57,7 @@ <%= l(:label_end_date) %> - <%= format_date(appointment.end_date || appointment.start_date) %> + <%= format_date(appointment.due_date ) %> diff --git a/app/views/customer_appointments/show.html.erb b/app/views/customer_appointments/show.html.erb index 67de750..bdcffec 100644 --- a/app/views/customer_appointments/show.html.erb +++ b/app/views/customer_appointments/show.html.erb @@ -15,57 +15,12 @@ (<%= format_date(@appointment.start_date) %> - <% if @appointment.end_date && @appointment.start_date != @appointment.end_date %> - - <%= format_date(@appointment.end_date) %> + <% if @appointment.duration > 1 %> + - <%= format_date(@appointment.due_date) %> <% end %>) - -
diff --git a/assets/stylesheets/calendar.js b/assets/javascripts/calendar.js similarity index 100% rename from assets/stylesheets/calendar.js rename to assets/javascripts/calendar.js diff --git a/assets/stylesheets/customer_appointments.css b/assets/stylesheets/customer_appointments.css index fb81f40..2d5a74b 100644 --- a/assets/stylesheets/customer_appointments.css +++ b/assets/stylesheets/customer_appointments.css @@ -23,8 +23,8 @@ table.cal td { background: #edf4ff; border: 1px solid #c5d8f3; border-radius: 3px; - font-size: 10px; - line-height: 1.2; + font-size: 0.9em; /* Matches Redmine calendar default text size */ + line-height: 1.3; overflow: visible !important; /* Allows tooltip popover to extend outside */ width: 100%; max-width: 100%; @@ -100,7 +100,7 @@ table.cal td { .appointment-hours { flex-shrink: 0; color: #444; - font-size: 9px; + font-size: 0.85em; /* Slightly smaller than main text to keep layout balanced */ font-weight: bold; margin-left: auto; } @@ -108,11 +108,11 @@ table.cal td { /* Option B: Multi-line wrapping for details */ .appointment-detail { color: #555; - font-size: 9px; + font-size: 0.9em; /* Matches calendar item body text */ white-space: normal; overflow-wrap: anywhere; word-break: break-word; - margin-top: 1px; + margin-top: 2px; min-width: 0; } @@ -203,4 +203,47 @@ table.cal td { .appointment-block .tip .buttons { margin: 0; text-align: right; +} + +.calendar-badge { + display: inline-block; + width: 38px; + border: 1px solid #b8c0c8; + border-radius: 4px; + overflow: hidden; + text-align: center; + vertical-align: middle; + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1); + margin-right: 8px; + line-height: 1; + background: #fff; +} + +.calendar-badge .month { + background-color: #c22b2b; /* Redmine primary red tone */ + color: #ffffff; + font-size: 9px; + font-weight: bold; + padding: 3px 0 2px 0; + text-transform: uppercase; + letter-spacing: 0.5px; +} + +.calendar-badge .day { + color: #333333; + font-size: 16px; + font-weight: bold; + padding: 4px 0 3px 0; +} + +.appointment-title { + vertical-align: middle; +} + +.appointment-dates { + font-size: 70%; + color: #666; + margin-left: 8px; + font-weight: normal; + vertical-align: middle; } \ No newline at end of file diff --git a/db/migrate/001_create_customer_appointments.rb b/db/migrate/001_create_customer_appointments.rb index d556171..c8300f3 100644 --- a/db/migrate/001_create_customer_appointments.rb +++ b/db/migrate/001_create_customer_appointments.rb @@ -15,8 +15,7 @@ class CreateCustomerAppointments < ActiveRecord::Migration[7.0] t.text :subject, null: false t.text :description, null: false - t.datetime :start_date, null: false - t.datetime :end_date + t.datetime :date, null: false t.integer :duration, default: 1 t.integer :estimated_hours, default: 1 @@ -31,7 +30,7 @@ class CreateCustomerAppointments < ActiveRecord::Migration[7.0] end - add_index :customer_appointments, :start_date + add_index :customer_appointments, :date end end \ No newline at end of file