);
},
- size: 90,
+ size: 150,
}),
];
diff --git a/frontend/src/components/SpecificationModal/SpecificationModal.tsx b/frontend/src/components/SpecificationModal/SpecificationModal.tsx
new file mode 100644
index 0000000..b0e6250
--- /dev/null
+++ b/frontend/src/components/SpecificationModal/SpecificationModal.tsx
@@ -0,0 +1,464 @@
+import { useState, useEffect } from 'react';
+import {
+ Dialog,
+ DialogTitle,
+ DialogContent,
+ DialogActions,
+ Button,
+ Typography,
+ Box,
+ LinearProgress,
+ Alert,
+ TextField,
+ IconButton,
+ Tooltip,
+ Tabs,
+ Tab,
+ List,
+ ListItem,
+ ListItemText,
+ ListItemSecondaryAction,
+ Divider,
+ Chip,
+} from '@mui/material';
+import {
+ Edit,
+ Save,
+ Close,
+ Refresh,
+ Delete,
+ Restore,
+ Visibility,
+ History,
+} from '@mui/icons-material';
+import Markdown from 'react-markdown';
+import type { Idea, SpecificationHistoryItem } from '../../types/idea';
+
+interface SpecificationModalProps {
+ open: boolean;
+ onClose: () => void;
+ idea: Idea | null;
+ specification: string | null;
+ isLoading: boolean;
+ error: Error | null;
+ onSave: (specification: string) => void;
+ isSaving: boolean;
+ onRegenerate: () => void;
+ history: SpecificationHistoryItem[];
+ isHistoryLoading: boolean;
+ onDeleteHistoryItem: (historyId: string) => void;
+ onRestoreFromHistory: (historyId: string) => void;
+ isRestoring: boolean;
+}
+
+interface TabPanelProps {
+ children?: React.ReactNode;
+ index: number;
+ value: number;
+}
+
+function TabPanel({ children, value, index }: TabPanelProps) {
+ return (
+
+ {value === index && children}
+
+ );
+}
+
+export function SpecificationModal({
+ open,
+ onClose,
+ idea,
+ specification,
+ isLoading,
+ error,
+ onSave,
+ isSaving,
+ onRegenerate,
+ history,
+ isHistoryLoading,
+ onDeleteHistoryItem,
+ onRestoreFromHistory,
+ isRestoring,
+}: SpecificationModalProps) {
+ const [isEditing, setIsEditing] = useState(false);
+ const [editedText, setEditedText] = useState('');
+ const [tabIndex, setTabIndex] = useState(0);
+ const [viewingHistoryItem, setViewingHistoryItem] = useState(null);
+
+ // Сбрасываем состояние при открытии/закрытии
+ useEffect(() => {
+ if (open && specification) {
+ setEditedText(specification);
+ setIsEditing(false);
+ setTabIndex(0);
+ setViewingHistoryItem(null);
+ }
+ }, [open, specification]);
+
+ const handleEdit = () => {
+ setEditedText(specification || '');
+ setIsEditing(true);
+ };
+
+ const handleCancel = () => {
+ setEditedText(specification || '');
+ setIsEditing(false);
+ };
+
+ const handleSave = () => {
+ onSave(editedText);
+ setIsEditing(false);
+ };
+
+ const handleRegenerate = () => {
+ setViewingHistoryItem(null);
+ setTabIndex(0);
+ onRegenerate();
+ };
+
+ const handleViewHistoryItem = (item: SpecificationHistoryItem) => {
+ setViewingHistoryItem(item);
+ };
+
+ const handleCloseHistoryView = () => {
+ setViewingHistoryItem(null);
+ };
+
+ const handleRestoreFromHistory = (historyId: string) => {
+ onRestoreFromHistory(historyId);
+ setViewingHistoryItem(null);
+ setTabIndex(0);
+ };
+
+ const formatDate = (dateString: string | null) => {
+ if (!dateString) return '';
+ return new Date(dateString).toLocaleString('ru-RU', {
+ day: '2-digit',
+ month: '2-digit',
+ year: 'numeric',
+ hour: '2-digit',
+ minute: '2-digit',
+ });
+ };
+
+ const hasHistory = history.length > 0;
+
+ return (
+