Repository files navigation

Ask-OFF WebApp

Intelligent Food Discovery & Conversational UI for Open Food Facts Canada

A high-performance, accessible web application for natural language food search, structured nutritional comparison, and grounded food inquiry across 124,145 Canadian Open Food Facts products.


ReactTypeScriptViteTailwindCSSTanStack QueryVitestOpen Food Facts


Overview

Ask-OFF WebApp is the official open-source frontend application for the Ask-OFF Canada ecosystem. It provides an intuitive, high-speed, and responsive interface designed to explore, analyze, and compare 124,145 Canadian grocery and packaged food products.

While traditional food catalogs present rigid, unstructured text fields, Ask-OFF delivers an interactive web experience powered by deterministic natural language search, official French/English bilingual product records, and evidence-grounded conversational inquiry.

Primary Objectives

  • Conversational & Keyword Food Discovery: Instant natural language exploration coupled with debounced, race-condition-free autocomplete and query match highlighting.
  • Deterministic Nutritional Transparency: Render official vector Nutri-Score gauges (A to E), NOVA industrial food processing groups (1 to 4), Eco-Scores, allergens, and per-100g nutritional facts tables.
  • Side-by-Side Nutritional Comparison: Multi-product matrix evaluating 2 to 4 food items simultaneously with macro-nutrient deltas and persistent browser storage.
  • Ask-OFF Assistant: Grounded conversational assistant (available via full page or floating action button) that queries the backend retrieval engine and provides verifiable citations to official Open Food Facts records.
  • Zero-Latency Route Navigation: Route-level code splitting using React.lazy and Suspense to deliver minimal initial JavaScript payloads and lightning-fast transitions.
  • Universal Accessibility (a11y): Built from the ground up with keyboard navigation, visible focus indicators, screen-reader semantics, and full prefers-reduced-motion accommodation.

System Architecture

Ask-OFF WebApp operates as a decoupled single-page application (SPA) communicating over HTTPS REST APIs with the FastAPI retrieval backend.

flowchart TD
subgraph Browser_Client [Client Browser — AskOFF WebApp]
A[User Interaction] --> B[React Router 7 Declarative Routes]
B --> C[Pages & Lazy-Loaded Chunks]
C --> D[Shared Reusable UI Components]
D --> E[TanStack Query Cache]
D --> F[Local State: CompareContext & ListsContext]
end
subgraph API_Layer [Frontend API Boundary]
E --> G[Typed REST Client: src/api/client.ts]
G --> H[Vite Development Proxy /api or Production HTTPS]
end
subgraph Backend_Infrastructure [Ask-OFF Backend — offCanada]
H --> I[FastAPI REST Gateway]
I --> J[Query Understanding & Intent Pipeline]
J --> K[(OpenSearch 2.x BM25 Cluster<br/>124,145 Canadian Records)]
end
Loading

Technology Stack

LayerTechnologiesPurpose
Frontend CoreReact 19.2+, TypeScript 6.0+, Vite 8.1+Ultra-fast modern web application with strict type safety and sub-second HMR
State & Data Fetching@tanstack/react-query 5.101+, Context APIServer-state caching, stale-while-revalidate, request deduplication, and local storage
Routing & Code SplittingReact Router 7.18+, React.lazy, SuspenseDeclarative route matching with discrete asynchronous chunks per route
Styling & Design SystemTailwindCSS 3.4+, PostCSS 8, AutoprefixerUtility-first responsive styling with custom typography, focus styling, and scrollbars
Icons & MediaLucide React 1.24+, SVG Vector GraphicsClean, accessible vector icons and dynamic Nutri-Score/NOVA badges
Testing SuiteVitest 4.1+, @testing-library/react, JSDOMComprehensive unit and component behavior test suites (24/24 tests passing)
Static Code AnalysisOxlint, TypeScript Compiler (tsc -b)Sub-second linter execution and zero-error strict type verification

Application Pages & Key Features

RoutePage ComponentKey Functionality
/ or /discoverLandingPageHero search, dynamic typewriter placeholders, brand chips, category highlights, and catalog showcase
/searchSearchPageSearch results grid, 9 dietary & health filter toggles, sort dropdown, responsive drawer, and pagination
/product/:idProductDetailsPageFull product specifications, 4-tier CDN images, nutrition facts per 100g, ingredients, allergens, and labels
/compareComparePageSide-by-side comparison table for 2–4 products with nutrient difference calculations and quick product picker
/offbotOffBotPageDedicated conversational food assistant with verifiable ODbL citations, grounded evidence cards, and follow-ups
/listsListsPageSaved grocery lists, favorites, and dietary bookmarks with print and JSON export capabilities
/recipesRecipesPageCurated recipe directory with ingredients linked directly to live search queries and barcode tokens
/extensionsExtensionsPageEcosystem showcase for community plugins, allergy advisors, barcode scanners, and grocery integrations
/statusDashboardPageLive backend service health check, OpenSearch connection status, record count, and autocomplete testing sandbox
/aboutAboutPageProject mission, architecture pillars, Open Food Facts partnership, and Open Database License (ODbL) attribution

Reusable Component Architecture

The frontend is structured around reusable, accessible presentation components:

src/components/
├── ErrorBoundary.tsx # Catches unhandled render errors with user-friendly recovery UI
├── OffBotChat.tsx # Core conversational chat engine, message list & ODbL citations
├── OffBotWidget.tsx # Spherical Floating Action Button (FAB) launcher and popup chat modal
├── ProductCard.tsx # Food product card with image, brand, Nutri-Score & macro stats
├── ProductImage.tsx # Resilient image loader with 4-tier Open Food Facts CDN normalization
├── ProductImagePlaceholder.tsx # Category-aware SVG fallback placeholder for items without images
├── NutritionTable.tsx # Structured nutrition table per 100g with European & Canadian standards
├── NutriScoreLogo.tsx # Official vector SVG Nutri-Score spectrum graphic (A to E)
├── NutriScoreBadge.tsx # Compact Nutri-Score pills, NOVA Group cards (1 to 4) & Eco-Score badges
├── SearchBar.tsx # Search input with debounced autocomplete, typewriter & AbortSignal
├── FilterSidebar.tsx # Desktop sidebar and mobile slide-over drawer for dietary filters
├── Pagination.tsx # Accessible page navigator with previous/next controls
├── LoadingSkeleton.tsx # Animated skeleton loaders for cards, detail sheets, and grids
├── EmptyState.tsx # Friendly empty states with recommended actions
├── ErrorState.tsx # Connection error displays with retry callbacks
└── ScrollToTop.tsx # Window scroll reset upon route navigation

Project Structure

AskOFF-WebApp/
├── public/ # Static public assets
│ ├── favicon.svg # Web application favicon
│ ├── icons.svg # SVG sprite sheet
│ └── logo.png # Project logo asset
├── docs/
│ └── design-references/ # Design artifacts, reference screenshots & visual mockups
│ ├── A.jpg
│ ├── B.jpg
│ ├── C.jpg
│ ├── image (1).png
│ ├── image.png
│ ├── Nutri-score-A.webp
│ └── Screenshot 2026-08-26 180122.png
├── src/
│ ├── api/
│ │ ├── client.ts # Typed REST API client, timeout handling & CDN normalization
│ │ └── assistantService.ts # Conversational query synthesis & citation models
│ ├── assets/ # Optimized vector icons and project logo
│ │ ├── app_qr.svg
│ │ ├── hero.png
│ │ ├── logo.png
│ │ └── phone_app.png
│ ├── components/ # 16 reusable UI and utility components
│ ├── constants/
│ │ └── queries.ts # Default sample queries and animated typewriter phrases
│ ├── context/
│ │ ├── AssistantContext.tsx # Chat state, conversation history, and active product focus
│ │ ├── CompareContext.tsx # Multi-product comparison state (stored in localStorage)
│ │ └── ListsContext.tsx # Favorites, shopping list & saved items (stored in localStorage)
│ ├── pages/ # 10 route page components
│ ├── tests/ # Vitest unit and React Testing Library component tests
│ │ ├── assistantService.test.ts
│ │ ├── badges.test.ts
│ │ ├── client.test.ts
│ │ ├── components.test.tsx
│ │ └── productCard.test.ts
│ ├── App.tsx # Main router, ErrorBoundary, QueryClient, and Providers
│ ├── index.css # Tailwind directives, focus-visible styles, and custom scrollbars
│ └── main.tsx # Application entry point (React StrictMode)
├── .env.example # Template for frontend environment variables
├── .gitignore # Git exclusion rules
├── .oxlintrc.json # Oxlint linter configuration
├── package.json # Project manifest, scripts, and dependencies
├── package-lock.json # Locked dependency tree
├── postcss.config.js # PostCSS plugin configuration
├── tailwind.config.js # TailwindCSS theme and font configurations
├── tsconfig.json # TypeScript project configuration root
├── tsconfig.app.json # TypeScript browser configuration
├── tsconfig.node.json # TypeScript Node/Vite configuration
├── vite.config.ts # Vite configuration with proxy and Vitest settings
├── CONTRIBUTORS.md # Contributor guidelines and workflow
└── README.md # Project documentation

Quick Start

Prerequisites

  • Node.js: v18.0.0 or higher (v20 LTS recommended)
  • npm: v9.0.0 or higher
  • Ask-OFF Backend API: Running locally on http://127.0.0.1:8000 or hosted remotely

Installation & Local Setup

1. Clone Repository

git clone https://github.com/SaitejaKommi/AskOFF-WebApp.git
cd AskOFF-WebApp

2. Install Dependencies

npm install

3. Configure Environment Variables

Copy the example environment configuration:

cp .env.example .env
VariableDefaultDescription
VITE_API_BASE_URL/apiBase URL or proxy prefix for the Ask-OFF FastAPI backend. In development, Vite automatically proxies /api to http://127.0.0.1:8000.

Security Note: All VITE_* variables are bundled directly into client-side code and visible to anyone inspecting network requests. Never expose private passwords, tokens, or backend credentials in frontend environment variables.

4. Run Development Server

npm run dev

The application will launch with hot module replacement (HMR) at http://localhost:5173.


Running Tests & Quality Checks

The repository enforces strict code quality, type safety, and automated test coverage:

# 1. Run all 24 automated unit & component tests
npm run test# 2. Run Oxlint static analysis (0 errors)
npm run lint
# 3. Verify TypeScript compilation
npx tsc -b
# 4. Compile production build
npm run build
# 5. Preview production bundle locally
npm run preview

Test Coverage Highlights

  • Component Behavior: User-facing tests for ProductCard, NutritionTable, EmptyState, ErrorState, and ErrorBoundary via @testing-library/react.
  • API Client Utilities: Validation of per-100g nutrient extraction, 4-tier CDN URL normalization, and product image resolution.
  • Assistant Service: Context-aware nutritional reasoning, allergen extraction, and structured citation verification.
  • Nutritional Standards: Enforcement of Nutri-Score calculation bounds (A to E) and NOVA food processing classifications (1 to 4).

Performance & Production Readiness

  • Asynchronous Route Splitting: Route components are dynamically imported using React.lazy(), cutting the initial main bundle size down to 266 kB (81.5 kB gzip) and serving per-page JavaScript chunks on demand.
  • Race-Condition-Free Autocomplete: Search inputs use AbortController to automatically cancel in-flight HTTP requests when the user continues typing, preventing stale network responses from overwriting newer suggestions.
  • Open Food Facts CDN Tiering: Barcode images with 9+ digits are normalized into 4-tier subdirectories (e.g. 0060383860479$\to$006/038/386/0479/1.jpg), preventing image delivery 404s.
  • Global Error Boundary: Client-side rendering exceptions are intercepted gracefully by ErrorBoundary, offering immediate recovery actions without blank screens.
  • Production Deployment: The project compiles to standard static assets in dist/ that can be hosted on any static platform (Cloudflare Pages, Vercel, Netlify, AWS S3 / CloudFront, or NGINX) with client-side routing fallback to /index.html.

Documentation & Guidelines

  • CONTRIBUTORS.md — Comprehensive contributing workflow, component conventions, testing practices, and accessibility rules.
  • docs/design-references/ — Design system assets, reference screenshots, and visual specifications.

Open Food Facts Attribution

This application interfaces with open product records provided by Open Food Facts, published under the Open Database License (ODbL). Individual product images and brand assets remain the property of their respective copyright holders under Open Food Facts contributor terms.

Ask-OFF is an independent community discovery project and is not operated by the Open Food Facts organization.


License

This project is licensed under the Apache 2.0 License.


🌱 Building intelligent, transparent food discovery for Open Food Facts Canada.

About

AskOFF WebApp - An open-source web application for discovering, searching, comparing, and exploring Open Food Facts products with a clean, user-friendly experience.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

