Files
cloud-host/frontend/src/components/resource-upgrade-confirm-modal.tsx
T
keyhan e99ab789ba Localize shared modal, overlay and deployment-progress components.
Localize the confirm modal, delete button, deleting overlays/modal, the
build-progress modal, deployment progress bar/manager and the resource
upgrade modal via a shared components dictionary. Build-phase labels now
resolve from the dictionary; deleting overlays take name/kind and build
their own localized message (callers updated).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 13:23:44 +03:30

124 lines
4.7 KiB
TypeScript

'use client';
import { AlertTriangle, CheckCircle, Clock, CreditCard, Wallet } from 'lucide-react';
import { useT } from '@/i18n/I18nProvider';
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;
}) {
const t = useT();
const c = t.components;
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">{c.upgradeTitle}</h2>
<p className="text-sm text-gray-500 mb-6">
{needsPay ? c.upgradeNeedsPay : c.upgradeNoCost}
</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">{c.currentHourlyCost}</span>
<span className="text-sm font-medium text-gray-900">
{c.tomanPerHour.replace('{n}', upgradeCostData.currentCost.hourly.toLocaleString('en-US'))}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600">{c.newHourlyCost}</span>
<span className="text-sm font-medium text-gray-900">
{c.tomanPerHour.replace('{n}', upgradeCostData.newCost.hourly.toLocaleString('en-US'))}
</span>
</div>
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600">{c.remainingHoursInPeriod}</span>
<span className="text-sm font-medium text-gray-900">{c.hoursUnit.replace('{n}', String(upgradeCostData.remainingHours))}</span>
</div>
<div className="border-t pt-3 flex items-center justify-between">
<span className="text-sm font-semibold text-gray-700">{c.proratedAmountToPay}</span>
<span className="text-lg font-bold text-primary-600">
{c.tomanUnit.replace('{n}', upgradeCostData.proratedAmount.toLocaleString('en-US'))}
</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">{c.walletBalance}</span>
</div>
<span className="text-lg font-bold text-blue-900">
{c.tomanUnit.replace('{n}', (walletBalance ?? 0).toLocaleString('en-US'))}
</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 rtl:mr-0 rtl:ml-1" />
{c.walletShortBy.replace(
'{n}',
(upgradeCostData.proratedAmount - (walletBalance ?? 0)).toLocaleString('en-US'),
)}
</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"
>
{t.common.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" /> {c.applying}
</>
) : needsPay ? (
<>
<CreditCard className="w-4 h-4" /> {c.createInvoicePay}
</>
) : (
<>
<CheckCircle className="w-4 h-4" /> {c.applyChanges}
</>
)}
</button>
</div>
</div>
</div>
);
}