Skip to content

Commit

Permalink
feat(fe:FSADT1-1603): CLIENT_EDITOR Client Location Section - Edit - …
Browse files Browse the repository at this point in the history
…Front-End (#1428)

* add the Edit location button

* reuse staff location group component on the Client edit page

* emit "save" location patch data

* move conversion functions to the application Service module

* call the PATCH client API

* fix json-patch path for addresses

* call API to deactivate/reactivate location

* lock scroll to the bottom while location container shrinks

* test: LocationView

* handle scroll on click Cancel

Also moves scrolling function to the service module

* test LocationView new features

* update code interface for province and country

* update error messages

* auto-scroll to the top-notification error

* validation check without side-effects

* fix test

* do not scroll to top on success

* test: save location

* test: location patch prefix and duplicate name validation

* do not auto-scroll to the error message anymore

* test: fix e2e test

* test: fix tests by increasing the viewport

* test: fix test by waiting to load the provinces list

* text: fix attempt by changing the viewport

* temp: run only the Location component

* fix: local intercept

* restore tests

* test: fix test

* Update ClientDetailsPage.vue

* update location name on accordion header

* cancel name change on accordion header

* select reason code for address change

* remove console logs

* fix reasons patch format

* fix: select reason for address change

* simplify code to manage change reasons

* test: select reason for Address change

---------

Co-authored-by: Maria Martinez <[email protected]>
Co-authored-by: Paulo Gomes da Cruz Junior <[email protected]>
  • Loading branch information
3 people authored Mar 7, 2025
1 parent bb05fb1 commit 341a2d3
Show file tree
Hide file tree
Showing 25 changed files with 1,514 additions and 221 deletions.
216 changes: 201 additions & 15 deletions frontend/cypress/e2e/pages/ClientDetailsPage.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ describe("Client Details Page", () => {
});

const testReadonly = (rawSelector: string, value?: string) => {
const selector = `div${rawSelector}`;
const selector = `:is(div, span)${rawSelector}`;
cy.get(selector).should("be.visible");
if (value !== undefined) {
cy.get(selector).contains(value);
Expand Down Expand Up @@ -268,14 +268,11 @@ describe("Client Details Page", () => {

expect(requestBody).to.deep.include({
op: "add",
path: "/reasons/0/reason",
value: "R1",
});

expect(requestBody).to.deep.include({
op: "add",
path: "/reasons/0/field",
value: "clientStatusCode",
path: "/reasons/0",
value: {
field: "clientStatusCode",
reason: "R1",
},
});
});
});
Expand Down Expand Up @@ -311,7 +308,7 @@ describe("Client Details Page", () => {

it("displays the location name on the accordion's title", () => {
cy.get("#location-00 [slot='title']").contains("00 - Mailing address");
cy.get("#location-01 [slot='title']").contains("01 - Accountant's address");
cy.get("#location-01 [slot='title']").contains("01 - Accountant address");
cy.get("#location-02 [slot='title']").contains("02 - Warehouse");
});

Expand All @@ -322,17 +319,17 @@ describe("Client Details Page", () => {
});
});

describe("2 locations - 1 deactivated and 1 active", () => {
describe("2 locations - 1 active and 1 deactivated", () => {
before(function () {
init.call(this);
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-00-deactivated").should("not.exist");
});

it("doesn't display the tag Deactivated when location is not expired", () => {
cy.get("cds-tag#location-01-deactivated").should("not.exist");
it("displays the tag Deactivated when location is expired", () => {
cy.get("cds-tag#location-01-deactivated").contains("Deactivated");
});
});

Expand Down Expand Up @@ -389,6 +386,195 @@ describe("Client Details Page", () => {
cy.get("#location-02 cds-accordion-item").should("have.attr", "open");
});
});

describe("when role:CLIENT_EDITOR", () => {
describe("name duplication", () => {
beforeEach(() => {
cy.visit("/clients/details/g");

// Clicks to expand the accordion
cy.get("#location-00 [slot='title']").click();

cy.get("#location-00-EditBtn").click();

cy.clearFormEntry("#name_0");

// This is the same name of the third location
cy.fillFormEntry("#name_0", "Warehouse");
});

it("shows the error on field Location name", () => {
cy.checkInputErrorMessage("#name_0", "This value is already in use");

cy.get("#location-00-SaveBtn").shadow().find("button").should("be.disabled");
});
});
describe("save", () => {
describe("on success", { testIsolation: false }, () => {
const getClientDetailsCounter = {
count: 0,
};

let patchClientDetailsRequest;
before(function () {
init.call(this);

cy.intercept(
{
method: "GET",
pathname: "/api/clients/details/*",
},
(req) => {
getClientDetailsCounter.count++;
req.continue();
},
).as("getClientDetails");

cy.intercept(
{
method: "PATCH",
pathname: "/api/clients/details/*",
},
(req) => {
patchClientDetailsRequest = req;
req.continue();
},
).as("patchClientDetails");

cy.visit("/clients/details/g");
cy.wait("@getClientDetails");

// Clicks to expand the accordion
cy.get("#location-00 [slot='title']").click();

cy.get("#location-00-EditBtn").click();
cy.clearFormEntry("#emailAddress_0");
cy.get("#location-00-SaveBtn").click();
cy.wait("@getClientDetails");
});

it("prefixes the path with the corresponding location code", () => {
expect(patchClientDetailsRequest.body[0].path).to.eq("/addresses/00/emailAddress");
});

it("shows the success toast", () => {
cy.get("cds-toast-notification[kind='success']").should("be.visible");
});

it("reloads data", () => {
// Called twice - one for the initial loading and one after saving.
cy.wrap(getClientDetailsCounter).its("count").should("eq", 2);
});

it("gets back into view mode", () => {
// Fields that belong to the form (edit mode)
testHidden("#city_0");
testHidden("#emailAddress_0");
testHidden("[data-id='input-notes_0']");

cy.get("#location-00-SaveBtn").should("not.exist");

testReadonly("#location-00-city-province");
testReadonly("#location-00-emailAddress");
testReadonly("#location-00-notes");

cy.get("#location-00-EditBtn").should("be.visible");
});
});

describe("on failure", { testIsolation: false }, () => {
before(function () {
init.call(this);

cy.visit("/clients/details/g");

// Clicks to expand the accordion
cy.get("#location-00 [slot='title']").click();

cy.get("#location-00-EditBtn").click();
cy.fillFormEntry("[data-id='input-notes_0']", "error", { area: true });
cy.get("#location-00-SaveBtn").click();
});

it("shows the error toast", () => {
cy.get("cds-toast-notification[kind='error']").should("be.visible");
});

it("stays in edit mode", () => {
cy.get("#city_0").should("be.visible");
cy.get("#emailAddress_0").should("be.visible");
cy.get("[data-id='input-notes_0']").should("be.visible");

cy.get("#location-00-SaveBtn").should("be.visible");
});
});

describe("with reason modal", { testIsolation: false }, () => {
beforeEach(function () {
init.call(this);

cy.intercept("PATCH", "/api/clients/details/*").as("saveClientDetails");

cy.intercept("GET", "/api/codes/update-reasons/*/*").as("getReasonsList");

cy.visit("/clients/details/g");

// Clicks to expand the accordion
cy.get("#location-00 [slot='title']").click();

cy.get("#location-00-EditBtn").click();
cy.fillFormEntry("#addr_0", "2 Update Av");
cy.fillFormEntry("#city_0", "Updateland");
cy.selectFormEntry("#province_0", "Quebec");
cy.get("#location-00-SaveBtn").click();

cy.wait("@getReasonsList").then(({ request }) => {
// requests the list of options related to Address change
expect(request.url.endsWith("/ADDR")).to.eq(true);
});
});

it("opens the reason modal and sends the correct PATCH request with reasons", () => {
cy.get("#reason-modal").should("be.visible");

cy.get("#input-reason-0").should("exist");

// Only one reason should be required
cy.get("#input-reason-1").should("not.exist");

cy.get("#input-reason-0").find('[part="trigger-button"]').click();

cy.get("#input-reason-0")
.find("cds-dropdown-item")
.first()
.should("be.visible")
.click();

cy.get("#reasonSaveBtn").click();

cy.wait("@saveClientDetails").then((interception) => {
const requestBody = interception.request.body;

cy.log("Request Body:", JSON.stringify(requestBody));

expect(requestBody).to.deep.include({
op: "add",
path: "/reasons/0",
value: {
field: "/addresses/00",
reason: "R1",
},
});

// Only 1 "add" operation (the reason one)
expect((requestBody as any[]).filter((item) => item.op === "add")).to.have.lengthOf(
1,
);
});
});
});
});
});
});

describe("contacts tab", () => {
Expand Down
4 changes: 3 additions & 1 deletion frontend/cypress/support/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ import './commands'
import { mount } from 'cypress/vue'
import '@cypress/code-coverage/support'
import VueDOMPurifyHTML from "vue-dompurify-html";
import '@/styles'
import '@/styles';
import directivesMap from '@/directivesMap';

declare global {
namespace Cypress {
Expand All @@ -18,6 +19,7 @@ declare global {

Cypress.Commands.add('mount', (component, options = {}) => {
options.global = options.global || {};
options.global.directives = directivesMap;
options.global.plugins = options.global.plugins || [];
options.global.plugins.push(VueDOMPurifyHTML);

Expand Down
2 changes: 1 addition & 1 deletion frontend/cypress/support/cypress.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ declare namespace Cypress {
fillFormEntry(field: string, value: string, delayMS?: number, area?: boolean): Chainable<void>;
fillFormEntry(field: string, value: string, options: FillFormEntryOptions): Chainable<void>;
clearFormEntry(field: string, area?: boolean): Chainable<void>;
selectFormEntry(field: string, value: string, box: boolean): Chainable<void>;
selectFormEntry(field: string, value: string): Chainable<void>;
markCheckbox(field: string): Chainable<void>;
unmarkCheckbox(field: string): Chainable<void>;
selectAutocompleteEntry(
Expand Down
5 changes: 4 additions & 1 deletion frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@
"vite": "^5.4.10",
"vite-plugin-istanbul": "^5.0.0",
"vitest": "^3.0.5",
"vue-component-type-helpers": "^2.2.2",
"vue-eslint-parser": "^9.4.3",
"vue-svg-loader": "^0.16.0",
"vue-tsc": "^2.2.0",
Expand Down
35 changes: 34 additions & 1 deletion frontend/src/assets/styles/global.scss
Original file line number Diff line number Diff line change
Expand Up @@ -829,11 +829,17 @@ cds-actionable-notification * {
&--stretched {
cds-button {
flex: 1;

&::part(button) {
// We don't need this padding since the button is already stretched thanks to "flex: 1".
// This allows adjacent buttons to have the same size.
padding-inline-end: unset;
}
}
}
}

.form-edit {
.form-edit, .tab-form {
display: flex;
flex-direction: column;
gap: 2rem;
Expand All @@ -845,6 +851,10 @@ cds-actionable-notification * {
}
}

.tab-form {
max-width: 36rem;
}

.frame-01 {
align-self: stretch;
display: flex;
Expand Down Expand Up @@ -1194,6 +1204,21 @@ cds-accordion-item:not([open]) {
align-items: end;
}

.grid {
display: grid;
column-gap: 1rem;
row-gap: inherit;
align-items: end;

&--2-per-row {
grid-template-columns: 1fr 1fr;
}

&--3-per-row {
grid-template-columns: 1fr 1fr 1fr;
}
}

.input-with-instruction {
display: flex;
flex-direction: column;
Expand Down Expand Up @@ -1523,6 +1548,14 @@ cds-accordion-item::part(expando) {
min-block-size: 3rem;
}

cds-accordion-item[open]::part(cds--accordion__wrapper) {
/*
Fixes issue where accordions would not auto-adjust size (height) according to its content, when
it grows dynamically.
*/
max-block-size: unset;
}

cds-accordion-item[open]:not([disabled])::part(content),
:host(cds-accordion-item[open]:not([disabled]))
.cds-ce--accordion__content--md {
Expand Down
Loading

0 comments on commit 341a2d3

Please sign in to comment.