Ask-OFF WebApp

Intelligent Food Discovery & Conversational UI for Open Food Facts Canada

A high-performance, accessible web application for natural language food search, structured nutritional comparison, and grounded food inquiry across 124,145 Canadian Open Food Facts products.


ReactTypeScriptViteTailwindCSSTanStack QueryVitestOpen Food Facts


Overview

Ask-OFF WebApp is the official open-source frontend application for the Ask-OFF Canada ecosystem. It provides an intuitive, high-speed, and responsive interface designed to explore, analyze, and compare 124,145 Canadian grocery and packaged food products.

While traditional food catalogs present rigid, unstructured text fields, Ask-OFF delivers an interactive web experience powered by deterministic natural language search, official French/English bilingual product records, and evidence-grounded conversational inquiry.

Primary Objectives

  • Conversational & Keyword Food Discovery: Instant natural language exploration coupled with debounced, race-condition-free autocomplete and query match highlighting.
  • Deterministic Nutritional Transparency: Render official vector Nutri-Score gauges (A to E), NOVA industrial food processing groups (1 to 4), Eco-Scores, allergens, and per-100g nutritional facts tables.
  • Side-by-Side Nutritional Comparison: Multi-product matrix evaluating 2 to 4 food items simultaneously with macro-nutrient deltas and persistent browser storage.
  • Ask-OFF Assistant: Grounded conversational assistant (available via full page or floating action button) that queries the backend retrieval engine and provides verifiable citations to official Open Food Facts records.
  • Zero-Latency Route Navigation: Route-level code splitting using React.lazy and Suspense to deliver minimal initial JavaScript payloads and lightning-fast transitions.
  • Universal Accessibility (a11y): Built from the ground up with keyboard navigation, visible focus indicators, screen-reader semantics, and full prefers-reduced-motion accommodation.

System Architecture

Ask-OFF WebApp operates as a decoupled single-page application (SPA) communicating over HTTPS REST APIs with the FastAPI retrieval backend.

flowchart TD
subgraph Browser_Client [Client Browser — AskOFF WebApp]
A[User Interaction] --> B[React Router 7 Declarative Routes]
B --> C[Pages & Lazy-Loaded Chunks]
C --> D[Shared Reusable UI Components]
D --> E[TanStack Query Cache]
D --> F[Local State: CompareContext & ListsContext]
end
subgraph API_Layer [Frontend API Boundary]
E --> G[Typed REST Client: src/api/client.ts]
G --> H[Vite Development Proxy /api or Production HTTPS]
end
subgraph Backend_Infrastructure [Ask-OFF Backend — offCanada]
H --> I[FastAPI REST Gateway]
I --> J[Query Understanding & Intent Pipeline]
J --> K[(OpenSearch 2.x BM25 Cluster<br/>124,145 Canadian Records)]
end
Loading

Technology Stack

LayerTechnologiesPurpose
Frontend CoreReact 19.2+, TypeScript 6.0+, Vite 8.1+Ultra-fast modern web application with strict type safety and sub-second HMR
State & Data Fetching@tanstack/react-query 5.101+, Context APIServer-state caching, stale-while-revalidate, request deduplication, and local storage
Routing & Code SplittingReact Router 7.18+, React.lazy, SuspenseDeclarative route matching with discrete asynchronous chunks per route
Styling & Design SystemTailwindCSS 3.4+, PostCSS 8, AutoprefixerUtility-first responsive styling with custom typography, focus styling, and scrollbars
Icons & MediaLucide React 1.24+, SVG Vector GraphicsClean, accessible vector icons and dynamic Nutri-Score/NOVA badges
Testing SuiteVitest 4.1+, @testing-library/react, JSDOMComprehensive unit and component behavior test suites (24/24 tests passing)
Static Code AnalysisOxlint, TypeScript Compiler (tsc -b)Sub-second linter execution and zero-error strict type verification

Application Pages & Key Features

RoutePage ComponentKey Functionality
/ or /discoverLandingPageHero search, dynamic typewriter placeholders, brand chips, category highlights, and catalog showcase
/searchSearchPageSearch results grid, 9 dietary & health filter toggles, sort dropdown, responsive drawer, and pagination
/product/:idProductDetailsPageFull product specifications, 4-tier CDN images, nutrition facts per 100g, ingredients, allergens, and labels
/compareComparePageSide-by-side comparison table for 2–4 products with nutrient difference calculations and quick product picker
/offbotOffBotPageDedicated conversational food assistant with verifiable ODbL citations, grounded evidence cards, and follow-ups
/listsListsPageSaved grocery lists, favorites, and dietary bookmarks with print and JSON export capabilities
/recipesRecipesPageCurated recipe directory with ingredients linked directly to live search queries and barcode tokens
/extensionsExtensionsPageEcosystem showcase for community plugins, allergy advisors, barcode scanners, and grocery integrations
/statusDashboardPageLive backend service health check, OpenSearch connection status, record count, and autocomplete testing sandbox
/aboutAboutPageProject mission, architecture pillars, Open Food Facts partnership, and Open Database License (ODbL) attribution

Reusable Component Architecture

The frontend is structured around reusable, accessible presentation components:

src/components/
├── ErrorBoundary.tsx # Catches unhandled render errors with user-friendly recovery UI
├── OffBotChat.tsx # Core conversational chat engine, message list & ODbL citations
├── OffBotWidget.tsx # Spherical Floating Action Button (FAB) launcher and popup chat modal
├── ProductCard.tsx # Food product card with image, brand, Nutri-Score & macro stats
├── ProductImage.tsx # Resilient image loader with 4-tier Open Food Facts CDN normalization
├── ProductImagePlaceholder.tsx # Category-aware SVG fallback placeholder for items without images
├── NutritionTable.tsx # Structured nutrition table per 100g with European & Canadian standards
├── NutriScoreLogo.tsx # Official vector SVG Nutri-Score spectrum graphic (A to E)
├── NutriScoreBadge.tsx # Compact Nutri-Score pills, NOVA Group cards (1 to 4) & Eco-Score badges
├── SearchBar.tsx # Search input with debounced autocomplete, typewriter & AbortSignal
├── FilterSidebar.tsx # Desktop sidebar and mobile slide-over drawer for dietary filters
├── Pagination.tsx # Accessible page navigator with previous/next controls
├── LoadingSkeleton.tsx # Animated skeleton loaders for cards, detail sheets, and grids
├── EmptyState.tsx # Friendly empty states with recommended actions
├── ErrorState.tsx # Connection error displays with retry callbacks
└── ScrollToTop.tsx # Window scroll reset upon route navigation

Project Structure

AskOFF-WebApp/
├── public/ # Static public assets
│ ├── favicon.svg # Web application favicon
│ ├── icons.svg # SVG sprite sheet
│ └── logo.png # Project logo asset
├── docs/
│ └── design-references/ # Design artifacts, reference screenshots & visual mockups
│ ├── A.jpg
│ ├── B.jpg
│ ├── C.jpg
│ ├── image (1).png
│ ├── image.png
│ ├── Nutri-score-A.webp
│ └── Screenshot 2026-08-26 180122.png
├── src/
│ ├── api/
│ │ ├── client.ts # Typed REST API client, timeout handling & CDN normalization
│ │ └── assistantService.ts # Conversational query synthesis & citation models
│ ├── assets/ # Optimized vector icons and project logo
│ │ ├── app_qr.svg
│ │ ├── hero.png
│ │ ├── logo.png
│ │ └── phone_app.png
│ ├── components/ # 16 reusable UI and utility components
│ ├── constants/
│ │ └── queries.ts # Default sample queries and animated typewriter phrases
│ ├── context/
│ │ ├── AssistantContext.tsx # Chat state, conversation history, and active product focus
│ │ ├── CompareContext.tsx # Multi-product comparison state (stored in localStorage)
│ │ └── ListsContext.tsx # Favorites, shopping list & saved items (stored in localStorage)
│ ├── pages/ # 10 route page components
│ ├── tests/ # Vitest unit and React Testing Library component tests
│ │ ├── assistantService.test.ts
│ │ ├── badges.test.ts
│ │ ├── client.test.ts
│ │ ├── components.test.tsx
│ │ └── productCard.test.ts
│ ├── App.tsx # Main router, ErrorBoundary, QueryClient, and Providers
│ ├── index.css # Tailwind directives, focus-visible styles, and custom scrollbars
│ └── main.tsx # Application entry point (React StrictMode)
├── .env.example # Template for frontend environment variables
├── .gitignore # Git exclusion rules
├── .oxlintrc.json # Oxlint linter configuration
├── package.json # Project manifest, scripts, and dependencies
├── package-lock.json # Locked dependency tree
├── postcss.config.js # PostCSS plugin configuration
├── tailwind.config.js # TailwindCSS theme and font configurations
├── tsconfig.json # TypeScript project configuration root
├── tsconfig.app.json # TypeScript browser configuration
├── tsconfig.node.json # TypeScript Node/Vite configuration
├── vite.config.ts # Vite configuration with proxy and Vitest settings
├── CONTRIBUTORS.md # Contributor guidelines and workflow
└── README.md # Project documentation

Quick Start

Prerequisites

  • Node.js: v18.0.0 or higher (v20 LTS recommended)
  • npm: v9.0.0 or higher
  • Ask-OFF Backend API: Running locally on http://127.0.0.1:8000 or hosted remotely

Installation & Local Setup

1. Clone Repository

git clone https://github.com/SaitejaKommi/AskOFF-WebApp.git
cd AskOFF-WebApp

2. Install Dependencies

npm install

3. Configure Environment Variables

Copy the example environment configuration:

cp .env.example .env
VariableDefaultDescription
VITE_API_BASE_URL/apiBase URL or proxy prefix for the Ask-OFF FastAPI backend. In development, Vite automatically proxies /api to http://127.0.0.1:8000.

Security Note: All VITE_* variables are bundled directly into client-side code and visible to anyone inspecting network requests. Never expose private passwords, tokens, or backend credentials in frontend environment variables.

4. Run Development Server

npm run dev

The application will launch with hot module replacement (HMR) at http://localhost:5173.


Running Tests & Quality Checks

The repository enforces strict code quality, type safety, and automated test coverage:

# 1. Run all 24 automated unit & component tests
npm run test# 2. Run Oxlint static analysis (0 errors)
npm run lint
# 3. Verify TypeScript compilation
npx tsc -b
# 4. Compile production build
npm run build
# 5. Preview production bundle locally
npm run preview

Test Coverage Highlights

  • Component Behavior: User-facing tests for ProductCard, NutritionTable, EmptyState, ErrorState, and ErrorBoundary via @testing-library/react.
  • API Client Utilities: Validation of per-100g nutrient extraction, 4-tier CDN URL normalization, and product image resolution.
  • Assistant Service: Context-aware nutritional reasoning, allergen extraction, and structured citation verification.
  • Nutritional Standards: Enforcement of Nutri-Score calculation bounds (A to E) and NOVA food processing classifications (1 to 4).

Performance & Production Readiness

  • Asynchronous Route Splitting: Route components are dynamically imported using React.lazy(), cutting the initial main bundle size down to 266 kB (81.5 kB gzip) and serving per-page JavaScript chunks on demand.
  • Race-Condition-Free Autocomplete: Search inputs use AbortController to automatically cancel in-flight HTTP requests when the user continues typing, preventing stale network responses from overwriting newer suggestions.
  • Open Food Facts CDN Tiering: Barcode images with 9+ digits are normalized into 4-tier subdirectories (e.g. 0060383860479$\to$006/038/386/0479/1.jpg), preventing image delivery 404s.
  • Global Error Boundary: Client-side rendering exceptions are intercepted gracefully by ErrorBoundary, offering immediate recovery actions without blank screens.
  • Production Deployment: The project compiles to standard static assets in dist/ that can be hosted on any static platform (Cloudflare Pages, Vercel, Netlify, AWS S3 / CloudFront, or NGINX) with client-side routing fallback to /index.html.

Documentation & Guidelines

  • CONTRIBUTORS.md — Comprehensive contributing workflow, component conventions, testing practices, and accessibility rules.
  • docs/design-references/ — Design system assets, reference screenshots, and visual specifications.

Open Food Facts Attribution

This application interfaces with open product records provided by Open Food Facts, published under the Open Database License (ODbL). Individual product images and brand assets remain the property of their respective copyright holders under Open Food Facts contributor terms.

Ask-OFF is an independent community discovery project and is not operated by the Open Food Facts organization.


License

This project is licensed under the Apache 2.0 License.


🌱 Building intelligent, transparent food discovery for Open Food Facts Canada.

About

AskOFF WebApp - An open-source web application for discovering, searching, comparing, and exploring Open Food Facts products with a clean, user-friendly experience.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Ask-OFF WebApp

Intelligent Food Discovery & Conversational UI for Open Food Facts Canada

A high-performance, accessible web application for natural language food search, structured nutritional comparison, and grounded food inquiry across 124,145 Canadian Open Food Facts products.


ReactTypeScriptViteTailwindCSSTanStack QueryVitestOpen Food Facts


Overview

Ask-OFF WebApp is the official open-source frontend application for the Ask-OFF Canada ecosystem. It provides an intuitive, high-speed, and responsive interface designed to explore, analyze, and compare 124,145 Canadian grocery and packaged food products.

While traditional food catalogs present rigid, unstructured text fields, Ask-OFF delivers an interactive web experience powered by deterministic natural language search, official French/English bilingual product records, and evidence-grounded conversational inquiry.

Primary Objectives

  • Conversational & Keyword Food Discovery: Instant natural language exploration coupled with debounced, race-condition-free autocomplete and query match highlighting.
  • Deterministic Nutritional Transparency: Render official vector Nutri-Score gauges (A to E), NOVA industrial food processing groups (1 to 4), Eco-Scores, allergens, and per-100g nutritional facts tables.
  • Side-by-Side Nutritional Comparison: Multi-product matrix evaluating 2 to 4 food items simultaneously with macro-nutrient deltas and persistent browser storage.
  • Ask-OFF Assistant: Grounded conversational assistant (available via full page or floating action button) that queries the backend retrieval engine and provides verifiable citations to official Open Food Facts records.
  • Zero-Latency Route Navigation: Route-level code splitting using React.lazy and Suspense to deliver minimal initial JavaScript payloads and lightning-fast transitions.
  • Universal Accessibility (a11y): Built from the ground up with keyboard navigation, visible focus indicators, screen-reader semantics, and full prefers-reduced-motion accommodation.

System Architecture

Ask-OFF WebApp operates as a decoupled single-page application (SPA) communicating over HTTPS REST APIs with the FastAPI retrieval backend.

flowchart TD
subgraph Browser_Client [Client Browser — AskOFF WebApp]
A[User Interaction] --> B[React Router 7 Declarative Routes]
B --> C[Pages & Lazy-Loaded Chunks]
C --> D[Shared Reusable UI Components]
D --> E[TanStack Query Cache]
D --> F[Local State: CompareContext & ListsContext]
end
subgraph API_Layer [Frontend API Boundary]
E --> G[Typed REST Client: src/api/client.ts]
G --> H[Vite Development Proxy /api or Production HTTPS]
end
subgraph Backend_Infrastructure [Ask-OFF Backend — offCanada]
H --> I[FastAPI REST Gateway]
I --> J[Query Understanding & Intent Pipeline]
J --> K[(OpenSearch 2.x BM25 Cluster<br/>124,145 Canadian Records)]
end
Loading

Technology Stack

LayerTechnologiesPurpose
Frontend CoreReact 19.2+, TypeScript 6.0+, Vite 8.1+Ultra-fast modern web application with strict type safety and sub-second HMR
State & Data Fetching@tanstack/react-query 5.101+, Context APIServer-state caching, stale-while-revalidate, request deduplication, and local storage
Routing & Code SplittingReact Router 7.18+, React.lazy, SuspenseDeclarative route matching with discrete asynchronous chunks per route
Styling & Design SystemTailwindCSS 3.4+, PostCSS 8, AutoprefixerUtility-first responsive styling with custom typography, focus styling, and scrollbars
Icons & MediaLucide React 1.24+, SVG Vector GraphicsClean, accessible vector icons and dynamic Nutri-Score/NOVA badges
Testing SuiteVitest 4.1+, @testing-library/react, JSDOMComprehensive unit and component behavior test suites (24/24 tests passing)
Static Code AnalysisOxlint, TypeScript Compiler (tsc -b)Sub-second linter execution and zero-error strict type verification

Application Pages & Key Features

RoutePage ComponentKey Functionality
/ or /discoverLandingPageHero search, dynamic typewriter placeholders, brand chips, category highlights, and catalog showcase
/searchSearchPageSearch results grid, 9 dietary & health filter toggles, sort dropdown, responsive drawer, and pagination
/product/:idProductDetailsPageFull product specifications, 4-tier CDN images, nutrition facts per 100g, ingredients, allergens, and labels
/compareComparePageSide-by-side comparison table for 2–4 products with nutrient difference calculations and quick product picker
/offbotOffBotPageDedicated conversational food assistant with verifiable ODbL citations, grounded evidence cards, and follow-ups
/listsListsPageSaved grocery lists, favorites, and dietary bookmarks with print and JSON export capabilities
/recipesRecipesPageCurated recipe directory with ingredients linked directly to live search queries and barcode tokens
/extensionsExtensionsPageEcosystem showcase for community plugins, allergy advisors, barcode scanners, and grocery integrations
/statusDashboardPageLive backend service health check, OpenSearch connection status, record count, and autocomplete testing sandbox
/aboutAboutPageProject mission, architecture pillars, Open Food Facts partnership, and Open Database License (ODbL) attribution

Reusable Component Architecture

The frontend is structured around reusable, accessible presentation components:

src/components/
├── ErrorBoundary.tsx # Catches unhandled render errors with user-friendly recovery UI
├── OffBotChat.tsx # Core conversational chat engine, message list & ODbL citations
├── OffBotWidget.tsx # Spherical Floating Action Button (FAB) launcher and popup chat modal
├── ProductCard.tsx # Food product card with image, brand, Nutri-Score & macro stats
├── ProductImage.tsx # Resilient image loader with 4-tier Open Food Facts CDN normalization
├── ProductImagePlaceholder.tsx # Category-aware SVG fallback placeholder for items without images
├── NutritionTable.tsx # Structured nutrition table per 100g with European & Canadian standards
├── NutriScoreLogo.tsx # Official vector SVG Nutri-Score spectrum graphic (A to E)
├── NutriScoreBadge.tsx # Compact Nutri-Score pills, NOVA Group cards (1 to 4) & Eco-Score badges
├── SearchBar.tsx # Search input with debounced autocomplete, typewriter & AbortSignal
├── FilterSidebar.tsx # Desktop sidebar and mobile slide-over drawer for dietary filters
├── Pagination.tsx # Accessible page navigator with previous/next controls
├── LoadingSkeleton.tsx # Animated skeleton loaders for cards, detail sheets, and grids
├── EmptyState.tsx # Friendly empty states with recommended actions
├── ErrorState.tsx # Connection error displays with retry callbacks
└── ScrollToTop.tsx # Window scroll reset upon route navigation

Project Structure

AskOFF-WebApp/
├── public/ # Static public assets
│ ├── favicon.svg # Web application favicon
│ ├── icons.svg # SVG sprite sheet
│ └── logo.png # Project logo asset
├── docs/
│ └── design-references/ # Design artifacts, reference screenshots & visual mockups
│ ├── A.jpg
│ ├── B.jpg
│ ├── C.jpg
│ ├── image (1).png
│ ├── image.png
│ ├── Nutri-score-A.webp
│ └── Screenshot 2026-08-26 180122.png
├── src/
│ ├── api/
│ │ ├── client.ts # Typed REST API client, timeout handling & CDN normalization
│ │ └── assistantService.ts # Conversational query synthesis & citation models
│ ├── assets/ # Optimized vector icons and project logo
│ │ ├── app_qr.svg
│ │ ├── hero.png
│ │ ├── logo.png
│ │ └── phone_app.png
│ ├── components/ # 16 reusable UI and utility components
│ ├── constants/
│ │ └── queries.ts # Default sample queries and animated typewriter phrases
│ ├── context/
│ │ ├── AssistantContext.tsx # Chat state, conversation history, and active product focus
│ │ ├── CompareContext.tsx # Multi-product comparison state (stored in localStorage)
│ │ └── ListsContext.tsx # Favorites, shopping list & saved items (stored in localStorage)
│ ├── pages/ # 10 route page components
│ ├── tests/ # Vitest unit and React Testing Library component tests
│ │ ├── assistantService.test.ts
│ │ ├── badges.test.ts
│ │ ├── client.test.ts
│ │ ├── components.test.tsx
│ │ └── productCard.test.ts
│ ├── App.tsx # Main router, ErrorBoundary, QueryClient, and Providers
│ ├── index.css # Tailwind directives, focus-visible styles, and custom scrollbars
│ └── main.tsx # Application entry point (React StrictMode)
├── .env.example # Template for frontend environment variables
├── .gitignore # Git exclusion rules
├── .oxlintrc.json # Oxlint linter configuration
├── package.json # Project manifest, scripts, and dependencies
├── package-lock.json # Locked dependency tree
├── postcss.config.js # PostCSS plugin configuration
├── tailwind.config.js # TailwindCSS theme and font configurations
├── tsconfig.json # TypeScript project configuration root
├── tsconfig.app.json # TypeScript browser configuration
├── tsconfig.node.json # TypeScript Node/Vite configuration
├── vite.config.ts # Vite configuration with proxy and Vitest settings
├── CONTRIBUTORS.md # Contributor guidelines and workflow
└── README.md # Project documentation

Quick Start

Prerequisites

  • Node.js: v18.0.0 or higher (v20 LTS recommended)
  • npm: v9.0.0 or higher
  • Ask-OFF Backend API: Running locally on http://127.0.0.1:8000 or hosted remotely

Installation & Local Setup

1. Clone Repository

git clone https://github.com/SaitejaKommi/AskOFF-WebApp.git
cd AskOFF-WebApp

2. Install Dependencies

npm install

3. Configure Environment Variables

Copy the example environment configuration:

cp .env.example .env
VariableDefaultDescription
VITE_API_BASE_URL/apiBase URL or proxy prefix for the Ask-OFF FastAPI backend. In development, Vite automatically proxies /api to http://127.0.0.1:8000.

Security Note: All VITE_* variables are bundled directly into client-side code and visible to anyone inspecting network requests. Never expose private passwords, tokens, or backend credentials in frontend environment variables.

4. Run Development Server

npm run dev

The application will launch with hot module replacement (HMR) at http://localhost:5173.


Running Tests & Quality Checks

The repository enforces strict code quality, type safety, and automated test coverage:

# 1. Run all 24 automated unit & component tests
npm run test# 2. Run Oxlint static analysis (0 errors)
npm run lint
# 3. Verify TypeScript compilation
npx tsc -b
# 4. Compile production build
npm run build
# 5. Preview production bundle locally
npm run preview

Test Coverage Highlights

  • Component Behavior: User-facing tests for ProductCard, NutritionTable, EmptyState, ErrorState, and ErrorBoundary via @testing-library/react.
  • API Client Utilities: Validation of per-100g nutrient extraction, 4-tier CDN URL normalization, and product image resolution.
  • Assistant Service: Context-aware nutritional reasoning, allergen extraction, and structured citation verification.
  • Nutritional Standards: Enforcement of Nutri-Score calculation bounds (A to E) and NOVA food processing classifications (1 to 4).

Performance & Production Readiness

  • Asynchronous Route Splitting: Route components are dynamically imported using React.lazy(), cutting the initial main bundle size down to 266 kB (81.5 kB gzip) and serving per-page JavaScript chunks on demand.
  • Race-Condition-Free Autocomplete: Search inputs use AbortController to automatically cancel in-flight HTTP requests when the user continues typing, preventing stale network responses from overwriting newer suggestions.
  • Open Food Facts CDN Tiering: Barcode images with 9+ digits are normalized into 4-tier subdirectories (e.g. 0060383860479$\to$006/038/386/0479/1.jpg), preventing image delivery 404s.
  • Global Error Boundary: Client-side rendering exceptions are intercepted gracefully by ErrorBoundary, offering immediate recovery actions without blank screens.
  • Production Deployment: The project compiles to standard static assets in dist/ that can be hosted on any static platform (Cloudflare Pages, Vercel, Netlify, AWS S3 / CloudFront, or NGINX) with client-side routing fallback to /index.html.

Documentation & Guidelines

  • CONTRIBUTORS.md — Comprehensive contributing workflow, component conventions, testing practices, and accessibility rules.
  • docs/design-references/ — Design system assets, reference screenshots, and visual specifications.

Open Food Facts Attribution

This application interfaces with open product records provided by Open Food Facts, published under the Open Database License (ODbL). Individual product images and brand assets remain the property of their respective copyright holders under Open Food Facts contributor terms.

Ask-OFF is an independent community discovery project and is not operated by the Open Food Facts organization.


License

This project is licensed under the Apache 2.0 License.


🌱 Building intelligent, transparent food discovery for Open Food Facts Canada.

About

AskOFF WebApp - An open-source web application for discovering, searching, comparing, and exploring Open Food Facts products with a clean, user-friendly experience.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Ask-OFF WebApp

Intelligent Food Discovery & Conversational UI for Open Food Facts Canada

A high-performance, accessible web application for natural language food search, structured nutritional comparison, and grounded food inquiry across 124,145 Canadian Open Food Facts products.


ReactTypeScriptViteTailwindCSSTanStack QueryVitestOpen Food Facts


Overview

Ask-OFF WebApp is the official open-source frontend application for the Ask-OFF Canada ecosystem. It provides an intuitive, high-speed, and responsive interface designed to explore, analyze, and compare 124,145 Canadian grocery and packaged food products.

While traditional food catalogs present rigid, unstructured text fields, Ask-OFF delivers an interactive web experience powered by deterministic natural language search, official French/English bilingual product records, and evidence-grounded conversational inquiry.

Primary Objectives

  • Conversational & Keyword Food Discovery: Instant natural language exploration coupled with debounced, race-condition-free autocomplete and query match highlighting.
  • Deterministic Nutritional Transparency: Render official vector Nutri-Score gauges (A to E), NOVA industrial food processing groups (1 to 4), Eco-Scores, allergens, and per-100g nutritional facts tables.
  • Side-by-Side Nutritional Comparison: Multi-product matrix evaluating 2 to 4 food items simultaneously with macro-nutrient deltas and persistent browser storage.
  • Ask-OFF Assistant: Grounded conversational assistant (available via full page or floating action button) that queries the backend retrieval engine and provides verifiable citations to official Open Food Facts records.
  • Zero-Latency Route Navigation: Route-level code splitting using React.lazy and Suspense to deliver minimal initial JavaScript payloads and lightning-fast transitions.
  • Universal Accessibility (a11y): Built from the ground up with keyboard navigation, visible focus indicators, screen-reader semantics, and full prefers-reduced-motion accommodation.

System Architecture

Ask-OFF WebApp operates as a decoupled single-page application (SPA) communicating over HTTPS REST APIs with the FastAPI retrieval backend.

flowchart TD
subgraph Browser_Client [Client Browser — AskOFF WebApp]
A[User Interaction] --> B[React Router 7 Declarative Routes]
B --> C[Pages & Lazy-Loaded Chunks]
C --> D[Shared Reusable UI Components]
D --> E[TanStack Query Cache]
D --> F[Local State: CompareContext & ListsContext]
end
subgraph API_Layer [Frontend API Boundary]
E --> G[Typed REST Client: src/api/client.ts]
G --> H[Vite Development Proxy /api or Production HTTPS]
end
subgraph Backend_Infrastructure [Ask-OFF Backend — offCanada]
H --> I[FastAPI REST Gateway]
I --> J[Query Understanding & Intent Pipeline]
J --> K[(OpenSearch 2.x BM25 Cluster<br/>124,145 Canadian Records)]
end
Loading

Technology Stack

LayerTechnologiesPurpose
Frontend CoreReact 19.2+, TypeScript 6.0+, Vite 8.1+Ultra-fast modern web application with strict type safety and sub-second HMR
State & Data Fetching@tanstack/react-query 5.101+, Context APIServer-state caching, stale-while-revalidate, request deduplication, and local storage
Routing & Code SplittingReact Router 7.18+, React.lazy, SuspenseDeclarative route matching with discrete asynchronous chunks per route
Styling & Design SystemTailwindCSS 3.4+, PostCSS 8, AutoprefixerUtility-first responsive styling with custom typography, focus styling, and scrollbars
Icons & MediaLucide React 1.24+, SVG Vector GraphicsClean, accessible vector icons and dynamic Nutri-Score/NOVA badges
Testing SuiteVitest 4.1+, @testing-library/react, JSDOMComprehensive unit and component behavior test suites (24/24 tests passing)
Static Code AnalysisOxlint, TypeScript Compiler (tsc -b)Sub-second linter execution and zero-error strict type verification

Application Pages & Key Features

RoutePage ComponentKey Functionality
/ or /discoverLandingPageHero search, dynamic typewriter placeholders, brand chips, category highlights, and catalog showcase
/searchSearchPageSearch results grid, 9 dietary & health filter toggles, sort dropdown, responsive drawer, and pagination
/product/:idProductDetailsPageFull product specifications, 4-tier CDN images, nutrition facts per 100g, ingredients, allergens, and labels
/compareComparePageSide-by-side comparison table for 2–4 products with nutrient difference calculations and quick product picker
/offbotOffBotPageDedicated conversational food assistant with verifiable ODbL citations, grounded evidence cards, and follow-ups
/listsListsPageSaved grocery lists, favorites, and dietary bookmarks with print and JSON export capabilities
/recipesRecipesPageCurated recipe directory with ingredients linked directly to live search queries and barcode tokens
/extensionsExtensionsPageEcosystem showcase for community plugins, allergy advisors, barcode scanners, and grocery integrations
/statusDashboardPageLive backend service health check, OpenSearch connection status, record count, and autocomplete testing sandbox
/aboutAboutPageProject mission, architecture pillars, Open Food Facts partnership, and Open Database License (ODbL) attribution

Reusable Component Architecture

The frontend is structured around reusable, accessible presentation components:

src/components/
├── ErrorBoundary.tsx # Catches unhandled render errors with user-friendly recovery UI
├── OffBotChat.tsx # Core conversational chat engine, message list & ODbL citations
├── OffBotWidget.tsx # Spherical Floating Action Button (FAB) launcher and popup chat modal
├── ProductCard.tsx # Food product card with image, brand, Nutri-Score & macro stats
├── ProductImage.tsx # Resilient image loader with 4-tier Open Food Facts CDN normalization
├── ProductImagePlaceholder.tsx # Category-aware SVG fallback placeholder for items without images
├── NutritionTable.tsx # Structured nutrition table per 100g with European & Canadian standards
├── NutriScoreLogo.tsx # Official vector SVG Nutri-Score spectrum graphic (A to E)
├── NutriScoreBadge.tsx # Compact Nutri-Score pills, NOVA Group cards (1 to 4) & Eco-Score badges
├── SearchBar.tsx # Search input with debounced autocomplete, typewriter & AbortSignal
├── FilterSidebar.tsx # Desktop sidebar and mobile slide-over drawer for dietary filters
├── Pagination.tsx # Accessible page navigator with previous/next controls
├── LoadingSkeleton.tsx # Animated skeleton loaders for cards, detail sheets, and grids
├── EmptyState.tsx # Friendly empty states with recommended actions
├── ErrorState.tsx # Connection error displays with retry callbacks
└── ScrollToTop.tsx # Window scroll reset upon route navigation

Project Structure

AskOFF-WebApp/
├── public/ # Static public assets
│ ├── favicon.svg # Web application favicon
│ ├── icons.svg # SVG sprite sheet
│ └── logo.png # Project logo asset
├── docs/
│ └── design-references/ # Design artifacts, reference screenshots & visual mockups
│ ├── A.jpg
│ ├── B.jpg
│ ├── C.jpg
│ ├── image (1).png
│ ├── image.png
│ ├── Nutri-score-A.webp
│ └── Screenshot 2026-08-26 180122.png
├── src/
│ ├── api/
│ │ ├── client.ts # Typed REST API client, timeout handling & CDN normalization
│ │ └── assistantService.ts # Conversational query synthesis & citation models
│ ├── assets/ # Optimized vector icons and project logo
│ │ ├── app_qr.svg
│ │ ├── hero.png
│ │ ├── logo.png
│ │ └── phone_app.png
│ ├── components/ # 16 reusable UI and utility components
│ ├── constants/
│ │ └── queries.ts # Default sample queries and animated typewriter phrases
│ ├── context/
│ │ ├── AssistantContext.tsx # Chat state, conversation history, and active product focus
│ │ ├── CompareContext.tsx # Multi-product comparison state (stored in localStorage)
│ │ └── ListsContext.tsx # Favorites, shopping list & saved items (stored in localStorage)
│ ├── pages/ # 10 route page components
│ ├── tests/ # Vitest unit and React Testing Library component tests
│ │ ├── assistantService.test.ts
│ │ ├── badges.test.ts
│ │ ├── client.test.ts
│ │ ├── components.test.tsx
│ │ └── productCard.test.ts
│ ├── App.tsx # Main router, ErrorBoundary, QueryClient, and Providers
│ ├── index.css # Tailwind directives, focus-visible styles, and custom scrollbars
│ └── main.tsx # Application entry point (React StrictMode)
├── .env.example # Template for frontend environment variables
├── .gitignore # Git exclusion rules
├── .oxlintrc.json # Oxlint linter configuration
├── package.json # Project manifest, scripts, and dependencies
├── package-lock.json # Locked dependency tree
├── postcss.config.js # PostCSS plugin configuration
├── tailwind.config.js # TailwindCSS theme and font configurations
├── tsconfig.json # TypeScript project configuration root
├── tsconfig.app.json # TypeScript browser configuration
├── tsconfig.node.json # TypeScript Node/Vite configuration
├── vite.config.ts # Vite configuration with proxy and Vitest settings
├── CONTRIBUTORS.md # Contributor guidelines and workflow
└── README.md # Project documentation

Quick Start

Prerequisites

  • Node.js: v18.0.0 or higher (v20 LTS recommended)
  • npm: v9.0.0 or higher
  • Ask-OFF Backend API: Running locally on http://127.0.0.1:8000 or hosted remotely

Installation & Local Setup

1. Clone Repository

git clone https://github.com/SaitejaKommi/AskOFF-WebApp.git
cd AskOFF-WebApp

2. Install Dependencies

npm install

3. Configure Environment Variables

Copy the example environment configuration:

cp .env.example .env
VariableDefaultDescription
VITE_API_BASE_URL/apiBase URL or proxy prefix for the Ask-OFF FastAPI backend. In development, Vite automatically proxies /api to http://127.0.0.1:8000.

Security Note: All VITE_* variables are bundled directly into client-side code and visible to anyone inspecting network requests. Never expose private passwords, tokens, or backend credentials in frontend environment variables.

4. Run Development Server

npm run dev

The application will launch with hot module replacement (HMR) at http://localhost:5173.


Running Tests & Quality Checks

The repository enforces strict code quality, type safety, and automated test coverage:

# 1. Run all 24 automated unit & component tests
npm run test# 2. Run Oxlint static analysis (0 errors)
npm run lint
# 3. Verify TypeScript compilation
npx tsc -b
# 4. Compile production build
npm run build
# 5. Preview production bundle locally
npm run preview

Test Coverage Highlights

  • Component Behavior: User-facing tests for ProductCard, NutritionTable, EmptyState, ErrorState, and ErrorBoundary via @testing-library/react.
  • API Client Utilities: Validation of per-100g nutrient extraction, 4-tier CDN URL normalization, and product image resolution.
  • Assistant Service: Context-aware nutritional reasoning, allergen extraction, and structured citation verification.
  • Nutritional Standards: Enforcement of Nutri-Score calculation bounds (A to E) and NOVA food processing classifications (1 to 4).

Performance & Production Readiness

  • Asynchronous Route Splitting: Route components are dynamically imported using React.lazy(), cutting the initial main bundle size down to 266 kB (81.5 kB gzip) and serving per-page JavaScript chunks on demand.
  • Race-Condition-Free Autocomplete: Search inputs use AbortController to automatically cancel in-flight HTTP requests when the user continues typing, preventing stale network responses from overwriting newer suggestions.
  • Open Food Facts CDN Tiering: Barcode images with 9+ digits are normalized into 4-tier subdirectories (e.g. 0060383860479$\to$006/038/386/0479/1.jpg), preventing image delivery 404s.
  • Global Error Boundary: Client-side rendering exceptions are intercepted gracefully by ErrorBoundary, offering immediate recovery actions without blank screens.
  • Production Deployment: The project compiles to standard static assets in dist/ that can be hosted on any static platform (Cloudflare Pages, Vercel, Netlify, AWS S3 / CloudFront, or NGINX) with client-side routing fallback to /index.html.

Documentation & Guidelines

  • CONTRIBUTORS.md — Comprehensive contributing workflow, component conventions, testing practices, and accessibility rules.
  • docs/design-references/ — Design system assets, reference screenshots, and visual specifications.

Open Food Facts Attribution

This application interfaces with open product records provided by Open Food Facts, published under the Open Database License (ODbL). Individual product images and brand assets remain the property of their respective copyright holders under Open Food Facts contributor terms.

Ask-OFF is an independent community discovery project and is not operated by the Open Food Facts organization.


License

This project is licensed under the Apache 2.0 License.


🌱 Building intelligent, transparent food discovery for Open Food Facts Canada.

About

AskOFF WebApp - An open-source web application for discovering, searching, comparing, and exploring Open Food Facts products with a clean, user-friendly experience.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

