From 3f894c5b5cc5548a24343b3d4c3128e0d6947c79 Mon Sep 17 00:00:00 2001 From: Mark Meyer Date: Tue, 13 Jun 2023 15:04:49 -0800 Subject: [PATCH 01/22] start ui components --- .../src/components/AdminEditReporting.vue | 90 +++++++++++++++++++ .../src/components/AdminSearchUser.vue | 34 +++++++ .../src/components/MultiSelect.vue | 55 ++++++++++++ training-front-end/src/pages/admin.astro | 25 ++++++ .../src/pages/admin_user_search.astro | 23 +++++ 5 files changed, 227 insertions(+) create mode 100644 training-front-end/src/components/AdminEditReporting.vue create mode 100644 training-front-end/src/components/AdminSearchUser.vue create mode 100644 training-front-end/src/components/MultiSelect.vue create mode 100644 training-front-end/src/pages/admin.astro create mode 100644 training-front-end/src/pages/admin_user_search.astro diff --git a/training-front-end/src/components/AdminEditReporting.vue b/training-front-end/src/components/AdminEditReporting.vue new file mode 100644 index 00000000..643faef1 --- /dev/null +++ b/training-front-end/src/components/AdminEditReporting.vue @@ -0,0 +1,90 @@ + + \ No newline at end of file diff --git a/training-front-end/src/components/AdminSearchUser.vue b/training-front-end/src/components/AdminSearchUser.vue new file mode 100644 index 00000000..e8ba9471 --- /dev/null +++ b/training-front-end/src/components/AdminSearchUser.vue @@ -0,0 +1,34 @@ + + + \ No newline at end of file diff --git a/training-front-end/src/components/MultiSelect.vue b/training-front-end/src/components/MultiSelect.vue new file mode 100644 index 00000000..135c083c --- /dev/null +++ b/training-front-end/src/components/MultiSelect.vue @@ -0,0 +1,55 @@ + + + \ No newline at end of file diff --git a/training-front-end/src/pages/admin.astro b/training-front-end/src/pages/admin.astro new file mode 100644 index 00000000..3ceb56e4 --- /dev/null +++ b/training-front-end/src/pages/admin.astro @@ -0,0 +1,25 @@ +--- +import BaseLayout from '@layouts/BaseLayout.astro'; +import HeroTraining from '@components/HeroTraining.astro'; +import AdminEditReporting from '@components/AdminEditReporting.vue'; +// import AuthRequired from '@components/AuthRequired.vue'; +// import AdminIndex from '@components/AdminIndex.vue'; + +const pageTitle = "Administration Dashboard"; + +--- + + + Admin Panel + + Administrators + + +
+ +
+ +
\ No newline at end of file diff --git a/training-front-end/src/pages/admin_user_search.astro b/training-front-end/src/pages/admin_user_search.astro new file mode 100644 index 00000000..069ddeda --- /dev/null +++ b/training-front-end/src/pages/admin_user_search.astro @@ -0,0 +1,23 @@ +--- +import BaseLayout from '@layouts/BaseLayout.astro'; +import HeroTraining from '@components/HeroTraining.astro'; +import AdminSearchUser from '@components/AdminSearchUser.vue'; +// import AuthRequired from '@components/AuthRequired.vue'; +// import AdminIndex from '@components/AdminIndex.vue'; + +const pageTitle = "Administration Dashboard"; + +--- + + + Admin Panel + + Administrators + + + + + \ No newline at end of file From a783ddd3dc9ca41b97e3a48ee79e51ffb7f1cbf5 Mon Sep 17 00:00:00 2001 From: Mark Meyer Date: Tue, 13 Jun 2023 15:42:49 -0800 Subject: [PATCH 02/22] get search results for users --- .../src/components/AdminSearchUser.vue | 68 +++++++++++++++++-- 1 file changed, 62 insertions(+), 6 deletions(-) diff --git a/training-front-end/src/components/AdminSearchUser.vue b/training-front-end/src/components/AdminSearchUser.vue index e8ba9471..2968b5d1 100644 --- a/training-front-end/src/components/AdminSearchUser.vue +++ b/training-front-end/src/components/AdminSearchUser.vue @@ -7,21 +7,25 @@ const searchterm = ref('') const searchResults = ref([]) async function search() { - searchResults.value = await fetch(`${api_url}/api/v1/certificates/${searchterm}`).then((r) => r.json()) + searchResults.value = await fetch(`${report_url}${searchterm.value}`).then((r) => r.json()) + console.log(searchResults.value) + } + function hasReporting(user) { + return user.report_agencies.length > 0 } \ No newline at end of file From 78fda5d5d6154e108048be9d9745d0ed882f8cbe Mon Sep 17 00:00:00 2001 From: weiwang-gsa Date: Wed, 14 Jun 2023 11:35:34 -0400 Subject: [PATCH 03/22] added printable for feet training --- .../pages/training_fleet_pc/printable.astro | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 training-front-end/src/pages/training_fleet_pc/printable.astro diff --git a/training-front-end/src/pages/training_fleet_pc/printable.astro b/training-front-end/src/pages/training_fleet_pc/printable.astro new file mode 100644 index 00000000..675a1d46 --- /dev/null +++ b/training-front-end/src/pages/training_fleet_pc/printable.astro @@ -0,0 +1,79 @@ +--- +import { getCollection } from 'astro:content'; +import BaseLayout from "@layouts/BaseLayout.astro"; +import HeroTraining from "@components/HeroTraining.astro" +import PrintIcon from "@components/icons/PrintIcon.vue" + +const pages = await getCollection('training_fleet_pc'); +const content_components = pages.map(async entry => ( + {...entry, Content: (await entry.render()).Content} +)) + +const entries = await Promise.all(content_components) +--- + + + Agency/Organization Program Coordinators (A/OPCs) Fleet Training + Fleet Training + + +
+
+
+ + + + {entries.map(item => ( +
+

{item.data.title}

+ +
+ ))} +
+
+
+
+ + \ No newline at end of file From cdb42e9a7e14c89f3e0b3ab3c5db89519cb3441a Mon Sep 17 00:00:00 2001 From: Mark Meyer Date: Wed, 14 Jun 2023 12:36:46 -0800 Subject: [PATCH 04/22] pull all admin into one parent component --- .../src/components/AdminEditReporting.vue | 49 ++++------- .../src/components/AdminSearchUser.vue | 88 ++++++------------- .../src/components/AdminUserSearchTable.vue | 72 +++++++++++++++ .../src/components/MultiSelect.vue | 6 +- 4 files changed, 120 insertions(+), 95 deletions(-) create mode 100644 training-front-end/src/components/AdminUserSearchTable.vue diff --git a/training-front-end/src/components/AdminEditReporting.vue b/training-front-end/src/components/AdminEditReporting.vue index 643faef1..a1d7b157 100644 --- a/training-front-end/src/components/AdminEditReporting.vue +++ b/training-front-end/src/components/AdminEditReporting.vue @@ -8,37 +8,15 @@ import { required, requiredIf, helpers } from '@vuelidate/validators'; const { withMessage } = helpers - -const user = reactive({ - "email": "mark.meyer@gsa.gov", - "name": "Mark Meyer", - "id": 1, - "agency_id": 51, - "agency": { - "name": "General Services Administration", - "bureau": null, - "id": 51 - }, - "roles": [ - { - "name": "Report", - "id": 1 - } - ], - "report_agencies": { - 51: { - "name": "General Services Administration", - "bureau": null, - "id": 51 - }, - 135: { - "name": "Department of Education", - "bureau": "Office of Career, Technical, and Adult Education", - "id": 135 - } + const props = defineProps({ + user: { + type: Object, + required: true, } }) + const emit = defineEmits(['addAgency', 'deleteAgency']) + const agency_options = useStore(agencyList) const bureaus = useStore(bureauList) const agencyId = useStore(selectedAgencyId) @@ -51,16 +29,16 @@ const user = reactive({ function editUserAgencies(e, checked) { if (checked) { const agency = agency_options.value.find(agency => agency.id == agencyId.value) - user.report_agencies[e.id] = { + emit("addAgency", { id: e.id, name: agency.name, bureau: e.name - } + }) } else { - delete user.report_agencies[e.id] + emit('deleteAgency', e.id) } - console.log("Event: ", e, checked) } + watch(() => user_input.agency_id, async() => { setSelectedAgencyId(user_input.agency_id) user_input.bureau_id = undefined @@ -75,8 +53,11 @@ const user = reactive({ \ No newline at end of file diff --git a/training-front-end/src/components/TrainingReportIndex.vue b/training-front-end/src/components/TrainingReportIndex.vue index 977d4470..f45f8d45 100644 --- a/training-front-end/src/components/TrainingReportIndex.vue +++ b/training-front-end/src/components/TrainingReportIndex.vue @@ -18,7 +18,7 @@ if (err.message == 'Unauthorized'){ err = { name: 'You are not authorized to receive reports.', - message: 'Your email account is not authorized to access training reports. If you should be authorized, you can contact the SmartPay team to gain access.' + message: 'Your email account is not authorized to access training reports. If you should be authorized, you can contact the SmartPay team to gain access.' } setError(err) } @@ -36,7 +36,7 @@ status="error" :heading="error.name" > - {{ error.message }} + Date: Wed, 14 Jun 2023 13:14:28 -0800 Subject: [PATCH 06/22] put link around correct text --- training-front-end/src/components/TrainingReportDownload.vue | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/training-front-end/src/components/TrainingReportDownload.vue b/training-front-end/src/components/TrainingReportDownload.vue index c6c4aefd..4e4c342f 100644 --- a/training-front-end/src/components/TrainingReportDownload.vue +++ b/training-front-end/src/components/TrainingReportDownload.vue @@ -34,7 +34,7 @@ heading="You are not authorized to receive reports." > Your email account is not authorized to access training reports. If you should be authorized, you can - contact the SmartPay team to gain access. + contact the SmartPay team to gain access. \ No newline at end of file From d555bd1aded54ad134716cedf3079aa1059e8ce2 Mon Sep 17 00:00:00 2001 From: Mark Meyer Date: Thu, 15 Jun 2023 06:37:33 -0800 Subject: [PATCH 07/22] add pagination; make agency component work on copy of user data until submitted to allow undoing it --- .../src/components/AdminEditReporting.vue | 27 +++- .../src/components/AdminSearchUser.vue | 42 +++++- .../src/components/AdminUserSearchTable.vue | 22 ++-- .../src/components/USWDSPagination.vue | 118 +++++++++++++++++ .../__tests__/USWDSPagination.spec.js | 123 ++++++++++++++++++ 5 files changed, 311 insertions(+), 21 deletions(-) create mode 100644 training-front-end/src/components/USWDSPagination.vue create mode 100644 training-front-end/src/components/__tests__/USWDSPagination.spec.js diff --git a/training-front-end/src/components/AdminEditReporting.vue b/training-front-end/src/components/AdminEditReporting.vue index a1d7b157..9e97f279 100644 --- a/training-front-end/src/components/AdminEditReporting.vue +++ b/training-front-end/src/components/AdminEditReporting.vue @@ -1,5 +1,5 @@ \ No newline at end of file diff --git a/training-front-end/src/components/USWDSPagination.vue b/training-front-end/src/components/USWDSPagination.vue new file mode 100644 index 00000000..3850d103 --- /dev/null +++ b/training-front-end/src/components/USWDSPagination.vue @@ -0,0 +1,118 @@ + + \ No newline at end of file diff --git a/training-front-end/src/components/__tests__/USWDSPagination.spec.js b/training-front-end/src/components/__tests__/USWDSPagination.spec.js new file mode 100644 index 00000000..021a5c24 --- /dev/null +++ b/training-front-end/src/components/__tests__/USWDSPagination.spec.js @@ -0,0 +1,123 @@ +import { describe, it, expect } from "vitest"; + +import { mount } from "@vue/test-utils"; +import PageNavigation from "../USWDSPagination.vue"; + +describe("PageNvigation", () => { + it("renders properly", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 18, currentPage: 1 } }); + const el = wrapper.find('[data-test="page-navigation-list"]') + expect(el.exists()).toBe(true) + }); + + it("does not show previously link when on first page", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 5, currentPage: 0 } }); + const el = wrapper.get('[data-test="previous-page-link"]') + expect(el.isVisible()).toBe(false) + }); + + it("does show previously link when not on first page", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 5, currentPage: 1 } }); + const el = wrapper.get('[data-test="previous-page-link"]') + expect(el.isVisible()).toBe(true) + }); + + it("does show previously link when not on last page", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 5, currentPage: 2 } }); + const el = wrapper.get('[data-test="next-page-link"]') + expect(el.isVisible()).toBe(true) + }); + + it("does not show previously link when on last page", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 5, currentPage: 4 } }); + const el = wrapper.get('[data-test="next-page-link"]') + expect(el.isVisible()).toBe(false) + }); + + it("renders first 5 page links", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 5, currentPage: 0 } }); + const elements = wrapper.findAll('[data-test="page-link"]') + + expect(elements.length).toBe(5) + expect(elements[0].text()).toBe('1') + expect(elements[1].text()).toBe('2') + expect(elements[2].text()).toBe('3') + expect(elements[3].text()).toBe('4') + expect(elements[4].text()).toBe('5') + }); + + it("renders a single page", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 1, currentPage: 0 } }); + const elements = wrapper.findAll('[data-test="page-link"]') + + expect(elements.length).toBe(1) + expect(elements[0].text()).toBe('1') + }); + + it("displays current page", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 5, currentPage: 2 } }); + const elements = wrapper.findAll('[data-test="page-link"]') + + expect(elements[2].classes()).toContain('usa-current') + expect(elements[0].classes()).not.toContain('usa-current') + }); + + it("displays end ellipsis then last link with more than 7 pages when first pages are selected", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 8, currentPage: 3 } }); + const startEllipsis = wrapper.find('[data-test="first-ellipsis"]') + const lastEllipsis = wrapper.find('[data-test="last-ellipsis"]') + const linkElements = wrapper.findAll('[data-test="page-link"]') + + expect(startEllipsis.exists()).toBe(false) + expect(lastEllipsis.exists()).toBe(true) + expect(linkElements.length).toBe(6) + expect(linkElements[3].text()).toBe('4') + expect(linkElements[3].classes()).toContain('usa-current') + expect(linkElements[4].text()).toBe('5') + expect(linkElements[5].text()).toBe('8') + }); + + it("displays start ellipsis then last link with more than 7 pages when last pages are selected", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 8, currentPage: 7 } }); + const startEllipsis = wrapper.find('[data-test="first-ellipsis"]') + const lastEllipsis = wrapper.find('[data-test="last-ellipsis"]') + const linkElements = wrapper.findAll('[data-test="page-link"]') + + expect(startEllipsis.exists()).toBe(true) + expect(lastEllipsis.exists()).toBe(false) + expect(linkElements.length).toBe(6) + expect(linkElements[5].text()).toBe('8') + expect(linkElements[1].text()).toBe('4') + }); + + it("displays both ellipsis with internal pages with more than 8 pages when internal page is selected", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 9, currentPage: 4 } }); + const startEllipsis = wrapper.find('[data-test="first-ellipsis"]') + const lastEllipsis = wrapper.find('[data-test="last-ellipsis"]') + const linkElements = wrapper.findAll('[data-test="page-link"]') + + expect(startEllipsis.exists()).toBe(true) + expect(lastEllipsis.exists()).toBe(true) + expect(linkElements.length).toBe(5) + expect(linkElements[1].text()).toBe('4') + expect(linkElements[2].text()).toBe('5') + expect(linkElements[2].classes()).toContain('usa-current') + expect(linkElements[3].text()).toBe('6') + }); + + it("emits the page index when link is clicked", () => { + const wrapper = mount(PageNavigation, { props: { numberOfPages: 5, currentPage: 3 } }); + const linkElements = wrapper.findAll('[data-test="page-link"]') + linkElements[4].trigger('click') + + expect(wrapper.emitted()).toHaveProperty('gotoPage') + const pageEvent = wrapper.emitted('gotoPage') + expect(pageEvent).toHaveLength(1) + expect(pageEvent[0]).toEqual([4]) + + linkElements[0].trigger('click') + expect(pageEvent).toHaveLength(2) + expect(pageEvent[1]).toEqual([0]) + + }); +}); From f3d40d856e8c1f4075669c6fd6fed55ec127da53 Mon Sep 17 00:00:00 2001 From: Mark Meyer Date: Thu, 15 Jun 2023 11:34:24 -0800 Subject: [PATCH 08/22] make combobox component and conosoldate admin page --- .../src/components/AdminEditReporting.vue | 59 ++++++++++++++----- .../src/components/AdminSearchUser.vue | 8 ++- .../src/components/MultiSelect.vue | 3 +- .../src/components/USWDSComboBox.vue | 53 +++++++++++++++++ training-front-end/src/pages/admin.astro | 6 +- .../src/pages/admin_user_search.astro | 23 -------- training-front-end/src/stores/agencies.js | 2 +- 7 files changed, 109 insertions(+), 45 deletions(-) create mode 100644 training-front-end/src/components/USWDSComboBox.vue delete mode 100644 training-front-end/src/pages/admin_user_search.astro diff --git a/training-front-end/src/components/AdminEditReporting.vue b/training-front-end/src/components/AdminEditReporting.vue index 9e97f279..3301ef05 100644 --- a/training-front-end/src/components/AdminEditReporting.vue +++ b/training-front-end/src/components/AdminEditReporting.vue @@ -1,11 +1,12 @@