Skip to content

Commit 4751d50

Browse files
committed
feat: modernize barcode scanning UI/UX with tabs, nutritional cards, and allergen detection
- Add tab-based navigation (AI Summary, Ingredients, Additives) with translations in 5 languages - Display compact nutritional info cards (calories, salt, sugar, protein) in horizontal row for mobile - Implement visual allergen flagging with red gradient borders and warning icons - Add multi-language allergen keyword detection (English, French, Spanish, Italian, Arabic) - Enhance empty state with phone icon, clear instructions, and numbered step badges - Add preference check with warning alert before scanning - Improve loading states with split feedback (scan confirmation + spinner) - Add prominent allergen warning banner with translated messages - Replace side-by-side layout with mobile-friendly tabs - Add search icon to scan button
1 parent aa17a40 commit 4751d50

File tree

3 files changed

+337
-109
lines changed

3 files changed

+337
-109
lines changed

resources/ui/src/assets/i18n/all.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,9 @@ const customTranslations: Record<string, Record<string, string>> = {
3838
ingredients_desc_additive:
3939
"Click on each additive to view AI-generated detailed descriptions",
4040
ingredients_no_additive: "The product does not have additives",
41+
tab_ai_summary: "AI Summary",
42+
tab_ingredients: "Ingredients",
43+
tab_additives: "Additives",
4144
allergen_warning_title: "Allergen Warning",
4245
allergen_warning_message: "This product contains allergens you're sensitive to:",
4346
summary_title: "Summary of ingredients generated by AI",
@@ -130,6 +133,9 @@ const customTranslations: Record<string, Record<string, string>> = {
130133
ingredients_desc_additive:
131134
"Cliquez sur chaque additif pour voir les descriptions détaillées générées par l'IA",
132135
ingredients_no_additive: "Le produit ne contient pas d'additifs",
136+
tab_ai_summary: "Résumé IA",
137+
tab_ingredients: "Ingrédients",
138+
tab_additives: "Additifs",
133139
allergen_warning_title: "Avertissement Allergène",
134140
allergen_warning_message: "Ce produit contient des allergènes auxquels vous êtes sensible :",
135141
summary_title: "Résumé des ingrédients généré par IA",
@@ -222,6 +228,9 @@ const customTranslations: Record<string, Record<string, string>> = {
222228
ingredients_desc_ingredient:
223229
"Fai clic su ogni ingrediente per visualizzare le descrizioni dettagliate generate dall’AI",
224230
ingredients_no_additive: "Il prodotto non contiene additivi",
231+
tab_ai_summary: "Riepilogo IA",
232+
tab_ingredients: "Ingredienti",
233+
tab_additives: "Additivi",
225234
ingredients_desc_additive:
226235
"Fai clic su ogni additivo per visualizzare le descrizioni dettagliate generate dall’AI",
227236
allergen_warning_title: "Avviso Allergeni",
@@ -317,6 +326,9 @@ const customTranslations: Record<string, Record<string, string>> = {
317326
ingredients_desc_ingredient:
318327
"Haz clic en cada ingrediente para ver descripciones detalladas generadas por IA",
319328
ingredients_no_additive: "El producto no contiene aditivos",
329+
tab_ai_summary: "Resumen IA",
330+
tab_ingredients: "Ingredientes",
331+
tab_additives: "Aditivos",
320332
ingredients_desc_additive:
321333
"Haz clic en cada aditivo para ver descripciones detalladas generadas por IA",
322334
allergen_warning_title: "Advertencia de Alérgenos",
@@ -406,6 +418,9 @@ arabic:{
406418
"ingredients_desc_ingredient": "انقر على كل مكون لعرض الوصف التفصيلي الناتج عن الذكاء الاصطناعي",
407419
"ingredients_desc_additive": "انقر على كل إضافة لعرض الوصف التفصيلي الناتج عن الذكاء الاصطناعي",
408420
"ingredients_no_additive": "المنتج لا يحتوي على إضافات",
421+
"tab_ai_summary": "ملخص الذكاء الاصطناعي",
422+
"tab_ingredients": "المكونات",
423+
"tab_additives": "الإضافات",
409424
"allergen_warning_title": "تحذير من مسببات الحساسية",
410425
"allergen_warning_message": "يحتوي هذا المنتج على مسببات حساسية تؤثر عليك:",
411426
"summary_title": "ملخص المكونات الناتجة عن الذكاء الاصطناعي",

resources/ui/src/pages/components/barcode.tsx

Lines changed: 111 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import Button from "@cloudscape-design/components/button";
44
import Ingredients from "./barcode_ingredients";
55
import Badge from "@cloudscape-design/components/badge";
66
import Link from "@cloudscape-design/components/link";
7+
import Alert from "@cloudscape-design/components/alert";
78
import {
89
Box,
910
Container,
@@ -43,10 +44,17 @@ const Barcode: React.FC = () => {
4344
const [productCode, setProductCode] = useState("");
4445
const [showScanner, setShowScanner] = useState(false);
4546
const [tempProductCode, setTempProductCode] = useState("");
47+
const [hasPreferences, setHasPreferences] = useState(false);
4648
const currentTranslations = customTranslations[language]; // Get translations for the current language or fallback to English
4749

4850
let html5QrcodeScanner: any;
4951

52+
// Check if user has set preferences
53+
useEffect(() => {
54+
const stored = localStorage.getItem("userPreferences");
55+
setHasPreferences(!!stored);
56+
}, []);
57+
5058
function onScanFailure(error: unknown) {
5159
console.warn(`Code scan error = ${error}`);
5260
}
@@ -122,7 +130,11 @@ const Barcode: React.FC = () => {
122130
>
123131
<div style={{ textAlign: "center" }}>
124132
{!showScanner && (
125-
<Button variant="primary" onClick={handleButtonClick}>
133+
<Button
134+
variant="primary"
135+
onClick={handleButtonClick}
136+
iconName="search"
137+
>
126138
{currentTranslations["scan_button_label"]}
127139
</Button>
128140
)}
@@ -153,27 +165,106 @@ const Barcode: React.FC = () => {
153165
</div>
154166

155167
{!showScanner && (
156-
<Box>
157-
<div style={{ textAlign: "left" }}>
158-
<h4>{currentTranslations["scan_main_title"]}</h4>
159-
160-
<SpaceBetween direction="vertical" size="m">
161-
<div>
162-
<p>
163-
<Badge color="green">1</Badge>{" "}
164-
{currentTranslations["scan_label_1"]}{" "}
165-
<Link href="/preference">
166-
{currentTranslations["scan_label_2"]}
167-
</Link>
168-
</p>
169-
<p>
170-
<Badge color="green">2</Badge>{" "}
171-
{currentTranslations["scan_label_3"]}
172-
</p>
168+
<div style={{
169+
background: "white",
170+
borderRadius: "8px",
171+
padding: "20px",
172+
border: "1px solid #e5e7eb",
173+
boxShadow: "0 1px 3px rgba(0,0,0,0.1)"
174+
}}>
175+
{!hasPreferences && (
176+
<Alert
177+
type="warning"
178+
header="Set your preferences first"
179+
>
180+
To get personalized nutritional information, please{" "}
181+
<Link href="/preference">set your preferences</Link> before scanning.
182+
</Alert>
183+
)}
184+
185+
<div style={{
186+
textAlign: "center",
187+
margin: hasPreferences ? "0 0 24px 0" : "16px 0 24px 0"
188+
}}>
189+
<div style={{
190+
fontSize: "48px",
191+
marginBottom: "8px",
192+
opacity: 0.8
193+
}}>
194+
📱
195+
</div>
196+
<h3 style={{
197+
margin: "0 0 8px 0",
198+
fontSize: "18px",
199+
fontWeight: "600",
200+
color: "#1f2937"
201+
}}>
202+
{currentTranslations["scan_main_title"]}
203+
</h3>
204+
<p style={{
205+
margin: 0,
206+
fontSize: "14px",
207+
color: "#6b7280"
208+
}}>
209+
Point your camera at a product barcode
210+
</p>
211+
</div>
212+
213+
<div style={{ display: "flex", flexDirection: "column", gap: "12px" }}>
214+
<div style={{
215+
display: "flex",
216+
alignItems: "center",
217+
gap: "12px"
218+
}}>
219+
<div style={{
220+
background: "#3b82f6",
221+
color: "white",
222+
width: "24px",
223+
height: "24px",
224+
borderRadius: "50%",
225+
display: "flex",
226+
alignItems: "center",
227+
justifyContent: "center",
228+
fontWeight: "600",
229+
fontSize: "14px",
230+
flexShrink: 0
231+
}}>
232+
1
233+
</div>
234+
<span style={{ fontSize: "15px", lineHeight: "1.5", color: "#374151" }}>
235+
{currentTranslations["scan_label_1"]}{" "}
236+
<Link href="/preference">
237+
{currentTranslations["scan_label_2"]}
238+
</Link>
239+
</span>
240+
</div>
241+
242+
<div style={{
243+
display: "flex",
244+
alignItems: "center",
245+
gap: "12px"
246+
}}>
247+
<div style={{
248+
background: "#3b82f6",
249+
color: "white",
250+
width: "24px",
251+
height: "24px",
252+
borderRadius: "50%",
253+
display: "flex",
254+
alignItems: "center",
255+
justifyContent: "center",
256+
fontWeight: "600",
257+
fontSize: "14px",
258+
flexShrink: 0
259+
}}>
260+
2
173261
</div>
174-
</SpaceBetween>
262+
<span style={{ fontSize: "15px", lineHeight: "1.5", color: "#374151" }}>
263+
{currentTranslations["scan_label_3"]}
264+
</span>
265+
</div>
175266
</div>
176-
</Box>
267+
</div>
177268
)}
178269
<div id="reader"></div>
179270

0 commit comments

Comments
 (0)