Ask-OFF WebApp

Intelligent Food Discovery & Conversational UI for Open Food Facts Canada

A high-performance, accessible web application for natural language food search, structured nutritional comparison, and grounded food inquiry across 124,145 Canadian Open Food Facts products.


ReactTypeScriptViteTailwindCSSTanStack QueryVitestOpen Food Facts


Overview

Ask-OFF WebApp is the official open-source frontend application for the Ask-OFF Canada ecosystem. It provides an intuitive, high-speed, and responsive interface designed to explore, analyze, and compare 124,145 Canadian grocery and packaged food products.

While traditional food catalogs present rigid, unstructured text fields, Ask-OFF delivers an interactive web experience powered by deterministic natural language search, official French/English bilingual product records, and evidence-grounded conversational inquiry.

Primary Objectives

  • Conversational & Keyword Food Discovery: Instant natural language exploration coupled with debounced, race-condition-free autocomplete and query match highlighting.
  • Deterministic Nutritional Transparency: Render official vector Nutri-Score gauges (A to E), NOVA industrial food processing groups (1 to 4), Eco-Scores, allergens, and per-100g nutritional facts tables.
  • Side-by-Side Nutritional Comparison: Multi-product matrix evaluating 2 to 4 food items simultaneously with macro-nutrient deltas and persistent browser storage.
  • Ask-OFF Assistant: Grounded conversational assistant (available via full page or floating action button) that queries the backend retrieval engine and provides verifiable citations to official Open Food Facts records.
  • Zero-Latency Route Navigation: Route-level code splitting using React.lazy and Suspense to deliver minimal initial JavaScript payloads and lightning-fast transitions.
  • Universal Accessibility (a11y): Built from the ground up with keyboard navigation, visible focus indicators, screen-reader semantics, and full prefers-reduced-motion accommodation.

System Architecture

Ask-OFF WebApp operates as a decoupled single-page application (SPA) communicating over HTTPS REST APIs with the FastAPI retrieval backend.

flowchart TD
subgraph Browser_Client [Client Browser — AskOFF WebApp]
A[User Interaction] --> B[React Router 7 Declarative Routes]
B --> C[Pages & Lazy-Loaded Chunks]
C --> D[Shared Reusable UI Components]
D --> E[TanStack Query Cache]
D --> F[Local State: CompareContext & ListsContext]
end
subgraph API_Layer [Frontend API Boundary]
E --> G[Typed REST Client: src/api/client.ts]
G --> H[Vite Development Proxy /api or Production HTTPS]
end
subgraph Backend_Infrastructure [Ask-OFF Backend — offCanada]
H --> I[FastAPI REST Gateway]
I --> J[Query Understanding & Intent Pipeline]
J --> K[(OpenSearch 2.x BM25 Cluster<br/>124,145 Canadian Records)]
end
Loading

Technology Stack

LayerTechnologiesPurpose
Frontend CoreReact 19.2+, TypeScript 6.0+, Vite 8.1+Ultra-fast modern web application with strict type safety and sub-second HMR
State & Data Fetching@tanstack/react-query 5.101+, Context APIServer-state caching, stale-while-revalidate, request deduplication, and local storage
Routing & Code SplittingReact Router 7.18+, React.lazy, SuspenseDeclarative route matching with discrete asynchronous chunks per route
Styling & Design SystemTailwindCSS 3.4+, PostCSS 8, AutoprefixerUtility-first responsive styling with custom typography, focus styling, and scrollbars
Icons & MediaLucide React 1.24+, SVG Vector GraphicsClean, accessible vector icons and dynamic Nutri-Score/NOVA badges
Testing SuiteVitest 4.1+, @testing-library/react, JSDOMComprehensive unit and component behavior test suites (24/24 tests passing)
Static Code AnalysisOxlint, TypeScript Compiler (tsc -b)Sub-second linter execution and zero-error strict type verification

Application Pages & Key Features

RoutePage ComponentKey Functionality
/ or /discoverLandingPageHero search, dynamic typewriter placeholders, brand chips, category highlights, and catalog showcase
/searchSearchPageSearch results grid, 9 dietary & health filter toggles, sort dropdown, responsive drawer, and pagination
/product/:idProductDetailsPageFull product specifications, 4-tier CDN images, nutrition facts per 100g, ingredients, allergens, and labels
/compareComparePageSide-by-side comparison table for 2–4 products with nutrient difference calculations and quick product picker
/offbotOffBotPageDedicated conversational food assistant with verifiable ODbL citations, grounded evidence cards, and follow-ups
/listsListsPageSaved grocery lists, favorites, and dietary bookmarks with print and JSON export capabilities
/recipesRecipesPageCurated recipe directory with ingredients linked directly to live search queries and barcode tokens
/extensionsExtensionsPageEcosystem showcase for community plugins, allergy advisors, barcode scanners, and grocery integrations
/statusDashboardPageLive backend service health check, OpenSearch connection status, record count, and autocomplete testing sandbox
/aboutAboutPageProject mission, architecture pillars, Open Food Facts partnership, and Open Database License (ODbL) attribution

Reusable Component Architecture

The frontend is structured around reusable, accessible presentation components:

src/components/
├── ErrorBoundary.tsx # Catches unhandled render errors with user-friendly recovery UI
├── OffBotChat.tsx # Core conversational chat engine, message list & ODbL citations
├── OffBotWidget.tsx # Spherical Floating Action Button (FAB) launcher and popup chat modal
├── ProductCard.tsx # Food product card with image, brand, Nutri-Score & macro stats
├── ProductImage.tsx # Resilient image loader with 4-tier Open Food Facts CDN normalization
├── ProductImagePlaceholder.tsx # Category-aware SVG fallback placeholder for items without images
├── NutritionTable.tsx # Structured nutrition table per 100g with European & Canadian standards
├── NutriScoreLogo.tsx # Official vector SVG Nutri-Score spectrum graphic (A to E)
├── NutriScoreBadge.tsx # Compact Nutri-Score pills, NOVA Group cards (1 to 4) & Eco-Score badges
├── SearchBar.tsx # Search input with debounced autocomplete, typewriter & AbortSignal
├── FilterSidebar.tsx # Desktop sidebar and mobile slide-over drawer for dietary filters
├── Pagination.tsx # Accessible page navigator with previous/next controls
├── LoadingSkeleton.tsx # Animated skeleton loaders for cards, detail sheets, and grids
├── EmptyState.tsx # Friendly empty states with recommended actions
├── ErrorState.tsx # Connection error displays with retry callbacks
└── ScrollToTop.tsx # Window scroll reset upon route navigation

Project Structure

AskOFF-WebApp/
├── public/ # Static public assets
│ ├── favicon.svg # Web application favicon
│ ├── icons.svg # SVG sprite sheet
│ └── logo.png # Project logo asset
├── docs/
│ └── design-references/ # Design artifacts, reference screenshots & visual mockups
│ ├── A.jpg
│ ├── B.jpg
│ ├── C.jpg
│ ├── image (1).png
│ ├── image.png
│ ├── Nutri-score-A.webp
│ └── Screenshot 2026-08-26 180122.png
├── src/
│ ├── api/
│ │ ├── client.ts # Typed REST API client, timeout handling & CDN normalization
│ │ └── assistantService.ts # Conversational query synthesis & citation models
│ ├── assets/ # Optimized vector icons and project logo
│ │ ├── app_qr.svg
│ │ ├── hero.png
│ │ ├── logo.png
│ │ └── phone_app.png
│ ├── components/ # 16 reusable UI and utility components
│ ├── constants/
│ │ └── queries.ts # Default sample queries and animated typewriter phrases
│ ├── context/
│ │ ├── AssistantContext.tsx # Chat state, conversation history, and active product focus
│ │ ├── CompareContext.tsx # Multi-product comparison state (stored in localStorage)
│ │ └── ListsContext.tsx # Favorites, shopping list & saved items (stored in localStorage)
│ ├── pages/ # 10 route page components
│ ├── tests/ # Vitest unit and React Testing Library component tests
│ │ ├── assistantService.test.ts
│ │ ├── badges.test.ts
│ │ ├── client.test.ts
│ │ ├── components.test.tsx
│ │ └── productCard.test.ts
│ ├── App.tsx # Main router, ErrorBoundary, QueryClient, and Providers
│ ├── index.css # Tailwind directives, focus-visible styles, and custom scrollbars
│ └── main.tsx # Application entry point (React StrictMode)
├── .env.example # Template for frontend environment variables
├── .gitignore # Git exclusion rules
├── .oxlintrc.json # Oxlint linter configuration
├── package.json # Project manifest, scripts, and dependencies
├── package-lock.json # Locked dependency tree
├── postcss.config.js # PostCSS plugin configuration
├── tailwind.config.js # TailwindCSS theme and font configurations
├── tsconfig.json # TypeScript project configuration root
├── tsconfig.app.json # TypeScript browser configuration
├── tsconfig.node.json # TypeScript Node/Vite configuration
├── vite.config.ts # Vite configuration with proxy and Vitest settings
├── CONTRIBUTORS.md # Contributor guidelines and workflow
└── README.md # Project documentation

Quick Start

Prerequisites

  • Node.js: v18.0.0 or higher (v20 LTS recommended)
  • npm: v9.0.0 or higher
  • Ask-OFF Backend API: Running locally on http://127.0.0.1:8000 or hosted remotely

Installation & Local Setup

1. Clone Repository

git clone https://github.com/SaitejaKommi/AskOFF-WebApp.git
cd AskOFF-WebApp

2. Install Dependencies

npm install

3. Configure Environment Variables

Copy the example environment configuration:

cp .env.example .env
VariableDefaultDescription
VITE_API_BASE_URL/apiBase URL or proxy prefix for the Ask-OFF FastAPI backend. In development, Vite automatically proxies /api to http://127.0.0.1:8000.

Security Note: All VITE_* variables are bundled directly into client-side code and visible to anyone inspecting network requests. Never expose private passwords, tokens, or backend credentials in frontend environment variables.

4. Run Development Server

npm run dev

The application will launch with hot module replacement (HMR) at http://localhost:5173.


Running Tests & Quality Checks

The repository enforces strict code quality, type safety, and automated test coverage:

# 1. Run all 24 automated unit & component tests
npm run test# 2. Run Oxlint static analysis (0 errors)
npm run lint
# 3. Verify TypeScript compilation
npx tsc -b
# 4. Compile production build
npm run build
# 5. Preview production bundle locally
npm run preview

Test Coverage Highlights

  • Component Behavior: User-facing tests for ProductCard, NutritionTable, EmptyState, ErrorState, and ErrorBoundary via @testing-library/react.
  • API Client Utilities: Validation of per-100g nutrient extraction, 4-tier CDN URL normalization, and product image resolution.
  • Assistant Service: Context-aware nutritional reasoning, allergen extraction, and structured citation verification.
  • Nutritional Standards: Enforcement of Nutri-Score calculation bounds (A to E) and NOVA food processing classifications (1 to 4).

Performance & Production Readiness

  • Asynchronous Route Splitting: Route components are dynamically imported using React.lazy(), cutting the initial main bundle size down to 266 kB (81.5 kB gzip) and serving per-page JavaScript chunks on demand.
  • Race-Condition-Free Autocomplete: Search inputs use AbortController to automatically cancel in-flight HTTP requests when the user continues typing, preventing stale network responses from overwriting newer suggestions.
  • Open Food Facts CDN Tiering: Barcode images with 9+ digits are normalized into 4-tier subdirectories (e.g. 0060383860479$\to$006/038/386/0479/1.jpg), preventing image delivery 404s.
  • Global Error Boundary: Client-side rendering exceptions are intercepted gracefully by ErrorBoundary, offering immediate recovery actions without blank screens.
  • Production Deployment: The project compiles to standard static assets in dist/ that can be hosted on any static platform (Cloudflare Pages, Vercel, Netlify, AWS S3 / CloudFront, or NGINX) with client-side routing fallback to /index.html.

Documentation & Guidelines

  • CONTRIBUTORS.md — Comprehensive contributing workflow, component conventions, testing practices, and accessibility rules.
  • docs/design-references/ — Design system assets, reference screenshots, and visual specifications.

Open Food Facts Attribution

This application interfaces with open product records provided by Open Food Facts, published under the Open Database License (ODbL). Individual product images and brand assets remain the property of their respective copyright holders under Open Food Facts contributor terms.

Ask-OFF is an independent community discovery project and is not operated by the Open Food Facts organization.


License

This project is licensed under the Apache 2.0 License.


🌱 Building intelligent, transparent food discovery for Open Food Facts Canada.

About

AskOFF WebApp - An open-source web application for discovering, searching, comparing, and exploring Open Food Facts products with a clean, user-friendly experience.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Ask-OFF WebApp

