Improve loan page and add company filter to USB bulk control
- Update Next.js to 16.0.7 - Fix accent on menu item "Emprestimos" to "Empréstimos" - Standardize loan page with project patterns (DateRangeButton, cyan color scheme, ToggleGroup) - Add company filter to USB bulk policy dialog - Update CardDescription text in devices overview - Fix useEffect dependency warning in desktop main.tsx 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
063c5dfde7
commit
38995b95c6
8 changed files with 1123 additions and 1184 deletions
|
|
@ -1269,6 +1269,7 @@ export function AdminDevicesOverview({
|
|||
const [selectedUsbPolicy, setSelectedUsbPolicy] = useState<"ALLOW" | "BLOCK_ALL" | "READONLY">("ALLOW")
|
||||
const [isApplyingUsbPolicy, setIsApplyingUsbPolicy] = useState(false)
|
||||
const [usbPolicySelection, setUsbPolicySelection] = useState<string[]>([])
|
||||
const [usbCompanyFilter, setUsbCompanyFilter] = useState<string>("all")
|
||||
const [isCreateDeviceOpen, setIsCreateDeviceOpen] = useState(false)
|
||||
const [createDeviceLoading, setCreateDeviceLoading] = useState(false)
|
||||
const [newDeviceName, setNewDeviceName] = useState("")
|
||||
|
|
@ -1713,15 +1714,18 @@ export function AdminDevicesOverview({
|
|||
}, [])
|
||||
|
||||
const handleSelectAllUsbDevices = useCallback((checked: boolean) => {
|
||||
const windowsDevices = filteredDevices.filter(
|
||||
const allWindowsDevices = filteredDevices.filter(
|
||||
(m) => (m.devicePlatform ?? "").toLowerCase() === "windows"
|
||||
)
|
||||
const windowsDevices = usbCompanyFilter === "all"
|
||||
? allWindowsDevices
|
||||
: allWindowsDevices.filter((m) => (m.companySlug ?? "") === usbCompanyFilter)
|
||||
if (checked) {
|
||||
setUsbPolicySelection(windowsDevices.map((m) => m.id))
|
||||
} else {
|
||||
setUsbPolicySelection([])
|
||||
}
|
||||
}, [filteredDevices])
|
||||
}, [filteredDevices, usbCompanyFilter])
|
||||
|
||||
const handleApplyBulkUsbPolicy = useCallback(async () => {
|
||||
if (usbPolicySelection.length === 0) {
|
||||
|
|
@ -1853,7 +1857,7 @@ export function AdminDevicesOverview({
|
|||
<CardHeader className="flex flex-col gap-3 md:flex-row md:items-start md:justify-between">
|
||||
<div>
|
||||
<CardTitle>Dispositivos registrados</CardTitle>
|
||||
<CardDescription>Sincronizadas via agente local instalado nas máquinas. Atualiza em tempo quase real.</CardDescription>
|
||||
<CardDescription>Sincronizadas via agente local instalado nas máquinas.</CardDescription>
|
||||
</div>
|
||||
<div className="flex flex-wrap items-center gap-2">
|
||||
<Button size="sm" onClick={handleOpenCreateDevice}>
|
||||
|
|
@ -2199,20 +2203,55 @@ export function AdminDevicesOverview({
|
|||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
{(() => {
|
||||
const windowsDevices = filteredDevices.filter(
|
||||
const allWindowsDevices = filteredDevices.filter(
|
||||
(m) => (m.devicePlatform ?? "").toLowerCase() === "windows"
|
||||
)
|
||||
if (windowsDevices.length === 0) {
|
||||
const windowsDevices = usbCompanyFilter === "all"
|
||||
? allWindowsDevices
|
||||
: allWindowsDevices.filter((m) => (m.companySlug ?? "") === usbCompanyFilter)
|
||||
const usbCompanyOptions = Array.from(
|
||||
new Map(
|
||||
allWindowsDevices
|
||||
.filter((m) => m.companySlug)
|
||||
.map((m) => [m.companySlug, m.companyName ?? m.companySlug])
|
||||
)
|
||||
).sort((a, b) => (a[1] ?? "").localeCompare(b[1] ?? "", "pt-BR"))
|
||||
if (allWindowsDevices.length === 0) {
|
||||
return (
|
||||
<div className="rounded-md border border-dashed border-slate-200 px-4 py-8 text-center text-sm text-muted-foreground">
|
||||
Nenhum dispositivo Windows disponível com os filtros atuais.
|
||||
</div>
|
||||
)
|
||||
}
|
||||
const allSelected = usbPolicySelection.length === windowsDevices.length
|
||||
const allSelected = windowsDevices.length > 0 && usbPolicySelection.length === windowsDevices.length
|
||||
const someSelected = usbPolicySelection.length > 0 && usbPolicySelection.length < windowsDevices.length
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-700">Filtrar por empresa</label>
|
||||
<Select
|
||||
value={usbCompanyFilter}
|
||||
onValueChange={(value) => {
|
||||
setUsbCompanyFilter(value)
|
||||
setUsbPolicySelection([])
|
||||
}}
|
||||
disabled={isApplyingUsbPolicy}
|
||||
>
|
||||
<SelectTrigger>
|
||||
<SelectValue placeholder="Selecione uma empresa" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">
|
||||
<span>Todas as empresas</span>
|
||||
</SelectItem>
|
||||
{usbCompanyOptions.map(([slug, name]) => (
|
||||
<SelectItem key={slug} value={slug ?? ""}>
|
||||
<span>{name}</span>
|
||||
</SelectItem>
|
||||
))}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex items-center justify-between rounded-md border border-slate-200 bg-slate-50 px-3 py-2 text-xs text-slate-600">
|
||||
<span>
|
||||
{usbPolicySelection.length} de {windowsDevices.length} dispositivos Windows selecionados
|
||||
|
|
@ -2220,9 +2259,14 @@ export function AdminDevicesOverview({
|
|||
<Checkbox
|
||||
checked={allSelected ? true : someSelected ? "indeterminate" : false}
|
||||
onCheckedChange={(value) => handleSelectAllUsbDevices(value === true || value === "indeterminate")}
|
||||
disabled={isApplyingUsbPolicy}
|
||||
disabled={isApplyingUsbPolicy || windowsDevices.length === 0}
|
||||
/>
|
||||
</div>
|
||||
{windowsDevices.length === 0 ? (
|
||||
<div className="rounded-md border border-dashed border-slate-200 px-4 py-6 text-center text-sm text-muted-foreground">
|
||||
Nenhum dispositivo Windows nesta empresa.
|
||||
</div>
|
||||
) : (
|
||||
<div className="max-h-48 space-y-1 overflow-y-auto rounded-md border border-slate-200 p-2">
|
||||
{windowsDevices.map((device) => {
|
||||
const checked = usbPolicySelection.includes(device.id)
|
||||
|
|
@ -2249,6 +2293,7 @@ export function AdminDevicesOverview({
|
|||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
<div className="space-y-2">
|
||||
<label className="text-sm font-medium text-slate-700">Política USB</label>
|
||||
<Select
|
||||
|
|
|
|||
|
|
@ -88,7 +88,7 @@ const navigation: NavigationGroup[] = [
|
|||
{ title: "Modo Play", url: "/play", icon: PlayCircle, requiredRole: "staff" },
|
||||
{ title: "Agenda", url: "/agenda", icon: CalendarDays, requiredRole: "staff" },
|
||||
{ title: "Dispositivos", url: "/admin/devices", icon: MonitorCog, requiredRole: "admin" },
|
||||
{ title: "Emprestimos", url: "/emprestimos", icon: Package, requiredRole: "staff" },
|
||||
{ title: "Empréstimos", url: "/emprestimos", icon: Package, requiredRole: "staff" },
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue