From 4c25599e0385fb2937304c07265eb1f02bf89de2 Mon Sep 17 00:00:00 2001 From: Shafay Javed Date: Mon, 30 Sep 2024 13:06:51 +0100 Subject: [PATCH] sending emails to additional contacts --- app/assets/stylesheets/base/_frontend.scss | 4 ++ .../cases/message_threads_controller.rb | 10 ++- .../cases/messages/replies_controller.rb | 57 +++++++++++++++++- .../tickets/message_threads_controller.rb | 5 ++ app/javascript/components/add-recipients.js | 39 +++++++++--- app/models/email/draft.rb | 1 + .../frameworks/evaluation/email_ticketable.rb | 3 +- app/models/support/case.rb | 4 ++ .../message_threads/_recipient_table.html.erb | 20 +++--- .../message_threads/_recipients.html.erb | 30 +++++++-- .../cases/messages/replies/_form.html.erb | 7 ++- .../messages/replies/add_recipient.html.erb | 1 + .../cases/messages/replies/edit.html.erb | 1 - config/routes.rb | 2 + db/schema.rb | 11 ---- dump.rdb | Bin 0 -> 88 bytes lib/microsoft_graph/client.rb | 10 ++- 17 files changed, 160 insertions(+), 45 deletions(-) create mode 100644 app/views/support/cases/messages/replies/add_recipient.html.erb create mode 100644 dump.rdb diff --git a/app/assets/stylesheets/base/_frontend.scss b/app/assets/stylesheets/base/_frontend.scss index 7704a81db..5965cf5d3 100644 --- a/app/assets/stylesheets/base/_frontend.scss +++ b/app/assets/stylesheets/base/_frontend.scss @@ -12,4 +12,8 @@ $govuk-new-organisation-colours: true; td.govuk-table__cell :first-child { margin-top: 0; +} + +.recipient-spacing { + margin-right: 8px; /* Adjust as needed */ } \ No newline at end of file diff --git a/app/controllers/support/cases/message_threads_controller.rb b/app/controllers/support/cases/message_threads_controller.rb index 2cdf1dcc5..5b5ef9048 100644 --- a/app/controllers/support/cases/message_threads_controller.rb +++ b/app/controllers/support/cases/message_threads_controller.rb @@ -26,7 +26,7 @@ def show end def new - @to_recipients = Array(current_case.email).to_json + @to_recipients = Array(current_case.email + @current_case.additional_contacts.pluck(:email)).to_json end def edit @@ -34,12 +34,14 @@ def edit end def create + contact_email = [[current_case.email, current_case.is_evaluator ? "Lead, Evaluator" : "Lead"]] if current_case.email.present? + emails = Array(contact_email + current_case.additional_contacts_emails) draft = Email::Draft.new( default_content: default_template, default_subject:, template_id: params[:template_id], ticket: current_case.to_model, - to_recipients: Array(current_case.email).to_json, + to_recipients: emails.to_json, ).save_draft! redirect_to edit_support_case_message_thread_path(id: draft.id) @@ -48,6 +50,10 @@ def create def submit @reply_form = Email::Draft.find(params[:id]) @reply_form.attributes = new_thread_params + emails = @reply_form.to_recipients.map(&:first) + @reply_form.to_recipients = emails.to_json + @reply_form.cc_recipients = @reply_form.cc_recipients.map(&:first).to_json if @reply_form.cc_recipients.present? + @reply_form.bcc_recipients = @reply_form.bcc_recipients.map(&:first).to_json if @reply_form.bcc_recipients.present? if @reply_form.valid?(:new_message) @reply_form.save_draft! @reply_form.deliver_as_new_message diff --git a/app/controllers/support/cases/messages/replies_controller.rb b/app/controllers/support/cases/messages/replies_controller.rb index fe7722949..00e51be64 100644 --- a/app/controllers/support/cases/messages/replies_controller.rb +++ b/app/controllers/support/cases/messages/replies_controller.rb @@ -15,7 +15,8 @@ def create default_content: default_template, template_id: params[:template_id], ticket: current_case.to_model, - reply_to_email: current_email, + reply_to_email: [current_email], + role: %w[Lead] + [current_case.is_evaluator == true ? "Evaulator" : ""], ).save_draft! redirect_to redirect_url @@ -23,8 +24,17 @@ def create def submit @reply_form = Email::Draft.find(params[:id]) + emails = @reply_form.to_recipients.map(&:first) + to_recipients_email = current_email.to_recipients.instance_of?(Array) ? current_email.to_recipients.map { |recipient| recipient["address"] } : [current_email.to_recipients["address"]] + cc_recipients_email = current_email.cc_recipients.instance_of?(Array) ? current_email.cc_recipients.map { |recipient| recipient["address"] } : [current_email.cc_recipients["address"]] + bcc_recipients_email = current_email.bcc_recipients.instance_of?(Array) ? current_email.bcc_recipients.map { |recipient| recipient["address"] } : [current_email.bcc_recipients["address"]] @reply_form.reply_to_email = current_email + @reply_form.to_recipients = emails.present? ? emails.uniq.to_json : to_recipients_email.to_json + @reply_form.cc_recipients = @reply_form.cc_recipients.present? ? @reply_form.cc_recipients.map(&:first).to_json : cc_recipients_email.to_json + @reply_form.bcc_recipients = @reply_form.bcc_recipients.present? ? @reply_form.bcc_recipients.map(&:first).to_json : bcc_recipients_email.to_json @reply_form.attributes = form_params + @reply_form.email.to_recipients = emails + @reply_form.reply_to_email.to_recipients = emails if @reply_form.valid? @reply_form.save_draft! @reply_form.delivery_as_reply @@ -47,6 +57,51 @@ def back_to_url_b64 current_url_b64 end + def add_recipient + @reply_form = Email::Draft.find(params[:id]) + sender_email = current_email.sender.instance_of?(Array) ? [current_email.sender.map { |recipient| [recipient["address"], [""]] }] : [[current_email.sender["address"], [""]]] + @sender_email = current_email.sender.instance_of?(Array) ? current_email.sender.map { |recipient| recipient["address"] } : [current_email.sender["address"]] + current_case_role = current_case.is_evaluator ? ["Lead, Evaluator"] : %w[Lead] + emails = [[current_case.email, current_case_role]] + current_case.additional_contacts_emails + sender_email + emails.each do |email| + # Extract the email part from @reply_form.to_recipients for comparison + existing_emails = @reply_form.to_recipients.map { |recipient| recipient[0] } + # Add the email only if it's not already in the list (ignoring roles) + @reply_form.to_recipients << email unless existing_emails.include?(email[0]) + end + if current_email.cc_recipients.present? && !@reply_form.cc_recipients.map { |recipient| recipient[0] }.include?(current_email.cc_recipients[0]["address"]) + sender_cc_recipients = current_email.cc_recipients.instance_of?(Array) ? current_email.cc_recipients.map { |recipient| [recipient["address"], [""]] } : current_email.cc_recipients[0]["address"] if current_email.cc_recipients.present? + @reply_form.cc_recipients = Array(@reply_form.cc_recipients + sender_cc_recipients).to_json if sender_cc_recipients.present? + end + if current_email.bcc_recipients.present? && !@reply_form.bcc_recipients.map { |recipient| recipient[0] }.include?(current_email.bcc_recipients[0]["address"]) + sender_bcc_recipients = current_email.bcc_recipients.instance_of?(Array) ? current_email.bcc_recipients[0]["address"] : [[current_email.bcc_recipients[0]["address"], [""]]] if current_email.bcc_recipients.present? + @reply_form.bcc_recipients = Array(@reply_form.bcc_recipients + sender_bcc_recipients).to_json if sender_bcc_recipients.present? + end + @reply_form.save_draft! + respond_to do |format| + format.turbo_stream do + render turbo_stream: turbo_stream.replace("recipient-frame", + partial: "support/cases/message_threads/recipient_table", + locals: { email: @reply_form }) + end + end + end + + def remove_recipient + @reply_form = Email::Draft.find(params[:id]) + @sender_email = current_email.sender.instance_of?(Array) ? current_email.sender.map { |recipient| recipient["address"] } : [current_email.sender["address"]] + recipient_to_remove = [params[:email], params[:role]] + @reply_form.to_recipients.reject! { |recipient| recipient == recipient_to_remove } + @reply_form.save_draft! + respond_to do |format| + format.turbo_stream do + render turbo_stream: turbo_stream.replace("recipient-frame", + partial: "support/cases/message_threads/recipient_table", + locals: { email: @reply_form }) + end + end + end + private def default_template = render_to_string(partial: "support/cases/messages/reply_form_template") diff --git a/app/controllers/tickets/message_threads_controller.rb b/app/controllers/tickets/message_threads_controller.rb index 8ffe215b6..ae4cb7adc 100644 --- a/app/controllers/tickets/message_threads_controller.rb +++ b/app/controllers/tickets/message_threads_controller.rb @@ -38,6 +38,11 @@ def create def submit @draft = Email::Draft.find(params[:id]) @draft.attributes = new_thread_params + emails = @draft.to_recipients.map(&:first) + @draft.to_recipients = emails.uniq.to_json + @draft.cc_recipients = @draft.cc_recipients.map(&:first).to_json if @draft.cc_recipients.present? + @draft.bcc_recipients = @draft.bcc_recipients.map(&:first).to_json if @draft.bcc_recipients.present? + @draft.email.to_recipients = emails.uniq.to_json if @draft.valid?(:new_message) @draft.save_draft! @draft.deliver_as_new_message diff --git a/app/javascript/components/add-recipients.js b/app/javascript/components/add-recipients.js index d41c24b99..758cc1f41 100644 --- a/app/javascript/components/add-recipients.js +++ b/app/javascript/components/add-recipients.js @@ -6,7 +6,9 @@ function getCollectionValues(collectionName) { function removeRecipient(value, collectionName) { const collection = document.getElementsByName(collectionName)[0]; const collectionValues = getCollectionValues(collectionName); - collection.value = JSON.stringify(collectionValues.filter(v => v !== value)); + collection.value = JSON.stringify( + collectionValues.filter(v => JSON.stringify(v) !== JSON.stringify(value)) + ); } function createRemoveLink(tableId, inputValue, collectionName) { @@ -33,22 +35,38 @@ function createRecipientRow(tableId, label, value, collectionName) { labelTh.scope = "row"; labelTh.appendChild(document.createTextNode(label)); - const recipientTd = document.createElement("td"); - recipientTd.classList.add("govuk-table__cell"); - const recipientEmail = document.createTextNode(value); - recipientTd.appendChild(recipientEmail); + const recipientEmailTd = document.createElement("td"); + recipientEmailTd.classList.add("govuk-table__cell"); + const recipientEmail = document.createTextNode(value[0]); + recipientEmailTd.appendChild(recipientEmail); + + const recipientRoleTd = document.createElement("td"); + recipientRoleTd.classList.add("govuk-table__cell", "recipient-spacing"); + const rolevalue = value[1] && value[1].length > 0 + ? (value[1][1] && value[1][1].length > 1) + ? [capitalizeFirstLetter(value[1][0]), capitalizeFirstLetter(value[1][1])] + : value[1][0].length > 1 + ? [capitalizeFirstLetter(value[1][0])] // Handle the single element correctly here + : [capitalizeFirstLetter(value[1])] + : ""; const recipientRole = document.createTextNode(rolevalue); + recipientRoleTd.appendChild(recipientRole); const recipientRemoveTd = document.createElement("td"); recipientRemoveTd.classList.add("govuk-table__cell", "govuk-table__cell--numeric"); recipientRemoveTd.appendChild(createRemoveLink(tableId, value, collectionName)); recipientRow.appendChild(labelTh); - recipientRow.appendChild(recipientTd); + recipientRow.appendChild(recipientEmailTd); + recipientRow.appendChild(recipientRoleTd); recipientRow.appendChild(recipientRemoveTd); return recipientRow; } +function capitalizeFirstLetter(string) { + return string.charAt(0).toUpperCase() + string.slice(1).toLowerCase(); +} + function showTables(inputFieldName) { const inputField = document.getElementsByName(inputFieldName)[0]; buildTable(inputField.dataset.table, inputField.dataset.collection); @@ -69,14 +87,19 @@ function addRecipient(inputFieldName) { const inputField = document.getElementsByName(inputFieldName)[0]; const collection = document.getElementsByName(inputField.dataset.collection)[0]; var collectionValues = collection.value ? JSON.parse(collection.value) : []; + const inputValue = inputField.value; - collectionValues.push(inputValue); - collectionValues = [...new Set(collectionValues)].filter(Boolean) // ensure values are unique and non-null + const newRecipient = [inputValue, []]; + collectionValues.push(newRecipient); + collectionValues = collectionValues.filter((item, index, self) => + item[0] && self.findIndex(r => r[0] === item[0]) === index + ); collection.value = JSON.stringify(collectionValues); buildTable(inputField.dataset.table, inputField.dataset.collection); inputField.value = ""; } + function getRecipientsButtons() { return document.querySelectorAll('[data-component="add-recipients"]'); } diff --git a/app/models/email/draft.rb b/app/models/email/draft.rb index f33ce73e4..7888562c4 100644 --- a/app/models/email/draft.rb +++ b/app/models/email/draft.rb @@ -7,6 +7,7 @@ class Email::Draft attribute :mailbox, default: -> { Email.default_mailbox } attribute :microsoft_graph, default: -> { MicrosoftGraph.client } attribute :reply_to_email + attribute :role attribute :ticket attribute :template_id attribute :template_parser, default: -> { Email::TemplateParser.new } diff --git a/app/models/frameworks/evaluation/email_ticketable.rb b/app/models/frameworks/evaluation/email_ticketable.rb index 6328ee8ad..6dbb87836 100644 --- a/app/models/frameworks/evaluation/email_ticketable.rb +++ b/app/models/frameworks/evaluation/email_ticketable.rb @@ -14,7 +14,8 @@ def email_prefix end def default_recipients - Array(contact.try(:email)).to_json + emails = contact.try(:email) || "" + Array([[emails, ""]]).to_json if emails.present? end def unique_attachments(folder: "all") diff --git a/app/models/support/case.rb b/app/models/support/case.rb index 3146768dd..5e389db83 100644 --- a/app/models/support/case.rb +++ b/app/models/support/case.rb @@ -203,5 +203,9 @@ def assign_to_agent(agent, assigned_by: Current.agent) update!(agent:) agent.notify_assigned_to_case(support_case: self, assigned_by:) end + + def additional_contacts_emails + case_additional_contacts.pluck(:email, :role) + end end end diff --git a/app/views/support/cases/message_threads/_recipient_table.html.erb b/app/views/support/cases/message_threads/_recipient_table.html.erb index 95f67e7c9..74e3f4b2b 100644 --- a/app/views/support/cases/message_threads/_recipient_table.html.erb +++ b/app/views/support/cases/message_threads/_recipient_table.html.erb @@ -1,11 +1,9 @@ -
-
- - - <%= render "support/cases/message_threads/recipients", recipients: email.to_recipients, recipient_type: "TO" %> - <%= render "support/cases/message_threads/recipients", recipients: email.cc_recipients, recipient_type: "CC" %> - <%= render "support/cases/message_threads/recipients", recipients: email.bcc_recipients, recipient_type: "BCC" %> - -
-
-
+<%= turbo_frame_tag "recipient-frame" do %> + + + <%= render "support/cases/message_threads/recipients", recipients: email.to_recipients, recipient_type: "TO" %> + <%= render "support/cases/message_threads/recipients", recipients: email.cc_recipients, recipient_type: "CC" %> + <%= render "support/cases/message_threads/recipients", recipients: email.bcc_recipients, recipient_type: "BCC" %> + +
+<% end %> diff --git a/app/views/support/cases/message_threads/_recipients.html.erb b/app/views/support/cases/message_threads/_recipients.html.erb index a2a81032d..db9dff2ee 100644 --- a/app/views/support/cases/message_threads/_recipients.html.erb +++ b/app/views/support/cases/message_threads/_recipients.html.erb @@ -1,6 +1,24 @@ -<% if recipients.present? %> - - <%= recipient_type %> - <%= recipients %> - -<% end %> +<% if recipients.present?%> + <%if params[:action] == "add_recipient" || params[:action] == "remove_recipient"%> + <%recipients.each do |recipient|%> + + <%= recipient_type %> + <%= recipient[0] %> + <%= recipient[1].join(", ").titleize %> + + <% unless @sender_email.present? && (@sender_email.include?(recipient[0]))%> + <%= link_to "remove", + remove_recipient_support_case_message_replies_path(id: params[:id], email: recipient[0], role: recipient[1]), + data: { turbo_frame: "recipient-frame", turbo_method: "post" }, + class: "govuk-link" %> + <%end%> + + + <%end%> + <%else%> + + <%= recipient_type %> + <%= recipients %> + + <%end%> +<%end%> \ No newline at end of file diff --git a/app/views/support/cases/messages/replies/_form.html.erb b/app/views/support/cases/messages/replies/_form.html.erb index a1f3a1e32..4ded216fa 100644 --- a/app/views/support/cases/messages/replies/_form.html.erb +++ b/app/views/support/cases/messages/replies/_form.html.erb @@ -22,8 +22,13 @@ <%= I18n.t("support.case.label.message_threads.using_template", template: @reply_form.template.title) %> <% end %> - <% if reply %> +
+ <%= link_to "Add Additional Contacts", + add_recipient_support_case_message_replies_path(id: params[:id]), + data: { turbo_frame: "recipient-frame", turbo_method: "post" }, + class: "govuk-button" %> +
<%= render "support/cases/message_threads/recipient_table", email: %> <% else %> <%= render "support/cases/messages/replies/new_message_components", form:, unique_id: %> diff --git a/app/views/support/cases/messages/replies/add_recipient.html.erb b/app/views/support/cases/messages/replies/add_recipient.html.erb new file mode 100644 index 000000000..363472e32 --- /dev/null +++ b/app/views/support/cases/messages/replies/add_recipient.html.erb @@ -0,0 +1 @@ +<%= render "support/cases/message_threads/recipient_table", email: @reply_form%> \ No newline at end of file diff --git a/app/views/support/cases/messages/replies/edit.html.erb b/app/views/support/cases/messages/replies/edit.html.erb index 236f85b5f..f3f3448fe 100644 --- a/app/views/support/cases/messages/replies/edit.html.erb +++ b/app/views/support/cases/messages/replies/edit.html.erb @@ -8,6 +8,5 @@ <% end %> - <%= render "support/cases/messages/replies/form", email: @last_received_reply, reply: true %> <% end %> \ No newline at end of file diff --git a/config/routes.rb b/config/routes.rb index 184acf1bb..e3961798a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -253,6 +253,8 @@ scope module: :messages do resources :replies, only: %i[create edit] do post "submit", on: :member + post "add_recipient", on: :collection + post "remove_recipient", on: :collection end end end diff --git a/db/schema.rb b/db/schema.rb index 44a01e8bf..c3d079e35 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -399,8 +399,6 @@ t.string "name", null: false t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.boolean "archived" - t.datetime "archived_at" t.index ["la_code"], name: "index_local_authorities_on_la_code", unique: true t.index ["name"], name: "index_local_authorities_on_name", unique: true end @@ -775,10 +773,6 @@ t.uuid "establishment_group_type_id" t.datetime "created_at", null: false t.datetime "updated_at", null: false - t.boolean "archived" - t.datetime "archived_at" - t.date "opened_date" - t.date "closed_date" t.index ["establishment_group_type_id"], name: "index_establishment_groups_on_establishment_group_type_id" t.index ["name"], name: "index_support_establishment_groups_on_name" t.index ["uid"], name: "index_support_establishment_groups_on_uid", unique: true @@ -884,11 +878,6 @@ t.string "federation_name" t.string "federation_code" t.uuid "local_authority_id" - t.boolean "archived" - t.datetime "archived_at" - t.date "closed_date" - t.string "reason_establishment_opened" - t.string "reason_establishment_closed" t.index ["establishment_type_id"], name: "index_support_organisations_on_establishment_type_id" t.index ["local_authority_id"], name: "index_support_organisations_on_local_authority_id" t.index ["urn"], name: "index_support_organisations_on_urn", unique: true diff --git a/dump.rdb b/dump.rdb new file mode 100644 index 0000000000000000000000000000000000000000..93adff75e16bef99c1977162395e6e5f848342df GIT binary patch literal 88 zcmWG?b@2=~FfcUy#aWb^l3A= { "address" => email } } } + end + { body: { "ContentType" => "HTML", @@ -193,6 +194,9 @@ def details_for_reply(draft, draft_message) # for it to appear under a "fold" in the email client "content" => "#{draft.body}#{draft_message.body.content}", }, + toRecipients: email_addresses[draft.to_recipients], + ccRecipients: email_addresses[draft.cc_recipients], + bccRecipients: email_addresses[draft.bcc_recipients], } end