Checkpoint: v4 : Import Excel (.xlsx) et CSV natif pour établissements et contacts, pages Mes Solutions Numériques et Solutions Logicielles avec accordéons, CGU réaffichée à chaque session, moteur de recherche multicritères corrigé. 33 tests passés, 0 erreur TypeScript.
This commit is contained in:
@@ -23,6 +23,7 @@ import {
|
|||||||
X,
|
X,
|
||||||
} from "lucide-react";
|
} from "lucide-react";
|
||||||
import { useState, useMemo, Fragment, useRef, useCallback } from "react";
|
import { useState, useMemo, Fragment, useRef, useCallback } from "react";
|
||||||
|
import * as XLSX from "xlsx";
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
|
|
||||||
// ─── Types ────────────────────────────────────────────────────────────────────
|
// ─── Types ────────────────────────────────────────────────────────────────────
|
||||||
@@ -866,11 +867,22 @@ function ImportEtablissementsPanel() {
|
|||||||
const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
const isXlsx = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
if (isXlsx) {
|
||||||
|
reader.onload = (ev) => {
|
||||||
|
const data = new Uint8Array(ev.target?.result as ArrayBuffer);
|
||||||
|
const wb = XLSX.read(data, { type: "array" });
|
||||||
|
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||||
|
const csv = XLSX.utils.sheet_to_csv(ws, { FS: ";" });
|
||||||
|
parseCSV(csv);
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
} else {
|
||||||
reader.onload = (ev) => parseCSV(ev.target?.result as string);
|
reader.onload = (ev) => parseCSV(ev.target?.result as string);
|
||||||
reader.readAsText(file, "UTF-8");
|
reader.readAsText(file, "UTF-8");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImport = async () => {
|
const handleImport = async () => {
|
||||||
setImporting(true);
|
setImporting(true);
|
||||||
let count = 0;
|
let count = 0;
|
||||||
@@ -939,7 +951,7 @@ function ImportEtablissementsPanel() {
|
|||||||
<FileText size={32} className="mx-auto text-muted-foreground/40 mb-2" />
|
<FileText size={32} className="mx-auto text-muted-foreground/40 mb-2" />
|
||||||
<p className="text-sm font-medium text-foreground">Cliquez pour sélectionner un fichier CSV</p>
|
<p className="text-sm font-medium text-foreground">Cliquez pour sélectionner un fichier CSV</p>
|
||||||
<p className="text-xs text-muted-foreground mt-1">Séparateur : point-virgule, virgule ou tabulation</p>
|
<p className="text-xs text-muted-foreground mt-1">Séparateur : point-virgule, virgule ou tabulation</p>
|
||||||
<input ref={fileRef} type="file" accept=".csv,.txt" className="hidden" onChange={handleFile} />
|
<input ref={fileRef} type="file" accept=".csv,.txt,.xlsx,.xls" className="hidden" onChange={handleFile} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Colonnes attendues */}
|
{/* Colonnes attendues */}
|
||||||
@@ -1078,11 +1090,22 @@ function ImportContactsPanel() {
|
|||||||
const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
|
const handleFile = (e: React.ChangeEvent<HTMLInputElement>) => {
|
||||||
const file = e.target.files?.[0];
|
const file = e.target.files?.[0];
|
||||||
if (!file) return;
|
if (!file) return;
|
||||||
|
const isXlsx = file.name.endsWith(".xlsx") || file.name.endsWith(".xls");
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
|
if (isXlsx) {
|
||||||
|
reader.onload = (ev) => {
|
||||||
|
const data = new Uint8Array(ev.target?.result as ArrayBuffer);
|
||||||
|
const wb = XLSX.read(data, { type: "array" });
|
||||||
|
const ws = wb.Sheets[wb.SheetNames[0]];
|
||||||
|
const csv = XLSX.utils.sheet_to_csv(ws, { FS: ";" });
|
||||||
|
parseCSV(csv);
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(file);
|
||||||
|
} else {
|
||||||
reader.onload = (ev) => parseCSV(ev.target?.result as string);
|
reader.onload = (ev) => parseCSV(ev.target?.result as string);
|
||||||
reader.readAsText(file, "UTF-8");
|
reader.readAsText(file, "UTF-8");
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleImport = async () => {
|
const handleImport = async () => {
|
||||||
setImporting(true);
|
setImporting(true);
|
||||||
let count = 0;
|
let count = 0;
|
||||||
@@ -1150,7 +1173,7 @@ function ImportContactsPanel() {
|
|||||||
<FileText size={32} className="mx-auto text-muted-foreground/40 mb-2" />
|
<FileText size={32} className="mx-auto text-muted-foreground/40 mb-2" />
|
||||||
<p className="text-sm font-medium text-foreground">Cliquez pour sélectionner un fichier CSV</p>
|
<p className="text-sm font-medium text-foreground">Cliquez pour sélectionner un fichier CSV</p>
|
||||||
<p className="text-xs text-muted-foreground mt-1">Séparateur : point-virgule, virgule ou tabulation</p>
|
<p className="text-xs text-muted-foreground mt-1">Séparateur : point-virgule, virgule ou tabulation</p>
|
||||||
<input ref={fileRef} type="file" accept=".csv,.txt" className="hidden" onChange={handleFile} />
|
<input ref={fileRef} type="file" accept=".csv,.txt,.xlsx,.xls" className="hidden" onChange={handleFile} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
{/* Colonnes attendues */}
|
{/* Colonnes attendues */}
|
||||||
|
|||||||
@@ -77,6 +77,7 @@
|
|||||||
"tailwindcss-animate": "^1.0.7",
|
"tailwindcss-animate": "^1.0.7",
|
||||||
"vaul": "^1.1.2",
|
"vaul": "^1.1.2",
|
||||||
"wouter": "^3.3.5",
|
"wouter": "^3.3.5",
|
||||||
|
"xlsx": "^0.18.5",
|
||||||
"zod": "^4.1.12"
|
"zod": "^4.1.12"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
|||||||
72
pnpm-lock.yaml
generated
72
pnpm-lock.yaml
generated
@@ -208,6 +208,9 @@ importers:
|
|||||||
wouter:
|
wouter:
|
||||||
specifier: ^3.3.5
|
specifier: ^3.3.5
|
||||||
version: 3.7.1(patch_hash=4e16e6ff3fde7d6c1024d3e0c8605dc9eb6afb690d0d49958c2f449091813072)(react@19.2.1)
|
version: 3.7.1(patch_hash=4e16e6ff3fde7d6c1024d3e0c8605dc9eb6afb690d0d49958c2f449091813072)(react@19.2.1)
|
||||||
|
xlsx:
|
||||||
|
specifier: ^0.18.5
|
||||||
|
version: 0.18.5
|
||||||
zod:
|
zod:
|
||||||
specifier: ^4.1.12
|
specifier: ^4.1.12
|
||||||
version: 4.1.12
|
version: 4.1.12
|
||||||
@@ -2389,6 +2392,10 @@ packages:
|
|||||||
add@2.0.6:
|
add@2.0.6:
|
||||||
resolution: {integrity: sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==}
|
resolution: {integrity: sha512-j5QzrmsokwWWp6kUcJQySpbG+xfOBqqKnup3OIk1pz+kB/80SLorZ9V8zHFLO92Lcd+hbvq8bT+zOGoPkmBV0Q==}
|
||||||
|
|
||||||
|
adler-32@1.3.1:
|
||||||
|
resolution: {integrity: sha512-ynZ4w/nUUv5rrsR8UUGoe1VC9hZj6V5hU9Qw1HlMDJGEJw5S7TfTErWTjMys6M7vr0YWcPqs3qAr4ss0nDfP+A==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
aria-hidden@1.2.6:
|
aria-hidden@1.2.6:
|
||||||
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
|
resolution: {integrity: sha512-ik3ZgC9dY/lYVVM++OISsaYDeg1tb0VtP5uL3ouh1koGOaUMDPpbFIei4JkFimWUFPn90sbMNMXQAIVOlnYKJA==}
|
||||||
engines: {node: '>=10'}
|
engines: {node: '>=10'}
|
||||||
@@ -2465,6 +2472,10 @@ packages:
|
|||||||
ccount@2.0.1:
|
ccount@2.0.1:
|
||||||
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==}
|
||||||
|
|
||||||
|
cfb@1.2.2:
|
||||||
|
resolution: {integrity: sha512-KfdUZsSOw19/ObEWasvBP/Ac4reZvAGauZhs6S/gqNhXhI7cKwvlH7ulj+dOEYnca4bm4SGo8C1bTAQvnTjgQA==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
chai@5.3.3:
|
chai@5.3.3:
|
||||||
resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
|
resolution: {integrity: sha512-4zNhdJD/iOjSH0A05ea+Ke6MU5mmpQcbQsSOkgdaUMJ9zTlDTD/GYlwohmIE2u0gaxHYiVHEn1Fw9mZ/ktJWgw==}
|
||||||
engines: {node: '>=18'}
|
engines: {node: '>=18'}
|
||||||
@@ -2510,6 +2521,10 @@ packages:
|
|||||||
react: ^18 || ^19 || ^19.0.0-rc
|
react: ^18 || ^19 || ^19.0.0-rc
|
||||||
react-dom: ^18 || ^19 || ^19.0.0-rc
|
react-dom: ^18 || ^19 || ^19.0.0-rc
|
||||||
|
|
||||||
|
codepage@1.15.0:
|
||||||
|
resolution: {integrity: sha512-3g6NUTPd/YtuuGrhMnOMRjFc+LJw/bnMp3+0r/Wcz3IXUuCosKRJvMphm5+Q+bvTVGcJJuRvVLuYba+WojaFaA==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
combined-stream@1.0.8:
|
combined-stream@1.0.8:
|
||||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@@ -2563,6 +2578,11 @@ packages:
|
|||||||
cose-base@2.2.0:
|
cose-base@2.2.0:
|
||||||
resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
|
resolution: {integrity: sha512-AzlgcsCbUMymkADOJtQm3wO9S3ltPfYOFD5033keQn9NJzIbtnZj+UdBJe7DYml/8TdbtHJW3j58SOnKhWY/5g==}
|
||||||
|
|
||||||
|
crc-32@1.2.2:
|
||||||
|
resolution: {integrity: sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
cssesc@3.0.0:
|
cssesc@3.0.0:
|
||||||
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@@ -3059,6 +3079,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
resolution: {integrity: sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
frac@1.1.2:
|
||||||
|
resolution: {integrity: sha512-w/XBfkibaTl3YDqASwfDUqkna4Z2p9cFSr1aHDt0WoMTECnRfBOv2WArlZILlqgWlmdIlALXGpM2AOhEk5W3IA==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
fraction.js@4.3.7:
|
fraction.js@4.3.7:
|
||||||
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
||||||
|
|
||||||
@@ -3972,6 +3996,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
|
resolution: {integrity: sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg==}
|
||||||
engines: {node: '>= 0.6'}
|
engines: {node: '>= 0.6'}
|
||||||
|
|
||||||
|
ssf@0.11.2:
|
||||||
|
resolution: {integrity: sha512-+idbmIXoYET47hH+d7dfm2epdOMUDjqcB4648sTZ+t2JwoyBFL/insLfB/racrDmsKB3diwsDA696pZMieAC5g==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
stackback@0.0.2:
|
stackback@0.0.2:
|
||||||
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==}
|
||||||
|
|
||||||
@@ -4317,11 +4345,24 @@ packages:
|
|||||||
engines: {node: '>=8'}
|
engines: {node: '>=8'}
|
||||||
hasBin: true
|
hasBin: true
|
||||||
|
|
||||||
|
wmf@1.0.2:
|
||||||
|
resolution: {integrity: sha512-/p9K7bEh0Dj6WbXg4JG0xvLQmIadrner1bi45VMJTfnbVHsc7yIajZyoSoK60/dtVBs12Fm6WkUI5/3WAVsNMw==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
|
word@0.3.0:
|
||||||
|
resolution: {integrity: sha512-OELeY0Q61OXpdUfTp+oweA/vtLVg5VDOXh+3he3PNzLGG/y0oylSOC1xRVj0+l4vQ3tj/bB1HVHv1ocXkQceFA==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
|
||||||
wouter@3.7.1:
|
wouter@3.7.1:
|
||||||
resolution: {integrity: sha512-od5LGmndSUzntZkE2R5CHhoiJ7YMuTIbiXsa0Anytc2RATekgv4sfWRAxLEULBrp7ADzinWQw8g470lkT8+fOw==}
|
resolution: {integrity: sha512-od5LGmndSUzntZkE2R5CHhoiJ7YMuTIbiXsa0Anytc2RATekgv4sfWRAxLEULBrp7ADzinWQw8g470lkT8+fOw==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
react: '>=16.8.0'
|
react: '>=16.8.0'
|
||||||
|
|
||||||
|
xlsx@0.18.5:
|
||||||
|
resolution: {integrity: sha512-dmg3LCjBPHZnQp5/F/+nnTa+miPJxUXB6vtk42YjBBKayDNagxGEeIdWApkYPOf3Z3pm3k62Knjzp7lMeTEtFQ==}
|
||||||
|
engines: {node: '>=0.8'}
|
||||||
|
hasBin: true
|
||||||
|
|
||||||
yallist@3.1.1:
|
yallist@3.1.1:
|
||||||
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==}
|
||||||
|
|
||||||
@@ -6757,6 +6798,8 @@ snapshots:
|
|||||||
|
|
||||||
add@2.0.6: {}
|
add@2.0.6: {}
|
||||||
|
|
||||||
|
adler-32@1.3.1: {}
|
||||||
|
|
||||||
aria-hidden@1.2.6:
|
aria-hidden@1.2.6:
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib: 2.8.1
|
tslib: 2.8.1
|
||||||
@@ -6840,6 +6883,11 @@ snapshots:
|
|||||||
|
|
||||||
ccount@2.0.1: {}
|
ccount@2.0.1: {}
|
||||||
|
|
||||||
|
cfb@1.2.2:
|
||||||
|
dependencies:
|
||||||
|
adler-32: 1.3.1
|
||||||
|
crc-32: 1.2.2
|
||||||
|
|
||||||
chai@5.3.3:
|
chai@5.3.3:
|
||||||
dependencies:
|
dependencies:
|
||||||
assertion-error: 2.0.1
|
assertion-error: 2.0.1
|
||||||
@@ -6892,6 +6940,8 @@ snapshots:
|
|||||||
- '@types/react'
|
- '@types/react'
|
||||||
- '@types/react-dom'
|
- '@types/react-dom'
|
||||||
|
|
||||||
|
codepage@1.15.0: {}
|
||||||
|
|
||||||
combined-stream@1.0.8:
|
combined-stream@1.0.8:
|
||||||
dependencies:
|
dependencies:
|
||||||
delayed-stream: 1.0.0
|
delayed-stream: 1.0.0
|
||||||
@@ -6932,6 +6982,8 @@ snapshots:
|
|||||||
dependencies:
|
dependencies:
|
||||||
layout-base: 2.0.1
|
layout-base: 2.0.1
|
||||||
|
|
||||||
|
crc-32@1.2.2: {}
|
||||||
|
|
||||||
cssesc@3.0.0: {}
|
cssesc@3.0.0: {}
|
||||||
|
|
||||||
csstype@3.1.3: {}
|
csstype@3.1.3: {}
|
||||||
@@ -7419,6 +7471,8 @@ snapshots:
|
|||||||
|
|
||||||
forwarded@0.2.0: {}
|
forwarded@0.2.0: {}
|
||||||
|
|
||||||
|
frac@1.1.2: {}
|
||||||
|
|
||||||
fraction.js@4.3.7: {}
|
fraction.js@4.3.7: {}
|
||||||
|
|
||||||
framer-motion@12.23.22(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
|
framer-motion@12.23.22(react-dom@19.2.1(react@19.2.1))(react@19.2.1):
|
||||||
@@ -8681,6 +8735,10 @@ snapshots:
|
|||||||
|
|
||||||
sqlstring@2.3.3: {}
|
sqlstring@2.3.3: {}
|
||||||
|
|
||||||
|
ssf@0.11.2:
|
||||||
|
dependencies:
|
||||||
|
frac: 1.1.2
|
||||||
|
|
||||||
stackback@0.0.2: {}
|
stackback@0.0.2: {}
|
||||||
|
|
||||||
statuses@2.0.1: {}
|
statuses@2.0.1: {}
|
||||||
@@ -9026,6 +9084,10 @@ snapshots:
|
|||||||
siginfo: 2.0.0
|
siginfo: 2.0.0
|
||||||
stackback: 0.0.2
|
stackback: 0.0.2
|
||||||
|
|
||||||
|
wmf@1.0.2: {}
|
||||||
|
|
||||||
|
word@0.3.0: {}
|
||||||
|
|
||||||
wouter@3.7.1(patch_hash=4e16e6ff3fde7d6c1024d3e0c8605dc9eb6afb690d0d49958c2f449091813072)(react@19.2.1):
|
wouter@3.7.1(patch_hash=4e16e6ff3fde7d6c1024d3e0c8605dc9eb6afb690d0d49958c2f449091813072)(react@19.2.1):
|
||||||
dependencies:
|
dependencies:
|
||||||
mitt: 3.0.1
|
mitt: 3.0.1
|
||||||
@@ -9033,6 +9095,16 @@ snapshots:
|
|||||||
regexparam: 3.0.0
|
regexparam: 3.0.0
|
||||||
use-sync-external-store: 1.6.0(react@19.2.1)
|
use-sync-external-store: 1.6.0(react@19.2.1)
|
||||||
|
|
||||||
|
xlsx@0.18.5:
|
||||||
|
dependencies:
|
||||||
|
adler-32: 1.3.1
|
||||||
|
cfb: 1.2.2
|
||||||
|
codepage: 1.15.0
|
||||||
|
crc-32: 1.2.2
|
||||||
|
ssf: 0.11.2
|
||||||
|
wmf: 1.0.2
|
||||||
|
word: 0.3.0
|
||||||
|
|
||||||
yallist@3.1.1: {}
|
yallist@3.1.1: {}
|
||||||
|
|
||||||
yallist@5.0.0: {}
|
yallist@5.0.0: {}
|
||||||
|
|||||||
12
todo.md
12
todo.md
@@ -64,9 +64,9 @@
|
|||||||
|
|
||||||
## Évolution v4
|
## Évolution v4
|
||||||
|
|
||||||
- [ ] CGU : afficher à chaque connexion (réinitialiser à chaque login, pas seulement à la fermeture du navigateur)
|
- [x] CGU : afficher à chaque connexion (réinitialiser à chaque login, pas seulement à la fermeture du navigateur)
|
||||||
- [ ] Moteur de recherche : corriger les filtres multicritères qui ne fonctionnent pas
|
- [x] Moteur de recherche : corriger les filtres multicritères qui ne fonctionnent pas
|
||||||
- [ ] Page "Mes Solutions Numériques" : liste de toutes mes solutions avec les établissements les utilisant (accordéon)
|
- [x] Page "Mes Solutions Numériques" : liste de toutes mes solutions avec les établissements les utilisant (accordéon)
|
||||||
- [ ] Page "Solutions Logicielles" : liste globale des solutions avec établissements équipés (accordéon), accessible depuis la sidebar
|
- [x] Page "Solutions Logicielles" : liste globale des solutions avec établissements équipés (accordéon), accessible depuis la sidebar
|
||||||
- [ ] Admin : onglet import établissements (CSV/Excel)
|
- [x] Admin : onglet import établissements (CSV/Excel)
|
||||||
- [ ] Admin : onglet import contacts (CSV/Excel)
|
- [x] Admin : onglet import contacts (CSV/Excel)
|
||||||
|
|||||||
Reference in New Issue
Block a user