@echo off
title Thu thap thong tin cau hinh may tinh
set "SELF=%~f0"
set "PS1=%TEMP%\ThongTinCauHinh_%RANDOM%.ps1"

rem ---- Tach phan PowerShell ra file tam tren o C (tranh loi khi chay tu USB / Google Drive / o mang) ----
powershell -NoProfile -ExecutionPolicy Bypass -Command "$c = [IO.File]::ReadAllText($env:SELF); [IO.File]::WriteAllText($env:PS1, $c.Substring($c.LastIndexOf('#==PS_START==#')), (New-Object Text.UTF8Encoding $true))"
if exist "%PS1%" goto :run
echo [LOI] Khong tao duoc file tam: %PS1%
pause
exit /b

:run
net session >nul 2>&1
if %errorlevel%==0 goto :admin

rem ---- Chua co quyen Admin: hoi quyen (UAC), truyen duong dan Desktop cua nguoi dung hien tai ----
echo Dang yeu cau quyen Administrator...
powershell -NoProfile -ExecutionPolicy Bypass -Command "try { Start-Process -FilePath powershell.exe -ArgumentList '-NoProfile','-ExecutionPolicy','Bypass','-File',('\"' + $env:PS1 + '\"'),('\"' + [Environment]::GetFolderPath('Desktop') + '\"') -Verb RunAs -Wait -ErrorAction Stop } catch { Write-Host 'Ban da tu choi cap quyen Administrator. Tool se khong chay.' -ForegroundColor Red; Start-Sleep 4 }"
goto :cleanup

:admin
powershell -NoProfile -ExecutionPolicy Bypass -File "%PS1%"

:cleanup
del "%PS1%" >nul 2>&1
exit /b

#==PS_START==#
Write-Host 'Dang thu thap thong tin cau hinh, vui long doi...'
$ErrorActionPreference = 'SilentlyContinue'
$desktopArg = $args[0]

# ---- Gui cau hinh len web quan ly tai san IT. De trong 2 dong duoi thi tool chi xuat file txt nhu cu ----
$QltsUrl   = 'https://qlts.nagakawa.com.vn/api/v1/cau-hinh/'   # vd: https://it.tencongty.vn/api/v1/cau-hinh/
$QltsToken = '5939fbaede53965f3b50e1eeea7d86c6eae50ec4'   # token cua tai khoan loai "Tool thu thap cau hinh" (tao trong trang Quan tri)
trap {
    Write-Host ''
    Write-Host ('[LOI] ' + $_) -ForegroundColor Red
    Write-Host 'Vui long chup man hinh nay gui cho bo phan IT.' -ForegroundColor Yellow
    Read-Host 'Nhan Enter de dong'
    exit 1
}

function GB($b) { if ($b) { '{0:N0} GB' -f ($b / 1GB) } else { 'N/A' } }
function Clean($s) { if ($s) { "$s".Trim() } else { 'N/A' } }
# Serial gia do hang de mac dinh: nhieu may cung chuoi -> khong dung de nhan dien may
function Real-Serial($s) {
    $t = "$s".Trim()
    if (-not $t -or $t -match '^(default string|to be filled by o\.?e\.?m\.?|system serial number|base board serial number|chassis serial number|bss-0123456789|0123456789|123456789|none|not specified|not applicable|n/a|oem|invalid|[0\s-]+)$') { return '' }
    return $t
}

# ---- Giai ma Product Key dang cai tu registry ----
function Get-InstalledKey {
    $dpid = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').DigitalProductId
    if (-not $dpid) { return 'N/A' }
    [int[]]$key = $dpid[52..66]
    $isWin8 = [math]::Floor($key[14] / 6) -band 1
    $key[14] = $key[14] -band 0xF7
    $chars = 'BCDFGHJKMPQRTVWXY2346789'
    $out = ''; $last = 0
    for ($i = 24; $i -ge 0; $i--) {
        $cur = 0
        for ($j = 14; $j -ge 0; $j--) {
            $cur = $cur * 256 + $key[$j]
            $key[$j] = [math]::Floor($cur / 24)
            $cur = $cur % 24
        }
        $out = $chars[$cur] + $out
        $last = $cur
    }
    if ($isWin8) { $out = $out.Substring(1).Insert($last, 'N') }
    return ($out.Substring(0,5), $out.Substring(5,5), $out.Substring(10,5), $out.Substring(15,5), $out.Substring(20,5)) -join '-'
}

