fix: Dynamic drop down bugs in firefox
All checks were successful
Deploy to Staging / Build Images (push) Successful in 3m39s
Deploy to Staging / Deploy to Staging (push) Successful in 36s
Deploy to Staging / Verify Staging (push) Successful in 5s
Deploy to Staging / Notify Staging Ready (push) Successful in 5s
Deploy to Staging / Notify Staging Failure (push) Has been skipped
All checks were successful
Deploy to Staging / Build Images (push) Successful in 3m39s
Deploy to Staging / Deploy to Staging (push) Successful in 36s
Deploy to Staging / Verify Staging (push) Successful in 5s
Deploy to Staging / Notify Staging Ready (push) Successful in 5s
Deploy to Staging / Notify Staging Failure (push) Has been skipped
This commit is contained in:
@@ -22,11 +22,13 @@ You are a senior software engineer specializsing in NodeJS, Typescript, front en
|
|||||||
- Make no assumptions.
|
- Make no assumptions.
|
||||||
- Ask clarifying questions.
|
- Ask clarifying questions.
|
||||||
- Ultrathink
|
- Ultrathink
|
||||||
- You will be syncing the desktop and mobile versions of the homepage
|
- Troubleshoot UX problems when using Firefox
|
||||||
|
|
||||||
*** CONTEXT ***
|
*** 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.
|
- 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 ***
|
*** CHANGES TO IMPLEMENT ***
|
||||||
|
|||||||
@@ -78,13 +78,14 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
const [engines, setEngines] = useState<string[]>([]);
|
const [engines, setEngines] = useState<string[]>([]);
|
||||||
const [trims, setTrims] = useState<string[]>([]);
|
const [trims, setTrims] = useState<string[]>([]);
|
||||||
const [transmissions, setTransmissions] = useState<string[]>([]);
|
const [transmissions, setTransmissions] = useState<string[]>([]);
|
||||||
const [selectedYear, setSelectedYear] = useState<number | undefined>();
|
|
||||||
const [selectedMake, setSelectedMake] = useState<string>('');
|
|
||||||
const [selectedModel, setSelectedModel] = useState<string>('');
|
|
||||||
const [selectedTrim, setSelectedTrim] = useState<string>('');
|
|
||||||
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
const [loadingDropdowns, setLoadingDropdowns] = useState(false);
|
||||||
const hasInitialized = useRef(false);
|
const hasInitialized = useRef(false);
|
||||||
const isInitializing = useRef(false);
|
const isInitializing = useRef(false);
|
||||||
|
// Track previous values for cascade change detection (replaces useState)
|
||||||
|
const prevYear = useRef<number | undefined>(undefined);
|
||||||
|
const prevMake = useRef<string>('');
|
||||||
|
const prevModel = useRef<string>('');
|
||||||
|
const prevTrim = useRef<string>('');
|
||||||
const [currentImageUrl, setCurrentImageUrl] = useState<string | undefined>(initialData?.imageUrl);
|
const [currentImageUrl, setCurrentImageUrl] = useState<string | undefined>(initialData?.imageUrl);
|
||||||
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
||||||
|
|
||||||
@@ -106,6 +107,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
const watchedYear = watch('year');
|
const watchedYear = watch('year');
|
||||||
const watchedMake = watch('make');
|
const watchedMake = watch('make');
|
||||||
const watchedModel = watch('model');
|
const watchedModel = watch('model');
|
||||||
|
const watchedTrim = watch('trimLevel');
|
||||||
|
|
||||||
// Load years on component mount
|
// Load years on component mount
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -133,7 +135,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
|
|
||||||
// Step 1: Set year and load makes
|
// Step 1: Set year and load makes
|
||||||
setSelectedYear(initialData.year);
|
prevYear.current = initialData.year;
|
||||||
const makesData = await vehiclesApi.getMakes(initialData.year);
|
const makesData = await vehiclesApi.getMakes(initialData.year);
|
||||||
setMakes(makesData);
|
setMakes(makesData);
|
||||||
|
|
||||||
@@ -143,7 +145,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 2: Set make and load models
|
// Step 2: Set make and load models
|
||||||
setSelectedMake(initialData.make);
|
prevMake.current = initialData.make;
|
||||||
const modelsData = await vehiclesApi.getModels(initialData.year, initialData.make);
|
const modelsData = await vehiclesApi.getModels(initialData.year, initialData.make);
|
||||||
setModels(modelsData);
|
setModels(modelsData);
|
||||||
|
|
||||||
@@ -153,13 +155,13 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Step 3: Set model and load trims (transmissions loaded after trim selected)
|
// 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);
|
const trimsData = await vehiclesApi.getTrims(initialData.year, initialData.make, initialData.model);
|
||||||
setTrims(trimsData);
|
setTrims(trimsData);
|
||||||
|
|
||||||
if (initialData.trimLevel) {
|
if (initialData.trimLevel) {
|
||||||
// Step 4: Set trim and load engines + transmissions
|
// Step 4: Set trim and load engines + transmissions
|
||||||
setSelectedTrim(initialData.trimLevel);
|
prevTrim.current = initialData.trimLevel;
|
||||||
const [enginesData, transmissionsData] = await Promise.all([
|
const [enginesData, transmissionsData] = await Promise.all([
|
||||||
vehiclesApi.getEngines(
|
vehiclesApi.getEngines(
|
||||||
initialData.year,
|
initialData.year,
|
||||||
@@ -202,18 +204,18 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
// Skip during initialization
|
// Skip during initialization
|
||||||
if (isInitializing.current) return;
|
if (isInitializing.current) return;
|
||||||
|
|
||||||
if (watchedYear && watchedYear !== selectedYear) {
|
if (watchedYear && watchedYear !== prevYear.current) {
|
||||||
const loadMakes = async () => {
|
const loadMakes = async () => {
|
||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
const makesData = await vehiclesApi.getMakes(watchedYear);
|
const makesData = await vehiclesApi.getMakes(watchedYear);
|
||||||
setMakes(makesData);
|
setMakes(makesData);
|
||||||
setSelectedYear(watchedYear);
|
prevYear.current = watchedYear;
|
||||||
|
|
||||||
// Clear dependent selections
|
// Clear dependent selections
|
||||||
setSelectedMake('');
|
prevMake.current = '';
|
||||||
setSelectedModel('');
|
prevModel.current = '';
|
||||||
setSelectedTrim('');
|
prevTrim.current = '';
|
||||||
setModels([]);
|
setModels([]);
|
||||||
setTrims([]);
|
setTrims([]);
|
||||||
setEngines([]);
|
setEngines([]);
|
||||||
@@ -233,24 +235,24 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
|
|
||||||
loadMakes();
|
loadMakes();
|
||||||
}
|
}
|
||||||
}, [watchedYear, selectedYear, setValue]);
|
}, [watchedYear, setValue]);
|
||||||
|
|
||||||
// Load models when make changes
|
// Load models when make changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Skip during initialization
|
// Skip during initialization
|
||||||
if (isInitializing.current) return;
|
if (isInitializing.current) return;
|
||||||
|
|
||||||
if (watchedMake && watchedYear && watchedMake !== selectedMake) {
|
if (watchedMake && watchedYear && watchedMake !== prevMake.current) {
|
||||||
const loadModels = async () => {
|
const loadModels = async () => {
|
||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
const modelsData = await vehiclesApi.getModels(watchedYear, watchedMake);
|
const modelsData = await vehiclesApi.getModels(watchedYear, watchedMake);
|
||||||
setModels(modelsData);
|
setModels(modelsData);
|
||||||
setSelectedMake(watchedMake);
|
prevMake.current = watchedMake;
|
||||||
|
|
||||||
// Clear dependent selections
|
// Clear dependent selections
|
||||||
setSelectedModel('');
|
prevModel.current = '';
|
||||||
setSelectedTrim('');
|
prevTrim.current = '';
|
||||||
setTrims([]);
|
setTrims([]);
|
||||||
setEngines([]);
|
setEngines([]);
|
||||||
setTransmissions([]);
|
setTransmissions([]);
|
||||||
@@ -268,23 +270,23 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
|
|
||||||
loadModels();
|
loadModels();
|
||||||
}
|
}
|
||||||
}, [watchedMake, watchedYear, selectedMake, setValue]);
|
}, [watchedMake, watchedYear, setValue]);
|
||||||
|
|
||||||
// Load trims when model changes
|
// Load trims when model changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Skip during initialization
|
// Skip during initialization
|
||||||
if (isInitializing.current) return;
|
if (isInitializing.current) return;
|
||||||
|
|
||||||
if (watchedModel && watchedYear && selectedMake && watchedModel !== selectedModel) {
|
if (watchedModel && watchedYear && watchedMake && watchedModel !== prevModel.current) {
|
||||||
const loadTrims = async () => {
|
const loadTrims = async () => {
|
||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
const trimsData = await vehiclesApi.getTrims(watchedYear, selectedMake, watchedModel);
|
const trimsData = await vehiclesApi.getTrims(watchedYear, watchedMake, watchedModel);
|
||||||
setTrims(trimsData);
|
setTrims(trimsData);
|
||||||
setSelectedModel(watchedModel);
|
prevModel.current = watchedModel;
|
||||||
|
|
||||||
// Clear deeper selections (trims, transmissions, engines)
|
// Clear deeper selections (engines, transmissions)
|
||||||
setSelectedTrim('');
|
prevTrim.current = '';
|
||||||
setTransmissions([]);
|
setTransmissions([]);
|
||||||
setEngines([]);
|
setEngines([]);
|
||||||
setValue('trimLevel', '');
|
setValue('trimLevel', '');
|
||||||
@@ -300,35 +302,34 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
|
|
||||||
loadTrims();
|
loadTrims();
|
||||||
}
|
}
|
||||||
}, [watchedModel, watchedYear, selectedMake, selectedModel, setValue]);
|
}, [watchedModel, watchedYear, watchedMake, setValue]);
|
||||||
|
|
||||||
// Load engines and transmissions when trim changes
|
// Load engines and transmissions when trim changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Skip during initialization
|
// Skip during initialization
|
||||||
if (isInitializing.current) return;
|
if (isInitializing.current) return;
|
||||||
|
|
||||||
const trimName = watch('trimLevel');
|
if (watchedTrim && watchedYear && watchedMake && watchedModel && watchedTrim !== prevTrim.current) {
|
||||||
if (trimName && watchedYear && selectedMake && selectedModel) {
|
|
||||||
const loadEnginesAndTransmissions = async () => {
|
const loadEnginesAndTransmissions = async () => {
|
||||||
setLoadingDropdowns(true);
|
setLoadingDropdowns(true);
|
||||||
try {
|
try {
|
||||||
const [enginesData, transmissionsData] = await Promise.all([
|
const [enginesData, transmissionsData] = await Promise.all([
|
||||||
vehiclesApi.getEngines(
|
vehiclesApi.getEngines(
|
||||||
watchedYear,
|
watchedYear,
|
||||||
selectedMake,
|
watchedMake,
|
||||||
selectedModel,
|
watchedModel,
|
||||||
trimName
|
watchedTrim
|
||||||
),
|
),
|
||||||
vehiclesApi.getTransmissions(
|
vehiclesApi.getTransmissions(
|
||||||
watchedYear,
|
watchedYear,
|
||||||
selectedMake,
|
watchedMake,
|
||||||
selectedModel,
|
watchedModel,
|
||||||
trimName
|
watchedTrim
|
||||||
)
|
)
|
||||||
]);
|
]);
|
||||||
setEngines(enginesData);
|
setEngines(enginesData);
|
||||||
setTransmissions(transmissionsData);
|
setTransmissions(transmissionsData);
|
||||||
setSelectedTrim(trimName);
|
prevTrim.current = watchedTrim;
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to load engines and transmissions:', error);
|
console.error('Failed to load engines and transmissions:', error);
|
||||||
setEngines([]);
|
setEngines([]);
|
||||||
@@ -340,7 +341,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
|
|
||||||
loadEnginesAndTransmissions();
|
loadEnginesAndTransmissions();
|
||||||
}
|
}
|
||||||
}, [watchedYear, selectedMake, selectedModel, watch('trimLevel')]);
|
}, [watchedYear, watchedMake, watchedModel, watchedTrim, setValue]);
|
||||||
|
|
||||||
const handleImageUpload = async (file: File) => {
|
const handleImageUpload = async (file: File) => {
|
||||||
if (isEditMode && vehicleId) {
|
if (isEditMode && vehicleId) {
|
||||||
@@ -433,7 +434,7 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
{...register('year', { valueAsNumber: true })}
|
{...register('year', { valueAsNumber: true })}
|
||||||
value={selectedYear || ''}
|
value={watchedYear || ''}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const year = parseInt(e.target.value);
|
const year = parseInt(e.target.value);
|
||||||
setValue('year', year);
|
setValue('year', year);
|
||||||
@@ -456,10 +457,9 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
{...register('make')}
|
{...register('make')}
|
||||||
value={selectedMake}
|
value={watchedMake || ''}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const make = e.target.value;
|
setValue('make', e.target.value);
|
||||||
setValue('make', make);
|
|
||||||
}}
|
}}
|
||||||
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"
|
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}
|
disabled={loadingDropdowns || !watchedYear || makes.length === 0}
|
||||||
@@ -488,10 +488,9 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
{...register('model')}
|
{...register('model')}
|
||||||
value={selectedModel}
|
value={watchedModel || ''}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const model = e.target.value;
|
setValue('model', e.target.value);
|
||||||
setValue('model', model);
|
|
||||||
}}
|
}}
|
||||||
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"
|
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 || !watchedMake || models.length === 0}
|
disabled={loadingDropdowns || !watchedMake || models.length === 0}
|
||||||
@@ -523,10 +522,9 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
{...register('trimLevel')}
|
{...register('trimLevel')}
|
||||||
value={selectedTrim}
|
value={watchedTrim || ''}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
const trim = e.target.value;
|
setValue('trimLevel', e.target.value);
|
||||||
setValue('trimLevel', trim);
|
|
||||||
}}
|
}}
|
||||||
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"
|
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}
|
disabled={loadingDropdowns || !watchedModel || trims.length === 0}
|
||||||
@@ -557,13 +555,13 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
<select
|
<select
|
||||||
{...register('engine')}
|
{...register('engine')}
|
||||||
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"
|
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 || !selectedTrim || engines.length === 0}
|
disabled={loadingDropdowns || !watchedTrim || engines.length === 0}
|
||||||
style={{ fontSize: '16px' }}
|
style={{ fontSize: '16px' }}
|
||||||
>
|
>
|
||||||
<option value="">
|
<option value="">
|
||||||
{loadingDropdowns
|
{loadingDropdowns
|
||||||
? 'Loading...'
|
? 'Loading...'
|
||||||
: !selectedTrim
|
: !watchedTrim
|
||||||
? 'Select trim first'
|
? 'Select trim first'
|
||||||
: engines.length === 0
|
: engines.length === 0
|
||||||
? 'N/A (Electric)'
|
? 'N/A (Electric)'
|
||||||
@@ -585,13 +583,13 @@ export const VehicleForm: React.FC<VehicleFormProps> = ({
|
|||||||
<select
|
<select
|
||||||
{...register('transmission')}
|
{...register('transmission')}
|
||||||
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"
|
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 || !selectedTrim || transmissions.length === 0}
|
disabled={loadingDropdowns || !watchedTrim || transmissions.length === 0}
|
||||||
style={{ fontSize: '16px' }}
|
style={{ fontSize: '16px' }}
|
||||||
>
|
>
|
||||||
<option value="">
|
<option value="">
|
||||||
{loadingDropdowns
|
{loadingDropdowns
|
||||||
? 'Loading...'
|
? 'Loading...'
|
||||||
: !selectedTrim
|
: !watchedTrim
|
||||||
? 'Select trim first'
|
? 'Select trim first'
|
||||||
: transmissions.length === 0
|
: transmissions.length === 0
|
||||||
? 'No transmissions available'
|
? 'No transmissions available'
|
||||||
|
|||||||
Reference in New Issue
Block a user