diff --git a/targets/frontend/src/components/layout/Nav.js b/targets/frontend/src/components/layout/Nav.js
index 06a91989d..68fda7eb1 100644
--- a/targets/frontend/src/components/layout/Nav.js
+++ b/targets/frontend/src/components/layout/Nav.js
@@ -90,19 +90,24 @@ export function Nav() {
diff --git a/targets/frontend/src/pages/kali/blocks.js b/targets/frontend/src/pages/kali/blocks.js
new file mode 100644
index 000000000..0652a6723
--- /dev/null
+++ b/targets/frontend/src/pages/kali/blocks.js
@@ -0,0 +1,226 @@
+/** @jsx jsx */
+import { useEffect, useMemo, useState } from "react";
+import { IoMdSave } from "react-icons/io";
+import { Button } from "src/components/button";
+import { Layout } from "src/components/layout/auth.layout";
+import { withCustomUrqlClient } from "src/hoc/CustomUrqlClient";
+import { withUserProvider } from "src/hoc/UserProvider";
+import { Card, jsx, Message, Select, Textarea } from "theme-ui";
+import { useMutation, useQuery } from "urql";
+
+const searchKaliDocumentQuery = `
+
+query KaliDocumentQuery {
+ kali_blocks(order_by: {title: asc}) {
+ id
+ idcc
+ title
+ blocks
+ }
+}
+
+`;
+
+const editKaliBlocksMutation = `
+mutation EditBlocks(
+ $id: String!,
+ $blocks: jsonb!,
+) {
+ update_kali_blocks_by_pk(
+ pk_columns: {id: $id},
+ _set: {
+ blocks: $blocks
+ }) {
+ id: id
+ }
+}
+`;
+
+// todo: move somewhere ?
+// d'après la note DGT "DGT - Fiche 2018-29 - Ordonnances 2017 -Fiche Hiérarchie des normes"
+const blocksDefinition = [
+ { id: 1, label: `Bloc 1 : Salaires minima hiérarchiques` },
+ { id: 2, label: `Bloc 2 : Classifications` },
+ {
+ id: 3,
+ label: `Bloc 3 : Mutualisation des fonds de financement du paritarisme`,
+ },
+ {
+ id: 4,
+ label: `Bloc 4 : Mutualisation des fonds de la formation professionnelle`,
+ },
+ { id: 5, label: `Bloc 5 : Prévoyance` },
+ {
+ id: 6,
+ label: `Bloc 6 : Durée du travail, répartition et aménagement des horaires`,
+ },
+ {
+ id: 7,
+ label: `Bloc 7 : CDD/CTT : durée minimale, majoration heures complémentaires et compléments d'heures`,
+ },
+ { id: 8, label: `Bloc 8 : CDI de chantier ou d'opération` },
+ {
+ id: 9,
+ label: `Bloc 9 : Egalité professionnelle entre les femmes et les hommes`,
+ },
+ {
+ id: 10,
+ label: `Bloc 10 : Conditions et les durées de renouvellement de la période d’essai`,
+ },
+ {
+ id: 11,
+ label: `Bloc 11 : Modalités de poursuite des contrats de travail`,
+ },
+ { id: 12, label: `Bloc 12 : Mise à disposition d’un salarié temporaire` },
+ { id: 13, label: `Bloc 13 : Rémunération minimale du salarié porté` },
+ {
+ id: 14,
+ label: `Bloc 14 : Prévention des effets de l’exposition aux facteurs de risques professionnels`,
+ },
+ {
+ id: 15,
+ label: `Bloc 15 : Insertion professionnelle et maintien dans l’emploi des travailleurs handicapés`,
+ },
+ {
+ id: 16,
+ label: `Bloc 16 : Effectif à partir duquel les délégués syndicaux peuvent être désignés, nombre et valorisation de leurs parcours syndical`,
+ },
+ { id: 17, label: `Bloc 17 : Primes pour travaux dangereux ou insalubres` },
+];
+
+function CcnBlocks({ id, blocks, onChange }) {
+ const nbBlocks = 17;
+ const initialSelections = useMemo(
+ () =>
+ Array.from(
+ { length: nbBlocks },
+ (k, v) => (blocks && blocks[v + 1] && blocks[v + 1]) || []
+ ),
+ [id, blocks]
+ );
+ useEffect(() => {
+ // reset selection state when input blocks change
+ setSelections(initialSelections);
+ setDirty(false);
+ }, [id]);
+ const [selections, setSelections] = useState(initialSelections);
+ const [dirty, setDirty] = useState(false);
+ const onTextAreaChange = (e, index) => {
+ const newSelection = e.target.value.split("\n");
+ const newSelections = [...selections];
+ newSelections[index] = newSelection;
+ setSelections(newSelections);
+ setDirty(true);
+ };
+ const onSaveClick = async () => {
+ const selectionsDict = selections.reduce(
+ (a, c, i) => ({
+ ...a,
+ [i + 1]: c,
+ }),
+ {}
+ );
+ await onChange(selectionsDict);
+ setDirty(false);
+ };
+ return (
+