diff --git a/frontend/src/components/format/FormatModal.jsx b/frontend/src/components/format/FormatModal.jsx index 5177be0..1e8186e 100644 --- a/frontend/src/components/format/FormatModal.jsx +++ b/frontend/src/components/format/FormatModal.jsx @@ -1,4 +1,4 @@ -import { useState, useEffect, useRef } from "react"; +import { useState, useEffect } from "react"; import PropTypes from "prop-types"; import { saveFormat, deleteFormat, getRegexes } from "../../api/api"; import ConditionModal from "../condition/ConditionModal"; @@ -6,7 +6,13 @@ import ConditionCard from "../condition/ConditionCard"; import Modal from "../ui/Modal"; import Alert from "../ui/Alert"; -function FormatModal({ format: initialFormat, isOpen, onClose, onSave }) { +function FormatModal({ + format: initialFormat, + isOpen, + onClose, + onSave, + isCloning = false, +}) { const [name, setName] = useState(""); const [description, setDescription] = useState(""); const [conditions, setConditions] = useState([]); @@ -17,10 +23,19 @@ function FormatModal({ format: initialFormat, isOpen, onClose, onSave }) { const [tags, setTags] = useState([]); const [newTag, setNewTag] = useState(""); const [isDeleting, setIsDeleting] = useState(false); + const [modalTitle, setModalTitle] = useState(""); useEffect(() => { if (isOpen) { - if (initialFormat && initialFormat.id !== 0) { + if (isCloning) { + setModalTitle("Clone Custom Format"); + } else if (initialFormat && initialFormat.id !== 0) { + setModalTitle("Edit Custom Format"); + } else { + setModalTitle("Add Custom Format"); + } + + if (initialFormat && (initialFormat.id !== 0 || isCloning)) { setName(initialFormat.name); setDescription(initialFormat.description); setConditions(initialFormat.conditions || []); @@ -31,12 +46,13 @@ function FormatModal({ format: initialFormat, isOpen, onClose, onSave }) { setConditions([]); setTags([]); } + setError(""); setNewTag(""); setIsDeleting(false); fetchRegexes(); } - }, [initialFormat, isOpen]); + }, [isOpen, initialFormat, isCloning]); const fetchRegexes = async () => { try { @@ -133,7 +149,7 @@ function FormatModal({ format: initialFormat, isOpen, onClose, onSave }) { {error &&
{error}
} @@ -213,17 +229,17 @@ function FormatModal({ format: initialFormat, isOpen, onClose, onSave }) { > Add Condition -
+
- {initialFormat && ( + {initialFormat && initialFormat.id !== 0 && (
); diff --git a/frontend/src/components/profile/ProfileModal.jsx b/frontend/src/components/profile/ProfileModal.jsx index 9a43648..12362f6 100644 --- a/frontend/src/components/profile/ProfileModal.jsx +++ b/frontend/src/components/profile/ProfileModal.jsx @@ -1,61 +1,83 @@ -import { useState, useEffect } from "react"; +import React, { useState, useEffect } from "react"; import PropTypes from "prop-types"; -import { saveProfile, deleteProfile, getFormats } from "../../api/api"; +import { saveProfile, deleteProfile } from "../../api/api"; import Modal from "../ui/Modal"; import Alert from "../ui/Alert"; +import { Loader } from "lucide-react"; function unsanitize(text) { return text.replace(/\\:/g, ":").replace(/\\n/g, "\n"); } -function ProfileModal({ profile: initialProfile, isOpen, onClose, onSave }) { +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 [error, setError] = useState(""); const [isDeleting, setIsDeleting] = useState(false); + const [loading, setLoading] = useState(true); + const [modalTitle, setModalTitle] = useState(""); useEffect(() => { if (isOpen) { - if (initialProfile && initialProfile.id !== 0) { - setName(unsanitize(initialProfile.name)); - setDescription(unsanitize(initialProfile.description)); - setTags(initialProfile.tags ? initialProfile.tags.map(unsanitize) : []); - setCustomFormats(initialProfile.custom_formats || []); - } else { - setName(""); - setDescription(""); - setTags([]); - setCustomFormats([]); - } - setError(""); - setNewTag(""); - fetchAllFormats(); - } - }, [initialProfile, isOpen]); + setLoading(true); - const fetchAllFormats = async () => { - try { - const formats = await getFormats(); - const updatedCustomFormats = formats.map((format) => { - const existingFormat = - initialProfile && initialProfile.custom_formats - ? initialProfile.custom_formats.find((cf) => cf.id === format.id) - : null; + 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 || [], }; }); - setCustomFormats(updatedCustomFormats); - } catch (error) { - console.error("Error fetching formats:", error); - setError("Failed to fetch formats."); + setCustomFormats(safeCustomFormats); + + // Extract all unique tags from custom formats + const allTags = [ + ...new Set(safeCustomFormats.flatMap((format) => format.tags)), + ]; + setFormatTags(allTags); + + // Initialize tag scores + const initialTagScores = {}; + allTags.forEach((tag) => { + initialTagScores[tag] = 0; + }); + setTagScores(initialTagScores); + + setError(""); + setNewTag(""); + setLoading(false); } - }; + }, [initialProfile, isOpen, formats, isCloning]); const handleSave = async () => { if (!name.trim()) { @@ -81,9 +103,7 @@ function ProfileModal({ profile: initialProfile, isOpen, onClose, onSave }) { const handleDelete = async () => { if (isDeleting) { try { - console.log("Attempting to delete profile with ID:", initialProfile.id); const response = await deleteProfile(initialProfile.id); - console.log("Delete response:", response); if (response.error) { Alert.error(`Failed to delete profile: ${response.message}`); } else { @@ -123,110 +143,163 @@ function ProfileModal({ profile: initialProfile, isOpen, onClose, onSave }) { ); }; + 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()) + ); + return ( - - {error &&
{error}
} -
- - setName(e.target.value)} - placeholder="Enter profile name" - className="w-full p-2 border rounded dark:bg-gray-700 dark:text-gray-200 dark:border-gray-600" - /> -
-
- -