#Requires -Version 5.1 # ============================================================================= # MEPRO IT AUTO-DEPLOY ◆ GUI Edition (WinForms) # PT Meprofarm — Corporate IT Deployment Automation # Author : IT Department | Version : 2.0 # # Pemanggilan one-liner: # irm nekahost.com/deploy.ps1 | iex # # Atau jalankan langsung: # powershell -NoProfile -ExecutionPolicy Bypass -File .\Mepro-AutoDeploy-GUI.ps1 # ============================================================================= $ProgressPreference = 'SilentlyContinue' $ErrorActionPreference = 'SilentlyContinue' # ── Auto-Elevasi ke Administrator ──────────────────────────────────────────── # Deteksi apakah sudah berjalan sebagai Admin; jika belum, simpan ke temp dan relaunch if (-not ([Security.Principal.WindowsPrincipal][Security.Principal.WindowsIdentity]::GetCurrent()).IsInRole( [Security.Principal.WindowsBuiltInRole]::Administrator)) { try { $tempFile = "$env:TEMP\MeproAutoDeploy_GUI.ps1" if ($PSCommandPath -and (Test-Path $PSCommandPath)) { Copy-Item $PSCommandPath $tempFile -Force } else { # Mode irm|iex: MyInvocation.MyCommand.Definition berisi isi script penuh $MyInvocation.MyCommand.Definition | Out-File -FilePath $tempFile -Encoding UTF8 -Force } Start-Process powershell.exe ` -ArgumentList "-NoProfile -ExecutionPolicy Bypass -File `"$tempFile`"" ` -Verb RunAs } catch { } exit } # ── Load Assembly WinForms & Drawing ───────────────────────────────────────── Add-Type -AssemblyName System.Windows.Forms Add-Type -AssemblyName System.Drawing [System.Windows.Forms.Application]::EnableVisualStyles() [System.Windows.Forms.Application]::SetCompatibleTextRenderingDefault($false) # ── Palet Warna Dark Theme ──────────────────────────────────────────────────── $C = @{ Bg0 = [System.Drawing.Color]::FromArgb(10, 10, 20) # Latar paling gelap Bg1 = [System.Drawing.Color]::FromArgb(18, 18, 34) # Header / toolbar Bg2 = [System.Drawing.Color]::FromArgb(26, 26, 46) # GroupBox / card Bg3 = [System.Drawing.Color]::FromArgb(38, 38, 64) # Hover state Accent = [System.Drawing.Color]::FromArgb(45, 190, 255) # Biru cyan utama Green = [System.Drawing.Color]::FromArgb(48, 210, 105) # Sukses / OK Red = [System.Drawing.Color]::FromArgb(255, 75, 75) # Error / Deselect Yellow = [System.Drawing.Color]::FromArgb(255, 200, 45) # Sub-label / warning Purple = [System.Drawing.Color]::FromArgb(185, 95, 255) # Section log header TextPri = [System.Drawing.Color]::FromArgb(222, 222, 240) # Teks primer TextSec = [System.Drawing.Color]::FromArgb(130, 130, 165) # Teks sekunder Border = [System.Drawing.Color]::FromArgb(45, 45, 75) # Garis pemisah LogBg = [System.Drawing.Color]::FromArgb(5, 5, 12) # Log background BtnExec = [System.Drawing.Color]::FromArgb(20, 130, 58) # Tombol eksekusi BtnSel = [System.Drawing.Color]::FromArgb(20, 75, 145) # Tombol Select All BtnDesel = [System.Drawing.Color]::FromArgb(125, 30, 30) # Tombol Deselect AccentBr = [System.Drawing.Color]::FromArgb(0, 220, 255) # Accent terang } # ============================================================================= # BACKEND EXECUTION SCRIPTBLOCK # Seluruh logika instalasi & tweak berjalan di Runspace terpisah agar GUI # tidak freeze. Pesan dikirim ke UI via ConcurrentQueue dengan format TEXT|WARNA # ============================================================================= $BackendScriptBlock = { param( [string[]]$SelectedTasks, [System.Collections.Concurrent.ConcurrentQueue[string]]$Queue ) $ProgressPreference = 'SilentlyContinue' $ErrorActionPreference = 'SilentlyContinue' # Helper log: kirim pesan ke Queue dengan format "teks|warna" function lLog { param($t, $c = "White") $Queue.Enqueue("$t|$c") } function lStep { param($m) lLog "[>>] $m" "Yellow" } function lOK { param($m) lLog " [OK] $m" "Green" } function lFail { param($m) lLog " [!!] $m" "Red" } function lInfo { param($m) lLog " [--] $m" "Cyan" } function lSect { param($m) lLog "`n ════ $m ════" "Purple" } # ────────────────────────────────────────────────────────────────────────── # [1] WINGET BOOTSTRAPPER — selalu dijalankan pertama kali # ────────────────────────────────────────────────────────────────────────── function Invoke-WingetBootstrap { lSect "WINGET BOOTSTRAPPER" $wg = Get-Command winget -ErrorAction SilentlyContinue if ($wg) { lOK "Winget tersedia di sistem." return } lStep "Winget tidak ditemukan. Mengunduh dari GitHub Microsoft..." try { $url = "https://github.com/microsoft/winget-cli/releases/latest/download/Microsoft.DesktopAppInstaller_8wekyb3d8bbwe.msixbundle" $dest = "$env:TEMP\WingetInstaller.msixbundle" Invoke-WebRequest -Uri $url -OutFile $dest -UseBasicParsing -ErrorAction Stop Add-AppxPackage -Path $dest -ErrorAction Stop Remove-Item $dest -Force -ErrorAction SilentlyContinue lOK "Winget berhasil diinstal." } catch { lFail "Gagal menginstal Winget: $_" lFail "Silakan install App Installer dari Microsoft Store secara manual." } } # ────────────────────────────────────────────────────────────────────────── # [2] ENGINE INSTALASI WINGET # ────────────────────────────────────────────────────────────────────────── function Install-Winget { param([string]$Id, [string]$Name, [string]$Src = "winget") lStep "Menginstal $Name ($Id)..." # Flag --disable-interactivity dan --silent mencegah prompt interaktif $null = winget install --id $Id ` --source $Src ` --silent ` --accept-package-agreements ` --accept-source-agreements ` --disable-interactivity ` 2>&1 # Exit code -1978335189 (0x8A150011) artinya sudah terinstal = OK if ($LASTEXITCODE -eq 0 -or $LASTEXITCODE -eq -1978335189) { lOK "$Name — Selesai." } else { lFail "$Name — Winget exit code: $LASTEXITCODE" } } # ────────────────────────────────────────────────────────────────────────── # [3] SPARK MESSENGER — installer custom dari server internal # ────────────────────────────────────────────────────────────────────────── function Install-Spark { lStep "Mengunduh Spark Messenger 2.7.7 dari server internal..." $url = "https://nekahost.com/installer/spark_2_7_7.exe" $dest = "$env:TEMP\spark_setup.exe" try { Invoke-WebRequest -Uri $url -OutFile $dest -UseBasicParsing -ErrorAction Stop lStep "Menjalankan silent install (argumen: -q)..." $proc = Start-Process $dest -ArgumentList "-q" -Wait -PassThru -ErrorAction Stop # Exit code 0 = sukses, 1 = beberapa installer mengembalikan 1 juga OK if ($proc.ExitCode -le 1) { lOK "Spark Messenger 2.7.7 — Selesai." } else { lFail "Spark Messenger — ExitCode tidak normal: $($proc.ExitCode)" } } catch { lFail "Gagal menginstal Spark Messenger: $_" } finally { # Selalu hapus file installer sementara meski terjadi error if (Test-Path $dest) { Remove-Item $dest -Force -ErrorAction SilentlyContinue lInfo "File installer sementara dihapus dari temp." } } } # ────────────────────────────────────────────────────────────────────────── # [4] WINDOWS TWEAKS # ────────────────────────────────────────────────────────────────────────── function Enable-SMB1Client { lStep "Mengaktifkan SMB 1.0 Client..." try { Enable-WindowsOptionalFeature -Online -FeatureName "SMB1Protocol-Client" ` -NoRestart -ErrorAction Stop | Out-Null lOK "SMB 1.0 Client — Berhasil diaktifkan." } catch { lFail "SMB 1.0 Client: $_" } } function Install-WMICCapability { lStep "Menginstal WMIC (Windows Capability)..." try { Add-WindowsCapability -Online -Name "WMIC~~~~" -ErrorAction Stop | Out-Null lOK "WMIC — Berhasil diinstal." } catch { # Tangani kasus WMIC sudah terinstal sebelumnya if ($_ -match "already") { lOK "WMIC — Sudah terinstal sebelumnya." } else { lFail "WMIC: $_" } } } function Set-DarkWallpaper { lStep "Mengatur wallpaper ke tema gelap default Windows..." try { $wallpaperPath = "$env:SystemRoot\Web\Wallpaper\Windows\img0.jpg" if (-not (Test-Path $wallpaperPath)) { lFail "File img0.jpg tidak ditemukan di: $wallpaperPath" return } # Simpan path ke registry Set-ItemProperty -Path "HKCU:\Control Panel\Desktop" ` -Name "Wallpaper" -Value $wallpaperPath -ErrorAction Stop # Panggil SystemParametersInfo via P/Invoke untuk apply tanpa reboot Add-Type -TypeDefinition @" using System; using System.Runtime.InteropServices; public class WallpaperSetter { [DllImport("user32.dll", CharSet = CharSet.Auto)] public static extern int SystemParametersInfo(int uAction, int uParam, string lpvParam, int fuWinIni); } "@ -ErrorAction SilentlyContinue # SPI_SETDESKWALLPAPER=20, SPIF_UPDATEINIFILE|SPIF_SENDCHANGE=3 [WallpaperSetter]::SystemParametersInfo(20, 0, $wallpaperPath, 3) | Out-Null lOK "Wallpaper — Diatur ke: img0.jpg (Dark Theme)." } catch { lFail "Wallpaper: $_" } } function Set-VFXCustomMode { lStep "Menerapkan Visual Effects mode Custom (base performance)..." try { $ve = "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\VisualEffects" $dt = "HKCU:\Control Panel\Desktop" if (-not (Test-Path $ve)) { New-Item $ve -Force | Out-Null } # VisualFXSetting=3 → Custom (bukan 1=Best Performance, 2=Best Appearance) Set-ItemProperty $ve -Name "VisualFXSetting" -Value 3 -Type DWord -ErrorAction Stop # Byte mask untuk mematikan SEMUA efek visual (akan diaktifkan per-item di bawah) Set-ItemProperty $dt -Name "UserPreferencesMask" ` -Value ([byte[]](0x90, 0x12, 0x03, 0x80, 0x10, 0x00, 0x00, 0x00)) ` -Type Binary -ErrorAction Stop lOK "Visual Effects — Custom mode diterapkan (semua efek nonaktif)." } catch { lFail "Visual Effects Custom: $_" } } function Enable-FontSmoothing { lStep "Mengaktifkan Font Smoothing (ClearType)..." try { $dt = "HKCU:\Control Panel\Desktop" Set-ItemProperty $dt -Name "FontSmoothing" -Value 2 -Type String -ErrorAction Stop Set-ItemProperty $dt -Name "FontSmoothingType" -Value 2 -Type DWord -ErrorAction Stop lOK "Font Smoothing (ClearType) — Aktif." } catch { lFail "Font Smoothing: $_" } } function Enable-ThumbnailView { lStep "Mengaktifkan Thumbnail Preview (bukan Icons saja)..." try { # IconsOnly=0 berarti TAMPILKAN thumbnail (bukan ikon saja) Set-ItemProperty ` "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" ` -Name "IconsOnly" -Value 0 -Type DWord -ErrorAction Stop lOK "Thumbnail Preview — Aktif." } catch { lFail "Thumbnail View: $_" } } function Enable-TranslucentSelection { lStep "Mengaktifkan Translucent Selection Rectangle..." try { Set-ItemProperty ` "HKCU:\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced" ` -Name "ListviewAlphaSelect" -Value 1 -Type DWord -ErrorAction Stop lOK "Translucent Selection Rectangle — Aktif." } catch { lFail "Translucent Selection: $_" } } function Enable-DragFullWindows { lStep "Mengaktifkan Show Window Contents While Dragging..." try { Set-ItemProperty "HKCU:\Control Panel\Desktop" ` -Name "DragFullWindows" -Value 1 -Type String -ErrorAction Stop lOK "Show Window Contents While Dragging — Aktif." } catch { lFail "Drag Full Windows: $_" } } function Restart-ExplorerSilent { # Restart Explorer agar semua tweak visual langsung berlaku lStep "Merestart Windows Explorer untuk menerapkan perubahan visual..." try { Stop-Process -Name "explorer" -Force -ErrorAction SilentlyContinue Start-Sleep -Milliseconds 1800 # Explorer biasanya auto-restart; cek dan paksa jika belum if (-not (Get-Process "explorer" -ErrorAction SilentlyContinue)) { Start-Process "explorer.exe" } lOK "Windows Explorer — Berhasil direstart." } catch { lFail "Explorer restart: $_" } } # ── PETA TASK KEY → FUNGSI ───────────────────────────────────────────── # Urutan eksekusi ditentukan di sini (ordered dictionary) $taskMap = [ordered]@{ "chrome" = { Install-Winget "Google.Chrome" "Google Chrome" } "firefox" = { Install-Winget "Mozilla.Firefox" "Mozilla Firefox" } "openoffice" = { Install-Winget "Apache.OpenOffice" "Apache OpenOffice" } "wps" = { Install-Winget "Kingsoft.WPSOffice" "Kingsoft WPS Office" } "7zip" = { Install-Winget "7zip.7zip" "7-Zip" } "adobereader" = { Install-Winget "Adobe.Acrobat.Reader.64-bit" "Adobe Acrobat Reader (64-bit)" } "thunderbird" = { Install-Winget "Mozilla.Thunderbird" "Mozilla Thunderbird" } "avg" = { Install-Winget "XP8BX2DWV7TF50" "AVG AntiVirus Free" "msstore" } "spark" = { Install-Spark } "smb1" = { Enable-SMB1Client } "wmic" = { Install-WMICCapability } "wallpaper" = { Set-DarkWallpaper } "vfx" = { Set-VFXCustomMode } "fontsmooth" = { Enable-FontSmoothing } "thumbnails" = { Enable-ThumbnailView } "alphaselect" = { Enable-TranslucentSelection } "dragfull" = { Enable-DragFullWindows } } # Task yang memerlukan restart Explorer setelah selesai $visualTweakKeys = @("vfx", "fontsmooth", "thumbnails", "alphaselect", "dragfull") # ── DISPATCH EKSEKUSI ───────────────────────────────────────────────── lSect "DEPLOYMENT DIMULAI" # Bootstrap Winget selalu dijalankan tanpa checkbox Invoke-WingetBootstrap $needExplorerRestart = $false foreach ($key in $taskMap.Keys) { if ($key -in $SelectedTasks) { & $taskMap[$key] if ($key -in $visualTweakKeys) { $needExplorerRestart = $true } } } # Restart Explorer sekali di akhir jika ada visual tweak yang diaktifkan if ($needExplorerRestart) { Restart-ExplorerSilent } lSect "DEPLOYMENT SELESAI" # Sinyal ke UI bahwa proses telah selesai $Queue.Enqueue("__DONE__") } # ============================================================================= # GUI — STATE VARIABEL (scope $script: agar bisa diakses lintas closure/timer) # ============================================================================= $script:ExecQueue = $null $script:ExecPS = $null $script:ExecRS = $null $script:ExecAsync = $null $script:ExecTimer = $null $script:TxtLog = $null # Referensi RichTextBox untuk helper Append-Log # Koleksi semua CheckBox (untuk Select All / Deselect All) $AllCheckBoxes = [System.Collections.Generic.List[System.Windows.Forms.CheckBox]]::new() # ============================================================================= # HELPER FUNCTIONS GUI # ============================================================================= # Buat CheckBox dengan tema gelap; .Name digunakan sebagai task key function New-AppCheckBox { param([string]$Label, [string]$TaskKey, [int]$X, [int]$Y, [int]$W = 248) $cb = New-Object System.Windows.Forms.CheckBox $cb.Name = $TaskKey # Name = kunci task untuk dispatch $cb.Text = " $Label" $cb.Checked = $true # Default: semua tercentang $cb.Location = New-Object System.Drawing.Point($X, $Y) $cb.Size = New-Object System.Drawing.Size($W, 23) $cb.ForeColor = [System.Drawing.Color]::White $cb.BackColor = [System.Drawing.Color]::Transparent $cb.Font = New-Object System.Drawing.Font("Segoe UI", 9) $cb.FlatStyle = [System.Windows.Forms.FlatStyle]::Standard $AllCheckBoxes.Add($cb) return $cb } # Buat GroupBox bercorak gelap dengan judul aksen cyan function New-DarkGroupBox { param([string]$Title, [int]$X, [int]$Y, [int]$W, [int]$H) $gb = New-Object System.Windows.Forms.GroupBox $gb.Text = " $Title" $gb.Location = New-Object System.Drawing.Point($X, $Y) $gb.Size = New-Object System.Drawing.Size($W, $H) $gb.ForeColor = $C.Accent $gb.BackColor = $C.Bg2 $gb.Font = New-Object System.Drawing.Font("Segoe UI Semibold", 9) return $gb } # Buat tombol flat modern function New-FlatButton { param( [string]$Label, [int]$X, [int]$Y, [int]$W, [int]$H, [System.Drawing.Color]$Bg, [System.Drawing.Color]$Fg, [float]$FontPt = 9.5 ) $btn = New-Object System.Windows.Forms.Button $btn.Text = $Label $btn.Location = New-Object System.Drawing.Point($X, $Y) $btn.Size = New-Object System.Drawing.Size($W, $H) $btn.BackColor = $Bg $btn.ForeColor = $Fg $btn.FlatStyle = [System.Windows.Forms.FlatStyle]::Flat $btn.FlatAppearance.BorderSize = 1 $btn.FlatAppearance.BorderColor = $Fg $btn.Font = New-Object System.Drawing.Font("Segoe UI Semibold", $FontPt) $btn.Cursor = [System.Windows.Forms.Cursors]::Hand $btn.UseVisualStyleBackColor = $false return $btn } # Append teks berwarna ke RichTextBox log function Append-Log { param([string]$Text, [System.Drawing.Color]$Color) if ($null -eq $script:TxtLog) { return } $script:TxtLog.SelectionStart = $script:TxtLog.TextLength $script:TxtLog.SelectionLength = 0 $script:TxtLog.SelectionColor = $Color $script:TxtLog.AppendText("$Text`n") $script:TxtLog.ScrollToCaret() } # Sub-label italic sebagai pemisah kategori di dalam GroupBox function New-SubLabel { param([string]$Text, [int]$X, [int]$Y, [int]$W = 350) $lbl = New-Object System.Windows.Forms.Label $lbl.Text = $Text $lbl.ForeColor = $C.Yellow $lbl.BackColor = [System.Drawing.Color]::Transparent $lbl.Font = New-Object System.Drawing.Font("Segoe UI", 8.5, [System.Drawing.FontStyle]::Italic) $lbl.Location = New-Object System.Drawing.Point($X, $Y) $lbl.Size = New-Object System.Drawing.Size($W, 18) return $lbl } # ============================================================================= # FORM UTAMA # Layout (ClientSize 820 x 832): # [0 - 88 ] Header panel # [88 - 90 ] Separator accent # [90 - 548] Panel scrollable (konten checkbox) # [548 - 604] Panel tombol kontrol # [604 - 606] Separator tipis # [606 - 630] Header log # [630 - 832] RichTextBox log console # ============================================================================= $form = New-Object System.Windows.Forms.Form $form.Text = "Mepro IT Auto-Deploy ◆ PT Meprofarm — IT Department" $form.ClientSize = New-Object System.Drawing.Size(820, 832) $form.StartPosition = "CenterScreen" $form.BackColor = $C.Bg0 $form.ForeColor = $C.TextPri $form.Font = New-Object System.Drawing.Font("Segoe UI", 9) $form.FormBorderStyle = "FixedSingle" $form.MaximizeBox = $false $form.Icon = [System.Drawing.SystemIcons]::Shield # ── HEADER PANEL ───────────────────────────────────────────────────────────── $pHeader = New-Object System.Windows.Forms.Panel $pHeader.Size = New-Object System.Drawing.Size(820, 88) $pHeader.Location = New-Object System.Drawing.Point(0, 0) $pHeader.BackColor = $C.Bg1 $lblTitle = New-Object System.Windows.Forms.Label $lblTitle.Text = " MEPRO IT AUTO-DEPLOY" $lblTitle.Font = New-Object System.Drawing.Font("Consolas", 20, [System.Drawing.FontStyle]::Bold) $lblTitle.ForeColor = $C.AccentBr $lblTitle.Location = New-Object System.Drawing.Point(10, 6) $lblTitle.Size = New-Object System.Drawing.Size(780, 44) $lblTitle.BackColor = [System.Drawing.Color]::Transparent $lblSub = New-Object System.Windows.Forms.Label $lblSub.Text = " PT Meprofarm ◆ IT Department ◆ Corporate Deployment Automation ◆ v2.0 WinForms GUI" $lblSub.Font = New-Object System.Drawing.Font("Segoe UI", 8.5) $lblSub.ForeColor = $C.TextSec $lblSub.Location = New-Object System.Drawing.Point(10, 52) $lblSub.Size = New-Object System.Drawing.Size(795, 20) $lblSub.BackColor = [System.Drawing.Color]::Transparent $pHeader.Controls.AddRange(@($lblTitle, $lblSub)) $form.Controls.Add($pHeader) # ── Garis separator aksen ── $sepTop = New-Object System.Windows.Forms.Panel $sepTop.Size = New-Object System.Drawing.Size(820, 2) $sepTop.Location = New-Object System.Drawing.Point(0, 88) $sepTop.BackColor = $C.Accent $form.Controls.Add($sepTop) # ── SCROLLABLE CONTENT PANEL ───────────────────────────────────────────────── $pScroll = New-Object System.Windows.Forms.Panel $pScroll.Location = New-Object System.Drawing.Point(0, 90) $pScroll.Size = New-Object System.Drawing.Size(820, 458) $pScroll.BackColor = $C.Bg0 $pScroll.AutoScroll = $true $form.Controls.Add($pScroll) # ═══════════════════════════════════════════════════════════ # GROUPBOX: BROWSER # ═══════════════════════════════════════════════════════════ $gbBrowser = New-DarkGroupBox "🌐 BROWSER" 8 8 248 95 $cbChrome = New-AppCheckBox "Google Chrome" "chrome" 10 26 $cbFirefox = New-AppCheckBox "Mozilla Firefox" "firefox" 10 54 $gbBrowser.Controls.AddRange(@($cbChrome, $cbFirefox)) $pScroll.Controls.Add($gbBrowser) # ═══════════════════════════════════════════════════════════ # GROUPBOX: OFFICE SUITE # ═══════════════════════════════════════════════════════════ $gbOffice = New-DarkGroupBox "📄 OFFICE SUITE" 265 8 248 95 $cbOpenOff = New-AppCheckBox "Apache OpenOffice" "openoffice" 10 26 $cbWPS = New-AppCheckBox "Kingsoft WPS Office" "wps" 10 54 $gbOffice.Controls.AddRange(@($cbOpenOff, $cbWPS)) $pScroll.Controls.Add($gbOffice) # ═══════════════════════════════════════════════════════════ # GROUPBOX: UTILITY # ═══════════════════════════════════════════════════════════ $gbUtility = New-DarkGroupBox "🔧 UTILITY" 522 8 288 122 $cb7Zip = New-AppCheckBox "7-Zip" "7zip" 10 26 265 $cbAdobe = New-AppCheckBox "Adobe Acrobat Reader (64-bit)" "adobereader" 10 54 265 $cbThunder = New-AppCheckBox "Mozilla Thunderbird" "thunderbird" 10 82 265 $gbUtility.Controls.AddRange(@($cb7Zip, $cbAdobe, $cbThunder)) $pScroll.Controls.Add($gbUtility) # ═══════════════════════════════════════════════════════════ # GROUPBOX: SECURITY # ═══════════════════════════════════════════════════════════ $gbSecurity = New-DarkGroupBox "🛡️ SECURITY" 8 113 248 65 $cbAVG = New-AppCheckBox "AVG AntiVirus Free (MS Store)" "avg" 10 28 230 $gbSecurity.Controls.AddRange(@($cbAVG)) $pScroll.Controls.Add($gbSecurity) # ═══════════════════════════════════════════════════════════ # GROUPBOX: APLIKASI INTERNAL # ═══════════════════════════════════════════════════════════ $gbInternal = New-DarkGroupBox "🏢 APLIKASI INTERNAL" 265 113 248 65 $cbSpark = New-AppCheckBox "Spark Messenger 2.7.7" "spark" 10 28 230 $gbInternal.Controls.AddRange(@($cbSpark)) $pScroll.Controls.Add($gbInternal) # ═══════════════════════════════════════════════════════════ # GROUPBOX: WINDOWS TWEAKS & KONFIGURASI # ═══════════════════════════════════════════════════════════ $gbTweaks = New-DarkGroupBox "⚙️ WINDOWS TWEAKS & KONFIGURASI SISTEM" 8 188 802 265 # ── Kolom Kiri: Fitur Sistem ── $lblSysCol = New-SubLabel "── Fitur Sistem" 12 26 $cbSMB1 = New-AppCheckBox "Enable SMB 1.0 Client" "smb1" 12 50 270 $cbWMIC = New-AppCheckBox "Install WMIC (Legacy CLI)" "wmic" 12 78 270 $cbWallpaper = New-AppCheckBox "Set Wallpaper Default Dark" "wallpaper" 12 106 270 # ── Kolom Kanan: Visual Effects ── $lblVFXCol = New-SubLabel "── Visual Effects (Custom Mode)" 415 26 $cbVFX = New-AppCheckBox "Apply Custom Performance Mode" "vfx" 415 50 290 $cbFontSmooth = New-AppCheckBox "Font Smoothing (ClearType)" "fontsmooth" 415 78 290 $cbThumbs = New-AppCheckBox "Thumbnail Preview (vs. Icons only)" "thumbnails" 415 106 290 $cbAlpha = New-AppCheckBox "Translucent Selection Rectangle" "alphaselect" 415 134 290 $cbDragFull = New-AppCheckBox "Show Window Contents While Dragging" "dragfull" 415 162 290 # Info tip di bagian bawah GroupBox $lblTip = New-Object System.Windows.Forms.Label $lblTip.Text = " ℹ Perubahan Visual Effects akan memicu restart Windows Explorer secara otomatis." $lblTip.ForeColor = $C.TextSec $lblTip.BackColor = [System.Drawing.Color]::Transparent $lblTip.Font = New-Object System.Drawing.Font("Segoe UI", 8, [System.Drawing.FontStyle]::Italic) $lblTip.Location = New-Object System.Drawing.Point(12, 234) $lblTip.Size = New-Object System.Drawing.Size(780, 18) $gbTweaks.Controls.AddRange(@( $lblSysCol, $cbSMB1, $cbWMIC, $cbWallpaper, $lblVFXCol, $cbVFX, $cbFontSmooth, $cbThumbs, $cbAlpha, $cbDragFull, $lblTip )) $pScroll.Controls.Add($gbTweaks) # ── PANEL TOMBOL KONTROL ───────────────────────────────────────────────────── $pBtnRow = New-Object System.Windows.Forms.Panel $pBtnRow.Location = New-Object System.Drawing.Point(0, 550) $pBtnRow.Size = New-Object System.Drawing.Size(820, 54) $pBtnRow.BackColor = $C.Bg1 $btnSelAll = New-FlatButton "☑ Pilih Semua" 10 11 158 32 $C.BtnSel $C.Accent $btnDeselAll = New-FlatButton "☐ Hapus Semua" 178 11 158 32 $C.BtnDesel $C.Red $btnExec = New-FlatButton "🚀 EKSEKUSI DEPLOYMENT" 388 8 422 38 $C.BtnExec $C.TextPri 11.5 $btnExec.FlatAppearance.BorderColor = $C.Green $pBtnRow.Controls.AddRange(@($btnSelAll, $btnDeselAll, $btnExec)) $form.Controls.Add($pBtnRow) # ── Separator tipis ── $sepMid = New-Object System.Windows.Forms.Panel $sepMid.Size = New-Object System.Drawing.Size(820, 2) $sepMid.Location = New-Object System.Drawing.Point(0, 604) $sepMid.BackColor = $C.Border $form.Controls.Add($sepMid) # ── LOG CONSOLE HEADER ─────────────────────────────────────────────────────── $pLogHdr = New-Object System.Windows.Forms.Panel $pLogHdr.Size = New-Object System.Drawing.Size(820, 26) $pLogHdr.Location = New-Object System.Drawing.Point(0, 606) $pLogHdr.BackColor = $C.Bg1 $lblLogHdr = New-Object System.Windows.Forms.Label $lblLogHdr.Text = " 📋 DEPLOYMENT LOG CONSOLE — Real-time output" $lblLogHdr.Font = New-Object System.Drawing.Font("Segoe UI Semibold", 8.5) $lblLogHdr.ForeColor = $C.TextSec $lblLogHdr.BackColor = [System.Drawing.Color]::Transparent $lblLogHdr.Location = New-Object System.Drawing.Point(0, 5) $lblLogHdr.Size = New-Object System.Drawing.Size(600, 18) $pLogHdr.Controls.Add($lblLogHdr) $form.Controls.Add($pLogHdr) # ── LOG RICHTEXTBOX ────────────────────────────────────────────────────────── $txtLog = New-Object System.Windows.Forms.RichTextBox $txtLog.Location = New-Object System.Drawing.Point(0, 632) $txtLog.Size = New-Object System.Drawing.Size(820, 200) $txtLog.BackColor = $C.LogBg $txtLog.ForeColor = $C.TextPri $txtLog.Font = New-Object System.Drawing.Font("Consolas", 9) $txtLog.ReadOnly = $true $txtLog.ScrollBars = "ForcedVertical" $txtLog.BorderStyle = [System.Windows.Forms.BorderStyle]::None $txtLog.WordWrap = $false $form.Controls.Add($txtLog) # Simpan referensi ke script scope agar Append-Log bisa mengaksesnya $script:TxtLog = $txtLog # ── Pesan selamat datang di log ── Append-Log " ══════════════════════════════════════════════════════════════════════════════" $C.Border Append-Log " Mepro IT Auto-Deploy ◆ PT Meprofarm IT Department ◆ v2.0 GUI Edition" $C.AccentBr Append-Log " ══════════════════════════════════════════════════════════════════════════════" $C.Border Append-Log " Semua komponen dicentang secara default. Gunakan tombol Pilih/Hapus jika perlu." $C.TextSec Append-Log " Winget Bootstrapper akan berjalan otomatis di awal setiap eksekusi." $C.TextSec Append-Log " ──────────────────────────────────────────────────────────────────────────────" $C.Border # ============================================================================= # EVENT HANDLERS # ============================================================================= # ── Tombol: Pilih Semua ── $btnSelAll.Add_Click({ foreach ($cb in $AllCheckBoxes) { $cb.Checked = $true } }) # ── Tombol: Hapus Semua ── $btnDeselAll.Add_Click({ foreach ($cb in $AllCheckBoxes) { $cb.Checked = $false } }) # ── Hover effects pada tombol ── $btnExec.Add_MouseEnter({ if ($btnExec.Enabled) { $btnExec.BackColor = [System.Drawing.Color]::FromArgb(30, 165, 72) } }) $btnExec.Add_MouseLeave({ if ($btnExec.Enabled) { $btnExec.BackColor = $C.BtnExec } }) $btnSelAll.Add_MouseEnter({ $btnSelAll.BackColor = $C.Bg3 }) $btnSelAll.Add_MouseLeave({ $btnSelAll.BackColor = $C.BtnSel }) $btnDeselAll.Add_MouseEnter({ $btnDeselAll.BackColor = $C.Bg3 }) $btnDeselAll.Add_MouseLeave({ $btnDeselAll.BackColor = $C.BtnDesel }) # ── Tombol: EKSEKUSI DEPLOYMENT ────────────────────────────────────────────── $btnExec.Add_Click({ # Kumpulkan task key dari semua checkbox yang dicentang # Setiap CheckBox.Name sudah di-set sebagai task key saat pembuatan $selectedTasks = @( $AllCheckBoxes | Where-Object { $_.Checked } | ForEach-Object { $_.Name } ) if ($selectedTasks.Count -eq 0) { [System.Windows.Forms.MessageBox]::Show( "Tidak ada komponen yang dipilih!`nSilakan centang minimal satu item sebelum eksekusi.", "Tidak Ada Pilihan", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Warning ) | Out-Null return } # ── Nonaktifkan semua kontrol selama proses berjalan ── $btnExec.Enabled = $false $btnExec.Text = " ⏳ Sedang Berjalan…" $btnExec.BackColor = $C.Bg3 $btnSelAll.Enabled = $false $btnDeselAll.Enabled = $false foreach ($cb in $AllCheckBoxes) { $cb.Enabled = $false } Append-Log "" $C.TextSec Append-Log " ════ EKSEKUSI DIMULAI ══════════════════════════════════════════════════" $C.AccentBr Append-Log (" Task terpilih (" + $selectedTasks.Count + "): " + ($selectedTasks -join ", ")) $C.TextSec Append-Log " ──────────────────────────────────────────────────────────────────────────────" $C.Border # ── Setup Runspace untuk eksekusi background (agar GUI tidak freeze) ── # Menggunakan ConcurrentQueue sebagai jembatan komunikasi thread-safe $script:ExecQueue = [System.Collections.Concurrent.ConcurrentQueue[string]]::new() $script:ExecRS = [System.Management.Automation.Runspaces.RunspaceFactory]::CreateRunspace() $script:ExecRS.ApartmentState = [System.Threading.ApartmentState]::STA $script:ExecRS.ThreadOptions = [System.Management.Automation.Runspaces.PSThreadOptions]::ReuseThread $script:ExecRS.Open() $script:ExecPS = [System.Management.Automation.PowerShell]::Create() $script:ExecPS.Runspace = $script:ExecRS $script:ExecPS.AddScript($BackendScriptBlock) | Out-Null $script:ExecPS.AddArgument($selectedTasks) | Out-Null $script:ExecPS.AddArgument($script:ExecQueue) | Out-Null # Jalankan secara asinkron — tidak memblokir UI thread $script:ExecAsync = $script:ExecPS.BeginInvoke() # ── Timer: polling pesan dari ConcurrentQueue setiap 150ms ── # Pendekatan ini memastikan GUI tetap responsif (DoEvents equivalent) $script:ExecTimer = New-Object System.Windows.Forms.Timer $script:ExecTimer.Interval = 150 $script:ExecTimer.Add_Tick({ # Peta warna berdasarkan string key yang dikirim backend $colorMap = @{ "Green" = $C.Green "Red" = $C.Red "Yellow" = $C.Yellow "Cyan" = $C.Accent "Purple" = $C.Purple "White" = $C.TextPri } # Drain semua pesan dari queue dalam satu tick $msg = [string]::Empty while ($script:ExecQueue.TryDequeue([ref]$msg)) { # ── Sinyal selesai ── if ($msg -eq "__DONE__") { $script:ExecTimer.Stop() $script:ExecTimer.Dispose() $script:ExecTimer = $null # Bersihkan sumber daya Runspace try { $script:ExecPS.EndInvoke($script:ExecAsync) } catch { } $script:ExecPS.Dispose(); $script:ExecPS = $null $script:ExecRS.Close(); $script:ExecRS.Dispose(); $script:ExecRS = $null # Tampilkan pesan sukses di log Append-Log " ──────────────────────────────────────────────────────────────────────────────" $C.Border Append-Log " ██ SEMUA INSTALASI SELESAI!" $C.Green Append-Log " ──────────────────────────────────────────────────────────────────────────────" $C.Border # Aktifkan kembali semua kontrol GUI $btnExec.Enabled = $true $btnExec.Text = "🚀 EKSEKUSI DEPLOYMENT" $btnExec.BackColor = $C.BtnExec $btnSelAll.Enabled = $true $btnDeselAll.Enabled = $true foreach ($cb in $AllCheckBoxes) { $cb.Enabled = $true } # Dialog MessageBox selesai (sesuai spesifikasi) [System.Windows.Forms.MessageBox]::Show( "Instalasi & Konfigurasi Selesai!`n`nHarap Restart PC Anda untuk mengaktifkan SMB 1.0 dan menerapkan semua perubahan sistem.", "Mepro IT Auto-Deploy — Selesai", [System.Windows.Forms.MessageBoxButtons]::OK, [System.Windows.Forms.MessageBoxIcon]::Information ) | Out-Null return } # ── Parse pesan format "TEKS|WARNA" dan render ke RichTextBox ── if ($msg -match "^(.*)\|(\w+)$") { $txt = $matches[1] $col = if ($colorMap.ContainsKey($matches[2])) { $colorMap[$matches[2]] } else { $C.TextPri } } else { $txt = $msg $col = $C.TextPri } Append-Log $txt $col } }) $script:ExecTimer.Start() }) # ── Cleanup saat form ditutup ──────────────────────────────────────────────── $form.Add_FormClosing({ # Hentikan timer dan bersihkan Runspace jika masih berjalan if ($null -ne $script:ExecTimer) { $script:ExecTimer.Stop() $script:ExecTimer.Dispose() } if ($null -ne $script:ExecPS) { try { $script:ExecPS.Stop() } catch { } $script:ExecPS.Dispose() } if ($null -ne $script:ExecRS) { $script:ExecRS.Close() $script:ExecRS.Dispose() } }) # ── Jalankan event loop WinForms ───────────────────────────────────────────── [System.Windows.Forms.Application]::Run($form)