From 31e2f64888b6dd83241736a434dedb2141b2d957 Mon Sep 17 00:00:00 2001 From: "daivid.alves" Date: Thu, 26 Feb 2026 19:50:21 -0300 Subject: [PATCH] Removendo OestPan --- .../components/AdvancedFiltersModal.jsx | 138 -- .../Oest_Pan/components/AutocompleteInput.jsx | 123 -- .../Oest_Pan/components/ExcelTable.jsx | 459 ---- .../components/FeedbackNotification.jsx | 115 - .../components/FinesCard/FinesCard.debug.jsx | 174 -- .../components/FinesCard/FinesCard.jsx | 267 --- .../Oest_Pan/components/FinesCard/index.js | 5 - .../Oest_Pan/components/PrafrotSidebar.css | 404 ---- .../Oest_Pan/components/PrafrotSidebar.jsx | 249 --- .../Oest_Pan/hooks/useAvailability.js | 76 - src/features/Oest_Pan/hooks/useClaims.js | 78 - src/features/Oest_Pan/hooks/useDispatcher.js | 31 - src/features/Oest_Pan/hooks/useDrivers.js | 27 - src/features/Oest_Pan/hooks/useFleetLists.js | 110 - src/features/Oest_Pan/hooks/useMaintenance.js | 262 --- src/features/Oest_Pan/hooks/useMoki.js | 76 - src/features/Oest_Pan/hooks/useMonitoring.js | 76 - .../Oest_Pan/hooks/usePrafrotStatistics.js | 95 - src/features/Oest_Pan/hooks/useStatus.js | 106 - src/features/Oest_Pan/hooks/useVehicles.js | 92 - src/features/Oest_Pan/hooks/useWorkshops.js | 76 - .../Oest_Pan/layout/PrafrotLayout.jsx | 92 - src/features/Oest_Pan/routes.jsx | 69 - .../Oest_Pan/services/dispatcherService.js | 17 - .../Oest_Pan/services/prafrotService.js | 410 ---- .../services/prafrotStatisticsService.js | 80 - src/features/Oest_Pan/utils/errorMessages.js | 129 -- .../Oest_Pan/views/AvailabilityView.jsx | 211 -- src/features/Oest_Pan/views/ClaimsView.jsx | 380 ---- src/features/Oest_Pan/views/ConfigView.jsx | 364 ---- src/features/Oest_Pan/views/DashboardView.jsx | 139 -- .../Oest_Pan/views/DispatcherView.jsx | 59 - src/features/Oest_Pan/views/DriversView.jsx | 92 - src/features/Oest_Pan/views/LoginView.jsx | 112 - .../Oest_Pan/views/MaintenanceView.jsx | 1936 ----------------- src/features/Oest_Pan/views/MokiView.jsx | 224 -- .../Oest_Pan/views/MonitoringView.jsx | 339 --- .../Oest_Pan/views/StatisticsView.jsx | 561 ----- src/features/Oest_Pan/views/StatusView.jsx | 349 --- src/features/Oest_Pan/views/TableDebug.jsx | 111 - src/features/Oest_Pan/views/VehiclesView.jsx | 548 ----- src/features/Oest_Pan/views/WorkshopsView.jsx | 221 -- 42 files changed, 9482 deletions(-) delete mode 100644 src/features/Oest_Pan/components/AdvancedFiltersModal.jsx delete mode 100644 src/features/Oest_Pan/components/AutocompleteInput.jsx delete mode 100644 src/features/Oest_Pan/components/ExcelTable.jsx delete mode 100644 src/features/Oest_Pan/components/FeedbackNotification.jsx delete mode 100644 src/features/Oest_Pan/components/FinesCard/FinesCard.debug.jsx delete mode 100644 src/features/Oest_Pan/components/FinesCard/FinesCard.jsx delete mode 100644 src/features/Oest_Pan/components/FinesCard/index.js delete mode 100644 src/features/Oest_Pan/components/PrafrotSidebar.css delete mode 100644 src/features/Oest_Pan/components/PrafrotSidebar.jsx delete mode 100644 src/features/Oest_Pan/hooks/useAvailability.js delete mode 100644 src/features/Oest_Pan/hooks/useClaims.js delete mode 100644 src/features/Oest_Pan/hooks/useDispatcher.js delete mode 100644 src/features/Oest_Pan/hooks/useDrivers.js delete mode 100644 src/features/Oest_Pan/hooks/useFleetLists.js delete mode 100644 src/features/Oest_Pan/hooks/useMaintenance.js delete mode 100644 src/features/Oest_Pan/hooks/useMoki.js delete mode 100644 src/features/Oest_Pan/hooks/useMonitoring.js delete mode 100644 src/features/Oest_Pan/hooks/usePrafrotStatistics.js delete mode 100644 src/features/Oest_Pan/hooks/useStatus.js delete mode 100644 src/features/Oest_Pan/hooks/useVehicles.js delete mode 100644 src/features/Oest_Pan/hooks/useWorkshops.js delete mode 100644 src/features/Oest_Pan/layout/PrafrotLayout.jsx delete mode 100644 src/features/Oest_Pan/routes.jsx delete mode 100644 src/features/Oest_Pan/services/dispatcherService.js delete mode 100644 src/features/Oest_Pan/services/prafrotService.js delete mode 100644 src/features/Oest_Pan/services/prafrotStatisticsService.js delete mode 100644 src/features/Oest_Pan/utils/errorMessages.js delete mode 100644 src/features/Oest_Pan/views/AvailabilityView.jsx delete mode 100644 src/features/Oest_Pan/views/ClaimsView.jsx delete mode 100644 src/features/Oest_Pan/views/ConfigView.jsx delete mode 100644 src/features/Oest_Pan/views/DashboardView.jsx delete mode 100644 src/features/Oest_Pan/views/DispatcherView.jsx delete mode 100644 src/features/Oest_Pan/views/DriversView.jsx delete mode 100644 src/features/Oest_Pan/views/LoginView.jsx delete mode 100644 src/features/Oest_Pan/views/MaintenanceView.jsx delete mode 100644 src/features/Oest_Pan/views/MokiView.jsx delete mode 100644 src/features/Oest_Pan/views/MonitoringView.jsx delete mode 100644 src/features/Oest_Pan/views/StatisticsView.jsx delete mode 100644 src/features/Oest_Pan/views/StatusView.jsx delete mode 100644 src/features/Oest_Pan/views/TableDebug.jsx delete mode 100644 src/features/Oest_Pan/views/VehiclesView.jsx delete mode 100644 src/features/Oest_Pan/views/WorkshopsView.jsx diff --git a/src/features/Oest_Pan/components/AdvancedFiltersModal.jsx b/src/features/Oest_Pan/components/AdvancedFiltersModal.jsx deleted file mode 100644 index 5714ba1..0000000 --- a/src/features/Oest_Pan/components/AdvancedFiltersModal.jsx +++ /dev/null @@ -1,138 +0,0 @@ -import React from 'react'; -import { X, Filter, Trash2, Check, ChevronDown } from 'lucide-react'; -import * as DialogPrimitive from '@radix-ui/react-dialog'; - -const AdvancedFiltersModal = ({ isOpen, onClose, onApply, options = {}, initialFilters = {}, config = [] }) => { - const [filters, setFilters] = React.useState(initialFilters); - - // Sync with parent when modal opens or initialFilters change - React.useEffect(() => { - if (isOpen) { - setFilters(initialFilters); - } - }, [isOpen, initialFilters]); - - const handleChange = (key, value) => { - setFilters(prev => ({ ...prev, [key]: value })); - }; - - const handleApply = () => { - onApply(filters); - }; - - const handleClear = () => { - setFilters({}); - }; - - const activeCount = Object.values(filters).filter(Boolean).length; - - return ( - - - - - - {/* Header */} -
-
- - -

Filtros Avançados

-
- - Selecione os filtros detalhados para refinar a busca de veículos. - - {activeCount > 0 && ( - - {activeCount} filtros ativos - - )} -
-
- - - - Close - -
-
- - {/* Body - Scrollable Form */} -
- {config.map((filter) => { - const label = filter.label || filter.field.replace(/_/g, ' ').toUpperCase(); - - if (filter.type === 'select') { - return ( -
- -
- -
- -
-
-
- ); - } - - if (filter.type === 'text') { - return ( -
- - handleChange(filter.field, e.target.value)} - /> -
- ); - } - - return null; - })} -
- - {/* Footer */} -
- - -
- -
-
-
- ); -}; - -export default AdvancedFiltersModal; - - - diff --git a/src/features/Oest_Pan/components/AutocompleteInput.jsx b/src/features/Oest_Pan/components/AutocompleteInput.jsx deleted file mode 100644 index 1eaee06..0000000 --- a/src/features/Oest_Pan/components/AutocompleteInput.jsx +++ /dev/null @@ -1,123 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react'; -import { Search, ChevronDown, Check } from 'lucide-react'; - -const AutocompleteInput = ({ - label, - value, - onChange, - options = [], - onSelect, - placeholder = "Pesquisar...", - displayKey = "label", // key to show in list - valueKey = "value", // key to use for value - searchKeys = [], // keys to search in. if empty, searches displayKey - className = "", - disabled = false, - required = false -}) => { - const [isOpen, setIsOpen] = useState(false); - const [searchTerm, setSearchTerm] = useState(value || ''); - const wrapperRef = useRef(null); - - useEffect(() => { - setSearchTerm(value || ''); - }, [value]); - - useEffect(() => { - const handleClickOutside = (event) => { - if (wrapperRef.current && !wrapperRef.current.contains(event.target)) { - setIsOpen(false); - } - }; - document.addEventListener('mousedown', handleClickOutside); - return () => document.removeEventListener('mousedown', handleClickOutside); - }, []); - - const filteredOptions = options.filter(option => { - if (!searchTerm) return true; - const term = searchTerm.toLowerCase(); - - // If searchKeys provided, search in those - if (searchKeys.length > 0) { - return searchKeys.some(key => { - const val = option[key]; - return val && String(val).toLowerCase().includes(term); - }); - } - - // Otherwise search in displayKey or direct string - const label = typeof option === 'object' ? option[displayKey] : option; - return String(label).toLowerCase().includes(term); - }); - - const handleSelect = (option) => { - const displayVal = typeof option === 'object' ? option[displayKey] : option; - const actualVal = typeof option === 'object' ? (option[valueKey] || option[displayKey]) : option; - - setSearchTerm(displayVal); - onChange(displayVal); // Update the input value - if (onSelect) onSelect(option); // Trigger specific selection logic - setIsOpen(false); - }; - - return ( -
- {label && ( - - )} -
- { - setSearchTerm(e.target.value); - onChange(e.target.value); - setIsOpen(true); - }} - onFocus={() => setIsOpen(true)} - disabled={disabled} - /> - - - {isOpen && filteredOptions.length > 0 && ( -
    - {filteredOptions.map((option, index) => { - const display = typeof option === 'object' ? option[displayKey] : option; - const isSelected = display === value; - - return ( -
  • handleSelect(option)} - > - {display} - {isSelected && } -
  • - ); - })} -
- )} - {isOpen && filteredOptions.length === 0 && searchTerm && ( -
- Sem resultados -
- )} -
-
- ); -}; - -export default AutocompleteInput; - - - diff --git a/src/features/Oest_Pan/components/ExcelTable.jsx b/src/features/Oest_Pan/components/ExcelTable.jsx deleted file mode 100644 index 3061fa0..0000000 --- a/src/features/Oest_Pan/components/ExcelTable.jsx +++ /dev/null @@ -1,459 +0,0 @@ -import React, { useMemo, useState, useEffect } from 'react'; -import { Edit2, Trash2, Filter, Columns, Layers, Download, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight } from 'lucide-react'; -import AdvancedFiltersModal from './AdvancedFiltersModal'; - -const ExcelTable = ({ - data, - columns, - filterDefs = [], - onEdit, - onDelete, - onRowClick, - loading = false, - - pageSize: initialPageSize = 50, - selectedIds = [], - onSelectionChange, - rowKey = 'id' -}) => { - const [showFilters, setShowFilters] = useState(false); - const [filters, setFilters] = useState({}); - const [sortConfig, setSortConfig] = useState({ key: null, direction: 'asc' }); - - // Pagination State - const [currentPage, setCurrentPage] = useState(1); - const [pageSize, setPageSize] = useState(initialPageSize); - - // ... (existing useMemo logic) ... - - // Helper para lidar com seleção - - - // 1. Extract Unique Values... - const options = React.useMemo(() => { - const opts = {}; - const getUnique = (key) => [...new Set(data.map(item => item[key]).filter(Boolean))].sort(); - - filterDefs.forEach(def => { - if (def.type === 'select') { - opts[def.field] = getUnique(def.field); - } - }); - - return opts; - }, [data, filterDefs]); - - // 2. Process Data... - const processedData = React.useMemo(() => { - let result = [...data]; - - // Filter - Object.keys(filters).forEach(key => { - if (filters[key]) { - result = result.filter(item => { - const itemValue = String(item[key] || '').toLowerCase(); - const filterValue = String(filters[key]).toLowerCase(); - return itemValue.includes(filterValue); - }); - } - }); - - // Sort - if (sortConfig.key) { - result.sort((a, b) => { - const valA = a[sortConfig.key] || ''; - const valB = b[sortConfig.key] || ''; - - if (valA < valB) return sortConfig.direction === 'asc' ? -1 : 1; - if (valA > valB) return sortConfig.direction === 'asc' ? 1 : -1; - return 0; - }); - } - - return result; - }, [data, filters, sortConfig]); - - // 3. Paginate... - const paginatedData = React.useMemo(() => { - const startIndex = (currentPage - 1) * pageSize; - return processedData.slice(startIndex, startIndex + pageSize); - }, [processedData, currentPage, pageSize]); - - // Reset page... - useEffect(() => { - setCurrentPage(1); - }, [filters, sortConfig, pageSize]); - - // Helper para lidar com seleção (Movido para cá para ter acesso a processedData) - const handleSelectAll = (e) => { - if (!onSelectionChange) return; - if (e.target.checked) { - const allIds = processedData.map(item => item[rowKey]); - onSelectionChange(allIds); - } else { - onSelectionChange([]); - } - }; - - const handleSelectRow = (id) => { - if (!onSelectionChange) return; - const newSelected = selectedIds.includes(id) - ? selectedIds.filter(x => x !== id) - : [...selectedIds, id]; - onSelectionChange(newSelected); - }; - - const isAllSelected = processedData && processedData.length > 0 && processedData.every(item => selectedIds.includes(item[rowKey])); - const isIndeterminate = processedData && processedData.some(item => selectedIds.includes(item[rowKey])) && !isAllSelected; - - - const totalPages = Math.ceil(processedData.length / pageSize); - - const handleSort = (key) => { - setSortConfig(current => ({ - key, - direction: current.key === key && current.direction === 'asc' ? 'desc' : 'asc' - })); - }; - - const handleApplyFilters = (newFilters) => { - setFilters(newFilters); - setShowFilters(false); - }; - - - - - - return ( - <> -
- - {/* Loading Overlay */} - {loading && ( -
-
-
-
-
-
-
-
- Carregando Dados... -
-
- )} - - {/* 1. Top Toolbar */} -
- - {/* Left Actions */} -
- {/* Filter Button */} - - - {/* Actions Button (Green Highlight) - Commented as requested - - */} -
- - {/* Right Actions - Commented out as requested -
- - - -
- */} -
- - - {/* 2. Table Header (The Complex Part) */} -
- - - - - - {/* Checkbox Header */} - - {onEdit || onDelete ? ( - - ) : null} - - {/* Dynamic Columns */} - {columns.map((col, idx) => ( - - ))} - {/* Spacer for scrollbar */} - - - - - {/* 3. Table Body - Displaying Paginated Data */} - - {paginatedData.map((row, idx) => ( - { - // Não disparar onRowClick se clicar em checkbox, botões ou links - if (e.target.closest('input[type="checkbox"]') || - e.target.closest('button') || - e.target.closest('a')) { - return; - } - onRowClick && onRowClick(row); - }} - className={`h-[44px] border-b border-slate-100 dark:border-[#252525] hover:bg-slate-50 dark:hover:bg-[#202020] transition-colors group text-sm ${idx % 2 === 0 ? 'bg-white dark:bg-[#151515]' : 'bg-slate-50/50 dark:bg-[#181818]'} ${idx === 0 ? '!bg-slate-100 dark:!bg-[#2b2b2b]' : ''} ${onRowClick ? 'cursor-pointer' : ''}`}> - - {/* Checkbox Cell */} - - - {/* Actions Cell */} - {(onEdit || onDelete) ? ( - - ) : null} - - {/* Data Cells */} - {columns.map((col, cIdx) => ( - - ))} - - - ))} - {/* Empty rows filler if needed */} - {!loading && paginatedData.length === 0 && ( - - - - )} - -
-
- { if (input) input.indeterminate = isIndeterminate; }} - onChange={handleSelectAll} - disabled={!onSelectionChange} - className="appearance-none w-3.5 h-3.5 border-2 border-slate-300 dark:border-slate-600 rounded-sm bg-white dark:bg-[#2a2a2a] checked:bg-orange-500 checked:border-orange-500 transition-all cursor-pointer relative checked:bg-[url('data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.5%207L5.5%2010L11.5%204%22%20stroke%3D%22%23FFF%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E')] bg-center bg-no-repeat bg-[length:70%]" - /> -
-
-
- Ações -
-
handleSort(col.field)} - className="h-[40px] border-r border-slate-200 dark:border-[#333] bg-slate-50 dark:bg-[#1b1b1b] px-3 relative group hover:bg-slate-100 dark:hover:bg-[#222] transition-colors cursor-pointer select-none" - style={{ width: col.width, minWidth: col.width }} - > -
-
- - {col.header} - -
- - {/* Sort/Menu Icons */} -
- - - - - - -
-
- - {/* Column Resizer Handle */} -
e.stopPropagation()} /> -
-
- handleSelectRow(row[rowKey])} - disabled={!onSelectionChange} - className="appearance-none w-3.5 h-3.5 border-2 border-slate-300 dark:border-slate-600 rounded-sm bg-white dark:bg-[#2a2a2a] checked:bg-orange-500 checked:border-orange-500 transition-all cursor-pointer relative checked:bg-[url('data:image/svg+xml;charset=UTF-8,%3Csvg%20width%3D%2214%22%20height%3D%2214%22%20viewBox%3D%220%200%2014%2014%22%20fill%3D%22none%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%3E%3Cpath%20d%3D%22M2.5%207L5.5%2010L11.5%204%22%20stroke%3D%22%23FFF%22%20stroke-width%3D%222%22%20stroke-linecap%3D%22round%22%20stroke-linejoin%3D%22round%22%2F%3E%3C%2Fsvg%3E')] bg-center bg-no-repeat bg-[length:70%]" - /> -
-
-
- - {onDelete && ( - - )} -
-
- {col.render ? col.render(row) : ( - {row[col.field] || '-'} - )} -
- {filters && Object.keys(filters).length > 0 ? 'Nenhum registro encontrado para os filtros selecionados.' : 'Nenhum registro disponível.'} -
-
- - {/* 4. Footer Steps (Pagination Controls) */} -
- - {/* Left: Totals */} -
-
-
- Total: - {processedData.length} - -
-
- -
-
- Página: - {currentPage} / {totalPages || 1} -
-
- - {/* Right: Pagination Controls */} -
-
- Exibindo {Math.min((currentPage - 1) * pageSize + 1, processedData.length)} a {Math.min(currentPage * pageSize, processedData.length)} -
- - {/* First Page */} - - - {/* Prev Page */} - - - {/* Manual Page Buttons (Simple logic) */} -
- -
- - {/* Next Page */} - - - {/* Last Page */} - -
-
-
- - setShowFilters(false)} - onApply={handleApplyFilters} - options={options} - initialFilters={filters} - config={filterDefs} - /> - - ); -}; - -export default ExcelTable; - - - diff --git a/src/features/Oest_Pan/components/FeedbackNotification.jsx b/src/features/Oest_Pan/components/FeedbackNotification.jsx deleted file mode 100644 index a8e34e3..0000000 --- a/src/features/Oest_Pan/components/FeedbackNotification.jsx +++ /dev/null @@ -1,115 +0,0 @@ -import React from 'react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { CheckCircle2, AlertCircle, Info, XCircle, X } from 'lucide-react'; -import { create } from 'zustand'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -// Store to manage global feedback state -export const useFeedbackStore = create((set) => ({ - notifications: [], - notify: (type, title, message, duration = 5000) => { - const id = Date.now(); - set((state) => ({ - notifications: [...state.notifications, { id, type, title, message, duration }] - })); - setTimeout(() => { - set((state) => ({ - notifications: state.notifications.filter(n => n.id !== id) - })); - }, duration); - }, - remove: (id) => set((state) => ({ - notifications: state.notifications.filter(n => n.id !== id) - })) -})); - -const icons = { - success: , - error: , - warning: , - info: -}; - -const colors = { - success: "border-orange-500/20 bg-orange-50/50 dark:bg-orange-500/10", - error: "border-rose-500/20 bg-rose-50/50 dark:bg-rose-500/10", - warning: "border-amber-500/20 bg-amber-50/50 dark:bg-amber-500/10", - info: "border-blue-500/20 bg-blue-50/50 dark:bg-blue-500/10" -}; - -export const FeedbackContainer = () => { - const { notifications, remove } = useFeedbackStore(); - - return ( -
- - {notifications.map((n) => ( - - {/* Background Glow */} -
- -
- {icons[n.type]} -
- -
-

- {n.title} -

-

- {n.message} -

-
- - - - {/* Progress Bar (Auto-expire) */} - - - ))} -
-
- ); -}; - -export const useFeedback = () => { - const notify = useFeedbackStore(s => s.notify); - - return { - success: (title, message) => notify('success', title || 'Sucesso!', message), - error: (title, message) => notify('error', title || 'Ops!', message), - warning: (title, message) => notify('warning', title || 'Atenção!', message), - info: (title, message) => notify('info', title || 'Informação', message), - - // Friendly back-end error parser - handleBackendError: (error) => { - console.error(error); - // Usa a função utilitária para obter mensagem amigável - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Ops!', friendlyMsg); - }, - - notifyFields: (fields) => { - notify('warning', 'Campos Obrigatórios', `Por favor, preencha: ${fields.join(', ')}`); - } - }; -}; - - - diff --git a/src/features/Oest_Pan/components/FinesCard/FinesCard.debug.jsx b/src/features/Oest_Pan/components/FinesCard/FinesCard.debug.jsx deleted file mode 100644 index 1d945b3..0000000 --- a/src/features/Oest_Pan/components/FinesCard/FinesCard.debug.jsx +++ /dev/null @@ -1,174 +0,0 @@ - -import React, { useState } from 'react'; -import { FinesCard } from './FinesCard'; -import { Button } from '@/components/ui/button'; -import { Label } from '@/components/ui/label'; -import { RefreshCcw } from 'lucide-react'; - -const FinesCardDebug = (initialProps) => { - - const sampleData = [ - { name: 'Excesso de Velocidade', value: 450.00, color: '#ef4444' }, // Red - { name: 'Estacionamento Irregular', value: 250.00, color: '#f59e0b' }, // Amber - { name: 'Documentação', value: 150.00, color: '#3b82f6' }, // Blue - ]; - - const totalSampleValue = sampleData.reduce((acc, curr) => acc + curr.value, 0); - - // Helper to generate consistent data - const generateChartData = (period, current, previous) => { - if (period === '30d') { - return [ - { name: 'Sem 1', value: previous * 0.8 }, - { name: 'Sem 2', value: previous * 1.1 }, - { name: 'Sem 3', value: previous * 0.9 }, - { name: 'Atual', value: current, active: true } // Highlighted - ]; - } - - if (period === '60d') { - return [ - { name: 'Mês -1', value: previous * 0.9 }, - { name: 'Anterior', value: previous }, - { name: 'Atual', value: current, active: true } - ]; - } - - if (period === '120d') { - return [ - { name: 'Mês -3', value: previous * 1.2 }, - { name: 'Mês -2', value: previous * 0.8 }, - { name: 'Anterior', value: previous }, - { name: 'Atual', value: current, active: true } - ]; - } - return []; - }; - - const [state, setState] = useState(() => { - const initialCurrent = 850; - const initialPrev = 1100; - return { - currentValue: initialCurrent, - currentCount: 3, - previousValue: initialPrev, - previousCount: 6, - hasData: true, - isLoading: false, - period: '30d', - data: sampleData, - monthlyData: generateChartData('30d', initialCurrent, initialPrev), - ...initialProps - }; - }); - - const toggleData = () => { - setState(prev => ({ - ...prev, - hasData: !prev.hasData, - currentValue: !prev.hasData ? totalSampleValue : 0, - currentCount: !prev.hasData ? 3 : 0 - })); - }; - - const handlePeriodChange = (p) => { - console.log('Period Changed:', p); - - // Simulate API logic: distinct values for each period - let newCurrent, newPrev, newCount; - - if (p === '30d') { newCurrent = 850; newPrev = 1100; newCount = 3; } - else if (p === '60d') { newCurrent = 1450; newPrev = 1200; newCount = 5; } - else if (p === '120d') { newCurrent = 2100; newPrev = 1800; newCount = 8; } - else { newCurrent = 0; newPrev = 0; newCount = 0; } - - setState(prev => ({ - ...prev, - period: p, // Update the period in state - currentValue: newCurrent, - previousValue: newPrev, - currentCount: newCount, - monthlyData: generateChartData(p, newCurrent, newPrev) - })); - }; - - const updatePrevValue = (e) => { - const val = Number(e.target.value); - setState(prev => ({ - ...prev, - previousValue: val, - monthlyData: generateChartData(prev.period, prev.currentValue, val) - })); - }; - - return ( -
- {/* Preview Area */} -
- -
- - {/* Controls Area */} -
-
-

Debug Controls

- -
- -
-
-
- -

Alterna entre estado vazio e populado

-
-
- -
-
- -
-
- Valor Mês Anterior - R$ {state.previousValue} -
- -
-
- -
-
Current Value: {state.currentValue}
-
Reduction: {state.previousValue > 0 ? ((state.currentValue - state.previousValue) / state.previousValue * 100).toFixed(1) : 0}%
-
-
-
- ); -}; - -export default FinesCardDebug; - - - diff --git a/src/features/Oest_Pan/components/FinesCard/FinesCard.jsx b/src/features/Oest_Pan/components/FinesCard/FinesCard.jsx deleted file mode 100644 index e15939a..0000000 --- a/src/features/Oest_Pan/components/FinesCard/FinesCard.jsx +++ /dev/null @@ -1,267 +0,0 @@ - -import React, { useState } from 'react'; -import { Card } from '@/components/ui/card'; -import { Badge } from '@/components/ui/badge'; -import { PieChart as PieChartIcon, Ban, AlertCircle, TrendingDown, TrendingUp, Minus, BarChart3, PieChart as PieIcon } from 'lucide-react'; -import { PieChart, Pie, Cell, ResponsiveContainer, Tooltip, BarChart, XAxis, YAxis, CartesianGrid, Bar } from 'recharts'; -import { cn } from '@/lib/utils'; - -/** - * FinesCard (Componente de Multas) - * - * Exibe o resumo de multas do mês atual comparado com o mês anterior. - * Permite alternar entre visualização por Categoria (Pie) e Histórico Mensal (Bar). - */ -export const FinesCard = ({ - currentDate = 'Mês', - currentValue = 0, - currentCount = 0, - previousValue = 0, - previousCount = 0, - data = [], // [{ name: 'Type', value: 100, color: '#f00' }] - monthlyData = [], // [{ name: 'Jan', value: 500, count: 2 }] - isLoading = false, - onPeriodChange, - className -}) => { - const [activeView, setActiveView] = useState('category'); // 'category' | 'history' - const [period, setPeriod] = useState('30d'); // '30d' | '60d' | '120d' - - const handlePeriodChange = (p) => { - setPeriod(p); - if (onPeriodChange) onPeriodChange(p); - }; - - // Formatters - const formatCurrency = (val) => new Intl.NumberFormat('pt-BR', { style: 'currency', currency: 'BRL' }).format(val); - const formatCompact = (val) => Intl.NumberFormat('pt-BR', { notation: "compact", maximumFractionDigits: 1 }).format(val); - - // Calculation for reduction/increase - const hasPrevious = previousValue > 0; - const diff = currentValue - previousValue; - const percentage = hasPrevious ? (diff / previousValue) * 100 : 0; - const isReduction = percentage < 0; - const isIncrease = percentage > 0; - - const trendColor = isReduction ? 'text-orange-500' : isIncrease ? 'text-rose-500' : 'text-slate-500'; - const badgeColor = isReduction ? 'bg-orange-500/10 text-orange-500 border-orange-500/20' : isIncrease ? 'bg-rose-500/10 text-rose-500 border-rose-500/20' : 'bg-slate-500/10 text-slate-500 border-slate-500/20'; - const TrendIcon = isReduction ? TrendingDown : isIncrease ? TrendingUp : Minus; - - const isEmpty = !data || data.length === 0 || currentValue === 0; - - const CustomTooltip = ({ active, payload, label }) => { - if (active && payload && payload.length) { - const pData = payload[0]; - return ( -
-

{label || pData.name}

-
-
- - {formatCurrency(pData.value)} - -
-
- ); - } - return null; - }; - - return ( - - - {/* Header */} -
-
-
-
- -
-
-

Multas

- Visão Geral -
-
- -
-
0 ? "text-slate-900 dark:text-white" : "text-orange-600 dark:text-orange-500")}> - {formatCurrency(currentValue)} -
-
{currentCount} registros
-
-
- - {/* Comparison Box */} -
-
- Mês Anterior -
- {hasPrevious ? `R$ ${formatCompact(previousValue)}` : '-'} - ({previousCount} registros) -
-
- - {hasPrevious && ( - - - {Math.abs(percentage).toFixed(1)}% - - )} - - {/* Background gradient for decoration */} -
-
- - {/* Filters & Toggles Row */} - {!isEmpty && !isLoading && ( -
- {/* Time Period Selector */} -
- {['30d', '60d', '120d'].map((p) => ( - - ))} -
- - {/* View Type Toggle (Icon Only) */} -
- - -
-
- )} -
- -
- {isLoading ? ( -
-
-
-
- ) : isEmpty ? ( - // Empty State -
-
- -
-
-

Sem dados

-

- Nenhum registro encontrado para o período. -

-
-
- ) : ( - // Charts Area -
- {activeView === 'category' ? ( - <> -
- - - - {data.map((entry, index) => ( - - ))} - - } /> - - -
- {/* Legend */} -
- {data.map((item, i) => ( -
-
- {item.name} -
- ))} -
- - ) : ( -
- - - - - `R$${val/1000}k`} - /> - } cursor={{ fill: 'rgba(148, 163, 184, 0.1)' }} /> - - {monthlyData.map((entry, index) => ( - - ))} - - - -
- Evolução últimos 6 meses -
-
- )} -
- )} -
- - ); -}; - - - - diff --git a/src/features/Oest_Pan/components/FinesCard/index.js b/src/features/Oest_Pan/components/FinesCard/index.js deleted file mode 100644 index 03df17a..0000000 --- a/src/features/Oest_Pan/components/FinesCard/index.js +++ /dev/null @@ -1,5 +0,0 @@ -export { FinesCard } from './FinesCard'; -export { FinesCard as default } from './FinesCard'; - - - diff --git a/src/features/Oest_Pan/components/PrafrotSidebar.css b/src/features/Oest_Pan/components/PrafrotSidebar.css deleted file mode 100644 index be69346..0000000 --- a/src/features/Oest_Pan/components/PrafrotSidebar.css +++ /dev/null @@ -1,404 +0,0 @@ -/* PrafrotSidebar CSS - Adaptive Theme (Light/Dark) */ - -:root { - /* Light Theme Variables (Default) */ - --pfs-bg: #ffffff; - --pfs-text: #475569; - --pfs-text-active: #0f172a; - --pfs-text-muted: #64748b; - --pfs-border: #f1f5f9; - --pfs-bg-search: #f8fafc; - --pfs-bg-hover: rgba(0, 0, 0, 0.03); - --pfs-tree-line: #e2e8f0; - --pfs-footer-bg: #f8fafc; - --pfs-user-card-bg: rgba(0, 0, 0, 0.01); - --pfs-toggle-bg: #ffffff; - --pfs-toggle-border: #e2e8f0; - --pfs-shadow: 0 10px 30px rgba(0, 0, 0, 0.04); -} - -.dark .pfs-container { - /* Dark Theme Variables */ - --pfs-bg: #1a1a1a; - --pfs-text: #ffffff; - --pfs-text-active: #141414; - --pfs-text-muted: #a0a0a0; - --pfs-border: rgba(255, 255, 255, 0.03); - --pfs-bg-search: #252525; - --pfs-bg-hover: rgba(255, 255, 255, 0.03); - --pfs-tree-line: #262626; - --pfs-footer-bg: #181818; - --pfs-user-card-bg: rgba(255, 255, 255, 0.02); - --pfs-toggle-bg: #262626; - --pfs-toggle-border: #333; - --pfs-shadow: 0 10px 30px rgba(0, 0, 0, 0.5); -} - -.pfs-container { - width: 260px; - height: calc(100vh - 20px); - background: var(--pfs-bg); - color: var(--pfs-text); - display: flex; - flex-direction: column; - position: fixed; - top: 10px; - left: 10px; - z-index: 40; - border-radius: 16px; - transition: all 0.4s cubic-bezier(0.4, 0, 0.2, 1); - overflow: hidden; - box-shadow: var(--pfs-shadow); - border: 1px solid var(--pfs-border); -} - -/* Subtle Top Glow - orange */ -.pfs-container::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2px; - background: linear-gradient(90deg, transparent, #f97316, transparent); - opacity: 0.3; -} - -.pfs-container.collapsed { - width: 80px; -} - -/* Toggle Area */ -.pfs-toggle-container { - padding: 1rem; - display: flex; - justify-content: flex-end; - min-height: 56px; - align-items: center; -} - -.collapsed .pfs-toggle-container { - justify-content: center; -} - -.pfs-toggle-btn { - width: 28px; - height: 28px; - border-radius: 6px; - background: var(--pfs-toggle-bg); - border: 1px solid var(--pfs-toggle-border); - color: var(--pfs-text-muted); - display: flex; - align-items: center; - justify-content: center; - cursor: pointer; - transition: all 0.2s ease; -} - -.pfs-toggle-btn:hover { - background: var(--pfs-bg-search); - color: #f97316; - border-color: #f97316; - transform: scale(1.05); -} - -/* Search Box */ -.pfs-search-wrapper { - padding: 0.5rem 1.25rem 1.5rem; - transition: all 0.3s ease; -} - -.collapsed .pfs-search-wrapper { - opacity: 0; - pointer-events: none; - height: 0; - padding: 0; -} - -.pfs-search-input { - width: 100%; - padding: 0.75rem 1rem 0.75rem 2.5rem; - background: var(--pfs-bg-search); - border: 1px solid var(--pfs-border); - border-radius: 12px; - color: var(--pfs-text); - font-size: 0.85rem; - outline: none; - transition: all 0.2s ease; -} - -.pfs-search-input:focus { - border-color: #f97316; - background: var(--pfs-bg); - box-shadow: 0 0 0 3px rgba(249, 115, 22, 0.1); -} - -/* Navigation Content */ -.pfs-nav-content { - flex: 1; - padding: 0.5rem 1rem; - overflow-y: auto; - overflow-x: hidden; -} - -.pfs-nav-content::-webkit-scrollbar { - width: 3px; -} - -.pfs-nav-content::-webkit-scrollbar-thumb { - background: var(--pfs-border); - border-radius: 10px; -} - -/* Menu Item Wrapper */ -.pfs-item-wrapper { - margin-bottom: 6px; -} - -/* Links & Tree Structure */ -.pfs-link { - display: flex; - align-items: center; - padding: 0.75rem 1rem; - border-radius: 12px; - color: var(--pfs-text-muted); - text-decoration: none; - transition: all 0.2s ease; - cursor: pointer; - position: relative; - gap: 14px; -} - -.pfs-link:hover:not(.active) { - background: var(--pfs-bg-hover); - color: var(--pfs-text); -} - -.pfs-link.active { - background: #f97316 !important; - color: var(--pfs-text-active) !important; - font-weight: 700; - box-shadow: 0 4px 15px rgba(249, 115, 22, 0.25); -} - -.pfs-icon { - flex-shrink: 0; - transition: transform 0.2s ease; -} - -.pfs-link:hover .pfs-icon:not(.active) { - transform: scale(1.1); -} - -.pfs-label { - flex: 1; - font-size: 0.9rem; - white-space: nowrap; - letter-spacing: -0.01em; -} - -.collapsed .pfs-label, -.collapsed .pfs-chevron { - display: none; -} - -.pfs-chevron { - transition: transform 0.3s cubic-bezier(0.4, 0, 0.2, 1); - color: var(--pfs-text-muted); -} - -.pfs-chevron.expanded { - transform: rotate(180deg); -} - -/* Submenu & Tree Lines */ -.pfs-submenu { - margin-left: 0.5rem; - padding-left: 1.25rem; - border-left: 2px solid var(--pfs-tree-line); - margin-top: 4px; - margin-bottom: 8px; -} - -.pfs-sublink { - display: flex; - align-items: center; - padding: 0.65rem 1rem; - border-radius: 10px; - color: var(--pfs-text-muted); - font-size: 0.85rem; - transition: all 0.2s ease; - text-decoration: none; - margin-bottom: 2px; - position: relative; - gap: 12px; -} - -.pfs-sublink::before { - content: ''; - position: absolute; - left: -1.25rem; - top: 50%; - width: 0.75rem; - height: 2px; - background: var(--pfs-tree-line); -} - -.pfs-sublink:hover { - color: var(--pfs-text); - background: var(--pfs-bg-hover); -} - -.pfs-sublink.active { - color: #f97316; - font-weight: 700; -} - -/* Icons in sublink */ -.pfs-sublink .pfs-icon { - opacity: 0.7; -} - -.pfs-sublink.active .pfs-icon { - opacity: 1; - color: #f97316; -} - -/* Footer Section */ -.pfs-footer { - padding: 1.25rem; - border-top: 1px solid var(--pfs-border); - background: var(--pfs-footer-bg); - display: flex; - flex-direction: column; - gap: 1rem; -} - -.collapsed .pfs-footer { - padding: 1.25rem 0.5rem; - align-items: center; -} - -/* Brand Area in Footer */ -.pfs-brand { - display: flex; - align-items: center; - gap: 12px; -} - -.pfs-brand-logo { - width: 36px; - height: 36px; - background: #f97316; - color: #141414; - border-radius: 10px; - display: flex; - align-items: center; - justify-content: center; - font-weight: 900; - box-shadow: 0 0 15px rgba(249, 115, 22, 0.2); - flex-shrink: 0; -} - -.pfs-brand-info { - display: flex; - flex-direction: column; -} - -.pfs-brand-name { - font-weight: 800; - font-size: 0.95rem; - letter-spacing: 0.5px; - text-transform: uppercase; - color: var(--pfs-text); -} - -.pfs-brand-name span { - color: #f97316; -} - -.pfs-app-sub { - font-size: 0.7rem; - color: var(--pfs-text-muted); - font-weight: 600; -} - -/* User Card */ -.pfs-user-card { - display: flex; - align-items: center; - gap: 12px; - padding: 0.625rem; - background: var(--pfs-user-card-bg); - border-radius: 12px; - border: 1px solid var(--pfs-border); -} - -.pfs-user-data { - display: flex; - flex-direction: column; - min-width: 0; -} - -.pfs-user-name { - font-size: 0.85rem; - font-weight: 700; - color: var(--pfs-text); - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; -} - -.pfs-logout-link { - font-size: 0.75rem; - color: var(--pfs-text-muted); - display: flex; - align-items: center; - gap: 4px; - transition: color 0.2s; - background: none; - border: none; - padding: 0; - cursor: pointer; - width: fit-content; -} - -.pfs-logout-link:hover { - color: #ef4444; -} - -/* Legal & Version */ -.pfs-legal { - display: flex; - flex-direction: column; - gap: 6px; - padding-top: 0.75rem; - border-top: 1px solid var(--pfs-border); -} - -.pfs-legal-item, .pfs-version { - display: flex; - align-items: center; - gap: 8px; - font-size: 0.7rem; - color: var(--pfs-text-muted); -} - -.pfs-legal-item svg, .pfs-version svg { - color: #f97316; - opacity: 0.6; -} - -/* Locks */ -.pfs-locked { - opacity: 0.4; - cursor: not-allowed !important; -} - -.pfs-lock-icon { - margin-left: auto; - color: #f97316 !important; -} - - - diff --git a/src/features/Oest_Pan/components/PrafrotSidebar.jsx b/src/features/Oest_Pan/components/PrafrotSidebar.jsx deleted file mode 100644 index 4e8c289..0000000 --- a/src/features/Oest_Pan/components/PrafrotSidebar.jsx +++ /dev/null @@ -1,249 +0,0 @@ -import React, { useState, useMemo } from 'react'; -import { Link, useLocation } from 'react-router-dom'; -import { motion, AnimatePresence } from 'framer-motion'; -import { - ChevronLeft, - ChevronRight, - Search, - ChevronDown, - LayoutDashboard, - Car, - Users, - Wrench, - Activity, - Radio, - AlertTriangle, - Store, - ClipboardList, - LogOut, - ShieldAlert, - Building2, - Award, - GitBranch, - Lock, - Settings -} from 'lucide-react'; -import { cn } from '@/lib/utils'; -import { useAuthContext } from '@/components/shared/AuthProvider'; -import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; -import logoOestePan from '@/assets/Img/Util/Oeste_Pan/Logo Oeste Pan.png'; -import './PrafrotSidebar.css'; - -const MENU_ITEMS = [ - { - id: 'dashboard', - label: 'Estatísticas', - icon: LayoutDashboard, - path: '/plataforma/oest-pan/estatisticas' - }, - { - id: 'cadastros', - label: 'Cadastros', - icon: ClipboardList, - children: [ - { id: 'c-veiculos', label: 'Veículos', path: '/plataforma/oest-pan/veiculos', icon: Car }, - { id: 'c-dispatcher', label: 'Dispatcher', path: '/plataforma/oest-pan/dispatcher', icon: ClipboardList }, - // { id: 'c-motoristas', label: 'Motoristas', path: '/plataforma/oest-pan/motoristas', icon: Users, disabled: true, disabledReason: 'Funcionalidade em manutenção' }, - { id: 'c-oficinas', label: 'Oficinas', path: '/plataforma/oest-pan/oficinas', icon: Store } - ] - }, - { - id: 'gerencia', - label: 'Gerência', - icon: Activity, - children: [ - { id: 'g-monitoramento', label: 'Monitoramento', path: '/plataforma/oest-pan/monitoramento', icon: Radio }, - { id: 'g-status', label: 'Status Frota', path: '/plataforma/oest-pan/status', icon: Activity }, - { id: 'g-manutencao', label: 'Manutenção', path: '/plataforma/oest-pan/manutencao', icon: Wrench }, - { id: 'g-sinistros', label: 'Sinistros', path: '/plataforma/oest-pan/sinistros', icon: AlertTriangle } - ] - }, - { - id: 'config', - label: 'Configurações', - icon: Settings, - path: '/plataforma/oest-pan/configuracoes' - } -]; - -// Optimized MenuItem component outside main render loop -const MenuItem = React.memo(({ item, isSub = false, isCollapsed, expandedItems, toggleExpand, pathname, searchTerm }) => { - const Icon = item.icon; - const hasChildren = item.children && item.children.length > 0; - const isExpanded = expandedItems[item.id]; - const isActive = pathname.startsWith(item.path) || (hasChildren && item.children.some(c => pathname.startsWith(c.path))); - const isLocked = item.disabled; - - // Filter sub-items if searching - const subItems = item.children?.filter(child => - !searchTerm || child.label.toLowerCase().includes(searchTerm.toLowerCase()) - ); - - const content = ( -
{ - if (isLocked) return; - if (hasChildren) toggleExpand(item.id); - }} - title={isLocked ? item.disabledReason : (isCollapsed ? item.label : '')} - > - - {(!isCollapsed || isSub) && {item.label}} - - {hasChildren && !isCollapsed && ( - - )} - - {isLocked && !isCollapsed && ( - - )} -
- ); - - return ( -
- {item.path && !hasChildren && !isLocked ? ( - - {content} - - ) : content} - - - {hasChildren && isExpanded && !isCollapsed && ( - - {subItems?.map(child => ( - - ))} - - )} - -
- ); -}); - -MenuItem.displayName = 'MenuItem'; - -export const OestPanSidebar = ({ isCollapsed, onToggle }) => { - const [searchTerm, setSearchTerm] = useState(''); - const [expandedItems, setExpandedItems] = useState({ cadastros: true, gerencia: true }); - const location = useLocation(); - const { user, logout } = useAuthContext(); - - const toggleExpand = React.useCallback((id) => { - setExpandedItems(prev => ({ - ...prev, - [id]: !prev[id] - })); - }, []); - - const filteredItems = useMemo(() => { - if (!searchTerm) return MENU_ITEMS; - - return MENU_ITEMS.filter(item => { - const matchParent = item.label.toLowerCase().includes(searchTerm.toLowerCase()); - const matchChildren = item.children?.some(child => - child.label.toLowerCase().includes(searchTerm.toLowerCase()) - ); - return matchParent || matchChildren; - }); - }, [searchTerm]); - - const handleLogout = () => { - logout('auth_oestepan'); - window.location.href = '/plataforma/oest-pan/login'; - }; - - return ( - - ); -}; - - - diff --git a/src/features/Oest_Pan/hooks/useAvailability.js b/src/features/Oest_Pan/hooks/useAvailability.js deleted file mode 100644 index cc86064..0000000 --- a/src/features/Oest_Pan/hooks/useAvailability.js +++ /dev/null @@ -1,76 +0,0 @@ -import { create } from 'zustand'; -import { prafrotService } from '../services/prafrotService'; -import { toast } from 'sonner'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -export const useAvailability = create((set, get) => ({ - availabilities: [], - loading: false, - error: null, - - fetchAvailabilities: async () => { - set({ loading: true, error: null }); - try { - const data = await prafrotService.getAvailability(); - set({ availabilities: Array.isArray(data) ? data : (data.data || []) }); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - toast.error(friendlyMsg); - } finally { - set({ loading: false }); - } - }, - - createAvailability: async (payload) => { - set({ loading: true }); - try { - await prafrotService.createAvailability(payload); - toast.success('Disponibilidade registrada com sucesso!'); - get().fetchAvailabilities(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateAvailability: async (id, payload) => { - set({ loading: true }); - try { - await prafrotService.updateAvailability(id, payload); - toast.success('Disponibilidade atualizada com sucesso!'); - get().fetchAvailabilities(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - deleteAvailability: async (id) => { - if (!window.confirm('Tem certeza que deseja excluir este registro de disponibilidade?')) return; - set({ loading: true }); - try { - await prafrotService.deleteAvailability(id); - toast.success('Disponibilidade excluída com sucesso!'); - get().fetchAvailabilities(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - } -})); - - - diff --git a/src/features/Oest_Pan/hooks/useClaims.js b/src/features/Oest_Pan/hooks/useClaims.js deleted file mode 100644 index 3a1ac09..0000000 --- a/src/features/Oest_Pan/hooks/useClaims.js +++ /dev/null @@ -1,78 +0,0 @@ -import { create } from 'zustand'; -import { prafrotService } from '../services/prafrotService'; -import { useFeedbackStore } from '../components/FeedbackNotification'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -const notify = (type, title, message) => useFeedbackStore.getState().notify(type, title, message); - -export const useClaims = create((set, get) => ({ - claims: [], - loading: false, - error: null, - - fetchClaims: async () => { - set({ loading: true, error: null }); - try { - const data = await prafrotService.getClaims(); - set({ claims: Array.isArray(data) ? data : (data.data || []) }); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - notify('error', 'Falha na Busca', friendlyMsg); - } finally { - set({ loading: false }); - } - }, - - createClaim: async (payload) => { - set({ loading: true }); - try { - await prafrotService.createClaims(payload); - notify('success', 'Evento Registrado', 'O sinistro/devolução foi salvo com sucesso.'); - get().fetchClaims(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Falha no Registro', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateClaim: async (id, payload) => { - set({ loading: true }); - try { - await prafrotService.updateClaims(id, payload); - notify('success', 'Registro Atualizado', 'As informações do evento foram modificadas.'); - get().fetchClaims(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro na Atualização', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - deleteClaim: async (id) => { - if (!window.confirm('Tem certeza que deseja excluir este registro de sinistro/devolução?')) return; - set({ loading: true }); - try { - await prafrotService.deleteClaim(id); - notify('success', 'Registro Removido', 'O registro foi excluído do sistema.'); - get().fetchClaims(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Falha na Exclusão', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - } -})); - - - diff --git a/src/features/Oest_Pan/hooks/useDispatcher.js b/src/features/Oest_Pan/hooks/useDispatcher.js deleted file mode 100644 index ccc9667..0000000 --- a/src/features/Oest_Pan/hooks/useDispatcher.js +++ /dev/null @@ -1,31 +0,0 @@ - -import { create } from 'zustand'; -import { dispatcherService } from '../services/dispatcherService'; -import { toast } from 'sonner'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -export const useDispatcher = create((set) => ({ - data: [], - loading: false, - error: null, - - fetchData: async () => { - set({ loading: true, error: null }); - try { - const result = await dispatcherService.getDispatcherData(); - // Handle potential API variations (e.g. { data: [...] } vs [...]) - const list = Array.isArray(result) ? result : (result?.data || []); - set({ data: list }); - } catch (error) { - console.error('Error fetching dispatcher data:', error); - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - toast.error(friendlyMsg); - } finally { - set({ loading: false }); - } - } -})); - - - diff --git a/src/features/Oest_Pan/hooks/useDrivers.js b/src/features/Oest_Pan/hooks/useDrivers.js deleted file mode 100644 index 360e5ee..0000000 --- a/src/features/Oest_Pan/hooks/useDrivers.js +++ /dev/null @@ -1,27 +0,0 @@ -import { create } from 'zustand'; -import { prafrotService } from '../services/prafrotService'; -import { toast } from 'sonner'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -export const useDrivers = create((set, get) => ({ - drivers: [], - loading: false, - error: null, - - fetchDrivers: async () => { - set({ loading: true, error: null }); - try { - const data = await prafrotService.getDrivers(); - set({ drivers: Array.isArray(data) ? data : (data.data || []) }); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - toast.error(friendlyMsg); - } finally { - set({ loading: false }); - } - } -})); - - - diff --git a/src/features/Oest_Pan/hooks/useFleetLists.js b/src/features/Oest_Pan/hooks/useFleetLists.js deleted file mode 100644 index 1a1a7e1..0000000 --- a/src/features/Oest_Pan/hooks/useFleetLists.js +++ /dev/null @@ -1,110 +0,0 @@ -import { create } from 'zustand'; -import { prafrotService } from '../services/prafrotService'; -import { toast } from 'sonner'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -export const useFleetLists = create((set, get) => ({ - listsConfig: null, - statusFrotaOptions: [], - statusManutencaoOptions: [], - motivoAtendimentoOptions: [], - responsaveisOptions: [], - validacaoOptions: [], - aprovacaoOptions: [], - loading: false, - error: null, - - fetchListsConfig: async () => { - set({ loading: true, error: null }); - try { - const config = await prafrotService.getListasConfig(); - set({ listsConfig: config }); - - // Se tivermos a configuração para status_frota, já buscamos as opções - if (config && config.status_frota && config.status_frota.rota) { - await get().fetchListOptions('status_frota', config.status_frota.rota); - } - - // Se tivermos a configuração para status_manutencao, buscamos as opções - if (config && config.status_manutencao && config.status_manutencao.rota) { - await get().fetchListOptions('status_manutencao', config.status_manutencao.rota); - } - - // Se tivermos a configuração para motivo_atendimento, buscamos as opções - if (config && config.motivo_atendimento && config.motivo_atendimento.rota) { - await get().fetchListOptions('motivo_atendimento', config.motivo_atendimento.rota); - } - - // Se tivermos a configuração para responsaveis, buscamos as opções - if (config && config.responsaveis && config.responsaveis.rota) { - await get().fetchListOptions('responsaveis', config.responsaveis.rota); - } - - // Se tivermos a configuração para validacao, buscamos as opções - if (config && config.validacao && config.validacao.rota) { - await get().fetchListOptions('validacao', config.validacao.rota); - } - - // Se tivermos a configuração para aprovacao, buscamos as opções - if (config && config.aprovacao && config.aprovacao.rota) { - await get().fetchListOptions('aprovacao', config.aprovacao.rota); - } - - } catch (error) { - console.error('Erro ao buscar configuração de listas:', error); - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - } finally { - set({ loading: false }); - } - }, - - fetchListOptions: async (listName, route) => { - try { - const data = await prafrotService.getListasByRoute(route); - let options = Array.isArray(data) ? data : (data.data || []); - - if (options.length > 0 && typeof options[0] === 'object') { - if (listName === 'status_frota') { - options = options.map(item => item.status_frota).filter(Boolean); - } else if (listName === 'status_manutencao') { - options = options.map(item => item.status_manutencao || item.status || Object.values(item)[0]).filter(Boolean); - } else if (listName === 'motivo_atendimento') { - options = options.map(item => item.motivo_atendimento || item.motivo || Object.values(item)[0]).filter(Boolean); - } else if (listName === 'responsaveis') { - options = options.map(item => item.responsaveis || item.responsavel || item.nome || Object.values(item)[0]).filter(Boolean); - } else if (listName === 'validacao') { - options = options.map(item => item.validacao || item.validacao_financeiro || Object.values(item)[0]).filter(Boolean); - } else if (listName === 'aprovacao') { - options = options.map(item => item.aprovacao || item.nome || item.responsavel || Object.values(item)[0]).filter(Boolean); - } else { - options = options.map(item => Object.values(item)[0] || JSON.stringify(item)); - } - } else { - options = options.map(String); - } - - if (listName === 'status_frota') { - set({ statusFrotaOptions: options }); - } else if (listName === 'status_manutencao') { - set({ statusManutencaoOptions: options }); - } else if (listName === 'motivo_atendimento') { - set({ motivoAtendimentoOptions: options }); - } else if (listName === 'responsaveis') { - set({ responsaveisOptions: options }); - } else if (listName === 'validacao') { - set({ validacaoOptions: options }); - } else if (listName === 'aprovacao') { - set({ aprovacaoOptions: options }); - } - - return options; - } catch (error) { - console.error(`Erro ao buscar opções para ${listName}:`, error); - return []; - } - } -})); - - - diff --git a/src/features/Oest_Pan/hooks/useMaintenance.js b/src/features/Oest_Pan/hooks/useMaintenance.js deleted file mode 100644 index 67df66a..0000000 --- a/src/features/Oest_Pan/hooks/useMaintenance.js +++ /dev/null @@ -1,262 +0,0 @@ -import { create } from 'zustand'; -import { prafrotService } from '../services/prafrotService'; -import { useFeedbackStore } from '../components/FeedbackNotification'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -const notify = (type, title, message) => useFeedbackStore.getState().notify(type, title, message); - -export const useMaintenance = create((set, get) => ({ - maintenances: [], - loading: false, - error: null, - - fetchMaintenances: async (type = 'total') => { - set({ loading: true, error: null }); - try { - let list = []; - if (type === 'total') { - const data = await prafrotService.getMaintenance(); - list = Array.isArray(data) ? data : (data.data || []); - } else { - const data = await prafrotService.getAbertoFechado(); - // Concatena abertas e fechadas para manter o estado global se necessário, - // mas a tela de manutenção gerencia o estado `abertoFechadoData` via hook. - // No entanto, para padronizar as ações (save/update), vamos manter o fetchMaintenances funcional. - const abertas = data.abertas || data.aberto || data.abertos || []; - const fechadas = data.fechadas || data.fechado || data.fechados || []; - list = [...abertas, ...fechadas]; - } - - const normalized = list.map(m => ({ ...m, previsao_entrega: m.previsao_entrega ?? m.previcao_entrega })); - set({ maintenances: normalized }); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - notify('error', 'Falha na Busca', friendlyMsg); - } finally { - set({ loading: false }); - } - }, - - // Helper para decidir qual rota atualizar baseado no contexto (geralmente chamado após mutações) - refreshMaintenances: async (currentFilter = 'total') => { - const fetch = get().fetchMaintenances; - const getGroups = get().getAbertoFechado; - - if (currentFilter === 'total') { - await fetch('total'); - } else { - // Atualiza ambos para garantir consistência - await fetch('total'); - await getGroups(); - } - }, - - createMaintenance: async (payload, files = null) => { - set({ loading: true }); - try { - await prafrotService.createMaintenance(payload, files); - notify('success', 'Cadastro Concluído', 'Manutenção registrada com sucesso no sistema.'); - get().fetchMaintenances(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Falha no Registro', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateMaintenance: async (id, payload, files = null) => { - set({ loading: true }); - try { - await prafrotService.updateMaintenance(id, payload, files); - notify('success', 'Atualização!', 'Dados da manutenção atualizados.'); - get().fetchMaintenances(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro na Atualização', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateMaintenanceBatch: async (ids, status) => { - set({ loading: true }); - try { - await prafrotService.updateMaintenanceBatch(ids, status); - - notify('success', 'Lote Atualizado', `${ids.length} manutenções foram atualizadas.`); - get().fetchMaintenances(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Falha em Massa', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - calcularDifOrcamento: async (orcamento_inicial, orcamento_final) => { - try { - const data = await prafrotService.calcularDifOrcamento(orcamento_inicial, orcamento_final); - const valor = data?.dif_orcamento ?? data?.valor ?? (typeof data === 'number' ? data : null); - return valor; - } catch (error) { - return null; - } - }, - - deleteMaintenance: async (id) => { - if (!window.confirm('Tem certeza que deseja excluir esta manutenção?')) return; - set({ loading: true }); - try { - await prafrotService.deleteMaintenance(id); - notify('success', 'Removido', 'Registro de manutenção excluído.'); - get().fetchMaintenances(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro na Remoção', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - fecharManutencao: async (id) => { - set({ loading: true }); - try { - await prafrotService.fecharManutencao(id); - notify('success', 'Manutenção Fechada', 'Manutenção fechada com sucesso.'); - get().fetchMaintenances(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro ao Fechar', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - abrirManutencao: async (id) => { - set({ loading: true }); - try { - await prafrotService.abrirManutencao(id); - notify('success', 'Manutenção Aberta', 'Manutenção aberta com sucesso.'); - get().fetchMaintenances(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro ao Abrir', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - getAbertoFechado: async () => { - set({ loading: true }); - try { - const data = await prafrotService.getAbertoFechado(); - // Atualiza também a lista principal 'maintenances' para refletir as mudanças - const abertas = data.abertas || data.aberto || data.abertos || []; - const fechadas = data.fechadas || data.fechado || data.fechados || []; - const normalized = [...abertas, ...fechadas].map(m => ({ ...m, previsao_entrega: m.previsao_entrega ?? m.previcao_entrega })); - set({ maintenances: normalized }); - return data; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro na Busca', friendlyMsg); - return null; - } finally { - set({ loading: false }); - } - }, - - // Histórico de manutenção - getHistoricoCompleto: async () => { - try { - const data = await prafrotService.getHistoricoCompleto(); - return Array.isArray(data) ? data : (data.data || []); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro ao Buscar Histórico', friendlyMsg); - return []; - } - }, - - getHistoricoDetalhado: async () => { - try { - const data = await prafrotService.getHistoricoDetalhado(); - return Array.isArray(data) ? data : (data.data || []); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro ao Buscar Histórico', friendlyMsg); - return []; - } - }, - - getHistoricoResumido: async () => { - try { - const data = await prafrotService.getHistoricoResumido(); - return Array.isArray(data) ? data : (data.data || []); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro ao Buscar Histórico', friendlyMsg); - return []; - } - }, - - getHistoricoEstatisticas: async () => { - try { - const data = await prafrotService.getHistoricoEstatisticas(); - return data; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro ao Buscar Estatísticas', friendlyMsg); - return null; - } - }, - - getHistoricoPeriodo: async (params) => { - try { - const data = await prafrotService.getHistoricoPeriodo(params); - return Array.isArray(data) ? data : (data.data || []); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro ao Buscar Histórico', friendlyMsg); - return []; - } - }, - - getHistoricoTop: async () => { - try { - const data = await prafrotService.getHistoricoTop(); - return Array.isArray(data) ? data : (data.data || []); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro ao Buscar Histórico', friendlyMsg); - return []; - } - }, - - getHistoricoPorPlaca: async (placa) => { - try { - const data = await prafrotService.getHistoricoPorPlaca(placa); - return Array.isArray(data) ? data : (data.data || []); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro ao Buscar Histórico', friendlyMsg); - return []; - } - } -})); - - - diff --git a/src/features/Oest_Pan/hooks/useMoki.js b/src/features/Oest_Pan/hooks/useMoki.js deleted file mode 100644 index 72883cb..0000000 --- a/src/features/Oest_Pan/hooks/useMoki.js +++ /dev/null @@ -1,76 +0,0 @@ -import { create } from 'zustand'; -import { prafrotService } from '../services/prafrotService'; -import { toast } from 'sonner'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -export const useMoki = create((set, get) => ({ - mokis: [], - loading: false, - error: null, - - fetchMokis: async () => { - set({ loading: true, error: null }); - try { - const data = await prafrotService.getMoki(); - set({ mokis: Array.isArray(data) ? data : (data.data || []) }); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - toast.error(friendlyMsg); - } finally { - set({ loading: false }); - } - }, - - createMoki: async (payload) => { - set({ loading: true }); - try { - await prafrotService.createMoki(payload); - toast.success('Moki registrado com sucesso!'); - get().fetchMokis(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateMoki: async (id, payload) => { - set({ loading: true }); - try { - await prafrotService.updateMoki(id, payload); - toast.success('Moki atualizado com sucesso!'); - get().fetchMokis(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - deleteMoki: async (id) => { - if (!window.confirm('Tem certeza que deseja excluir este checklist?')) return; - set({ loading: true }); - try { - await prafrotService.deleteMoki(id); - toast.success('Checklist excluído com sucesso!'); - get().fetchMokis(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - } -})); - - - diff --git a/src/features/Oest_Pan/hooks/useMonitoring.js b/src/features/Oest_Pan/hooks/useMonitoring.js deleted file mode 100644 index 96457e8..0000000 --- a/src/features/Oest_Pan/hooks/useMonitoring.js +++ /dev/null @@ -1,76 +0,0 @@ -import { create } from 'zustand'; -import { prafrotService } from '../services/prafrotService'; -import { toast } from 'sonner'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -export const useMonitoring = create((set, get) => ({ - monitorings: [], - loading: false, - error: null, - - fetchMonitoring: async () => { - set({ loading: true, error: null }); - try { - const data = await prafrotService.getMonitoring(); - set({ monitorings: Array.isArray(data) ? data : (data.data || []) }); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - toast.error(friendlyMsg); - } finally { - set({ loading: false }); - } - }, - - createMonitoring: async (payload) => { - set({ loading: true }); - try { - await prafrotService.createMonitoring(payload); - toast.success('Monitoramento registrado com sucesso!'); - get().fetchMonitoring(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateMonitoring: async (id, payload) => { - set({ loading: true }); - try { - await prafrotService.updateMonitoring(id, payload); - toast.success('Monitoramento atualizado com sucesso!'); - get().fetchMonitoring(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - deleteMonitoring: async (id) => { - if (!window.confirm('Tem certeza que deseja excluir este monitoramento?')) return; - set({ loading: true }); - try { - await prafrotService.deleteMonitoring(id); - toast.success('Monitoramento excluído com sucesso!'); - get().fetchMonitoring(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - } -})); - - - diff --git a/src/features/Oest_Pan/hooks/usePrafrotStatistics.js b/src/features/Oest_Pan/hooks/usePrafrotStatistics.js deleted file mode 100644 index 93ae43a..0000000 --- a/src/features/Oest_Pan/hooks/usePrafrotStatistics.js +++ /dev/null @@ -1,95 +0,0 @@ -import { useState, useEffect, useCallback } from 'react'; -import { prafrotStatisticsService } from '../services/prafrotStatisticsService'; -import { toast } from 'sonner'; - -export const usePrafrotStatistics = () => { - const [loading, setLoading] = useState(true); - const [data, setData] = useState({ - totalPlacas: 0, - placasPorBase: [], - placasPorAno: [], - placasPorCategoria: [], - placasPorModelo: [], - placasPorTipo: [], - placasPorStatus: [], - placasPorManutencao: [], - placasPorProprietario: [], - // placasPorDisponibilidade: [], - placasPorUnidade: [], - placasPorSinistro: [], - placasPorManutencaoStatus: [], - quantitativoManutencao: [] - }); - - const fetchStatistics = useCallback(async () => { - setLoading(true); - try { - const [ - totalRes, - baseRes, - anoRes, - catRes, - modRes, - tipoRes, - statusRes, - manutRes, - propRes, - // dispRes, - unidRes, - sinRes, - mStatusRes, - qManutRes - ] = await Promise.all([ - prafrotStatisticsService.getTotalPlacas(), - prafrotStatisticsService.getPlacasPorBase(), - prafrotStatisticsService.getPlacasPorAno(), - prafrotStatisticsService.getPlacasPorCategoria(), - prafrotStatisticsService.getPlacasPorModelo(), - prafrotStatisticsService.getPlacasPorTipo(), - prafrotStatisticsService.getPlacasPorStatus(), - prafrotStatisticsService.getPlacasPorManutencao(), - prafrotStatisticsService.getPlacasPorProprietario(), - // prafrotStatisticsService.getPlacasPorDisponibilidade(), - prafrotStatisticsService.getPlacasPorUnidade(), - prafrotStatisticsService.getPlacasPorSinistro(), - prafrotStatisticsService.getPlacasPorManutencaoStatus(), - prafrotStatisticsService.getQuantitativoManutencao() - ]); - - setData({ - totalPlacas: totalRes?.total_placas || totalRes?.[0]?.total_placas || 0, - placasPorBase: Array.isArray(baseRes) ? baseRes : [], - placasPorAno: Array.isArray(anoRes) ? anoRes : [], - placasPorCategoria: Array.isArray(catRes) ? catRes : [], - placasPorModelo: Array.isArray(modRes) ? modRes : [], - placasPorTipo: Array.isArray(tipoRes) ? tipoRes : [], - placasPorStatus: Array.isArray(statusRes) ? statusRes : [], - placasPorManutencao: Array.isArray(manutRes) ? manutRes : [], - placasPorProprietario: Array.isArray(propRes) ? propRes : [], - // placasPorDisponibilidade: Array.isArray(dispRes) ? dispRes : [], - placasPorUnidade: Array.isArray(unidRes) ? unidRes : [], - placasPorSinistro: Array.isArray(sinRes) ? sinRes : [], - placasPorManutencaoStatus: Array.isArray(mStatusRes) ? mStatusRes : [], - quantitativoManutencao: Array.isArray(qManutRes) ? qManutRes : [] - }); - } catch (error) { - console.error('Erro ao buscar estatísticas:', error); - toast.error('Erro ao carregar dados estatísticos'); - } finally { - setLoading(false); - } - }, []); - - useEffect(() => { - fetchStatistics(); - }, [fetchStatistics]); - - return { - loading, - data, - refresh: fetchStatistics - }; -}; - - - diff --git a/src/features/Oest_Pan/hooks/useStatus.js b/src/features/Oest_Pan/hooks/useStatus.js deleted file mode 100644 index 5450eca..0000000 --- a/src/features/Oest_Pan/hooks/useStatus.js +++ /dev/null @@ -1,106 +0,0 @@ -import { create } from 'zustand'; -import { prafrotService } from '../services/prafrotService'; -import { toast } from 'sonner'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -export const useStatus = create((set, get) => ({ - statusList: [], - loading: false, - error: null, - - fetchStatus: async () => { - set({ loading: true, error: null }); - try { - const data = await prafrotService.getStatus(); - set({ statusList: Array.isArray(data) ? data : (data.data || []) }); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - toast.error(friendlyMsg); - } finally { - set({ loading: false }); - } - }, - - createStatus: async (payload) => { - set({ loading: true }); - try { - await prafrotService.createStatus(payload); - toast.success('Status registrado com sucesso!'); - get().fetchStatus(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateStatus: async (id, payload) => { - set({ loading: true }); - try { - await prafrotService.updateStatus(id, payload); - toast.success('Status atualizado com sucesso!'); - get().fetchStatus(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateStatusInline: async (id, statusFrota) => { - // Agora utiliza o endpoint em lote mesmo para uma única atualização inline - try { - await prafrotService.updateStatusBatch([id], statusFrota); - toast.success('Status atualizado!'); - get().fetchStatus(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } - }, - - updateStatusBatch: async (ids, statusFrota) => { - set({ loading: true }); - try { - await prafrotService.updateStatusBatch(ids, statusFrota); - toast.success(`${ids.length} itens atualizados com sucesso!`); - get().fetchStatus(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - deleteStatus: async (id) => { - if (!window.confirm('Tem certeza que deseja excluir este registro de status?')) return; - set({ loading: true }); - try { - await prafrotService.deleteStatus(id); - toast.success('Status excluído com sucesso!'); - get().fetchStatus(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - } -})); - - - diff --git a/src/features/Oest_Pan/hooks/useVehicles.js b/src/features/Oest_Pan/hooks/useVehicles.js deleted file mode 100644 index b1d6757..0000000 --- a/src/features/Oest_Pan/hooks/useVehicles.js +++ /dev/null @@ -1,92 +0,0 @@ -import { create } from 'zustand'; -import { prafrotService } from '../services/prafrotService'; -import { useFeedbackStore } from '../components/FeedbackNotification'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -const notify = (type, title, message) => useFeedbackStore.getState().notify(type, title, message); - -export const useVehicles = create((set, get) => ({ - vehicles: [], - loading: false, - error: null, - - fetchVehicles: async () => { - set({ loading: true, error: null }); - try { - const data = await prafrotService.getVehicles(); - // Ensure data is an array - set({ vehicles: Array.isArray(data) ? data : (data.data || []) }); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - notify('error', 'Falha na Busca', friendlyMsg); - } finally { - set({ loading: false }); - } - }, - - createVehicle: async (vehicleData) => { - set({ loading: true }); - try { - await prafrotService.createVehicle(vehicleData); - notify('success', 'Cadastro Concluído', 'Veículo registrado com sucesso no sistema.'); - get().fetchVehicles(); // Refresh list - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Falha no Cadastro', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateVehicle: async (id, vehicleData) => { - set({ loading: true }); - try { - await prafrotService.updateVehicle(id, vehicleData); - notify('success', 'Atualização!', 'Os dados do veículo foram atualizados.'); - get().fetchVehicles(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro na Atualização', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateVehicleStatus: async (id, statusFrota) => { - try { - await prafrotService.updateVehicleStatus(id, statusFrota); - notify('success', 'Status Alterado', 'O status do veículo foi modificado com sucesso.'); - get().fetchVehicles(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro de Status', friendlyMsg); - return false; - } - }, - - deleteVehicle: async (id) => { - if (!window.confirm('Tem certeza que deseja excluir este veículo?')) return; - set({ loading: true }); - try { - await prafrotService.deleteVehicle(id); - notify('success', 'Registro Removido', 'O veículo foi removido permanentemente.'); - get().fetchVehicles(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - notify('error', 'Erro na Remoção', friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - } -})); - - - diff --git a/src/features/Oest_Pan/hooks/useWorkshops.js b/src/features/Oest_Pan/hooks/useWorkshops.js deleted file mode 100644 index 38f72c8..0000000 --- a/src/features/Oest_Pan/hooks/useWorkshops.js +++ /dev/null @@ -1,76 +0,0 @@ -import { create } from 'zustand'; -import { prafrotService } from '../services/prafrotService'; -import { toast } from 'sonner'; -import { extractFriendlyMessage } from '../utils/errorMessages'; - -export const useWorkshops = create((set, get) => ({ - workshops: [], - loading: false, - error: null, - - fetchWorkshops: async () => { - set({ loading: true, error: null }); - try { - const data = await prafrotService.getWorkshops(); - set({ workshops: Array.isArray(data) ? data : (data.data || []) }); - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - set({ error: friendlyMsg }); - toast.error(friendlyMsg); - } finally { - set({ loading: false }); - } - }, - - createWorkshop: async (payload) => { - set({ loading: true }); - try { - await prafrotService.createWorkshop(payload); - toast.success('Oficina registrada com sucesso!'); - get().fetchWorkshops(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - updateWorkshop: async (id, payload) => { - set({ loading: true }); - try { - await prafrotService.updateWorkshop(id, payload); - toast.success('Oficina atualizada com sucesso!'); - get().fetchWorkshops(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - }, - - deleteWorkshop: async (id) => { - if (!window.confirm('Tem certeza que deseja excluir esta oficina?')) return; - set({ loading: true }); - try { - await prafrotService.deleteWorkshop(id); - toast.success('Oficina excluída com sucesso!'); - get().fetchWorkshops(); - return true; - } catch (error) { - const friendlyMsg = extractFriendlyMessage(error); - toast.error(friendlyMsg); - return false; - } finally { - set({ loading: false }); - } - } -})); - - - diff --git a/src/features/Oest_Pan/layout/PrafrotLayout.jsx b/src/features/Oest_Pan/layout/PrafrotLayout.jsx deleted file mode 100644 index 082e0cd..0000000 --- a/src/features/Oest_Pan/layout/PrafrotLayout.jsx +++ /dev/null @@ -1,92 +0,0 @@ -import React, { useState, useEffect } from 'react'; -import { Outlet } from 'react-router-dom'; -import { - Menu, - Sun, - Moon, -} from 'lucide-react'; -import { cn } from '@/lib/utils'; -import { useDocumentMetadata } from '@/hooks/useDocumentMetadata'; -import { OestPanSidebar } from '../components/PrafrotSidebar'; -import { FeedbackContainer } from '../components/FeedbackNotification'; - -export const OestPanLayout = () => { - useDocumentMetadata('Oeste Pan', 'oest-pan'); - const [isSidebarCollapsed, setIsSidebarCollapsed] = useState(false); - const [isDarkMode, setIsDarkMode] = useState(true); - - useEffect(() => { - if (isDarkMode) { - document.documentElement.classList.add('dark'); - } else { - document.documentElement.classList.remove('dark'); - } - }, [isDarkMode]); - - return ( -
- - {/* New Sidebar Component */} - setIsSidebarCollapsed(!isSidebarCollapsed)} - /> - - {/* Main Content Area */} -
- {/* Header */} -
- {/*
- -
*/} - -
- {/* Theme Toggle */} - -
-
- - {/* Content */} -
-
-
- -
-
-
-
-
- ); -}; - - - diff --git a/src/features/Oest_Pan/routes.jsx b/src/features/Oest_Pan/routes.jsx deleted file mode 100644 index f74a3e4..0000000 --- a/src/features/Oest_Pan/routes.jsx +++ /dev/null @@ -1,69 +0,0 @@ -import { lazy, Suspense } from 'react'; -import { Route, Routes, Navigate } from 'react-router-dom'; -import { OestPanLayout } from './layout/PrafrotLayout'; -import { Zap } from 'lucide-react'; - -// Lazy loading views -const VehiclesView = lazy(() => import('./views/VehiclesView')); -const MaintenanceView = lazy(() => import('./views/MaintenanceView')); -const AvailabilityView = lazy(() => import('./views/AvailabilityView')); -const MokiView = lazy(() => import('./views/MokiView')); -const StatusView = lazy(() => import('./views/StatusView')); -const MonitoringView = lazy(() => import('./views/MonitoringView')); -const WorkshopsView = lazy(() => import('./views/WorkshopsView')); -const ClaimsView = lazy(() => import('./views/ClaimsView')); -const DriversView = lazy(() => import('./views/DriversView')); -const LoginView = lazy(() => import('./views/LoginView')); -const StatisticsView = lazy(() => import('./views/StatisticsView')); -const DispatcherView = lazy(() => import('./views/DispatcherView')); -const ConfigView = lazy(() => import('./views/ConfigView')); - -// Loading component matching Prafrot theme -const OestPanLoader = () => ( -
-
-
-
- -
-
-
-
- Oeste Pan System - Carregando... -
-
-
-); - -export const OestPanRoutes = () => { - return ( - }> - - }> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - } /> - - } /> - } /> - - - - ); -}; - -// Export LoginView separately for use in App.jsx -export { LoginView }; - - - diff --git a/src/features/Oest_Pan/services/dispatcherService.js b/src/features/Oest_Pan/services/dispatcherService.js deleted file mode 100644 index aeec0a9..0000000 --- a/src/features/Oest_Pan/services/dispatcherService.js +++ /dev/null @@ -1,17 +0,0 @@ - -import api from '@/services/api'; -import { handleRequest } from '@/services/serviceUtils'; - -const ENDPOINT = '/dispatcher'; - -export const dispatcherService = { - getDispatcherData: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`${ENDPOINT}/apresentar`); - return data; - } - }) -}; - - - diff --git a/src/features/Oest_Pan/services/prafrotService.js b/src/features/Oest_Pan/services/prafrotService.js deleted file mode 100644 index 39c6f4d..0000000 --- a/src/features/Oest_Pan/services/prafrotService.js +++ /dev/null @@ -1,410 +0,0 @@ -import api from '@/services/api'; -import { handleRequest } from '@/services/serviceUtils'; - -// Endpoints definidos pelo usuário -const ENDPOINTS = { - VEHICLES: '/cadastro_frota', - MAINTENANCE: '/manutencao_frota', - AVAILABILITY: '/disponibilidade_frota', - MOKI: '/moki_frota', - STATUS: '/status_frota', - MONITORING: '/monitoramento_frota', - CLAIMS: '/sinistro_devolucao_venda_frota', - WORKSHOPS: '/oficinas_frota', - AUTH: '/auth_monitoramento' -}; - -export const prafrotService = { - // --- Autenticação --- - login: (credentials) => handleRequest({ - apiFn: async () => { - const { data } = await api.post(ENDPOINTS.AUTH, credentials); - return data; - } - }), - - // --- Cadastro de Frota (Veículos) --- - getVehicles: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`${ENDPOINTS.VEHICLES}/apresentar`); - return data; - } - }), - createVehicle: (payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.post(ENDPOINTS.VEHICLES, payload); - return data; - } - }), - updateVehicle: (id, payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.VEHICLES}/${id}`, payload); - return data; - } - }), - deleteVehicle: (id) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.VEHICLES}/delete/${id}`); - return data; - } - }), - - // --- Manutenção --- - getMaintenance: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`${ENDPOINTS.MAINTENANCE}/apresentar`); - return data; - } - }), - calcularDifOrcamento: (orcamento_inicial, orcamento_final) => handleRequest({ - apiFn: async () => { - const { data } = await api.post(`${ENDPOINTS.MAINTENANCE}/calcular_dif_orcamento`, { - orcamento_inicial, - orcamento_final - }); - return data; - } - }), - createMaintenance: (payload, files = null) => handleRequest({ - apiFn: async () => { - // Envia previsao_entrega diretamente - const body = { ...payload }; - if (files && (files.pdf_orcamento || files.nota_fiscal)) { - const form = new FormData(); - form.append('data_json', JSON.stringify(body)); - if (files.pdf_orcamento instanceof File) form.append('pdf_orcamento', files.pdf_orcamento); - if (files.nota_fiscal instanceof File) form.append('nota_fiscal', files.nota_fiscal); - const { data } = await api.post(ENDPOINTS.MAINTENANCE, form, { - headers: { 'Content-Type': 'multipart/form-data' } - }); - return data; - } - const { data } = await api.post(ENDPOINTS.MAINTENANCE, body); - return data; - } - }), - updateMaintenance: (id, payload, files = null) => handleRequest({ - apiFn: async () => { - // Envia previsao_entrega diretamente (o backend deve estar atualizado ou o frontend deve padronizar) - const body = { ...payload }; - if (files && (files.pdf_orcamento || files.nota_fiscal)) { - const form = new FormData(); - form.append('data_json', JSON.stringify(body)); - if (files.pdf_orcamento instanceof File) form.append('pdf_orcamento', files.pdf_orcamento); - if (files.nota_fiscal instanceof File) form.append('nota_fiscal', files.nota_fiscal); - const { data } = await api.put(`${ENDPOINTS.MAINTENANCE}/${id}`, form, { - headers: { 'Content-Type': 'multipart/form-data' } - }); - return data; - } - const { data } = await api.put(`${ENDPOINTS.MAINTENANCE}/${id}`, body); - return data; - } - }), - updateMaintenanceBatch: (ids, status) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.MAINTENANCE}/edit/em_lote`, { ids, status }); - return data; - } - }), - deleteMaintenance: (id) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.MAINTENANCE}/delete/${id}`); - return data; - } - }), - // Fechar/Abrir manutenção - fecharManutencao: (id) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.MAINTENANCE}/fechar_manutencao/${id}`); - return data; - } - }), - abrirManutencao: (id) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.MAINTENANCE}/abrir_manutencao/${id}`); - return data; - } - }), - getAbertoFechado: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`${ENDPOINTS.MAINTENANCE}/aberto_fechado/apresentar`); - return data; - } - }), - // Histórico de manutenção - getHistoricoCompleto: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get('/historico/completo'); - return data; - } - }), - getHistoricoDetalhado: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get('/historico/detalhado'); - return data; - } - }), - getHistoricoResumido: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get('/historico/resumido'); - return data; - } - }), - getHistoricoEstatisticas: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get('/historico/estatisticas'); - return data; - } - }), - getHistoricoPeriodo: (params) => handleRequest({ - apiFn: async () => { - const { data } = await api.get('/historico/periodo', { params }); - return data; - } - }), - getHistoricoTop: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get('/historico/top'); - return data; - } - }), - getHistoricoPorPlaca: (placa) => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`/historico/placa/${placa}`); - return data; - } - }), - - // --- Disponibilidade --- - getAvailability: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`${ENDPOINTS.AVAILABILITY}/apresentar`); - return data; - } - }), - createAvailability: (payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.post(ENDPOINTS.AVAILABILITY, payload); - return data; - } - }), - updateAvailability: (id, payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.AVAILABILITY}/${id}`, payload); - return data; - } - }), - deleteAvailability: (id) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.AVAILABILITY}/delete/${id}`); - return data; - } - }), - - // --- Moki (Checklists) --- - getMoki: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`${ENDPOINTS.MOKI}/apresentar`); - return data; - } - }), - createMoki: (payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.post(ENDPOINTS.MOKI, payload); - return data; - } - }), - updateMoki: (id, payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.MOKI}/${id}`, payload); - return data; - } - }), - deleteMoki: (id) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.MOKI}/delete/${id}`); - return data; - } - }), - - // --- Status Frota --- - getStatus: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`${ENDPOINTS.STATUS}/apresentar`); - return data; - } - }), - createStatus: (payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.post(ENDPOINTS.STATUS, payload); - return data; - } - }), - updateStatus: (id, payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.STATUS}/${id}`, payload); - return data; - } - }), - updateStatusBatch: (ids, statusFrota) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.STATUS}/edit/em_lote`, { ids, status_frota: statusFrota }); - return data; - } - }), - deleteStatus: (id) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.STATUS}/delete/${id}`); - return data; - } - }), - - // --- Monitoramento --- - getMonitoring: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`${ENDPOINTS.MONITORING}/apresentar`); - return data; - } - }), - createMonitoring: (payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.post(ENDPOINTS.MONITORING, payload); - return data; - } - }), - updateMonitoring: (id, payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.MONITORING}/${id}`, payload); - return data; - } - }), - deleteMonitoring: (id) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.MONITORING}/delete/${id}`); - return data; - } - }), - - // --- Sinistro / Devolução --- - getClaims: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`${ENDPOINTS.CLAIMS}/apresentar`); - return data; - } - }), - createClaims: (payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.post(ENDPOINTS.CLAIMS, payload); - return data; - } - }), - updateClaims: (id, payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.CLAIMS}/${id}`, payload); - return data; - } - }), - deleteClaim: (id) => handleRequest({ - apiFn: async () => { - // Usando a rota específica fornecida pelo usuário para exclusão de sinistro - const { data } = await api.put(`/sinistro_frota/delete/${id}`); - return data; - } - }), - - // --- Oficinas --- - getWorkshops: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`${ENDPOINTS.WORKSHOPS}/apresentar`); - return data; - } - }), - createWorkshop: (payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.post(ENDPOINTS.WORKSHOPS, payload); - return data; - } - }), - updateWorkshop: (id, payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.WORKSHOPS}/${id}`, payload); - return data; - } - }), - deleteWorkshop: (id) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`${ENDPOINTS.WORKSHOPS}/delete/${id}`); - return data; - } - }), - - // --- Motoristas (Drivers) --- - getDrivers: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get('/motoristas/listagem'); - return data; - } - }), - - // --- Listas de Configuração (Config Lists) --- - getListasConfig: () => handleRequest({ - apiFn: async () => { - const { data } = await api.get('/listas_frota/config'); - return data; - } - }), - - getListasByRoute: (route) => handleRequest({ - apiFn: async () => { - const { data } = await api.get(`/listas_frota/${route}`); - return data; - } - }), - - createLista: (route, payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.post(`/listas_frota/${route}`, payload); - return data; - } - }), - - updateLista: (route, id, payload) => handleRequest({ - apiFn: async () => { - const { data } = await api.put(`/listas_frota/${route}/${id}`, payload); - return data; - } - }), - - deleteLista: (route, id) => handleRequest({ - apiFn: async () => { - const { data } = await api.delete(`/listas_frota/${route}/${id}`); - return data; - } - }), - - // --- Update Vehicle Status (Inline Edit) --- - updateVehicleStatus: (idVehicle, statusFrota) => handleRequest({ - apiFn: async () => { - const { data } = await api.put('/status_frota/edit/em_lote', { - ids: [idVehicle], - status_frota: statusFrota - }); - return data; - } - }), - - // --- Update Status Batch (Bulk Edit) --- - updateStatusBatch: (ids, statusFrota) => handleRequest({ - apiFn: async () => { - const { data } = await api.put('/status_frota/edit/em_lote', { - ids, - status_frota: statusFrota - }); - return data; - } - }) -}; - - - diff --git a/src/features/Oest_Pan/services/prafrotStatisticsService.js b/src/features/Oest_Pan/services/prafrotStatisticsService.js deleted file mode 100644 index 1cb4791..0000000 --- a/src/features/Oest_Pan/services/prafrotStatisticsService.js +++ /dev/null @@ -1,80 +0,0 @@ -import api from '@/services/api'; -import { handleRequest } from '@/services/serviceUtils'; - -const ENDPOINTS = { - TOTAL_PLACAS: '/dashs_frota/cadastro_frota/total_placas', - PLACAS_POR_BASE: '/dashs_frota/cadastro_frota/placas_por_base', - PLACAS_POR_ANO: '/dashs_frota/cadastro_frota/placas_por_ano', - PLACAS_POR_CATEGORIA: '/dashs_frota/cadastro_frota/placas_por_categoria', - PLACAS_POR_MODELO: '/dashs_frota/cadastro_frota/placas_por_modelo', - PLACAS_POR_TIPO: '/dashs_frota/cadastro_frota/placas_por_tipo', - PLACAS_POR_STATUS: '/dashs_frota/status_frota/placas_por_status', - PLACAS_POR_MANUTENCAO: '/dashs_frota/status_frota/placas_por_manutencao', - PLACAS_POR_PROPRIETARIO: '/dashs_frota/status_frota/placas_por_proprietario', -// PLACAS_POR_DISPONIBILIDADE: '/dashs_frota/disponibilidade/placas', - PLACAS_POR_UNIDADE: '/dashs_frota/monitoramento/placas_por_unidade', - PLACAS_POR_SINISTRO: '/dashs_frota/sinistro/placas_por_status', - PLACAS_POR_MANUTENCAO_STATUS: '/dashs_frota/manutencao/placas_por_manutencao_status', - QUANTITATIVO_MANUTENCAO: '/quantitativo_manutencao' -}; - -export const prafrotStatisticsService = { - getTotalPlacas: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.TOTAL_PLACAS) - }), - - getPlacasPorBase: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_BASE) - }), - - getPlacasPorAno: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_ANO) - }), - - getPlacasPorCategoria: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_CATEGORIA) - }), - - getPlacasPorModelo: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_MODELO) - }), - - getPlacasPorTipo: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_TIPO) - }), - - getPlacasPorStatus: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_STATUS) - }), - - getPlacasPorManutencao: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_MANUTENCAO) - }), - - getPlacasPorProprietario: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_PROPRIETARIO) - }), - -/* getPlacasPorDisponibilidade: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_DISPONIBILIDADE) - }), */ - - getPlacasPorUnidade: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_UNIDADE) - }), - - getPlacasPorSinistro: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_SINISTRO) - }), - - getPlacasPorManutencaoStatus: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.PLACAS_POR_MANUTENCAO_STATUS) - }), - - getQuantitativoManutencao: () => handleRequest({ - apiFn: () => api.get(ENDPOINTS.QUANTITATIVO_MANUTENCAO) - }) -}; - - - diff --git a/src/features/Oest_Pan/utils/errorMessages.js b/src/features/Oest_Pan/utils/errorMessages.js deleted file mode 100644 index b22aae2..0000000 --- a/src/features/Oest_Pan/utils/errorMessages.js +++ /dev/null @@ -1,129 +0,0 @@ -/** - * Utilitário para traduzir erros HTTP em mensagens amigáveis ao usuário - * Sem expor detalhes técnicos ou códigos do servidor - */ - -/** - * Traduz um erro HTTP em uma mensagem amigável ao usuário - * @param {Error} error - Objeto de erro do Axios ou erro genérico - * @returns {string} Mensagem amigável ao usuário - */ -export const getFriendlyErrorMessage = (error) => { - // Se o erro não tiver resposta (erro de rede, timeout, etc.) - if (!error.response) { - if (error.code === 'ECONNABORTED' || error.message?.includes('timeout')) { - return 'A operação está demorando mais que o esperado. Por favor, tente novamente.'; - } - if (error.message?.includes('Network Error') || error.message?.includes('ERR_NETWORK')) { - return 'Não foi possível conectar ao servidor. Verifique sua conexão com a internet.'; - } - return 'Não foi possível completar a operação. Por favor, tente novamente.'; - } - - const status = error.response?.status; - const errorData = error.response?.data; - - // Tenta usar mensagem do backend se for amigável - if (errorData?.message && typeof errorData.message === 'string') { - // Verifica se a mensagem não contém códigos técnicos - const technicalPatterns = [ - /^HTTP \d{3}/, - /^Error \d{3}/, - /status code/i, - /status: \d{3}/i, - /\[.*\]/, - /at .*\.js/, - /line \d+/i, - /stack trace/i - ]; - - const isTechnical = technicalPatterns.some(pattern => pattern.test(errorData.message)); - if (!isTechnical) { - return errorData.message; - } - } - - // Traduz códigos de status HTTP em mensagens amigáveis - switch (status) { - case 400: - // Bad Request - Dados inválidos - if (errorData?.errors && Array.isArray(errorData.errors)) { - const errors = errorData.errors.map(e => e.message || e).join(', '); - return `Verifique os dados informados: ${errors}`; - } - return 'Os dados informados estão incorretos. Por favor, verifique e tente novamente.'; - - case 401: - // Unauthorized - Não autorizado - return 'Sua sessão expirou. Por favor, faça login novamente.'; - - case 403: - // Forbidden - Acesso negado - return 'Você não tem permissão para realizar esta operação.'; - - case 404: - // Not Found - Recurso não encontrado - return 'A informação solicitada não foi encontrada.'; - - case 409: - // Conflict - Conflito - return 'Já existe um registro com essas informações. Verifique os dados e tente novamente.'; - - case 422: - // Unprocessable Entity - Erro de validação - if (errorData?.errors && Array.isArray(errorData.errors)) { - const errors = errorData.errors.map(e => e.message || e).join(', '); - return `Dados inválidos: ${errors}`; - } - return 'Os dados informados não são válidos. Verifique e tente novamente.'; - - case 500: - // Internal Server Error - return 'Ocorreu um erro no servidor. Nossa equipe foi notificada. Por favor, tente novamente em alguns instantes.'; - - case 502: - // Bad Gateway - return 'O servidor está temporariamente indisponível. Tente novamente em alguns instantes.'; - - case 503: - // Service Unavailable - return 'O serviço está temporariamente indisponível. Tente novamente em alguns instantes.'; - - case 504: - // Gateway Timeout - return 'A operação está demorando mais que o esperado. Por favor, tente novamente.'; - - default: - // Outros erros HTTP - if (status >= 500) { - return 'Ocorreu um erro no servidor. Por favor, tente novamente mais tarde.'; - } - if (status >= 400) { - return 'Não foi possível processar sua solicitação. Verifique os dados e tente novamente.'; - } - return 'Ocorreu um erro inesperado. Por favor, tente novamente.'; - } -}; - -/** - * Extrai uma mensagem de erro amigável, priorizando mensagens do backend - * mas garantindo que sejam amigáveis - * @param {Error} error - Objeto de erro - * @returns {string} Mensagem amigável - */ -export const extractFriendlyMessage = (error) => { - // Se já temos uma mensagem amigável do backend, usa ela - if (error.response?.data?.message && typeof error.response.data.message === 'string') { - const msg = error.response.data.message; - // Verifica se não é uma mensagem técnica - if (!msg.match(/^(HTTP|Error|status|\[|at |line )/i)) { - return msg; - } - } - - // Caso contrário, usa a tradução baseada no status - return getFriendlyErrorMessage(error); -}; - - - diff --git a/src/features/Oest_Pan/views/AvailabilityView.jsx b/src/features/Oest_Pan/views/AvailabilityView.jsx deleted file mode 100644 index 1a55a04..0000000 --- a/src/features/Oest_Pan/views/AvailabilityView.jsx +++ /dev/null @@ -1,211 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useAvailability } from '../hooks/useAvailability'; -import { useVehicles } from '../hooks/useVehicles'; -import ExcelTable from '../components/ExcelTable'; -import { Plus, Search, Calendar as CalendarIcon } from 'lucide-react'; -import { - Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription -} from "@/components/ui/dialog"; -import { toast } from 'sonner'; - -// Reusing styled components locally -const DarkInput = ({ label, ...props }) => ( -
- {label && } - -
-); - -const DarkSelect = ({ label, options, value, onChange }) => ( -
- {label && } - -
-); - -const DarkButton = ({ children, variant = 'primary', className = '', ...props }) => { - const baseClass = "px-4 py-2 rounded-lg font-bold text-sm transition-all shadow-lg active:scale-95 flex items-center justify-center gap-2"; - const variants = { - primary: "bg-orange-600 hover:bg-orange-500 text-white shadow-orange-500/10", - secondary: "bg-slate-100 dark:bg-[#2a2a2a] hover:bg-slate-200 dark:hover:bg-[#333] text-slate-700 dark:text-slate-200 border border-slate-200 dark:border-[#333]", - ghost: "bg-transparent hover:bg-slate-100 dark:hover:bg-[#2a2a2a] text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-white" - }; - - return ( - - ); -}; - -export default function AvailabilityView() { - const { availabilities, fetchAvailabilities, createAvailability, updateAvailability, deleteAvailability } = useAvailability(); - const { vehicles, fetchVehicles } = useVehicles(); - const [searchTerm, setSearchTerm] = useState(''); - const [isModalOpen, setIsModalOpen] = useState(false); - const [editingItem, setEditingItem] = useState(null); - - const initialFormState = { - iddisponibilidade_frota: '', idveiculo_frota: '', placa: '', disponibilidade: '', status_disponibilidade: 'Disponível' - }; - const [formData, setFormData] = useState(initialFormState); - - useEffect(() => { - fetchAvailabilities(); - fetchVehicles(); - }, []); - - const handleOpenModal = (item = null) => { - if (item) { - setEditingItem(item); - setFormData({ ...initialFormState, ...item }); - } else { - setEditingItem(null); - setFormData(initialFormState); - } - setIsModalOpen(true); - }; - - const handlePlateChange = (e) => { - const val = e.target.value; - const foundVehicle = vehicles.find(v => v.placa === val); - setFormData({ - ...formData, - placa: val, - idveiculo_frota: foundVehicle ? foundVehicle.idveiculo_frota : '' - }); - }; - - const handleSubmit = async (e) => { - e.preventDefault(); - const payload = { - ...formData, - iddisponibilidade_frota: formData.iddisponibilidade_frota ? Number(formData.iddisponibilidade_frota) : undefined, - idveiculo_frota: formData.idveiculo_frota ? Number(formData.idveiculo_frota) : null - }; - - let success; - if (editingItem) { - success = await updateAvailability(Number(editingItem.iddisponibilidade_frota || editingItem.id), payload); - } else { - success = await createAvailability(payload); - } - if (success) setIsModalOpen(false); - }; - - const filteredData = availabilities.filter(item => - item.placa?.toLowerCase().includes(searchTerm.toLowerCase()) - ); - - return ( -
-
-
-

Disponibilidade e Agenda

-

Visualização de disponibilidade da frota.

-
-
-
- - setSearchTerm(e.target.value)} - /> -
- handleOpenModal()}> - Novo Registro - -
-
- -
- row.disponibilidade?.split('T')[0] }, - { header: 'STATUS', field: 'status_disponibilidade', width: '150px', render: (row) => ( - - {row.status_disponibilidade} - - )} - ]} - filterDefs={[ - { field: 'placa', label: 'Placa', type: 'text', placeholder: 'Buscar placa...' }, - { field: 'status_disponibilidade', label: 'Status', type: 'select' }, - ]} - onEdit={handleOpenModal} - onDelete={(item) => deleteAvailability(item.iddisponibilidade_frota)} - /> -
- - - - - - {editingItem ? `Editando ID: ${editingItem.iddisponibilidade_frota}` : 'Nova Disponibilidade'} - - - -
- {formData.iddisponibilidade_frota && ( -
-

ID do Registro

-

{formData.iddisponibilidade_frota}

-
- )} -
- - - - {vehicles.map(v => ( - - {formData.idveiculo_frota && ( - Veículo vinculado (ID: {formData.idveiculo_frota}) - )} -
- - setFormData({...formData, disponibilidade: e.target.value})} /> - setFormData({...formData, status_disponibilidade: v})} /> - - - setIsModalOpen(false)}>Cancelar - Salvar - - -
-
-
- ); -} - - - diff --git a/src/features/Oest_Pan/views/ClaimsView.jsx b/src/features/Oest_Pan/views/ClaimsView.jsx deleted file mode 100644 index e6b0f13..0000000 --- a/src/features/Oest_Pan/views/ClaimsView.jsx +++ /dev/null @@ -1,380 +0,0 @@ -import React, { useEffect, useState } from 'react'; -import { useClaims } from '../hooks/useClaims'; -import { useVehicles } from '../hooks/useVehicles'; -import { useDrivers } from '../hooks/useDrivers'; -import AutocompleteInput from '../components/AutocompleteInput'; -import ExcelTable from '../components/ExcelTable'; -import { Plus, Search } from 'lucide-react'; -import { - Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter, DialogDescription -} from "@/components/ui/dialog"; -import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; - -// Reusing styled components locally -const DarkInput = ({ label, ...props }) => ( -
- {label && } - -
-); - -const DarkSelect = ({ label, options, value, onChange }) => ( -
- {label && } - -
-); - -const DarkButton = ({ children, variant = 'primary', className = '', ...props }) => { - const baseClass = "px-4 py-2 rounded-xl font-bold text-sm transition-all shadow-lg active:scale-95 flex items-center justify-center gap-2"; - const variants = { - primary: "bg-orange-600 hover:bg-orange-500 text-white shadow-orange-500/10", - secondary: "bg-slate-100 dark:bg-[#2a2a2a] hover:bg-slate-200 dark:hover:bg-[#333] text-slate-700 dark:text-slate-200 border border-slate-200 dark:border-[#333]", - ghost: "bg-transparent hover:bg-slate-100 dark:hover:bg-[#2a2a2a] text-slate-500 dark:text-slate-400 hover:text-slate-700 dark:hover:text-white" - }; - - return ( - - ); -}; - -export default function ClaimsView() { - const { claims, loading, fetchClaims, createClaim, updateClaim, deleteClaim } = useClaims(); - const [searchTerm, setSearchTerm] = useState(''); - const [isModalOpen, setIsModalOpen] = useState(false); - const [editingItem, setEditingItem] = useState(null); - - const initialFormState = { - agendamento: '', ano_fabricacao: '', ano_modelo: '', atuacao: '', base: '', - categoria: '', chassi: '', cmou: '', combustivel: '', contrato: '', - coordenador: '', cor: '', d: '', data_entrada: '', data_limite: '', - dias_parado: '', dispatcher: '', fabricante: '', fiscal_operacao: '', - geotab: 'NÃO', gestor: '', id_rota: '', idsinistro_devolucao_frota: '', - km_atual: '', km_preventiva: '', melifleet: 'No', modelo: '', motorista: '', - obs: '', placa: '', pooltrack: 'NÃO', previsao: '', primeira_locacao: '', - proprietario: '', prox_preventiva: '', renavan: '', sascar: 'NÃO', - status: '', t4s: 'NÃO', tipo_frota: '', tipo_placa: '', u: 'SIM', - uf: '', ultima_preventiva: '', valor_aluguel: '', valor_fipe: '' - }; - const [formData, setFormData] = useState(initialFormState); - - const { vehicles, fetchVehicles } = useVehicles(); - const { drivers, fetchDrivers } = useDrivers(); - - useEffect(() => { - fetchClaims(); - fetchVehicles(); - fetchDrivers(); - }, []); - - const handleVehicleSelect = (vehicle) => { - if (!vehicle) return; - setFormData(prev => ({ - ...prev, - placa: vehicle.placa || prev.placa, - chassi: vehicle.chassi || prev.chassi, - renavan: vehicle.renavam || vehicle.renavan || prev.renavan, - modelo: vehicle.modelo || vehicle.mod_veiculo || prev.modelo, - fabricante: vehicle.fabricante || vehicle.marca || prev.fabricante, - cor: vehicle.cor || prev.cor, - ano_fabricacao: vehicle.ano_fabricacao || vehicle.ano_fab || prev.ano_fabricacao, - ano_modelo: vehicle.ano_modelo || vehicle.ano_mod || prev.ano_modelo, - combustivel: vehicle.combustivel || prev.combustivel, - categoria: vehicle.categoria || vehicle.cat_veiculo || prev.categoria, - tipo_placa: vehicle.tipo_placa || prev.tipo_placa, - proprietario: vehicle.proprietario || prev.proprietario, - // Add more fields if available in vehicle object - })); - }; - - const handleOpenModal = (item = null) => { - if (item) { - setEditingItem(item); - setFormData({ ...initialFormState, ...item }); - } else { - setEditingItem(null); - setFormData(initialFormState); - } - setIsModalOpen(true); - }; - - const handleSubmit = async (e) => { - e.preventDefault(); - let success; - if (editingItem) { - success = await updateClaim(editingItem.idsinistro_devolucao_frota, formData); - } else { - success = await createClaim(formData); - } - if (success) setIsModalOpen(false); - }; - - const filteredData = Array.isArray(claims) ? claims.filter(item => - item.placa?.toLowerCase().includes(searchTerm.toLowerCase()) || - item.motorista?.toLowerCase().includes(searchTerm.toLowerCase()) - ) : []; - - return ( -
-
-
-

Sinistros e Devoluções

-

Gestão de incidentes e movimentações de frota.

-
-
-
- - setSearchTerm(e.target.value)} - /> -
- handleOpenModal()}> - Novo Registro - -
-
- -
- ( - - {row.status} - - )}, - { header: 'MOTORISTA', field: 'motorista', width: '150px' }, - { header: 'DATA ENTRADA', field: 'data_entrada', width: '120px' }, - { header: 'PREVISÃO', field: 'previsao', width: '120px' }, - { header: 'DIAS PARADO', field: 'dias_parado', width: '100px', className: 'text-red-400 font-bold' }, - { header: 'MODELO', field: 'modelo', width: '120px' }, - { header: 'FABRICANTE', field: 'fabricante', width: '120px' }, - { header: 'COMBUSTÍVEL', field: 'combustivel', width: '120px' }, - { header: 'ANO FAB.', field: 'ano_fabricacao', width: '80px' }, - { header: 'ANO MOD.', field: 'ano_modelo', width: '80px' }, - { header: 'TIPO FROTA', field: 'tipo_frota', width: '120px' }, - { header: 'CATEGORIA', field: 'categoria', width: '120px' }, - { header: 'TIPO PLACA', field: 'tipo_placa', width: '100px' }, - { header: 'BASE', field: 'base', width: '100px' }, - { header: 'UF', field: 'uf', width: '60px' }, - { header: 'GESTOR', field: 'gestor', width: '140px' }, - { header: 'COORDENADOR', field: 'coordenador', width: '140px' }, - { header: 'DISPATCHER', field: 'dispatcher', width: '140px' }, - { header: 'FISCAL OPER.', field: 'fiscal_operacao', width: '140px' }, - { header: 'ATUAÇÃO', field: 'atuacao', width: '140px' }, - { header: 'ID ROTA', field: 'id_rota', width: '120px' }, - { header: 'AGENDAMENTO', field: 'agendamento', width: '120px' }, - { header: 'PROPRIETÁRIO', field: 'proprietario', width: '140px' }, - { header: 'CONTRATO', field: 'contrato', width: '120px' }, - { header: 'DATA LIMITE', field: 'data_limite', width: '110px' }, - { header: '1ª LOCAÇÃO', field: 'primeira_locacao', width: '110px' }, - { header: 'KM ATUAL', field: 'km_atual', width: '100px' }, - { header: 'KM PREV.', field: 'km_preventiva', width: '100px' }, - { header: 'ÚLT. PREV.', field: 'ultima_preventiva', width: '100px' }, - { header: 'PRÓX. PREV.', field: 'prox_preventiva', width: '100px' }, - { header: 'MELIFLEET', field: 'melifleet', width: '100px' }, - { header: 'GEOTAB', field: 'geotab', width: '80px' }, - { header: 'SASCAR', field: 'sascar', width: '80px' }, - { header: 'POOLTRACK', field: 'pooltrack', width: '80px' }, - { header: 'T4S', field: 't4s', width: '80px' }, - { header: 'RENAVAN', field: 'renavan', width: '150px' }, - { header: 'CHASSI', field: 'chassi', width: '180px' }, - { header: 'CMOU', field: 'cmou', width: '80px' }, - { header: 'D', field: 'd', width: '100px' }, - { header: 'U', field: 'u', width: '100px' }, - { header: 'VALOR ALUGUEL', field: 'valor_aluguel', width: '120px' }, - { header: 'VALOR FIPE', field: 'valor_fipe', width: '120px' }, - { header: 'OBS', field: 'obs', width: '200px' }, - ]} - filterDefs={[ - { field: 'placa', label: 'Placa', type: 'text', placeholder: 'Buscar placa...' }, - { field: 'status', label: 'Status', type: 'select' }, - { field: 'base', label: 'Base', type: 'select' }, - ]} - onEdit={handleOpenModal} - onDelete={(item) => deleteClaim(item.idsinistro_devolucao_frota)} - /> -
- - - - - - {editingItem ? 'Editar Registro' : 'Novo Registro (Sinistro/Devolução)'} - - - Informações detalhadas sobre o evento. - - - -
- - - {['basicos', 'ocorrencia', 'operacional', 'financeiro', 'tecnico'].map(tab => ( - - {tab} - - ))} - - -
- -
- setFormData({...formData, placa: v})} - options={vehicles} - displayKey="placa" - valueKey="placa" - searchKeys={['placa']} - onSelect={handleVehicleSelect} - required - /> - setFormData({...formData, chassi: e.target.value})} /> - setFormData({...formData, renavan: e.target.value})} /> -
-
- setFormData({...formData, modelo: e.target.value})} /> - setFormData({...formData, fabricante: e.target.value})} /> - setFormData({...formData, cor: e.target.value})} /> -
-
- setFormData({...formData, ano_fabricacao: e.target.value})} /> - setFormData({...formData, ano_modelo: e.target.value})} /> - setFormData({...formData, combustivel: e.target.value})} /> -
-
- setFormData({...formData, categoria: e.target.value})} /> - setFormData({...formData, tipo_placa: e.target.value})} /> -
-
- - -
- setFormData({...formData, status: v})} /> - setFormData({...formData, data_entrada: e.target.value})} /> - setFormData({...formData, previsao: e.target.value})} /> -
-
- setFormData({...formData, dias_parado: e.target.value})} /> - setFormData({...formData, agendamento: e.target.value})} /> -
-
- setFormData({...formData, d: e.target.value})} /> - setFormData({...formData, u: e.target.value})} /> -
-
- -