From efee9f287487a5cef1e12e63ba5fc8c41335aec0 Mon Sep 17 00:00:00 2001 From: Daniel Nottingham Date: Sat, 27 Dec 2025 11:40:01 -0300 Subject: [PATCH] feat: Add color to medical shifts and recurrences --- .../medical_shift_recurrences_controller.rb | 3 ++- .../api/v1/medical_shifts_controller.rb | 2 +- app/models/medical_shift.rb | 1 + app/models/medical_shift_recurrence.rb | 1 + .../medical_shift_recurrence_serializer.rb | 3 ++- app/serializers/medical_shift_serializer.rb | 2 +- ...color_to_medical_shifts_and_recurrences.rb | 8 +++++++ db/schema.rb | 4 +++- spec/factories/medical_shift_recurrences.rb | 1 + spec/factories/medical_shifts.rb | 1 + spec/models/medical_shift_recurrence_spec.rb | 8 +++++++ spec/models/medical_shift_spec.rb | 8 +++++++ .../medical_shift_recurrences_request_spec.rb | 9 ++++--- .../api/v1/medical_shifts_request_spec.rb | 24 ++++++++++++------- 14 files changed, 59 insertions(+), 16 deletions(-) create mode 100644 db/migrate/20251220153000_add_color_to_medical_shifts_and_recurrences.rb diff --git a/app/controllers/api/v1/medical_shift_recurrences_controller.rb b/app/controllers/api/v1/medical_shift_recurrences_controller.rb index 89b332b..9b3b7b9 100644 --- a/app/controllers/api/v1/medical_shift_recurrences_controller.rb +++ b/app/controllers/api/v1/medical_shift_recurrences_controller.rb @@ -69,7 +69,8 @@ def recurrence_params :workload, :start_hour, :hospital_name, - :amount_cents + :amount_cents, + :color ).to_h end end diff --git a/app/controllers/api/v1/medical_shifts_controller.rb b/app/controllers/api/v1/medical_shifts_controller.rb index fb300a5..bd99736 100644 --- a/app/controllers/api/v1/medical_shifts_controller.rb +++ b/app/controllers/api/v1/medical_shifts_controller.rb @@ -84,7 +84,7 @@ def medical_shift end def medical_shift_params - params.permit(:hospital_name, :workload, :start_date, :start_hour, :amount_cents, :paid).to_h + params.permit(:hospital_name, :workload, :start_date, :start_hour, :amount_cents, :paid, :color).to_h end def serialized_medical_shifts(medical_shifts) diff --git a/app/models/medical_shift.rb b/app/models/medical_shift.rb index 9752e7e..7a8354c 100644 --- a/app/models/medical_shift.rb +++ b/app/models/medical_shift.rb @@ -21,6 +21,7 @@ class MedicalShift < ApplicationRecord validates :amount_cents, presence: true validates :amount_cents, numericality: { greater_than_or_equal_to: 0 } + validates :color, format: { with: /\A#[a-fA-F0-9]{6}\z/ } def shift daytime_start = Time.new(2000, 0o1, 0o1, 0o7, 0o0, 0o0, 0o0) diff --git a/app/models/medical_shift_recurrence.rb b/app/models/medical_shift_recurrence.rb index 9af09d4..578fa18 100644 --- a/app/models/medical_shift_recurrence.rb +++ b/app/models/medical_shift_recurrence.rb @@ -19,6 +19,7 @@ class MedicalShiftRecurrence < ApplicationRecord validates :start_hour, presence: true validates :hospital_name, presence: true validates :amount_cents, presence: true, numericality: { greater_than_or_equal_to: 0 } + validates :color, format: { with: /\A#[a-fA-F0-9]{6}\z/ } validates :day_of_week, presence: true, if: lambda { frequency.in?(%w[weekly biweekly]) diff --git a/app/serializers/medical_shift_recurrence_serializer.rb b/app/serializers/medical_shift_recurrence_serializer.rb index 4d109f4..dc3d555 100644 --- a/app/serializers/medical_shift_recurrence_serializer.rb +++ b/app/serializers/medical_shift_recurrence_serializer.rb @@ -12,7 +12,8 @@ class MedicalShiftRecurrenceSerializer < ActiveModel::Serializer :hospital_name, :amount_cents, :last_generated_until, - :user_id + :user_id, + :color def amount_cents object.amount.format diff --git a/app/serializers/medical_shift_serializer.rb b/app/serializers/medical_shift_serializer.rb index eeb8263..8db23f4 100644 --- a/app/serializers/medical_shift_serializer.rb +++ b/app/serializers/medical_shift_serializer.rb @@ -2,7 +2,7 @@ class MedicalShiftSerializer < ActiveModel::Serializer attributes :id, :hospital_name, :workload, :date, :hour, :amount_cents, :paid, :shift, :title, - :medical_shift_recurrence_id + :medical_shift_recurrence_id, :color def date object.start_date.strftime("%d/%m/%Y") diff --git a/db/migrate/20251220153000_add_color_to_medical_shifts_and_recurrences.rb b/db/migrate/20251220153000_add_color_to_medical_shifts_and_recurrences.rb new file mode 100644 index 0000000..e1a6838 --- /dev/null +++ b/db/migrate/20251220153000_add_color_to_medical_shifts_and_recurrences.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +class AddColorToMedicalShiftsAndRecurrences < ActiveRecord::Migration[7.1] + def change + add_column :medical_shifts, :color, :string, default: "#ffffff", null: false + add_column :medical_shift_recurrences, :color, :string, default: "#ffffff", null: false + end +end diff --git a/db/schema.rb b/db/schema.rb index 5807e43..bfc5525 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema[7.1].define(version: 2025_11_20_195423) do +ActiveRecord::Schema[7.1].define(version: 2025_12_20_153000) do # These are extensions that must be enabled in order to support this database enable_extension "citext" enable_extension "plpgsql" @@ -113,6 +113,7 @@ t.datetime "deleted_at" t.datetime "created_at", null: false t.datetime "updated_at", null: false + t.string "color", default: "#ffffff", null: false t.index ["deleted_at"], name: "index_medical_shift_recurrences_on_deleted_at" t.index ["last_generated_until"], name: "index_medical_shift_recurrences_on_last_generated_until" t.index ["user_id", "deleted_at"], name: "index_medical_shift_recurrences_on_user_id_and_deleted_at" @@ -131,6 +132,7 @@ t.time "start_hour", null: false t.datetime "deleted_at" t.bigint "medical_shift_recurrence_id" + t.string "color", default: "#ffffff", null: false t.index ["deleted_at"], name: "index_medical_shifts_on_deleted_at" t.index ["medical_shift_recurrence_id"], name: "index_medical_shifts_on_medical_shift_recurrence_id" t.index ["paid"], name: "index_medical_shifts_on_paid" diff --git a/spec/factories/medical_shift_recurrences.rb b/spec/factories/medical_shift_recurrences.rb index e7ad604..ad1fbcf 100644 --- a/spec/factories/medical_shift_recurrences.rb +++ b/spec/factories/medical_shift_recurrences.rb @@ -11,6 +11,7 @@ start_hour { "19:00:00" } hospital_name { create(:hospital).name } amount_cents { 120_000 } + color { "#ffffff" } trait :weekly do frequency { "weekly" } diff --git a/spec/factories/medical_shifts.rb b/spec/factories/medical_shifts.rb index 8d32dad..1787d19 100644 --- a/spec/factories/medical_shifts.rb +++ b/spec/factories/medical_shifts.rb @@ -10,6 +10,7 @@ start_hour { "10:51:23" } amount_cents { 1 } paid { false } + color { "#ffffff" } traits_for_enum(:workload, MedicalShifts::Workloads.list) diff --git a/spec/models/medical_shift_recurrence_spec.rb b/spec/models/medical_shift_recurrence_spec.rb index 83afd51..3a2a120 100644 --- a/spec/models/medical_shift_recurrence_spec.rb +++ b/spec/models/medical_shift_recurrence_spec.rb @@ -19,6 +19,14 @@ it { is_expected.to validate_numericality_of(:amount_cents).is_greater_than_or_equal_to(0) } + it { is_expected.to allow_value("#FF00aa").for(:color) } + it { is_expected.not_to allow_value("FF00aa").for(:color) } + it { is_expected.not_to allow_value("#fff").for(:color) } + it { is_expected.not_to allow_value("#ff00aaff").for(:color) } + it { is_expected.not_to allow_value("#ff00zz").for(:color) } + it { is_expected.not_to allow_value("random value").for(:color) } + it { is_expected.not_to allow_value("").for(:color) } + context "when frequency is weekly" do it "requires day_of_week to be present and valid" do weekly = build(:medical_shift_recurrence, :weekly, day_of_week: nil) diff --git a/spec/models/medical_shift_spec.rb b/spec/models/medical_shift_spec.rb index 8f57219..6055d7b 100644 --- a/spec/models/medical_shift_spec.rb +++ b/spec/models/medical_shift_spec.rb @@ -22,6 +22,14 @@ it { is_expected.to validate_presence_of(:amount_cents) } it { is_expected.to validate_numericality_of(:amount_cents).is_greater_than_or_equal_to(0) } + + it { is_expected.to allow_value("#FF00aa").for(:color) } + it { is_expected.not_to allow_value("FF00aa").for(:color) } + it { is_expected.not_to allow_value("#fff").for(:color) } + it { is_expected.not_to allow_value("#ff00aaff").for(:color) } + it { is_expected.not_to allow_value("#ff00zz").for(:color) } + it { is_expected.not_to allow_value("random value").for(:color) } + it { is_expected.not_to allow_value("").for(:color) } end describe ".enumerations" do diff --git a/spec/requests/api/v1/medical_shift_recurrences_request_spec.rb b/spec/requests/api/v1/medical_shift_recurrences_request_spec.rb index 57b97e6..3e076c2 100644 --- a/spec/requests/api/v1/medical_shift_recurrences_request_spec.rb +++ b/spec/requests/api/v1/medical_shift_recurrences_request_spec.rb @@ -84,7 +84,8 @@ "start_date", "workload", "hospital_name", - "amount_cents" + "amount_cents", + "color" ) end @@ -121,7 +122,8 @@ workload: MedicalShifts::Workloads::SIX, start_hour: "19:00:00", hospital_name: "Hospital Teste", - amount_cents: 120_000 + amount_cents: 120_000, + color: "#ffffff" } } end @@ -142,7 +144,7 @@ end.to change(MedicalShiftRecurrence, :count).by(1) end - it "returns the created recurrence" do + it "returns the created recurrence" do # rubocop:disable Rspec/MultipleExpectations post "/api/v1/medical_shift_recurrences", params: valid_params, headers: auth_headers @@ -152,6 +154,7 @@ expect(body["medical_shift_recurrence"]).to be_present expect(body["medical_shift_recurrence"]["frequency"]).to eq("weekly") expect(body["medical_shift_recurrence"]["day_of_week"]).to eq(1) + expect(body["medical_shift_recurrence"]["color"]).to eq("#ffffff") end it "generates shifts immediately" do diff --git a/spec/requests/api/v1/medical_shifts_request_spec.rb b/spec/requests/api/v1/medical_shifts_request_spec.rb index 98e6a05..7c440bf 100644 --- a/spec/requests/api/v1/medical_shifts_request_spec.rb +++ b/spec/requests/api/v1/medical_shifts_request_spec.rb @@ -41,7 +41,8 @@ "amount_cents" => medical_shifts.second.amount.format, "paid" => medical_shifts.second.paid, "shift" => medical_shifts.second.shift, - "title" => "#{second_hospital_name} | #{second_workload} | #{second_shift}" + "title" => "#{second_hospital_name} | #{second_workload} | #{second_shift}", + "color" => medical_shifts.second.color }, { "id" => medical_shifts.first.id, @@ -53,7 +54,8 @@ "amount_cents" => medical_shifts.first.amount.format, "paid" => medical_shifts.first.paid, "shift" => medical_shifts.first.shift, - "title" => "#{first_hospital_name} | #{first_workload} | #{first_shift}" + "title" => "#{first_hospital_name} | #{first_workload} | #{first_shift}", + "color" => medical_shifts.first.color } ) end @@ -135,7 +137,8 @@ "amount_cents" => paid_medical_shifts.second.amount.format, "paid" => paid_medical_shifts.second.paid, "shift" => paid_medical_shifts.second.shift, - "title" => "#{second_hospital_name} | #{second_workload} | #{second_shift}" + "title" => "#{second_hospital_name} | #{second_workload} | #{second_shift}", + "color" => paid_medical_shifts.second.color }, { "id" => paid_medical_shifts.first.id, @@ -147,7 +150,8 @@ "amount_cents" => paid_medical_shifts.first.amount.format, "paid" => paid_medical_shifts.first.paid, "shift" => paid_medical_shifts.first.shift, - "title" => "#{first_hospital_name} | #{first_workload} | #{first_shift}" + "title" => "#{first_hospital_name} | #{first_workload} | #{first_shift}", + "color" => paid_medical_shifts.first.color } ) end @@ -176,7 +180,8 @@ "amount_cents" => unpaid_medical_shifts.second.amount.format, "paid" => unpaid_medical_shifts.second.paid, "shift" => unpaid_medical_shifts.second.shift, - "title" => "#{second_hospital_name} | #{second_workload} | #{second_shift}" + "title" => "#{second_hospital_name} | #{second_workload} | #{second_shift}", + "color" => unpaid_medical_shifts.second.color }, { "id" => unpaid_medical_shifts.first.id, @@ -188,7 +193,8 @@ "amount_cents" => unpaid_medical_shifts.first.amount.format, "paid" => unpaid_medical_shifts.first.paid, "shift" => unpaid_medical_shifts.first.shift, - "title" => "#{first_hospital_name} | #{first_workload} | #{first_shift}" + "title" => "#{first_hospital_name} | #{first_workload} | #{first_shift}", + "color" => unpaid_medical_shifts.first.color } ) end @@ -255,7 +261,8 @@ start_date: "2024-01-29", start_hour: "10:51:23", amount_cents: 1, - paid: false + paid: false, + color: "#000000" } post path, params: params, headers: headers @@ -273,7 +280,8 @@ amount_cents: MedicalShift.last.amount.format, paid: params[:paid], shift: MedicalShift.last.shift, - title: "#{hospital_name} | #{workload} | #{shift}" + title: "#{hospital_name} | #{workload} | #{shift}", + color: "#000000" ) end end