e99ab789ba
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>
124 lines
4.7 KiB
TypeScript
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>
|
|
);
|
|
}
|