Files
profilarr/frontend/src/hooks/useRegexModal.js

166 lines
5.0 KiB
JavaScript

import {useState, useCallback} from 'react';
import {RegexPatterns} from '@api/data';
import Alert from '@ui/Alert';
import {useRegexTesting} from './useRegexTesting';
export const useRegexModal = (initialPattern, onSave) => {
// Form state
const [name, setName] = useState('');
const [originalName, setOriginalName] = useState('');
const [description, setDescription] = useState('');
const [patternValue, setPatternValue] = useState('');
const [tags, setTags] = useState([]);
const [tests, setTests] = useState([]);
const [isCloning, setIsCloning] = useState(false);
// UI state with more specific error handling
const [formErrors, setFormErrors] = useState({
name: '',
pattern: '',
general: ''
});
const [activeTab, setActiveTab] = useState('general');
const [isDeleting, setIsDeleting] = useState(false);
// Initialize testing functionality
const {isRunningTests, runTests} = useRegexTesting();
const initializeForm = useCallback((pattern, cloning) => {
setIsCloning(cloning || false);
if (pattern) {
const initialName = cloning ? `${pattern.name}` : pattern.name;
setName(initialName);
setOriginalName(cloning ? '' : pattern.name);
setDescription(pattern.description || '');
setPatternValue(pattern.pattern || '');
setTags(pattern.tags || []);
setTests(pattern.tests || []);
} else {
setName('');
setOriginalName('');
setDescription('');
setPatternValue('');
setTags([]);
setTests([]);
}
setFormErrors({name: '', pattern: '', general: ''});
setIsDeleting(false);
}, []);
const handleSave = async () => {
// Name validation
if (!name.trim()) {
Alert.error('Name is required');
return;
}
if (name.length > 64) {
Alert.error('Name must be less than 64 characters');
return;
}
// Pattern validation
if (!patternValue.trim()) {
Alert.error('Pattern is required');
return;
}
// Validate pattern with .NET regex engine
try {
const validationResult = await RegexPatterns.verify(patternValue);
if (!validationResult.valid) {
Alert.error(`Invalid regex pattern: ${validationResult.error || 'Pattern validation failed'}`);
return;
}
} catch (error) {
console.error('Pattern validation error:', error);
Alert.error('Failed to validate pattern. Please check the pattern and try again.');
return;
}
try {
// Clean tests to only include saved data
const cleanTests = tests.map((test, index) => ({
id: test.id || index + 1,
input: test.input,
expected: test.expected
}));
const data = {
name,
pattern: patternValue,
description,
tags,
tests: cleanTests
};
if (initialPattern && !isCloning) {
const hasNameChanged = name !== originalName;
await RegexPatterns.update(
initialPattern.file_name.replace('.yml', ''),
data,
hasNameChanged ? name : undefined
);
Alert.success('Pattern updated successfully');
} else {
await RegexPatterns.create(data);
Alert.success('Pattern created successfully');
}
onSave();
} catch (error) {
console.error('Error saving pattern:', error);
Alert.error(
error.message || 'Failed to save pattern. Please try again.'
);
}
};
const handleRunTests = useCallback(
async (pattern, tests) => {
try {
const testResults = await runTests(pattern, tests);
// We don't update the tests state with results
// Results are only used for display, not saved
return testResults;
} catch (error) {
console.error('Error running tests:', error);
Alert.error(
error.message || 'Failed to run tests. Please try again.'
);
return null;
}
},
[runTests]
);
return {
// Form state
name,
description,
patternValue,
tags,
tests,
// UI state
formErrors,
activeTab,
isDeleting,
isRunningTests,
isCloning,
// Actions
setName,
setDescription,
setPatternValue,
setTags,
setTests,
setActiveTab,
setIsDeleting,
// Main handlers
initializeForm,
handleSave,
handleRunTests
};
};