Move USB policy control to modal dialog
- Add USB modal state and clickable InfoChip for USB policy chip - Create Dialog with UsbPolicyControl component for USB management - Add variant prop to UsbPolicyControl (card/inline) for flexible rendering - Remove inline UsbPolicyControl from bottom of device page - USB control now accessible by clicking USB chip in device summary 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
parent
6007cf6740
commit
06ebad930c
2 changed files with 183 additions and 140 deletions
|
|
@ -3129,7 +3129,7 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
const hasRemoteAccess = remoteAccessEntries.length > 0
|
||||
|
||||
const summaryChips = useMemo(() => {
|
||||
const chips: Array<{ key: string; label: string; value: string; icon: ReactNode; tone?: "warning" | "muted" }> = []
|
||||
const chips: Array<{ key: string; label: string; value: string; icon: ReactNode; tone?: "warning" | "muted"; onClick?: () => void }> = []
|
||||
const osName = osNameDisplay || "Sistema desconhecido"
|
||||
const osVersionRaw = device?.osVersion ?? windowsVersionLabel ?? ""
|
||||
const osVersion = formatOsVersionDisplay(osNameDisplay, osVersionRaw)
|
||||
|
|
@ -3259,6 +3259,7 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
const [togglingActive, setTogglingActive] = useState(false)
|
||||
const [isResettingAgent, setIsResettingAgent] = useState(false)
|
||||
const [showAllWindowsSoftware, setShowAllWindowsSoftware] = useState(false)
|
||||
const [isUsbModalOpen, setIsUsbModalOpen] = useState(false)
|
||||
const jsonText = useMemo(() => {
|
||||
const payload = {
|
||||
id: device?.id,
|
||||
|
|
@ -3927,10 +3928,40 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
{/* ping integrado na badge de status */}
|
||||
<div className="grid gap-3 sm:grid-cols-2 xl:grid-cols-3">
|
||||
{summaryChips.map((chip) => (
|
||||
<InfoChip key={chip.key} label={chip.label} value={chip.value} icon={chip.icon} tone={chip.tone} />
|
||||
<InfoChip
|
||||
key={chip.key}
|
||||
label={chip.label}
|
||||
value={chip.value}
|
||||
icon={chip.icon}
|
||||
tone={chip.tone}
|
||||
onClick={chip.key === "usb-policy" && canManageRemoteAccess ? () => setIsUsbModalOpen(true) : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Modal de Controle USB */}
|
||||
<Dialog open={isUsbModalOpen} onOpenChange={setIsUsbModalOpen}>
|
||||
<DialogContent className="max-w-xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Usb className="size-5" />
|
||||
Controle de USB - {device.displayName ?? device.hostname}
|
||||
</DialogTitle>
|
||||
<DialogDescription>
|
||||
Gerencie as politicas de acesso a dispositivos USB de armazenamento
|
||||
</DialogDescription>
|
||||
</DialogHeader>
|
||||
<UsbPolicyControl
|
||||
machineId={device.id}
|
||||
machineName={device.displayName ?? device.hostname}
|
||||
actorEmail={primaryLinkedUser?.email}
|
||||
actorName={primaryLinkedUser?.name}
|
||||
disabled={isDeactivated}
|
||||
variant="inline"
|
||||
/>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
|
||||
<div className="rounded-2xl border border-slate-200 bg-white/80 px-4 py-4 shadow-sm">
|
||||
<div className="flex flex-wrap items-center justify-between gap-3">
|
||||
<p className="text-xs font-semibold uppercase tracking-wide text-slate-500">Controles do dispositivo</p>
|
||||
|
|
@ -5257,16 +5288,6 @@ export function DeviceDetails({ device }: DeviceDetailsProps) {
|
|||
) : null}
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{canManageRemoteAccess && device?.osName?.toLowerCase().includes("windows") ? (
|
||||
<UsbPolicyControl
|
||||
machineId={device.id}
|
||||
machineName={device.displayName ?? device.hostname}
|
||||
actorEmail={primaryLinkedUser?.email}
|
||||
actorName={primaryLinkedUser?.name}
|
||||
disabled={isDeactivated}
|
||||
/>
|
||||
) : null}
|
||||
</div>
|
||||
|
||||
{windowsCpuDetails.length > 0 ? (
|
||||
|
|
@ -6196,7 +6217,7 @@ function DetailLine({ label, value, classNameValue, layout = "spread" }: DetailL
|
|||
)
|
||||
}
|
||||
|
||||
function InfoChip({ label, value, icon, tone = "default" }: { label: string; value: string; icon?: ReactNode; tone?: "default" | "warning" | "muted" }) {
|
||||
function InfoChip({ label, value, icon, tone = "default", onClick }: { label: string; value: string; icon?: ReactNode; tone?: "default" | "warning" | "muted"; onClick?: () => void }) {
|
||||
const toneClasses =
|
||||
tone === "warning"
|
||||
? "border-amber-200 bg-amber-50 text-amber-700"
|
||||
|
|
@ -6204,8 +6225,16 @@ function InfoChip({ label, value, icon, tone = "default" }: { label: string; val
|
|||
? "border-slate-200 bg-slate-50 text-neutral-600"
|
||||
: "border-slate-200 bg-white text-neutral-800"
|
||||
|
||||
const clickableClasses = onClick ? "cursor-pointer hover:ring-2 hover:ring-blue-200 transition-all" : ""
|
||||
|
||||
return (
|
||||
<div className={cn("flex items-center gap-3 rounded-xl border px-3 py-2 shadow-sm", toneClasses)}>
|
||||
<div
|
||||
className={cn("flex items-center gap-3 rounded-xl border px-3 py-2 shadow-sm", toneClasses, clickableClasses)}
|
||||
onClick={onClick}
|
||||
role={onClick ? "button" : undefined}
|
||||
tabIndex={onClick ? 0 : undefined}
|
||||
onKeyDown={onClick ? (e) => { if (e.key === "Enter" || e.key === " ") onClick() } : undefined}
|
||||
>
|
||||
{icon ? <span className="text-neutral-500">{icon}</span> : null}
|
||||
<div className="min-w-0 leading-tight">
|
||||
<p className="text-xs uppercase text-neutral-500">{label}</p>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue