Files
igny8/frontend/src/components/keywords-library/BulkAddConfirmation.tsx
IGNY8 VPS (Salman) 43df7af989 keywrods libarry update
2026-01-18 17:57:56 +00:00

186 lines
6.1 KiB
TypeScript

/**
* BulkAddConfirmation - Confirmation modal for bulk adding keywords
* Shows preview of keywords to be added and handles the bulk add action
*/
import { useState } from 'react';
import { Modal } from '../ui/modal';
import Button from '../ui/button/Button';
import { PlusIcon, CheckCircleIcon, AlertIcon } from '../../icons';
import { Spinner } from '../ui/spinner/Spinner';
interface BulkAddConfirmationProps {
isOpen: boolean;
onClose: () => void;
onConfirm: () => Promise<{ added: number; skipped: number; total_requested: number }>;
keywordCount: number;
sectorName?: string;
statTypeLabel?: string;
}
export default function BulkAddConfirmation({
isOpen,
onClose,
onConfirm,
keywordCount,
sectorName,
statTypeLabel,
}: BulkAddConfirmationProps) {
const [isAdding, setIsAdding] = useState(false);
const [result, setResult] = useState<{ added: number; skipped: number; total_requested: number } | null>(null);
const [error, setError] = useState<string | null>(null);
const handleConfirm = async () => {
setIsAdding(true);
setError(null);
try {
const response = await onConfirm();
setResult(response);
} catch (err: any) {
setError(err.message || 'Failed to add keywords');
} finally {
setIsAdding(false);
}
};
const handleClose = () => {
setResult(null);
setError(null);
onClose();
};
// Success state
if (result) {
return (
<Modal isOpen={isOpen} onClose={handleClose} showCloseButton={true}>
<div className="p-6 text-center">
<div className="mx-auto w-14 h-14 rounded-full bg-success-100 dark:bg-success-900/30 flex items-center justify-center mb-4">
<CheckCircleIcon className="w-8 h-8 text-success-600 dark:text-success-400" />
</div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Keywords Added Successfully!
</h3>
<div className="space-y-2 mb-6">
<p className="text-3xl font-bold text-success-600 dark:text-success-400">
{result.added}
</p>
<p className="text-sm text-gray-500 dark:text-gray-400">
keywords added to your site
</p>
{result.skipped > 0 && (
<p className="text-sm text-amber-600 dark:text-amber-400">
{result.skipped} keywords were skipped (already exist)
</p>
)}
</div>
<Button onClick={handleClose} variant="primary">
Done
</Button>
</div>
</Modal>
);
}
// Error state
if (error) {
return (
<Modal isOpen={isOpen} onClose={handleClose} showCloseButton={true}>
<div className="p-6 text-center">
<div className="mx-auto w-14 h-14 rounded-full bg-error-100 dark:bg-error-900/30 flex items-center justify-center mb-4">
<AlertIcon className="w-8 h-8 text-error-600 dark:text-error-400" />
</div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Failed to Add Keywords
</h3>
<p className="text-sm text-gray-500 dark:text-gray-400 mb-6">
{error}
</p>
<div className="flex gap-3 justify-center">
<Button onClick={handleClose} variant="outline">
Close
</Button>
<Button onClick={handleConfirm} variant="primary">
Try Again
</Button>
</div>
</div>
</Modal>
);
}
// Confirmation state
return (
<Modal isOpen={isOpen} onClose={handleClose} showCloseButton={!isAdding}>
<div className="p-6">
<div className="text-center mb-6">
<div className="mx-auto w-14 h-14 rounded-full bg-brand-100 dark:bg-brand-900/30 flex items-center justify-center mb-4">
<PlusIcon className="w-8 h-8 text-brand-600 dark:text-brand-400" />
</div>
<h3 className="text-lg font-semibold text-gray-900 dark:text-white mb-2">
Add Keywords to Your Site
</h3>
<p className="text-sm text-gray-500 dark:text-gray-400">
You're about to add keywords to your site's keyword list.
</p>
</div>
{/* Summary */}
<div className="bg-gray-50 dark:bg-gray-800/50 rounded-lg p-4 mb-6">
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-gray-600 dark:text-gray-400">Keywords to add:</span>
<span className="text-lg font-bold text-gray-900 dark:text-white">{keywordCount}</span>
</div>
{sectorName && (
<div className="flex items-center justify-between mb-2">
<span className="text-sm text-gray-600 dark:text-gray-400">Sector:</span>
<span className="text-sm font-medium text-gray-900 dark:text-white">{sectorName}</span>
</div>
)}
{statTypeLabel && (
<div className="flex items-center justify-between">
<span className="text-sm text-gray-600 dark:text-gray-400">Category:</span>
<span className="text-sm font-medium text-gray-900 dark:text-white">{statTypeLabel}</span>
</div>
)}
</div>
{/* Actions */}
<div className="flex gap-3 justify-end">
<Button onClick={handleClose} variant="outline" disabled={isAdding}>
Cancel
</Button>
<Button
onClick={handleConfirm}
variant="primary"
disabled={isAdding}
>
{isAdding ? (
<span className="flex items-center gap-2">
<Spinner size="sm" />
Adding...
</span>
) : (
<span className="flex items-center gap-2">
<PlusIcon className="w-4 h-4" />
Add {keywordCount} Keywords
</span>
)}
</Button>
</div>
</div>
</Modal>
);
}