diff --git a/app/models/exports/pdf/common/common.rb b/app/models/exports/pdf/common/common.rb index b4fabe6710d..d3e59d45d17 100644 --- a/app/models/exports/pdf/common/common.rb +++ b/app/models/exports/pdf/common/common.rb @@ -378,8 +378,16 @@ module Exports::PDF::Common::Common make_link_anchor(work_package.id, escape_tags(value)) end + def prawn_color(color) + color&.hexcode&.sub("#", "") || "F0F0F0" + end + + def status_prawn_color(status) + prawn_color(status&.color) + end + def wp_status_prawn_color(work_package) - work_package.status.color&.hexcode&.sub("#", "") || "F0F0F0" + status_prawn_color(work_package.status) end def add_pdf_table_anchors(prawn_table) diff --git a/app/models/project/pdf_export/project_initiation.rb b/app/models/project/pdf_export/project_initiation.rb index 3b8d7aa7ae8..71ab84ef54a 100644 --- a/app/models/project/pdf_export/project_initiation.rb +++ b/app/models/project/pdf_export/project_initiation.rb @@ -34,6 +34,7 @@ class Project::PDFExport::ProjectInitiation < Exports::Exporter include Exports::PDF::Common::Logo include Exports::PDF::Common::Macro include Exports::PDF::Common::Markdown + include Exports::PDF::Common::Badge include Exports::PDF::Components::Page include Exports::PDF::Components::Cover include Project::PDFExport::Common::ProjectAttributes @@ -86,8 +87,9 @@ class Project::PDFExport::ProjectInitiation < Exports::Exporter end def render_project_initiation + pdf.title = heading write_cover_page! if with_cover? - write_title! + write_project_initiation_title write_project_initiation write_headers_footers end @@ -202,6 +204,52 @@ class Project::PDFExport::ProjectInitiation < Exports::Exporter end end + def write_project_initiation_title + status = project_initiation_status + return write_title! if status.nil? + + write_title_with_badge(status.name, status_prawn_color(status)) + end + + def write_title_with_badge(badge_text, color) + offset = styles.status_badge_offset + with_margin(styles.page_heading_margins) do + pdf.formatted_text( + title_fragments(badge_text, color, offset), + title_options(badge_text, offset) + ) + end + end + + def title_fragments(badge_text, color, offset) + badge_style = styles.status_badge + [ + styles.page_heading.merge(text: heading), + { text: " " }, + prawn_badge(badge_text, color, offset: offset, font_size: badge_style[:size], line_height: badge_style[:size]) + ] + end + + def title_options(badge_text, offset) + styles.page_heading.merge(draw_text_callback: prawn_badge_draw_text_callback(badge_text, offset)) + end + + def project_initiation_status + status = project_initiation_work_package_status + return status unless status.nil? + + return nil if project.project_creation_wizard_status_when_submitted_id.blank? + + Status.find_by(id: project.project_creation_wizard_status_when_submitted_id) + end + + def project_initiation_work_package_status + return nil if project.project_creation_wizard_artifact_work_package_id.blank? + + work_package = WorkPackage.find_by(id: project.project_creation_wizard_artifact_work_package_id) + work_package&.status + end + def write_project_initiation collect_sections_data.each do |section| next if section[:fields].empty? diff --git a/app/models/project/pdf_export/project_initiation/schema.json b/app/models/project/pdf_export/project_initiation/schema.json index b99bc5d899f..524ee3f69ab 100644 --- a/app/models/project/pdf_export/project_initiation/schema.json +++ b/app/models/project/pdf_export/project_initiation/schema.json @@ -342,6 +342,11 @@ "margin_bottom": 4 } }, + "properties": { + "badge": { + "$ref": "#/$defs/badge" + } + }, "allOf": [ { "$ref": "#/$defs/font" @@ -472,6 +477,28 @@ } ] }, + "badge": { + "type": "object", + "title": "Status Badge", + "x-example": { + "badge": { + "offset": 1, + "size": 8 + } + }, + "properties": { + "offset": { + "title": "Offset in title", + "examples": [1], + "$ref": "#/$defs/measurement_signed" + } + }, + "allOf": [ + { + "$ref": "#/$defs/font" + } + ] + }, "section": { "title": "Section", "description": "Styling for a section in the PDF report export", diff --git a/app/models/project/pdf_export/project_initiation/standard.yml b/app/models/project/pdf_export/project_initiation/standard.yml index faa77c0c85b..832239f4873 100644 --- a/app/models/project/pdf_export/project_initiation/standard.yml +++ b/app/models/project/pdf_export/project_initiation/standard.yml @@ -81,6 +81,9 @@ project: color: "000000" margin_bottom: 8 styles: [ "bold" ] + badge: + size: 10 + offset: 1 markdown_label: size: 10 color: "000000" diff --git a/app/models/project/pdf_export/project_initiation/styles.rb b/app/models/project/pdf_export/project_initiation/styles.rb index bbe635bc900..78196f98614 100644 --- a/app/models/project/pdf_export/project_initiation/styles.rb +++ b/app/models/project/pdf_export/project_initiation/styles.rb @@ -48,6 +48,14 @@ module Project::PDFExport::ProjectInitiation::Styles } end + def status_badge + resolve_font(@styles.dig(:project, :title, :badge)) + end + + def status_badge_offset + resolve_pt(@styles.dig(:project, :title, :badge, :offset), 0) + end + def section_title_margins resolve_margin(@styles.dig(:section, :title)) end diff --git a/script/pdf_export/styles.yml b/script/pdf_export/styles.yml index 31f07c61ce4..18f53c451b9 100644 --- a/script/pdf_export/styles.yml +++ b/script/pdf_export/styles.yml @@ -9,3 +9,5 @@ styles: path: "modules/meeting/app/workers/meetings/pdf/default" - name: "project-report" path: "app/models/projects/exports/pdf_export" + - name: "project-initiation" + path: "app/models/project/pdf_export/project_initiation" diff --git a/spec/models/project/pdf_export/project_initiation_spec.rb b/spec/models/project/pdf_export/project_initiation_spec.rb index b3e349a24dd..ad78b5b4504 100644 --- a/spec/models/project/pdf_export/project_initiation_spec.rb +++ b/spec/models/project/pdf_export/project_initiation_spec.rb @@ -43,6 +43,9 @@ RSpec.describe Project::PDFExport::ProjectInitiation, with_flag: { project_initi let(:current_user) { create(:user, member_with_permissions: { project => %i[view_projects view_project_attributes] }) } let(:export_time) { DateTime.new(2025, 11, 13, 13, 37) } let(:export_time_formatted) { format_time(export_time) } + let(:wizard_status) { create(:status, name: "Submitted") } + let(:status) { create(:status, name: "Approved") } + let(:work_package) { create(:work_package, status:) } let(:custom_artefact_name_key) { "project_mandate" } let(:section_a) { create(:project_custom_field_section, name: "Section A") } let(:section_b) { create(:project_custom_field_section, name: "Section B") } @@ -54,6 +57,7 @@ RSpec.describe Project::PDFExport::ProjectInitiation, with_flag: { project_initi project_custom_field: disabled_custom_field, creation_wizard: false) end + let(:heading) { project_creation_wizard_name(project) } subject do result = Timecop.freeze(export_time) do @@ -74,7 +78,9 @@ RSpec.describe Project::PDFExport::ProjectInitiation, with_flag: { project_initi let(:current_user) { create(:admin) } it "exports a PDF containing project initiation using the custom defined name" do - custom_artefact_name = project_creation_wizard_name(project) + custom_artefact_name = I18n.t(project.project_creation_wizard_artifact_name, + default: :project_initiation_request, + scope: "settings.project_initiation_request.name.options") expected_document = [ project.name, custom_artefact_name, export_time_formatted, # cover page custom_artefact_name, @@ -106,8 +112,6 @@ RSpec.describe Project::PDFExport::ProjectInitiation, with_flag: { project_initi end it "exports a PDF containing project initiation with custom attributes grouped by sections" do - heading = project_creation_wizard_name(project) - expected_document = [ project.name, heading, export_time_formatted, # cover page @@ -137,4 +141,36 @@ RSpec.describe Project::PDFExport::ProjectInitiation, with_flag: { project_initi expect(subject).to eq expected_document end end + + context "with a status" do + let(:project) { create(:project, project_creation_wizard_status_when_submitted_id: wizard_status.id) } + + it "exports a PDF containing project initiation using the custom defined name" do + expected_document = [ + project.name, heading, export_time_formatted, # cover page + heading, " ", "    Submitted    ", + "Project", + "Name", project.name, + "Description", "–", + "1/1", export_time_formatted, "#{project.name} | #{heading}" + ].join(" ") + expect(subject).to eq expected_document + end + end + + context "with a work package status" do + let(:project) { create(:project, project_creation_wizard_artifact_work_package_id: work_package.id) } + + it "exports a PDF containing project initiation using the custom defined name" do + expected_document = [ + project.name, heading, export_time_formatted, # cover page + heading, " ", "    Approved    ", + "Project", + "Name", project.name, + "Description", "–", + "1/1", export_time_formatted, "#{project.name} | #{heading}" + ].join(" ") + expect(subject).to eq expected_document + end + end end