transcription-cost-usage-re.../frontend/components/cost-update-form.tsx

345 lines
11 KiB
TypeScript

"use client"
import type React from "react"
import { useState, useEffect } 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 { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
import { Alert, AlertDescription } from "@/components/ui/alert"
import { Textarea } from "@/components/ui/textarea"
import { Loader2, Save, RefreshCw } from "lucide-react"
const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://127.0.0.1:5000/api/v1"
interface ModelPrice {
_id: { $oid: string }
provider: string
product: string
currency: string
price: string
billingBy: string
billingUnit: number
type: string
createdAt: { $date: string }
updatedAt: { $date: string }
__v: number
}
interface ModelPricesResponse {
success: boolean
data: ModelPrice[]
}
interface CostUpdateResponse {
success: boolean
docs_updated: number
}
interface CostUpdatePayload {
product: string
start_date: string
end_date: string
price: string
billing_unit: number
company_ids?: string[]
}
export default function CostUpdateForm() {
const [isLoading, setIsLoading] = useState(false)
const [isLoadingData, setIsLoadingData] = useState(false)
const [error, setError] = useState("")
const [success, setSuccess] = useState("")
// Estados para os dados dos combos
const [products, setProducts] = useState<string[]>([])
const [billingOptions, setBillingOptions] = useState<{ billingBy: string; billingUnit: number }[]>([])
const [formData, setFormData] = useState({
product: "",
start_date: "",
end_date: "",
price: "",
billing_unit: "",
company_ids: "",
})
const getAuthHeaders = () => {
const token = localStorage.getItem("access_token")
return {
Authorization: `Bearer ${token}`,
"Content-Type": "application/json",
}
}
// Função para buscar dados dos modelos
const fetchModelPrices = async () => {
setIsLoadingData(true)
setError("")
try {
const response = await fetch(`${API_BASE_URL}/usage/model/prices?type=stt`, {
headers: getAuthHeaders(),
})
if (response.ok) {
const result: ModelPricesResponse = await response.json()
if (result.success && Array.isArray(result.data)) {
// Extrair produtos únicos
const uniqueProducts = [...new Set(result.data.map((item) => item.product))]
setProducts(uniqueProducts)
// Extrair opções de billing únicas
const uniqueBillingOptions = result.data.reduce(
(acc, item) => {
const existing = acc.find(
(option) => option.billingBy === item.billingBy && option.billingUnit === item.billingUnit,
)
if (!existing) {
acc.push({
billingBy: item.billingBy,
billingUnit: item.billingUnit,
})
}
return acc
},
[] as { billingBy: string; billingUnit: number }[],
)
setBillingOptions(uniqueBillingOptions)
}
} else {
const errorData = await response.json()
setError(errorData.message || "Erro ao buscar dados dos modelos")
}
} catch (err) {
console.log("Erro de conexão com o servidor: ",err)
setError("Erro de conexão com o servidor")
} finally {
setIsLoadingData(false)
}
}
// Carregar dados ao montar o componente
useEffect(() => {
fetchModelPrices()
}, [])
const handleSubmit = async (e: React.FormEvent) => {
e.preventDefault()
setIsLoading(true)
setError("")
setSuccess("")
try {
// Converte company_ids de string para array (se fornecido)
let company_ids: string[] | undefined
if (formData.company_ids.trim()) {
company_ids = formData.company_ids
.split(",")
.map((id) => id.trim())
.filter((id) => id.length > 0)
}
const payload: CostUpdatePayload = {
product: formData.product,
start_date: formData.start_date,
end_date: formData.end_date,
price: formData.price,
billing_unit: Number(formData.billing_unit),
}
// Só adiciona company_ids se foi fornecido
if (company_ids && company_ids.length > 0) {
payload.company_ids = company_ids
}
const response = await fetch(`${API_BASE_URL}/usage/cost`, {
method: "PATCH",
headers: getAuthHeaders(),
body: JSON.stringify(payload),
})
if (response.ok) {
const result: CostUpdateResponse = await response.json()
if (result.success) {
setSuccess(`Custos atualizados com sucesso! ${result.docs_updated} registros foram atualizados.`)
setFormData({
product: "",
start_date: "",
end_date: "",
price: "",
billing_unit: "",
company_ids: "",
})
} else {
setError("Erro na resposta do servidor")
}
} else {
const errorData = await response.json()
setError(errorData.message || "Erro ao atualizar custos")
}
} catch (err) {
console.log("====> Erro de conexão com o servidor: ", err)
setError("Erro de conexão com o servidor")
} finally {
setIsLoading(false)
}
}
const handleInputChange = (field: string, value: string) => {
setFormData((prev) => ({ ...prev, [field]: value }))
}
const handleBillingChange = (billingBy: string) => {
// Encontrar o billingUnit correspondente
const selectedOption = billingOptions.find((option) => option.billingBy === billingBy)
if (selectedOption) {
setFormData((prev) => ({
...prev,
billing_unit: selectedOption.billingUnit.toString(),
}))
}
}
return (
<Card>
<CardHeader>
<CardTitle className="flex items-center justify-between">
Atualizar Custos de Uso
<Button variant="outline" size="sm" onClick={fetchModelPrices} disabled={isLoadingData}>
{isLoadingData ? <Loader2 className="h-4 w-4 animate-spin" /> : <RefreshCw className="h-4 w-4" />}
</Button>
</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleSubmit} className="space-y-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-4">
<div className="space-y-2">
<Label htmlFor="product">Produto *</Label>
<Select
value={formData.product}
onValueChange={(value) => handleInputChange("product", value)}
disabled={isLoadingData}
>
<SelectTrigger>
<SelectValue placeholder={isLoadingData ? "Carregando..." : "Selecione um produto"} />
</SelectTrigger>
<SelectContent>
{products.map((product) => (
<SelectItem key={product} value={product}>
{product}
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="billing_by">Unidade de Cobrança *</Label>
<Select
value={
billingOptions.find((option) => option.billingUnit.toString() === formData.billing_unit)?.billingBy ||
""
}
onValueChange={handleBillingChange}
disabled={isLoadingData}
>
<SelectTrigger>
<SelectValue placeholder={isLoadingData ? "Carregando..." : "Selecione a unidade"} />
</SelectTrigger>
<SelectContent>
{billingOptions.map((option, index) => (
<SelectItem key={`${option.billingBy}-${option.billingUnit}-${index}`} value={option.billingBy}>
{option.billingBy} ({option.billingUnit})
</SelectItem>
))}
</SelectContent>
</Select>
</div>
<div className="space-y-2">
<Label htmlFor="start_date">Data Inicial *</Label>
<Input
id="start_date"
type="date"
value={formData.start_date}
onChange={(e) => handleInputChange("start_date", e.target.value)}
required
/>
</div>
<div className="space-y-2">
<Label htmlFor="end_date">Data Final *</Label>
<Input
id="end_date"
type="date"
value={formData.end_date}
onChange={(e) => handleInputChange("end_date", e.target.value)}
required
/>
</div>
<div className="space-y-2 md:col-span-2">
<Label htmlFor="price">Preço *</Label>
<Input
id="price"
type="text"
value={formData.price}
onChange={(e) => handleInputChange("price", e.target.value)}
placeholder="0.024"
required
/>
</div>
<div className="space-y-2 md:col-span-2">
<Label htmlFor="company_ids">IDs das Empresas (opcional)</Label>
<Textarea
id="company_ids"
value={formData.company_ids}
onChange={(e) => handleInputChange("company_ids", e.target.value)}
placeholder="123, 456, 789 (separados por vírgula - deixe vazio para atualizar todas)"
rows={3}
/>
<p className="text-sm text-gray-500">
Digite os IDs das empresas separados por vírgula. Deixe vazio para atualizar todas as empresas.
</p>
</div>
{/* Campo oculto para mostrar o billing_unit selecionado */}
{formData.billing_unit && (
<div className="space-y-2 md:col-span-2">
<Label>Valor da Unidade de Cobrança</Label>
<Input value={formData.billing_unit} disabled className="bg-gray-50" />
<p className="text-sm text-gray-500">
Este valor é definido automaticamente baseado na unidade de cobrança selecionada.
</p>
</div>
)}
</div>
{error && (
<Alert variant="destructive">
<AlertDescription>{error}</AlertDescription>
</Alert>
)}
{success && (
<Alert>
<AlertDescription>{success}</AlertDescription>
</Alert>
)}
<Button type="submit" disabled={isLoading || isLoadingData} className="w-full">
{isLoading ? <Loader2 className="mr-2 h-4 w-4 animate-spin" /> : <Save className="mr-2 h-4 w-4" />}
Atualizar Custos
</Button>
</form>
</CardContent>
</Card>
)
}