Checkpoint: Correctif UX Salaires : pendant le chargement de la sonde AES-256-GCM, l'interface affiche désormais une vérification neutre et désactive l'import au lieu d'afficher à tort une alerte de blocage. Test de régression ajouté. Validation complète réussie : 58 tests, TypeScript et build.
All checks were successful
Validation applicative / TypeScript, tests et build (push) Successful in 1m52s
All checks were successful
Validation applicative / TypeScript, tests et build (push) Successful in 1m52s
This commit is contained in:
18
client/src/pages/Salaires.test.tsx
Normal file
18
client/src/pages/Salaires.test.tsx
Normal file
@@ -0,0 +1,18 @@
|
||||
import { renderToStaticMarkup } from "react-dom/server";
|
||||
import { describe, expect, it } from "vitest";
|
||||
import { PayrollSecurityNotice } from "./Salaires";
|
||||
|
||||
describe("PayrollSecurityNotice", () => {
|
||||
it("affiche un état neutre de vérification, et non une alerte, tant que la sonde de chiffrement charge", () => {
|
||||
const html = renderToStaticMarkup(<PayrollSecurityNotice isLoading ready={undefined} />);
|
||||
|
||||
expect(html).toContain("Vérification du chiffrement sécurisé…");
|
||||
expect(html).not.toContain("Import bloqué");
|
||||
});
|
||||
|
||||
it("n'affiche l'alerte de blocage que lorsque la sonde a répondu indisponible", () => {
|
||||
const html = renderToStaticMarkup(<PayrollSecurityNotice isLoading={false} ready={false} />);
|
||||
|
||||
expect(html).toContain("Import bloqué");
|
||||
});
|
||||
});
|
||||
@@ -61,7 +61,7 @@ export default function Salaires() {
|
||||
const filters = useMemo(() => ({ annee, mois }), [annee, mois]);
|
||||
const { data, isLoading, isError } = trpc.salaires.list.useQuery(filters);
|
||||
const { data: periods = [] } = trpc.salaires.periods.useQuery();
|
||||
const { data: security } = trpc.salaires.getSecurityStatus.useQuery();
|
||||
const { data: security, isLoading: isSecurityLoading } = trpc.salaires.getSecurityStatus.useQuery();
|
||||
|
||||
const years = useMemo(() => {
|
||||
const options = new Set([now.getFullYear(), ...periods.map((period) => period.annee)]);
|
||||
@@ -77,7 +77,7 @@ export default function Salaires() {
|
||||
toast.error("Le fichier sélectionné doit être un PDF.");
|
||||
return;
|
||||
}
|
||||
if (!security?.ready) {
|
||||
if (security?.ready !== true) {
|
||||
toast.error("Le chiffrement sécurisé est indisponible : l'import est bloqué.");
|
||||
return;
|
||||
}
|
||||
@@ -143,12 +143,7 @@ export default function Salaires() {
|
||||
</header>
|
||||
|
||||
<div className="flex-1 overflow-auto px-6 py-5 space-y-5">
|
||||
{!security?.ready && (
|
||||
<section className="rounded-xl border border-rose-200 bg-rose-50 px-4 py-3 flex gap-3 text-sm text-rose-900">
|
||||
<CircleAlert className="w-4 h-4 mt-0.5 shrink-0 text-rose-600" />
|
||||
<p><strong>Import bloqué.</strong> Le chiffrement AES-256-GCM n'est pas disponible dans cet environnement. Aucun PDF n'est transmis sans chiffrement.</p>
|
||||
</section>
|
||||
)}
|
||||
<PayrollSecurityNotice isLoading={isSecurityLoading} ready={security?.ready} />
|
||||
|
||||
<section className="bg-card border border-border rounded-xl shadow-sm p-4">
|
||||
<div className="flex flex-wrap items-end gap-3">
|
||||
@@ -185,7 +180,7 @@ export default function Salaires() {
|
||||
<FileText className="w-6 h-6 text-emerald-600 shrink-0" />
|
||||
<span className="min-w-0 flex-1"><strong className="block text-sm text-foreground truncate">{selectedFile?.name ?? "Choisir la liasse PDF"}</strong><span className="text-xs text-muted-foreground">PDF uniquement, 25 Mo maximum</span><input ref={inputRef} type="file" accept="application/pdf,.pdf" aria-label="Choisir la liasse PDF" onChange={(event) => setSelectedFile(event.target.files?.[0] ?? null)} className="mt-1.5 block max-w-full text-xs text-muted-foreground file:mr-3 file:rounded-md file:border-0 file:bg-emerald-600 file:px-2.5 file:py-1 file:text-xs file:font-semibold file:text-white hover:file:bg-emerald-700" /></span>
|
||||
</div>
|
||||
<button onClick={uploadLiasse} disabled={isUploading || hasExistingPeriod || !selectedFile || !security?.ready} className="h-10 inline-flex items-center justify-center gap-2 rounded-lg bg-emerald-600 px-4 text-sm font-semibold text-white hover:bg-emerald-700 disabled:cursor-not-allowed disabled:opacity-50 transition-colors active:scale-[0.97]">
|
||||
<button onClick={uploadLiasse} disabled={isUploading || isSecurityLoading || hasExistingPeriod || !selectedFile || security?.ready !== true} className="h-10 inline-flex items-center justify-center gap-2 rounded-lg bg-emerald-600 px-4 text-sm font-semibold text-white hover:bg-emerald-700 disabled:cursor-not-allowed disabled:opacity-50 transition-colors active:scale-[0.97]">
|
||||
{isUploading ? <Loader2 className="w-4 h-4 animate-spin" /> : <LockKeyhole className="w-4 h-4" />} {isUploading ? "Archivage…" : "Importer et chiffrer"}
|
||||
</button>
|
||||
</div>
|
||||
@@ -224,3 +219,11 @@ function StatCard({ icon: Icon, label, value, accent }: { icon: React.ElementTyp
|
||||
function DataNotice({ message }: { message: string }) {
|
||||
return <div className="py-12 text-center text-sm text-muted-foreground"><CircleAlert className="w-5 h-5 mx-auto mb-2 text-muted-foreground/60" />{message}</div>;
|
||||
}
|
||||
|
||||
export function PayrollSecurityNotice({ isLoading, ready }: { isLoading: boolean; ready: boolean | undefined }) {
|
||||
if (isLoading) {
|
||||
return <section className="rounded-xl border border-sky-200 bg-sky-50 px-4 py-3 flex gap-3 text-sm text-sky-900"><Loader2 className="w-4 h-4 mt-0.5 shrink-0 animate-spin text-sky-700" /><p><strong>Vérification du chiffrement sécurisé…</strong> L'import reste temporairement désactivé.</p></section>;
|
||||
}
|
||||
if (ready === true) return null;
|
||||
return <section className="rounded-xl border border-rose-200 bg-rose-50 px-4 py-3 flex gap-3 text-sm text-rose-900"><CircleAlert className="w-4 h-4 mt-0.5 shrink-0 text-rose-600" /><p><strong>Import bloqué.</strong> Le chiffrement AES-256-GCM n'est pas disponible dans cet environnement. Aucun PDF n'est transmis sans chiffrement.</p></section>;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user