From 5bbafd983f41ac6301d8ce9c73b5ab75f5c824a9 Mon Sep 17 00:00:00 2001 From: adriano Date: Wed, 3 Sep 2025 11:50:16 -0300 Subject: [PATCH] feat: implemente authorization type to client --- backend/app/routes/billing_routes.py | 2 +- backend/app/routes/usage_routes.py | 4 +- backend/app/schemas/auth_sigup_schema.py | 2 +- frontend/app/dashboard/page.tsx | 35 +++++++++------ frontend/components/transcription-table.tsx | 30 ++++++++----- frontend/components/user-management.tsx | 47 ++++++++++++--------- frontend/lib/auth.ts | 4 ++ 7 files changed, 75 insertions(+), 49 deletions(-) diff --git a/backend/app/routes/billing_routes.py b/backend/app/routes/billing_routes.py index 0c070d0..31089ef 100644 --- a/backend/app/routes/billing_routes.py +++ b/backend/app/routes/billing_routes.py @@ -40,7 +40,7 @@ class UpdateProduct(Resource): @billing_ns.route('/products') class ListProducts(Resource): @jwt_required() - @role_required('admin', 'user') + @role_required('admin', 'user', 'client') def get(self): response = requests.get(url=f'{BILLING_API_URL}/billing/products', headers=HEADERS) return response.json(), response.status_code \ No newline at end of file diff --git a/backend/app/routes/usage_routes.py b/backend/app/routes/usage_routes.py index 6c007d7..b6df1ea 100644 --- a/backend/app/routes/usage_routes.py +++ b/backend/app/routes/usage_routes.py @@ -28,7 +28,7 @@ class TranscriptionExport(Resource): @usage_ns.response(400, 'Validation error') @usage_ns.response(404, 'File not found') @jwt_required() - @role_required('admin', 'user') + @role_required('admin', 'user', 'client') def get(self): """ Export transcription report in XLSX. @@ -88,7 +88,7 @@ class TranscriptionUsageData(Resource): @usage_ns.response(200, 'success') @usage_ns.response(400, 'Validation error') @jwt_required() - @role_required('admin', 'user') + @role_required('admin', 'user', 'client') def get(self): """ Get transcription report data. diff --git a/backend/app/schemas/auth_sigup_schema.py b/backend/app/schemas/auth_sigup_schema.py index e961489..2f582a2 100644 --- a/backend/app/schemas/auth_sigup_schema.py +++ b/backend/app/schemas/auth_sigup_schema.py @@ -4,4 +4,4 @@ from typing import List, Literal class SigUpRequest(BaseModel): email: str password: str - roles: List[Literal["admin", "user"]] \ No newline at end of file + roles: List[Literal["admin", "user", "client"]] \ No newline at end of file diff --git a/frontend/app/dashboard/page.tsx b/frontend/app/dashboard/page.tsx index 8d96c89..7c4d26b 100644 --- a/frontend/app/dashboard/page.tsx +++ b/frontend/app/dashboard/page.tsx @@ -10,7 +10,7 @@ import TranscriptionTable from "@/components/transcription-table" import ModelPricesTable from "@/components/model-prices-table" import ProductManagement from "@/components/product-management" import UserManagement from "@/components/user-management" -import { isAdmin, isTokenExpired, getCurrentUserEmail, getUserRoles } from "@/lib/auth" +import { isAdmin, isTokenExpired, getCurrentUserEmail, getUserRoles, isClient } from "@/lib/auth" export default function Dashboard() { const [isAuthenticated, setIsAuthenticated] = useState(false) @@ -18,6 +18,7 @@ export default function Dashboard() { const [userIsAdmin, setUserIsAdmin] = useState(false) const [userEmail, setUserEmail] = useState(null) const [userRoles, setUserRoles] = useState([]) + const [userIsClient, setUserIsClient] = useState(false) const router = useRouter() useEffect(() => { @@ -34,10 +35,12 @@ export default function Dashboard() { const email = getCurrentUserEmail() const roles = getUserRoles() const adminStatus = isAdmin() + const clientStatus = isClient() setUserEmail(email) setUserRoles(roles) setUserIsAdmin(adminStatus) + setUserIsClient(clientStatus) setIsAuthenticated(true) setIsLoading(false) }, [router]) @@ -83,9 +86,11 @@ export default function Dashboard() {
- + Dados de Transcrição - Preços dos Modelos + {!userIsClient && Preços dos Modelos} Produtos {userIsAdmin && Usuários} @@ -102,17 +107,19 @@ export default function Dashboard() { - - - - Preços dos Modelos - Gerencie os preços dos modelos de IA disponíveis - - - - - - + {!userIsClient && ( + + + + Preços dos Modelos + Gerencie os preços dos modelos de IA disponíveis + + + + + + + )} diff --git a/frontend/components/transcription-table.tsx b/frontend/components/transcription-table.tsx index c28bbcb..41ccc65 100644 --- a/frontend/components/transcription-table.tsx +++ b/frontend/components/transcription-table.tsx @@ -9,12 +9,13 @@ import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Alert, AlertDescription } from "@/components/ui/alert" import { Download, Search, Loader2, DollarSign, TrendingUp, Calculator } from "lucide-react" +import { isClient } from "@/lib/auth" const API_BASE_URL = process.env.NEXT_PUBLIC_API_URL || "http://localhost:5000/api/v1" function formatDateBr(dateStr: string) { - const [year, month, day] = dateStr.split("-"); - return `${day}/${month}/${year}`; + const [year, month, day] = dateStr.split("-") + return `${day}/${month}/${year}` } export function formatDateTime(raw?: string): string { @@ -57,14 +58,14 @@ interface HitTranscriptionData { qtd_token_input: number // Quantidade de tokens(input) qtd_token_output: number // Quantidade de tokens(output) total_cost_token: number // Total token(input + output) - llm_provider: string // Provider llm + llm_provider: string // Provider llm // tts_model: string // Modelo de trascrição de audio para texto tts_provider: string // Provedor do tts tts_cost: number // Custo total dos minutos do audio em texto tts_usage: number // Tempo total de trascrição do audio em segundos - total_min: number, // Minutos de ligação + total_min: number // Minutos de ligação custo_hit: string // Custo HIT client_total_cost: string // Custo Cliente client_price: string // Preço Cliente por Minuto @@ -140,6 +141,8 @@ export default function TranscriptionTable() { const [currentPage, setCurrentPage] = useState(1) const [pageSize, setPageSize] = useState(20) + const [userIsClient, setUserIsClient] = useState(false) + const getAuthHeaders = () => { const token = localStorage.getItem("access_token") return { @@ -295,12 +298,19 @@ export default function TranscriptionTable() { // Buscar cotação do dólar quando o componente for montado useEffect(() => { fetchExchangeRate() + const clientStatus = isClient() + setUserIsClient(clientStatus) + + // Forçar "client" para usuários client + if (clientStatus) { + setWho("client") + } }, []) return (
{/* Card de Informações de Custo */} - {costInfo && exchangeRate && ( + {!userIsClient && costInfo && exchangeRate && (
@@ -389,7 +399,7 @@ export default function TranscriptionTable() { Cliente - HIT + {!userIsClient && HIT}
@@ -482,8 +492,8 @@ export default function TranscriptionTable() { Preço Final LLM ($) Provider LLM {/* Model TTS */} - Provider TTS - Preço Final TTS ($) + Provider STT + Preço Final STT ($) Segundos Transcritos ) : ( @@ -531,7 +541,7 @@ export default function TranscriptionTable() { {formatDateTime((item as HitTranscriptionData).end_call || "-")} {(item as HitTranscriptionData).qtd_token_input || "-"} {(item as HitTranscriptionData).qtd_token_output || "-"} - + {(item as HitTranscriptionData)?.total_cost_token ? `$ ${Number((item as HitTranscriptionData).total_cost_token).toFixed(7)}` @@ -563,8 +573,6 @@ export default function TranscriptionTable() { ? `R$ ${Number((item as ClientTranscriptionData).client_total_cost).toFixed(2)}` : "-"} - - )} diff --git a/frontend/components/user-management.tsx b/frontend/components/user-management.tsx index bf8321f..307e8fa 100644 --- a/frontend/components/user-management.tsx +++ b/frontend/components/user-management.tsx @@ -1,5 +1,7 @@ "use client" +import { DialogTrigger } from "@/components/ui/dialog" + import { useState, useEffect } from "react" import { Button } from "@/components/ui/button" import { Input } from "@/components/ui/input" @@ -9,14 +11,7 @@ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Alert, AlertDescription } from "@/components/ui/alert" import { Badge } from "@/components/ui/badge" import { Checkbox } from "@/components/ui/checkbox" -import { - Dialog, - DialogContent, - DialogDescription, - DialogHeader, - DialogTitle, - DialogTrigger, -} from "@/components/ui/dialog" +import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle } from "@/components/ui/dialog" import { Edit2, Plus, Save, X, Loader2, RefreshCw, Users, Shield, Trash2 } from "lucide-react" import { isAdmin, getCurrentUserEmail } from "@/lib/auth" @@ -46,11 +41,22 @@ interface UpdateUserRequest { password?: string roles: string[] } - + +// interface DeleteUserResponse { +// success: boolean +// message: string +// } + +// interface UpdateUserResponse { +// success: boolean +// message: string +// } + // Roles disponíveis no sistema const AVAILABLE_ROLES = [ { id: "admin", label: "Administrador", description: "Acesso total ao sistema" }, { id: "user", label: "Usuário", description: "Acesso parcial, não pode gerenciar usuários" }, + { id: "client", label: "Cliente", description: "Acesso limitado, apenas visualização de dados de cliente" }, ] export default function UserManagement() { @@ -105,8 +111,7 @@ export default function UserManagement() { const errorData = await response.json() setError(errorData.message || "Erro ao buscar usuários") } - } catch (err: unknown) { - console.log(err) + } catch { setError("Erro de conexão com o servidor") } finally { setIsLoading(false) @@ -130,7 +135,8 @@ export default function UserManagement() { body: JSON.stringify(createForm), }) - if (response.ok) { + if (response.ok) { + // const result = await response.json() setSuccess("Usuário criado com sucesso!") setCreateForm({ email: "", password: "", roles: [] }) setIsCreateDialogOpen(false) @@ -139,8 +145,7 @@ export default function UserManagement() { const errorData = await response.json() setError(errorData.message || "Erro ao criar usuário") } - } catch (err:unknown) { - console.log(err) + } catch { setError("Erro de conexão com o servidor") } finally { setIsCreating(false) @@ -174,7 +179,8 @@ export default function UserManagement() { body: JSON.stringify(updateData), }) - if (response.ok) { + if (response.ok) { + // const result: UpdateUserResponse = await response.json() setSuccess("Usuário atualizado com sucesso!") setEditingUser(null) setEditForm({ email: "", password: "", roles: [] }) @@ -183,8 +189,7 @@ export default function UserManagement() { const errorData = await response.json() setError(errorData.message || "Erro ao atualizar usuário") } - } catch (err) { - console.log(err) + } catch { setError("Erro de conexão com o servidor") } finally { setIsUpdating(false) @@ -206,15 +211,15 @@ export default function UserManagement() { headers: getAuthHeaders(), }) - if (response.ok) { + if (response.ok) { + // const result: DeleteUserResponse = await response.json() setSuccess("Usuário excluído com sucesso!") await fetchUsers() } else { const errorData = await response.json() setError(errorData.message || "Erro ao excluir usuário") } - } catch (err:unknown) { - console.log(err) + } catch { setError("Erro de conexão com o servidor") } finally { setIsDeleting(null) @@ -259,6 +264,8 @@ export default function UserManagement() { return "default" as const case "user": return "secondary" as const + case "client": + return "outline" as const default: return "outline" as const } diff --git a/frontend/lib/auth.ts b/frontend/lib/auth.ts index 605f1a1..f3e3769 100644 --- a/frontend/lib/auth.ts +++ b/frontend/lib/auth.ts @@ -61,6 +61,10 @@ export function isAdmin(): boolean { return hasRole("admin") } +export function isClient(): boolean { + return hasRole("client") +} + export function getCurrentUserEmail(): string | null { try { const token = localStorage.getItem("access_token")