Design Library

Case file - evidence, counterfactual and decision

flair-case-file/ · 7 files

componentskill: emil-design-eng + design-taste-frontendpublic-recordscomplianceevidenceformsdense

Brief

The prompt that produced this. Re-run it to generate more in the same taste.

Aesthetic
institutional utility - DSFR-sober, ruled, evidence-led, dense, trust-first, unornamented
Reference
FLAIR case dialog, fiche cas
Intent
give an agent everything needed to accept or reject a scored dossier, and make recording that judgement the only way out of the screen
Guardrails
always show the evidence and its confidence before the score is justified, state what would lower the score alongside what raised it, require a matricule before a decision can be logged, require a reason for anything other than retaining
never let a decision be recorded anonymously, present an estimated amount as a determined one, hide the existing decision history for the dossier, imply the tool decided

Install

pnpm dlx shadcn@latest add http://localhost:3040/r/flair-case-file.json

Source

Meta block and library type import stripped - this is what pastes cleanly elsewhere.

'use client'

import { useState } from 'react'
import { CaseFile } from './case-file'
import type { ActionControle, Decision, FlairCase } from './lib'

const cas: FlairCase = {
  id: 'FLR-2026-00418',
  type: 'particulier',
  score: 92,
  rang: 1,
  montant_estime: 148_300,
  typologies: ['REVENUS_OCCULTES', 'COMPTE_ETRANGER_NON_DECLARE'],
  identite: {
    nom: 'Valcour',
    prenom: 'Noémie',
    departement: '35',
    commune: 'Rennes',
    categorie: 'Foyer fiscal — salariés',
  },
  preuves: [
    {
      typologie: 'REVENUS_OCCULTES',
      source: 'DSN',
      description:
        "Le salaire net social remonté par l'employeur dépasse de 62 400 € les salaires portés sur la déclaration 2042 pour la même année.",
      ecart: 62_400,
      fiabilite: 0.93,
    },
    {
      typologie: 'COMPTE_ETRANGER_NON_DECLARE',
      source: 'CRS',
      description:
        "Un compte détenu à l'étranger est signalé par échange automatique d'informations, sans déclaration 3916 ni revenus de capitaux correspondants.",
      ecart: 41_900,
      fiabilite: 0.71,
    },
    {
      typologie: 'REVENUS_OCCULTES',
      source: 'FLUX BANCAIRES',
      description:
        'Encaissements récurrents sans contrepartie déclarée identifiée, concentrés sur le dernier trimestre.',
      ecart: 0,
      fiabilite: 0.54,
    },
  ],
  contrefactuels: [
    'Une déclaration 3916 régularisant le compte étranger ferait passer le sous-score « compte étranger » sous le seuil de remontée.',
    'Un écart DSN inférieur à 15 % des salaires déclarés ramènerait le score global sous 50.',
  ],
  declare_vs_detecte: [
    { indicateur: 'Salaires annuels', declare: 34_200, detecte: 96_600, source: 'DSN' },
    { indicateur: 'Revenus de capitaux', declare: 0, detecte: 41_900, source: 'CRS' },
    { indicateur: 'Opérations créditrices', declare: 12, detecte: 38, source: 'Flux bancaires' },
  ],
  chronologie: [
    { annee: 2022, evenement: 'Première DSN employeur au-dessus des salaires déclarés.' },
    { annee: 2023, evenement: 'Signalement CRS reçu pour un compte ouvert en 2021.' },
    { annee: 2024, evenement: 'Écart DSN confirmé, dossier remonté au ciblage.' },
  ],
}

export default function FlairCaseFile() {
  const [matricule, setMatricule] = useState('')
  const [decisions, setDecisions] = useState<Decision[]>([])

  const enregistrer = (entree: { action: ActionControle; motif: string; matricule: string }) => {
    setDecisions((prev) => [
      // Newest first, matching how the dossier's history reads.
      { uid: `d-${prev.length + 1}`, dateIso: new Date().toISOString(), ...entree },
      ...prev,
    ])
  }

  return (
    <main className="min-h-[100dvh] bg-[#F6F6F6] p-4 text-[#161616] sm:p-8">
      <CaseFile
        cas={cas}
        decisionsCas={decisions}
        matricule={matricule}
        onMatriculeChange={setMatricule}
        onDecision={enregistrer}
      />
    </main>
  )
}