$cs    = Get-WmiObject Win32_ComputerSystem
$bios  = Get-WmiObject Win32_BIOS
$board = Get-WmiObject Win32_BaseBoard
$encl  = Get-WmiObject Win32_SystemEnclosure
$os    = Get-WmiObject Win32_OperatingSystem

# ---- Laptop hay Desktop ----
$laptopTypes = 8,9,10,11,12,14,18,21,30,31,32
$isLaptop = $false
foreach ($t in $encl.ChassisTypes) { if ($laptopTypes -contains $t) { $isLaptop = $true } }
if (-not $isLaptop -and (Get-WmiObject Win32_Battery)) { $isLaptop = $true }

# ---- Hang / Model (rut gon ten hang) ----
$placeholder = 'O\.E\.M|System manufacturer|System Product Name|Default string|To be filled|^\s*$'
function Short-Brand($m) {
    switch -Regex ("$m") {
        'Micro-Star|^MSI'        { return 'MSI' }
        'ASUSTeK|^ASUS'          { return 'ASUS' }
        'Hewlett|^HP\b'          { return 'HP' }
        'Dell'                   { return 'Dell' }
        'Lenovo'                 { return 'Lenovo' }
        'Acer'                   { return 'Acer' }
        'Gigabyte'               { return 'Gigabyte' }
        'ASRock'                 { return 'ASRock' }
        'Apple'                  { return 'Apple' }
        'Microsoft'              { return 'Microsoft (Surface)' }
        'Samsung'                { return 'Samsung' }
        'Toshiba|Dynabook'       { return 'Toshiba/Dynabook' }
        'Fujitsu'                { return 'Fujitsu' }
        'LG Electronics'         { return 'LG' }
        'Intel'                  { return 'Intel' }
        'VMware'                 { return 'VMware (may ao)' }
        default                  { return (Clean $m) }
    }
}
$rawBrand = $cs.Manufacturer
$model = Clean $cs.Model
if ($rawBrand -match 'Lenovo') {
    $ver = (Get-WmiObject Win32_ComputerSystemProduct).Version
    if ($ver -and $ver -notmatch $placeholder) { $model = "$ver".Trim() + ' (' + $model + ')' }
}
# May bo / tu rap: BIOS de chu mac dinh -> lay theo mainboard
if ("$rawBrand" -match $placeholder) { $rawBrand = $board.Manufacturer }
if ($model -match $placeholder -or $model -eq 'N/A') { $model = 'Mainboard ' + (Clean $board.Product) }
$brand = Short-Brand $rawBrand

$L = New-Object System.Collections.Generic.List[string]
$sep = '=' * 60
$L.Add($sep)
$L.Add('              THONG TIN CAU HINH MAY TINH')
$L.Add('   Ngay xuat: ' + (Get-Date -Format 'dd/MM/yyyy HH:mm:ss'))
$L.Add($sep)

# ---- Serial ----
$L.Add('')
$L.Add('[1] SERIAL NUMBER')
$L.Add('    Loai may          : ' + $(if ($isLaptop) { 'Laptop' } else { 'May tinh de ban (Desktop)' }))
$L.Add('    Hang              : ' + $brand)
$L.Add('    Model             : ' + $model)
if ($isLaptop) {
    $L.Add('    Serial Number     : ' + (Clean $bios.SerialNumber))
    $L.Add('    Serial Mainboard  : ' + (Clean $board.SerialNumber))
} else {
    $L.Add('    Serial Mainboard  : ' + (Clean $board.SerialNumber))
    $L.Add('    Mainboard         : ' + (Clean $board.Manufacturer) + ' ' + (Clean $board.Product))
    $L.Add('    Serial (BIOS)     : ' + (Clean $bios.SerialNumber))
}

