diff --git a/docs/PROMPTS.md b/docs/PROMPTS.md index 52b06d2..c216b27 100644 --- a/docs/PROMPTS.md +++ b/docs/PROMPTS.md @@ -22,11 +22,13 @@ You are a senior software engineer specializsing in NodeJS, Typescript, front en - Make no assumptions. - Ask clarifying questions. - Ultrathink -- You will be syncing the desktop and mobile versions of the homepage +- Troubleshoot UX problems when using Firefox *** CONTEXT *** - This is a modern web app for managing a vehicle fleet. It has both a desktop and mobile versions of the site that both need to maintain feature parity. It's currently deployed via docker compose but in the future will be deployed via k8s. -- Read README.md CLAUDE.md and AI-INDEX.md and follow relevant instructions to understand this code repository in the context of this change. +- Read README.md CLAUDE.md and AI-INDEX.md and follow relevant instructions to understand this code repository in the context of this change. +- The vehicles dynamic drop downs are broken on Firefox. The dropdowns don't populate immediately and require selecting and unselecting options to work. +- There is a console error "Error: Can't find the actor ID for objects-manager from root or target actor's form. types.js:559:11" *** CHANGES TO IMPLEMENT *** diff --git a/frontend/src/features/vehicles/components/VehicleForm.tsx b/frontend/src/features/vehicles/components/VehicleForm.tsx index 66516ce..4445569 100644 --- a/frontend/src/features/vehicles/components/VehicleForm.tsx +++ b/frontend/src/features/vehicles/components/VehicleForm.tsx @@ -78,13 +78,14 @@ export const VehicleForm: React.FC = ({ const [engines, setEngines] = useState([]); const [trims, setTrims] = useState([]); const [transmissions, setTransmissions] = useState([]); - const [selectedYear, setSelectedYear] = useState(); - const [selectedMake, setSelectedMake] = useState(''); - const [selectedModel, setSelectedModel] = useState(''); - const [selectedTrim, setSelectedTrim] = useState(''); const [loadingDropdowns, setLoadingDropdowns] = useState(false); const hasInitialized = useRef(false); const isInitializing = useRef(false); + // Track previous values for cascade change detection (replaces useState) + const prevYear = useRef(undefined); + const prevMake = useRef(''); + const prevModel = useRef(''); + const prevTrim = useRef(''); const [currentImageUrl, setCurrentImageUrl] = useState(initialData?.imageUrl); const [previewUrl, setPreviewUrl] = useState(null); @@ -106,6 +107,7 @@ export const VehicleForm: React.FC = ({ const watchedYear = watch('year'); const watchedMake = watch('make'); const watchedModel = watch('model'); + const watchedTrim = watch('trimLevel'); // Load years on component mount useEffect(() => { @@ -133,7 +135,7 @@ export const VehicleForm: React.FC = ({ setLoadingDropdowns(true); // Step 1: Set year and load makes - setSelectedYear(initialData.year); + prevYear.current = initialData.year; const makesData = await vehiclesApi.getMakes(initialData.year); setMakes(makesData); @@ -143,7 +145,7 @@ export const VehicleForm: React.FC = ({ } // Step 2: Set make and load models - setSelectedMake(initialData.make); + prevMake.current = initialData.make; const modelsData = await vehiclesApi.getModels(initialData.year, initialData.make); setModels(modelsData); @@ -153,13 +155,13 @@ export const VehicleForm: React.FC = ({ } // Step 3: Set model and load trims (transmissions loaded after trim selected) - setSelectedModel(initialData.model); + prevModel.current = initialData.model; const trimsData = await vehiclesApi.getTrims(initialData.year, initialData.make, initialData.model); setTrims(trimsData); if (initialData.trimLevel) { // Step 4: Set trim and load engines + transmissions - setSelectedTrim(initialData.trimLevel); + prevTrim.current = initialData.trimLevel; const [enginesData, transmissionsData] = await Promise.all([ vehiclesApi.getEngines( initialData.year, @@ -202,18 +204,18 @@ export const VehicleForm: React.FC = ({ // Skip during initialization if (isInitializing.current) return; - if (watchedYear && watchedYear !== selectedYear) { + if (watchedYear && watchedYear !== prevYear.current) { const loadMakes = async () => { setLoadingDropdowns(true); try { const makesData = await vehiclesApi.getMakes(watchedYear); setMakes(makesData); - setSelectedYear(watchedYear); + prevYear.current = watchedYear; // Clear dependent selections - setSelectedMake(''); - setSelectedModel(''); - setSelectedTrim(''); + prevMake.current = ''; + prevModel.current = ''; + prevTrim.current = ''; setModels([]); setTrims([]); setEngines([]); @@ -233,24 +235,24 @@ export const VehicleForm: React.FC = ({ loadMakes(); } - }, [watchedYear, selectedYear, setValue]); + }, [watchedYear, setValue]); // Load models when make changes useEffect(() => { // Skip during initialization if (isInitializing.current) return; - if (watchedMake && watchedYear && watchedMake !== selectedMake) { + if (watchedMake && watchedYear && watchedMake !== prevMake.current) { const loadModels = async () => { setLoadingDropdowns(true); try { const modelsData = await vehiclesApi.getModels(watchedYear, watchedMake); setModels(modelsData); - setSelectedMake(watchedMake); + prevMake.current = watchedMake; // Clear dependent selections - setSelectedModel(''); - setSelectedTrim(''); + prevModel.current = ''; + prevTrim.current = ''; setTrims([]); setEngines([]); setTransmissions([]); @@ -268,23 +270,23 @@ export const VehicleForm: React.FC = ({ loadModels(); } - }, [watchedMake, watchedYear, selectedMake, setValue]); + }, [watchedMake, watchedYear, setValue]); // Load trims when model changes useEffect(() => { // Skip during initialization if (isInitializing.current) return; - if (watchedModel && watchedYear && selectedMake && watchedModel !== selectedModel) { + if (watchedModel && watchedYear && watchedMake && watchedModel !== prevModel.current) { const loadTrims = async () => { setLoadingDropdowns(true); try { - const trimsData = await vehiclesApi.getTrims(watchedYear, selectedMake, watchedModel); + const trimsData = await vehiclesApi.getTrims(watchedYear, watchedMake, watchedModel); setTrims(trimsData); - setSelectedModel(watchedModel); + prevModel.current = watchedModel; - // Clear deeper selections (trims, transmissions, engines) - setSelectedTrim(''); + // Clear deeper selections (engines, transmissions) + prevTrim.current = ''; setTransmissions([]); setEngines([]); setValue('trimLevel', ''); @@ -300,35 +302,34 @@ export const VehicleForm: React.FC = ({ loadTrims(); } - }, [watchedModel, watchedYear, selectedMake, selectedModel, setValue]); + }, [watchedModel, watchedYear, watchedMake, setValue]); // Load engines and transmissions when trim changes useEffect(() => { // Skip during initialization if (isInitializing.current) return; - const trimName = watch('trimLevel'); - if (trimName && watchedYear && selectedMake && selectedModel) { + if (watchedTrim && watchedYear && watchedMake && watchedModel && watchedTrim !== prevTrim.current) { const loadEnginesAndTransmissions = async () => { setLoadingDropdowns(true); try { const [enginesData, transmissionsData] = await Promise.all([ vehiclesApi.getEngines( watchedYear, - selectedMake, - selectedModel, - trimName + watchedMake, + watchedModel, + watchedTrim ), vehiclesApi.getTransmissions( watchedYear, - selectedMake, - selectedModel, - trimName + watchedMake, + watchedModel, + watchedTrim ) ]); setEngines(enginesData); setTransmissions(transmissionsData); - setSelectedTrim(trimName); + prevTrim.current = watchedTrim; } catch (error) { console.error('Failed to load engines and transmissions:', error); setEngines([]); @@ -340,7 +341,7 @@ export const VehicleForm: React.FC = ({ loadEnginesAndTransmissions(); } - }, [watchedYear, selectedMake, selectedModel, watch('trimLevel')]); + }, [watchedYear, watchedMake, watchedModel, watchedTrim, setValue]); const handleImageUpload = async (file: File) => { if (isEditMode && vehicleId) { @@ -433,7 +434,7 @@ export const VehicleForm: React.FC = ({ { - const make = e.target.value; - setValue('make', make); + setValue('make', e.target.value); }} className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi" disabled={loadingDropdowns || !watchedYear || makes.length === 0} @@ -488,10 +488,9 @@ export const VehicleForm: React.FC = ({ { - const trim = e.target.value; - setValue('trimLevel', trim); + setValue('trimLevel', e.target.value); }} className="w-full px-3 py-2 border rounded-md min-h-[44px] bg-white text-gray-900 border-gray-300 focus:outline-none focus:ring-2 focus:ring-primary-500 dark:bg-scuro dark:text-avus dark:border-silverstone dark:focus:ring-abudhabi dark:focus:border-abudhabi" disabled={loadingDropdowns || !watchedModel || trims.length === 0} @@ -557,13 +555,13 @@ export const VehicleForm: React.FC = ({