Skip to content

Commit fd6c15f

Browse files
author
Brenden Walker
committed
cleanup recipe UI and filter on item names excluding category name when filtering
1 parent 937c982 commit fd6c15f

5 files changed

Lines changed: 90 additions & 23 deletions

File tree

kitchenhub/frontend/src/pages/IngredientsCatalogPage.jsx

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,7 @@ function IngredientsCatalogPage() {
115115
const filteredIngredients = searchText
116116
? ingredients.filter((ing) => {
117117
const name = (ing.name || '').toLowerCase();
118-
const details = (ing.details || '').toLowerCase();
119-
const dept = (ing.department_name || '').toLowerCase();
120-
const shopping = (ing.shopping_measure || '').toLowerCase();
121-
return name.includes(searchText) || details.includes(searchText) || dept.includes(searchText) || shopping.includes(searchText);
118+
return name.includes(searchText);
122119
})
123120
: ingredients;
124121

@@ -255,7 +252,7 @@ function IngredientsCatalogPage() {
255252
<div className="filter-section">
256253
<input
257254
type="text"
258-
placeholder="Filter by name, details, department, or shopping measure..."
255+
placeholder="Filter by ingredient name..."
259256
value={ingredientFilter}
260257
onChange={(e) => setIngredientFilter(e.target.value)}
261258
className="filter-input"

kitchenhub/frontend/src/pages/RecipeDetailPage.jsx

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import {
1212
updateRecipeIngredient,
1313
removeRecipeIngredient,
1414
} from '../api/api';
15+
import { formatRecipeQuantity } from '../utils/recipeQuantity';
1516
import './RecipeDetailPage.css';
1617

1718
function RecipeDetailPage() {
@@ -199,7 +200,7 @@ function RecipeDetailPage() {
199200

200201
const formatIngredientLine = (row) => {
201202
const name = row.ingredient_details ? `${row.ingredient_name} (${row.ingredient_details})` : row.ingredient_name;
202-
const qty = row.qty != null ? String(row.qty) : '';
203+
const qty = row.qty != null ? formatRecipeQuantity(row.qty) : '';
203204
const measure = row.measurement_name || '';
204205
const part = [qty, measure].filter(Boolean).join(' ');
205206
return part ? `${part} ${name}` : name;
@@ -475,13 +476,6 @@ function RecipeDetailPage() {
475476
</form>
476477
) : (
477478
<>
478-
{recipe.instructions && (
479-
<div className="recipe-instructions">
480-
<h2>Instructions</h2>
481-
<pre className="recipe-instructions-text">{recipe.instructions}</pre>
482-
</div>
483-
)}
484-
485479
<div className="recipe-ingredients-section">
486480
<h2>Ingredients</h2>
487481
{recipe.ingredients && recipe.ingredients.length > 0 ? (
@@ -505,6 +499,13 @@ function RecipeDetailPage() {
505499
<p className="recipe-no-ingredients">No ingredients yet.</p>
506500
)}
507501
</div>
502+
503+
{recipe.instructions && (
504+
<div className="recipe-instructions">
505+
<h2>Instructions</h2>
506+
<pre className="recipe-instructions-text">{recipe.instructions}</pre>
507+
</div>
508+
)}
508509
</>
509510
)}
510511
</>

kitchenhub/frontend/src/pages/RecipesPage.jsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,7 @@ function RecipesPage() {
4848
const filteredRecipes = searchText
4949
? recipes.filter((r) => {
5050
const name = (r.name || '').toLowerCase();
51-
const cats = (r.category_names || '').toLowerCase();
52-
return name.includes(searchText) || cats.includes(searchText);
51+
return name.includes(searchText);
5352
})
5453
: recipes;
5554

@@ -81,7 +80,7 @@ function RecipesPage() {
8180
</select>
8281
<input
8382
type="text"
84-
placeholder="Filter by name or category..."
83+
placeholder="Filter by recipe name..."
8584
value={recipeFilter}
8685
onChange={(e) => setRecipeFilter(e.target.value)}
8786
className="recipes-filter-input"

kitchenhub/frontend/src/pages/ShoppingListPage.jsx

Lines changed: 4 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -290,13 +290,10 @@ function ShoppingListPage() {
290290
}
291291
};
292292

293-
// Filter items based on search text
294-
const filteredItems = items.filter(item => {
293+
// Filter items by name only (department/category is for grouping, not search)
294+
const filteredItems = items.filter((item) => {
295295
const searchText = itemFilter.toLowerCase();
296-
return (
297-
item.name.toLowerCase().includes(searchText) ||
298-
(item.department_name && item.department_name.toLowerCase().includes(searchText))
299-
);
296+
return item.name.toLowerCase().includes(searchText);
300297
});
301298

302299
// Group items by department for better organization
@@ -488,7 +485,7 @@ function ShoppingListPage() {
488485
<div className="filter-section">
489486
<input
490487
type="text"
491-
placeholder="Filter items by name or department..."
488+
placeholder="Filter items by name..."
492489
value={itemFilter}
493490
onChange={(e) => setItemFilter(e.target.value)}
494491
className="filter-input"
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Format a numeric quantity for recipe display: mixed fractions using
3+
* common denominators (halves, thirds, quarters, eighths, sixteenths).
4+
*/
5+
function gcd(a, b) {
6+
a = Math.abs(a);
7+
b = Math.abs(b);
8+
while (b) {
9+
const t = b;
10+
b = a % b;
11+
a = t;
12+
}
13+
return a || 1;
14+
}
15+
16+
export function formatRecipeQuantity(qty) {
17+
if (qty == null || qty === '') return '';
18+
const n = Number(qty);
19+
if (Number.isNaN(n)) return String(qty);
20+
21+
const eps = 1e-7;
22+
const matchTol = 0.021;
23+
24+
if (Math.abs(n) < eps) return '0';
25+
26+
const negative = n < 0;
27+
const prefix = negative ? '-' : '';
28+
const abs = Math.abs(n);
29+
30+
if (Math.abs(abs - Math.round(abs)) < matchTol) {
31+
return prefix + String(Math.round(abs));
32+
}
33+
34+
let whole = Math.floor(abs + eps);
35+
let frac = abs - whole;
36+
37+
if (frac > 1 - matchTol) {
38+
whole += 1;
39+
frac = 0;
40+
}
41+
42+
if (frac < matchTol) {
43+
return prefix + String(whole);
44+
}
45+
46+
const denoms = [2, 3, 4, 8, 16];
47+
let bestNum = 1;
48+
let bestDen = 8;
49+
let bestErr = Infinity;
50+
for (const den of denoms) {
51+
for (let num = 1; num < den; num++) {
52+
const err = Math.abs(num / den - frac);
53+
if (err < bestErr) {
54+
bestErr = err;
55+
bestNum = num;
56+
bestDen = den;
57+
}
58+
}
59+
}
60+
61+
if (bestErr > 0.045) {
62+
const s = abs.toFixed(4).replace(/\.?0+$/, '');
63+
return prefix + s;
64+
}
65+
66+
const g = gcd(bestNum, bestDen);
67+
bestNum /= g;
68+
bestDen /= g;
69+
70+
const fracPart = `${bestNum}/${bestDen}`;
71+
if (whole === 0) return prefix + fracPart;
72+
return `${prefix}${whole} ${fracPart}`;
73+
}

0 commit comments

Comments
 (0)