# ---- Product Key ----
$sls = Get-WmiObject SoftwareLicensingService
$oemKey = $sls.OA3xOriginalProductKey
$oemEdition = $sls.OA3xOriginalProductKeyDescription   # vd: [4.0] CoreSingleLanguage OEM:DM
$editionId = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').EditionID   # vd: Professional
$L.Add('')
$L.Add('[2] PRODUCT KEY')
$L.Add('    OEM Product Key   : ' + $(if ($oemKey) { $oemKey } else { 'Khong co (may khong co key OEM trong BIOS)' }))
$L.Add('    Key dang cai      : ' + (Get-InstalledKey))
if ($oemKey) { $L.Add('    Ban Windows kem may: ' + (Clean $oemEdition)) }

# ---- He dieu hanh ----
$L.Add('')
$L.Add('[3] OPERATING SYSTEM')
$L.Add('    He dieu hanh      : ' + (Clean $os.Caption) + ' (' + (Clean $os.OSArchitecture) + ')')
$L.Add('    Phien ban         : ' + (Clean $os.Version) + ' - Build ' + (Clean $os.BuildNumber))
$disp = (Get-ItemProperty 'HKLM:\SOFTWARE\Microsoft\Windows NT\CurrentVersion').DisplayVersion
if ($disp) { $L.Add('    Version           : ' + $disp) }
$L.Add('    Edition ID        : ' + (Clean $editionId))

# ---- Ten may ----
$L.Add('')
$L.Add('[4] COMPUTER NAME')
$L.Add('    Ten may           : ' + $env:COMPUTERNAME)
$L.Add('    Domain/Workgroup  : ' + (Clean $cs.Domain))
$L.Add('    User dang dang nhap: ' + (Clean $cs.UserName))

# ---- CPU ----
$L.Add('')
$L.Add('[5] CPU')
foreach ($p in Get-WmiObject Win32_Processor) {
    $L.Add('    Ten CPU           : ' + (Clean $p.Name))
    $L.Add('    So nhan / luong   : ' + $p.NumberOfCores + ' nhan / ' + $p.NumberOfLogicalProcessors + ' luong')
    $L.Add('    Toc do toi da     : ' + $p.MaxClockSpeed + ' MHz')
}

# ---- RAM ----
$memTypes = @{20='DDR';21='DDR2';24='DDR3';26='DDR4';34='DDR5';35='LPDDR5'}
$sticks = @(Get-WmiObject Win32_PhysicalMemory)
$slots = (Get-WmiObject Win32_PhysicalMemoryArray | Measure-Object MemoryDevices -Sum).Sum
$L.Add('')
$L.Add('[6] RAM')
$L.Add('    Tong dung luong   : ' + (GB $cs.TotalPhysicalMemory) + ' (su dung ' + $sticks.Count + '/' + $slots + ' khe)')
$i = 1
foreach ($m in $sticks) {
    $type = $memTypes[[int]$m.SMBIOSMemoryType]; if (-not $type) { $type = 'N/A' }
    $L.Add(('    Thanh {0} [{1}]    : {2} - {3} - {4} MHz - {5} {6}' -f $i, (Clean $m.DeviceLocator), (GB $m.Capacity), $type, $m.Speed, (Clean $m.Manufacturer), (Clean $m.PartNumber)))
    $i++
}

# ---- Card man hinh ----
$vram = @{}
Get-ChildItem 'HKLM:\SYSTEM\ControlSet001\Control\Class\{4d36e968-e325-11ce-bfc1-08002be10318}' | ForEach-Object {
    $p = Get-ItemProperty $_.PSPath
    if ($p.DriverDesc -and $p.'HardwareInformation.qwMemorySize') { $vram[$p.DriverDesc] = [int64]$p.'HardwareInformation.qwMemorySize' }
}
$L.Add('')
$L.Add('[7] CARD MAN HINH')
foreach ($v in Get-WmiObject Win32_VideoController) {
    $mem = $vram[$v.Name]; if (-not $mem) { $mem = [int64]$v.AdapterRAM }
    $memText = if ($mem -gt 0) { '{0:N0} MB' -f ($mem / 1MB) } else { 'N/A' }
    $L.Add('    - ' + (Clean $v.Name) + ' | VRAM: ' + $memText + ' | Driver: ' + (Clean $v.DriverVersion))
}

