Add equipment loan feature and USB bulk control

- Add emprestimos (equipment loan) module in Convex with queries/mutations
- Create emprestimos page with full CRUD and status tracking
- Add USB bulk control to admin devices overview
- Fix Portuguese accents in USB policy control component
- Fix dead code warnings in Rust agent
- Fix tiptap type error in rich text editor
This commit is contained in:
rever-tecnologia 2025-12-04 14:23:58 -03:00
parent 49aa143a80
commit 063c5dfde7
11 changed files with 1448 additions and 26 deletions

View file

@ -27,6 +27,18 @@ import { ptBR } from "date-fns/locale"
type UsbPolicyValue = "ALLOW" | "BLOCK_ALL" | "READONLY"
interface UsbPolicyEvent {
id: string
oldPolicy?: string
newPolicy: string
status: string
error?: string
actorEmail?: string
actorName?: string
createdAt: number
appliedAt?: number
}
const POLICY_OPTIONS: Array<{ value: UsbPolicyValue; label: string; description: string; icon: typeof Shield }> = [
{
value: "ALLOW",
@ -123,7 +135,7 @@ export function UsbPolicyControl({
const handleApplyPolicy = async () => {
if (selectedPolicy === usbPolicy?.policy) {
toast.info("A politica selecionada ja esta aplicada.")
toast.info("A política selecionada já está aplicada.")
return
}
@ -136,10 +148,10 @@ export function UsbPolicyControl({
actorEmail,
actorName,
})
toast.success("Politica USB enviada para aplicacao.")
toast.success("Política USB enviada para aplicação.")
} catch (error) {
console.error("[usb-policy] Falha ao aplicar politica", error)
toast.error("Falha ao aplicar politica USB. Tente novamente.")
console.error("[usb-policy] Falha ao aplicar política", error)
toast.error("Falha ao aplicar política USB. Tente novamente.")
} finally {
setIsApplying(false)
}
@ -179,21 +191,21 @@ export function UsbPolicyControl({
{usbPolicy?.error && (
<div className="rounded-lg border border-red-200 bg-red-50 p-3">
<p className="text-sm font-medium text-red-700">Erro na aplicacao</p>
<p className="text-sm font-medium text-red-700">Erro na aplicação</p>
<p className="text-xs text-red-600">{usbPolicy.error}</p>
</div>
)}
<div className="flex items-end gap-2">
<div className="flex-1 space-y-1.5">
<label className="text-xs font-medium text-muted-foreground">Alterar politica</label>
<label className="text-xs font-medium text-muted-foreground">Alterar política</label>
<Select
value={selectedPolicy}
onValueChange={(value) => setSelectedPolicy(value as UsbPolicyValue)}
disabled={disabled || isApplying}
>
<SelectTrigger>
<SelectValue placeholder="Selecione uma politica" />
<SelectValue placeholder="Selecione uma política" />
</SelectTrigger>
<SelectContent>
{POLICY_OPTIONS.map((option) => {
@ -230,8 +242,8 @@ export function UsbPolicyControl({
</TooltipTrigger>
<TooltipContent>
{selectedPolicy === usbPolicy?.policy
? "A politica ja esta aplicada"
: `Aplicar politica "${getPolicyConfig(selectedPolicy).label}"`}
? "A política já está aplicada"
: `Aplicar política "${getPolicyConfig(selectedPolicy).label}"`}
</TooltipContent>
</Tooltip>
</TooltipProvider>
@ -245,17 +257,17 @@ export function UsbPolicyControl({
onClick={() => setShowHistory(!showHistory)}
>
<History className="size-4" />
{showHistory ? "Ocultar historico" : "Ver historico de alteracoes"}
{showHistory ? "Ocultar histórico" : "Ver histórico de alterações"}
</Button>
{showHistory && policyEvents && (
<div className="mt-3 space-y-2">
{policyEvents.length === 0 ? (
<p className="text-center text-xs text-muted-foreground py-2">
Nenhuma alteracao registrada
Nenhuma alteração registrada
</p>
) : (
policyEvents.map((event) => (
policyEvents.map((event: UsbPolicyEvent) => (
<div
key={event.id}
className="flex items-start justify-between rounded-md border bg-white p-2 text-xs"
@ -287,7 +299,7 @@ export function UsbPolicyControl({
{usbPolicy?.reportedAt && (
<p className="text-xs text-muted-foreground">
Ultimo relato do agente: {formatEventDate(usbPolicy.reportedAt)}
Último relato do agente: {formatEventDate(usbPolicy.reportedAt)}
</p>
)}
</CardContent>