From 143a54ccd24caddcca08b7c1fadaf598edabfca3 Mon Sep 17 00:00:00 2001 From: Gustavo Hidalgo Date: Sat, 14 Feb 2026 15:06:39 -0500 Subject: [PATCH] Fix hanging UI on internal ingredient edit --- CHANGELOG.md | 1 + .../IngredientInternalUpdateRow.tsx | 198 ++++++------- .../Ingredients/IngredientNormalizerRow.tsx | 108 +++---- .../Ingredients/IngredientUnifiedRow.tsx | 276 +++++++++--------- 4 files changed, 295 insertions(+), 288 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a270a2b..1c0257f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Fixed - Fix crash when switching ingredient units caused by reading the event target inside a deferred React state updater +- Fix UI hang on ingredient views by deferring accordion body rendering until expanded via `mountOnEnter` ### Changed diff --git a/src/CookTime/client-app/src/components/Ingredients/IngredientInternalUpdateRow.tsx b/src/CookTime/client-app/src/components/Ingredients/IngredientInternalUpdateRow.tsx index 2fb4e63..dcfd0d2 100644 --- a/src/CookTime/client-app/src/components/Ingredients/IngredientInternalUpdateRow.tsx +++ b/src/CookTime/client-app/src/components/Ingredients/IngredientInternalUpdateRow.tsx @@ -56,110 +56,112 @@ export default function IngredientInternalUpdateRow(props: IngredientInternalUpd - -
- - Ingredient ID - - + +
+ + + Ingredient ID + + - - Ingredient Names (semi-colon separated) - setUpdate({ ...update, ingredientNames: e.target.value })} - value={update.ingredientNames} - /> - + + Ingredient Names (semi-colon separated) + setUpdate({ ...update, ingredientNames: e.target.value })} + value={update.ingredientNames} + /> + - - Nutrition Description - - + + Nutrition Description + + - - - - NDB Number - { - if (ndbNumber !== null) { - setUpdate({ ...update, ndbNumber }); - } - }} - fieldType="ndb" - /> - - - - - GTIN/UPC Number - { - if (gtinUpc !== null) { - setUpdate({ ...update, gtinUpc }); - } - }} - fieldType="gtin" - /> - - - + + + + NDB Number + { + if (ndbNumber !== null) { + setUpdate({ ...update, ndbNumber }); + } + }} + fieldType="ndb" + /> + + + + + GTIN/UPC Number + { + if (gtinUpc !== null) { + setUpdate({ ...update, gtinUpc }); + } + }} + fieldType="gtin" + /> + + + - - - - Count RegEx - setUpdate({ ...update, countRegex: e.target.value })} - value={update.countRegex} - /> - - - - - Unit Mass (kg) - { - let unitMass = Number.parseFloat(e.target.value); - if (e.target.value === "0.") { - setUpdate({ ...update, expectedUnitMass: "0." }); - } else { - if (isNaN(unitMass)) { - unitMass = 0.1; + + + + Count RegEx + setUpdate({ ...update, countRegex: e.target.value })} + value={update.countRegex} + /> + + + + + Unit Mass (kg) + { + let unitMass = Number.parseFloat(e.target.value); + if (e.target.value === "0.") { + setUpdate({ ...update, expectedUnitMass: "0." }); + } else { + if (isNaN(unitMass)) { + unitMass = 0.1; + } + setUpdate({ ...update, expectedUnitMass: unitMass.toString() }) } - setUpdate({ ...update, expectedUnitMass: unitMass.toString() }) - } - }} - value={update.expectedUnitMass} - /> - - - + }} + value={update.expectedUnitMass} + /> + + + -
- {isSubmitting ? ( - - ) : ( - - )} -
- - +
+ {isSubmitting ? ( + + ) : ( + + )} +
+ +
+
); } \ No newline at end of file diff --git a/src/CookTime/client-app/src/components/Ingredients/IngredientNormalizerRow.tsx b/src/CookTime/client-app/src/components/Ingredients/IngredientNormalizerRow.tsx index adc8c0a..86efe0f 100644 --- a/src/CookTime/client-app/src/components/Ingredients/IngredientNormalizerRow.tsx +++ b/src/CookTime/client-app/src/components/Ingredients/IngredientNormalizerRow.tsx @@ -38,7 +38,7 @@ export default function IngredientNormalizerRow(props: IngredientNormalizerRowPr onError(`Failed to replace ingredient: ${errorText}`); return; } - + location.reload(); } catch (err) { onError(`Network error: ${err instanceof Error ? err.message : 'Unknown error'}`); @@ -55,7 +55,7 @@ export default function IngredientNormalizerRow(props: IngredientNormalizerRowPr onError(`Failed to delete ingredient: ${errorText}`); return; } - + location.reload(); } catch (err) { onError(`Network error: ${err instanceof Error ? err.message : 'Unknown error'}`); @@ -79,63 +79,65 @@ export default function IngredientNormalizerRow(props: IngredientNormalizerRowPr - -
- - Ingredient ID - - - Copy this ID to merge this ingredient into another - - + +
+ + + Ingredient ID + + + Copy this ID to merge this ingredient into another + + - - - - Recipes Using - - - - - - Has Nutrition Data - - - - + + + + Recipes Using + + + + + + Has Nutrition Data + + + + - - Replace With (search by ingredient name) - setReplacement({ ...replacement, keptId: ingredientId })} - excludeId={replacedId} - /> - - This will merge all recipe references to the target ingredient - - + + Replace With (search by ingredient name) + setReplacement({ ...replacement, keptId: ingredientId })} + excludeId={replacedId} + /> + + This will merge all recipe references to the target ingredient + + -
- - {usage === 0 && ( +
- )} -
- - + {usage === 0 && ( + + )} +
+ +
+
); } \ No newline at end of file diff --git a/src/CookTime/client-app/src/components/Ingredients/IngredientUnifiedRow.tsx b/src/CookTime/client-app/src/components/Ingredients/IngredientUnifiedRow.tsx index d32d616..95bf2f2 100644 --- a/src/CookTime/client-app/src/components/Ingredients/IngredientUnifiedRow.tsx +++ b/src/CookTime/client-app/src/components/Ingredients/IngredientUnifiedRow.tsx @@ -132,156 +132,158 @@ export default function IngredientUnifiedRow(props: IngredientUnifiedRowProps) { - -
- - - - Ingredient ID - - - - - - Recipe Usage - - - - + +
+ + + + + Ingredient ID + + + + + + Recipe Usage + + + + - - Ingredient Names (semi-colon separated) - setUpdate({ ...update, ingredientNames: e.target.value })} - value={update.ingredientNames} - /> - + + Ingredient Names (semi-colon separated) + setUpdate({ ...update, ingredientNames: e.target.value })} + value={update.ingredientNames} + /> + - - Nutrition Description - - + + Nutrition Description + + - - - - NDB Number (SR Legacy) - { - if (ndbNumber !== null) { - setUpdate({ ...update, ndbNumber }); - } - }} - fieldType="ndb" - /> - - - - - GTIN/UPC Number (Branded) - { - if (gtinUpc !== null) { - setUpdate({ ...update, gtinUpc }); - } - }} - fieldType="gtin" - /> - - - + + + + NDB Number (SR Legacy) + { + if (ndbNumber !== null) { + setUpdate({ ...update, ndbNumber }); + } + }} + fieldType="ndb" + /> + + + + + GTIN/UPC Number (Branded) + { + if (gtinUpc !== null) { + setUpdate({ ...update, gtinUpc }); + } + }} + fieldType="gtin" + /> + + + - - - - Count RegEx - setUpdate({ ...update, countRegex: e.target.value })} - value={update.countRegex} - /> - - - - - Unit Mass (kg) - { - let unitMass = Number.parseFloat(e.target.value); - if (e.target.value === "0.") { - setUpdate({ ...update, expectedUnitMass: "0." }); - } else { - if (isNaN(unitMass)) { - unitMass = 0.1; + + + + Count RegEx + setUpdate({ ...update, countRegex: e.target.value })} + value={update.countRegex} + /> + + + + + Unit Mass (kg) + { + let unitMass = Number.parseFloat(e.target.value); + if (e.target.value === "0.") { + setUpdate({ ...update, expectedUnitMass: "0." }); + } else { + if (isNaN(unitMass)) { + unitMass = 0.1; + } + setUpdate({ ...update, expectedUnitMass: unitMass.toString() }); } - setUpdate({ ...update, expectedUnitMass: unitMass.toString() }); - } - }} - value={update.expectedUnitMass} - /> - - - + }} + value={update.expectedUnitMass} + /> + + + -
- {isSubmitting ? ( - - ) : ( - - )} -
+
+ {isSubmitting ? ( + + ) : ( + + )} +
-
+
-
Merge or Delete
+
Merge or Delete
- - Merge Into Another Ingredient - setMergeTargetId(ingredientId)} - excludeId={ingredientId} - /> - - This will move all recipe references to the target ingredient and delete this one - - + + Merge Into Another Ingredient + setMergeTargetId(ingredientId)} + excludeId={ingredientId} + /> + + This will move all recipe references to the target ingredient and delete this one + + -
- - {usage === 0 && ( +
- )} -
- - + {usage === 0 && ( + + )} +
+ +
+
); }