Intelligent Food Discovery & Conversational UI for Open Food Facts Canada

A high-performance, accessible web application for natural language food search, structured nutritional comparison, and grounded food inquiry across 124,145 Canadian Open Food Facts products.


ReactTypeScriptViteTailwindCSSTanStack QueryVitestOpen Food Facts


Overview

Ask-OFF WebApp is the official open-source frontend application for the Ask-OFF Canada ecosystem. It provides an intuitive, high-speed, and responsive interface designed to explore, analyze, and compare 124,145 Canadian grocery and packaged food products.

While traditional food catalogs present rigid, unstructured text fields, Ask-OFF delivers an interactive web experience powered by deterministic natural language search, official French/English bilingual product records, and evidence-grounded conversational inquiry.

Primary Objectives

  • Conversational & Keyword Food Discovery: Instant natural language exploration coupled with debounced, race-condition-free autocomplete and query match highlighting.
  • Deterministic Nutritional Transparency: Render official vector Nutri-Score gauges (A to E), NOVA industrial food processing groups (1 to 4), Eco-Scores, allergens, and per-100g nutritional facts tables.
  • Side-by-Side Nutritional Comparison: Multi-product matrix evaluating 2 to 4 food items simultaneously with macro-nutrient deltas and persistent browser storage.
  • Ask-OFF Assistant: Grounded conversational assistant (available via full page or floating action button) that queries the backend retrieval engine and provides verifiable citations to official Open Food Facts records.
  • Zero-Latency Route Navigation: Route-level code splitting using React.lazy and Suspense to deliver minimal initial JavaScript payloads and lightning-fast transitions.
  • Universal Accessibility (a11y): Built from the ground up with keyboard navigation, visible focus indicators, screen-reader semantics, and full prefers-reduced-motion accommodation.

System Architecture

Ask-OFF WebApp operates as a decoupled single-page application (SPA) communicating over HTTPS REST APIs with the FastAPI retrieval backend.

flowchart TD
subgraph Browser_Client [Client Browser — AskOFF WebApp]
A[User Interaction] --> B[React Router 7 Declarative Routes]
B --> C[Pages & Lazy-Loaded Chunks]
C --> D[Shared Reusable UI Components]
D --> E[TanStack Query Cache]
D --> F[Local State: CompareContext & ListsContext]
end
subgraph API_Layer [Frontend API Boundary]
E --> G[Typed REST Client: src/api/client.ts]
G --> H[Vite Development Proxy /api or Production HTTPS]
end
subgraph Backend_Infrastructure [Ask-OFF Backend — offCanada]
H --> I[FastAPI REST Gateway]
I --> J[Query Understanding & Intent Pipeline]
J --> K[(OpenSearch 2.x BM25 Cluster<br/>124,145 Canadian Records)]
end
Loading

Technology Stack

LayerTechnologiesPurpose
Frontend CoreReact 19.2+, TypeScript 6.0+, Vite 8.1+Ultra-fast modern web application with strict type safety and sub-second HMR
State & Data Fetching@tanstack/react-query 5.101+, Context APIServer-state caching, stale-while-revalidate, request deduplication, and local storage
Routing & Code SplittingReact Router 7.18+, React.lazy, SuspenseDeclarative route matching with discrete asynchronous chunks per route
Styling & Design SystemTailwindCSS 3.4+, PostCSS 8, AutoprefixerUtility-first responsive styling with custom typography, focus styling, and scrollbars
Icons & MediaLucide React 1.24+, SVG Vector GraphicsClean, accessible vector icons and dynamic Nutri-Score/NOVA badges
Testing SuiteVitest 4.1+, @testing-library/react, JSDOMComprehensive unit and component behavior test suites (24/24 tests passing)
Static Code AnalysisOxlint, TypeScript Compiler (tsc -b)Sub-second linter execution and zero-error strict type verification

Application Pages & Key Features

RoutePage ComponentKey Functionality
/ or /discoverLandingPageHero search, dynamic typewriter placeholders, brand chips, category highlights, and catalog showcase
/searchSearchPageSearch results grid, 9 dietary & health filter toggles, sort dropdown, responsive drawer, and pagination
/product/:idProductDetailsPageFull product specifications, 4-tier CDN images, nutrition facts per 100g, ingredients, allergens, and labels
/compareComparePageSide-by-side comparison table for 2–4 products with nutrient difference calculations and quick product picker
/offbotOffBotPageDedicated conversational food assistant with verifiable ODbL citations, grounded evidence cards, and follow-ups
/listsListsPageSaved grocery lists, favorites, and dietary bookmarks with print and JSON export capabilities
/recipesRecipesPageCurated recipe directory with ingredients linked directly to live search queries and barcode tokens
/extensionsExtensionsPageEcosystem showcase for community plugins, allergy advisors, barcode scanners, and grocery integrations
/statusDashboardPageLive backend service health check, OpenSearch connection status, record count, and autocomplete testing sandbox
/aboutAboutPageProject mission, architecture pillars, Open Food Facts partnership, and Open Database License (ODbL) attribution

Reusable Component Architecture

The frontend is structured around reusable, accessible presentation components:

src/components/
├── ErrorBoundary.tsx # Catches unhandled render errors with user-friendly recovery UI
├── OffBotChat.tsx # Core conversational chat engine, message list & ODbL citations
├── OffBotWidget.tsx # Spherical Floating Action Button (FAB) launcher and popup chat modal
├── ProductCard.tsx # Food product card with image, brand, Nutri-Score & macro stats
├── ProductImage.tsx # Resilient image loader with 4-tier Open Food Facts CDN normalization
├── ProductImagePlaceholder.tsx # Category-aware SVG fallback placeholder for items without images
├── NutritionTable.tsx # Structured nutrition table per 100g with European & Canadian standards
├── NutriScoreLogo.tsx # Official vector SVG Nutri-Score spectrum graphic (A to E)
├── NutriScoreBadge.tsx # Compact Nutri-Score pills, NOVA Group cards (1 to 4) & Eco-Score badges
├── SearchBar.tsx # Search input with debounced autocomplete, typewriter & AbortSignal
├── FilterSidebar.tsx # Desktop sidebar and mobile slide-over drawer for dietary filters
├── Pagination.tsx # Accessible page navigator with previous/next controls
├── LoadingSkeleton.tsx # Animated skeleton loaders for cards, detail sheets, and grids
├── EmptyState.tsx # Friendly empty states with recommended actions
├── ErrorState.tsx # Connection error displays with retry callbacks
└── ScrollToTop.tsx # Window scroll reset upon route navigation

Project Structure

AskOFF-WebApp/
├── public/ # Static public assets
│ ├── favicon.svg # Web application favicon
│ ├── icons.svg # SVG sprite sheet
│ └── logo.png # Project logo asset
├── docs/
│ └── design-references/ # Design artifacts, reference screenshots & visual mockups
│ ├── A.jpg
│ ├── B.jpg
│ ├── C.jpg
│ ├── image (1).png
│ ├── image.png
│ ├── Nutri-score-A.webp
│ └── Screenshot 2026-08-26 180122.png
├── src/
│ ├── api/
│ │ ├── client.ts # Typed REST API client, timeout handling & CDN normalization
│ │ └── assistantService.ts # Conversational query synthesis & citation models
│ ├── assets/ # Optimized vector icons and project logo
│ │ ├── app_qr.svg
│ │ ├── hero.png
│ │ ├── logo.png
│ │ └── phone_app.png
│ ├── components/ # 16 reusable UI and utility components
│ ├── constants/
│ │ └── queries.ts # Default sample queries and animated typewriter phrases
│ ├── context/
│ │ ├── AssistantContext.tsx # Chat state, conversation history, and active product focus
│ │ ├── CompareContext.tsx # Multi-product comparison state (stored in localStorage)
│ │ └── ListsContext.tsx # Favorites, shopping list & saved items (stored in localStorage)
│ ├── pages/ # 10 route page components
│ ├── tests/ # Vitest unit and React Testing Library component tests
│ │ ├── assistantService.test.ts
│ │ ├── badges.test.ts
│ │ ├── client.test.ts
│ │ ├── components.test.tsx
│ │ └── productCard.test.ts
│ ├── App.tsx # Main router, ErrorBoundary, QueryClient, and Providers
│ ├── index.css # Tailwind directives, focus-visible styles, and custom scrollbars
│ └── main.tsx # Application entry point (React StrictMode)
├── .env.example # Template for frontend environment variables
├── .gitignore # Git exclusion rules
├── .oxlintrc.json # Oxlint linter configuration
├── package.json # Project manifest, scripts, and dependencies
├── package-lock.json # Locked dependency tree
├── postcss.config.js # PostCSS plugin configuration
├── tailwind.config.js # TailwindCSS theme and font configurations
├── tsconfig.json # TypeScript project configuration root
├── tsconfig.app.json # TypeScript browser configuration
├── tsconfig.node.json # TypeScript Node/Vite configuration
├── vite.config.ts # Vite configuration with proxy and Vitest settings
├── CONTRIBUTORS.md # Contributor guidelines and workflow
└── README.md # Project documentation

Quick Start

Prerequisites

  • Node.js: v18.0.0 or higher (v20 LTS recommended)
  • npm: v9.0.0 or higher
  • Ask-OFF Backend API: Running locally on http://127.0.0.1:8000 or hosted remotely

Installation & Local Setup

1. Clone Repository

git clone https://github.com/SaitejaKommi/AskOFF-WebApp.git
cd AskOFF-WebApp

2. Install Dependencies

npm install

3. Configure Environment Variables

Copy the example environment configuration:

cp .env.example .env
VariableDefaultDescription
VITE_API_BASE_URL/apiBase URL or proxy prefix for the Ask-OFF FastAPI backend. In development, Vite automatically proxies /api to http://127.0.0.1:8000.

Security Note: All VITE_* variables are bundled directly into client-side code and visible to anyone inspecting network requests. Never expose private passwords, tokens, or backend credentials in frontend environment variables.

4. Run Development Server

npm run dev

The application will launch with hot module replacement (HMR) at http://localhost:5173.


Running Tests & Quality Checks

The repository enforces strict code quality, type safety, and automated test coverage:

# 1. Run all 24 automated unit & component tests
npm run test# 2. Run Oxlint static analysis (0 errors)
npm run lint
# 3. Verify TypeScript compilation
npx tsc -b
# 4. Compile production build
npm run build
# 5. Preview production bundle locally
npm run preview

Test Coverage Highlights

  • Component Behavior: User-facing tests for ProductCard, NutritionTable, EmptyState, ErrorState, and ErrorBoundary via @testing-library/react.
  • API Client Utilities: Validation of per-100g nutrient extraction, 4-tier CDN URL normalization, and product image resolution.
  • Assistant Service: Context-aware nutritional reasoning, allergen extraction, and structured citation verification.
  • Nutritional Standards: Enforcement of Nutri-Score calculation bounds (A to E) and NOVA food processing classifications (1 to 4).

Performance & Production Readiness

  • Asynchronous Route Splitting: Route components are dynamically imported using React.lazy(), cutting the initial main bundle size down to 266 kB (81.5 kB gzip) and serving per-page JavaScript chunks on demand.
  • Race-Condition-Free Autocomplete: Search inputs use AbortController to automatically cancel in-flight HTTP requests when the user continues typing, preventing stale network responses from overwriting newer suggestions.
  • Open Food Facts CDN Tiering: Barcode images with 9+ digits are normalized into 4-tier subdirectories (e.g. 0060383860479$\to$006/038/386/0479/1.jpg), preventing image delivery 404s.
  • Global Error Boundary: Client-side rendering exceptions are intercepted gracefully by ErrorBoundary, offering immediate recovery actions without blank screens.
  • Production Deployment: The project compiles to standard static assets in dist/ that can be hosted on any static platform (Cloudflare Pages, Vercel, Netlify, AWS S3 / CloudFront, or NGINX) with client-side routing fallback to /index.html.

Documentation & Guidelines

  • CONTRIBUTORS.md — Comprehensive contributing workflow, component conventions, testing practices, and accessibility rules.
  • docs/design-references/ — Design system assets, reference screenshots, and visual specifications.

Open Food Facts Attribution

This application interfaces with open product records provided by Open Food Facts, published under the Open Database License (ODbL). Individual product images and brand assets remain the property of their respective copyright holders under Open Food Facts contributor terms.

Ask-OFF is an independent community discovery project and is not operated by the Open Food Facts organization.


License

This project is licensed under the Apache 2.0 License.


🌱 Building intelligent, transparent food discovery for Open Food Facts Canada.

About

AskOFF WebApp - An open-source web application for discovering, searching, comparing, and exploring Open Food Facts products with a clean, user-friendly experience.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

