diff --git a/frontend/cypress/e2e/pages/ClientDetailsPage.cy.ts b/frontend/cypress/e2e/pages/ClientDetailsPage.cy.ts index 709918988..b49460a01 100644 --- a/frontend/cypress/e2e/pages/ClientDetailsPage.cy.ts +++ b/frontend/cypress/e2e/pages/ClientDetailsPage.cy.ts @@ -1,11 +1,15 @@ describe("Client Details Page", () => { beforeEach(() => { - cy.visit("/"); + cy.location().then((location) => { + if (location.pathname === "blank") { + cy.visit("/"); - cy.login("uattest@gov.bc.ca", "Uat Test", "idir", { - given_name: "James", - family_name: "Baxter", - "cognito:groups": ["CLIENT_VIEWER"], + cy.login("uattest@gov.bc.ca", "Uat Test", "idir", { + given_name: "James", + family_name: "Baxter", + "cognito:groups": ["CLIENT_VIEWER"], + }); + } }); }); @@ -87,4 +91,90 @@ describe("Client Details Page", () => { }); }); }); + + describe("locations tab", () => { + describe("non-user action tests", { testIsolation: false }, () => { + describe("3 active locations", () => { + before(() => { + cy.visit("/clients/details/g"); + }); + it("displays the number of locations", () => { + cy.get("#panel-locations").contains("03 locations"); + }); + + it("displays one collapsed accordion component for each location", () => { + cy.get("#panel-locations").within(() => { + // There are 3 accordions + cy.get("cds-accordion").should("have.length", 3); + + // All accordions are initially collapsed + cy.get("cds-accordion cds-accordion-item").each(($el) => { + expect($el).not.to.have.attr("open"); + }); + }); + }); + + it("displays the location name on the accordion's title", () => { + cy.get("#location-00 [slot='title']").contains("Mailing address"); + cy.get("#location-01 [slot='title']").contains("Accountant's address"); + cy.get("#location-02 [slot='title']").contains("Warehouse"); + }); + + it("displays the address on the accordion's title while it's collapsed", () => { + cy.get("#location-00-title-address").should("be.visible"); + cy.get("#location-01-title-address").should("be.visible"); + cy.get("#location-02-title-address").should("be.visible"); + }); + }); + + describe("2 locations - 1 deactivated and 1 active", () => { + before(() => { + cy.visit("/clients/details/gd"); + }); + it("displays the tag Deactivated when location is expired", () => { + cy.get("cds-tag#location-00-deactivated").contains("Deactivated"); + }); + + it("doesn't display the tag Deactivated when location is not expired", () => { + cy.get("cds-tag#location-01-deactivated").should("not.exist"); + }); + }); + }); + + it("hides the address on the accordion's title when it's expanded", () => { + cy.visit("/clients/details/g"); + + // Clicks to expand the accordion + cy.get("#location-00 [slot='title']").click(); + + cy.get("#location-00-title-address").should("not.be.visible"); + }); + + it("keeps accordions' states while tabs are switched", () => { + cy.visit("/clients/details/g"); + + // Expand first and third locations, leave second one collapsed + cy.get("#location-00 [slot='title']").click(); + cy.get("#location-02 [slot='title']").click(); + + // Switch to tab another tab (Contacts) + cy.get("#tab-contacts").click(); + + // Make sure the current tab panel was effectively switched + cy.get("#panel-locations").should("have.attr", "hidden"); + cy.get("#panel-contacts").should("not.have.attr", "hidden"); + + // Switch back to tab Locations + cy.get("#tab-locations").click(); + + // First location is still open + cy.get("#location-00 cds-accordion-item").should("have.attr", "open"); + + // Second location is still closed + cy.get("#location-01 cds-accordion-item").should("not.have.attr", "open"); + + // Third location is still open + cy.get("#location-02 cds-accordion-item").should("have.attr", "open"); + }); + }); }); diff --git a/frontend/src/assets/styles/global.scss b/frontend/src/assets/styles/global.scss index 2a50c072a..e91a387b8 100644 --- a/frontend/src/assets/styles/global.scss +++ b/frontend/src/assets/styles/global.scss @@ -1038,7 +1038,7 @@ cds-actionable-notification * { display: flex; flex-direction: column; padding: 1rem; - gap: 2.5rem; + gap: 1rem; border-radius: 0.5rem; border: 1px solid var(--light-theme-border-border-subtle-00, #dfdfe1); background: #fff; @@ -1124,6 +1124,18 @@ cds-actionable-notification * { margin: 0.5rem 0; } +.flex-column-1_5rem { + display: flex; + flex-direction: column; + gap: 1.5rem; +} + +.flex-column-0_25rem { + display: flex; + flex-direction: column; + gap: 0.25rem; +} + .horizontal-input-grouping { display: flex; flex-direction: row; @@ -1221,6 +1233,9 @@ cds-text-input::part(label) { .label-with-icon { display: flex; gap: 0.5rem; +} + +.line-height-0 { line-height: 0; // prevents the label from being pushed away from its input field (below it). } @@ -1441,6 +1456,7 @@ cds-side-nav { .client-details-screen { padding-left: 0; padding-right: 0; + padding-bottom: 0; gap: 0; } @@ -1456,8 +1472,8 @@ cds-accordion-item[open]:not([disabled])::part(content), :host(cds-accordion-item[open]:not([disabled])) .cds-ce--accordion__content--md { padding-right: 1rem !important; - padding-top: 0rem; - padding-bottom: inherit; + padding-top: 0; + padding-bottom: 0; display: flex; flex-direction: column; gap: 2rem; @@ -1604,10 +1620,30 @@ cds-header-panel[expanded] { background: var(--light-theme-layer-layer-02, #fff); } +.margin-left-1_75rem { + margin-left: 1.75rem; +} + .no-padding { padding: 0; } +.padding-left-1rem { + padding-left: 1rem; +} + +.padding-left-1_625rem { + padding-left: 1.625rem; +} + +.colorless { + color: unset; +} + +cds-accordion-item[open] .hide-open { + display: none; +} + // Copied from .cds--side-nav__overlay-active in @carbon/styles/css/styles.css so to make it available on all breakpoints .overlay-active { z-index: 6000; @@ -1760,10 +1796,19 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01:not(.default-typo } .tab-panel { - display: flex; padding: 2.5rem; - gap: 2rem; background: var(--light-theme-layer-layer-01, #f3f3f5); + + & > div { + display: flex; + flex-direction: column; + gap: 2rem; + width: 100%; + + &[hidden] { + display: none; + } + } } /* Small (up to 671px) */ @@ -1935,6 +1980,7 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01:not(.default-typo .client-details-screen { padding-left: 0; padding-right: 0; + padding-bottom: 0; } .client-details-content { @@ -2079,6 +2125,7 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01:not(.default-typo .client-details-screen { padding-left: 0; padding-right: 0; + padding-bottom: 0; } .client-details-content { @@ -2162,6 +2209,7 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01:not(.default-typo .client-details-screen { --padding-right: 0; --padding-left: 16rem; + padding-bottom: 0; } .client-details-content { padding-top: 0; @@ -2252,6 +2300,7 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01:not(.default-typo .client-details-screen { --padding-right: 0; --padding-left: 16rem; + padding-bottom: 0; } .client-details-content { @@ -2324,6 +2373,7 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01:not(.default-typo .client-details-screen { --padding-right: 0; --padding-left: 16rem; + padding-bottom: 0; } .client-details-content { @@ -2380,6 +2430,7 @@ div.internal-grouping-01:has(svg.warning) span.body-compact-01:not(.default-typo .client-details-screen { --padding-right: 0; --padding-left: 16rem; + padding-bottom: 0; } .client-details-content { diff --git a/frontend/src/components/grouping/StaffLocationGroupComponent.vue b/frontend/src/components/grouping/StaffLocationGroupComponent.vue index a970c6d9b..619b8d402 100644 --- a/frontend/src/components/grouping/StaffLocationGroupComponent.vue +++ b/frontend/src/components/grouping/StaffLocationGroupComponent.vue @@ -517,7 +517,7 @@ const getLocationDescription = (address: Address, index: number): string => @empty="validation.notes = true" @error="validation.notes = !$event" > -
+
Notes
diff --git a/frontend/src/dto/CommonTypesDto.ts b/frontend/src/dto/CommonTypesDto.ts index cf537db93..d8cbb6ae1 100644 --- a/frontend/src/dto/CommonTypesDto.ts +++ b/frontend/src/dto/CommonTypesDto.ts @@ -254,6 +254,30 @@ export interface ClientDoingBusinessAs { doingBusinessAsName: string; } +export interface ClientLocation { + clientNumber: string; + clientLocnCode: string; + clientLocnName: string; + addressOne: string; + addressTwo: string; + addressThree: string; + city: string; + provinceCode: string; + provinceDesc: string; + postalCode: string; + countryCode: string; + countryDesc: string; + businessPhone: string; + homePhone: string; + cellPhone: string; + faxNumber: string; + emailAddress: string; + locnExpiredInd: string; + returnedMailDate: string; + trustLocationInd: string; + cliLocnComment: string; +} + export interface ClientDetails { clientNumber: string; clientName: string; @@ -277,4 +301,5 @@ export interface ClientDetails { goodStandingInd: string; birthdate: string; doingBusinessAs: ClientDoingBusinessAs[]; + addresses: ClientLocation[]; } diff --git a/frontend/src/pages/ClientDetailsPage.vue b/frontend/src/pages/ClientDetailsPage.vue index 0bcced8f0..4230145b4 100644 --- a/frontend/src/pages/ClientDetailsPage.vue +++ b/frontend/src/pages/ClientDetailsPage.vue @@ -8,28 +8,28 @@ import "@carbon/web-components/es/components/notification/index"; import "@carbon/web-components/es/components/button/index"; import "@carbon/web-components/es/components/tabs/index"; import "@carbon/web-components/es/components/tag/index"; +import "@carbon/web-components/es/components/accordion/index"; // Composables import { useFetchTo } from "@/composables/useFetch"; import { useRouter } from "vue-router"; -// @ts-ignore import Location16 from "@carbon/icons-vue/es/location/16"; -// @ts-ignore import User16 from "@carbon/icons-vue/es/user/16"; -// @ts-ignore import NetworkEnterprise16 from "@carbon/icons-vue/es/network--enterprise/16"; -// @ts-ignore import RecentlyViewed16 from "@carbon/icons-vue/es/recently-viewed/16"; +import LocationStar20 from "@carbon/icons-vue/es/location--star/20"; +import Location20 from "@carbon/icons-vue/es/location/20"; import { adminEmail, getObfuscatedEmailLink, toTitleCase } from "@/services/ForestClientService"; -import type { ClientDetails } from "@/dto/CommonTypesDto"; +import type { ClientDetails, ClientLocation } from "@/dto/CommonTypesDto"; // Page components import SummaryView from "@/pages/client-details/SummaryView.vue"; +import LocationView from "@/pages/client-details/LocationView.vue"; -//Route related +// Route related const router = useRouter(); const clientNumber = router.currentRoute.value.params.id; @@ -52,6 +52,37 @@ const clientFullName = computed(() => { } return ""; }); + +const formatCount = (count = 0) => { + return String(count).padStart(2, "0"); +}; + +const formatAddress = (location: ClientLocation) => { + const { addressOne, city, provinceCode, countryDesc, postalCode } = location; + const list = [addressOne, city, provinceCode, countryDesc, postalCode]; + return list.join(", "); +}; + +const pluralize = (word: string, count = 0) => { + if (count === 1) { + return word; + } + return `${word}s`; +}; + +const compareString = (a: string, b: string) => { + if (a < b) { + return -1; + } + if (a > b) { + return 1; + } + return 0; +}; + +const sortedLocations = computed(() => + data.value.addresses.toSorted((a, b) => compareString(a.clientLocnCode, b.clientLocnCode)), +);