<#
$btnTimeline.Add_Click({
if ($list.SelectedItems.Count -gt 0) {
Show-StarTimeline -SelectedItem $list.SelectedItems
}
})
#>
function Show-StarTimeline {
param(
$selectedItem
)
# On force la récupération de la PREMIÈRE ligne sélectionnée pour éviter l'erreur de tableau
$sel = $list.SelectedItems[0]
try {
# 2. Récupération des données avec nettoyage des espaces
$dEffet = [datetime]::ParseExact($sel.SubItems[3].Text.Trim(), "dd/MM/yyyy", $null)
$dCont = [datetime]::ParseExact($sel.SubItems[4].Text.Trim(), "dd/MM/yyyy", $null)
$dSusp = [datetime]::ParseExact($sel.SubItems[5].Text.Trim(), "dd/MM/yyyy", $null)
$dResil = [datetime]::ParseExact($sel.SubItems[6].Text.Trim(), "dd/MM/yyyy", $null)
$clientNom = $sel.SubItems[1].Text
$policeNum = $sel.Text
} catch {
[System.Windows.Forms.MessageBox]::Show("Erreur : Les dates dans le tableau doivent être au format dd/MM/yyyy (ex: 20/01/2026)")
return
}
# --- CONFIGURATION DE LA FENÊTRE ---
$timelineForm = New-Object System.Windows.Forms.Form
$timelineForm.Text = "STAR - ANALYSE TEMPORELLE 2026"
$timelineForm.Size = New-Object System.Drawing.Size(1000, 500)
$timelineForm.StartPosition = "CenterParent"
$timelineForm.BackColor = [System.Drawing.Color]::FromArgb(5, 5, 10)
$timelineForm.FormBorderStyle = [System.Windows.Forms.FormBorderStyle]::FixedDialog
$timelineForm.MaximizeBox = $false
$script:animProgress = 0.0
$script:glowStep = 0.0
$script:glowDirection = 1
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 15
$picTimeline = New-Object System.Windows.Forms.PictureBox
$picTimeline.Dock = "Fill"
$timelineForm.Controls.Add($picTimeline)
$picTimeline.Add_Paint({
param($sender,$e)
$g = $e.Graphics
$g.SmoothingMode = "AntiAlias"
$g.TextRenderingHint = "ClearTypeGridFit"
$today = (Get-Date).Date
[float]$startX = 100
[float]$endX = $sender.Width - 100
[float]$yBase = $sender.Height / 2 + 20
[double]$totalJours = ($dResil - $dEffet).TotalDays
if ($totalJours -le 0) { $totalJours = 1 }
$etapes = @(
@{Name="EFFET"; Date=$dEffet; Color=[System.Drawing.Color]::Cyan; Pos="Up"},
@{Name="CONTENTIEUX"; Date=$dCont; Color=[System.Drawing.Color]::Lime; Pos="Down"},
@{Name="SUSPENSION"; Date=$dSusp; Color=[System.Drawing.Color]::Yellow; Pos="Up"},
@{Name="RÉSILIATION"; Date=$dResil; Color=[System.Drawing.Color]::OrangeRed; Pos="Down"}
)
$prochaine = $null
foreach($et in ($etapes | Sort-Object Date)) {
if ($et.Date.Date -ge $today) { $prochaine = $et; break }
}
# Animation progression
$easedT = 1 - [Math]::Pow(1 - $script:animProgress, 3)
[float]$ratioTarget = ($today - $dEffet).TotalDays / $totalJours
if ($ratioTarget -lt 0) { $ratioTarget = 0 }
if ($ratioTarget -gt 1) { $ratioTarget = 1 }
[float]$currentX = $startX + (($ratioTarget * $easedT) * ($endX - $startX))
# 1. LIGNE GRADIENT
$rectLine = [System.Drawing.RectangleF]::FromLTRB($startX, $yBase-1, ($currentX + 1), $yBase+1)
if ($rectLine.Width -gt 1) {
$brushL = New-Object System.Drawing.Drawing2D.LinearGradientBrush($rectLine, [System.Drawing.Color]::Cyan, [System.Drawing.Color]::OrangeRed, 0.0)
$g.DrawLine((New-Object System.Drawing.Pen($brushL, 2)), $startX, $yBase, $currentX, $yBase)
}
$g.DrawLine((New-Object System.Drawing.Pen([System.Drawing.Color]::FromArgb(40, 40, 50), 2)), $currentX, $yBase, $endX, $yBase)
# 3 & 5. ÉTAPES (GLASSMORPHISM & FOCUS)
foreach ($et in $etapes) {
[float]$ratioX = ($et.Date - $dEffet).TotalDays / $totalJours
[float]$posX = $startX + ($ratioX * ($endX - $startX))
$isPassed = $today -gt $et.Date.Date
$alphaBox = if ($isPassed) { 30 } else { 180 }
$accent = if ($isPassed) { [System.Drawing.Color]::FromArgb(80, 120, 120, 120) } else { $et.Color }
$textColor = if ($isPassed) { [System.Drawing.Color]::Gray } else { [System.Drawing.Color]::White }
$boxW = 135; $boxH = 50
$boxY = if ($et.Pos -eq "Up") { $yBase - 110 } else { $yBase + 60 }
# Glassmorphism
$g.FillRectangle((New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb($alphaBox, 10, 10, 20))), ($posX - $boxW/2), $boxY, $boxW, $boxH)
$g.DrawRectangle((New-Object System.Drawing.Pen([System.Drawing.Color]::FromArgb(40, 255, 255, 255))), ($posX - $boxW/2), $boxY, $boxW, $boxH)
$barY = if ($et.Pos -eq "Up") { ($boxY + $boxH - 4) } else { $boxY }
$g.FillRectangle((New-Object System.Drawing.SolidBrush($accent)), ($posX - $boxW/2), $barY, $boxW, 4)
$g.DrawString($et.Name, (New-Object System.Drawing.Font("Segoe UI", 8, [System.Drawing.FontStyle]::Bold)), (New-Object System.Drawing.SolidBrush($textColor)), ($posX - $boxW/2 + 8), ($boxY + 10))
$g.DrawString($et.Date.ToString("dd MMM yyyy"), (New-Object System.Drawing.Font("Consolas", 8)), (New-Object System.Drawing.SolidBrush($accent)), ($posX - $boxW/2 + 8), ($boxY + 28))
$lineYEnd = if ($et.Pos -eq "Up") { $boxY + $boxH } else { $boxY }
$g.DrawLine((New-Object System.Drawing.Pen($accent, 1)), $posX, $yBase, $posX, $lineYEnd)
$g.FillEllipse((New-Object System.Drawing.SolidBrush($accent)), ($posX - 6), ($yBase - 6), 12, 12)
}
# --- TRIANGLE ET POINT D'AUJOURD'HUI ---
$pSize = 10 + ($script:glowStep * 20)
$pAlpha = [int](180 * (1.0 - $script:glowStep))
$g.FillEllipse((New-Object System.Drawing.SolidBrush([System.Drawing.Color]::FromArgb($pAlpha, [System.Drawing.Color]::Lime))), ($currentX - $pSize/2), ($yBase - $pSize/2), $pSize, $pSize)
$g.FillEllipse([System.Drawing.Brushes]::White, ($currentX - 4), ($yBase - 4), 8, 8)
$triPts = @(
(New-Object System.Drawing.PointF($currentX, ($yBase - 12))),
(New-Object System.Drawing.PointF(($currentX - 6), ($yBase - 22))),
(New-Object System.Drawing.PointF(($currentX + 6), ($yBase - 22)))
)
$g.FillPolygon([System.Drawing.Brushes]::White, $triPts)
# Dashboard Header
$g.DrawString("STAR - CYCLE TEMPOREL", (New-Object System.Drawing.Font("Segoe UI Light", 16)), [System.Drawing.Brushes]::White, 40, 20)
if ($prochaine) {
$diff = ($prochaine.Date.Date - $today).Days
$txt = if ($diff -eq 0) { "ÉCHÉANCE : AUJOURD'HUI ($($prochaine.Name))" } else { "$($prochaine.Name) DANS $diff JOURS" }
$g.DrawString($txt, (New-Object System.Drawing.Font("Segoe UI", 14, [System.Drawing.FontStyle]::Bold)), (New-Object System.Drawing.SolidBrush($prochaine.Color)), 40, 55)
} else {
$g.DrawString("DOSSIER RÉSILLIÉ / CLÔTURÉ", (New-Object System.Drawing.Font("Segoe UI", 16, [System.Drawing.FontStyle]::Bold)), [System.Drawing.Brushes]::OrangeRed, 40, 55)
$g.DrawString("PROCÉDURE DE RECOUVREMENT TERMINÉE", (New-Object System.Drawing.Font("Segoe UI", 9)), [System.Drawing.Brushes]::Crimson, 43, 85)
}
$g.DrawString("CLIENT : $clientNom | POLICE : $policeNum", (New-Object System.Drawing.Font("Segoe UI", 9)), [System.Drawing.Brushes]::Gray, 40, $sender.Height - 60)
})
$timer.Add_Tick({
if ($script:animProgress -lt 1.0) { $script:animProgress += 0.02 }
$script:glowStep += (0.05 * $script:glowDirection)
if ($script:glowStep -ge 1.0 -or $script:glowStep -le 0.0) { $script:glowDirection *= -1 }
$picTimeline.Invalidate()
})
$timelineForm.Add_Load({ $timer.Start() })
$timelineForm.ShowDialog()
$timer.Dispose()
}