# ---- O cung ----
$L.Add('')
$L.Add('[8] O CUNG')
$pd = @(Get-PhysicalDisk)
if ($pd.Count -gt 0) {
    foreach ($d in $pd) {
        $L.Add(('    - {0} | {1} | {2} | {3} | Serial: {4}' -f (Clean $d.FriendlyName), (GB $d.Size), (Clean $d.MediaType), (Clean $d.BusType), (Clean $d.SerialNumber)))
    }
} else {
    foreach ($d in Get-WmiObject Win32_DiskDrive) {
        $L.Add(('    - {0} | {1} | {2} | Serial: {3}' -f (Clean $d.Model), (GB $d.Size), (Clean $d.InterfaceType), (Clean $d.SerialNumber)))
    }
}
$L.Add('    Phan vung:')
foreach ($v in Get-WmiObject Win32_LogicalDisk -Filter 'DriveType=3') {
    $L.Add(('      {0}  Tong: {1}  -  Con trong: {2}' -f $v.DeviceID, (GB $v.Size), (GB $v.FreeSpace)))
}

# ---- MAC ----
$L.Add('')
$L.Add('[9] MAC ADDRESS')
foreach ($n in Get-WmiObject Win32_NetworkAdapter -Filter 'PhysicalAdapter=True' | Where-Object { $_.MACAddress -and $_.PNPDeviceID -notlike 'ROOT\*' -and $_.PNPDeviceID -notlike 'SWD\*' }) {
    $L.Add(('    - {0} : {1}' -f (Clean $n.Name), $n.MACAddress.Replace(':', '-')))
}

