diff --git a/packages/ui/src/api/user.js b/packages/ui/src/api/user.js
index cc5ff2992..6726558e2 100644
--- a/packages/ui/src/api/user.js
+++ b/packages/ui/src/api/user.js
@@ -25,6 +25,11 @@ const getPlanProration = (subscriptionId, newPlanId) =>
const updateSubscriptionPlan = (subscriptionId, newPlanId, prorationDate) =>
client.post(`/organization/update-subscription-plan`, { subscriptionId, newPlanId, prorationDate })
const getCurrentUsage = () => client.get(`/organization/get-current-usage`)
+const getPredictionEligibility = () => client.get(`/organization/prediction-eligibility`)
+const purchaseCredits = (packageType) => client.post(`/organization/purchase-credits`, { packageType })
+const getCreditsBalance = () => client.get(`/organization/credits-balance`)
+const getUsageWithCredits = () => client.get(`/organization/usage-with-credits`)
+const getCreditsPackages = () => client.get(`/organization/credits-packages`)
// workspace users
const getAllUsersByWorkspaceId = (workspaceId) => client.get(`/workspaceuser?workspaceId=${workspaceId}`)
@@ -55,5 +60,10 @@ export default {
getPlanProration,
updateSubscriptionPlan,
getCurrentUsage,
+ getPredictionEligibility,
+ purchaseCredits,
+ getCreditsBalance,
+ getUsageWithCredits,
+ getCreditsPackages,
deleteOrganizationUser
}
diff --git a/packages/ui/src/views/account/index.jsx b/packages/ui/src/views/account/index.jsx
index cd4a57679..50950dcd7 100644
--- a/packages/ui/src/views/account/index.jsx
+++ b/packages/ui/src/views/account/index.jsx
@@ -33,7 +33,7 @@ import SettingsSection from '@/ui-component/form/settings'
import PricingDialog from '@/ui-component/subscription/PricingDialog'
// Icons
-import { IconAlertCircle, IconCreditCard, IconExternalLink, IconSparkles, IconX } from '@tabler/icons-react'
+import { IconAlertCircle, IconCoins, IconCreditCard, IconExternalLink, IconSparkles, IconX } from '@tabler/icons-react'
// API
import accountApi from '@/api/account.api'
@@ -88,6 +88,12 @@ const AccountSettings = () => {
const [purchasedSeats, setPurchasedSeats] = useState(0)
const [occupiedSeats, setOccupiedSeats] = useState(0)
const [totalSeats, setTotalSeats] = useState(0)
+ const [creditsBalance, setCreditsBalance] = useState(null)
+ const [creditsPackages, setCreditsPackages] = useState([])
+ const [usageWithCredits, setUsageWithCredits] = useState(null)
+ const [openCreditsDialog, setOpenCreditsDialog] = useState(false)
+ const [selectedPackage, setSelectedPackage] = useState(null)
+ const [isPurchasingCredits, setIsPurchasingCredits] = useState(false)
const predictionsUsageInPercent = useMemo(() => {
return usage ? calculatePercentage(usage.predictions?.usage, usage.predictions?.limit) : 0
@@ -106,6 +112,11 @@ const AccountSettings = () => {
const getCustomerDefaultSourceApi = useApi(userApi.getCustomerDefaultSource)
const updateAdditionalSeatsApi = useApi(userApi.updateAdditionalSeats)
const getCurrentUsageApi = useApi(userApi.getCurrentUsage)
+ const getCreditsBalanceApi = useApi(userApi.getCreditsBalance)
+ const getCreditsPackagesApi = useApi(userApi.getCreditsPackages)
+ const getUsageWithCreditsApi = useApi(userApi.getUsageWithCredits)
+ const purchaseCreditsApi = useApi(userApi.purchaseCredits)
+ const getPredictionEligibilityApi = useApi(userApi.getPredictionEligibility)
useEffect(() => {
if (isCloud) {
@@ -113,6 +124,10 @@ const AccountSettings = () => {
getPricingPlansApi.request()
getAdditionalSeatsQuantityApi.request(currentUser?.activeOrganizationSubscriptionId)
getCurrentUsageApi.request()
+ getCreditsBalanceApi.request()
+ getCreditsPackagesApi.request()
+ getUsageWithCreditsApi.request()
+ getPredictionEligibilityApi.request()
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [isCloud])
@@ -140,13 +155,31 @@ const AccountSettings = () => {
}, [getCurrentUsageApi.data])
useEffect(() => {
- if (openRemoveSeatsDialog || openAddSeatsDialog) {
+ if (getCreditsBalanceApi.data) {
+ setCreditsBalance(getCreditsBalanceApi.data)
+ }
+ }, [getCreditsBalanceApi.data])
+
+ useEffect(() => {
+ if (getCreditsPackagesApi.data) {
+ setCreditsPackages(getCreditsPackagesApi.data)
+ }
+ }, [getCreditsPackagesApi.data])
+
+ useEffect(() => {
+ if (getUsageWithCreditsApi.data) {
+ setUsageWithCredits(getUsageWithCreditsApi.data)
+ }
+ }, [getUsageWithCreditsApi.data])
+
+ useEffect(() => {
+ if (openRemoveSeatsDialog || openAddSeatsDialog || openCreditsDialog) {
setSeatsQuantity(0)
getCustomerDefaultSourceApi.request(currentUser?.activeOrganizationCustomerId)
}
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [openRemoveSeatsDialog, openAddSeatsDialog])
+ }, [openRemoveSeatsDialog, openAddSeatsDialog, openCreditsDialog])
useEffect(() => {
if (getAdditionalSeatsProrationApi.data) {
@@ -447,6 +480,61 @@ const AccountSettings = () => {
}
}
+ const handlePurchaseCredits = async (packageType) => {
+ try {
+ setIsPurchasingCredits(true)
+
+ const response = await purchaseCreditsApi.request(packageType)
+
+ if (response.data?.success) {
+ enqueueSnackbar({
+ message: 'Credits purchased successfully!',
+ options: {
+ key: new Date().getTime() + Math.random(),
+ variant: 'success',
+ action: (key) => (
+
+ )
+ }
+ })
+
+ // Refresh credits data
+ getCreditsBalanceApi.request()
+ getUsageWithCreditsApi.request()
+ setOpenCreditsDialog(false)
+ setSelectedPackage(null)
+ }
+ } catch (error) {
+ console.error('Error purchasing credits:', error)
+ enqueueSnackbar({
+ message: `Failed to purchase credits: ${
+ typeof error.response?.data === 'object' ? error.response.data.message : error.response?.data || error.message
+ }`,
+ options: {
+ key: new Date().getTime() + Math.random(),
+ variant: 'error',
+ persist: true,
+ action: (key) => (
+
+ )
+ }
+ })
+ } finally {
+ setIsPurchasingCredits(false)
+ }
+ }
+
+ const handleCreditsDialogClose = () => {
+ if (!isPurchasingCredits) {
+ setOpenCreditsDialog(false)
+ setSelectedPackage(null)
+ }
+ }
+
// Calculate empty seats
const emptySeats = Math.min(purchasedSeats, totalSeats - occupiedSeats)
@@ -726,6 +814,78 @@ const AccountSettings = () => {
+
+
+
+
+ Available Credits:
+
+ {getCreditsBalanceApi.loading ? (
+
+ ) : (
+ creditsBalance?.balance || 0
+ )}
+
+
+ {usageWithCredits && (
+
+ Credits Used This Month:
+
+ {getUsageWithCreditsApi.loading ? (
+
+ ) : (
+ usageWithCredits?.creditsUsed || 0
+ )}
+
+
+ )}
+
+ Purchase credits for predictions beyond your plan limits
+
+
+
+ setOpenCreditsDialog(true)}
+ startIcon={}
+ sx={{ borderRadius: 2, height: 40 }}
+ >
+ Buy Credits
+
+
+
+
@@ -1457,6 +1617,148 @@ const AccountSettings = () => {
)}
+
+ {/* Credits Purchase Dialog */}
+
)
}