Files
Remote-Control-Center/mc_test/src/renderer/components/SaveConfirmationModal.tsx
2025-11-25 09:56:15 +03:00

177 lines
7.1 KiB
TypeScript
Executable File
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Machine save confirmation modal
*/
import React, { useState } from 'react';
import { Save, X } from 'lucide-react';
import { useMachineStore } from '../store/machineStore';
import { log } from '../utils/logger';
import { Button } from './shared/Button';
interface SaveConfirmationModalProps {
isOpen: boolean;
onClose: () => void;
onSuccess?: () => void;
}
export const SaveConfirmationModal: React.FC<SaveConfirmationModalProps> = ({
isOpen,
onClose,
onSuccess
}) => {
const {
machineToSave,
createSavedMachine,
setMachineToSave,
setShowSaveConfirmModal
} = useMachineStore();
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const handleConfirm = async () => {
if (!machineToSave) {
setError('Нет данных для сохранения');
return;
}
setLoading(true);
setError(null);
try {
log.info('SaveConfirmationModal', 'Saving machine', { name: machineToSave.name });
await createSavedMachine(machineToSave);
log.info('SaveConfirmationModal', 'Machine saved successfully');
// Сбрасываем состояние
setMachineToSave(null);
setShowSaveConfirmModal(false);
if (onSuccess) {
onSuccess();
}
onClose();
} catch (err) {
const errorMessage = err instanceof Error ? err.message : 'Не удалось сохранить машину';
log.error('SaveConfirmationModal', 'Failed to save machine', { error: errorMessage });
setError(errorMessage);
} finally {
setLoading(false);
}
};
const handleCancel = () => {
setMachineToSave(null);
setShowSaveConfirmModal(false);
setError(null);
onClose();
};
if (!isOpen || !machineToSave) return null;
return (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4">
<div className="bg-kaspersky-bg-card rounded-lg shadow-2xl border border-kaspersky-border max-w-md w-full select-none">
<div className="p-6">
{/* Header */}
<div className="flex items-center gap-3 mb-6">
<div className="flex-shrink-0 w-12 h-12 bg-kaspersky-primary bg-opacity-10 rounded-full flex items-center justify-center border-2 border-kaspersky-primary border-opacity-30">
<Save className="w-6 h-6 text-kaspersky-primary" />
</div>
<div>
<h2 className="text-xl font-bold text-kaspersky-text-dark">Сохранить машину?</h2>
<p className="text-sm text-kaspersky-text-light">Подтвердите сохранение в профиль</p>
</div>
</div>
{/* Error */}
{error && (
<div className="mb-4 p-3 bg-kaspersky-danger bg-opacity-10 border border-kaspersky-danger rounded text-kaspersky-danger text-sm">
{error}
</div>
)}
{/* Machine Info */}
<div className="mb-6 p-4 bg-kaspersky-bg rounded-lg border border-kaspersky-border space-y-3">
<div className="flex justify-between items-center">
<span className="text-kaspersky-text-light text-sm font-medium">Название:</span>
<span className="text-kaspersky-text-dark font-semibold">{machineToSave.name}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-kaspersky-text-light text-sm font-medium">Хост:</span>
<span className="text-kaspersky-text-dark font-mono text-sm">{machineToSave.hostname}:{machineToSave.port}</span>
</div>
<div className="flex justify-between items-center">
<span className="text-kaspersky-text-light text-sm font-medium">Протокол:</span>
<span className="text-kaspersky-primary uppercase text-sm font-bold">{machineToSave.protocol}</span>
</div>
{machineToSave.description && (
<div className="pt-3 border-t border-kaspersky-border">
<span className="text-kaspersky-text-light text-sm font-medium block mb-1">Описание:</span>
<span className="text-kaspersky-text text-sm">{machineToSave.description}</span>
</div>
)}
{machineToSave.tags && machineToSave.tags.length > 0 && (
<div className="pt-3 border-t border-kaspersky-border">
<span className="text-kaspersky-text-light text-sm font-medium block mb-2">Теги:</span>
<div className="flex flex-wrap gap-1.5">
{machineToSave.tags.map((tag, index) => (
<span
key={index}
className="px-2.5 py-1 bg-kaspersky-primary bg-opacity-20 text-kaspersky-primary rounded-md text-xs font-medium border border-kaspersky-primary border-opacity-30"
>
{tag}
</span>
))}
</div>
</div>
)}
{machineToSave.is_favorite && (
<div className="pt-3 border-t border-kaspersky-border flex items-center gap-2">
<svg className="w-5 h-5 text-kaspersky-warning" fill="currentColor" viewBox="0 0 20 20">
<path d="M9.049 2.927c.3-.921 1.603-.921 1.902 0l1.07 3.292a1 1 0 00.95.69h3.462c.969 0 1.371 1.24.588 1.81l-2.8 2.034a1 1 0 00-.364 1.118l1.07 3.292c.3.921-.755 1.688-1.54 1.118l-2.8-2.034a1 1 0 00-1.175 0l-2.8 2.034c-.784.57-1.838-.197-1.539-1.118l1.07-3.292a1 1 0 00-.364-1.118L2.98 8.72c-.783-.57-.38-1.81.588-1.81h3.461a1 1 0 00.951-.69l1.07-3.292z" />
</svg>
<span className="text-kaspersky-warning text-sm font-semibold">В избранном</span>
</div>
)}
</div>
{/* Description */}
<div className="mb-6 p-3 bg-kaspersky-primary bg-opacity-5 rounded-lg border border-kaspersky-primary border-opacity-20">
<p className="text-kaspersky-text text-sm">
💾 Машина будет сохранена в вашем профиле и доступна из любой сессии.
Учетные данные для подключения будут запрашиваться отдельно при каждом подключении.
</p>
</div>
{/* Actions */}
<div className="flex gap-3">
<Button
onClick={handleConfirm}
disabled={loading}
variant="primary"
fullWidth
isLoading={loading}
leftIcon={!loading ? <Save size={18} /> : undefined}
>
{loading ? 'Сохранение...' : 'Сохранить'}
</Button>
<Button
onClick={handleCancel}
disabled={loading}
variant="secondary"
leftIcon={<X size={18} />}
>
Отмена
</Button>
</div>
</div>
</div>
</div>
);
};