# ---- [10] Man hinh ----
# Laptop  : chi man hinh lien (co lam tron ve co chuan, do phan giai, hang tam man hinh).
# May ban : tung man hinh roi dang cam (model, hang, co, do phan giai, serial, nam SX) de web quan ly thanh tai san rieng.
function Wmi-Text($arr) { (($arr | Where-Object { $_ -gt 0 } | ForEach-Object { [char]$_ }) -join '').Trim() }
function Round-Inch($w, $h, $sizes) {
    if (-not ($w -gt 0 -and $h -gt 0)) { return '' }
    $d = [math]::Sqrt($w * $w + $h * $h) / 2.54
    $n = $sizes | Sort-Object { [math]::Abs($_ - $d) } | Select-Object -First 1
    return ($n.ToString([Globalization.CultureInfo]::InvariantCulture) + ' inch')
}
function Monitor-Brand($code) {
    switch ("$code") {
        'DEL' { 'Dell' }      'GSM' { 'LG' }        'LGD' { 'LG' }       'SAM' { 'Samsung' }  'SEC' { 'Samsung' }
        'ACR' { 'Acer' }      'AUS' { 'ASUS' }      'HWP' { 'HP' }       'HPN' { 'HP' }       'LEN' { 'Lenovo' }
        'VSC' { 'ViewSonic' } 'PHL' { 'Philips' }   'BNQ' { 'BenQ' }     'AOC' { 'AOC' }      'MSI' { 'MSI' }
        'GBT' { 'Gigabyte' }  'EIZ' { 'EIZO' }      'NEC' { 'NEC' }      'SNY' { 'Sony' }     'HKC' { 'HKC' }
        'CMN' { 'Innolux' }   'AUO' { 'AUO' }       'BOE' { 'BOE' }      'SHP' { 'Sharp' }    'IVO' { 'InfoVision' }
        default { "$code" }
    }
}
$internalTech = @(6, 11, 13, 2147483648)   # LVDS, DisplayPort embedded, UDI embedded, Internal
$basics = @(Get-WmiObject -Namespace root\wmi -Class WmiMonitorBasicDisplayParams)
$modes  = @(Get-WmiObject -Namespace root\wmi -Class WmiMonitorListedSupportedSourceModes)
$ids    = @(Get-WmiObject -Namespace root\wmi -Class WmiMonitorID)
$monitors = @()
foreach ($c in Get-WmiObject -Namespace root\wmi -Class WmiMonitorConnectionParams) {
    $inst = $c.InstanceName
    $bp  = $basics | Where-Object { $_.InstanceName -eq $inst } | Select-Object -First 1
    $sm  = $modes  | Where-Object { $_.InstanceName -eq $inst } | Select-Object -First 1
    $mid = $ids    | Where-Object { $_.InstanceName -eq $inst } | Select-Object -First 1
    $res = ''
    if ($sm -and $sm.MonitorSourceModes) {
        $mode = $sm.MonitorSourceModes[$sm.PreferredMonitorSourceModeIndex]
        $res = '{0}x{1}' -f $mode.HorizontalActivePixels, $mode.VerticalActivePixels
    }
    $serial = ''; $brandM = ''; $modelM = ''; $year = ''
    if ($mid) {
        $serial = Wmi-Text $mid.SerialNumberID
        if ($serial -match '^0*$') { $serial = '' }   # man hinh khong ghi serial
        $brandM = Monitor-Brand (Wmi-Text $mid.ManufacturerName)
        $modelM = Wmi-Text $mid.UserFriendlyName
        if ($mid.YearOfManufacture -gt 1990) { $year = "$($mid.YearOfManufacture)" }
    }
    $monitors += [pscustomobject]@{
        Internal = ($internalTech -contains [int64]$c.VideoOutputTechnology)
        W = $bp.MaxHorizontalImageSize; H = $bp.MaxVerticalImageSize
        Res = $res; Brand = $brandM; Model = $modelM; Serial = $serial; Year = $year
    }
}
$screenText = ''
$monitorList = @()
$L.Add('')
$L.Add('[10] MAN HINH')
if ($isLaptop) {
    $p = $monitors | Where-Object { $_.Internal } | Select-Object -First 1
    if ($p) {
        $sizes = 10.1, 11.6, 12.5, 13.3, 13.5, 14, 15.6, 16, 17.3, 18
        $screenText = (@((Round-Inch $p.W $p.H $sizes), $p.Res, ($p.Brand + ' ' + $p.Model).Trim()) | Where-Object { $_ }) -join ' - '
    }
    $L.Add('    Man hinh laptop   : ' + $(if ($screenText) { $screenText } else { 'N/A (nap dang gap hoac khong doc duoc)' }))
} else {
    $sizes = 15.6, 17, 18.5, 19, 19.5, 20, 21.5, 22, 23, 23.8, 24, 25, 27, 28, 31.5, 32, 34, 43
    $ext = @($monitors | Where-Object { -not $_.Internal })
    if ($ext.Count -eq 0) { $L.Add('    Khong doc duoc man hinh nao (man hinh tat, cam qua bo chia / KVM, hoac cap VGA)') }
    foreach ($m in $ext) {
        $co = Round-Inch $m.W $m.H $sizes
        $L.Add(('    - {0} | Hang: {1} | Co: {2} | Do phan giai: {3} | Serial: {4} | Nam SX: {5}' -f `
            $(if ($m.Model) { $m.Model } else { 'Khong ro model' }), (Clean $m.Brand), (Clean $co), (Clean $m.Res), (Clean $m.Serial), (Clean $m.Year)))
        $monitorList += [ordered]@{ hang = $m.Brand; model = $m.Model; co = $co; do_phan_giai = $m.Res; serial = $m.Serial; nam_sx = $m.Year }
    }
}

$L.Add('')
$L.Add($sep)

$desktop = $desktopArg
if (-not $desktop -or -not (Test-Path $desktop)) { $desktop = [Environment]::GetFolderPath('Desktop') }

# Thu ghi ra Desktop; neu bi chan (antivirus / Controlled Folder Access) thi ghi sang thu muc du phong
$saved = $null; $errs = @()
foreach ($dir in @($desktop, (Join-Path $env:PUBLIC 'Desktop'), 'C:\')) {
    $file = Join-Path $dir 'thong tin cau hinh.txt'
    try {
        [IO.File]::WriteAllLines($file, $L.ToArray(), (New-Object Text.UTF8Encoding $true))
        $saved = $file; break
    } catch { $errs += "$dir : $($_.Exception.InnerException.Message)" }
}

Write-Host ''
if ($saved) {
    Write-Host "Da xuat file: $saved" -ForegroundColor Green
    if ($saved -notlike "$desktop*") {
        Write-Host 'Khong ghi duoc ra Desktop (co the do phan mem diet virus chan), file da duoc luu o vi tri tren.' -ForegroundColor Yellow
        $errs | ForEach-Object { Write-Host "  $_" -ForegroundColor DarkGray }
        Read-Host 'Nhan Enter de dong'
    }
    Start-Process notepad.exe $saved
} else {
    Write-Host '[LOI] Khong ghi duoc file ket qua:' -ForegroundColor Red
    $errs | ForEach-Object { Write-Host "  $_" -ForegroundColor Red }
    Write-Host 'Vui long chup man hinh nay gui cho bo phan IT.' -ForegroundColor Yellow
    Read-Host 'Nhan Enter de dong'
}

# ---- Cua so khai bao nguoi dung (Ho ten + Phong ban lay tu web). Chu tieng Viet viet dang \uXXXX vi file phai ASCII ----
function U($s) { [regex]::Unescape($s) }
function Get-PhongBan {
    $u = $QltsUrl -replace 'cau-hinh/?$', 'danh-muc/'
    $r = Invoke-WebRequest -Uri $u -UseBasicParsing -TimeoutSec 15 -Headers @{ Authorization = "Token $QltsToken" } -ErrorAction Stop
    $txt = [Text.Encoding]::UTF8.GetString($r.RawContentStream.ToArray())   # doc UTF-8 cho dung dau tieng Viet
    return @(($txt | ConvertFrom-Json).phong_ban)
}
function Show-KhaiBao($ds) {
    Add-Type -AssemblyName System.Windows.Forms
    Add-Type -AssemblyName System.Drawing
    $f = New-Object Windows.Forms.Form
    $f.Text = U 'Th\u00f4ng tin ng\u01b0\u1eddi s\u1eed d\u1ee5ng m\u00e1y'; $f.Size = New-Object Drawing.Size(520, 270); $f.StartPosition = 'CenterScreen'
    $f.TopMost = $true; $f.FormBorderStyle = 'FixedDialog'; $f.MaximizeBox = $false; $f.MinimizeBox = $false
    $f.Font = New-Object Drawing.Font('Segoe UI', 10)
    $l0 = New-Object Windows.Forms.Label; $l0.Text = U 'C\u1ea5u h\u00ecnh m\u00e1y s\u1ebd \u0111\u01b0\u1ee3c g\u1eedi l\u00ean web qu\u1ea3n l\u00fd t\u00e0i s\u1ea3n IT. Vui l\u00f2ng nh\u1eadp th\u00f4ng tin ng\u01b0\u1eddi \u0111ang d\u00f9ng m\u00e1y n\u00e0y.'; $l0.SetBounds(16, 12, 480, 40)
    $l1 = New-Object Windows.Forms.Label; $l1.Text = U 'H\u1ecd v\u00e0 t\u00ean:'; $l1.SetBounds(16, 62, 140, 24)
    $tb = New-Object Windows.Forms.TextBox; $tb.SetBounds(160, 60, 330, 26)
    $l2 = New-Object Windows.Forms.Label; $l2.Text = U 'Ph\u00f2ng ban / \u0110\u01a1n v\u1ecb:'; $l2.SetBounds(16, 102, 140, 24)
    $cb = New-Object Windows.Forms.ComboBox; $cb.SetBounds(160, 100, 330, 26); $cb.DropDownStyle = 'DropDownList'
    foreach ($d in $ds) { [void]$cb.Items.Add(('{0} - {1} ({2})' -f $d.ma, $d.ten, $d.chi_nhanh)) }
    $ok = New-Object Windows.Forms.Button; $ok.Text = U 'G\u1eedi l\u00ean web'; $ok.SetBounds(290, 160, 120, 34)
    $no = New-Object Windows.Forms.Button; $no.Text = U 'B\u1ecf qua'; $no.SetBounds(416, 160, 74, 34)
    $no.DialogResult = 'Cancel'; $f.CancelButton = $no; $f.AcceptButton = $ok
    $ok.Add_Click({
        if ($tb.Text.Trim() -and $cb.SelectedIndex -ge 0) { $f.DialogResult = 'OK'; $f.Close() }
        else { [void][Windows.Forms.MessageBox]::Show((U 'Vui l\u00f2ng nh\u1eadp h\u1ecd t\u00ean v\u00e0 ch\u1ecdn ph\u00f2ng ban.'), (U 'Th\u00f4ng tin ng\u01b0\u1eddi s\u1eed d\u1ee5ng m\u00e1y')) }
    })
    $f.Controls.AddRange(@($l0, $l1, $tb, $l2, $cb, $ok, $no))
    $f.Add_Shown({ $f.Activate(); $tb.Focus() })
    if ($f.ShowDialog() -eq 'OK') {
        return @{ ho_ten = $tb.Text.Trim(); phong_ban_id = [string]$ds[$cb.SelectedIndex].id }
    }
    return $null
}
# ---- Gui len web (chi chay khi da dien $QltsUrl va $QltsToken o dau phan PowerShell) ----
if ($QltsUrl -and $QltsToken) {
    try {
        [Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
        # Hoi ho ten + phong ban (danh sach lay tu web). Bo qua -> van gui cau hinh, khong khai bao nguoi dung
        $decl = $null
        try { $decl = Show-KhaiBao (Get-PhongBan) } catch {
            Write-Host ('Khong lay duoc danh sach phong ban tu web: ' + $_.Exception.Message) -ForegroundColor Yellow }
        $diskList = if ($pd.Count -gt 0) { $pd | ForEach-Object { (Clean $_.FriendlyName) + ' - ' + (GB $_.Size) + ' ' + (Clean $_.MediaType) } }
                    else { Get-WmiObject Win32_DiskDrive | ForEach-Object { (Clean $_.Model) + ' - ' + (GB $_.Size) } }
        $macList = Get-WmiObject Win32_NetworkAdapter -Filter 'PhysicalAdapter=True' |
                   Where-Object { $_.MACAddress -and $_.PNPDeviceID -notlike 'ROOT\*' -and $_.PNPDeviceID -notlike 'SWD\*' } |
                   ForEach-Object { (Clean $_.Name) + ': ' + $_.MACAddress.Replace(':', '-') }
        $body = [ordered]@{
            loai_may      = $(if ($isLaptop) { 'Laptop' } else { 'Desktop' })
            hang          = "$brand"
            model         = "$model"
            serial        = $(if ($isLaptop) { Real-Serial $bios.SerialNumber } else {
                                $sb = Real-Serial $board.SerialNumber; if ($sb) { $sb } else { Real-Serial $bios.SerialNumber } })
            cpu           = (@(Get-WmiObject Win32_Processor | ForEach-Object { Clean $_.Name }) -join "`n")
            ram           = (GB $cs.TotalPhysicalMemory)
            gpu           = (@(Get-WmiObject Win32_VideoController | ForEach-Object { Clean $_.Name }) -join "`n")
            screen        = "$screenText"
            man_hinh_roi  = @($monitorList)
            disk          = (@($diskList) -join "`n")
            os_name       = (Clean $os.Caption) + ' (' + (Clean $os.OSArchitecture) + ')'
            computer_name = "$env:COMPUTERNAME"
            oem_key       = "$oemKey"
            oem_edition   = $(if ($oemKey) { "$oemEdition" } else { '' })
            os_edition    = "$editionId"
            installed_key = (Get-InstalledKey)
            mac_address   = (@($macList) -join "`n")
            nguoi_khai_bao = $decl
        }
        $json = $body | ConvertTo-Json
        $resp = Invoke-RestMethod -Uri $QltsUrl -Method Post -TimeoutSec 20 -ContentType 'application/json; charset=utf-8' `
                -Headers @{ Authorization = "Token $QltsToken" } -Body ([Text.Encoding]::UTF8.GetBytes($json)) -ErrorAction Stop
        if ($resp.ket_qua -eq 'da_tao_moi') { Write-Host ('Da tao tai san moi tren web: ' + $resp.ma_tai_san + ' (cho bo phan IT xac nhan)') -ForegroundColor Green }
        elseif ($resp.ma_tai_san) { Write-Host ('Da cap nhat cau hinh len web, ma tai san: ' + $resp.ma_tai_san) -ForegroundColor Green }
        else { Write-Host 'Da gui cau hinh len web. Bo phan IT se gan vao tai san.' -ForegroundColor Green }
        Start-Sleep 3
    } catch {
        Write-Host ('Khong gui duoc len web (file txt van da xuat binh thuong): ' + $_.Exception.Message) -ForegroundColor Yellow
        Start-Sleep 6
    }
}
