Phase 3 - credts, usage, plans app pages #Migrations
This commit is contained in:
168
frontend/src/components/billing/InsufficientCreditsModal.tsx
Normal file
168
frontend/src/components/billing/InsufficientCreditsModal.tsx
Normal file
@@ -0,0 +1,168 @@
|
||||
/**
|
||||
* Insufficient Credits Modal
|
||||
* Shows when user doesn't have enough credits for an operation
|
||||
* Provides options to upgrade plan or buy credits
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import { Modal } from '../ui/modal';
|
||||
import Button from '../ui/button/Button';
|
||||
import { ZapIcon, TrendingUpIcon, CreditCardIcon } from '../../icons';
|
||||
import { useNavigate } from 'react-router-dom';
|
||||
|
||||
interface InsufficientCreditsModalProps {
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
requiredCredits: number;
|
||||
availableCredits: number;
|
||||
operationType?: string;
|
||||
}
|
||||
|
||||
export default function InsufficientCreditsModal({
|
||||
isOpen,
|
||||
onClose,
|
||||
requiredCredits,
|
||||
availableCredits,
|
||||
operationType = 'this operation',
|
||||
}: InsufficientCreditsModalProps) {
|
||||
const navigate = useNavigate();
|
||||
const shortfall = requiredCredits - availableCredits;
|
||||
|
||||
const handleUpgradePlan = () => {
|
||||
onClose();
|
||||
navigate('/account/billing/upgrade');
|
||||
};
|
||||
|
||||
const handleBuyCredits = () => {
|
||||
onClose();
|
||||
navigate('/account/billing/credits');
|
||||
};
|
||||
|
||||
const handleViewUsage = () => {
|
||||
onClose();
|
||||
navigate('/account/usage');
|
||||
};
|
||||
|
||||
return (
|
||||
<Modal isOpen={isOpen} onClose={onClose} showCloseButton={true}>
|
||||
<div className="p-6 text-center">
|
||||
{/* Warning Icon */}
|
||||
<div className="relative flex items-center justify-center w-20 h-20 mx-auto mb-6">
|
||||
<div className="absolute inset-0 bg-warning-100 dark:bg-warning-900/30 rounded-full"></div>
|
||||
<div className="relative bg-warning-500 rounded-full w-14 h-14 flex items-center justify-center">
|
||||
<ZapIcon className="w-7 h-7 text-white" />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Title */}
|
||||
<h2 className="text-xl font-bold text-gray-900 dark:text-white mb-2">
|
||||
Insufficient Credits
|
||||
</h2>
|
||||
|
||||
{/* Message */}
|
||||
<p className="text-gray-600 dark:text-gray-400 mb-6">
|
||||
You don't have enough credits for {operationType}.
|
||||
</p>
|
||||
|
||||
{/* Credit Stats */}
|
||||
<div className="bg-gray-50 dark:bg-gray-800 rounded-lg p-4 mb-6">
|
||||
<div className="grid grid-cols-3 gap-4 text-center">
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 mb-1">Required</div>
|
||||
<div className="text-lg font-bold text-warning-600 dark:text-warning-400">
|
||||
{requiredCredits.toLocaleString()}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 mb-1">Available</div>
|
||||
<div className="text-lg font-bold text-gray-900 dark:text-white">
|
||||
{availableCredits.toLocaleString()}
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div className="text-xs text-gray-500 dark:text-gray-400 mb-1">Shortfall</div>
|
||||
<div className="text-lg font-bold text-error-600 dark:text-error-400">
|
||||
{shortfall.toLocaleString()}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Action Buttons */}
|
||||
<div className="space-y-3">
|
||||
<Button
|
||||
variant="primary"
|
||||
fullWidth
|
||||
onClick={handleUpgradePlan}
|
||||
className="flex items-center justify-center gap-2"
|
||||
>
|
||||
<TrendingUpIcon className="w-4 h-4" />
|
||||
Upgrade Plan
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
variant="outline"
|
||||
fullWidth
|
||||
onClick={handleBuyCredits}
|
||||
className="flex items-center justify-center gap-2"
|
||||
>
|
||||
<CreditCardIcon className="w-4 h-4" />
|
||||
Buy Credits
|
||||
</Button>
|
||||
|
||||
<button
|
||||
onClick={handleViewUsage}
|
||||
className="text-sm text-gray-500 dark:text-gray-400 hover:text-gray-700 dark:hover:text-gray-300"
|
||||
>
|
||||
View Usage Details →
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Cancel Button */}
|
||||
<div className="mt-4 pt-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<Button
|
||||
variant="ghost"
|
||||
tone="neutral"
|
||||
onClick={onClose}
|
||||
>
|
||||
Cancel
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Modal>
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Hook to manage insufficient credits modal state
|
||||
*/
|
||||
export function useInsufficientCreditsModal() {
|
||||
const [isOpen, setIsOpen] = React.useState(false);
|
||||
const [modalProps, setModalProps] = React.useState({
|
||||
requiredCredits: 0,
|
||||
availableCredits: 0,
|
||||
operationType: 'this operation',
|
||||
});
|
||||
|
||||
const showInsufficientCreditsModal = (props: {
|
||||
requiredCredits: number;
|
||||
availableCredits: number;
|
||||
operationType?: string;
|
||||
}) => {
|
||||
setModalProps({
|
||||
requiredCredits: props.requiredCredits,
|
||||
availableCredits: props.availableCredits,
|
||||
operationType: props.operationType || 'this operation',
|
||||
});
|
||||
setIsOpen(true);
|
||||
};
|
||||
|
||||
const closeModal = () => setIsOpen(false);
|
||||
|
||||
return {
|
||||
isOpen,
|
||||
modalProps,
|
||||
showInsufficientCreditsModal,
|
||||
closeModal,
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user