Some checks are pending
E2E / Playwright e2e (push) Waiting to run
- Replaced hardcoded "Agenda" labels with dynamic ULTICAL_APP_NAME in various components for consistency. - Introduced new AiUsageSection and CompteAiUsageSection components to track AI usage and costs. - Updated settings and metadata to reflect changes in AI cost policies and usage limits. - Enhanced user interface elements for better accessibility and user experience across admin settings.
106 lines
2.7 KiB
TypeScript
106 lines
2.7 KiB
TypeScript
"use client"
|
|
|
|
import { useMutation, useQuery, useQueryClient } from "@tanstack/react-query"
|
|
import { apiClient } from "@/lib/api/client"
|
|
|
|
export type AdminAIUsageSummary = {
|
|
cost_today_micro_eur: number
|
|
cost_month_micro_eur: number
|
|
cost_today_eur: number
|
|
cost_month_eur: number
|
|
currency: string
|
|
daily_series: {
|
|
date: string
|
|
cost_org_micro_eur: number
|
|
cost_user_micro_eur: number
|
|
cost_org_eur: number
|
|
requests: number
|
|
}[]
|
|
top_users: {
|
|
user_id: string
|
|
email: string
|
|
display_name: string
|
|
cost_org_micro_eur: number
|
|
cost_org_eur: number
|
|
}[]
|
|
top_models: {
|
|
model_id: string
|
|
cost_micro_eur: number
|
|
cost_eur: number
|
|
request_count: number
|
|
}[]
|
|
}
|
|
|
|
export type AdminAIPricingEntry = {
|
|
model_id: string
|
|
provider_type: string
|
|
input_micro_eur_per_mtok: number
|
|
cached_input_micro_eur_per_mtok?: number
|
|
output_micro_eur_per_mtok: number
|
|
input_eur_per_mtok: number
|
|
output_eur_per_mtok: number
|
|
}
|
|
|
|
export function useAdminAIUsage(scope: "org" | "user" = "org") {
|
|
return useQuery({
|
|
queryKey: ["admin", "ai", "usage", scope],
|
|
queryFn: () =>
|
|
apiClient.get<AdminAIUsageSummary>(`/admin/ai/usage?scope=${scope}`),
|
|
staleTime: 30_000,
|
|
})
|
|
}
|
|
|
|
export function useAdminAIPricing() {
|
|
return useQuery({
|
|
queryKey: ["admin", "ai", "pricing"],
|
|
queryFn: async () => {
|
|
const res = await apiClient.get<{ prices: AdminAIPricingEntry[] }>(
|
|
"/admin/ai/pricing"
|
|
)
|
|
return res.prices ?? []
|
|
},
|
|
staleTime: 60_000,
|
|
})
|
|
}
|
|
|
|
export function useUpdateAdminAIPricing() {
|
|
const queryClient = useQueryClient()
|
|
return useMutation({
|
|
mutationFn: (prices: AdminAIPricingEntry[]) =>
|
|
apiClient.put<{ prices: AdminAIPricingEntry[] }>("/admin/ai/pricing", {
|
|
prices,
|
|
}),
|
|
onSuccess: () => {
|
|
void queryClient.invalidateQueries({ queryKey: ["admin", "ai", "pricing"] })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useUpdateUserAICostPolicy(userId: string) {
|
|
const queryClient = useQueryClient()
|
|
return useMutation({
|
|
mutationFn: (body: {
|
|
daily_limit_eur?: number | null
|
|
monthly_limit_eur?: number | null
|
|
warn_threshold_pct?: number
|
|
}) => apiClient.put(`/admin/users/${userId}/ai-policy`, body),
|
|
onSuccess: () => {
|
|
void queryClient.invalidateQueries({ queryKey: ["admin", "ai"] })
|
|
},
|
|
})
|
|
}
|
|
|
|
export function useUpdateGroupAICostPolicy(groupId: string) {
|
|
const queryClient = useQueryClient()
|
|
return useMutation({
|
|
mutationFn: (body: {
|
|
daily_limit_eur?: number | null
|
|
monthly_limit_eur?: number | null
|
|
warn_threshold_pct?: number
|
|
}) => apiClient.put(`/admin/user-groups/${groupId}/ai-policy`, body),
|
|
onSuccess: () => {
|
|
void queryClient.invalidateQueries({ queryKey: ["admin", "ai"] })
|
|
},
|
|
})
|
|
}
|