<#
====EXECUTION=====
@echo off
:: Lance PowerShell en mode discret, ignore la politique de restriction et exécute le script
start /min powershell.exe -ExecutionPolicy Bypass -WindowStyle Hidden -File "%~dp0recouvrement.ps1"
exit
====STRUCTURE======
dans le script ils sont en dur
#>
Add-Type -AssemblyName System.Windows.Forms
Add-Type -AssemblyName System.Drawing
$colorForm = [System.Drawing.Color]::FromArgb(10,10,20)
$colorGroup = [System.Drawing.Color]::FromArgb(25,25,45)
$colorTextbox = [System.Drawing.Color]::FromArgb(18,18,35)
$colorList = [System.Drawing.Color]::FromArgb(15,15,30)
$colorButton = [System.Drawing.Color]::FromArgb(80,0,130)
$starGreenVif = [System.Drawing.Color]::FromArgb(140, 198, 63) # Vert Anis STAR officiel (Logo étoile)
$starGreenFonce = [System.Drawing.Color]::FromArgb(0, 104, 87) # Vert Forêt STAR officiel (Texte et accents)
$neonBlue = [System.Drawing.Color]::DeepSkyBlue
$neonGreen = [System.Drawing.Color]::Lime
$fontDefault = New-Object System.Drawing.Font("Segoe UI", 10)
$fontTitle = New-Object System.Drawing.Font("Segoe UI Semibold", 20, [System.Drawing.FontStyle]::Bold)
$fontList = New-Object System.Drawing.Font("Consolas",10,[System.Drawing.FontStyle]::Bold)
$global:dataFile = "C:\Users\walid\Desktop\recouvrement.csv"
if (-not (Test-Path $dataFile)) { New-Item -ItemType File -Path $dataFile | Out-Null }
function Format-PrimeWalid($valeur) {
# On enlève tout ce qui n'est pas un chiffre
$propre = $valeur -replace '[^\d]',''
if ($propre -eq "" -or $propre -eq $null) { return "0" }
# Configuration du formatage avec espace
$nfi = (New-Object System.Globalization.CultureInfo("fr-FR")).NumberFormat
$nfi.NumberGroupSeparator = " "
$nfi.NumberDecimalDigits = 0
return ([long]$propre).ToString("N", $nfi)
}
function Use-LongDelay($police, $irdsChecked)
{
if ($irdsChecked) { return $true }
$patterns = @("^555","^504","^501","^ci0555","^ci0554")
foreach ($p in $patterns) {
if (($police -match $p) -and $irdsChecked) { return $true }
}
return $false
}
# =========================================================
# FADE IN
# =========================================================
function Fade-InForm($form) {
$form.Opacity = 0
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 20
$timer.Add_Tick({
if ($form.Opacity -lt 1) {
$form.Opacity += 0.05
} else {
$form.Opacity = 1
$this.Stop()
}
})
$timer.Start()
}
function Ensure-IRDSColumn($item) {
while ($item.SubItems.Count -lt 10) {
$item.SubItems.Add("") | Out-Null
}
}
# =========================================================
# FORM
# =========================================================
$form = New-Object System.Windows.Forms.Form
$form.Text = " 🚀 OUTIL RECOUVREMENT - BY WALID TOUMI"
$form.Size = New-Object System.Drawing.Size(1150,650)
$form.StartPosition = "CenterScreen"
$form.Font = $fontDefault
$form.BackColor = $colorForm
$form.MinimumSize = New-Object System.Drawing.Size(1150,650)
$form.Add_Paint({
if ($form.ClientRectangle.Width -le 0 -or $form.ClientRectangle.Height -le 0) {
return
}
$brush = New-Object System.Drawing.Drawing2D.LinearGradientBrush(
$form.ClientRectangle,
[System.Drawing.Color]::FromArgb(5,5,15),
[System.Drawing.Color]::FromArgb(45,0,90),
[System.Drawing.Drawing2D.LinearGradientMode]::Vertical
)
$_.Graphics.FillRectangle($brush, $form.ClientRectangle)
$brush.Dispose()
})
# =========================================================
# TITRE
# =========================================================
$title = New-Object System.Windows.Forms.Label
$title.Text = "SUIVI DES QUITTANCES IMPAYEES"
$title.Font = $fontTitle
$title.ForeColor = $neonBlue
$title.AutoSize = $true
$title.Location = New-Object System.Drawing.Point(20,15)
$form.Controls.Add($title)
# ===================================================
# LISTVIEW
# ===================================================
$list = New-Object System.Windows.Forms.ListView
$list.Location = New-Object System.Drawing.Point(20,60)
$list.Size = New-Object System.Drawing.Size(1080,250)
$list.View = 'Details'
$list.FullRowSelect = $true
$list.GridLines = $false
$list.BackColor = $colorList
$list.ForeColor = [System.Drawing.Color]::White
$list.Font = $fontList
$list.Anchor = "Top,Left,Right"
$list.HideSelection = $false
$list.add_ColumnClick({
param($sender, $e)
# Inverse le tri si on clique deux fois sur la même colonne
if ($script:lastCol -eq $e.Column) { $script:sortAsc = -not $script:sortAsc }
else { $script:sortAsc = $true }
$script:lastCol = $e.Column
$list.BeginUpdate()
# On récupère tous les items, on trie et on réinsère
$items = @($list.Items)
$list.Items.Clear()
$sorted = $items | Sort-Object @{Expression={
$val = $_.SubItems[$e.Column].Text
# Si c'est la colonne Prime (index 2), on trie par nombre
if ($e.Column -eq 2) {
[double]($val -replace '[^\d,.]','')
} else { $val }
}} -Descending:(-not $script:sortAsc)
$list.Items.AddRange($sorted)
$list.EndUpdate()
})
# Créer la barre de statut
$statusBar = New-Object System.Windows.Forms.StatusStrip
$statusBar.SizingGrip = $false # Empêche le redimensionnement de la barre de statut
$statusBar.Dock = "Bottom" # Place la barre de statut en bas
$statusBar.BackColor = $neonBlue
$statusBar.Add_Paint({
param($s, $e)
$e.Graphics.DrawLine((New-Object Drawing.Pen($neonBlue, 3)), 0, 0, $s.Width, 0)
})
# Créer un label pour afficher les informations de statut
$lblStatus = New-Object System.Windows.Forms.ToolStripStatusLabel
$lblStatus.Spring = $true
$lblStatus.TextAlign = "MiddleLeft"
$lblStatus.ForeColor = 'black' # Couleur du texte
$statusBar.Items.Add($lblStatus) # Ajoute le label à la barre de statut
# Ajoute la barre de statut au formulaire
$form.Controls.Add($statusBar)
function Refresh-Status {
$ok=0;$c=0;$s=0;$r=0;$primes=0.0
foreach ($row in $list.Items) {
switch ($row.SubItems[7].Text) {
"OK"{$ok++}
"Contentieux"{$c++}
"Suspendu"{$s++}
"Résilié"{$r++}
}
# On supprime les espaces ( \s+ ) avant de transformer en nombre pour le calcul
$nombrePourCalcul = $row.SubItems[2].Text -replace '\s+',''
$val = 0.0
if ([double]::TryParse($nombrePourCalcul.Replace(',', '.'), [ref]$val)) {
$primes += $val
}
}
# Met à jour le texte du label de la barre de statut
$lblStatus.Text = "🟢 DANS LES DELAIS : $ok 🟡 PRE-CONTENTIEUX : $c 🟠 SUSPENDU : $s 🔴 RESILIER : $r ֍ A COLLECTER : $($primes.tostring("N2")) "
$lblStatus.Text += " | 💾 Sauvegardé à $(Get-Date -Format 'HH:mm:ss')"
}
#####################################################
$columns = @(
"Police",
"Assuré",
"Prime",
"DateEffet",
"Contentieux",
"Suspension",
"Résiliation",
"Statut",
"NombreJoursAvant",
"IRDS/STE"
)
foreach ($c in $columns) { $list.Columns.Add($c,130) | Out-Null }
$form.Controls.Add($list)
$grp = New-Object System.Windows.Forms.GroupBox
$grp.Text = "Dossier"
$grp.Location = New-Object System.Drawing.Point(20,330)
$grp.Size = New-Object System.Drawing.Size(1080,180)
$grp.ForeColor = $neonGreen
$grp.BackColor = $colorGroup
$grp.Anchor = "Bottom,Left,Right"
$form.Controls.Add($grp)
function Add-Label($t,$x,$y) {
$l = New-Object System.Windows.Forms.Label
$l.Text = $t
$l.Location = New-Object System.Drawing.Point($x,$y)
$l.AutoSize = $true
$l.ForeColor = [System.Drawing.Color]::White
$grp.Controls.Add($l)
}
function Add-TB($x,$y) {
$tb = New-Object System.Windows.Forms.TextBox
$tb.Location = New-Object System.Drawing.Point($x,$y)
$tb.Size = New-Object System.Drawing.Size(160,22)
$tb.BackColor = $colorTextbox
$tb.ForeColor = [System.Drawing.Color]::Gray # Gris car verrouillé
$tb.ReadOnly = $true # Empeche l'écriture
$grp.Controls.Add($tb)
return $tb
}
Add-Label "Police" 20 30; $tbPolice = Add-TB 100 28
Add-Label "Assuré" 20 70; $tbAssure = Add-TB 100 68
Add-Label "Prime" 280 30; $tbPrime = Add-TB 350 28
Add-Label "Date effet" 20 110
$dtEffet = New-Object System.Windows.Forms.DateTimePicker
$dtEffet.Location = New-Object System.Drawing.Point(100,108)
$dtEffet.Format = "Short"
$grp.Controls.Add($dtEffet)
$chkIRDS = New-Object System.Windows.Forms.CheckBox
$chkIRDS.Text = "IRDS / STE"
$chkIRDS.Location = New-Object System.Drawing.Point(280,70)
$chkIRDS.ForeColor = [System.Drawing.Color]::White
$chkIRDS.BackColor = $colorGroup
$grp.Controls.Add($chkIRDS)
# ===============================
# STATUT AUTOMATIQUE
# ===============================
function Get-Statut($effet, $police, $irdsChecked) {
$today = (Get-Date).Date
$longDelay = Use-LongDelay $police $irdsChecked
if ($longDelay) {
if ($today -gt $effet.AddDays(120)) { return "Résilié" }
if ($today -gt $effet.AddDays(110)) { return "Suspendu" }
if ($today -gt $effet.AddDays(90)) { return "Contentieux" }
}
else {
if ($today -gt $effet.AddDays(60)) { return "Résilié" }
if ($today -gt $effet.AddDays(50)) { return "Suspendu" }
if ($today -gt $effet.AddDays(30)) { return "Contentieux" }
}
return "OK"
}
function Get-NombreJoursAvant($effet, $statut, $police, $irdsChecked) {
$today = (Get-Date).Date
$longDelay = Use-LongDelay $police $irdsChecked
if ($longDelay) {
$c = $effet.AddDays(90)
$s = $effet.AddDays(110)
$r = $effet.AddDays(120)
}
else {
$c = $effet.AddDays(30)
$s = $effet.AddDays(50)
$r = $effet.AddDays(60)
}
switch ($statut) {
"OK" { return "Contentieux dans $(($c-$today).Days) jrs" }
"Contentieux" { return "Suspendu dans $(($s-$today).Days) jrs" }
"Suspendu" { return "Résilié dans $(($r-$today).Days) jrs" }
default { return "x" }
}
}
function Get-DossierData {
param(
[datetime]$Effet,
[string]$Police,
[bool]$IRDS
)
# Définition des préfixes spéciaux
$specialPrefixes = @("^555","^504","^501","^ci0555","^ci0554")
$isSpecial = $false
foreach ($prefix in $specialPrefixes) {
if ($Police -match $prefix) { $isSpecial = $true; break }
}
# LOGIQUE DE CALCUL
if ($IRDS -or -not $isSpecial) {
# IRDS coché OU police normale → délai long
$Contentieux = $Effet.AddDays(90)
$Suspension = $Effet.AddDays(110)
$Resiliation = $Effet.AddDays(120)
} else {
# police spéciale → délai court
$Contentieux = $Effet.AddDays(30)
$Suspension = $Effet.AddDays(50)
$Resiliation = $Effet.AddDays(60)
}
return @{
Contentieux = $Contentieux
Suspension = $Suspension
Résiliation = $Resiliation
}
}
# ===============================
# AJOUTER ITEM LISTVIEW
# ===============================
function Add-ListViewItem($Police,$Assure,$Prime,$DateEffet) {
try { $effet = [datetime]$DateEffet }
catch { $effet = (Get-Date).Date }
$irds = $chkIRDS.Checked
# Création de l'item
$item = New-Object System.Windows.Forms.ListViewItem($Police)
$item.SubItems.Add($Assure) | Out-Null
# On ajoute la prime bien formatée dès le départ
$primeStylee = Format-PrimeWalid $tbPrime.Text
$item.SubItems.Add($primeStylee) | Out-Null
$item.SubItems.Add($effet.ToString('dd/MM/yyyy')) | Out-Null
$item.SubItems.Add($(if($irds){"Oui"}else{"Non"})) | Out-Null
# --- AJOUTER LES PLACEHOLDERS pour les colonnes manquantes ---
1..5 | ForEach-Object { $item.SubItems.Add("") | Out-Null }
# Les 5 colonnes : Contentieux, Suspension, Résiliation, Statut, NombreJoursAvant
# Ajouter à la liste
$list.Items.Add($item) | Out-Null
# CALCUL CENTRAL ET MISE À JOUR
Update-ListViewItem $item
Refresh-Status
}
function Update-ListViewItem {
param($item)
# --- S'assurer que toutes les colonnes existent ---
while ($item.SubItems.Count -lt 10) { $item.SubItems.Add("") | Out-Null }
try {
$effet = [datetime]::ParseExact($item.SubItems[3].Text,'dd/MM/yyyy',$null)
} catch {
$effet = (Get-Date).Date
}
$item.SubItems[2].Text = $item.SubItems[2].Text
$police = $item.SubItems[0].Text
$irds = ($item.SubItems[9].Text -eq "Oui")
# --- CALCUL DES DATES ---
$data = Get-DossierData $effet $police $irds
$item.SubItems[4].Text = $data.Contentieux.ToString('dd/MM/yyyy')
$item.SubItems[5].Text = $data.Suspension.ToString('dd/MM/yyyy')
$item.SubItems[6].Text = $data.Résiliation.ToString('dd/MM/yyyy')
# --- CALCUL STATUT ---
$statut = Get-Statut $effet $police $irds
$item.SubItems[7].Text = $statut
# --- CALCUL NOMBRE JOURS RESTANTS ---
$nbJoursAvant = Get-NombreJoursAvant $effet $statut $police $irds
$item.SubItems[8].Text = $nbJoursAvant
# --- COULEUR ---
switch ($statut) {
"Contentieux" { $item.BackColor = [System.Drawing.Color]::FromArgb(255,140,0) }
"Suspendu" { $item.BackColor = [System.Drawing.Color]::FromArgb(255,69,0) }
"Résilié" { $item.BackColor = [System.Drawing.Color]::FromArgb(220,20,60) }
default { $item.BackColor = $colorList }
}
$item.ForeColor = [System.Drawing.Color]::White
}
function Recalculate-SelectedItem {
if ($list.SelectedItems.Count -eq 0) { return }
Update-ListViewItem $list.SelectedItems[0]
Refresh-Status
Save-Data
}
function Generate-StarLetterPdf($selectedItem, $savePath) {
# Couleurs Officielles STAR
$starGreenFonce = [System.Drawing.Color]::FromArgb(0, 104, 87) # Vert Foncé (Sérieux)
$starGreenVif = [System.Drawing.Color]::FromArgb(140, 198, 63) # Vert Anis (Énergie/Étoile)
# Récupération des données
$police = $selectedItem.Text
$assure = $selectedItem.SubItems[1].Text
$prime = $selectedItem.SubItems[2].Text
$dateSusp = $selectedItem.SubItems[5].Text
$dateResil = $selectedItem.SubItems[6].Text
$pd = New-Object System.Drawing.Printing.PrintDocument
$pd.PrinterSettings.PrinterName = "Microsoft Print to PDF"
$pd.PrinterSettings.PrintToFile = $true
$pd.PrinterSettings.PrintFileName = $savePath
$pd.Add_PrintPage({
param($sender, $e)
$g = $e.Graphics
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::AntiAlias
# --- POLICES ---
$fLogo = New-Object System.Drawing.Font("Segoe UI", 26, [System.Drawing.FontStyle]::Bold)
$fSlogan = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Italic)
$fTitle = New-Object System.Drawing.Font("Segoe UI", 14, [System.Drawing.FontStyle]::Bold)
$fHeader = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold)
$fBody = New-Object System.Drawing.Font("Segoe UI", 11)
$fSmall = New-Object System.Drawing.Font("Segoe UI", 8)
# --- BROSSES ET STYLOS ---
$brushFonce = New-Object System.Drawing.SolidBrush($starGreenFonce)
$brushVif = New-Object System.Drawing.SolidBrush($starGreenVif)
$penVif = New-Object System.Drawing.Pen($starGreenVif, 3)
# --- 1. EN-TÊTE DESIGN ---
# Bandeau décoratif en haut
$g.FillRectangle($brushFonce, 0, 0, 850, 40)
$g.FillRectangle($brushVif, 0, 40, 850, 5)
# Logo Textuel (En attendant un vrai logo image)
$g.DrawString("STAR", $fLogo, $brushFonce, 50, 60)
$g.DrawString("ASSURANCES", $fHeader, $brushFonce, 50, 105)
$g.DrawString("AGENCE BEN BRAHEM 210 - KHAZNADAR", $fSmall, [System.Drawing.Brushes]::Gray, 50, 125)
# Bloc Date et Destinataire
$g.DrawString("Tunis, le $(Get-Date -Format 'dd/MM/yyyy')", $fBody, [System.Drawing.Brushes]::Black, 500, 140)
# Encadré Destinataire
$rectDest = New-Object System.Drawing.Rectangle(480, 180, 300, 100)
$g.DrawRectangle($penVif, $rectDest)
$g.DrawString("À l'attention de :", $fSmall, [System.Drawing.Brushes]::Gray, 490, 190)
$g.DrawString($assure.ToUpper(), $fTitle, [System.Drawing.Brushes]::Black, 490, 215)
# --- 2. OBJET ---
$y = 320
$g.FillRectangle($brushFonce, 50, $y, 730, 35)
$g.DrawString("OBJET : RAPPEL DE REGLEMENT - POLICE N° $police", $fHeader, [System.Drawing.Brushes]::White, 65, $y + 8)
# --- 3. CORPS DE LA LETTRE ---
$y += 70
$g.DrawString("Monsieur / Madame,", $fBody, [System.Drawing.Brushes]::Black, 50, $y); $y += 40
$corps = "Sauf erreur ou omission de notre part, le paiement de votre prime d'assurance citée en référence ne nous est pas parvenu. Nous vous rappelons que conformément aux conditions générales de votre contrat, le règlement doit être effectué à l'échéance convenue."
$rectCorps = New-Object System.Drawing.RectangleF(50, $y, 730, 100)
$g.DrawString($corps, $fBody, [System.Drawing.Brushes]::Black, $rectCorps); $y += 90
# --- 4. TABLEAU RÉCAPITULATIF (Très pro) ---
# Fond du titre de tableau
$g.FillRectangle($brushVif, 50, $y, 730, 30)
$g.DrawString("SITUATION DE VOTRE COMPTE", $fHeader, [System.Drawing.Brushes]::White, 300, $y + 7)
$y += 30
# Détails
$g.DrawRectangle([System.Drawing.Pens]::LightGray, 50, $y, 730, 120)
$g.DrawString("MONTANT TOTAL À RÉGULARISER :", $fBody, [System.Drawing.Brushes]::Black, 70, $y + 20)
$g.DrawString($prime + " DT", $fTitle, $brushFonce, 500, $y + 18)
$g.DrawLine([System.Drawing.Pens]::LightGray, 70, $y + 55, 710, $y + 55)
$g.DrawString("DATE DE SUSPENSION DES GARANTIES :", $fBody, [System.Drawing.Brushes]::Black, 70, $y + 65)
$g.DrawString($dateSusp, $fHeader, [System.Drawing.Brushes]::DarkRed, 500, $y + 65)
$g.DrawString("DATE DE RÉSILIATION DÉFINITIVE :", $fBody, [System.Drawing.Brushes]::Black, 70, $y + 90)
$g.DrawString($dateResil, $fHeader, [System.Drawing.Brushes]::Red, 500, $y + 90)
$y += 160
# --- 5. CONCLUSION ET SIGNATURE ---
$cloture = "Nous vous invitons à régulariser cette situation par chèque, virement ou espèces auprès de notre agence dans les plus brefs délais afin de maintenir la validité de vos garanties."
$g.DrawString($cloture, $fBody, [System.Drawing.Brushes]::Black, (New-Object System.Drawing.RectangleF(50, $y, 730, 60))); $y += 80
$g.DrawString("Veuillez agréer, Monsieur / Madame, l'expression de nos salutations distinguées.", $fBody, [System.Drawing.Brushes]::Black, 50, $y); $y += 60
# Bloc Signature
$g.DrawString("La Direction de l'Agence", $fHeader, $brushFonce, 500, $y)
# Simulation d'un espace pour cachet
$g.DrawEllipse([System.Drawing.Pens]::LightGray, 550, $y + 30, 120, 80)
$g.DrawString("Walid Toumi", $fHeader, [System.Drawing.Brushes]::Black, 500, $y + 120)
# --- 6. PIED DE PAGE ---
$g.FillRectangle($brushFonce, 0, 1100, 850, 60)
$infoFooter = "Société Tunisienne d'Assurances et de Réassurances - Agence Ben Brahem`nSiège social : Avenue de Paris, Tunis - Document généré par le système de recouvrement le $(Get-Date)"
$g.DrawString($infoFooter, $fSmall, [System.Drawing.Brushes]::White, 150, 1110)
})
$pd.Print()
}
$chkIRDS.Add_CheckedChanged({
if ($list.SelectedItems.Count -eq 0) { return }
$item = $list.SelectedItems[0]
# Assure que toutes les colonnes existent
while ($item.SubItems.Count -lt 10) { $item.SubItems.Add("") | Out-Null }
# Met à jour IRDS
$item.SubItems[9].Text = if ($chkIRDS.Checked) { "Oui" } else { "Non" }
# Recalculer dates, statut et couleur
Update-ListViewItem $item
Refresh-Status
Save-Data
})
# ===============================
# SAUVEGARDE CSV
# ===============================
$allItems = New-Object System.Collections.ArrayList
function Refresh-AllItems {
$allItems.Clear()
foreach ($i in $list.Items) { [void]$allItems.Add($i.Clone()) }
}
function Save-Data {
$data = foreach ($i in $list.Items) {
[PSCustomObject]@{
Police = $i.SubItems[0].Text
Assuré = $i.SubItems[1].Text
Prime = $i.SubItems[2].Text
DateEffet = $i.SubItems[3].Text
Contentieux = $i.SubItems[4].Text
Suspension = $i.SubItems[5].Text
Résiliation = $i.SubItems[6].Text
Statut = $i.SubItems[7].Text
NombreJoursAvant = $i.SubItems[8].Text
}
}
$data | Export-Csv $dataFile -NoTypeInformation -Force -Encoding UTF8
Refresh-AllItems
}
# ===============================
# FACTORISATION BOUTONS
# ===============================
function New-Button($text,$x,$y,$action) {
$btn = New-Object System.Windows.Forms.Button
$btn.Text = $text
$btn.Location = New-Object System.Drawing.Point($x,$y)
$btn.Size = New-Object System.Drawing.Size(140,30)
$btn.BackColor = $colorButton
$btn.ForeColor = [System.Drawing.Color]::White
$btn.Add_Click($action)
$grp.Controls.Add($btn)
return $btn
}
# ===============================
# BOUTONS
# ===============================
New-Button "Ajouter dossier" 600 40 {
Add-ListViewItem $tbPolice.Text $tbAssure.Text $tbPrime.Text $dtEffet.Value
Refresh-Status
Save-Data
}
New-Button "Supprimer (Chef)" 760 40 {
$selectedItems = $list.SelectedItems
$count = $selectedItems.Count
if ($count -eq 0) {
[System.Windows.Forms.MessageBox]::Show("Veuillez sélectionner un ou plusieurs dossiers à supprimer.", "Aucune sélection", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
return
}
# 1. Construction du message récapitulatif
$msg = "Êtes-vous sûr de vouloir supprimer ces $count dossier(s) ?`n`n"
foreach ($item in $selectedItems) {
$msg += "• Police : $($item.Text) - Assuré : $($item.SubItems[1].Text)`n"
}
# 2. Affichage de la boîte de dialogue de confirmation
$result = [System.Windows.Forms.MessageBox]::Show($msg, "Confirmation de suppression", [System.Windows.Forms.MessageBoxButtons]::YesNo, [System.Windows.Forms.MessageBoxIcon]::Question)
if ($result -eq [System.Windows.Forms.DialogResult]::Yes) {
# 3. Suppression des éléments (on boucle à l'envers pour ne pas casser les index)
for ($i = $count - 1; $i -ge 0; $i--) {
$list.Items.Remove($selectedItems[$i])
}
# 4. Mise à jour et sauvegarde
Refresh-Status
Save-Data
}
}
#############################################
# Ajout du bouton Timeline
$btnTimeline = New-Object System.Windows.Forms.Button
$btnTimeline.Text = "⏱ Timeline"
$btnTimeline.Location = New-Object System.Drawing.Point(600, 120) # Emplacement du bouton
$btnTimeline.Size = New-Object System.Drawing.Size(140,30) # Taille du bouton
$btnTimeline.BackColor = $colorButton # Couleur de fond
$btnTimeline.ForeColor = [System.Drawing.Color]::White # Couleur du texte
$grp.Controls.Add($btnTimeline) # Ajoute le bouton au groupe de boutons
$btnTimeline.Add_Click({
if ($list.SelectedItems.Count -eq 0) { return }
$sel = $list.SelectedItems[0]
try {
$dEffet = [datetime]::ParseExact($sel.SubItems[3].Text, "dd/MM/yyyy", $null)
$dCont = [datetime]::ParseExact($sel.SubItems[4].Text, "dd/MM/yyyy", $null)
$dSusp = [datetime]::ParseExact($sel.SubItems[5].Text, "dd/MM/yyyy", $null)
$dResil = [datetime]::ParseExact($sel.SubItems[6].Text, "dd/MM/yyyy", $null)
$clientNom = $sel.SubItems[1].Text
$policeNum = $sel.Text
} catch {
[System.Windows.Forms.MessageBox]::Show("Erreur de format de date.")
return
}
$timelineForm = New-Object System.Windows.Forms.Form
$timelineForm.Text = "STAR - Analyse d'échéance : $clientNom"
$timelineForm.Size = New-Object System.Drawing.Size(950, 450)
$timelineForm.StartPosition = "CenterParent"
$timelineForm.BackColor = [System.Drawing.Color]::FromArgb(25, 28, 32)
$panelTimeline = New-Object System.Windows.Forms.Panel
$panelTimeline.Dock = "Fill"
$timelineForm.Controls.Add($panelTimeline)
$panelTimeline.Add_Paint({
param($sender,$e)
$g = $e.Graphics
$g.SmoothingMode = [System.Drawing.Drawing2D.SmoothingMode]::HighQuality
# --- RESSOURCES ---
$starGreenVif = [System.Drawing.Color]::FromArgb(140, 198, 63)
$fHeader = New-Object System.Drawing.Font("Segoe UI", 12, [System.Drawing.FontStyle]::Bold)
$fBold = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
$fStatus = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold)
$today = (Get-Date).Date
[float]$startX = 100
[float]$endX = $sender.Width - 100
[float]$yBase = $sender.Height / 2
[double]$totalJours = ($dResil - $dEffet).TotalDays
if ($totalJours -le 0) { $totalJours = 1 }
# --- CALCUL DE LA PROCHAINE ÉTAPE ---
$etapes = @(
@{Name="EFFET"; Date=$dEffet; Color=[System.Drawing.Color]::SkyBlue; Pos="Up"},
@{Name="CONTENTIEUX"; Date=$dCont; Color=$starGreenVif; Pos="Down"},
@{Name="SUSPENSION"; Date=$dSusp; Color=[System.Drawing.Color]::Orange; Pos="Up"},
@{Name="RÉSILIATION"; Date=$dResil; Color=[System.Drawing.Color]::Crimson; Pos="Down"}
)
$prochaineEtape = $null
foreach($et in ($etapes | Sort-Object Date)) {
if ($et.Date -gt $today) {
$prochaineEtape = $et
break
}
}
# --- DESSIN DU RAIL ET PROGRESSION ---
$penRail = New-Object System.Drawing.Pen([System.Drawing.Color]::FromArgb(70, 70, 70), 4)
$g.DrawLine($penRail, $startX, $yBase, $endX, $yBase)
[float]$ratioToday = ($today - $dEffet).TotalDays / $totalJours
if ($ratioToday -gt 1) { [float]$ratioToday = 1 }
if ($ratioToday -lt 0) { [float]$ratioToday = 0 }
[float]$todayX = $startX + ($ratioToday * ($endX - $startX))
$g.DrawLine((New-Object System.Drawing.Pen($starGreenVif, 6)), $startX, $yBase, $todayX, $yBase)
# --- DESSIN DES ÉTAPES ---
foreach ($et in $etapes) {
[float]$ratioX = ($et.Date - $dEffet).TotalDays / $totalJours
[float]$posX = $startX + ($ratioX * ($endX - $startX))
[float]$boxW = 115; [float]$boxH = 40
[float]$offsetY = if ($et.Pos -eq "Up") { -80 } else { 40 }
[float]$boxY = $yBase + $offsetY
if ($et.Pos -eq "Up") { $boxY -= $boxH }
$isPassed = $today -ge $et.Date
$bColor = if ($isPassed) { $et.Color } else { [System.Drawing.Color]::FromArgb(60, 60, 60) }
$g.DrawLine([System.Drawing.Pens]::Gray, $posX, $yBase, $posX, $boxY + ($boxH / 2))
$g.FillRectangle((New-Object System.Drawing.SolidBrush($bColor)), ($posX - $boxW/2), $boxY, $boxW, $boxH)
$g.DrawRectangle([System.Drawing.Pens]::White, ($posX - $boxW/2), $boxY, $boxW, $boxH)
$txt = "$($et.Name)`n$($et.Date.ToString('dd/MM/yyyy'))"
$sf = New-Object System.Drawing.StringFormat; $sf.Alignment = 1; $sf.LineAlignment = 1
$g.DrawString($txt, $fBold, [System.Drawing.Brushes]::White, (New-Object System.Drawing.RectangleF(($posX - $boxW/2), $boxY, $boxW, $boxH)), $sf)
$g.FillEllipse((New-Object System.Drawing.SolidBrush($bColor)), ($posX - 8), ($yBase - 8), 16, 16)
}
# --- BLOC "AUJOURD'HUI" AMÉLIORÉ (Compte à rebours) ---
$labelX = $sender.Width / 2
$g.DrawString("★ AUJOURD'HUI : $($today.ToString('dd/MM/yyyy')) ★", $fStatus, [System.Drawing.Brushes]::White, $labelX - 120, 30)
if ($prochaineEtape) {
$joursRestants = ($prochaineEtape.Date - $today).Days
# bug: $infoNext = "PROCHAINE ÉTAPE : $($prochaineEtape.Name) DANS $joursRestants JOURS"
$infoNext = "PROCHAINE ÉTAPE DANS: $joursRestants JOURS"
$g.DrawString($infoNext, $fBold, (New-Object System.Drawing.SolidBrush($prochaineEtape.Color)), $labelX - 120, 55)
} else {
$g.DrawString("PROCÉDURE TERMINÉE (RÉSILIÉ)", $fBold, [System.Drawing.Brushes]::Crimson, $labelX - 120, 55)
}
# Flèche vers le rail
$penArrow = New-Object System.Drawing.Pen([System.Drawing.Color]::DeepSkyBlue, 2)
$penArrow.DashStyle = 2; $penArrow.EndCap = 16
$g.DrawLine($penArrow, $labelX, 80, $todayX, $yBase - 10)
# Pied de page
$g.DrawString("STAR ASSURANCES - CLIENT : $clientNom (Police: $policeNum)", $fHeader, (New-Object System.Drawing.SolidBrush($starGreenVif)), 40, $sender.Height - 60)
})
$timelineForm.ShowDialog()
})
###########################################
# Création du bouton Charger
$btnLoadFile = New-Object System.Windows.Forms.Button
$btnLoadFile.Text = "📂 Charger CSV"
$btnLoadFile.Location = New-Object System.Drawing.Point(920, 80)
$btnLoadFile.Size = New-Object System.Drawing.Size(140, 30)
$btnLoadFile.BackColor = $colorButton
$btnLoadFile.ForeColor = [System.Drawing.Color]::White
$grp.Controls.Add($btnLoadFile)
$btnPdf = New-Object System.Windows.Forms.Button
$btnPdf.Text = "📄 Export PDF"
$btnPdf.Location = New-Object System.Drawing.Point(920,40)
$btnPdf.Size = New-Object System.Drawing.Size(140,30)
$btnPdf.BackColor = $colorButton
$btnPdf.ForeColor = 'White'
$grp.Controls.Add($btnPdf)
$btnPdf.Add_Click({
if ($list.SelectedItems.Count -eq 0) {
[System.Windows.Forms.MessageBox]::Show("Veuillez sélectionner un client.", "Sélection requise")
return
}
$sel = $list.SelectedItems[0]
$save = New-Object System.Windows.Forms.SaveFileDialog
$save.Filter = "Document PDF (*.pdf)|*.pdf"
$save.FileName = "RAPPEL DE REGELEMENT_$($sel.Text).pdf"
if ($save.ShowDialog() -eq "OK") {
try {
# Appel de la fonction de génération PDF
Generate-StarLetterPdf -selectedItem $sel -savePath $save.FileName
[System.Windows.Forms.MessageBox]::Show("Le PDF a été généré.", "Succès")
Start-Process $save.FileName # Ouvre le PDF automatiquement
}
catch {
[System.Windows.Forms.MessageBox]::Show("Erreur lors de la génération : " + $_.Exception.Message)
}
}
})
# =========================================================
# BOUTON EXPORT EXCEL (À mettre avec les autres boutons)
# =========================================================
$btnExportExcel = New-Object System.Windows.Forms.Button
$btnExportExcel.Text = "📊 Export EXCEL"
# Positionné à droite du bouton PDF (X=920, Y=120 pour ne pas chevaucher)
$btnExportExcel.Location = New-Object System.Drawing.Point(920, 120)
$btnExportExcel.Size = New-Object System.Drawing.Size(140, 30)
$btnExportExcel.BackColor = $colorButton
$btnExportExcel.ForeColor = [System.Drawing.Color]::White
$btnExportExcel.FlatStyle = "Flat"
$btnExportExcel.Font = New-Object System.Drawing.Font("Segoe UI", 9, [System.Drawing.FontStyle]::Bold)
$btnExportExcel.Cursor = "Hand"
$grp.Controls.Add($btnExportExcel)
$btnExportExcel.Add_Click({
if ($list.Items.Count -eq 0) {
[System.Windows.Forms.MessageBox]::Show("La liste est vide, rien à exporter.", "Info")
return
}
try {
# Initialisation Excel
$excel = New-Object -ComObject Excel.Application
$excel.Visible = $true
$wb = $excel.Workbooks.Add()
$ws = $wb.Worksheets.Item(1)
$ws.Name = "Recouvrement $(Get-Date -Format "MM YYYY")"
# Titres des colonnes
$headers = @("Police", "Assuré", "Prime", "Date Effet", "Contentieux", "Suspension", "Résiliation", "Statut", "Délai", "IRDS")
for ($i = 0; $i -lt $headers.Count; $i++) {
$ws.Cells.Item(1, $i + 1) = $headers[$i]
$ws.Cells.Item(1, $i + 1).Font.Bold = $true
$ws.Cells.Item(1, $i + 1).Interior.Color = [System.Drawing.ColorTranslator]::ToOle([System.Drawing.Color]::LightGray)
}
# Remplissage des lignes
$r = 2
foreach ($item in $list.Items) {
for ($c = 0; $c -lt $item.SubItems.Count; $c++) {
$cellValue = $item.SubItems[$c].Text
$ws.Cells.Item($r, $c + 1) = $cellValue
# Couleur spécifique pour la colonne Statut (Index 7)
if ($c -eq 7) {
$color = switch($cellValue) {
"OK" { [System.Drawing.Color]::Green }
"Contentieux" { [System.Drawing.Color]::Orange }
"Suspendu" { [System.Drawing.Color]::DarkOrange }
"Résilié" { [System.Drawing.Color]::Red }
Default { [System.Drawing.Color]::Black }
}
$ws.Cells.Item($r, $c + 1).Font.Color = [System.Drawing.ColorTranslator]::ToOle($color)
}
}
$r++
}
# Mise en forme automatique
$ws.Columns.AutoFit() | Out-Null
[System.Windows.Forms.MessageBox]::Show("Le rapport Excel a été généré avec succès !", "Succès Export")
}
catch {
[System.Windows.Forms.MessageBox]::Show("Erreur : Assurez-vous qu'Excel est installé.`nDétail : $($_.Exception.Message)", "Erreur")
}
})
$btnEdit = New-Object System.Windows.Forms.Button
$btnEdit.Text = "Modifier dossier"
$btnEdit.Location = New-Object System.Drawing.Point(600,80)
$btnEdit.Size = New-Object System.Drawing.Size(140,30)
$btnEdit.BackColor = $colorButton
$btnEdit.ForeColor = [System.Drawing.Color]::White
$btnEdit.Enabled = $false
$grp.Controls.Add($btnEdit)
$btnEdit.Add_Click({
# 1. Vérification de sélection
if ($list.SelectedItems.Count -eq 0) {
[System.Windows.Forms.MessageBox]::Show("Veuillez sélectionner un dossier dans la liste avant de modifier.", "Sélection manquante", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning)
return
}
$sel = $list.SelectedItems[0]
# 2. Transfert des données des TextBoxes vers la ligne sélectionnée
$sel.Text = $tbPolice.Text # Colonne 0 (Police)
$sel.SubItems[1].Text = $tbAssure.Text # Colonne 1 (Assuré)
# Nettoyage de la Prime (on garde uniquement chiffres, virgule et point)
$sel.SubItems[2].Text = Format-PrimeWalid $tbPrime.Text
$sel.SubItems[3].Text = $dtEffet.Value.ToString('dd/MM/yyyy') # Colonne 3 (Date Effet)
# Mise à jour IRDS (Colonne 9)
$sel.SubItems[9].Text = if ($chkIRDS.Checked) { "Oui" } else { "Non" }
# 3. Recalcul automatique (Statut, Dates théoriques et Couleurs)
# On appelle la fonction de la Partie 3 pour recalculer selon la date du jour (17/01/2026)
Update-ListViewItem $sel
# 4. Rafraîchissement des visuels et sauvegarde
Refresh-Status # Met à jour la barre bleue en bas (Totaux)
Save-Data # Enregistre les changements dans le CSV
# COMMANDE IMPORTANTE : On force le Timeline à se redessiner
# pour refléter les nouvelles dates du dossier
if ($null -ne $panelTimeline) { $panelTimeline.Invalidate() }
[System.Windows.Forms.MessageBox]::Show("Dossier $($tbPolice.Text) mis à jour avec succès !", "Mise à jour", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information)
})
# ===============================
# BOUTON DETAIL
# ===============================
$btnDetail = New-Object System.Windows.Forms.Button
$btnDetail.Text = "Détail"
$btnDetail.Location = New-Object System.Drawing.Point(760,80)
$btnDetail.Size = New-Object System.Drawing.Size(140,30)
$btnDetail.BackColor = $colorButton
$btnDetail.ForeColor = [System.Drawing.Color]::White
$btnDetail.Enabled = $True
$grp.Controls.Add($btnDetail)
# ===============================
# GROUPE RECHERCHE DYNAMIQUE
# ===============================
$grpSearch = New-Object System.Windows.Forms.GroupBox
$grpSearch.Text = "Recherche avancée"
$grpSearch.Location = New-Object System.Drawing.Point(20,520)
$grpSearch.Size = New-Object System.Drawing.Size(1080,70)
$grpSearch.ForeColor = $neonGreen
$grpSearch.BackColor = $colorGroup
$grpSearch.Anchor = "Bottom,Left,Right"
$form.Controls.Add($grpSearch)
$lblField = New-Object System.Windows.Forms.Label
$lblField.Text = "Champ :"
$lblField.Location = New-Object System.Drawing.Point(20,30)
$lblField.AutoSize = $true
$lblField.ForeColor = [System.Drawing.Color]::White
$grpSearch.Controls.Add($lblField)
$cbField = New-Object System.Windows.Forms.ComboBox
$cbField.Location = New-Object System.Drawing.Point(80,26)
$cbField.Size = New-Object System.Drawing.Size(150,22)
$cbField.DropDownStyle = "DropDownList"
$columns | ForEach-Object { $cbField.Items.Add($_) | Out-Null }
$cbField.SelectedIndex = 0
$grpSearch.Controls.Add($cbField)
$tbSearch = New-Object System.Windows.Forms.TextBox
$tbSearch.Location = New-Object System.Drawing.Point(250,26)
$tbSearch.Size = New-Object System.Drawing.Size(220,22)
$tbSearch.BackColor = $colorTextbox
$tbSearch.ForeColor = [System.Drawing.Color]::White
$grpSearch.Controls.Add($tbSearch)
$btnReset = New-Object System.Windows.Forms.Button
$btnReset.Text = "Réinitialiser"
$btnReset.Location = New-Object System.Drawing.Point(480,24)
$btnReset.Size = New-Object System.Drawing.Size(110,26)
$btnReset.BackColor = $colorButton
$btnReset.ForeColor = [System.Drawing.Color]::White
$grpSearch.Controls.Add($btnReset)
$form.Add_Shown({ Refresh-AllItems })
$tbSearch.Add_TextChanged({
$fieldIndex = $cbField.SelectedIndex
$searchText = $tbSearch.Text.ToLower()
$list.BeginUpdate() # STOPE LE DESSIN (Évite le clignotement)
$list.Items.Clear()
# Filtrage efficace
$results = $allItems | Where-Object {
$_.SubItems[$fieldIndex].Text.ToLower().Contains($searchText)
}
if ($results) {
foreach ($res in $results) {
$list.Items.Add($res.Clone()) | Out-Null
}
}
$list.EndUpdate() # RELANCE LE DESSIN D'UN COUP
})
$btnReset.Add_Click({
$tbSearch.Clear()
$list.Items.Clear()
foreach ($i in $allItems) { $list.Items.Add($i.Clone()) | Out-Null }
})
# ===============================
# CHARGEMENT DES DONNÉES EXISTANTES
# ===============================
if (Test-Path $dataFile) {
$list.BeginUpdate()
$list.Items.Clear()
$csvData = Import-Csv $dataFile
$itemsToAdd = @()
foreach ($row in $csvData) {
# --- Effet
try { $effet = [datetime]::ParseExact($row.DateEffet,'dd/MM/yyyy',$null) }
catch { $effet = (Get-Date).Date }
# --- IRDS
$irds = [bool]$row.IRDS
# --- Statut
$statut = $row.Statut
if ([string]::IsNullOrWhiteSpace($statut)) { $statut = Get-Statut $effet $row.Police $irds }
# --- Dates de calcul
$c = $effet.AddDays(30)
$s = $effet.AddDays(50)
$r = $effet.AddDays(60)
# --- Nombre de jours avant étape suivante
$nombre = $row.NombreJoursAvant
if ([string]::IsNullOrWhiteSpace($nombre)) {
$nombre = Get-NombreJoursAvant $effet $statut $row.Police $irds
}
# --- Création de l'item
$item = New-Object System.Windows.Forms.ListViewItem($row.Police)
$item.SubItems.Add($row.Assuré) | Out-Null
$item.SubItems.Add($row.Prime) | Out-Null
$item.SubItems.Add($effet.ToString('dd/MM/yyyy')) | Out-Null
$item.SubItems.Add($c.ToString('dd/MM/yyyy')) | Out-Null
$item.SubItems.Add($s.ToString('dd/MM/yyyy')) | Out-Null
$item.SubItems.Add($r.ToString('dd/MM/yyyy')) | Out-Null
$item.SubItems.Add($statut) | Out-Null
$item.SubItems.Add($nombre) | Out-Null
# --- Couleur selon statut
switch ($statut) {
"Contentieux" { $item.BackColor = [System.Drawing.Color]::FromArgb(255,140,0) }
"Suspendu" { $item.BackColor = [System.Drawing.Color]::FromArgb(255,69,0) }
"Résilié" { $item.BackColor = [System.Drawing.Color]::FromArgb(220,20,60) }
default { $item.BackColor = $colorList }
}
$item.ForeColor = [System.Drawing.Color]::White
$itemsToAdd += $item
}
# --- Ajouter tous les items en une seule opération
$list.Items.AddRange($itemsToAdd)
$list.EndUpdate()
# --- Rafraîchir statuts et afficher alertes
Refresh-Status
}
# ===============================
# MISE À JOUR AUTOMATIQUE AU DÉMARRAGE
# ===============================
$list.BeginUpdate()
foreach ($i in $list.Items) {
Update-ListViewItem $i
}
$list.EndUpdate()
Refresh-Status
# ===============================
# ACTIVATION DES BOUTONS SUR SÉLECTION
# ===============================
$list.add_SelectedIndexChanged({
if ($list.SelectedItems.Count -gt 0) {
$sel = $list.SelectedItems[0]
# On active le bouton et les champs
$btnEdit.Enabled = $true
$tbPolice.ReadOnly = $false
$tbAssure.ReadOnly = $false
$tbPrime.ReadOnly = $false
# On remplit les champs
$tbPolice.Text = $sel.Text
$tbAssure.Text = $sel.SubItems[1].Text
$tbPrime.Text = $sel.SubItems[2].Text
try {
$dtEffet.Value = [datetime]::ParseExact($sel.SubItems[3].Text, 'dd/MM/yyyy', $null)
} catch {
$dtEffet.Value = (Get-Date)
}
$chkIRDS.Checked = ($sel.SubItems[9].Text -eq "Oui")
# On redessine le Timeline pour le nouveau dossier choisi
if ($null -ne $panelTimeline) { $panelTimeline.Invalidate() }
} else {
$btnEdit.Enabled = $true
}
})
# ===============================
# ACTION DU BOUTON DETAIL
# ===============================
$btnDetail.Add_Click({
if ($list.SelectedItems.Count -gt 0) {
$policeId = $list.SelectedItems[0].Text
# Parcourir tous les fichiers CSV du dossier
$clientFiles = Get-ChildItem "C:\Users\walid\Desktop\sqi\detail" -Filter *.csv
$clientData = $null
foreach ($file in $clientFiles) {
$data = Import-Csv $file.FullName
$found = $data | Where-Object { $_.Contrat -eq $policeId -or $_.Police -eq $policeId }
if ($found) { $clientData = $found; break }
}
if ($clientData) {
# Fenêtre dashboard futuriste
$detailForm = New-Object System.Windows.Forms.Form
$detailForm.Text = "📊 Dashboard Client $policeId"
$detailForm.Size = New-Object System.Drawing.Size(700,500)
$detailForm.StartPosition = "CenterParent"
$detailForm.Font = New-Object System.Drawing.Font("Segoe UI", 11)
# Dégradé de fond
$detailForm.Add_Paint({
$brush = New-Object System.Drawing.Drawing2D.LinearGradientBrush(
$detailForm.ClientRectangle,
[System.Drawing.Color]::FromArgb(15,15,25), # haut sombre
[System.Drawing.Color]::MediumPurple, # bas violet futuriste
[System.Drawing.Drawing2D.LinearGradientMode]::Vertical
)
$_.Graphics.FillRectangle($brush, $detailForm.ClientRectangle)
$brush.Dispose()
})
# Titre stylisé
$lblTitle = New-Object System.Windows.Forms.Label
$lblTitle.Text = "Données Client"
$lblTitle.Font = New-Object System.Drawing.Font("Segoe UI Semibold", 20, [System.Drawing.FontStyle]::Bold)
$lblTitle.ForeColor = [System.Drawing.Color]::White
$lblTitle.BackColor = [System.Drawing.Color]::Transparent
$lblTitle.Location = New-Object System.Drawing.Point(20,20)
$lblTitle.AutoSize = $true
$detailForm.Controls.Add($lblTitle)
# Panneau défilant
$panel = New-Object System.Windows.Forms.Panel
$panel.Location = New-Object System.Drawing.Point(20,70)
$panel.Size = New-Object System.Drawing.Size(640,340)
$panel.AutoScroll = $true
$panel.BackColor = [System.Drawing.Color]::Transparent
$detailForm.Controls.Add($panel)
# Affichage automatique des colonnes
$y = 10
foreach ($prop in $clientData.PSObject.Properties) {
if ($null -ne $prop.Value -and $prop.Value -ne "") {
$card = New-Object System.Windows.Forms.Panel
$card.Location = New-Object System.Drawing.Point(10,$y)
$card.Size = New-Object System.Drawing.Size(600,70)
$card.BackColor = [System.Drawing.Color]::FromArgb(35,35,55)
$card.BorderStyle = "FixedSingle"
# Nom du champ
$lblName = New-Object System.Windows.Forms.Label
$lblName.Text = $prop.Name
$lblName.Location = New-Object System.Drawing.Point(15,10)
$lblName.AutoSize = $true
$lblName.ForeColor = [System.Drawing.Color]::DeepSkyBlue
$lblName.Font = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Bold)
$card.Controls.Add($lblName)
# Valeur futuriste (TextBox stylisée)
$tbValue = New-Object System.Windows.Forms.TextBox
$tbValue.Text = $prop.Value
$tbValue.Location = New-Object System.Drawing.Point(15,35)
$tbValue.Size = New-Object System.Drawing.Size(560,28)
$tbValue.ReadOnly = $true
$tbValue.BorderStyle = "FixedSingle"
$tbValue.BackColor = [System.Drawing.Color]::FromArgb(25,25,40) # fond sombre
$tbValue.ForeColor = $neonGreen # texte vert néon
$tbValue.Font = New-Object System.Drawing.Font("Consolas", 11, [System.Drawing.FontStyle]::Bold)
$tbValue.TextAlign = "Center" # centré
$card.Controls.Add($tbValue)
$panel.Controls.Add($card)
$y += 80
}
}
# Bouton Fermer stylisé
$btnClose = New-Object System.Windows.Forms.Button
$btnClose.Text = "✖ Fermer"
$btnClose.Location = New-Object System.Drawing.Point(540,420)
$btnClose.Size = New-Object System.Drawing.Size(100,30)
$btnClose.BackColor = [System.Drawing.Color]::FromArgb(90,90,120)
$btnClose.ForeColor = [System.Drawing.Color]::White
$btnClose.FlatStyle = "Flat"
$btnClose.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold)
$btnClose.Add_Click({ $detailForm.Close() })
$detailForm.Controls.Add($btnClose)
$detailForm.ShowDialog($form)
}
else {
function Show-CustomError($policeId) {
$errorForm = New-Object System.Windows.Forms.Form
$errorForm.Text = "⚠️ Erreur"
$errorForm.Size = New-Object System.Drawing.Size(400,200)
$errorForm.StartPosition = "CenterParent"
$errorForm.BackColor = [System.Drawing.Color]::FromArgb(25,25,40)
$errorForm.Font = New-Object System.Drawing.Font("Segoe UI", 11)
# Dégradé de fond
$errorForm.Add_Paint({
$brush = New-Object System.Drawing.Drawing2D.LinearGradientBrush(
$errorForm.ClientRectangle,
[System.Drawing.Color]::FromArgb(30,30,50), # haut sombre
[System.Drawing.Color]::Crimson, # bas rouge vif
[System.Drawing.Drawing2D.LinearGradientMode]::Vertical
)
$_.Graphics.FillRectangle($brush, $errorForm.ClientRectangle)
$brush.Dispose()
})
# Titre
$lblTitle = New-Object System.Windows.Forms.Label
$lblTitle.Text = "Client introuvable"
$lblTitle.Font = New-Object System.Drawing.Font("Segoe UI Semibold", 14, [System.Drawing.FontStyle]::Bold)
$lblTitle.ForeColor = [System.Drawing.Color]::White
$lblTitle.Location = New-Object System.Drawing.Point(20,20)
$lblTitle.AutoSize = $true
$errorForm.Controls.Add($lblTitle)
# Message explicatif
$lblMsg = New-Object System.Windows.Forms.Label
$lblMsg.Text = "Aucun fichier client n’a été trouvé pour la police $policeId"
$lblMsg.Location = New-Object System.Drawing.Point(20,60)
$lblMsg.Size = New-Object System.Drawing.Size(350,60)
$lblMsg.ForeColor = [System.Drawing.Color]::White
$lblMsg.AutoSize = $false
$errorForm.Controls.Add($lblMsg)
# Bouton OK stylisé
$btnOk = New-Object System.Windows.Forms.Button
$btnOk.Text = "OK"
$btnOk.Location = New-Object System.Drawing.Point(280,120)
$btnOk.Size = New-Object System.Drawing.Size(80,30)
$btnOk.BackColor = [System.Drawing.Color]::FromArgb(70,20,20)
$btnOk.ForeColor = [System.Drawing.Color]::White
$btnOk.FlatStyle = "Flat"
$btnOk.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold)
$btnOk.Add_Click({ $errorForm.Close() })
$errorForm.Controls.Add($btnOk)
$errorForm.ShowDialog()
}
Show-CustomError $policeId
}
}
})
Fade-InForm $form
# ===============================
# AFFICHAGE DE LA FENÊTRE
# ===============================
$form.ShowDialog()