186 lines
6.1 KiB
TypeScript
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>
|
|
);
|
|
}
|