Function Show-Journal {
param(
[string]$police,
[string]$client,
[string]$userName
)
# --- PERSISTANCE INTERNE ---
function Internal-GetJournal {
if (Test-Path $script:journalFile) {
try { return Get-Content $script:journalFile -Encoding utf8 | ConvertFrom-Json }
catch { return New-Object PSObject }
}
return New-Object PSObject
}
function Internal-SaveJournal {
param($data)
try { $data | ConvertTo-Json -Depth 10 | Out-File $script:journalFile -Encoding utf8 }
catch { [System.Windows.Forms.MessageBox]::Show("Erreur sauvegarde JSON") }
}
# Initialisation
$journalData = Internal-GetJournal
if ($null -eq $journalData) { $journalData = New-Object PSObject }
$script:editIndex = -1
$startTime = Get-Date
$script:actionDone = $false
# --- DARK MODE ADAPTATIF 2026 ---
$hour = (Get-Date).Hour
$isNight = ($hour -lt 7 -or $hour -gt 19)
$bgMain = if ($isNight) { [System.Drawing.Color]::FromArgb(5, 5, 10) } else { [System.Drawing.Color]::FromArgb(10, 12, 18) }
$bgHeader = if ($isNight) { [System.Drawing.Color]::FromArgb(15, 15, 30) } else { [System.Drawing.Color]::FromArgb(20, 25, 40) }
# --- FENÊTRE ---
$jouForm = New-Object System.Windows.Forms.Form
$jouForm.Text = "TERMINAL AUDIT v3.0 (2026) - $client"
$jouForm.Size = New-Object System.Drawing.Size(700, 820)
$jouForm.BackColor = $bgMain
$jouForm.StartPosition = "CenterParent"
$jouForm.FormBorderStyle = "FixedDialog"
# --- HEADER HUD ---
$panelHeader = New-Object System.Windows.Forms.Panel
$panelHeader.Size = New-Object System.Drawing.Size(700, 110)
$panelHeader.BackColor = $bgHeader
$jouForm.Controls.Add($panelHeader)
$lblTitle = New-Object System.Windows.Forms.Label
$lblTitle.Text = "AUDIT RECOUVREMENT | $police"
$lblTitle.SetBounds(20, 15, 500, 25)
$lblTitle.ForeColor = [System.Drawing.Color]::Cyan
$lblTitle.Font = New-Object System.Drawing.Font("Segoe UI", 12, [System.Drawing.FontStyle]::Bold)
$panelHeader.Controls.Add($lblTitle)
# --- ALERTE DOSSIER FROID ---
[array]$notesInit = if ($journalData.PSObject.Properties[$police]) { @($journalData."$police") } else { @() }
if ($notesInit.Count -gt 0) {
if ($notesInit[0] -match "\[(\d{2}/\d{2})") {
try {
$lastDate = [DateTime]::ParseExact($matches[1] + "/$((Get-Date).Year)", "dd/MM/yyyy", $null)
if (((Get-Date) - $lastDate).Days -gt 15) {
$lblTitle.ForeColor = [System.Drawing.Color]::OrangeRed
$lblTitle.Text += " [ALERTE: DOSSIER FROID]"
}
} catch {}
}
}
$lblStats = New-Object System.Windows.Forms.Label
$lblStats.SetBounds(22, 45, 600, 20)
$lblStats.ForeColor = [System.Drawing.Color]::FromArgb(150, 150, 180)
$lblStats.Font = New-Object System.Drawing.Font("Consolas", 9, [System.Drawing.FontStyle]::Regular)
$panelHeader.Controls.Add($lblStats)
$txtSearch = New-Object System.Windows.Forms.TextBox
$txtSearch.SetBounds(20, 75, 645, 25)
$txtSearch.BackColor = [System.Drawing.Color]::FromArgb(30, 35, 50)
$txtSearch.ForeColor = [System.Drawing.Color]::White
$txtSearch.BorderStyle = "FixedSingle"
$panelHeader.Controls.Add($txtSearch)
# --- HISTORIQUE ---
$logDisplay = New-Object System.Windows.Forms.RichTextBox
$logDisplay.SetBounds(20, 130, 645, 250)
$logDisplay.BackColor = [System.Drawing.Color]::FromArgb(15, 18, 25)
$logDisplay.ForeColor = [System.Drawing.Color]::White
$logDisplay.Font = New-Object System.Drawing.Font("Consolas", 11, [System.Drawing.FontStyle]::Regular)
$logDisplay.ReadOnly = $true
$logDisplay.BorderStyle = "None"
$jouForm.Controls.Add($logDisplay)
$logDisplay.Add_MouseDown({
if ($_.Button -eq [System.Windows.Forms.MouseButtons]::Right) {
$charIndex = $this.GetCharIndexFromPosition($_.Location)
$this.SelectionStart = $charIndex; $this.SelectionLength = 0
}
})
# --- MISE À JOUR AFFICHAGE ---
function Update-JournalDisplay ($filter = "") {
$logDisplay.ReadOnly = $false; $logDisplay.Clear()
[array]$notes = if ($journalData.PSObject.Properties[$police]) { @($journalData."$police") } else { @() }
$lastDateText = if($notes.Count -gt 0){ $notes[0].Substring(1,11) } else { "AUCUN" }
$lblStats.Text = "ACTIONS: $($notes.Count) | DERNIER CONTACT: $lastDateText"
foreach ($n in $notes) {
if ($filter -eq "" -or $n -like "*$filter*") {
if ($n -match "\[(.*?)\] \[(.*?)\] ⚡ (.*)") {
$logDisplay.SelectionColor = [System.Drawing.Color]::DarkCyan;
$logDisplay.AppendText("[$($matches[1])] ")
$logDisplay.SelectionColor = [System.Drawing.Color]::LimeGreen;
$logDisplay.AppendText("($($matches[2])) ")
$logDisplay.SelectionColor = [System.Drawing.Color]::White;
$logDisplay.AppendText("» $($matches[3])`n")
$logDisplay.SelectionColor = [System.Drawing.Color]::FromArgb(40,45,60);
$logDisplay.AppendText("-" * 80 + "`n")
}
}
}
$logDisplay.ReadOnly = $true
}
# --- CALCULATEUR DE PROMESSE
function Show-PromiseCalc {
$pForm = New-Object System.Windows.Forms.Form
$pForm.Size = New-Object System.Drawing.Size(350, 180)
$pForm.Text = "CALCULATEUR D'ÉCHÉANCE"
$pForm.BackColor = [System.Drawing.Color]::FromArgb(20,20,30)
$pForm.StartPosition = [System.Windows.Forms.FormStartPosition]::CenterParent
$pForm.FormBorderStyle = "FixedDialog"
$lblP = New-Object System.Windows.Forms.Label
$lblP.Text = "Date du règlement (JJ/MM) :"
$lblP.SetBounds(15,15,300,25)
$lblP.ForeColor = [System.Drawing.Color]::White
$lblP.Font = New-Object System.Drawing.Font("Segoe UI", 11)
$txtP = New-Object System.Windows.Forms.TextBox
$txtP.SetBounds(15,45,150,30)
$txtP.Font = New-Object System.Drawing.Font("Segoe UI", 12)
$btnC = New-Object System.Windows.Forms.Button
$btnC.Text = "CALCULER ET AJOUTER"
$btnC.SetBounds(15,85,200,35)
$btnC.ForeColor = [System.Drawing.Color]::Cyan
$btnC.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat
$btnC.Cursor = [System.Windows.Forms.Cursors]::Hand
$btnC.Add_Click({
# Regex pour vérifier le format JJ/MM
if ($txtP.Text -match "^(\d{2})/(\d{2})$") {
try {
# Récupération dynamique de l'année actuelle
$currentYear = (Get-Date).Year
# Tentative de création de la date avec l'année en cours
$targetDate = [DateTime]::ParseExact($txtP.Text + "/$currentYear", "dd/MM/yyyy", $null)
# Sécurité : Si la date saisie est déjà passée (ex: on est en Nov, on tape 01/02)
# On bascule automatiquement sur l'année suivante
if ($targetDate.Date -lt (Get-Date).Date) {
$targetDate = $targetDate.AddYears(1)
}
# Calcul de la différence réelle en jours
$today = (Get-Date).Date
$diff = ($targetDate - $today).Days + 1
# Formattage propre pour l'affichage
$finalDateStr = $targetDate.ToString("dd/MM/yyyy")
# Ajout direct dans votre champ de note principal
$txtNote.SelectionFont = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Bold)
$txtNote.AppendText(" | Échéance : $diff jours (le $finalDateStr)")
$pForm.Close()
} catch {
[System.Windows.Forms.MessageBox]::Show("Erreur : La date '$($txtP.Text)' n'est pas valide.", "Erreur de saisie")
}
} else {
[System.Windows.Forms.MessageBox]::Show("Merci de respecter le format JJ/MM (ex: 12/05)", "Format incorrect")
}
})
$pForm.Controls.AddRange(@($lblP, $txtP, $btnC))
$pForm.ShowDialog()
}
# --- RACCOURCIS ---
$flow = New-Object System.Windows.Forms.FlowLayoutPanel; $flow.SetBounds(20, 395, 645, 65)
$jouForm.Controls.Add($flow)
$labels = @("SMS", "WHATSAPP", "EMAIL", "TELEPHONE", "PROMESSE", "ABSENCE", "INJOIGNABLE", "NRP", "FAUX NUMERO", "MEMOIRE")
foreach ($l in $labels) {
$btnA = New-Object System.Windows.Forms.Button;
$btnA.Text = $l;
$btnA.FlatStyle = "Flat";
$btnA.ForeColor = [System.Drawing.Color]::Cyan
$btnA.Font = New-Object System.Drawing.Font("Segoe UI", 7, [System.Drawing.FontStyle]::Bold)
$btnA.Size = New-Object System.Drawing.Size(100, 25)
$btnA.Add_Click({
$txtNote.Clear()
$txtNote.SelectionFont = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Bold)
$txtNote.SelectionColor = [System.Drawing.Color]::Cyan; $txtNote.AppendText("[$($this.Text)]")
$txtNote.SelectionFont = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Regular)
$txtNote.SelectionColor = [System.Drawing.Color]::White; $txtNote.AppendText(" ")
if ($this.Text -eq "PROMESSE") { Show-PromiseCalc }
$txtNote.Focus()
})
$flow.Controls.Add($btnA)
}
# --- SAISIE ---
$saisieContainer = New-Object System.Windows.Forms.Panel; $saisieContainer.SetBounds(20, 470, 645, 120); $saisieContainer.BackColor = [System.Drawing.Color]::FromArgb(25, 30, 45); $saisieContainer.BorderStyle = "FixedSingle"
$jouForm.Controls.Add($saisieContainer)
$txtNote = New-Object System.Windows.Forms.RichTextBox; $txtNote.SetBounds(8, 8, 625, 100); $txtNote.BackColor = [System.Drawing.Color]::FromArgb(25, 30, 45); $txtNote.ForeColor = [System.Drawing.Color]::White; $txtNote.BorderStyle = "None"; $txtNote.Font = New-Object System.Drawing.Font("Segoe UI", 11, [System.Drawing.FontStyle]::Regular)
$saisieContainer.Controls.Add($txtNote)
# --- ACTIONS ---
$btnSave = New-Object System.Windows.Forms.Button;
$btnSave.Text = "VALIDER ET ARCHIVER";
$btnSave.SetBounds(20, 600, 645, 50);
$btnSave.FlatStyle = "Flat";
$btnSave.BackColor = [System.Drawing.Color]::FromArgb(0, 120, 215);
$btnSave.ForeColor = [System.Drawing.Color]::White
$btnSave.Font = New-Object System.Drawing.Font("Segoe UI", 10, [System.Drawing.FontStyle]::Bold)
$btnSave.Add_Click({
if ($txtNote.Text.Trim() -eq "") { return }
$entry = "[$(Get-Date -Format 'dd/MM HH:mm')] [$userName] ⚡ $($txtNote.Text.Replace('"',"'"))"
[array]$notes = if ($journalData.PSObject.Properties[$police]) { @($journalData."$police") } else { @() }
if ($script:editIndex -ge 0) { $notes[$script:editIndex] = $entry; $script:editIndex = -1 } else {
$notes = @($entry) + $notes }
$btnSave.BackColor = [System.Drawing.Color]::FromArgb(0, 120, 215)
$journalData | Add-Member -MemberType NoteProperty -Name $police -Value $notes -Force
Internal-SaveJournal -data $journalData;
$txtNote.Clear();
Update-JournalDisplay;
$script:actionDone = $true
})
$jouForm.Controls.Add($btnSave)
# --- AUTO-JOURNALISATION ---
$jouForm.Add_FormClosing({
if (-not $script:actionDone -and ((Get-Date) - $startTime).TotalMinutes -gt 3) {
$autoEntry = "[$(Get-Date -Format 'dd/MM HH:mm')] [$userName] ⚡ [SYSTEME] Consultation dossier sans action (>3min)"
[array]$n = if ($journalData.PSObject.Properties[$police]) { @($journalData."$police") } else { @() }
$journalData | Add-Member -MemberType NoteProperty -Name $police -Value (@($autoEntry) + $n) -Force
Internal-SaveJournal -data $journalData
}
})
$ctxMenu = New-Object System.Windows.Forms.ContextMenuStrip
$ctxMenu.Items.Add("📝 Modifier", $null, {
$line = $logDisplay.GetLineFromCharIndex($logDisplay.SelectionStart)
$idx = [Math]::Max(0, [Math]::Floor($line / 3))
[array]$n = @($journalData."$police")
if ($idx -lt $n.Count) { $txtNote.Text = ($n[$idx] -split " ⚡ ")[1];
$script:editIndex = $idx;
$btnSave.BackColor = [System.Drawing.Color]::DarkOrange; $txtNote.Focus() }
})
# bugs de suppression cause CR/LF indexation de lignes à fixer
$ctxMenu.Items.Add("❌ Supprimer", $null, {
$line = $logDisplay.GetLineFromCharIndex($logDisplay.SelectionStart)
$idx = [Math]::Max(0, [Math]::Floor($line / 3))
[array]$n = @($journalData."$police")
$confirm = [System.Windows.Forms.MessageBox]::Show("Supprimer cette note ?`n`n$($n[$idx])", "Audit", 4)
if ($idx -lt $n.Count -and $confirm -eq 6) {
$new = for($i=0; $i -lt $n.Count; $i++){ if($i -ne $idx){ $n[$i] } }
$journalData."$police" = @($new);
Internal-SaveJournal -data $journalData;
Update-JournalDisplay
}
})
$logDisplay.ContextMenuStrip = $ctxMenu
$txtSearch.Add_TextChanged({ Update-JournalDisplay $this.Text })
Update-JournalDisplay
$jouForm.ShowDialog()
}