import React, {useState, useEffect} from 'react'; import PropTypes from 'prop-types'; import {saveProfile, deleteProfile} from '../../api/api'; import Modal from '../ui/Modal'; import Alert from '../ui/Alert'; import {Loader, ArrowUp, ArrowDown} from 'lucide-react'; function unsanitize(text) { return text.replace(/\\:/g, ':').replace(/\\n/g, '\n'); } function ProfileModal({ profile: initialProfile, isOpen, onClose, onSave, formats, isCloning = false }) { const [name, setName] = useState(''); const [description, setDescription] = useState(''); const [tags, setTags] = useState([]); const [newTag, setNewTag] = useState(''); const [customFormats, setCustomFormats] = useState([]); const [formatTags, setFormatTags] = useState([]); const [tagScores, setTagScores] = useState({}); const [tagFilter, setTagFilter] = useState(''); const [formatFilter, setFormatFilter] = useState(''); const [error, setError] = useState(''); const [isDeleting, setIsDeleting] = useState(false); const [loading, setLoading] = useState(true); const [modalTitle, setModalTitle] = useState(''); const [formatSortKey, setFormatSortKey] = useState('score'); const [formatSortDirection, setFormatSortDirection] = useState('desc'); const [tagSortKey, setTagSortKey] = useState('name'); const [tagSortDirection, setTagSortDirection] = useState('desc'); useEffect(() => { if (isOpen) { setLoading(true); setModalTitle( isCloning ? 'Clone Profile' : initialProfile && initialProfile.id !== 0 ? 'Edit Profile' : 'Add Profile' ); setName(unsanitize(initialProfile?.name || '')); setDescription(unsanitize(initialProfile?.description || '')); setTags( initialProfile?.tags ? initialProfile.tags.map(unsanitize) : [] ); const initialCustomFormats = initialProfile?.custom_formats || []; const safeCustomFormats = formats.map(format => { const existingFormat = initialCustomFormats.find( cf => cf.id === format.id ); return { id: format.id, name: format.name, score: existingFormat ? existingFormat.score : 0, tags: format.tags || [], date_created: format.date_created, date_modified: format.date_modified }; }); setCustomFormats(safeCustomFormats); const allTags = [ ...new Set(safeCustomFormats.flatMap(format => format.tags)) ]; setFormatTags(allTags); const initialTagScores = {}; allTags.forEach(tag => { initialTagScores[tag] = 0; }); setTagScores(initialTagScores); setError(''); setNewTag(''); setLoading(false); } }, [initialProfile, isOpen, formats, isCloning]); const handleSave = async () => { if (!name.trim()) { setError('Name is required.'); return; } try { await saveProfile({ id: initialProfile ? initialProfile.id : 0, name, description, tags, custom_formats: customFormats }); onSave(); onClose(); } catch (error) { console.error('Error saving profile:', error); setError('Failed to save profile. Please try again.'); } }; const handleDelete = async () => { if (isDeleting) { try { const response = await deleteProfile(initialProfile.id); if (response.error) { Alert.error( `Failed to delete profile: ${response.message}` ); } else { Alert.success('Profile deleted successfully'); onSave(); onClose(); } } catch (error) { console.error('Error deleting profile:', error); Alert.error('Failed to delete profile. Please try again.'); } finally { setIsDeleting(false); } } else { setIsDeleting(true); } }; const handleAddTag = () => { if (newTag.trim() && !tags.includes(newTag.trim())) { setTags([...tags, newTag.trim()]); setNewTag(''); } }; const handleRemoveTag = tagToRemove => { setTags(tags.filter(tag => tag !== tagToRemove)); }; const handleScoreChange = (formatId, score) => { setCustomFormats( customFormats.map(format => format.id === formatId ? {...format, score: Math.max(parseInt(score) || 0, 0)} : format ) ); }; const handleTagScoreChange = (tag, score) => { setTagScores({...tagScores, [tag]: Math.max(parseInt(score) || 0, 0)}); // Update scores for all custom formats with this tag setCustomFormats( customFormats.map(format => { if (format.tags.includes(tag)) { return { ...format, score: Math.max(parseInt(score) || 0, 0) }; } return format; }) ); }; const filteredTags = formatTags.filter(tag => tag.toLowerCase().includes(tagFilter.toLowerCase()) ); const filteredFormats = customFormats.filter(format => format.name.toLowerCase().includes(formatFilter.toLowerCase()) ); const handleInputFocus = event => { event.target.select(); }; const sortedFormats = [...filteredFormats].sort((a, b) => { if (formatSortKey === 'name') { return formatSortDirection === 'asc' ? a.name.localeCompare(b.name) : b.name.localeCompare(a.name); } else if (formatSortKey === 'score') { return formatSortDirection === 'asc' ? a.score - b.score : b.score - a.score; } else if (formatSortKey === 'dateCreated') { return formatSortDirection === 'asc' ? new Date(a.date_created) - new Date(b.date_created) : new Date(b.date_created) - new Date(a.date_created); } else if (formatSortKey === 'dateModified') { return formatSortDirection === 'asc' ? new Date(a.date_modified) - new Date(b.date_modified) : new Date(b.date_modified) - new Date(a.date_modified); } return 0; }); const sortedTags = [...filteredTags].sort((a, b) => { if (tagSortKey === 'name') { return tagSortDirection === 'asc' ? a.localeCompare(b) : b.localeCompare(a); } else if (tagSortKey === 'score') { return tagSortDirection === 'asc' ? tagScores[a] - tagScores[b] : tagScores[b] - tagScores[a]; } else if (tagSortKey === 'dateCreated') { return tagSortDirection === 'asc' ? new Date(a.date_created) - new Date(b.date_created) : new Date(b.date_created) - new Date(a.date_created); } else if (tagSortKey === 'dateModified') { return tagSortDirection === 'asc' ? new Date(a.date_modified) - new Date(b.date_modified) : new Date(b.date_modified) - new Date(a.date_modified); } return 0; }); const SortDropdown = ({options, currentKey, currentDirection, onSort}) => { const [isOpen, setIsOpen] = useState(false); const handleSort = key => { if (key === currentKey) { onSort(key, currentDirection === 'asc' ? 'desc' : 'asc'); } else { onSort(key, 'desc'); } setIsOpen(false); }; return (