Refactor appointment model and views to use a single date field; update tooltip and calendar display for improved functionality

This commit is contained in:
ricky committed 2026-08-07 21:20:46 -04:00
1 parent 88a40b356c
commit 86533981bc
8 files changed
+66 -76

No files matched your search

@@ -41,8 +41,7 @@ class CustomerAppointmentsController < ApplicationController
def new def new
@appointment = CustomerAppointment.new @appointment = CustomerAppointment.new
@appointment.project = @project @appointment.project = @project
@appointment.start_date = (params[:start_date].to_date + 9.hours) if params[:start_date].present? @appointment.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?
end end
def create def create
@@ -89,8 +88,7 @@ class CustomerAppointmentsController < ApplicationController
:description, :description,
:status, :status,
:user_id, :user_id,
:start_date, :date,
:end_date,
:estimated_hours, :estimated_hours,
:estimate_id, :estimate_id,
:duration :duration
+9 -13
View File
@@ -17,8 +17,7 @@ class CustomerAppointment < ActiveRecord::Base
belongs_to :estimate, optional: true belongs_to :estimate, optional: true
validates :subject, presence: true validates :subject, presence: true
validates :start_date, presence: true validates :date, presence: true
validate :date_order
STATUSES = %w[ STATUSES = %w[
Scheduled Scheduled
@@ -29,9 +28,15 @@ class CustomerAppointment < ActiveRecord::Base
Cancelled Cancelled
] ]
# Cast datetime to Date for Redmine Calendar compatibility
def start_date
date&.to_date
end
def due_date def due_date
return end_date&.to_date if end_date.present? return nil unless date
return start_date&.to_date # Accounts for multi-day duration spanning across dates
start_date + ([duration.to_i, 1].max - 1).days
end end
def self.visible(user) def self.visible(user)
@@ -40,15 +45,6 @@ class CustomerAppointment < ActiveRecord::Base
private 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) def log(msg)
Rails.logger.info "[Event] #{msg}" Rails.logger.info "[Event] #{msg}"
end end
@@ -24,10 +24,9 @@
<%= f.collection_select :user_id, User.active.sorted, :id, :name, { include_blank: true }, { label: false } %> <%= f.collection_select :user_id, User.active.sorted, :id, :name, { include_blank: true }, { label: false } %>
</p> </p>
<p><%= f.datetime_local_field :start_date %></p> <p><%= f.datetime_local_field :date %></p>
<p><%= f.date_field :end_date, size: 10 %></p>
<p><%= f.number_field :estimated_hours, step: 0.1, size: 6 %></p> <p><%= f.number_field :estimated_hours, step: 1, size: 6 %></p>
<p><%= f.number_field :duration, step: 1, size: 6, label: :label_duration %></p> <p><%= f.number_field :duration, step: 1, size: 6, label: :label_duration %></p>
<p><%= f.select :status, CustomerAppointment::STATUSES, label: :field_status %></p> <p><%= f.select :status, CustomerAppointment::STATUSES, label: :field_status %></p>
@@ -57,7 +57,7 @@
<tr> <tr>
<th><%= l(:label_end_date) %></th> <th><%= l(:label_end_date) %></th>
<td><%= format_date(appointment.end_date || appointment.start_date) %></td> <td><%= format_date(appointment.due_date ) %></td>
</tr> </tr>
<tr> <tr>
+2 -47
View File
@@ -15,57 +15,12 @@
<span class="appointment-dates"> <span class="appointment-dates">
(<%= format_date(@appointment.start_date) %> (<%= format_date(@appointment.start_date) %>
<% if @appointment.end_date && @appointment.start_date != @appointment.end_date %> <% if @appointment.duration > 1 %>
- <%= format_date(@appointment.end_date) %> - <%= format_date(@appointment.due_date) %>
<% end %>) <% end %>)
</span> </span>
</h2> </h2>
<style>
.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;
}
</style>
<div class="issue details"> <div class="issue details">
<div class="attributes"> <div class="attributes">
<div class="splitcontent"> <div class="splitcontent">
File renamed without changes.
+48 -5
View File
@@ -23,8 +23,8 @@ table.cal td {
background: #edf4ff; background: #edf4ff;
border: 1px solid #c5d8f3; border: 1px solid #c5d8f3;
border-radius: 3px; border-radius: 3px;
font-size: 10px; font-size: 0.9em; /* Matches Redmine calendar default text size */
line-height: 1.2; line-height: 1.3;
overflow: visible !important; /* Allows tooltip popover to extend outside */ overflow: visible !important; /* Allows tooltip popover to extend outside */
width: 100%; width: 100%;
max-width: 100%; max-width: 100%;
@@ -100,7 +100,7 @@ table.cal td {
.appointment-hours { .appointment-hours {
flex-shrink: 0; flex-shrink: 0;
color: #444; color: #444;
font-size: 9px; font-size: 0.85em; /* Slightly smaller than main text to keep layout balanced */
font-weight: bold; font-weight: bold;
margin-left: auto; margin-left: auto;
} }
@@ -108,11 +108,11 @@ table.cal td {
/* Option B: Multi-line wrapping for details */ /* Option B: Multi-line wrapping for details */
.appointment-detail { .appointment-detail {
color: #555; color: #555;
font-size: 9px; font-size: 0.9em; /* Matches calendar item body text */
white-space: normal; white-space: normal;
overflow-wrap: anywhere; overflow-wrap: anywhere;
word-break: break-word; word-break: break-word;
margin-top: 1px; margin-top: 2px;
min-width: 0; min-width: 0;
} }
@@ -204,3 +204,46 @@ table.cal td {
margin: 0; margin: 0;
text-align: right; 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;
}
@@ -15,8 +15,7 @@ class CreateCustomerAppointments < ActiveRecord::Migration[7.0]
t.text :subject, null: false t.text :subject, null: false
t.text :description, null: false t.text :description, null: false
t.datetime :start_date, null: false t.datetime :date, null: false
t.datetime :end_date
t.integer :duration, default: 1 t.integer :duration, default: 1
t.integer :estimated_hours, default: 1 t.integer :estimated_hours, default: 1
@@ -31,7 +30,7 @@ class CreateCustomerAppointments < ActiveRecord::Migration[7.0]
end end
add_index :customer_appointments, :start_date add_index :customer_appointments, :date
end end
end end