Ask-OFF WebApp

Intelligent Food Discovery & Conversational UI for Open Food Facts Canada

A high-performance, accessible web application for natural language food search, structured nutritional comparison, and grounded food inquiry across 124,145 Canadian Open Food Facts products.


ReactTypeScriptViteTailwindCSSTanStack QueryVitestOpen Food Facts


Overview

Ask-OFF WebApp is the official open-source frontend application for the Ask-OFF Canada ecosystem. It provides an intuitive, high-speed, and responsive interface designed to explore, analyze, and compare 124,145 Canadian grocery and packaged food products.

While traditional food catalogs present rigid, unstructured text fields, Ask-OFF delivers an interactive web experience powered by deterministic natural language search, official French/English bilingual product records, and evidence-grounded conversational inquiry.

Primary Objectives

  • Conversational & Keyword Food Discovery: Instant natural language exploration coupled with debounced, race-condition-free autocomplete and query match highlighting.
  • Deterministic Nutritional Transparency: Render official vector Nutri-Score gauges (A to E), NOVA industrial food processing groups (1 to 4), Eco-Scores, allergens, and per-100g nutritional facts tables.
  • Side-by-Side Nutritional Comparison: Multi-product matrix evaluating 2 to 4 food items simultaneously with macro-nutrient deltas and persistent browser storage.
  • Ask-OFF Assistant: Grounded conversational assistant (available via full page or floating action button) that queries the backend retrieval engine and provides verifiable citations to official Open Food Facts records.
  • Zero-Latency Route Navigation: Route-level code splitting using React.lazy and Suspense to deliver minimal initial JavaScript payloads and lightning-fast transitions.
  • Universal Accessibility (a11y): Built from the ground up with keyboard navigation, visible focus indicators, screen-reader semantics, and full prefers-reduced-motion accommodation.

System Architecture

Ask-OFF WebApp operates as a decoupled single-page application (SPA) communicating over HTTPS REST APIs with the FastAPI retrieval backend.

flowchart TD
subgraph Browser_Client [Client Browser — AskOFF WebApp]
A[User Interaction] --> B[React Router 7 Declarative Routes]
B --> C[Pages & Lazy-Loaded Chunks]
C --> D[Shared Reusable UI Components]
D --> E[TanStack Query Cache]
D --> F[Local State: CompareContext & ListsContext]
end
subgraph API_Layer [Frontend API Boundary]
E --> G[Typed REST Client: src/api/client.ts]
G --> H[Vite Development Proxy /api or Production HTTPS]
end
subgraph Backend_Infrastructure [Ask-OFF Backend — offCanada]
H --> I[FastAPI REST Gateway]
I --> J[Query Understanding & Intent Pipeline]
J --> K[(OpenSearch 2.x BM25 Cluster<br/>124,145 Canadian Records)]
end
Loading

Technology Stack

LayerTechnologiesPurpose
Frontend CoreReact 19.2+, TypeScript 6.0+, Vite 8.1+Ultra-fast modern web application with strict type safety and sub-second HMR
State & Data Fetching@tanstack/react-query 5.101+, Context APIServer-state caching, stale-while-revalidate, request deduplication, and local storage
Routing & Code SplittingReact Router 7.18+, React.lazy, SuspenseDeclarative route matching with discrete asynchronous chunks per route
Styling & Design SystemTailwindCSS 3.4+, PostCSS 8, AutoprefixerUtility-first responsive styling with custom typography, focus styling, and scrollbars
Icons & MediaLucide React 1.24+, SVG Vector GraphicsClean, accessible vector icons and dynamic Nutri-Score/NOVA badges
Testing SuiteVitest 4.1+, @testing-library/react, JSDOMComprehensive unit and component behavior test suites (24/24 tests passing)
Static Code AnalysisOxlint, TypeScript Compiler (tsc -b)Sub-second linter execution and zero-error strict type verification

Application Pages & Key Features

RoutePage ComponentKey Functionality
/ or /discoverLandingPageHero search, dynamic typewriter placeholders, brand chips, category highlights, and catalog showcase
/searchSearchPageSearch results grid, 9 dietary & health filter toggles, sort dropdown, responsive drawer, and pagination
/product/:idProductDetailsPageFull product specifications, 4-tier CDN images, nutrition facts per 100g, ingredients, allergens, and labels
/compareComparePageSide-by-side comparison table for 2–4 products with nutrient difference calculations and quick product picker
/offbotOffBotPageDedicated conversational food assistant with verifiable ODbL citations, grounded evidence cards, and follow-ups
/listsListsPageSaved grocery lists, favorites, and dietary bookmarks with print and JSON export capabilities
/recipesRecipesPageCurated recipe directory with ingredients linked directly to live search queries and barcode tokens
/extensionsExtensionsPageEcosystem showcase for community plugins, allergy advisors, barcode scanners, and grocery integrations
/statusDashboardPageLive backend service health check, OpenSearch connection status, record count, and autocomplete testing sandbox
/aboutAboutPageProject mission, architecture pillars, Open Food Facts partnership, and Open Database License (ODbL) attribution

Reusable Component Architecture

The frontend is structured around reusable, accessible presentation components:

src/components/
├── ErrorBoundary.tsx # Catches unhandled render errors with user-friendly recovery UI
├── OffBotChat.tsx # Core conversational chat engine, message list & ODbL citations
├── OffBotWidget.tsx # Spherical Floating Action Button (FAB) launcher and popup chat modal
├── ProductCard.tsx # Food product card with image, brand, Nutri-Score & macro stats
├── ProductImage.tsx # Resilient image loader with 4-tier Open Food Facts CDN normalization
├── ProductImagePlaceholder.tsx # Category-aware SVG fallback placeholder for items without images
├── NutritionTable.tsx # Structured nutrition table per 100g with European & Canadian standards
├── NutriScoreLogo.tsx # Official vector SVG Nutri-Score spectrum graphic (A to E)
├── NutriScoreBadge.tsx # Compact Nutri-Score pills, NOVA Group cards (1 to 4) & Eco-Score badges
├── SearchBar.tsx # Search input with debounced autocomplete, typewriter & AbortSignal
├── FilterSidebar.tsx # Desktop sidebar and mobile slide-over drawer for dietary filters
├── Pagination.tsx # Accessible page navigator with previous/next controls
├── LoadingSkeleton.tsx # Animated skeleton loaders for cards, detail sheets, and grids
├── EmptyState.tsx # Friendly empty states with recommended actions
├── ErrorState.tsx # Connection error displays with retry callbacks
└── ScrollToTop.tsx # Window scroll reset upon route navigation

Project Structure

AskOFF-WebApp/
├── public/ # Static public assets
│ ├── favicon.svg # Web application favicon
│ ├── icons.svg # SVG sprite sheet
│ └── logo.png # Project logo asset
├── docs/
│ └── design-references/ # Design artifacts, reference screenshots & visual mockups
│ ├── A.jpg
│ ├── B.jpg
│ ├── C.jpg
│ ├── image (1).png
│ ├── image.png
│ ├── Nutri-score-A.webp
│ └── Screenshot 2026-08-26 180122.png
├── src/
│ ├── api/
│ │ ├── client.ts # Typed REST API client, timeout handling & CDN normalization
│ │ └── assistantService.ts # Conversational query synthesis & citation models
│ ├── assets/ # Optimized vector icons and project logo
│ │ ├── app_qr.svg
│ │ ├── hero.png
│ │ ├── logo.png
│ │ └── phone_app.png
│ ├── components/ # 16 reusable UI and utility components
│ ├── constants/
│ │ └── queries.ts # Default sample queries and animated typewriter phrases
│ ├── context/
│ │ ├── AssistantContext.tsx # Chat state, conversation history, and active product focus
│ │ ├── CompareContext.tsx # Multi-product comparison state (stored in localStorage)
│ │ └── ListsContext.tsx # Favorites, shopping list & saved items (stored in localStorage)
│ ├── pages/ # 10 route page components
│ ├── tests/ # Vitest unit and React Testing Library component tests
│ │ ├── assistantService.test.ts
│ │ ├── badges.test.ts
│ │ ├── client.test.ts
│ │ ├── components.test.tsx
│ │ └── productCard.test.ts
│ ├── App.tsx # Main router, ErrorBoundary, QueryClient, and Providers
│ ├── index.css # Tailwind directives, focus-visible styles, and custom scrollbars
│ └── main.tsx # Application entry point (React StrictMode)
├── .env.example # Template for frontend environment variables
├── .gitignore # Git exclusion rules
├── .oxlintrc.json # Oxlint linter configuration
├── package.json # Project manifest, scripts, and dependencies
├── package-lock.json # Locked dependency tree
├── postcss.config.js # PostCSS plugin configuration
├── tailwind.config.js # TailwindCSS theme and font configurations
├── tsconfig.json # TypeScript project configuration root
├── tsconfig.app.json # TypeScript browser configuration
├── tsconfig.node.json # TypeScript Node/Vite configuration
├── vite.config.ts # Vite configuration with proxy and Vitest settings
├── CONTRIBUTORS.md # Contributor guidelines and workflow
└── README.md # Project documentation

Quick Start

Prerequisites

  • Node.js: v18.0.0 or higher (v20 LTS recommended)
  • npm: v9.0.0 or higher
  • Ask-OFF Backend API: Running locally on http://127.0.0.1:8000 or hosted remotely

Installation & Local Setup

1. Clone Repository

git clone https://github.com/SaitejaKommi/AskOFF-WebApp.git
cd AskOFF-WebApp

2. Install Dependencies

npm install

3. Configure Environment Variables

Copy the example environment configuration:

cp .env.example .env
VariableDefaultDescription
VITE_API_BASE_URL/apiBase URL or proxy prefix for the Ask-OFF FastAPI backend. In development, Vite automatically proxies /api to http://127.0.0.1:8000.

Security Note: All VITE_* variables are bundled directly into client-side code and visible to anyone inspecting network requests. Never expose private passwords, tokens, or backend credentials in frontend environment variables.

4. Run Development Server

npm run dev

The application will launch with hot module replacement (HMR) at http://localhost:5173.


Running Tests & Quality Checks

The repository enforces strict code quality, type safety, and automated test coverage:

# 1. Run all 24 automated unit & component tests
npm run test# 2. Run Oxlint static analysis (0 errors)
npm run lint
# 3. Verify TypeScript compilation
npx tsc -b
# 4. Compile production build
npm run build
# 5. Preview production bundle locally
npm run preview

Test Coverage Highlights

  • Component Behavior: User-facing tests for ProductCard, NutritionTable, EmptyState, ErrorState, and ErrorBoundary via @testing-library/react.
  • API Client Utilities: Validation of per-100g nutrient extraction, 4-tier CDN URL normalization, and product image resolution.
  • Assistant Service: Context-aware nutritional reasoning, allergen extraction, and structured citation verification.
  • Nutritional Standards: Enforcement of Nutri-Score calculation bounds (A to E) and NOVA food processing classifications (1 to 4).

Performance & Production Readiness

  • Asynchronous Route Splitting: Route components are dynamically imported using React.lazy(), cutting the initial main bundle size down to 266 kB (81.5 kB gzip) and serving per-page JavaScript chunks on demand.
  • Race-Condition-Free Autocomplete: Search inputs use AbortController to automatically cancel in-flight HTTP requests when the user continues typing, preventing stale network responses from overwriting newer suggestions.
  • Open Food Facts CDN Tiering: Barcode images with 9+ digits are normalized into 4-tier subdirectories (e.g. 0060383860479$\to$006/038/386/0479/1.jpg), preventing image delivery 404s.
  • Global Error Boundary: Client-side rendering exceptions are intercepted gracefully by ErrorBoundary, offering immediate recovery actions without blank screens.
  • Production Deployment: The project compiles to standard static assets in dist/ that can be hosted on any static platform (Cloudflare Pages, Vercel, Netlify, AWS S3 / CloudFront, or NGINX) with client-side routing fallback to /index.html.

Documentation & Guidelines

  • CONTRIBUTORS.md — Comprehensive contributing workflow, component conventions, testing practices, and accessibility rules.
  • docs/design-references/ — Design system assets, reference screenshots, and visual specifications.

Open Food Facts Attribution

This application interfaces with open product records provided by Open Food Facts, published under the Open Database License (ODbL). Individual product images and brand assets remain the property of their respective copyright holders under Open Food Facts contributor terms.

Ask-OFF is an independent community discovery project and is not operated by the Open Food Facts organization.


License

This project is licensed under the Apache 2.0 License.


🌱 Building intelligent, transparent food discovery for Open Food Facts Canada.

About

AskOFF WebApp - An open-source web application for discovering, searching, comparing, and exploring Open Food Facts products with a clean, user-friendly experience.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

