Files
cloud-host/frontend/src/components/resource-upgrade-confirm-modal.tsx
T
keyhan 695e05f948 Add managed databases and services with billing-aligned upgrades.
Introduce product types for managed PostgreSQL, Redis, and RabbitMQ with a dedicated dashboard, Helm-only deploy pipeline, external access, snapshots with progress, and prorated resource or storage upgrades matching application billing rules. PVCs use an expandable StorageClass with automatic migration when legacy disks cannot resize in place.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 19:00:09 +03:30

122 lines
4.6 KiB
TypeScript

'use client';
import { AlertTriangle, CheckCircle, Clock, CreditCard, Wallet } from 'lucide-react';
export type UpgradeCostSummary = {
proratedAmount: number;
remainingHours: number;
currentCost: { hourly: number; monthly: number; yearly: number };
newCost: { hourly: number; monthly: number; yearly: number };
};
export function ResourceUpgradeConfirmModal({
open,
upgradeCostData,
walletBalance,
isPending,
onCancel,
onConfirm,
}: {
open: boolean;
upgradeCostData: UpgradeCostSummary | null;
walletBalance?: number;
isPending: boolean;
onCancel: () => void;
onConfirm: () => void;
}) {
if (!open || !upgradeCostData) return null;
const needsPay = upgradeCostData.proratedAmount > 0;
const walletShort =
needsPay && walletBalance != null && upgradeCostData.proratedAmount > walletBalance;
return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50 p-4">
<div className="bg-white rounded-2xl shadow-2xl max-w-md w-full p-6 animate-fade-in">
<h2 className="text-xl font-bold text-gray-900 mb-2">Confirm Resource Upgrade</h2>
<p className="text-sm text-gray-500 mb-6">
{needsPay
? 'This upgrade requires payment for the remaining billing period.'
: 'No additional cost for this change.'}
</p>
<div className="bg-gray-50 rounded-xl p-4 mb-6 space-y-3">
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600">Current hourly cost</span>
<span className="text-sm font-medium text-gray-900">
{upgradeCostData.currentCost.hourly.toLocaleString()} Toman/hour
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600">New hourly cost</span>
<span className="text-sm font-medium text-gray-900">
{upgradeCostData.newCost.hourly.toLocaleString()} Toman/hour
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600">Remaining hours in period</span>
<span className="text-sm font-medium text-gray-900">{upgradeCostData.remainingHours} hours</span>
</div>
<div className="border-t pt-3 flex items-center justify-between">
<span className="text-sm font-semibold text-gray-700">Prorated amount to pay</span>
<span className="text-lg font-bold text-primary-600">
{upgradeCostData.proratedAmount.toLocaleString()} Toman
</span>
</div>
</div>
<div className="bg-blue-50 rounded-xl p-4 mb-6 flex items-center justify-between">
<div className="flex items-center gap-3">
<Wallet className="w-5 h-5 text-blue-500" />
<span className="text-sm text-blue-700">Wallet Balance</span>
</div>
<span className="text-lg font-bold text-blue-900">
{walletBalance?.toLocaleString() ?? 0} Toman
</span>
</div>
{walletShort && (
<div className="bg-amber-50 border border-amber-200 rounded-lg p-3 mb-4">
<p className="text-sm text-amber-700">
<AlertTriangle className="w-4 h-4 inline mr-1" />
Wallet is short by{' '}
{(upgradeCostData.proratedAmount - (walletBalance ?? 0)).toLocaleString()} Toman. You can pay the
delta by gateway on the invoice page.
</p>
</div>
)}
<div className="flex gap-3">
<button
type="button"
onClick={onCancel}
className="flex-1 px-4 py-2.5 border border-gray-200 rounded-xl font-medium text-gray-700 hover:bg-gray-50 transition-all"
>
Cancel
</button>
<button
type="button"
onClick={onConfirm}
disabled={isPending}
className="flex-1 px-4 py-2.5 bg-primary-600 text-white rounded-xl font-medium hover:bg-primary-700 transition-all disabled:opacity-50 disabled:cursor-not-allowed flex items-center justify-center gap-2"
>
{isPending ? (
<>
<Clock className="w-4 h-4 animate-spin" /> Applying
</>
) : needsPay ? (
<>
<CreditCard className="w-4 h-4" /> Create Invoice & Pay
</>
) : (
<>
<CheckCircle className="w-4 h-4" /> Apply Changes
</>
)}
</button>
</div>
</div>
</div>
);
}