-
Notifications
You must be signed in to change notification settings - Fork 11
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
7 changed files
with
396 additions
and
17 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
import { declarationRepo, representationEquilibreeRepo } from "@api/core-domain/repo"; | ||
import { GetAllDeclarationsBySiren } from "@api/core-domain/useCases/GetAllDeclarationsBySiren"; | ||
import { GetDeclarationOpmcBySirenAndYear } from "@api/core-domain/useCases/GetDeclarationOpmcBySirenAndYear"; | ||
import { GetRepresentationEquilibreeBySiren } from "@api/core-domain/useCases/GetRepresentationEquilibreeBySiren"; | ||
import { assertServerSession } from "@api/utils/auth"; | ||
|
||
export async function getAllDeclarationsBySiren(siren: string) { | ||
await assertServerSession({ | ||
owner: { | ||
check: siren, | ||
message: "Not authorized to fetch declarations for this siren.", | ||
}, | ||
staff: true, | ||
}); | ||
|
||
// handle default errors | ||
const useCase = new GetAllDeclarationsBySiren(declarationRepo); | ||
return await useCase.execute({ siren }); | ||
} | ||
|
||
export async function getAllRepresentationEquilibreeBySiren(siren: string) { | ||
await assertServerSession({ | ||
owner: { | ||
check: siren, | ||
message: "Not authorized to fetch representation equilibree for this siren.", | ||
}, | ||
staff: true, | ||
}); | ||
|
||
// handle default errors | ||
const useCase = new GetRepresentationEquilibreeBySiren(representationEquilibreeRepo); | ||
return await useCase.execute({ siren }); | ||
} | ||
|
||
export async function getAllDeclarationOpmcSirenAndYear(siren: string, year: number) { | ||
await assertServerSession({ | ||
owner: { | ||
check: siren, | ||
message: "Not authorized to fetch representation equilibree for this siren.", | ||
}, | ||
staff: true, | ||
}); | ||
|
||
// handle default errors | ||
const useCase = new GetDeclarationOpmcBySirenAndYear(declarationRepo); | ||
return await useCase.execute({ siren, year }); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
131 changes: 129 additions & 2 deletions
131
packages/app/src/app/(default)/mon-espace/mes-declarations/IndexList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,134 @@ | ||
export const IndexList = () => { | ||
"use client"; | ||
|
||
import Table from "@codegouvfr/react-dsfr/Table"; | ||
import { CompanyWorkforceRange } from "@common/core-domain/domain/valueObjects/declaration/CompanyWorkforceRange"; | ||
import { type DeclarationDTO } from "@common/core-domain/dtos/DeclarationDTO"; | ||
import { type DeclarationOpmcDTO } from "@common/core-domain/dtos/DeclarationOpmcDTO"; | ||
import { formatIsoToFr } from "@common/utils/date"; | ||
import { Heading, Link } from "@design-system"; | ||
import { isBefore, sub } from "date-fns"; | ||
import { capitalize, upperCase } from "lodash"; | ||
|
||
import { buildHelpersObjectifsMesures } from "../../index-egapro/objectifs-mesures/[siren]/[year]/ObjectifsMesuresForm"; | ||
|
||
//Note: For 2022, first year of OPMC, we consider that the duration to be frozen is 2 years, but for next years, it will be 1 year like isFrozenDeclaration. | ||
const OPMC_FROZEN_DURATION = { years: 2 }; | ||
|
||
const isFrozenDeclarationForOPMC = (declaration: DeclarationOpmcDTO) => | ||
declaration?.["declaration-existante"]?.date | ||
? isBefore(new Date(declaration?.["declaration-existante"]?.date), sub(new Date(), OPMC_FROZEN_DURATION)) | ||
: false; | ||
|
||
enum declarationOpmcStatus { | ||
ALREADY_FILLED = "Déjà renseignés", | ||
COMPLETED = "Renseignés", | ||
INDEX_OVER_85 = "Index supérieur à 85", | ||
NOT_APPLICABLE = "Non applicable", | ||
NOT_MODIFIABLE = "Déclaration non modifiable", | ||
NOT_MODIFIABLE_CORRECT = "Déclaration non modifiable sur données correctes", | ||
NOT_MODIFIABLE_INCORRECT = "Déclaration non modifiable sur données incorrectes", | ||
TO_COMPLETE = "À renseigner", | ||
YEAR_NOT_APPLICABLE = "Année non applicable", | ||
} | ||
|
||
const getDeclarationOpmcStatus = (declaration?: DeclarationOpmcDTO) => { | ||
if (!declaration) return declarationOpmcStatus.NOT_APPLICABLE; | ||
const { after2021, index, initialValuesObjectifsMesures, objectifsMesuresSchema } = | ||
buildHelpersObjectifsMesures(declaration); | ||
|
||
if (!declaration["resultat-global"] || index === undefined) return declarationOpmcStatus.NOT_APPLICABLE; | ||
if (!after2021) return declarationOpmcStatus.YEAR_NOT_APPLICABLE; | ||
if (index >= 85) return declarationOpmcStatus.INDEX_OVER_85; | ||
|
||
try { | ||
objectifsMesuresSchema.parse(initialValuesObjectifsMesures); | ||
if (isFrozenDeclarationForOPMC(declaration)) return declarationOpmcStatus.NOT_MODIFIABLE_CORRECT; | ||
} catch (e) { | ||
if (isFrozenDeclarationForOPMC(declaration)) return declarationOpmcStatus.NOT_MODIFIABLE_INCORRECT; | ||
return declarationOpmcStatus.TO_COMPLETE; | ||
} | ||
return declarationOpmcStatus.COMPLETED; | ||
}; | ||
|
||
const formatDeclarationOpmcStatus = (status: declarationOpmcStatus, siren: string, year: number) => { | ||
const withLink = (text: string) => ( | ||
<Link key={`${siren}-objectifs-mesures`} href={`/index-egapro/objectifs-mesures/${siren}/${year}`}> | ||
{text} | ||
</Link> | ||
); | ||
|
||
switch (status) { | ||
case declarationOpmcStatus.COMPLETED: | ||
return withLink(declarationOpmcStatus.COMPLETED); | ||
case declarationOpmcStatus.INDEX_OVER_85: | ||
return declarationOpmcStatus.INDEX_OVER_85; | ||
case declarationOpmcStatus.NOT_APPLICABLE: | ||
return declarationOpmcStatus.NOT_APPLICABLE; | ||
case declarationOpmcStatus.NOT_MODIFIABLE_CORRECT: | ||
return withLink(declarationOpmcStatus.ALREADY_FILLED); | ||
case declarationOpmcStatus.NOT_MODIFIABLE_INCORRECT: | ||
return withLink(declarationOpmcStatus.NOT_MODIFIABLE); | ||
case declarationOpmcStatus.TO_COMPLETE: | ||
return withLink(declarationOpmcStatus.TO_COMPLETE); | ||
case declarationOpmcStatus.YEAR_NOT_APPLICABLE: | ||
return declarationOpmcStatus.YEAR_NOT_APPLICABLE; | ||
default: | ||
return declarationOpmcStatus.NOT_APPLICABLE; | ||
} | ||
}; | ||
|
||
export const IndexList = ({ | ||
declarations, | ||
declarationOpmcList, | ||
}: { | ||
declarationOpmcList: DeclarationOpmcDTO[]; | ||
declarations: DeclarationDTO[]; | ||
}) => { | ||
const headers = [ | ||
"SIREN", | ||
"ANNÉE INDICATEUR", | ||
"STRUCTURE", | ||
"TRANCHE D'EFFECTIF", | ||
"DATE DE DÉCLARATION", | ||
"INDEX", | ||
"OBJECTIF ET MESURES", | ||
"RÉCAPITULATIF", | ||
]; | ||
|
||
const data = declarations.map(declaration => { | ||
const rowYear = declaration.commencer?.annéeIndicateurs; | ||
const rowSiren = declaration.commencer?.siren; | ||
return [ | ||
<Link key={declaration.commencer?.siren} href={`/index-egapro/declaration/${rowSiren}/${rowYear}`}> | ||
{declaration.commencer?.siren} | ||
</Link>, | ||
rowYear, | ||
declaration.entreprise?.type === "ues" | ||
? upperCase(declaration.entreprise?.type) | ||
: capitalize(declaration.entreprise?.type), | ||
declaration.entreprise?.tranche ? CompanyWorkforceRange.Label[declaration.entreprise.tranche] : undefined, | ||
formatIsoToFr(declaration["declaration-existante"].date || ""), | ||
declaration["resultat-global"]?.index || <span title="Non calculable">NC</span>, | ||
formatDeclarationOpmcStatus( | ||
getDeclarationOpmcStatus( | ||
declarationOpmcList.find(declarationOpmc => declarationOpmc.commencer?.annéeIndicateurs === rowYear), | ||
), | ||
rowSiren || "", | ||
rowYear || 0, | ||
), | ||
<Link | ||
key={`${rowSiren}-pdf`} | ||
href={`/index-egapro/declaration/${rowSiren}/${rowYear}/pdf`} | ||
download={`declaration_egapro_${rowSiren}_${Number(rowYear) + 1}.pdf`} | ||
> | ||
Télécharger | ||
</Link>, | ||
]; | ||
}); | ||
return ( | ||
<div> | ||
<h1>IndexList</h1> | ||
<Heading as="h1" variant="h5" text="Liste des déclarations transmises - Index Égalité Professionnelle" /> | ||
<Table headers={headers} data={data}></Table> | ||
</div> | ||
); | ||
}; |
61 changes: 61 additions & 0 deletions
61
packages/app/src/app/(default)/mon-espace/mes-declarations/RepeqList.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,61 @@ | ||
import Table from "@codegouvfr/react-dsfr/Table"; | ||
import { type RepresentationEquilibreeDTO } from "@common/core-domain/dtos/RepresentationEquilibreeDTO"; | ||
import { formatIsoToFr } from "@common/utils/date"; | ||
import { Heading, Link } from "@design-system"; | ||
|
||
export const RepeqList = ({ | ||
representationEquilibrees, | ||
}: { | ||
representationEquilibrees: RepresentationEquilibreeDTO[]; | ||
}) => { | ||
const headers = [ | ||
"SIREN", | ||
"ANNÉE ÉCARTS", | ||
"DATE DE DÉCLARATION", | ||
"% FEMMES CADRES", | ||
"% HOMMES CADRES", | ||
"% FEMMES MEMBRES", | ||
"% HOMMES MEMBRES", | ||
"RÉCAPITULATIF", | ||
]; | ||
|
||
function getPercent( | ||
filterReasonKey: string, | ||
percentKey: string, | ||
representationEquilibree: RepresentationEquilibreeDTO, | ||
) { | ||
return ( | ||
(!(filterReasonKey in representationEquilibree) && | ||
representationEquilibree[percentKey as keyof RepresentationEquilibreeDTO]?.toString()) || | ||
"NC" | ||
); | ||
} | ||
|
||
const data = representationEquilibrees.map(representationEquilibree => [ | ||
<Link | ||
key={representationEquilibree.siren} | ||
href={`/representation-equilibree/${representationEquilibree.siren}/${representationEquilibree.year}`} | ||
> | ||
{representationEquilibree.siren} | ||
</Link>, | ||
representationEquilibree.year, | ||
formatIsoToFr(representationEquilibree.declaredAt), | ||
getPercent("notComputableReasonExecutives", "executiveWomenPercent", representationEquilibree), | ||
getPercent("notComputableReasonExecutives", "executiveMenPercent", representationEquilibree), | ||
getPercent("notComputableReasonMembers", "memberWomenPercent", representationEquilibree), | ||
getPercent("notComputableReasonMembers", "memberMenPercent", representationEquilibree), | ||
<Link | ||
key={`${representationEquilibree.siren}-pdf`} | ||
href={`/representation-equilibree/${representationEquilibree.siren}/${representationEquilibree.year}/pdf`} | ||
download={`declaration_egapro_${representationEquilibree.siren}_${Number(representationEquilibree.year) + 1}.pdf`} | ||
> | ||
Télécharger | ||
</Link>, | ||
]); | ||
return ( | ||
<div> | ||
<Heading as="h1" variant="h5" text="Liste des déclarations transmises - Représentation Équilibrée" /> | ||
<Table headers={headers} data={data}></Table> | ||
</div> | ||
); | ||
}; |
80 changes: 80 additions & 0 deletions
80
packages/app/src/app/(default)/mon-espace/mes-declarations/SelectSiren.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
"use client"; | ||
|
||
import { fr } from "@codegouvfr/react-dsfr"; | ||
import SelectNext from "@codegouvfr/react-dsfr/SelectNext"; | ||
import { Grid, GridCol } from "@design-system"; | ||
import { zodResolver } from "@hookform/resolvers/zod"; | ||
import { first } from "lodash"; | ||
import { useRouter } from "next/navigation"; | ||
import { useEffect } from "react"; | ||
import { useForm } from "react-hook-form"; | ||
import { z } from "zod"; | ||
|
||
export const SelectSiren = ({ | ||
sirenListWithCompanyName, | ||
currentSiren, | ||
}: { | ||
currentSiren?: string; | ||
sirenListWithCompanyName: Array<{ companyName: string; siren: string }>; | ||
}) => { | ||
const router = useRouter(); | ||
const sirenSchema = z.object({ | ||
siren: z | ||
.string() | ||
.length(9) | ||
.regex(/^\d+$/) | ||
.refine(siren => sirenList.includes(siren), { | ||
message: "Vous n'avez pas les droits sur ce Siren.", | ||
}), | ||
}); | ||
|
||
type sirenFormType = z.infer<typeof sirenSchema>; | ||
|
||
const { | ||
register, | ||
handleSubmit, | ||
watch, | ||
formState: { isValid }, | ||
} = useForm<sirenFormType>({ | ||
resolver: zodResolver(sirenSchema), | ||
}); | ||
|
||
const sirenValue = watch("siren"); | ||
const sirenList = sirenListWithCompanyName.map(({ siren }) => siren); | ||
|
||
const onSubmit = (data: sirenFormType) => { | ||
router.push(`/mon-espace/mes-declarations?siren=${data.siren}`); | ||
}; | ||
|
||
useEffect(() => { | ||
if (isValid && sirenValue !== currentSiren) { | ||
handleSubmit(onSubmit)(); | ||
} | ||
}, [sirenValue, isValid, handleSubmit, onSubmit]); | ||
|
||
return ( | ||
<form onSubmit={handleSubmit(onSubmit)}> | ||
<Grid> | ||
<GridCol sm={3}> | ||
<SelectNext | ||
label="SIREN" | ||
nativeSelectProps={{ | ||
...register("siren"), | ||
}} | ||
options={sirenList.map(value => ({ | ||
value, | ||
label: value, | ||
selected: currentSiren ? value === currentSiren : value === first(sirenList), | ||
}))} | ||
/> | ||
</GridCol> | ||
<GridCol sm={9}> | ||
<div className={fr.cx("fr-pt-10v", "fr-pl-2v")}> | ||
<span className={fr.cx("fr-icon-building-line", "fr-pr-2v")} aria-hidden="true"></span> | ||
<span>{sirenListWithCompanyName.find(data => data.siren === sirenValue)?.companyName || ""}</span> | ||
</div> | ||
</GridCol> | ||
</Grid> | ||
</form> | ||
); | ||
}; |
Oops, something went wrong.