From 99fde890175608b272f9304d5bfd258d78f10b83 Mon Sep 17 00:00:00 2001
From: Cameron Dixon
Date: Thu, 20 Jun 2024 17:16:40 -0400
Subject: [PATCH 01/32] Update domain_invitation_description.html
added more detail to give better direction to analysts
---
.../includes/descriptions/domain_invitation_description.html | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html b/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
index 7765b9203..3a5609ee8 100644
--- a/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
+++ b/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
@@ -5,8 +5,8 @@ accept and become a domain manager.
-An “invited” status indicates that the recipient has not logged in to the registrar since the invitation was sent.
-A “received” status indicates that the recipient has logged in.
+An “invited” status indicates that the recipient has not logged in to the registrar since the invitation was sent. Deleting an invitation with a "invited" status will prevent the user from signing in.
+A “received” status indicates that the recipient has logged in. Deleting an invitation with a "received" status will stop a user's email address from showing in the domain managers list, but it will not revoke their access from the domain. To remove a user who has already signed in, go to User domain roles and delete the role for the correct domain/manager combination.
From 9d116df83fd29f7f056504b877c599d7296c2d73 Mon Sep 17 00:00:00 2001
From: Cameron Dixon
Date: Fri, 21 Jun 2024 21:46:31 -0400
Subject: [PATCH 02/32] Update
src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
Co-authored-by: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
---
.../includes/descriptions/domain_invitation_description.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html b/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
index 3a5609ee8..03d0cd99a 100644
--- a/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
+++ b/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
@@ -6,7 +6,7 @@ accept and become a domain manager.
An “invited” status indicates that the recipient has not logged in to the registrar since the invitation was sent. Deleting an invitation with a "invited" status will prevent the user from signing in.
-A “received” status indicates that the recipient has logged in. Deleting an invitation with a "received" status will stop a user's email address from showing in the domain managers list, but it will not revoke their access from the domain. To remove a user who has already signed in, go to User domain roles and delete the role for the correct domain/manager combination.
+A “received” status indicates that the recipient has logged in. Deleting an invitation with a "received" status will stop a user's email address from showing in the domain managers list, but it will not revoke their access from the domain. To remove a user who has already signed in, go to User domain roles and delete the role for the correct domain/manager combination.
From 64933e02c60c1ffffdf7f09d7c12430450076870 Mon Sep 17 00:00:00 2001
From: Cameron Dixon
Date: Tue, 25 Jun 2024 09:32:46 -0400
Subject: [PATCH 03/32] Update test_admin.py
adding another "invited"
---
src/registrar/tests/test_admin.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/tests/test_admin.py b/src/registrar/tests/test_admin.py
index 802974b6e..798dfaad8 100644
--- a/src/registrar/tests/test_admin.py
+++ b/src/registrar/tests/test_admin.py
@@ -2820,7 +2820,7 @@ class TestDomainInvitationAdmin(TestCase):
)
# Assert that the filters are added
- self.assertContains(response, "invited", count=4)
+ self.assertContains(response, "invited", count=5)
self.assertContains(response, "Invited", count=2)
self.assertContains(response, "retrieved", count=2)
self.assertContains(response, "Retrieved", count=2)
From 2e28a0ffe5c22a059bb136bcd64ba16e4f5b0dcb Mon Sep 17 00:00:00 2001
From: Cameron Dixon
Date: Tue, 25 Jun 2024 12:53:08 -0400
Subject: [PATCH 04/32] Update
src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
Co-authored-by: Katherine-Osos <119689946+Katherine-Osos@users.noreply.github.com>
---
.../includes/descriptions/domain_invitation_description.html | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html b/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
index 03d0cd99a..23c617f1c 100644
--- a/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
+++ b/src/registrar/templates/django/admin/includes/descriptions/domain_invitation_description.html
@@ -5,7 +5,7 @@ accept and become a domain manager.
-An “invited” status indicates that the recipient has not logged in to the registrar since the invitation was sent. Deleting an invitation with a "invited" status will prevent the user from signing in.
+An “invited” status indicates that the recipient has not logged in to the registrar since the invitation was sent. Deleting an invitation with an "invited" status will prevent the user from signing in.
A “received” status indicates that the recipient has logged in. Deleting an invitation with a "received" status will stop a user's email address from showing in the domain managers list, but it will not revoke their access from the domain. To remove a user who has already signed in, go to User domain roles and delete the role for the correct domain/manager combination.
From b4214300246ecd0306bff93983370a6ee61b8312 Mon Sep 17 00:00:00 2001
From: Rebecca Hsieh
Date: Wed, 17 Jul 2024 10:50:01 -0700
Subject: [PATCH 05/32] Test out copy functionality
---
src/registrar/templates/admin/change_form.html | 5 +++++
.../templates/admin/change_form_object_tools.html | 9 ++++++++-
2 files changed, 13 insertions(+), 1 deletion(-)
diff --git a/src/registrar/templates/admin/change_form.html b/src/registrar/templates/admin/change_form.html
index 78dac9ac0..3efbe554e 100644
--- a/src/registrar/templates/admin/change_form.html
+++ b/src/registrar/templates/admin/change_form.html
@@ -1,4 +1,5 @@
{% extends "admin/change_form.html" %}
+{% load static i18n %}
{% comment %} Replace the Django ul markup with a div. We'll edit the child markup accordingly in change_form_object_tools {% endcomment %}
{% block object-tools %}
@@ -10,3 +11,7 @@
{% endif %}
{% endblock %}
+
+{% block extrahead %}
+
+{% endblock %}
\ No newline at end of file
diff --git a/src/registrar/templates/admin/change_form_object_tools.html b/src/registrar/templates/admin/change_form_object_tools.html
index 28c655bbc..48a6b101d 100644
--- a/src/registrar/templates/admin/change_form_object_tools.html
+++ b/src/registrar/templates/admin/change_form_object_tools.html
@@ -16,5 +16,12 @@
{% translate "History" %}
+ {% if opts.model_name == 'domainrequest' %}
+
+ {% translate "Copy request summary" %}
+
+
+ {% endif %}
{% endif %}
-{% endblock %}
\ No newline at end of file
+{% endblock %}
+
From 721e91aac75b77742c229dc7516d03fe865bd3b8 Mon Sep 17 00:00:00 2001
From: Rebecca Hsieh
Date: Wed, 17 Jul 2024 11:05:09 -0700
Subject: [PATCH 06/32] Add in js file
---
src/registrar/assets/js/copy-summary.js | 34 +++++++++++++++++++++++++
1 file changed, 34 insertions(+)
create mode 100644 src/registrar/assets/js/copy-summary.js
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
new file mode 100644
index 000000000..42cedf008
--- /dev/null
+++ b/src/registrar/assets/js/copy-summary.js
@@ -0,0 +1,34 @@
+
+document.addEventListener('DOMContentLoaded', function() {
+ document.getElementById('copy-summary-btn').addEventListener('click', function() {
+ // Generate the summary text
+ const organizationType = document.getElementById('id_organization_type').value;
+ const requestedDomain = document.getElementById('id_requested_domain').value;
+ const existingWebsites = Array.from(document.querySelectorAll('#id_current_websites')).map(el => el.text).join(', ');
+ const alternativeDomains = Array.from(document.querySelectorAll('#id_alternative_domains')).map(el => el.text).join(', ');
+ const submitter = document.getElementById('id_submitter').value;
+ const seniorOfficial = document.getElementById('id_senior_official').value;
+ const otherContacts = Array.from(document.querySelectorAll('#id_other_contacts option:checked')).map(el => el.text).join('\n* ');
+
+ const summary = `*Recommendation:*\n\n` +
+ `*Organization Type:* ${organizationType}\n\n` +
+ `*Requested Domain:* ${requestedDomain}\n\n` +
+ `*Existing website(s):*\n${existingWebsites}\n\n` +
+ `*Rationale:*\n\n` +
+ `*Alternate Domain(s):*\n* ${alternativeDomains.split(', ').join('\n* ')}\n\n` +
+ `*Submitter:*\n\n* ${submitter}\n\n` +
+ `*Senior Official:*\n\n* ${seniorOfficial}\n\n` +
+ `*Additional Contact(s):*\n\n* ${otherContacts}\n\n`;
+
+ // Create a temporary textarea element to hold the summary
+ const textArea = document.createElement('textarea');
+ textArea.value = summary;
+ document.body.appendChild(textArea);
+ textArea.select();
+ document.execCommand('copy');
+ document.body.removeChild(textArea);
+
+ alert('Summary copied to clipboard!');
+ alert("hello");
+ });
+});
From e19bcaf55f61cc52d7f43cd38df19378d47290e8 Mon Sep 17 00:00:00 2001
From: Rebecca Hsieh
Date: Wed, 17 Jul 2024 11:05:42 -0700
Subject: [PATCH 07/32] Remove extra line
---
src/registrar/assets/js/copy-summary.js | 1 -
1 file changed, 1 deletion(-)
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
index 42cedf008..9edad3f6b 100644
--- a/src/registrar/assets/js/copy-summary.js
+++ b/src/registrar/assets/js/copy-summary.js
@@ -29,6 +29,5 @@ document.addEventListener('DOMContentLoaded', function() {
document.body.removeChild(textArea);
alert('Summary copied to clipboard!');
- alert("hello");
});
});
From bc334bd41fe276eb386753d931a7e284fc44567c Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Mon, 22 Jul 2024 16:25:14 -0600
Subject: [PATCH 08/32] updated button format and javascript
---
src/registrar/assets/js/copy-summary.js | 51 ++++++++++++-------
.../admin/change_form_object_tools.html | 19 +++----
2 files changed, 44 insertions(+), 26 deletions(-)
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
index 9edad3f6b..b268956a5 100644
--- a/src/registrar/assets/js/copy-summary.js
+++ b/src/registrar/assets/js/copy-summary.js
@@ -8,25 +8,42 @@ document.addEventListener('DOMContentLoaded', function() {
const alternativeDomains = Array.from(document.querySelectorAll('#id_alternative_domains')).map(el => el.text).join(', ');
const submitter = document.getElementById('id_submitter').value;
const seniorOfficial = document.getElementById('id_senior_official').value;
- const otherContacts = Array.from(document.querySelectorAll('#id_other_contacts option:checked')).map(el => el.text).join('\n* ');
+ const otherContacts = Array.from(document.querySelectorAll('#id_other_contacts option:checked')).map(el => el.text).join('\n ');
- const summary = `*Recommendation:*\n\n` +
- `*Organization Type:* ${organizationType}\n\n` +
- `*Requested Domain:* ${requestedDomain}\n\n` +
- `*Existing website(s):*\n${existingWebsites}\n\n` +
- `*Rationale:*\n\n` +
- `*Alternate Domain(s):*\n* ${alternativeDomains.split(', ').join('\n* ')}\n\n` +
- `*Submitter:*\n\n* ${submitter}\n\n` +
- `*Senior Official:*\n\n* ${seniorOfficial}\n\n` +
- `*Additional Contact(s):*\n\n* ${otherContacts}\n\n`;
+ const summary = `Recommendation: ` +
+ `Organization Type: ${organizationType}` +
+ `Requested Domain: ${requestedDomain}` +
+ `Existing website(s): ${existingWebsites}` +
+ `Rationale: ` +
+ `Alternate Domain(s): ${alternativeDomains.split(', ').join('\n ')}` +
+ `Submitter: ${submitter}` +
+ `Senior Official: ${seniorOfficial}` +
+ `Additional Contact(s): ${otherContacts}`;
- // Create a temporary textarea element to hold the summary
- const textArea = document.createElement('textarea');
- textArea.value = summary;
- document.body.appendChild(textArea);
- textArea.select();
- document.execCommand('copy');
- document.body.removeChild(textArea);
+ // Create a temporary element
+ let tempElement = document.createElement('div');
+ tempElement.innerHTML = summary;
+ // Append the element to the body
+ document.body.appendChild(tempElement);
+
+ // Use the Selection and Range APIs to select the element's content
+ let range = document.createRange();
+ range.selectNodeContents(tempElement);
+ let selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ // Use the Clipboard API to write the selected HTML content to the clipboard
+ navigator.clipboard.write([
+ new ClipboardItem({
+ 'text/html': new Blob([tempElement.innerHTML], { type: 'text/html' })
+ })
+ ]).then(() => {
+ console.log('Bold text copied to clipboard successfully!');
+ }).catch(err => {
+ console.error('Failed to copy text: ', err);
+ });
+ document.body.removeChild(tempElement);
alert('Summary copied to clipboard!');
});
diff --git a/src/registrar/templates/admin/change_form_object_tools.html b/src/registrar/templates/admin/change_form_object_tools.html
index 48a6b101d..c2d22e9e2 100644
--- a/src/registrar/templates/admin/change_form_object_tools.html
+++ b/src/registrar/templates/admin/change_form_object_tools.html
@@ -13,15 +13,16 @@
{% else %}
-
- {% translate "History" %}
-
- {% if opts.model_name == 'domainrequest' %}
-
- {% translate "Copy request summary" %}
-
-
- {% endif %}
+
{% endif %}
{% endblock %}
From b245dfb2fe801506b3bbc3a0713d5551be8d8330 Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 23 Jul 2024 10:55:02 -0600
Subject: [PATCH 09/32] fixed dom extraction
---
src/registrar/assets/js/copy-summary.js | 39 ++++++++++++++++++-------
1 file changed, 28 insertions(+), 11 deletions(-)
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
index b268956a5..d12efb872 100644
--- a/src/registrar/assets/js/copy-summary.js
+++ b/src/registrar/assets/js/copy-summary.js
@@ -2,23 +2,40 @@
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('copy-summary-btn').addEventListener('click', function() {
// Generate the summary text
- const organizationType = document.getElementById('id_organization_type').value;
- const requestedDomain = document.getElementById('id_requested_domain').value;
- const existingWebsites = Array.from(document.querySelectorAll('#id_current_websites')).map(el => el.text).join(', ');
- const alternativeDomains = Array.from(document.querySelectorAll('#id_alternative_domains')).map(el => el.text).join(', ');
- const submitter = document.getElementById('id_submitter').value;
- const seniorOfficial = document.getElementById('id_senior_official').value;
- const otherContacts = Array.from(document.querySelectorAll('#id_other_contacts option:checked')).map(el => el.text).join('\n ');
+
+ const organizationTypeElement = document.getElementById('id_organization_type');
+ const organizationType = organizationTypeElement.options[organizationTypeElement.selectedIndex].text;
+
+ const alternativeDomainsDiv = document.querySelector('.form-row.field-alternative_domains .readonly');
+ const alternativeDomainslinks = alternativeDomainsDiv.querySelectorAll('a');
+ const alternativeDomains = Array.from(alternativeDomainslinks).map(link => link.textContent);
+
+ const existingWebsitesDiv = document.querySelector('.form-row.field-current_websites .readonly');
+ const existingWebsiteslinks = existingWebsitesDiv.querySelectorAll('a');
+ const existingWebsites = Array.from(existingWebsiteslinks).map(link => link.textContent);
+
+ const otherContactsDiv = document.querySelector('.form-row.field-other_contacts .readonly');
+ const otherContactslinks = otherContactsDiv.querySelectorAll('a');
+ const otherContacts = Array.from(otherContactslinks).map(link => link.textContent);
+
+ const requestedDomainElement = document.getElementById('id_requested_domain');
+ const requestedDomain = requestedDomainElement.options[requestedDomainElement.selectedIndex].text;
+
+ const submitterElement = document.getElementById('id_submitter');
+ const submitter = submitterElement.options[submitterElement.selectedIndex].text;
+
+ const seniorOfficialElement = document.getElementById('id_senior_official');
+ const seniorOfficial = seniorOfficialElement.options[seniorOfficialElement.selectedIndex].text;
const summary = `Recommendation: ` +
`Organization Type: ${organizationType}` +
`Requested Domain: ${requestedDomain}` +
- `Existing website(s): ${existingWebsites}` +
- `Rationale: ` +
- `Alternate Domain(s): ${alternativeDomains.split(', ').join('\n ')}` +
+ `Existing website(s): ${existingWebsites.join('')}` +
+ `Rationale: ` +
+ `Alternate Domain(s): ${alternativeDomains.join('')}` +
`Submitter: ${submitter}` +
`Senior Official: ${seniorOfficial}` +
- `Additional Contact(s): ${otherContacts}`;
+ `Additional Contact(s): ${otherContacts.join('')}`;
// Create a temporary element
let tempElement = document.createElement('div');
From c33a4b61f579f6e93f238521e0d39a66965e0996 Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 23 Jul 2024 13:42:02 -0600
Subject: [PATCH 10/32] fixed formatting
---
src/registrar/assets/js/copy-summary.js | 63 ++++++++++++++++---
.../admin/includes/contact_detail_list.html | 19 +++---
2 files changed, 65 insertions(+), 17 deletions(-)
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
index d12efb872..c10adbffa 100644
--- a/src/registrar/assets/js/copy-summary.js
+++ b/src/registrar/assets/js/copy-summary.js
@@ -1,41 +1,86 @@
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('copy-summary-btn').addEventListener('click', function() {
- // Generate the summary text
+ /// Generate the summary text
+ //------ Organization Type
const organizationTypeElement = document.getElementById('id_organization_type');
const organizationType = organizationTypeElement.options[organizationTypeElement.selectedIndex].text;
+ //------ Alternative Domains
const alternativeDomainsDiv = document.querySelector('.form-row.field-alternative_domains .readonly');
const alternativeDomainslinks = alternativeDomainsDiv.querySelectorAll('a');
const alternativeDomains = Array.from(alternativeDomainslinks).map(link => link.textContent);
+ //------ Existing Websites
const existingWebsitesDiv = document.querySelector('.form-row.field-current_websites .readonly');
const existingWebsiteslinks = existingWebsitesDiv.querySelectorAll('a');
const existingWebsites = Array.from(existingWebsiteslinks).map(link => link.textContent);
+ //------ Additional Contacts
+ // 1 - Create a hyperlinks map so we can display contact details and also link to the contact
const otherContactsDiv = document.querySelector('.form-row.field-other_contacts .readonly');
- const otherContactslinks = otherContactsDiv.querySelectorAll('a');
- const otherContacts = Array.from(otherContactslinks).map(link => link.textContent);
+ const otherContactLinks = otherContactsDiv.querySelectorAll('a');
+ const nameToUrlMap = {};
+ otherContactLinks.forEach(link => {
+ const name = link.textContent.trim();
+ const url = link.href;
+ nameToUrlMap[name] = url;
+ });
+
+ // 2 - Iterate through contact details and assemble html for summary
+ let otherContactsSummary = ""
+ // Get the table rows of contact details
+ const otherContactsTable = document.querySelector('.form-row.field-other_contacts table tbody');
+ const otherContactsRows = otherContactsTable.querySelectorAll('tr');
+ const bulletList = document.createElement('ul');
+ otherContactsRows.forEach(contactRow => {
+ // Extract the contact details
+ const name = contactRow.querySelector('th').textContent.trim();
+ const title = contactRow.querySelectorAll('td')[0].textContent.trim();
+ const email = contactRow.querySelectorAll('td')[1].textContent.trim();
+ const phone = contactRow.querySelectorAll('td')[2].textContent.trim();
+ const url = nameToUrlMap[name] || '#';
+ // Format the contact information
+ const listItem = document.createElement('li');
+ listItem.innerH = `${name} , ${title}, ${email}, ${phone}`;
+ bulletList.appendChild(listItem);
+ });
+ otherContactsSummary += bulletList.outerHTML
+
+ //------ Requested Domains
const requestedDomainElement = document.getElementById('id_requested_domain');
const requestedDomain = requestedDomainElement.options[requestedDomainElement.selectedIndex].text;
- const submitterElement = document.getElementById('id_submitter');
- const submitter = submitterElement.options[submitterElement.selectedIndex].text;
+ //------ Submitter
+ // Function to extract text by ID and handle missing elements
+ function extractTextById(id) {
+ const element = document.getElementById(id);
+ return element ? element.textContent.trim()+"," : '';
+ }
+ // Extract the submitter name, title, email, and phone number
+ const submitterName = extractTextById('contact_info_name');
+ const submitterTitle = extractTextById('contact_info_title');
+ const submitterEmail = extractTextById('contact_info_email');
+ const submitterPhone = extractTextById('contact_info_phone');
+ // Format the contact information
+ let submitterInfo = `${submitterName} ${submitterTitle} ${submitterEmail} ${submitterPhone}`;
+
+ //------ Senior Official
const seniorOfficialElement = document.getElementById('id_senior_official');
const seniorOfficial = seniorOfficialElement.options[seniorOfficialElement.selectedIndex].text;
const summary = `Recommendation: ` +
`Organization Type: ${organizationType}` +
`Requested Domain: ${requestedDomain}` +
- `Existing website(s): ${existingWebsites.join('')}` +
+ `Existing website(s): ${existingWebsites.join(',')}` +
`Rationale: ` +
- `Alternate Domain(s): ${alternativeDomains.join('')}` +
- `Submitter: ${submitter}` +
+ `Alternate Domain(s): ${alternativeDomains.join(',')}` +
+ `Submitter: ${submitterInfo}` +
`Senior Official: ${seniorOfficial}` +
- `Additional Contact(s): ${otherContacts.join('')}`;
+ `Additional Contact(s): ${otherContactsSummary}`;
// Create a temporary element
let tempElement = document.createElement('div');
diff --git a/src/registrar/templates/django/admin/includes/contact_detail_list.html b/src/registrar/templates/django/admin/includes/contact_detail_list.html
index 2ee490d76..8358e5440 100644
--- a/src/registrar/templates/django/admin/includes/contact_detail_list.html
+++ b/src/registrar/templates/django/admin/includes/contact_detail_list.html
@@ -2,25 +2,28 @@
+
{% if show_formatted_name %}
{% if user.get_formatted_name %}
- {{ user.get_formatted_name }}
+ {{ user.get_formatted_name }}
{% else %}
- None
+ None
{% endif %}
{% endif %}
+
{% if user.has_contact_info %}
{# Title #}
{% if user.title %}
- {{ user.title }}
-
+ {{ user.title }}
{% else %}
- None
+ None
{% endif %}
+
+
{# Email #}
{% if user.email %}
- {{ user.email }}
+ {{ user.email }}
{% include "admin/input_with_clipboard.html" with field=user invisible_input_field=True %}
{% else %}
@@ -29,7 +32,7 @@
{# Phone #}
{% if user.phone %}
- {{ user.phone }}
+ {{ user.phone.as_national }}
{% else %}
None
@@ -40,6 +43,6 @@
{% endif %}
{% if user_verification_type %}
- {{ user_verification_type }}
+ {{ user_verification_type }}
{% endif %}
From 4666da4df709502ff283c8b0592426a4088a4eae Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 23 Jul 2024 13:50:40 -0600
Subject: [PATCH 11/32] fix wierd typo
---
src/registrar/assets/js/copy-summary.js | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
index c10adbffa..cd437f0a6 100644
--- a/src/registrar/assets/js/copy-summary.js
+++ b/src/registrar/assets/js/copy-summary.js
@@ -43,7 +43,7 @@ document.addEventListener('DOMContentLoaded', function() {
const url = nameToUrlMap[name] || '#';
// Format the contact information
const listItem = document.createElement('li');
- listItem.innerH = `${name} , ${title}, ${email}, ${phone}`;
+ listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
bulletList.appendChild(listItem);
});
otherContactsSummary += bulletList.outerHTML
From 72fdcfdac05e834adac59e59f83ad2d1804134ef Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 23 Jul 2024 14:22:10 -0600
Subject: [PATCH 12/32] more formatting fixes
---
src/registrar/assets/js/copy-summary.js | 33 ++++++++++++++++---------
1 file changed, 21 insertions(+), 12 deletions(-)
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
index cd437f0a6..824002f76 100644
--- a/src/registrar/assets/js/copy-summary.js
+++ b/src/registrar/assets/js/copy-summary.js
@@ -55,31 +55,40 @@ document.addEventListener('DOMContentLoaded', function() {
//------ Submitter
// Function to extract text by ID and handle missing elements
- function extractTextById(id) {
- const element = document.getElementById(id);
- return element ? element.textContent.trim()+"," : '';
+ function extractTextById(id, divElement) {
+ if (divElement) {
+ const element = divElement.querySelector(`#${id}`);
+ return element ? ", " + element.textContent.trim() : '';
+ }
+ return '';
}
// Extract the submitter name, title, email, and phone number
- const submitterName = extractTextById('contact_info_name');
- const submitterTitle = extractTextById('contact_info_title');
- const submitterEmail = extractTextById('contact_info_email');
- const submitterPhone = extractTextById('contact_info_phone');
- // Format the contact information
+ const submitterDiv = document.querySelector('.form-row.field-submitter');
+ const submitterNameElement = document.getElementById('id_submitter');
+ const submitterName = submitterNameElement.options[submitterNameElement.selectedIndex].text;
+ const submitterTitle = extractTextById('contact_info_title', submitterDiv);
+ const submitterEmail = extractTextById('contact_info_email', submitterDiv);
+ const submitterPhone = extractTextById('contact_info_phone', submitterDiv);
let submitterInfo = `${submitterName} ${submitterTitle} ${submitterEmail} ${submitterPhone}`;
//------ Senior Official
+ const seniorOfficialDiv = document.querySelector('.form-row.field-senior_official');
const seniorOfficialElement = document.getElementById('id_senior_official');
- const seniorOfficial = seniorOfficialElement.options[seniorOfficialElement.selectedIndex].text;
+ const seniorOfficialName = seniorOfficialElement.options[seniorOfficialElement.selectedIndex].text;
+ const seniorOfficialTitle = extractTextById('contact_info_title', seniorOfficialDiv);
+ const seniorOfficialEmail = extractTextById('contact_info_email', seniorOfficialDiv);
+ const seniorOfficialPhone = extractTextById('contact_info_phone', seniorOfficialDiv);
+ let seniorOfficialInfo = `${seniorOfficialName} ${seniorOfficialTitle} ${seniorOfficialEmail} ${seniorOfficialPhone}`;
const summary = `Recommendation: ` +
`Organization Type: ${organizationType}` +
`Requested Domain: ${requestedDomain}` +
- `Existing website(s): ${existingWebsites.join(',')}` +
+ `Existing website(s): ${existingWebsites.join(', ')}` +
`Rationale: ` +
- `Alternate Domain(s): ${alternativeDomains.join(',')}` +
+ `Alternate Domain(s): ${alternativeDomains.join(', ')}` +
`Submitter: ${submitterInfo}` +
- `Senior Official: ${seniorOfficial}` +
+ `Senior Official: ${seniorOfficialInfo}` +
`Additional Contact(s): ${otherContactsSummary}`;
// Create a temporary element
From 711c71c1147cfbc5a01d6e3c1c8240bfabd9ae98 Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 23 Jul 2024 14:37:58 -0600
Subject: [PATCH 13/32] Catching edge-cases
---
src/registrar/assets/js/copy-summary.js | 55 ++++++++++---------
.../admin/includes/contact_detail_list.html | 2 +-
2 files changed, 31 insertions(+), 26 deletions(-)
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
index 824002f76..de15596d2 100644
--- a/src/registrar/assets/js/copy-summary.js
+++ b/src/registrar/assets/js/copy-summary.js
@@ -1,7 +1,7 @@
document.addEventListener('DOMContentLoaded', function() {
document.getElementById('copy-summary-btn').addEventListener('click', function() {
- /// Generate the summary text
+ /// Generate a rich HTML summary text and copy to clipboard
//------ Organization Type
const organizationTypeElement = document.getElementById('id_organization_type');
@@ -20,33 +20,38 @@ document.addEventListener('DOMContentLoaded', function() {
//------ Additional Contacts
// 1 - Create a hyperlinks map so we can display contact details and also link to the contact
const otherContactsDiv = document.querySelector('.form-row.field-other_contacts .readonly');
- const otherContactLinks = otherContactsDiv.querySelectorAll('a');
- const nameToUrlMap = {};
- otherContactLinks.forEach(link => {
- const name = link.textContent.trim();
- const url = link.href;
- nameToUrlMap[name] = url;
- });
+ let otherContactLinks = [];
+ if (otherContactsDiv) {
+ otherContactLinks = otherContactsDiv.querySelectorAll('a');
+ const nameToUrlMap = {};
+ otherContactLinks.forEach(link => {
+ const name = link.textContent.trim();
+ const url = link.href;
+ nameToUrlMap[name] = url;
+ });
+ }
// 2 - Iterate through contact details and assemble html for summary
let otherContactsSummary = ""
// Get the table rows of contact details
const otherContactsTable = document.querySelector('.form-row.field-other_contacts table tbody');
- const otherContactsRows = otherContactsTable.querySelectorAll('tr');
- const bulletList = document.createElement('ul');
- otherContactsRows.forEach(contactRow => {
- // Extract the contact details
- const name = contactRow.querySelector('th').textContent.trim();
- const title = contactRow.querySelectorAll('td')[0].textContent.trim();
- const email = contactRow.querySelectorAll('td')[1].textContent.trim();
- const phone = contactRow.querySelectorAll('td')[2].textContent.trim();
- const url = nameToUrlMap[name] || '#';
- // Format the contact information
- const listItem = document.createElement('li');
- listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
- bulletList.appendChild(listItem);
- });
- otherContactsSummary += bulletList.outerHTML
+ if (otherContactsTable) {
+ const otherContactsRows = otherContactsTable.querySelectorAll('tr');
+ const bulletList = document.createElement('ul');
+ otherContactsRows.forEach(contactRow => {
+ // Extract the contact details
+ const name = contactRow.querySelector('th').textContent.trim();
+ const title = contactRow.querySelectorAll('td')[0].textContent.trim();
+ const email = contactRow.querySelectorAll('td')[1].textContent.trim();
+ const phone = contactRow.querySelectorAll('td')[2].textContent.trim();
+ const url = nameToUrlMap[name] || '#';
+ // Format the contact information
+ const listItem = document.createElement('li');
+ listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
+ bulletList.appendChild(listItem);
+ });
+ otherContactsSummary += bulletList.outerHTML
+ }
//------ Requested Domains
@@ -69,7 +74,7 @@ document.addEventListener('DOMContentLoaded', function() {
const submitterTitle = extractTextById('contact_info_title', submitterDiv);
const submitterEmail = extractTextById('contact_info_email', submitterDiv);
const submitterPhone = extractTextById('contact_info_phone', submitterDiv);
- let submitterInfo = `${submitterName} ${submitterTitle} ${submitterEmail} ${submitterPhone}`;
+ let submitterInfo = `${submitterName}${submitterTitle}${submitterEmail}${submitterPhone}`;
//------ Senior Official
@@ -79,7 +84,7 @@ document.addEventListener('DOMContentLoaded', function() {
const seniorOfficialTitle = extractTextById('contact_info_title', seniorOfficialDiv);
const seniorOfficialEmail = extractTextById('contact_info_email', seniorOfficialDiv);
const seniorOfficialPhone = extractTextById('contact_info_phone', seniorOfficialDiv);
- let seniorOfficialInfo = `${seniorOfficialName} ${seniorOfficialTitle} ${seniorOfficialEmail} ${seniorOfficialPhone}`;
+ let seniorOfficialInfo = `${seniorOfficialName}${seniorOfficialTitle}${seniorOfficialEmail}${seniorOfficialPhone}`;
const summary = `Recommendation: ` +
`Organization Type: ${organizationType}` +
diff --git a/src/registrar/templates/django/admin/includes/contact_detail_list.html b/src/registrar/templates/django/admin/includes/contact_detail_list.html
index 8358e5440..418d1464b 100644
--- a/src/registrar/templates/django/admin/includes/contact_detail_list.html
+++ b/src/registrar/templates/django/admin/includes/contact_detail_list.html
@@ -32,7 +32,7 @@
{# Phone #}
{% if user.phone %}
- {{ user.phone.as_national }}
+ {{ user.phone }}
{% else %}
None
From 44fea22cf976efccc1aaf7be47b544c239282c10 Mon Sep 17 00:00:00 2001
From: Alysia Broddrick
Date: Tue, 30 Jul 2024 09:13:50 -0700
Subject: [PATCH 14/32] Add new developer sandbox 'ad' infrastructure
---
.github/workflows/migrate.yaml | 1 +
.github/workflows/reset-db.yaml | 1 +
ops/manifests/manifest-ad.yaml | 32 ++++++++++++++++++++++++++++++++
src/registrar/config/settings.py | 1 +
4 files changed, 35 insertions(+)
create mode 100644 ops/manifests/manifest-ad.yaml
diff --git a/.github/workflows/migrate.yaml b/.github/workflows/migrate.yaml
index 3ebee59f9..70ff8ee95 100644
--- a/.github/workflows/migrate.yaml
+++ b/.github/workflows/migrate.yaml
@@ -16,6 +16,7 @@ on:
- stable
- staging
- development
+ - ad
- ms
- ag
- litterbox
diff --git a/.github/workflows/reset-db.yaml b/.github/workflows/reset-db.yaml
index 49e4b5e5f..b6fa0fec5 100644
--- a/.github/workflows/reset-db.yaml
+++ b/.github/workflows/reset-db.yaml
@@ -16,6 +16,7 @@ on:
options:
- staging
- development
+ - ad
- ms
- ag
- litterbox
diff --git a/ops/manifests/manifest-ad.yaml b/ops/manifests/manifest-ad.yaml
new file mode 100644
index 000000000..73d6f96ff
--- /dev/null
+++ b/ops/manifests/manifest-ad.yaml
@@ -0,0 +1,32 @@
+---
+applications:
+- name: getgov-ad
+ buildpacks:
+ - python_buildpack
+ path: ../../src
+ instances: 1
+ memory: 512M
+ stack: cflinuxfs4
+ timeout: 180
+ command: ./run.sh
+ health-check-type: http
+ health-check-http-endpoint: /health
+ health-check-invocation-timeout: 40
+ env:
+ # Send stdout and stderr straight to the terminal without buffering
+ PYTHONUNBUFFERED: yup
+ # Tell Django where to find its configuration
+ DJANGO_SETTINGS_MODULE: registrar.config.settings
+ # Tell Django where it is being hosted
+ DJANGO_BASE_URL: https://getgov-ad.app.cloud.gov
+ # Tell Django how much stuff to log
+ DJANGO_LOG_LEVEL: INFO
+ # default public site location
+ GETGOV_PUBLIC_SITE_URL: https://get.gov
+ # Flag to disable/enable features in prod environments
+ IS_PRODUCTION: False
+ routes:
+ - route: getgov-ad.app.cloud.gov
+ services:
+ - getgov-credentials
+ - getgov-ad-database
diff --git a/src/registrar/config/settings.py b/src/registrar/config/settings.py
index 3da0a104a..8dc2587b9 100644
--- a/src/registrar/config/settings.py
+++ b/src/registrar/config/settings.py
@@ -665,6 +665,7 @@ ALLOWED_HOSTS = [
"getgov-stable.app.cloud.gov",
"getgov-staging.app.cloud.gov",
"getgov-development.app.cloud.gov",
+ "getgov-ad.app.cloud.gov",
"getgov-ms.app.cloud.gov",
"getgov-ag.app.cloud.gov",
"getgov-litterbox.app.cloud.gov",
From 53e1fe96931b584b824bf474c7c30126681d4614 Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 30 Jul 2024 13:31:38 -0600
Subject: [PATCH 15/32] update code for extracting "other contact" details
---
src/registrar/assets/js/copy-summary.js | 22 +++++++++++-----------
1 file changed, 11 insertions(+), 11 deletions(-)
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
index de15596d2..2416b07bb 100644
--- a/src/registrar/assets/js/copy-summary.js
+++ b/src/registrar/assets/js/copy-summary.js
@@ -34,16 +34,16 @@ document.addEventListener('DOMContentLoaded', function() {
// 2 - Iterate through contact details and assemble html for summary
let otherContactsSummary = ""
// Get the table rows of contact details
- const otherContactsTable = document.querySelector('.form-row.field-other_contacts table tbody');
- if (otherContactsTable) {
- const otherContactsRows = otherContactsTable.querySelectorAll('tr');
- const bulletList = document.createElement('ul');
- otherContactsRows.forEach(contactRow => {
- // Extract the contact details
- const name = contactRow.querySelector('th').textContent.trim();
- const title = contactRow.querySelectorAll('td')[0].textContent.trim();
- const email = contactRow.querySelectorAll('td')[1].textContent.trim();
- const phone = contactRow.querySelectorAll('td')[2].textContent.trim();
+ // Select all contact elements
+ const contacts = document.querySelectorAll('.dja-detail-list dl');
+
+ // Iterate through each contact element
+ contacts.forEach(contact => {
+ const name = contact.querySelector('a#contact_info_name').innerText;
+ const title = contact.querySelector('span#contact_info_title').innerText;
+ const email = contact.querySelector('span#contact_info_email').innerText;
+ const phone = contact.querySelector('span#contact_info_phone').innerText;
+
const url = nameToUrlMap[name] || '#';
// Format the contact information
const listItem = document.createElement('li');
@@ -51,7 +51,7 @@ document.addEventListener('DOMContentLoaded', function() {
bulletList.appendChild(listItem);
});
otherContactsSummary += bulletList.outerHTML
- }
+ });
//------ Requested Domains
From 6c82ec9dc296438a352890585f1d778ca8647ab4 Mon Sep 17 00:00:00 2001
From: Rebecca Hsieh
Date: Tue, 30 Jul 2024 13:33:14 -0700
Subject: [PATCH 16/32] Update for organization to have election
---
src/registrar/models/domain_request.py | 8 ++++++++
src/registrar/utility/csv_export.py | 5 +++--
2 files changed, 11 insertions(+), 2 deletions(-)
diff --git a/src/registrar/models/domain_request.py b/src/registrar/models/domain_request.py
index a7252e16b..1ff1e501a 100644
--- a/src/registrar/models/domain_request.py
+++ b/src/registrar/models/domain_request.py
@@ -215,6 +215,14 @@ class DomainRequest(TimeStampedModel):
}
return org_election_map
+ @classmethod
+ def get_org_label(cls, org_name: str):
+ # Translating the key that is given to the direct readable value
+ if not org_name:
+ return None
+
+ return cls(org_name).label if org_name else None
+
class OrganizationChoicesVerbose(models.TextChoices):
"""
Tertiary organization choices
diff --git a/src/registrar/utility/csv_export.py b/src/registrar/utility/csv_export.py
index 5fbd255aa..d852df5db 100644
--- a/src/registrar/utility/csv_export.py
+++ b/src/registrar/utility/csv_export.py
@@ -374,8 +374,9 @@ class DomainExport(BaseExport):
if first_ready_on is None:
first_ready_on = "(blank)"
- domain_org_type = model.get("generic_org_type")
- human_readable_domain_org_type = DomainRequest.OrganizationChoices.get_org_label(domain_org_type)
+ # organization_type has generic_org_type AND is_election
+ domain_org_type = model.get("organization_type")
+ human_readable_domain_org_type = DomainRequest.OrgChoicesElectionOffice.get_org_label(domain_org_type)
domain_federal_type = model.get("federal_type")
human_readable_domain_federal_type = BranchChoices.get_branch_label(domain_federal_type)
domain_type = human_readable_domain_org_type
From e446aa9511fa98b3655cbe79ec601c75a89d10e1 Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 30 Jul 2024 16:05:15 -0600
Subject: [PATCH 17/32] copy logic updates, icon updates
---
src/registrar/assets/js/copy-summary.js | 12 +++++-------
.../templates/admin/change_form_object_tools.html | 8 +++++++-
2 files changed, 12 insertions(+), 8 deletions(-)
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
index 2416b07bb..178f61185 100644
--- a/src/registrar/assets/js/copy-summary.js
+++ b/src/registrar/assets/js/copy-summary.js
@@ -21,9 +21,9 @@ document.addEventListener('DOMContentLoaded', function() {
// 1 - Create a hyperlinks map so we can display contact details and also link to the contact
const otherContactsDiv = document.querySelector('.form-row.field-other_contacts .readonly');
let otherContactLinks = [];
+ const nameToUrlMap = {};
if (otherContactsDiv) {
otherContactLinks = otherContactsDiv.querySelectorAll('a');
- const nameToUrlMap = {};
otherContactLinks.forEach(link => {
const name = link.textContent.trim();
const url = link.href;
@@ -38,20 +38,20 @@ document.addEventListener('DOMContentLoaded', function() {
const contacts = document.querySelectorAll('.dja-detail-list dl');
// Iterate through each contact element
+ const bulletList = document.createElement('ul');
contacts.forEach(contact => {
const name = contact.querySelector('a#contact_info_name').innerText;
const title = contact.querySelector('span#contact_info_title').innerText;
const email = contact.querySelector('span#contact_info_email').innerText;
const phone = contact.querySelector('span#contact_info_phone').innerText;
-
const url = nameToUrlMap[name] || '#';
+
// Format the contact information
const listItem = document.createElement('li');
listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
bulletList.appendChild(listItem);
- });
- otherContactsSummary += bulletList.outerHTML
});
+ otherContactsSummary += bulletList.outerHTML
//------ Requested Domains
@@ -115,12 +115,10 @@ document.addEventListener('DOMContentLoaded', function() {
'text/html': new Blob([tempElement.innerHTML], { type: 'text/html' })
})
]).then(() => {
- console.log('Bold text copied to clipboard successfully!');
+ console.log('Summary copied to clipboard successfully!');
}).catch(err => {
console.error('Failed to copy text: ', err);
});
document.body.removeChild(tempElement);
-
- alert('Summary copied to clipboard!');
});
});
diff --git a/src/registrar/templates/admin/change_form_object_tools.html b/src/registrar/templates/admin/change_form_object_tools.html
index c2d22e9e2..1094f4c86 100644
--- a/src/registrar/templates/admin/change_form_object_tools.html
+++ b/src/registrar/templates/admin/change_form_object_tools.html
@@ -1,4 +1,5 @@
{% load i18n admin_urls %}
+{% load i18n static %}
{% comment %} Replace li with p for more semantic HTML if we have a single child {% endcomment %}
{% block object-tools-items %}
@@ -19,7 +20,12 @@
{% if opts.model_name == 'domainrequest' %}
- {% translate "Copy request summary" %}
+
+
+
+
+ {% translate "Copy request summary" %}
+
{% endif %}
From 0953e50bb0d01155436f1ec5ab49e971d19cbbe6 Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 30 Jul 2024 17:39:02 -0600
Subject: [PATCH 18/32] styling updates
---
src/registrar/assets/sass/_theme/_buttons.scss | 5 +++++
src/registrar/templates/admin/change_form_object_tools.html | 2 +-
2 files changed, 6 insertions(+), 1 deletion(-)
diff --git a/src/registrar/assets/sass/_theme/_buttons.scss b/src/registrar/assets/sass/_theme/_buttons.scss
index 7fa379c0b..577c9ce29 100644
--- a/src/registrar/assets/sass/_theme/_buttons.scss
+++ b/src/registrar/assets/sass/_theme/_buttons.scss
@@ -128,6 +128,11 @@ a.withdraw:active {
vertical-align: bottom;
}
+a.historylink .usa-icon {
+ vertical-align: middle;
+ margin: 0px;
+}
+
a.usa-button--unstyled:visited {
color: color('primary');
}
diff --git a/src/registrar/templates/admin/change_form_object_tools.html b/src/registrar/templates/admin/change_form_object_tools.html
index 1094f4c86..03c519241 100644
--- a/src/registrar/templates/admin/change_form_object_tools.html
+++ b/src/registrar/templates/admin/change_form_object_tools.html
@@ -22,7 +22,7 @@
-
+
{% translate "Copy request summary" %}
From 7b56562417f3a5c2eb5b8e8982eabf9c3092860e Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 30 Jul 2024 18:45:44 -0600
Subject: [PATCH 19/32] Fixed error with javascript file, more styling
---
src/registrar/assets/js/copy-summary.js | 124 -------------
src/registrar/assets/js/get-gov-admin.js | 163 +++++++++++++++++-
.../assets/sass/_theme/_buttons.scss | 7 +-
src/registrar/assets/sass/_theme/_links.scss | 5 +
.../templates/admin/change_form.html | 4 -
.../admin/change_form_object_tools.html | 6 +-
6 files changed, 165 insertions(+), 144 deletions(-)
delete mode 100644 src/registrar/assets/js/copy-summary.js
diff --git a/src/registrar/assets/js/copy-summary.js b/src/registrar/assets/js/copy-summary.js
deleted file mode 100644
index 178f61185..000000000
--- a/src/registrar/assets/js/copy-summary.js
+++ /dev/null
@@ -1,124 +0,0 @@
-
-document.addEventListener('DOMContentLoaded', function() {
- document.getElementById('copy-summary-btn').addEventListener('click', function() {
- /// Generate a rich HTML summary text and copy to clipboard
-
- //------ Organization Type
- const organizationTypeElement = document.getElementById('id_organization_type');
- const organizationType = organizationTypeElement.options[organizationTypeElement.selectedIndex].text;
-
- //------ Alternative Domains
- const alternativeDomainsDiv = document.querySelector('.form-row.field-alternative_domains .readonly');
- const alternativeDomainslinks = alternativeDomainsDiv.querySelectorAll('a');
- const alternativeDomains = Array.from(alternativeDomainslinks).map(link => link.textContent);
-
- //------ Existing Websites
- const existingWebsitesDiv = document.querySelector('.form-row.field-current_websites .readonly');
- const existingWebsiteslinks = existingWebsitesDiv.querySelectorAll('a');
- const existingWebsites = Array.from(existingWebsiteslinks).map(link => link.textContent);
-
- //------ Additional Contacts
- // 1 - Create a hyperlinks map so we can display contact details and also link to the contact
- const otherContactsDiv = document.querySelector('.form-row.field-other_contacts .readonly');
- let otherContactLinks = [];
- const nameToUrlMap = {};
- if (otherContactsDiv) {
- otherContactLinks = otherContactsDiv.querySelectorAll('a');
- otherContactLinks.forEach(link => {
- const name = link.textContent.trim();
- const url = link.href;
- nameToUrlMap[name] = url;
- });
- }
-
- // 2 - Iterate through contact details and assemble html for summary
- let otherContactsSummary = ""
- // Get the table rows of contact details
- // Select all contact elements
- const contacts = document.querySelectorAll('.dja-detail-list dl');
-
- // Iterate through each contact element
- const bulletList = document.createElement('ul');
- contacts.forEach(contact => {
- const name = contact.querySelector('a#contact_info_name').innerText;
- const title = contact.querySelector('span#contact_info_title').innerText;
- const email = contact.querySelector('span#contact_info_email').innerText;
- const phone = contact.querySelector('span#contact_info_phone').innerText;
- const url = nameToUrlMap[name] || '#';
-
- // Format the contact information
- const listItem = document.createElement('li');
- listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
- bulletList.appendChild(listItem);
- });
- otherContactsSummary += bulletList.outerHTML
-
-
- //------ Requested Domains
- const requestedDomainElement = document.getElementById('id_requested_domain');
- const requestedDomain = requestedDomainElement.options[requestedDomainElement.selectedIndex].text;
-
- //------ Submitter
- // Function to extract text by ID and handle missing elements
- function extractTextById(id, divElement) {
- if (divElement) {
- const element = divElement.querySelector(`#${id}`);
- return element ? ", " + element.textContent.trim() : '';
- }
- return '';
- }
- // Extract the submitter name, title, email, and phone number
- const submitterDiv = document.querySelector('.form-row.field-submitter');
- const submitterNameElement = document.getElementById('id_submitter');
- const submitterName = submitterNameElement.options[submitterNameElement.selectedIndex].text;
- const submitterTitle = extractTextById('contact_info_title', submitterDiv);
- const submitterEmail = extractTextById('contact_info_email', submitterDiv);
- const submitterPhone = extractTextById('contact_info_phone', submitterDiv);
- let submitterInfo = `${submitterName}${submitterTitle}${submitterEmail}${submitterPhone}`;
-
-
- //------ Senior Official
- const seniorOfficialDiv = document.querySelector('.form-row.field-senior_official');
- const seniorOfficialElement = document.getElementById('id_senior_official');
- const seniorOfficialName = seniorOfficialElement.options[seniorOfficialElement.selectedIndex].text;
- const seniorOfficialTitle = extractTextById('contact_info_title', seniorOfficialDiv);
- const seniorOfficialEmail = extractTextById('contact_info_email', seniorOfficialDiv);
- const seniorOfficialPhone = extractTextById('contact_info_phone', seniorOfficialDiv);
- let seniorOfficialInfo = `${seniorOfficialName}${seniorOfficialTitle}${seniorOfficialEmail}${seniorOfficialPhone}`;
-
- const summary = `Recommendation: ` +
- `Organization Type: ${organizationType}` +
- `Requested Domain: ${requestedDomain}` +
- `Existing website(s): ${existingWebsites.join(', ')}` +
- `Rationale: ` +
- `Alternate Domain(s): ${alternativeDomains.join(', ')}` +
- `Submitter: ${submitterInfo}` +
- `Senior Official: ${seniorOfficialInfo}` +
- `Additional Contact(s): ${otherContactsSummary}`;
-
- // Create a temporary element
- let tempElement = document.createElement('div');
- tempElement.innerHTML = summary;
- // Append the element to the body
- document.body.appendChild(tempElement);
-
- // Use the Selection and Range APIs to select the element's content
- let range = document.createRange();
- range.selectNodeContents(tempElement);
- let selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
-
- // Use the Clipboard API to write the selected HTML content to the clipboard
- navigator.clipboard.write([
- new ClipboardItem({
- 'text/html': new Blob([tempElement.innerHTML], { type: 'text/html' })
- })
- ]).then(() => {
- console.log('Summary copied to clipboard successfully!');
- }).catch(err => {
- console.error('Failed to copy text: ', err);
- });
- document.body.removeChild(tempElement);
- });
-});
diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js
index d8bc21899..fe25b2feb 100644
--- a/src/registrar/assets/js/get-gov-admin.js
+++ b/src/registrar/assets/js/get-gov-admin.js
@@ -207,6 +207,7 @@ function addOrRemoveSessionBoolean(name, add){
})();
+
/** An IIFE for pages in DjangoAdmin that use a clipboard button
*/
(function (){
@@ -233,21 +234,17 @@ function addOrRemoveSessionBoolean(name, add){
buttonIcon.setAttribute('xlink:href', baseHref + '#check');
// Change the button text
- nearestSpan = button.querySelector("span")
+ let nearestSpan = button.querySelector("span")
+ let original_text = nearestSpan.innerText
nearestSpan.innerText = "Copied to clipboard"
setTimeout(function() {
// Change back to the copy icon
buttonIcon.setAttribute('xlink:href', currentHref);
- if (button.classList.contains('usa-button__small-text')) {
- nearestSpan.innerText = "Copy email";
- } else {
- nearestSpan.innerText = "Copy";
- }
+ nearestSpan.innerText = original_text;
}, 2000);
}
-
}).catch(function(error) {
console.error('Clipboard copy failed', error);
});
@@ -605,3 +602,155 @@ function initializeWidgetOnList(list, parentId) {
}
}
})();
+
+
+/** An IIFE for copy summary button (appears in DomainRegistry models)
+*/
+(function (){
+ const copyButton = document.getElementById('copy-summary-btn');
+
+ if (copyButton) {
+ copyButton.addEventListener('click', function() {
+ /// Generate a rich HTML summary text and copy to clipboard
+
+ //------ Organization Type
+ const organizationTypeElement = document.getElementById('id_organization_type');
+ const organizationType = organizationTypeElement.options[organizationTypeElement.selectedIndex].text;
+
+ //------ Alternative Domains
+ const alternativeDomainsDiv = document.querySelector('.form-row.field-alternative_domains .readonly');
+ const alternativeDomainslinks = alternativeDomainsDiv.querySelectorAll('a');
+ const alternativeDomains = Array.from(alternativeDomainslinks).map(link => link.textContent);
+
+ //------ Existing Websites
+ const existingWebsitesDiv = document.querySelector('.form-row.field-current_websites .readonly');
+ const existingWebsiteslinks = existingWebsitesDiv.querySelectorAll('a');
+ const existingWebsites = Array.from(existingWebsiteslinks).map(link => link.textContent);
+
+ //------ Additional Contacts
+ // 1 - Create a hyperlinks map so we can display contact details and also link to the contact
+ const otherContactsDiv = document.querySelector('.form-row.field-other_contacts .readonly');
+ let otherContactLinks = [];
+ const nameToUrlMap = {};
+ if (otherContactsDiv) {
+ otherContactLinks = otherContactsDiv.querySelectorAll('a');
+ otherContactLinks.forEach(link => {
+ const name = link.textContent.trim();
+ const url = link.href;
+ nameToUrlMap[name] = url;
+ });
+ }
+
+ // 2 - Iterate through contact details and assemble html for summary
+ let otherContactsSummary = ""
+ // Get the table rows of contact details
+ // Select all contact elements
+ const contacts = document.querySelectorAll('.dja-detail-list dl');
+
+ // Iterate through each contact element
+ const bulletList = document.createElement('ul');
+ contacts.forEach(contact => {
+ const name = contact.querySelector('a#contact_info_name').innerText;
+ const title = contact.querySelector('span#contact_info_title').innerText;
+ const email = contact.querySelector('span#contact_info_email').innerText;
+ const phone = contact.querySelector('span#contact_info_phone').innerText;
+ const url = nameToUrlMap[name] || '#';
+
+ // Format the contact information
+ const listItem = document.createElement('li');
+ listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
+ bulletList.appendChild(listItem);
+ });
+ otherContactsSummary += bulletList.outerHTML
+
+
+ //------ Requested Domains
+ const requestedDomainElement = document.getElementById('id_requested_domain');
+ const requestedDomain = requestedDomainElement.options[requestedDomainElement.selectedIndex].text;
+
+ //------ Submitter
+ // Function to extract text by ID and handle missing elements
+ function extractTextById(id, divElement) {
+ if (divElement) {
+ const element = divElement.querySelector(`#${id}`);
+ return element ? ", " + element.textContent.trim() : '';
+ }
+ return '';
+ }
+ // Extract the submitter name, title, email, and phone number
+ const submitterDiv = document.querySelector('.form-row.field-submitter');
+ const submitterNameElement = document.getElementById('id_submitter');
+ const submitterName = submitterNameElement.options[submitterNameElement.selectedIndex].text;
+ const submitterTitle = extractTextById('contact_info_title', submitterDiv);
+ const submitterEmail = extractTextById('contact_info_email', submitterDiv);
+ const submitterPhone = extractTextById('contact_info_phone', submitterDiv);
+ let submitterInfo = `${submitterName}${submitterTitle}${submitterEmail}${submitterPhone}`;
+
+
+ //------ Senior Official
+ const seniorOfficialDiv = document.querySelector('.form-row.field-senior_official');
+ const seniorOfficialElement = document.getElementById('id_senior_official');
+ const seniorOfficialName = seniorOfficialElement.options[seniorOfficialElement.selectedIndex].text;
+ const seniorOfficialTitle = extractTextById('contact_info_title', seniorOfficialDiv);
+ const seniorOfficialEmail = extractTextById('contact_info_email', seniorOfficialDiv);
+ const seniorOfficialPhone = extractTextById('contact_info_phone', seniorOfficialDiv);
+ let seniorOfficialInfo = `${seniorOfficialName}${seniorOfficialTitle}${seniorOfficialEmail}${seniorOfficialPhone}`;
+
+ const summary = `Recommendation: ` +
+ `Organization Type: ${organizationType}` +
+ `Requested Domain: ${requestedDomain}` +
+ `Existing website(s): ${existingWebsites.join(', ')}` +
+ `Rationale: ` +
+ `Alternate Domain(s): ${alternativeDomains.join(', ')}` +
+ `Submitter: ${submitterInfo}` +
+ `Senior Official: ${seniorOfficialInfo}` +
+ `Additional Contact(s): ${otherContactsSummary}`;
+
+ // Create a temporary element
+ let tempElement = document.createElement('div');
+ tempElement.innerHTML = summary;
+ // Append the element to the body
+ document.body.appendChild(tempElement);
+
+ // Use the Selection and Range APIs to select the element's content
+ let range = document.createRange();
+ range.selectNodeContents(tempElement);
+ let selection = window.getSelection();
+ selection.removeAllRanges();
+ selection.addRange(range);
+
+ // Use the Clipboard API to write the selected HTML content to the clipboard
+ navigator.clipboard.write([
+ new ClipboardItem({
+ 'text/html': new Blob([tempElement.innerHTML], { type: 'text/html' })
+ })
+ ]).then(() => {
+ // Change the icon to a checkmark on successful copy
+ let buttonIcon = copyButton.querySelector('.usa-button__clipboard use');
+ if (buttonIcon) {
+ let currentHref = buttonIcon.getAttribute('xlink:href');
+ let baseHref = currentHref.split('#')[0];
+
+ // Append the new icon reference
+ buttonIcon.setAttribute('xlink:href', baseHref + '#check');
+
+ // Change the button text
+ nearestSpan = copyButton.querySelector("span")
+ original_text = nearestSpan.innerText
+ nearestSpan.innerText = "Copied to clipboard"
+
+ setTimeout(function() {
+ // Change back to the copy icon
+ buttonIcon.setAttribute('xlink:href', currentHref);
+ nearestSpan.innerText = original_text
+ }, 2000);
+
+ }
+ console.log('Summary copied to clipboard successfully!');
+ }).catch(err => {
+ console.error('Failed to copy text: ', err);
+ });
+ document.body.removeChild(tempElement);
+ });
+ }
+})();
\ No newline at end of file
diff --git a/src/registrar/assets/sass/_theme/_buttons.scss b/src/registrar/assets/sass/_theme/_buttons.scss
index 577c9ce29..d246366d8 100644
--- a/src/registrar/assets/sass/_theme/_buttons.scss
+++ b/src/registrar/assets/sass/_theme/_buttons.scss
@@ -128,11 +128,6 @@ a.withdraw:active {
vertical-align: bottom;
}
-a.historylink .usa-icon {
- vertical-align: middle;
- margin: 0px;
-}
-
a.usa-button--unstyled:visited {
color: color('primary');
}
@@ -218,4 +213,4 @@ a.usa-button--unstyled:visited {
.margin-right-neg-4px {
margin-right: -4px;
-}
+}
\ No newline at end of file
diff --git a/src/registrar/assets/sass/_theme/_links.scss b/src/registrar/assets/sass/_theme/_links.scss
index e9b71733a..35546face 100644
--- a/src/registrar/assets/sass/_theme/_links.scss
+++ b/src/registrar/assets/sass/_theme/_links.scss
@@ -15,3 +15,8 @@
margin-right: units(0.5);
}
}
+
+.modelLink-icon {
+ margin-bottom: 2px;
+ vertical-align: middle;
+}
\ No newline at end of file
diff --git a/src/registrar/templates/admin/change_form.html b/src/registrar/templates/admin/change_form.html
index 3efbe554e..f2ac7f2df 100644
--- a/src/registrar/templates/admin/change_form.html
+++ b/src/registrar/templates/admin/change_form.html
@@ -10,8 +10,4 @@
{% endblock %}
{% endif %}
-{% endblock %}
-
-{% block extrahead %}
-
{% endblock %}
\ No newline at end of file
diff --git a/src/registrar/templates/admin/change_form_object_tools.html b/src/registrar/templates/admin/change_form_object_tools.html
index 03c519241..ec50f493a 100644
--- a/src/registrar/templates/admin/change_form_object_tools.html
+++ b/src/registrar/templates/admin/change_form_object_tools.html
@@ -16,12 +16,12 @@
{% else %}
- {% translate "History" %}
+ {% translate "History" %}
{% if opts.model_name == 'domainrequest' %}
-
-
+
+
{% translate "Copy request summary" %}
From 892c8331b382212dffd9c0cc060811e647028b09 Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 30 Jul 2024 19:41:26 -0600
Subject: [PATCH 20/32] Fixed logic for different display options of "other
contacts"
---
src/registrar/assets/js/get-gov-admin.js | 42 ++++++++++++++------
src/registrar/assets/sass/_theme/_links.scss | 8 +---
2 files changed, 31 insertions(+), 19 deletions(-)
diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js
index fe25b2feb..1fa687dd9 100644
--- a/src/registrar/assets/js/get-gov-admin.js
+++ b/src/registrar/assets/js/get-gov-admin.js
@@ -643,24 +643,42 @@ function initializeWidgetOnList(list, parentId) {
// 2 - Iterate through contact details and assemble html for summary
let otherContactsSummary = ""
- // Get the table rows of contact details
- // Select all contact elements
- const contacts = document.querySelectorAll('.dja-detail-list dl');
-
- // Iterate through each contact element
const bulletList = document.createElement('ul');
- contacts.forEach(contact => {
- const name = contact.querySelector('a#contact_info_name').innerText;
- const title = contact.querySelector('span#contact_info_title').innerText;
- const email = contact.querySelector('span#contact_info_email').innerText;
- const phone = contact.querySelector('span#contact_info_phone').innerText;
- const url = nameToUrlMap[name] || '#';
+ // CASE 1 - Contacts are not in a table (this happens if there is only one or two other contacts)
+ const contacts = document.querySelectorAll('.field-other_contacts .dja-detail-list dl');
+ if (contacts) {
+ contacts.forEach(contact => {
+ const name = contact.querySelector('a#contact_info_name').innerText;
+ const title = contact.querySelector('span#contact_info_title').innerText;
+ const email = contact.querySelector('span#contact_info_email').innerText;
+ const phone = contact.querySelector('span#contact_info_phone').innerText;
+ const url = nameToUrlMap[name] || '#';
+ // Format the contact information
+ const listItem = document.createElement('li');
+ listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
+ bulletList.appendChild(listItem);
+ });
+
+ }
+
+ // CASE 2 - Contacts are in a table (this happens if there is more than 2 contacts)
+ const otherContactsTable = document.querySelector('.form-row.field-other_contacts table tbody');
+ if (otherContactsTable) {
+ const otherContactsRows = otherContactsTable.querySelectorAll('tr');
+ otherContactsRows.forEach(contactRow => {
+ // Extract the contact details
+ const name = contactRow.querySelector('th').textContent.trim();
+ const title = contactRow.querySelectorAll('td')[0].textContent.trim();
+ const email = contactRow.querySelectorAll('td')[1].textContent.trim();
+ const phone = contactRow.querySelectorAll('td')[2].textContent.trim();
+ const url = nameToUrlMap[name] || '#';
// Format the contact information
const listItem = document.createElement('li');
listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
bulletList.appendChild(listItem);
- });
+ });
+ }
otherContactsSummary += bulletList.outerHTML
diff --git a/src/registrar/assets/sass/_theme/_links.scss b/src/registrar/assets/sass/_theme/_links.scss
index 35546face..bf750d26a 100644
--- a/src/registrar/assets/sass/_theme/_links.scss
+++ b/src/registrar/assets/sass/_theme/_links.scss
@@ -11,12 +11,6 @@
}
.usa-icon {
// align icon with x height
- margin-top: units(0.5);
- margin-right: units(0.5);
+ vertical-align: middle;
}
}
-
-.modelLink-icon {
- margin-bottom: 2px;
- vertical-align: middle;
-}
\ No newline at end of file
From 9a5b07c79fd8e8baa0641fcfe600b01eea156a40 Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 30 Jul 2024 19:49:47 -0600
Subject: [PATCH 21/32] Added logic for case when there are NO additional
contacts
---
src/registrar/assets/js/get-gov-admin.js | 21 ++++++++++++---------
1 file changed, 12 insertions(+), 9 deletions(-)
diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js
index 1fa687dd9..61b9a0487 100644
--- a/src/registrar/assets/js/get-gov-admin.js
+++ b/src/registrar/assets/js/get-gov-admin.js
@@ -649,15 +649,18 @@ function initializeWidgetOnList(list, parentId) {
const contacts = document.querySelectorAll('.field-other_contacts .dja-detail-list dl');
if (contacts) {
contacts.forEach(contact => {
- const name = contact.querySelector('a#contact_info_name').innerText;
- const title = contact.querySelector('span#contact_info_title').innerText;
- const email = contact.querySelector('span#contact_info_email').innerText;
- const phone = contact.querySelector('span#contact_info_phone').innerText;
- const url = nameToUrlMap[name] || '#';
- // Format the contact information
- const listItem = document.createElement('li');
- listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
- bulletList.appendChild(listItem);
+ // Check if the element is not empty
+ if (contact.children.length > 0) {
+ const name = contact.querySelector('a#contact_info_name').innerText;
+ const title = contact.querySelector('span#contact_info_title').innerText;
+ const email = contact.querySelector('span#contact_info_email').innerText;
+ const phone = contact.querySelector('span#contact_info_phone').innerText;
+ const url = nameToUrlMap[name] || '#';
+ // Format the contact information
+ const listItem = document.createElement('li');
+ listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
+ bulletList.appendChild(listItem);
+ }
});
}
From f7caee0d11875d1f51ffd9bbc57e2fdfe398d67c Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Tue, 30 Jul 2024 20:23:45 -0600
Subject: [PATCH 22/32] fixed unit tests
---
src/registrar/assets/sass/_theme/_links.scss | 2 +-
src/registrar/tests/test_admin_request.py | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/src/registrar/assets/sass/_theme/_links.scss b/src/registrar/assets/sass/_theme/_links.scss
index bf750d26a..dc68a161e 100644
--- a/src/registrar/assets/sass/_theme/_links.scss
+++ b/src/registrar/assets/sass/_theme/_links.scss
@@ -11,6 +11,6 @@
}
.usa-icon {
// align icon with x height
- vertical-align: middle;
+ vertical-align: middle;
}
}
diff --git a/src/registrar/tests/test_admin_request.py b/src/registrar/tests/test_admin_request.py
index 7a7c75ffc..a03e1ea54 100644
--- a/src/registrar/tests/test_admin_request.py
+++ b/src/registrar/tests/test_admin_request.py
@@ -1411,7 +1411,7 @@ class TestDomainRequestAdmin(MockEppLib):
self.test_helper.assert_response_contains_distinct_values(response, expected_other_employees_fields)
# Test for the copy link
- self.assertContains(response, "usa-button__clipboard", count=4)
+ self.assertContains(response, "usa-button__clipboard", count=5)
# Test that Creator counts display properly
self.assertNotContains(response, "Approved domains")
From d3b88ccb2b8084181949b7c959437882c2168ddd Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Wed, 31 Jul 2024 10:31:07 -0600
Subject: [PATCH 23/32] Styling updates
---
src/registrar/assets/js/get-gov-admin.js | 2 +-
src/registrar/assets/sass/_theme/_admin.scss | 5 +++++
src/registrar/assets/sass/_theme/_links.scss | 4 +++-
src/registrar/assets/sass/_theme/styles.scss | 2 +-
src/registrar/templates/admin/change_form_object_tools.html | 2 +-
5 files changed, 11 insertions(+), 4 deletions(-)
diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js
index 61b9a0487..e4b51f503 100644
--- a/src/registrar/assets/js/get-gov-admin.js
+++ b/src/registrar/assets/js/get-gov-admin.js
@@ -646,7 +646,7 @@ function initializeWidgetOnList(list, parentId) {
const bulletList = document.createElement('ul');
// CASE 1 - Contacts are not in a table (this happens if there is only one or two other contacts)
- const contacts = document.querySelectorAll('.field-other_contacts .dja-detail-list dl');
+ const contacts = document.querySelectorAll('.field-other_contacts .dja-detail-list dt');
if (contacts) {
contacts.forEach(contact => {
// Check if the element is not empty
diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss
index d7d116046..1c1ff8ddc 100644
--- a/src/registrar/assets/sass/_theme/_admin.scss
+++ b/src/registrar/assets/sass/_theme/_admin.scss
@@ -377,6 +377,11 @@ input.admin-confirm-button {
}
}
+.usa-icon {
+ // align icon with x height
+ vertical-align: middle;
+}
+
.module--custom {
a {
font-size: 13px;
diff --git a/src/registrar/assets/sass/_theme/_links.scss b/src/registrar/assets/sass/_theme/_links.scss
index dc68a161e..fd1c3dee9 100644
--- a/src/registrar/assets/sass/_theme/_links.scss
+++ b/src/registrar/assets/sass/_theme/_links.scss
@@ -11,6 +11,8 @@
}
.usa-icon {
// align icon with x height
- vertical-align: middle;
+ margin-top: units(0.5);
+ margin-right: units(0.5);
}
}
+
diff --git a/src/registrar/assets/sass/_theme/styles.scss b/src/registrar/assets/sass/_theme/styles.scss
index 4775b60c9..f9df015b4 100644
--- a/src/registrar/assets/sass/_theme/styles.scss
+++ b/src/registrar/assets/sass/_theme/styles.scss
@@ -27,4 +27,4 @@
/*--------------------------------------------------
--- Admin ---------------------------------*/
-@forward "admin";
+@forward "admin";
\ No newline at end of file
diff --git a/src/registrar/templates/admin/change_form_object_tools.html b/src/registrar/templates/admin/change_form_object_tools.html
index ec50f493a..ac765bde6 100644
--- a/src/registrar/templates/admin/change_form_object_tools.html
+++ b/src/registrar/templates/admin/change_form_object_tools.html
@@ -21,7 +21,7 @@
{% if opts.model_name == 'domainrequest' %}
-
+
{% translate "Copy request summary" %}
From f1d896e6b6f8b19e6f6f50fd12d498d08481ea59 Mon Sep 17 00:00:00 2001
From: Rachid Mrad
Date: Wed, 31 Jul 2024 13:22:32 -0400
Subject: [PATCH 24/32] cleanup
---
src/registrar/assets/js/get-gov-admin.js | 31 ++++--------------
src/registrar/assets/sass/_theme/_admin.scss | 32 +++++++------------
.../admin/change_form_object_tools.html | 2 +-
.../templates/admin/input_with_clipboard.html | 4 +--
.../admin/includes/detail_table_fieldset.html | 2 +-
src/registrar/tests/test_admin.py | 2 +-
src/registrar/tests/test_admin_domain.py | 2 +-
src/registrar/tests/test_admin_request.py | 2 +-
8 files changed, 26 insertions(+), 51 deletions(-)
diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js
index e4b51f503..ebb74842d 100644
--- a/src/registrar/assets/js/get-gov-admin.js
+++ b/src/registrar/assets/js/get-gov-admin.js
@@ -212,11 +212,6 @@ function addOrRemoveSessionBoolean(name, add){
*/
(function (){
- function copyInnerTextToClipboard(elem) {
- let text = elem.innerText
- navigator.clipboard.writeText(text)
- }
-
function copyToClipboardAndChangeIcon(button) {
// Assuming the input is the previous sibling of the button
let input = button.previousElementSibling;
@@ -225,7 +220,7 @@ function addOrRemoveSessionBoolean(name, add){
if (input) {
navigator.clipboard.writeText(input.value).then(function() {
// Change the icon to a checkmark on successful copy
- let buttonIcon = button.querySelector('.usa-button__clipboard use');
+ let buttonIcon = button.querySelector('.copy-to-clipboard use');
if (buttonIcon) {
let currentHref = buttonIcon.getAttribute('xlink:href');
let baseHref = currentHref.split('#')[0];
@@ -252,7 +247,7 @@ function addOrRemoveSessionBoolean(name, add){
}
function handleClipboardButtons() {
- clipboardButtons = document.querySelectorAll(".usa-button__clipboard")
+ clipboardButtons = document.querySelectorAll(".copy-to-clipboard")
clipboardButtons.forEach((button) => {
// Handle copying the text to your clipboard,
@@ -275,20 +270,7 @@ function addOrRemoveSessionBoolean(name, add){
});
}
- function handleClipboardLinks() {
- let emailButtons = document.querySelectorAll(".usa-button__clipboard-link");
- if (emailButtons){
- emailButtons.forEach((button) => {
- button.addEventListener("click", ()=>{
- copyInnerTextToClipboard(button);
- })
- });
- }
- }
-
handleClipboardButtons();
- handleClipboardLinks();
-
})();
@@ -607,7 +589,7 @@ function initializeWidgetOnList(list, parentId) {
/** An IIFE for copy summary button (appears in DomainRegistry models)
*/
(function (){
- const copyButton = document.getElementById('copy-summary-btn');
+ const copyButton = document.getElementById('id-copy-to-clipboard-summary');
if (copyButton) {
copyButton.addEventListener('click', function() {
@@ -743,11 +725,11 @@ function initializeWidgetOnList(list, parentId) {
// Use the Clipboard API to write the selected HTML content to the clipboard
navigator.clipboard.write([
new ClipboardItem({
- 'text/html': new Blob([tempElement.innerHTML], { type: 'text/html' })
+ 'text/plain': new Blob([tempElement.innerHTML], { type: 'text/plain' })
})
]).then(() => {
// Change the icon to a checkmark on successful copy
- let buttonIcon = copyButton.querySelector('.usa-button__clipboard use');
+ let buttonIcon = copyButton.querySelector('use');
if (buttonIcon) {
let currentHref = buttonIcon.getAttribute('xlink:href');
let baseHref = currentHref.split('#')[0];
@@ -768,10 +750,11 @@ function initializeWidgetOnList(list, parentId) {
}
console.log('Summary copied to clipboard successfully!');
+ console.log(tempElement.innerHTML);
}).catch(err => {
console.error('Failed to copy text: ', err);
});
document.body.removeChild(tempElement);
});
}
-})();
\ No newline at end of file
+})();
diff --git a/src/registrar/assets/sass/_theme/_admin.scss b/src/registrar/assets/sass/_theme/_admin.scss
index 1c1ff8ddc..711bfe960 100644
--- a/src/registrar/assets/sass/_theme/_admin.scss
+++ b/src/registrar/assets/sass/_theme/_admin.scss
@@ -369,19 +369,11 @@ input.admin-confirm-button {
padding: 10px 8px;
line-height: normal;
}
- .usa-icon {
- top: 2px;
- }
a.button:active, a.button:focus {
text-decoration: none;
}
}
-.usa-icon {
- // align icon with x height
- vertical-align: middle;
-}
-
.module--custom {
a {
font-size: 13px;
@@ -452,15 +444,12 @@ address.margin-top-neg-1__detail-list {
}
}
-td button.usa-button__clipboard-link, address.dja-address-contact-list {
+address.dja-address-contact-list {
font-size: unset;
}
address.dja-address-contact-list {
color: var(--body-quiet-color);
- button.usa-button__clipboard-link {
- font-size: unset;
- }
}
// Mimic the normal label size
@@ -469,11 +458,18 @@ address.dja-address-contact-list {
font-size: 0.875rem;
color: var(--body-quiet-color);
}
+}
- address button.usa-button__clipboard-link, td button.usa-button__clipboard-link {
- font-size: 0.875rem !important;
- }
+// Targets the unstyled buttons in the form
+.button--clipboard {
+ color: var(--link-fg);
+}
+// Targets the DJA buttom with a nested icon
+button .usa-icon,
+.button .usa-icon,
+.button--clipboard .usa-icon {
+ vertical-align: middle;
}
.errors span.select2-selection {
@@ -668,7 +664,7 @@ address.dja-address-contact-list {
align-items: center;
- .usa-button__icon {
+ .usa-button--icon {
position: absolute;
right: auto;
left: 4px;
@@ -686,10 +682,6 @@ address.dja-address-contact-list {
}
}
-button.usa-button__clipboard {
- color: var(--link-fg);
-}
-
.no-outline-on-click:focus {
outline: none !important;
}
diff --git a/src/registrar/templates/admin/change_form_object_tools.html b/src/registrar/templates/admin/change_form_object_tools.html
index ac765bde6..198140c19 100644
--- a/src/registrar/templates/admin/change_form_object_tools.html
+++ b/src/registrar/templates/admin/change_form_object_tools.html
@@ -20,7 +20,7 @@
{% if opts.model_name == 'domainrequest' %}
-
+
diff --git a/src/registrar/templates/admin/input_with_clipboard.html b/src/registrar/templates/admin/input_with_clipboard.html
index 20a029bed..ea2fbce33 100644
--- a/src/registrar/templates/admin/input_with_clipboard.html
+++ b/src/registrar/templates/admin/input_with_clipboard.html
@@ -8,7 +8,7 @@ Template for an input field with a clipboard
{{ field }}
@@ -25,7 +25,7 @@ Template for an input field with a clipboard
Date: Wed, 31 Jul 2024 15:05:46 -0400
Subject: [PATCH 25/32] added uuid
---
src/registrar/fixtures_users.py | 10 ++++++++++
1 file changed, 10 insertions(+)
diff --git a/src/registrar/fixtures_users.py b/src/registrar/fixtures_users.py
index 74fd4d15d..a96cb3483 100644
--- a/src/registrar/fixtures_users.py
+++ b/src/registrar/fixtures_users.py
@@ -22,6 +22,11 @@ class UserFixture:
"""
ADMINS = [
+ {
+ "username": "aad084c3-66cc-4632-80eb-41cdf5c5bcbf",
+ "first_name": "Aditi",
+ "last_name": "Green",
+ },
{
"username": "be17c826-e200-4999-9389-2ded48c43691",
"first_name": "Matthew",
@@ -120,6 +125,11 @@ class UserFixture:
]
STAFF = [
+ {
+ "username": "ffec5987-aa84-411b-a05a-a7ee5cbcde54",
+ "first_name": "Aditi-Analyst",
+ "last_name": "Green-Analyst"
+ },
{
"username": "d6bf296b-fac5-47ff-9c12-f88ccc5c1b99",
"first_name": "Matthew-Analyst",
From 4bc19d020c8418925779dd991cd78bf3e6f0b62f Mon Sep 17 00:00:00 2001
From: CocoByte
Date: Wed, 31 Jul 2024 14:06:38 -0600
Subject: [PATCH 26/32] Fixed compiler, fixed edge case where copy-paste
doesn't work in plain text situations
---
src/package-lock.json | 9 ++--
src/package.json | 4 +-
src/registrar/assets/js/get-gov-admin.js | 58 ++++++++++--------------
3 files changed, 32 insertions(+), 39 deletions(-)
diff --git a/src/package-lock.json b/src/package-lock.json
index 2ff464d5e..08e70dd51 100644
--- a/src/package-lock.json
+++ b/src/package-lock.json
@@ -9,7 +9,7 @@
"version": "1.0.0",
"license": "ISC",
"dependencies": {
- "@uswds/uswds": "^3.8.0",
+ "@uswds/uswds": "^3.8.1",
"pa11y-ci": "^3.0.1",
"sass": "^1.54.8"
},
@@ -187,9 +187,10 @@
}
},
"node_modules/@uswds/uswds": {
- "version": "3.8.0",
- "resolved": "https://registry.npmjs.org/@uswds/uswds/-/uswds-3.8.0.tgz",
- "integrity": "sha512-rMwCXe/u4HGkfskvS1Iuabapi/EXku3ChaIFW7y/dUhc7R1TXQhbbfp8YXEjmXPF0yqJnv9T08WPgS0fQqWZ8w==",
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/@uswds/uswds/-/uswds-3.8.1.tgz",
+ "integrity": "sha512-bKG/B9mJF1v0yoqth48wQDzST5Xyu3OxxpePIPDyhKWS84oDrCehnu3Z88JhSjdIAJMl8dtjtH8YvdO9kZUpAg==",
+ "license": "SEE LICENSE IN LICENSE.md",
"dependencies": {
"classlist-polyfill": "1.2.0",
"object-assign": "4.1.1",
diff --git a/src/package.json b/src/package.json
index 58ae3a4ed..e16bc8198 100644
--- a/src/package.json
+++ b/src/package.json
@@ -10,11 +10,11 @@
"author": "",
"license": "ISC",
"dependencies": {
- "@uswds/uswds": "^3.8.0",
+ "@uswds/uswds": "^3.8.1",
"pa11y-ci": "^3.0.1",
"sass": "^1.54.8"
},
"devDependencies": {
"@uswds/compile": "^1.0.0-beta.3"
}
-}
\ No newline at end of file
+}
diff --git a/src/registrar/assets/js/get-gov-admin.js b/src/registrar/assets/js/get-gov-admin.js
index ebb74842d..0a6eb8423 100644
--- a/src/registrar/assets/js/get-gov-admin.js
+++ b/src/registrar/assets/js/get-gov-admin.js
@@ -628,21 +628,19 @@ function initializeWidgetOnList(list, parentId) {
const bulletList = document.createElement('ul');
// CASE 1 - Contacts are not in a table (this happens if there is only one or two other contacts)
- const contacts = document.querySelectorAll('.field-other_contacts .dja-detail-list dt');
+ const contacts = document.querySelectorAll('.field-other_contacts .dja-detail-list dd');
if (contacts) {
contacts.forEach(contact => {
// Check if the element is not empty
- if (contact.children.length > 0) {
- const name = contact.querySelector('a#contact_info_name').innerText;
- const title = contact.querySelector('span#contact_info_title').innerText;
- const email = contact.querySelector('span#contact_info_email').innerText;
- const phone = contact.querySelector('span#contact_info_phone').innerText;
- const url = nameToUrlMap[name] || '#';
- // Format the contact information
- const listItem = document.createElement('li');
- listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
- bulletList.appendChild(listItem);
- }
+ const name = contact.querySelector('a#contact_info_name')?.innerText;
+ const title = contact.querySelector('span#contact_info_title')?.innerText;
+ const email = contact.querySelector('span#contact_info_email')?.innerText;
+ const phone = contact.querySelector('span#contact_info_phone')?.innerText;
+ const url = nameToUrlMap[name] || '#';
+ // Format the contact information
+ const listItem = document.createElement('li');
+ listItem.innerHTML = `${name} , ${title}, ${email}, ${phone}`;
+ bulletList.appendChild(listItem);
});
}
@@ -699,35 +697,29 @@ function initializeWidgetOnList(list, parentId) {
const seniorOfficialPhone = extractTextById('contact_info_phone', seniorOfficialDiv);
let seniorOfficialInfo = `${seniorOfficialName}${seniorOfficialTitle}${seniorOfficialEmail}${seniorOfficialPhone}`;
- const summary = `Recommendation: ` +
+ const html_summary = `Recommendation: ` +
`Organization Type: ${organizationType}` +
`Requested Domain: ${requestedDomain}` +
- `Existing website(s): ${existingWebsites.join(', ')}` +
+ `Current Websites: ${existingWebsites.join(', ')}` +
`Rationale: ` +
- `Alternate Domain(s): ${alternativeDomains.join(', ')}` +
+ `Alternative Domains: ${alternativeDomains.join(', ')}` +
`Submitter: ${submitterInfo}` +
`Senior Official: ${seniorOfficialInfo}` +
- `Additional Contact(s): ${otherContactsSummary}`;
+ `Other Employees: ${otherContactsSummary}`;
+ const plain_summary = html_summary.replace(/<\/?[^>]+(>|$)/g, '');
- // Create a temporary element
- let tempElement = document.createElement('div');
- tempElement.innerHTML = summary;
- // Append the element to the body
- document.body.appendChild(tempElement);
+ // Create Blobs with the summary content
+ const html_blob = new Blob([html_summary], { type: 'text/html' });
+ const plain_blob = new Blob([plain_summary], { type: 'text/plain' });
- // Use the Selection and Range APIs to select the element's content
- let range = document.createRange();
- range.selectNodeContents(tempElement);
- let selection = window.getSelection();
- selection.removeAllRanges();
- selection.addRange(range);
+ // Create a ClipboardItem with the Blobs
+ const clipboardItem = new ClipboardItem({
+ 'text/html': html_blob,
+ 'text/plain': plain_blob
+ });
- // Use the Clipboard API to write the selected HTML content to the clipboard
- navigator.clipboard.write([
- new ClipboardItem({
- 'text/plain': new Blob([tempElement.innerHTML], { type: 'text/plain' })
- })
- ]).then(() => {
+ // Write the ClipboardItem to the clipboard
+ navigator.clipboard.write([clipboardItem]).then(() => {
// Change the icon to a checkmark on successful copy
let buttonIcon = copyButton.querySelector('use');
if (buttonIcon) {
From 2edcabe76fa9f90413192c527583623f055f7dd5 Mon Sep 17 00:00:00 2001
From: zandercymatics <141044360+zandercymatics@users.noreply.github.com>
Date: Thu, 1 Aug 2024 08:34:56 -0600
Subject: [PATCH 27/32] Readd deleted content
---
src/registrar/templates/includes/domains_table.html | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/src/registrar/templates/includes/domains_table.html b/src/registrar/templates/includes/domains_table.html
index 528f56151..348ab21d7 100644
--- a/src/registrar/templates/includes/domains_table.html
+++ b/src/registrar/templates/includes/domains_table.html
@@ -1,11 +1,9 @@
{% load static %}