- {canReserve
- ? "Você não possui nenhum veículo reservado no momento."
- : "Você pode visualizar os veículos disponíveis, mas não possui permissão para fazer reservas."
- }
+
+ {/* Catálogo de veículos disponíveis para reserva */}
+
+
+
+
+ Veículos disponíveis para reserva ({filteredVehicles.length})
+
+
+
+
+
+
+
+ {isLoadingVehicles ? (
+
+
+
Carregando veículos...
+
+ ) : filteredVehicles.length ? (
+
+ {filteredVehicles.map((vehicle) => (
+
+
+
+
+
+
+
+
{vehicle.model}
+
{vehicle.plate}
+
+ {vehicle.enterprise}
+
+
+
+ {Number(vehicle.kilometers).toLocaleString()} km
+
+ Não há veículos disponíveis para o filtro selecionado.
+
+
+ )}
+
+
+
+ {!canReserve && (
+
+
+
Visualização de Veículos
+
+ Você pode visualizar os veículos disponíveis, mas não possui permissão para fazer reservas.
- {canReserve && (
-
- Reservar um veículo
-
- )}
)}
+
+ {/* Modal de reserva: pede o intervalo e aponta disponibilidade */}
+
+
{/* Modal de edição de reserva */}
+
+ {/* Link auxiliar para a visão de catálogo dedicada (filtro por data/horário) */}
+ {canReserve && (
+
+
+ Ver catálogo com filtro por data e horário específicos
+
+
+ )}
)
}
-
diff --git a/src/components/admin/vehicles/manual-rent-form.tsx b/src/components/admin/vehicles/manual-rent-form.tsx
new file mode 100644
index 00000000..2f070bc0
--- /dev/null
+++ b/src/components/admin/vehicles/manual-rent-form.tsx
@@ -0,0 +1,234 @@
+"use client"
+
+import { useMemo, useState } from "react"
+import { Loader2 } from "lucide-react"
+
+import { api } from "@/trpc/react"
+import { toast } from "sonner"
+import { Button } from "@/components/ui/button"
+import { Input } from "@/components/ui/input"
+import { Label } from "@/components/ui/label"
+import { Switch } from "@/components/ui/switch"
+import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
+import { DateTimePicker } from "@/components/ui/date-time-picker"
+import { EmpresaFilialFilter, type EmpresaFilialValue } from "@/components/ui/empresa-filial-filter"
+
+interface ManualRentFormProps {
+ /** Chamado após criar o agendamento com sucesso (ex.: fechar o modal). */
+ onSuccess: () => void
+ onCancel: () => void
+}
+
+/**
+ * Agendamento manual feito por um admin em nome de outro usuário.
+ * Reaproveita o filtro empresa→filial e os componentes do Design System.
+ */
+export function ManualRentForm({ onSuccess, onCancel }: ManualRentFormProps) {
+ // Usuário alvo do agendamento
+ const [searchQuery, setSearchQuery] = useState("")
+ const [selectedUser, setSelectedUser] = useState<{ id: string; firstName: string | null; lastName: string | null; email: string } | null>(null)
+
+ // Filtro de frota (padrão novo) + veículo escolhido
+ const [empresaFilial, setEmpresaFilial] = useState({ empresaId: "", filialId: "" })
+ const [vehicleId, setVehicleId] = useState("")
+
+ // Datas e dados da viagem
+ const [isScheduled, setIsScheduled] = useState(false)
+ const [scheduledDate, setScheduledDate] = useState(undefined)
+ const [endDate, setEndDate] = useState(undefined)
+ const [driver, setDriver] = useState("")
+ const [passangers, setPassangers] = useState("")
+ const [destiny, setDestiny] = useState("")
+
+ const { data: users, isLoading: isLoadingUsers } = api.user.listForChat.useQuery(
+ { search: searchQuery },
+ { enabled: searchQuery.length > 2 },
+ )
+
+ const { data: vehiclesData } = api.vehicle.getAll.useQuery({ limit: 100, availble: true })
+
+ // Veículos disponíveis filtrados por empresa/filial (padrão novo).
+ const vehicles = useMemo(() => {
+ return (vehiclesData?.items ?? []).filter((vehicle) => {
+ if (empresaFilial.filialId) return vehicle.filialId === empresaFilial.filialId
+ if (empresaFilial.empresaId) return vehicle.filial?.empresa.id === empresaFilial.empresaId
+ return true
+ })
+ }, [vehiclesData, empresaFilial])
+
+ const createForUser = api.vehicleRent.createForUser.useMutation({
+ onSuccess: () => {
+ toast.success("Agendamento criado com sucesso!")
+ onSuccess()
+ },
+ onError: (error) => {
+ toast.error("Erro ao criar agendamento: " + error.message)
+ },
+ })
+
+ const handleSubmit = (e: React.FormEvent) => {
+ e.preventDefault()
+
+ if (!selectedUser) {
+ toast.error("Selecione o usuário do agendamento.")
+ return
+ }
+ if (!vehicleId) {
+ toast.error("Selecione o veículo.")
+ return
+ }
+ if (isScheduled && (!scheduledDate || scheduledDate <= new Date())) {
+ toast.error("A data de agendamento deve ser posterior à data atual.")
+ return
+ }
+ if (!endDate || endDate <= new Date() || (scheduledDate && endDate <= scheduledDate)) {
+ toast.error("A data de devolução deve ser posterior à data atual ou à data de agendamento.")
+ return
+ }
+ if (driver.trim().length <= 2) {
+ toast.error("Informe o motorista (mínimo 3 caracteres).")
+ return
+ }
+ if (destiny.trim().length <= 2) {
+ toast.error("Informe o destino (mínimo 3 caracteres).")
+ return
+ }
+
+ createForUser.mutate({
+ userId: selectedUser.id,
+ vehicleId,
+ possibleEnd: endDate,
+ startDate: isScheduled ? scheduledDate : undefined,
+ driver,
+ destiny,
+ passangers: passangers || undefined,
+ })
+ }
+
+ return (
+
+ )
+}
diff --git a/src/components/admin/vehicles/vehicle-form.tsx b/src/components/admin/vehicles/vehicle-form.tsx
index 2777d6cb..99068ea8 100644
--- a/src/components/admin/vehicles/vehicle-form.tsx
+++ b/src/components/admin/vehicles/vehicle-form.tsx
@@ -59,29 +59,19 @@ export function VehicleForm({ vehicle, onSuccess, onCancel }: VehicleFormProps)
const filialId = watch("filialId")
- // Filiais da empresa selecionada
+ // Empresa efetiva (fonte única da verdade): a escolha explícita do usuário tem
+ // precedência; senão, deriva da filial salva/selecionada. Assim, ao editar um
+ // veículo a empresa aparece preenchida assim que `filiaisData` carrega, sem
+ // depender de efeitos que poderiam apagar a filial salva.
+ const empresaFromFilial = filiaisData.find((f) => f.id === filialId)?.empresa.id ?? ""
+ const effectiveEmpresaId = empresaId !== "" ? empresaId : empresaFromFilial
+
+ // Filiais da empresa efetiva
const filiais = useMemo(
- () => filiaisData.filter((f) => f.empresa.id === empresaId),
- [filiaisData, empresaId],
+ () => filiaisData.filter((f) => f.empresa.id === effectiveEmpresaId),
+ [filiaisData, effectiveEmpresaId],
)
- // Ao editar, pré-seleciona a empresa a partir da filial atual do veículo.
- useEffect(() => {
- if (vehicle?.filialId && filiaisData.length > 0) {
- const current = filiaisData.find((f) => f.id === vehicle.filialId)
- if (current) setEmpresaId(current.empresa.id)
- }
- }, [vehicle?.filialId, filiaisData])
-
- // Ao trocar de empresa, limpa a filial que não pertence mais à empresa.
- useEffect(() => {
- if (!filialId) return
- const allowed = new Set(filiais.map((f) => f.id))
- if (!allowed.has(filialId)) {
- setValue("filialId", "")
- }
- }, [filiais, filialId, setValue])
-
// Atualizar o campo imageUrl quando uma nova imagem for enviada
useEffect(() => {
if (uploadedImageUrl) {
@@ -169,7 +159,7 @@ export function VehicleForm({ vehicle, onSuccess, onCancel }: VehicleFormProps)