Files
new-api-custom/web/src/features/keys/components/astra-cost-notice.tsx
T

97 lines
3.6 KiB
TypeScript

/*
Copyright (C) 2023-2026 QuantumNous
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>.
For commercial licensing, please contact support@quantumnous.com
*/
import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query'
import { useState } from 'react'
import { useTranslation } from 'react-i18next'
import { ConfirmDialog } from '@/components/confirm-dialog'
import { getUserProfile, updateUserSettings } from '@/features/profile/api'
import { parseUserSettings } from '@/features/profile/lib/format'
import { handleServerError } from '@/lib/handle-server-error'
import { requireServerSuccess } from '@/lib/server-error-message'
import { useAuthStore } from '@/stores/auth-store'
import { getApiKeys } from '../api'
export function AstraCostNotice() {
const { t } = useTranslation()
const userId = useAuthStore((state) => state.auth.user?.id)
const queryClient = useQueryClient()
const [dismissedUserId, setDismissedUserId] = useState<number>()
const noticeKey = ['astra-cost-notice', userId]
const { data: profile } = useQuery({
queryKey: noticeKey,
queryFn: async () => requireServerSuccess(await getUserProfile()).data,
enabled: userId !== undefined,
})
const { data: hasMigratedToken } = useQuery({
queryKey: ['astra-cost-notice-tokens', userId],
queryFn: async () => {
let page = 1
for (;;) {
const result = await getApiKeys({ p: page, size: 100 })
if (!result.success || !result.data) {
throw new Error(result.message || t('Failed to load API keys'))
}
if (
result.data.items.some((token) => token.astra_auto_switch === true)
) {
return true
}
if (page * result.data.page_size >= result.data.total) return false
page += 1
}
},
enabled: userId !== undefined,
})
const acknowledge = useMutation({
mutationFn: async () =>
requireServerSuccess(
await updateUserSettings({ astra_cost_notice_ack: true })
),
onSuccess: () => {
setDismissedUserId(userId)
void queryClient.invalidateQueries({ queryKey: noticeKey })
},
onError: (error) =>
handleServerError(error, t('Failed to update settings')),
})
return (
<ConfirmDialog
open={
!!profile &&
hasMigratedToken === true &&
!parseUserSettings(profile.setting).astra_cost_notice_ack &&
dismissedUserId !== userId
}
onOpenChange={(value) => {
if (!value && !acknowledge.isPending) setDismissedUserId(userId)
}}
title={t('Astra auto-switch and charges')}
desc={t(
'Existing API keys keep Astra auto-switch enabled; new keys default to off. Rejected results still incur usage charges, and every additional upstream attempt is charged separately. You can change this setting for each API key.'
)}
cancelBtnText={t('Remind me next time')}
confirmText={t('I understand')}
isLoading={acknowledge.isPending}
handleConfirm={() => acknowledge.mutate()}
/>
)
}