Skip to content

Commit

Permalink
Add observers in reports list page and refactor the frontend builders
Browse files Browse the repository at this point in the history
  • Loading branch information
Maxime Naulleau committed Nov 7, 2024
1 parent 2a5c5ca commit ef9e870
Show file tree
Hide file tree
Showing 36 changed files with 260 additions and 88 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,7 @@ describe('Reporter Controller', () => {
transparency: Transparency.MARCH_2025,
grade: Magistrat.Grade.HH,
targettedPosition: 'a position',
observersCount: 1,
};
const aReport = ReportBuilder.fromListingVM(aReportListingVM)
.withNominationFileId('ca1619e2-263d-49b6-b928-6a04ee681138')
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ describe('SQL Report Listing VM Query', () => {
transparency: aReport.transparency,
grade: aReport.grade,
targettedPosition: aReport.targettedPosition,
observersCount: aReport.observers?.length || 0,
},
],
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { ReportListingVMQuery } from 'src/reporter-context/business-logic/gatewa
import { DrizzleDb } from 'src/shared-kernel/adapters/secondary/gateways/repositories/drizzle/config/drizzle-instance';
import { DateOnly } from 'src/shared-kernel/business-logic/models/date-only';
import { reports } from './schema/report-pm';
import { sql } from 'drizzle-orm';

export class SqlReportListingVMQuery implements ReportListingVMQuery {
constructor(private readonly db: DrizzleDb) {}
Expand All @@ -19,6 +20,7 @@ export class SqlReportListingVMQuery implements ReportListingVMQuery {
transparency: reports.transparency,
grade: reports.grade,
targettedPosition: reports.targettedPosition,
observersCount: sql<number>`COALESCE(array_length(${reports.observers}, 1), 0)`,
})
.from(reports)
.execute();
Expand All @@ -36,6 +38,7 @@ export class SqlReportListingVMQuery implements ReportListingVMQuery {
transparency: report.transparency,
grade: report.grade,
targettedPosition: report.targettedPosition,
observersCount: report.observersCount,
})),
};
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ describe('SQL Report Retrieval VM Query', () => {
it('retrieves with empty values', async () => {
const expectedRules = prepareExpectedRules(aReportRule);
const result = await sqlReportRetrievalVMQuery.retrieveReport(aReport.id);
expect(result).toEqual<ReportRetrievalVM>(
expect(result).toEqual(
ReportRetrievalVMBuilder.fromWriteModel(aReport)
.withDueDate(null)
.withComment(null)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { NominationFile, ReportRetrievalVM } from 'shared-models';
import { eq } from 'drizzle-orm';
import { eq, sql } from 'drizzle-orm';
import { ReportRetrievalVMQuery } from 'src/reporter-context/business-logic/gateways/queries/report-retrieval-vm.query';
import { DrizzleDb } from 'src/shared-kernel/adapters/secondary/gateways/repositories/drizzle/config/drizzle-instance';
import { DateOnly } from 'src/shared-kernel/business-logic/models/date-only';
Expand All @@ -25,6 +25,8 @@ export class SqlReportRetrievalVMQuery implements ReportRetrievalVMQuery {
targettedPosition: reports.targettedPosition,
comment: reports.comment,
rank: reports.rank,
observers: reports.observers,
observersCount: sql<number>`COALESCE(array_length(${reports.observers}, 1), 0)`,
// Rule fields
ruleId: reportRules.id,
ruleGroup: reportRules.ruleGroup,
Expand Down Expand Up @@ -93,6 +95,7 @@ export class SqlReportRetrievalVMQuery implements ReportRetrievalVMQuery {
targettedPosition: reportData.targettedPosition,
comment: reportData.comment ? reportData.comment : null,
rank: reportData.rank,
observers: reportData.observers,
rules,
};

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@ export class ReportRetrievalVMBuilder {
private comment: string | null;
private rank: string;
private rules: NominationFile.Rules;
private observers: string[] | null;
private observersCount: number;

constructor() {
this.id = 'report-id';
Expand All @@ -45,6 +47,7 @@ export class ReportRetrievalVMBuilder {
this.targettedPosition = 'targetted position';
this.comment = 'comments';
this.rank = '(2 sur une liste de 100)';
this.observers = ['observer 1', 'observer 2'];

const defaultValue: NominationFile.RuleValue = {
id: 'rule-id',
Expand Down Expand Up @@ -94,11 +97,11 @@ export class ReportRetrievalVMBuilder {
};
}

withId(id: string): this {
withId(id: string) {
this.id = id;
return this;
}
withName(name: string): this {
withName(name: string) {
this.name = name;
return this;
}
Expand Down Expand Up @@ -146,6 +149,11 @@ export class ReportRetrievalVMBuilder {
this.rank = rank;
return this;
}
withObservers(observers: string[] | null) {
this.observers = observers;
this.observersCount = observers?.length || 0;
return this;
}
withOverseasToOverseasRule(options: Partial<NominationFile.RuleValue>): this {
const rule =
this.rules.management[NominationFile.ManagementRule.OVERSEAS_TO_OVERSEAS];
Expand All @@ -156,7 +164,7 @@ export class ReportRetrievalVMBuilder {
};
return this;
}
withRules(rules: NominationFile.Rules): this {
withRules(rules: NominationFile.Rules) {
this.rules = rules;
return this;
}
Expand All @@ -176,6 +184,7 @@ export class ReportRetrievalVMBuilder {
targettedPosition: this.targettedPosition,
comment: this.comment,
rank: this.rank,
observers: this.observers,
rules: this.rules,
};
}
Expand All @@ -196,6 +205,7 @@ export class ReportRetrievalVMBuilder {
.withCurrentPosition(report.currentPosition)
.withTargettedPosition(report.targettedPosition)
.withComment(report.comment)
.withRank(report.rank);
.withRank(report.rank)
.withObservers(report.observers);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export class ReportBuilder {
private comment: string | null;
private rank: string;
private reporterName: string | null;
private observers: string[];
private observers: string[] | null;

constructor() {
this.id = 'report-id';
Expand Down Expand Up @@ -112,6 +112,10 @@ export class ReportBuilder {
this.rank = rank;
return this;
}
withObservers(observers: string[] | null): ReportBuilder {
this.observers = observers;
return this;
}

build(): NominationFileReport {
return new NominationFileReport(
Expand Down Expand Up @@ -183,6 +187,7 @@ export class ReportBuilder {
.withCurrentPosition(reportRetrievalVM.currentPosition)
.withTargettedPosition(reportRetrievalVM.targettedPosition)
.withComment(reportRetrievalVM.comment)
.withRank(reportRetrievalVM.rank);
.withRank(reportRetrievalVM.rank)
.withObservers(reportRetrievalVM.observers);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,5 +41,6 @@ describe('List reports', () => {
transparency: Transparency.MARCH_2025,
grade: Magistrat.Grade.HH,
targettedPosition: 'a position',
observersCount: 1,
};
});
2 changes: 1 addition & 1 deletion apps/webapp/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
"types:check": "tsc --noEmit -p ./tsconfig.app.json",
"types:check:watch": "tsc --noEmit -p ./tsconfig.app.json --watch",
"preview": "vite preview",
"test": "DEBUG_PRINT_LIMIT=10 vitest",
"test": "DEBUG_PRINT_LIMIT=1000 vitest",
"test:all": "vitest run",
"postinstall": "pnpm dsfr:build",
"dsfr:build": "react-dsfr update-icons"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,7 @@ describe("Nomination Case List Component", () => {
await screen.findByText("Transparence");
await screen.findByText("Grade actuel");
await screen.findByText("Poste ciblé");
await screen.findByText("Observants");
});

it("shows it in the table", async () => {
Expand All @@ -65,6 +66,7 @@ describe("Nomination Case List Component", () => {
await screen.findByText("Mars 2025");
await screen.findByText("I");
await screen.findByText("PG TJ Marseille");
await screen.findByText("2");
});
});

Expand Down Expand Up @@ -92,4 +94,4 @@ const user = {

const aNominationFile = new NominationFileBuilder()
.withReporterName(user.reporterName)
.build();
.buildListVM();
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ export const NominationFilesTable: React.FC<NominationFilesTableProps> = ({
"Transparence",
"Grade actuel",
"Poste ciblé",
"Observants",
]}
bordered
data={nominationFiles.map((nominationFile) => [
Expand All @@ -29,8 +30,9 @@ export const NominationFilesTable: React.FC<NominationFilesTableProps> = ({
{nominationFile.name}
</a>,
<div>{nominationFile.transparency}</div>,
<div>{nominationFile.grade}</div>,
<div className="text-center">{nominationFile.grade}</div>,
<div>{nominationFile.targettedPosition}</div>,
<div className="text-center">{nominationFile.observersCount}</div>,
])}
/>
);
Original file line number Diff line number Diff line change
@@ -1,14 +1,13 @@
import "@testing-library/jest-dom";
import { act, render, screen, waitFor } from "@testing-library/react";
import userEvent from "@testing-library/user-event";
import { Provider } from "react-redux";
import { NominationFile } from "shared-models";
import { NominationFileBuilder } from "../../../../core-logic/builders/NominationFile.builder";
import { NominationFileVM } from "../../../../core-logic/view-models/NominationFileVM";
import { AppState } from "../../../../store/appState";
import { ReduxStore, initReduxStore } from "../../../../store/reduxStore";
import { FakeNominationFileGateway } from "../../../secondary/gateways/FakeNominationFile.gateway";
import { NominationFileOverview } from "./NominationFileOverview";
import { NominationFileVM } from "../../../../core-logic/view-models/NominationFileVM";

describe("Nomination Case Overview Component", () => {
let store: ReduxStore;
Expand Down Expand Up @@ -57,6 +56,24 @@ describe("Nomination Case Overview Component", () => {
await expectMagistratIdentity();
});

it("shows the observers", async () => {
renderNominationFile(aValidatedNomination.id);
await screen.findByText("Observants");
for (const [
index,
observer,
] of aValidatedNomination.observers!.entries()) {
if (index === 0) {
await screen.findByText(observer);
} else {
const observer2Name = await screen.findByText("observer 2");
expect(observer2Name).toHaveClass("fr-text--bold");
await screen.findByText("VPI TJ Rennes");
await screen.findByText("(1 sur une liste de 2)");
}
}
});

it("shows the rules", async () => {
act(() => {
renderNominationFile(aValidatedNomination.id);
Expand Down Expand Up @@ -101,7 +118,7 @@ describe("Nomination Case Overview Component", () => {
const aNomination = new NominationFileBuilder()
.withId("without-comment")
.withComment(null)
.build();
.buildRetrieveVM();
nominationFileGateway.addNominationFile(aNomination);

renderNominationFile(aNomination.id);
Expand Down Expand Up @@ -217,7 +234,9 @@ describe("Nomination Case Overview Component", () => {
];
it(`when checked, '${anotherRuleLabel}' can also be checked`, async () => {
nominationFileGateway.addNominationFile(
new NominationFileBuilder().withTransferTimeValidated(false).build(),
new NominationFileBuilder()
.withTransferTimeValidated(false)
.buildRetrieveVM(),
);
renderNominationFile("nomination-file-id");

Expand Down Expand Up @@ -305,8 +324,12 @@ describe("Nomination Case Overview Component", () => {
const aValidatedNomination = new NominationFileBuilder()
.withId("nomination-file-id")
.withBiography(" - John Doe's biography - second line - third line ")
.build();
.withObservers([
"observer 1",
"observer 2\nVPI TJ Rennes\n(1 sur une liste de 2)",
])
.buildRetrieveVM();

const anUnvalidatedNomination = new NominationFileBuilder()
.withAllRulesUnvalidated()
.build();
.buildRetrieveVM();
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { Comment } from "./Comment";
import { MagistratIdentity } from "./MagistratIdentity";
import { NominationRules } from "./NominationRules";
import { VMNominationFileRuleValue } from "../../../../core-logic/view-models/NominationFileVM";
import { Observers } from "./Observers";

export type NominationFileOverviewProps = {
id: string;
Expand Down Expand Up @@ -89,6 +90,7 @@ export const NominationFileOverview: React.FC<NominationFileOverviewProps> = ({
rank={nominationFile.rank}
/>
<Biography biography={nominationFile.biography} />
<Observers observers={nominationFile.observers} />
<NominationRules
rulesChecked={nominationFile.rulesChecked}
onUpdateNominationRule={onUpdateNominationRule}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { cx } from "@codegouvfr/react-dsfr/fr/cx";
import { Card } from "./Card";
import { NominationFileVM } from "../../../../core-logic/view-models/NominationFileVM";
import clsx from "clsx";

export const Observers = ({
observers,
}: Pick<NominationFileVM, "observers">) => {
if (!observers) return null;

return (
<Card>
<label className={cx("fr-h2")} id="observers">
Observants
</label>
<div
aria-labelledby="observers"
className={clsx(
"whitespace-pre-line leading-10 w-full flex flex-col gap-4",
)}
>
{observers.map(([observerName, ...observerInformation]) => (
<div key={observerName}>
<div key={observerName} className={cx("fr-text--bold")}>
{observerName}
</div>
<ObserverInformation observerInformation={observerInformation} />
</div>
))}
</div>
</Card>
);
};

const ObserverInformation = ({
observerInformation,
}: {
observerInformation: string[];
}) => {
return (
<div aria-labelledby="observers" className="whitespace-pre-line w-full">
{observerInformation.map((info) => (
<div key={info}>{info}</div>
))}
</div>
);
};
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,18 @@ describe("Select Nomination Case", () => {
.withName("John Doe")
.withDueDate(new DateOnly(2030, 10, 30))
.withBiography("The biography.")
.withObservers([
"observer 1",
"observer 2\nVPI TJ Rennes\n(1 sur une liste de 2)",
])
.buildRetrieveVM();
const aNominationFileVM = NominationFileBuilderVM.fromStoreModel(
aNominationFile,
)
.withAllRulesChecked(false)
.withObservers([
["observer 1"],
["observer 2", "VPI TJ Rennes", "(1 sur une liste de 2)"],
])
.build();
const aNominationFileVM: NominationFileVM =
NominationFileBuilderVM.fromStoreModel(aNominationFile)
.withAllRulesChecked(false)
.build();
});
Loading

0 comments on commit ef9e870

Please sign in to comment.