Ask-OFF WebApp

Intelligent Food Discovery & Conversational UI for Open Food Facts Canada

A high-performance, accessible web application for natural language food search, structured nutritional comparison, and grounded food inquiry across 124,145 Canadian Open Food Facts products.


ReactTypeScriptViteTailwindCSSTanStack QueryVitestOpen Food Facts


Overview

Ask-OFF WebApp is the official open-source frontend application for the Ask-OFF Canada ecosystem. It provides an intuitive, high-speed, and responsive interface designed to explore, analyze, and compare 124,145 Canadian grocery and packaged food products.

While traditional food catalogs present rigid, unstructured text fields, Ask-OFF delivers an interactive web experience powered by deterministic natural language search, official French/English bilingual product records, and evidence-grounded conversational inquiry.

Primary Objectives

  • Conversational & Keyword Food Discovery: Instant natural language exploration coupled with debounced, race-condition-free autocomplete and query match highlighting.
  • Deterministic Nutritional Transparency: Render official vector Nutri-Score gauges (A to E), NOVA industrial food processing groups (1 to 4), Eco-Scores, allergens, and per-100g nutritional facts tables.
  • Side-by-Side Nutritional Comparison: Multi-product matrix evaluating 2 to 4 food items simultaneously with macro-nutrient deltas and persistent browser storage.
  • Ask-OFF Assistant: Grounded conversational assistant (available via full page or floating action button) that queries the backend retrieval engine and provides verifiable citations to official Open Food Facts records.
  • Zero-Latency Route Navigation: Route-level code splitting using React.lazy and Suspense to deliver minimal initial JavaScript payloads and lightning-fast transitions.
  • Universal Accessibility (a11y): Built from the ground up with keyboard navigation, visible focus indicators, screen-reader semantics, and full prefers-reduced-motion accommodation.

System Architecture

Ask-OFF WebApp operates as a decoupled single-page application (SPA) communicating over HTTPS REST APIs with the FastAPI retrieval backend.

flowchart TD
subgraph Browser_Client [Client Browser — AskOFF WebApp]
A[User Interaction] --> B[React Router 7 Declarative Routes]
B --> C[Pages & Lazy-Loaded Chunks]
C --> D[Shared Reusable UI Components]
D --> E[TanStack Query Cache]
D --> F[Local State: CompareContext & ListsContext]
end
subgraph API_Layer [Frontend API Boundary]
E --> G[Typed REST Client: src/api/client.ts]
G --> H[Vite Development Proxy /api or Production HTTPS]
end
subgraph Backend_Infrastructure [Ask-OFF Backend — offCanada]
H --> I[FastAPI REST Gateway]
I --> J[Query Understanding & Intent Pipeline]
J --> K[(OpenSearch 2.x BM25 Cluster<br/>124,145 Canadian Records)]
end
Loading

Technology Stack

LayerTechnologiesPurpose
Frontend CoreReact 19.2+, TypeScript 6.0+, Vite 8.1+Ultra-fast modern web application with strict type safety and sub-second HMR
State & Data Fetching@tanstack/react-query 5.101+, Context APIServer-state caching, stale-while-revalidate, request deduplication, and local storage
Routing & Code SplittingReact Router 7.18+, React.lazy, SuspenseDeclarative route matching with discrete asynchronous chunks per route
Styling & Design SystemTailwindCSS 3.4+, PostCSS 8, AutoprefixerUtility-first responsive styling with custom typography, focus styling, and scrollbars
Icons & MediaLucide React 1.24+, SVG Vector GraphicsClean, accessible vector icons and dynamic Nutri-Score/NOVA badges
Testing SuiteVitest 4.1+, @testing-library/react, JSDOMComprehensive unit and component behavior test suites (24/24 tests passing)
Static Code AnalysisOxlint, TypeScript Compiler (tsc -b)Sub-second linter execution and zero-error strict type verification

Application Pages & Key Features

RoutePage ComponentKey Functionality
/ or /discoverLandingPageHero search, dynamic typewriter placeholders, brand chips, category highlights, and catalog showcase
/searchSearchPageSearch results grid, 9 dietary & health filter toggles, sort dropdown, responsive drawer, and pagination
/product/:idProductDetailsPageFull product specifications, 4-tier CDN images, nutrition facts per 100g, ingredients, allergens, and labels
/compareComparePageSide-by-side comparison table for 2–4 products with nutrient difference calculations and quick product picker
/offbotOffBotPageDedicated conversational food assistant with verifiable ODbL citations, grounded evidence cards, and follow-ups
/listsListsPageSaved grocery lists, favorites, and dietary bookmarks with print and JSON export capabilities
/recipesRecipesPageCurated recipe directory with ingredients linked directly to live search queries and barcode tokens
/extensionsExtensionsPageEcosystem showcase for community plugins, allergy advisors, barcode scanners, and grocery integrations
/statusDashboardPageLive backend service health check, OpenSearch connection status, record count, and autocomplete testing sandbox
/aboutAboutPageProject mission, architecture pillars, Open Food Facts partnership, and Open Database License (ODbL) attribution

Reusable Component Architecture

The frontend is structured around reusable, accessible presentation components:

src/components/
├── ErrorBoundary.tsx # Catches unhandled render errors with user-friendly recovery UI
├── OffBotChat.tsx # Core conversational chat engine, message list & ODbL citations
├── OffBotWidget.tsx # Spherical Floating Action Button (FAB) launcher and popup chat modal
├── ProductCard.tsx # Food product card with image, brand, Nutri-Score & macro stats
├── ProductImage.tsx # Resilient image loader with 4-tier Open Food Facts CDN normalization
├── ProductImagePlaceholder.tsx # Category-aware SVG fallback placeholder for items without images
├── NutritionTable.tsx # Structured nutrition table per 100g with European & Canadian standards
├── NutriScoreLogo.tsx # Official vector SVG Nutri-Score spectrum graphic (A to E)
├── NutriScoreBadge.tsx # Compact Nutri-Score pills, NOVA Group cards (1 to 4) & Eco-Score badges
├── SearchBar.tsx # Search input with debounced autocomplete, typewriter & AbortSignal
├── FilterSidebar.tsx # Desktop sidebar and mobile slide-over drawer for dietary filters
├── Pagination.tsx # Accessible page navigator with previous/next controls
├── LoadingSkeleton.tsx # Animated skeleton loaders for cards, detail sheets, and grids
├── EmptyState.tsx # Friendly empty states with recommended actions
├── ErrorState.tsx # Connection error displays with retry callbacks
└── ScrollToTop.tsx # Window scroll reset upon route navigation

Project Structure

AskOFF-WebApp/
├── public/ # Static public assets
│ ├── favicon.svg # Web application favicon
│ ├── icons.svg # SVG sprite sheet
│ └── logo.png # Project logo asset
├── docs/
│ └── design-references/ # Design artifacts, reference screenshots & visual mockups
│ ├── A.jpg
│ ├── B.jpg
│ ├── C.jpg
│ ├── image (1).png
│ ├── image.png
│ ├── Nutri-score-A.webp
│ └── Screenshot 2026-08-26 180122.png
├── src/
│ ├── api/
│ │ ├── client.ts # Typed REST API client, timeout handling & CDN normalization
│ │ └── assistantService.ts # Conversational query synthesis & citation models
│ ├── assets/ # Optimized vector icons and project logo
│ │ ├── app_qr.svg
│ │ ├── hero.png
│ │ ├── logo.png
│ │ └── phone_app.png
│ ├── components/ # 16 reusable UI and utility components
│ ├── constants/
│ │ └── queries.ts # Default sample queries and animated typewriter phrases
│ ├── context/
│ │ ├── AssistantContext.tsx # Chat state, conversation history, and active product focus
│ │ ├── CompareContext.tsx # Multi-product comparison state (stored in localStorage)
│ │ └── ListsContext.tsx # Favorites, shopping list & saved items (stored in localStorage)
│ ├── pages/ # 10 route page components
│ ├── tests/ # Vitest unit and React Testing Library component tests
│ │ ├── assistantService.test.ts
│ │ ├── badges.test.ts
│ │ ├── client.test.ts
│ │ ├── components.test.tsx
│ │ └── productCard.test.ts
│ ├── App.tsx # Main router, ErrorBoundary, QueryClient, and Providers
│ ├── index.css # Tailwind directives, focus-visible styles, and custom scrollbars
│ └── main.tsx # Application entry point (React StrictMode)
├── .env.example # Template for frontend environment variables
├── .gitignore # Git exclusion rules
├── .oxlintrc.json # Oxlint linter configuration
├── package.json # Project manifest, scripts, and dependencies
├── package-lock.json # Locked dependency tree
├── postcss.config.js # PostCSS plugin configuration
├── tailwind.config.js # TailwindCSS theme and font configurations
├── tsconfig.json # TypeScript project configuration root
├── tsconfig.app.json # TypeScript browser configuration
├── tsconfig.node.json # TypeScript Node/Vite configuration
├── vite.config.ts # Vite configuration with proxy and Vitest settings
├── CONTRIBUTORS.md # Contributor guidelines and workflow
└── README.md # Project documentation

Quick Start

Prerequisites

  • Node.js: v18.0.0 or higher (v20 LTS recommended)
  • npm: v9.0.0 or higher
  • Ask-OFF Backend API: Running locally on http://127.0.0.1:8000 or hosted remotely

Installation & Local Setup

1. Clone Repository

git clone https://github.com/SaitejaKommi/AskOFF-WebApp.git
cd AskOFF-WebApp

2. Install Dependencies

npm install

3. Configure Environment Variables

Copy the example environment configuration:

cp .env.example .env
VariableDefaultDescription
VITE_API_BASE_URL/apiBase URL or proxy prefix for the Ask-OFF FastAPI backend. In development, Vite automatically proxies /api to http://127.0.0.1:8000.

Security Note: All VITE_* variables are bundled directly into client-side code and visible to anyone inspecting network requests. Never expose private passwords, tokens, or backend credentials in frontend environment variables.

4. Run Development Server

npm run dev

The application will launch with hot module replacement (HMR) at http://localhost:5173.


Running Tests & Quality Checks

The repository enforces strict code quality, type safety, and automated test coverage:

# 1. Run all 24 automated unit & component tests
npm run test# 2. Run Oxlint static analysis (0 errors)
npm run lint
# 3. Verify TypeScript compilation
npx tsc -b
# 4. Compile production build
npm run build
# 5. Preview production bundle locally
npm run preview

Test Coverage Highlights

  • Component Behavior: User-facing tests for ProductCard, NutritionTable, EmptyState, ErrorState, and ErrorBoundary via @testing-library/react.
  • API Client Utilities: Validation of per-100g nutrient extraction, 4-tier CDN URL normalization, and product image resolution.
  • Assistant Service: Context-aware nutritional reasoning, allergen extraction, and structured citation verification.
  • Nutritional Standards: Enforcement of Nutri-Score calculation bounds (A to E) and NOVA food processing classifications (1 to 4).

Performance & Production Readiness

  • Asynchronous Route Splitting: Route components are dynamically imported using React.lazy(), cutting the initial main bundle size down to 266 kB (81.5 kB gzip) and serving per-page JavaScript chunks on demand.
  • Race-Condition-Free Autocomplete: Search inputs use AbortController to automatically cancel in-flight HTTP requests when the user continues typing, preventing stale network responses from overwriting newer suggestions.
  • Open Food Facts CDN Tiering: Barcode images with 9+ digits are normalized into 4-tier subdirectories (e.g. 0060383860479$\to$006/038/386/0479/1.jpg), preventing image delivery 404s.
  • Global Error Boundary: Client-side rendering exceptions are intercepted gracefully by ErrorBoundary, offering immediate recovery actions without blank screens.
  • Production Deployment: The project compiles to standard static assets in dist/ that can be hosted on any static platform (Cloudflare Pages, Vercel, Netlify, AWS S3 / CloudFront, or NGINX) with client-side routing fallback to /index.html.

Documentation & Guidelines

  • CONTRIBUTORS.md — Comprehensive contributing workflow, component conventions, testing practices, and accessibility rules.
  • docs/design-references/ — Design system assets, reference screenshots, and visual specifications.

Open Food Facts Attribution

This application interfaces with open product records provided by Open Food Facts, published under the Open Database License (ODbL). Individual product images and brand assets remain the property of their respective copyright holders under Open Food Facts contributor terms.

Ask-OFF is an independent community discovery project and is not operated by the Open Food Facts organization.


License

This project is licensed under the Apache 2.0 License.


🌱 Building intelligent, transparent food discovery for Open Food Facts Canada.

About

AskOFF WebApp - An open-source web application for discovering, searching, comparing, and exploring Open Food Facts products with a clean, user-friendly experience.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages