transcription-cost-usage-re.../frontend/components/transcription-table.tsx

433 lines
15 KiB
TypeScript
Raw Normal View History

2025-06-09 11:13:05 +00:00
"use client"
import { useState } from "react"
import { Button } from "@/components/ui/button"
import { Input } from "@/components/ui/input"
import { Label } from "@/components/ui/label"
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Alert, AlertDescription } from "@/components/ui/alert"
import { Download, Search, Loader2 } from "lucide-react"
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:5000/api/v1"
// Primeiro, vamos atualizar as interfaces para os diferentes tipos de dados
// Substitua a interface TranscriptionData existente com estas interfaces:
interface ClientTranscriptionData {
uniqueid: string
src: string
dst: string
start_call: string
total_billsec: number
total_min: string
}
interface HitTranscriptionData {
uniqueid: string
src: string
dst: string
start_call: string
end_call: string
total_billsec: number
companyId: string
custo_HIT: number
price: number
client_price: string
}
type TranscriptionData = ClientTranscriptionData | HitTranscriptionData
// Primeiro, vamos atualizar as interfaces para refletir a nova estrutura da API:
interface PaginationInfo {
total: number
page: number
page_size: number
total_pages: number
}
interface ApiResponse {
success: boolean
data: {
data: TranscriptionData[]
pagination: PaginationInfo
}
}
export default function TranscriptionTable() {
// Agora, atualize o estado para usar o tipo correto
// Substitua a linha do useState por:
const [data, setData] = useState<TranscriptionData[]>([])
const [isLoading, setIsLoading] = useState(false)
const [isExporting, setIsExporting] = useState(false)
const [error, setError] = useState("")
// Filtros
const [companyId, setCompanyId] = useState("")
const [startDate, setStartDate] = useState("")
const [endDate, setEndDate] = useState("")
// E atualize a definição do estado who:
const [who, setWho] = useState<"client" | "hit">("client")
// Adicionar estados para paginação após os estados existentes:
const [pagination, setPagination] = useState<PaginationInfo>({
total: 0,
page: 1,
page_size: 20,
total_pages: 0,
})
const [currentPage, setCurrentPage] = useState(1)
const [pageSize, setPageSize] = useState(20)
const getAuthHeaders = () => {
const token = localStorage.getItem("access_token")
return {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
}
}
// Atualizar a função fetchData para incluir parâmetros de paginação:
const fetchData = async (page: number = currentPage) => {
if (!companyId || !startDate || !endDate) {
setError("Preencha todos os campos obrigatórios")
return
}
setIsLoading(true)
setError("")
try {
const params = new URLSearchParams({
companyId,
startDate,
endDate,
who,
page: page.toString(),
page_size: pageSize.toString(),
})
const response = await fetch(`${API_BASE_URL}/usage/data/trascription?${params}`, {
headers: getAuthHeaders(),
})
if (response.ok) {
const result: ApiResponse = await response.json()
if (result.success && result.data) {
setData(result.data.data || [])
setPagination(result.data.pagination)
setCurrentPage(result.data.pagination.page)
} else {
setData([])
setPagination({ total: 0, page: 1, page_size: 20, total_pages: 0 })
}
} else {
const errorData = await response.json()
setError(errorData.message || "Erro ao buscar dados")
}
} catch (err) {
setError("Erro de conexão com o servidor")
} finally {
setIsLoading(false)
}
}
const exportToExcel = async () => {
if (!companyId || !startDate || !endDate) {
setError("Preencha todos os campos obrigatórios")
return
}
setIsExporting(true)
setError("")
try {
const params = new URLSearchParams({
companyId,
startDate,
endDate,
who,
})
const response = await fetch(`${API_BASE_URL}/usage/export/trascription?${params}`, {
headers: getAuthHeaders(),
})
if (response.ok) {
const blob = await response.blob()
const url = window.URL.createObjectURL(blob)
const a = document.createElement("a")
a.href = url
a.download = `transcription-report-${who}-${startDate}-${endDate}.xlsx`
document.body.appendChild(a)
a.click()
window.URL.revokeObjectURL(url)
document.body.removeChild(a)
} else {
const errorData = await response.json()
setError(errorData.message || "Erro ao exportar dados")
}
} catch (err) {
setError("Erro de conexão com o servidor")
} finally {
setIsExporting(false)
}
}
// Adicionar funções de navegação de páginas:
const goToPage = (page: number) => {
if (page >= 1 && page <= pagination.total_pages) {
setCurrentPage(page)
fetchData(page)
}
}
const goToFirstPage = () => goToPage(1)
const goToPreviousPage = () => goToPage(currentPage - 1)
const goToNextPage = () => goToPage(currentPage + 1)
const goToLastPage = () => goToPage(pagination.total_pages)
return (
<div className="space-y-6">
{/* Filtros */}
<Card>
<CardHeader>
<CardTitle>Filtros de Consulta</CardTitle>
</CardHeader>
<CardContent>
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-6 gap-4">
<div className="space-y-2">
<Label htmlFor="companyId">ID da Empresa *</Label>
<Input
id="companyId"
value={companyId}
onChange={(e) => setCompanyId(e.target.value)}
placeholder="123"
/>
</div>
<div className="space-y-2">
<Label htmlFor="startDate">Data Inicial *</Label>
<Input id="startDate" type="date" value={startDate} onChange={(e) => setStartDate(e.target.value)} />
</div>
<div className="space-y-2">
<Label htmlFor="endDate">Data Final *</Label>
<Input id="endDate" type="date" value={endDate} onChange={(e) => setEndDate(e.target.value)} />
</div>
<div className="space-y-2">
<Label htmlFor="who">Tipo de Consulta *</Label>
{/* Também vamos atualizar o Select para usar "hit" em vez de "company"
// Substitua o componente Select para o campo "who": */}
<Select value={who} onValueChange={(value: "client" | "hit") => setWho(value)}>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="client">Cliente</SelectItem>
<SelectItem value="hit">HIT</SelectItem>
</SelectContent>
</Select>
</div>
{/* Adicionar seletor de tamanho de página nos filtros: */}
<div className="space-y-2">
<Label htmlFor="pageSize">Registros por página</Label>
<Select
value={pageSize.toString()}
onValueChange={(value) => {
setPageSize(Number(value))
setCurrentPage(1)
}}
>
<SelectTrigger>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="10">10</SelectItem>
<SelectItem value="20">20</SelectItem>
<SelectItem value="50">50</SelectItem>
<SelectItem value="100">100</SelectItem>
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label>&nbsp;</Label>
<div className="flex gap-2">
<Button onClick={() => fetchData(1)} disabled={isLoading} className="flex-1">
{isLoading ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Search className="mr-2 h-4 w-4" />}
Buscar
</Button>
<Button onClick={exportToExcel} disabled={isExporting || data.length === 0} variant="outline">
{isExporting ? <Loader2 className="h-4 w-4 animate-spin" /> : <Download className="h-4 w-4" />}
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
{error && (
<Alert variant="destructive">
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{/* Tabela de Dados */}
<Card>
<CardHeader>
{/* Atualizar o título da tabela para mostrar informações de paginação: */}
<CardTitle>
Resultados da Consulta
{pagination.total > 0 && (
<span className="text-sm font-normal text-gray-500 ml-2">
(Página {currentPage} de {pagination.total_pages} - {pagination.total} registros total)
</span>
)}
</CardTitle>
</CardHeader>
<CardContent>
{/* Agora, atualize a parte da tabela para exibir colunas diferentes com base no valor de who
// Substitua a seção da tabela com: */}
{data.length > 0 ? (
<div className="overflow-x-auto">
<Table>
<TableHeader>
<TableRow>
<TableHead>ID Único</TableHead>
<TableHead>Origem</TableHead>
<TableHead>Destino</TableHead>
<TableHead>Início</TableHead>
{who === "hit" && <TableHead>Fim</TableHead>}
<TableHead>Duração (s)</TableHead>
{who === "client" ? (
<TableHead>Total (min)</TableHead>
) : (
<>
<TableHead>Empresa</TableHead>
<TableHead>Custo HIT</TableHead>
<TableHead>Preço</TableHead>
<TableHead>Preço Cliente</TableHead>
</>
)}
</TableRow>
</TableHeader>
<TableBody>
{data.map((item, index) => (
<TableRow key={item.uniqueid || index}>
<TableCell>{item.uniqueid || "-"}</TableCell>
<TableCell>{item.src || "-"}</TableCell>
<TableCell>{item.dst || "-"}</TableCell>
<TableCell>{item.start_call || "-"}</TableCell>
{who === "hit" && <TableCell>{(item as HitTranscriptionData).end_call || "-"}</TableCell>}
<TableCell>{item.total_billsec || "-"}</TableCell>
{who === "client" ? (
<TableCell>{(item as ClientTranscriptionData).total_min || "-"}</TableCell>
) : (
<>
<TableCell>{(item as HitTranscriptionData).companyId || "-"}</TableCell>
<TableCell>
{(item as HitTranscriptionData).custo_HIT
? `$${(item as HitTranscriptionData).custo_HIT.toFixed(6)}`
: "-"}
</TableCell>
<TableCell>
{(item as HitTranscriptionData).price
? `$${(item as HitTranscriptionData).price.toFixed(4)}`
: "-"}
</TableCell>
<TableCell>
{(item as HitTranscriptionData).client_price
? `$${(item as HitTranscriptionData).client_price}`
: "-"}
</TableCell>
</>
)}
</TableRow>
))}
</TableBody>
</Table>
</div>
) : (
<div className="text-center py-8 text-gray-500">
Nenhum dado encontrado. Use os filtros acima para buscar dados.
</div>
)}
{/* Componente de Paginação */}
{pagination.total > 0 && (
<div className="flex items-center justify-between px-2 py-4">
<div className="flex items-center space-x-2">
<p className="text-sm text-gray-700">
Mostrando <span className="font-medium">{(currentPage - 1) * pagination.page_size + 1}</span> até{" "}
<span className="font-medium">{Math.min(currentPage * pagination.page_size, pagination.total)}</span>{" "}
de <span className="font-medium">{pagination.total}</span> resultados
</p>
</div>
<div className="flex items-center space-x-2">
<div className="flex items-center space-x-1">
<Button variant="outline" size="sm" onClick={goToFirstPage} disabled={currentPage === 1}>
Primeira
</Button>
<Button variant="outline" size="sm" onClick={goToPreviousPage} disabled={currentPage === 1}>
Anterior
</Button>
<div className="flex items-center space-x-1">
{/* Páginas numeradas */}
{Array.from({ length: Math.min(5, pagination.total_pages) }, (_, i) => {
let pageNum
if (pagination.total_pages <= 5) {
pageNum = i + 1
} else if (currentPage <= 3) {
pageNum = i + 1
} else if (currentPage >= pagination.total_pages - 2) {
pageNum = pagination.total_pages - 4 + i
} else {
pageNum = currentPage - 2 + i
}
return (
<Button
key={pageNum}
variant={currentPage === pageNum ? "default" : "outline"}
size="sm"
onClick={() => goToPage(pageNum)}
className="w-10"
>
{pageNum}
</Button>
)
})}
</div>
<Button
variant="outline"
size="sm"
onClick={goToNextPage}
disabled={currentPage === pagination.total_pages}
>
Próxima
</Button>
<Button
variant="outline"
size="sm"
onClick={goToLastPage}
disabled={currentPage === pagination.total_pages}
>
Última
</Button>
</div>
</div>
</div>
)}
</CardContent>
</Card>
</div>
)
}