Files
smoothschedule/frontend/src/components/ConfirmationModal.tsx
poduck c7f241b30a feat(i18n): Comprehensive internationalization of frontend components and pages
Translate all hardcoded English strings to use i18n translation keys:

Components:
- TransactionDetailModal: payment details, refunds, technical info
- ConnectOnboarding/ConnectOnboardingEmbed: Stripe Connect setup
- StripeApiKeysForm: API key management
- DomainPurchase: domain registration flow
- Sidebar: navigation labels
- Schedule/Sidebar, PendingSidebar: scheduler UI
- MasqueradeBanner: masquerade status
- Dashboard widgets: metrics, capacity, customers, tickets
- Marketing: PricingTable, PluginShowcase, BenefitsSection
- ConfirmationModal, ServiceList: common UI

Pages:
- Staff: invitation flow, role management
- Customers: form placeholders
- Payments: transactions, payouts, billing
- BookingSettings: URL and redirect configuration
- TrialExpired: upgrade prompts and features
- PlatformSettings, PlatformBusinesses: admin UI
- HelpApiDocs: API documentation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-12-03 21:40:54 -05:00

135 lines
4.4 KiB
TypeScript

import React from 'react';
import { X, AlertTriangle, CheckCircle, Info, AlertCircle } from 'lucide-react';
import { useTranslation } from 'react-i18next';
type ModalVariant = 'info' | 'warning' | 'danger' | 'success';
interface ConfirmationModalProps {
isOpen: boolean;
onClose: () => void;
onConfirm: () => void;
title: string;
message: string | React.ReactNode;
confirmText?: string;
cancelText?: string;
variant?: ModalVariant;
isLoading?: boolean;
}
const variantConfig: Record<ModalVariant, {
icon: React.ReactNode;
iconBg: string;
confirmButtonClass: string;
}> = {
info: {
icon: <Info size={24} className="text-blue-600 dark:text-blue-400" />,
iconBg: 'bg-blue-100 dark:bg-blue-900/30',
confirmButtonClass: 'bg-blue-600 hover:bg-blue-700 text-white',
},
warning: {
icon: <AlertTriangle size={24} className="text-amber-600 dark:text-amber-400" />,
iconBg: 'bg-amber-100 dark:bg-amber-900/30',
confirmButtonClass: 'bg-amber-600 hover:bg-amber-700 text-white',
},
danger: {
icon: <AlertCircle size={24} className="text-red-600 dark:text-red-400" />,
iconBg: 'bg-red-100 dark:bg-red-900/30',
confirmButtonClass: 'bg-red-600 hover:bg-red-700 text-white',
},
success: {
icon: <CheckCircle size={24} className="text-green-600 dark:text-green-400" />,
iconBg: 'bg-green-100 dark:bg-green-900/30',
confirmButtonClass: 'bg-green-600 hover:bg-green-700 text-white',
},
};
const ConfirmationModal: React.FC<ConfirmationModalProps> = ({
isOpen,
onClose,
onConfirm,
title,
message,
confirmText,
cancelText,
variant = 'info',
isLoading = false,
}) => {
const { t } = useTranslation();
if (!isOpen) return null;
const config = variantConfig[variant];
const handleConfirm = () => {
onConfirm();
};
return (
<div className="fixed inset-0 bg-black/50 flex items-center justify-center z-50">
<div className="bg-white dark:bg-gray-800 rounded-xl shadow-xl w-full max-w-md mx-4">
{/* Modal Header */}
<div className="flex items-center justify-between p-6 border-b border-gray-200 dark:border-gray-700">
<div className="flex items-center gap-3">
<div className={`p-2 rounded-lg ${config.iconBg}`}>
{config.icon}
</div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white">{title}</h3>
</div>
<button
onClick={onClose}
disabled={isLoading}
className="p-2 hover:bg-gray-100 dark:hover:bg-gray-700 rounded-lg transition-colors disabled:opacity-50"
>
<X size={20} className="text-gray-500 dark:text-gray-400" />
</button>
</div>
{/* Modal Body */}
<div className="p-6">
<div className="text-gray-600 dark:text-gray-300">
{typeof message === 'string' ? <p>{message}</p> : message}
</div>
</div>
{/* Modal Footer */}
<div className="flex items-center justify-end gap-3 p-6 border-t border-gray-200 dark:border-gray-700">
<button
onClick={onClose}
disabled={isLoading}
className="px-4 py-2 text-gray-700 dark:text-gray-300 border border-gray-300 dark:border-gray-600 rounded-lg hover:bg-gray-50 dark:hover:bg-gray-700 transition-colors disabled:opacity-50"
>
{cancelText || t('common.cancel')}
</button>
<button
onClick={handleConfirm}
disabled={isLoading}
className={`px-4 py-2 rounded-lg font-medium transition-colors disabled:opacity-50 flex items-center gap-2 ${config.confirmButtonClass}`}
>
{isLoading && (
<svg className="animate-spin h-4 w-4" viewBox="0 0 24 24">
<circle
className="opacity-25"
cx="12"
cy="12"
r="10"
stroke="currentColor"
strokeWidth="4"
fill="none"
/>
<path
className="opacity-75"
fill="currentColor"
d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4zm2 5.291A7.962 7.962 0 014 12H0c0 3.042 1.135 5.824 3 7.938l3-2.647z"
/>
</svg>
)}
{confirmText || t('common.confirm')}
</button>
</div>
</div>
</div>
);
};
export default ConfirmationModal;