Skip to content

feat: add landing page, Malaysian food database, infinite scroll, and… - #1

Merged
rekabytes merged 1 commit into
mainfrom
dev
Dec 24, 2025
Merged

feat: add landing page, Malaysian food database, infinite scroll, and…#1
rekabytes merged 1 commit into
mainfrom
dev

Conversation

@rekabytes

@rekabytes rekabytes commented Dec 24, 2025

Copy link
Copy Markdown
Owner

… README

  • Add modern landing page with 10 sections (hero, features, testimonials, FAQ, etc.)
  • Add 97 Malaysian foods with categories to database
  • Implement infinite scroll pagination (load 10, more on scroll)
  • Add category filter on search page
  • Create comprehensive contributor README with branch strategy
  • Update color scheme to green/dark theme

Summary by CodeRabbit

  • New Features

    • Added search and browse interface with category filtering and infinite scrolling
    • New API endpoints for paginated food listings and category retrieval
    • Expanded food database with categorized Malaysian cuisines
  • Documentation

    • Comprehensive README with project overview, setup instructions, and contribution guidelines
  • Style

    • Redesigned color system and typography
    • New UI components (Button, Container, SectionHeading)
    • Landing page with Hero, Features, FAQ, Testimonials, and Call-to-Action sections

✏️ Tip: You can customize this high-level summary in your review settings.

… README

- Add modern landing page with 10 sections (hero, features, testimonials, FAQ, etc.)
- Add 97 Malaysian foods with categories to database
- Implement infinite scroll pagination (load 10, more on scroll)
- Add category filter on search page
- Create comprehensive contributor README with branch strategy
- Update color scheme to green/dark theme
@coderabbitai

coderabbitai Bot commented Dec 24, 2025

Copy link
Copy Markdown
📝 Walkthrough

Walkthrough

This pull request introduces comprehensive food search and categorization features across the entire stack. Backend adds paginated and category endpoints; database seeding expands to categorized Malaysian foods with indexing; frontend introduces a SearchPage with infinite scroll, a landing page composed of reusable sections, and new UI primitives. Documentation is provided via README.

Changes

Cohort / File(s) Summary
Backend Food API
packages/kal-backend/src/routers/food.ts
Added allPaginated endpoint (cursor-based pagination with optional category filtering) and categories endpoint (returns distinct category list); extended all endpoint to include category field in response
Database Seeding
packages/kal-db/scripts/seed.ts, packages/kal-db/scripts/seed-safe.ts
Replaced flat food dataset with comprehensive categorized Malaysian foods (Rice, Noodles, Roti, Meat, Seafood, Vegetables, Soups, Snacks, Desserts, Drinks, Common Ingredients); added index creation on category field; enhanced seed.ts to support DATABASE_URL environment variable with dynamic database name derivation
Home & Landing Page
packages/kal-frontend/src/app/page.tsx
Transformed from interactive food search component to static LandingPage composition; removed TRPC data fetching and replaced with modular landing section components
Search & Browse Page
packages/kal-frontend/src/app/search/page.tsx
New client-side page implementing dual-mode interface: debounced search (1.5s) via trpc.food.search and infinite-paginated browse via trpc.food.allPaginated with IntersectionObserver; category filtering; detail modal on food selection; loading states with skeletons
Landing Section Components
packages/kal-frontend/src/components/landing/ {Navbar, Hero, ProblemSolution, HowItWorks, Features, SampleFoods, Testimonials, FAQ, CTA}.tsx
Nine new landing page section components: navigation bar with mobile menu, hero with stats, problem/solution comparison, step-by-step guide, feature cards, sample foods grid, testimonials carousel, FAQ accordion, and final call-to-action
UI Component Primitives
packages/kal-frontend/src/components/ui/ {Button, Container, SectionHeading}.tsx
Three new reusable UI components: Button (supports Link or button with variant/size props); Container (responsive max-width wrapper with size presets); SectionHeading (title + optional subtitle with centering control)
Global Styling
packages/kal-frontend/src/app/globals.css
Replaced old foreground/background RGB color system with new semantic CSS variables (bg, surface, border, text, accents); removed dark-mode media query; added universal border-box sizing; enhanced accessibility with :focus-visible outline and selection styling; enabled smooth scrolling
Design Tokens
packages/kal-frontend/tailwind.config.ts
Updated semantic color palette (dark, accent, content groups); added custom animations (fade-in, slide-up with corresponding keyframes); introduced sans fontFamily configuration
Documentation
README.md
New comprehensive project documentation covering features, tech stack, project structure, getting started guide (prerequisites, installation, environment setup, running migrations/seeding), branch strategy, API endpoints overview, testing, deployment, contributing guidelines, and contributors

Sequence Diagram

sequenceDiagram
    actor User
    participant SearchUI as SearchPage
    participant State as Component State
    participant TRPC as TRPC Client
    participant Backend as Food API
    participant DB as Database

    User->>SearchUI: Load search page
    SearchUI->>SearchUI: Initialize categories query
    TRPC->>Backend: categoriesQuery()
    Backend->>DB: Fetch distinct categories
    DB-->>Backend: [categories]
    Backend-->>TRPC: [categories]
    TRPC-->>SearchUI: Render category filter

    alt User searches for food
        User->>SearchUI: Type search query
        SearchUI->>State: Debounce input (1.5s)
        Note over State: After 1.5s delay
        State->>TRPC: searchQuery(term)
        TRPC->>Backend: food.search(term)
        Backend->>DB: Find foods by name
        DB-->>Backend: [matching foods]
        Backend-->>TRPC: Results
        TRPC-->>SearchUI: Render search results
    else User selects category
        User->>SearchUI: Click category filter
        State->>TRPC: allPaginatedQuery(cursor=0, limit, category)
        TRPC->>Backend: food.allPaginated()
        Backend->>DB: Fetch foods + count
        DB-->>Backend: [foods], total
        Backend-->>TRPC: Items, nextCursor, total
        TRPC-->>SearchUI: Render paginated results
    end

    User->>SearchUI: Scroll near bottom
    SearchUI->>SearchUI: IntersectionObserver triggers
    State->>TRPC: fetchNextPage(cursor)
    TRPC->>Backend: allPaginated(nextCursor, limit)
    Backend->>DB: Fetch next batch
    DB-->>Backend: [foods]
    Backend-->>TRPC: Results
    TRPC-->>SearchUI: Append to results grid

    User->>SearchUI: Click food card
    SearchUI->>SearchUI: Open detail modal
    SearchUI->>User: Show macro breakdown, serving info
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~22 minutes

Poem

🐰 A feast of features hops into view,
Categories sorted, search anew,
Landing pages dance with infinite scroll,
UI blocks stack toward the goal,
Malaysian meals find their place,
In this tracker's growing space!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: add landing page, Malaysian food database, infinite scroll, and…' directly aligns with the main changes: a new landing page with 10 sections, categorized Malaysian foods dataset, infinite scroll pagination, and category filtering.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch dev

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (12)
README.md (2)

32-44: Add language identifier to the fenced code block.

The code block lacks a language identifier, which impacts syntax highlighting and accessibility.

🔎 Proposed fix
-```
+```text
 Kal-Monorepo/
 ├── packages/

221-227: Add language identifier to the fenced code block.

The code block lacks a language identifier for proper rendering.

🔎 Proposed fix
-```
+```text
 feat: add new feature
 fix: bug fix
packages/kal-frontend/src/components/ui/Container.tsx (1)

7-24: Consider minor optimizations for maintainability.

The component works correctly, but two optional refinements could improve it:

  1. Move the sizes object outside the component to avoid recreation on each render
  2. Consider using a className utility like clsx or cn for better className composition
🔎 Proposed refactor
+const sizes = {
+  sm: "max-w-2xl",
+  md: "max-w-4xl",
+  lg: "max-w-5xl",
+  xl: "max-w-6xl",
+};
+
 export function Container({
   children,
   className = "",
   size = "lg",
 }: ContainerProps) {
-  const sizes = {
-    sm: "max-w-2xl",
-    md: "max-w-4xl",
-    lg: "max-w-5xl",
-    xl: "max-w-6xl",
-  };

   return (
     <div className={`mx-auto px-6 ${sizes[size]} ${className}`}>
       {children}
     </div>
   );
 }
packages/kal-frontend/src/components/ui/Button.tsx (1)

12-38: Consider using a className utility for better composition.

Similar to the Container component, consider extracting the variants and sizes objects outside the component and using a className utility like clsx or cn for more robust className composition, especially when dealing with conditional classes.

packages/kal-db/scripts/seed-safe.ts (1)

9-158: Consider extracting shared code to reduce duplication.

The helper functions (getDatabaseUri, getDbName) and the entire food dataset are duplicated between seed.ts and seed-safe.ts. This creates a maintenance burden where changes must be synchronized across both files.

Consider creating a shared module (e.g., seed-common.ts) that exports:

  • The helper functions
  • The food dataset
  • Shared database connection logic

Both seed scripts can then import from this common module.

packages/kal-frontend/src/components/landing/Testimonials.tsx (1)

39-41: Consider adding accessibility attributes to the decorative SVG.

The quote icon is decorative and should include aria-hidden="true" to prevent it from being announced by screen readers.

🔎 Suggested improvement
-                <svg className="w-8 h-8 text-accent/30 mb-4" fill="currentColor" viewBox="0 0 24 24">
+                <svg className="w-8 h-8 text-accent/30 mb-4" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                  <path d="M14.017 21v-7.391c0-5.704 3.731-9.57 8.983-10.609l.995 2.151c-2.432.917-3.995 3.638-3.995 5.849h4v10h-9.983zm-14.017 0v-7.391c0-5.704 3.748-9.57 9-10.609l.996 2.151c-2.433.917-3.996 3.638-3.996 5.849h3.983v10h-9.983z" />
                </svg>
packages/kal-frontend/src/components/landing/CTA.tsx (1)

17-19: Consider adding accessibility attributes to the decorative SVG.

The arrow icon is decorative and should include aria-hidden="true" to prevent screen readers from announcing it separately from the button text.

🔎 Suggested improvement
-            <svg className="w-5 h-5 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+            <svg className="w-5 h-5 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
              <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
            </svg>
packages/kal-frontend/src/components/landing/FAQ.tsx (1)

43-58: Consider enhancing accordion accessibility.

The accordion buttons should include ARIA attributes to properly convey state to assistive technologies. The chevron SVG should be marked as decorative.

🔎 Suggested improvements
              <button
                onClick={() => setOpenIndex(openIndex === index ? null : index)}
+               aria-expanded={openIndex === index}
+               aria-controls={`faq-answer-${index}`}
                className="w-full px-6 py-5 flex items-center justify-between text-left hover:bg-dark-elevated/50 transition-colors"
              >
                <span className="font-medium text-content-primary">{faq.question}</span>
                <svg
                  className={`w-5 h-5 text-content-muted transition-transform duration-200 ${
                    openIndex === index ? "rotate-180" : ""
                  }`}
                  fill="none"
                  stroke="currentColor"
                  viewBox="0 0 24 24"
+                 aria-hidden="true"
                >
                  <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" />
                </svg>
              </button>
              {openIndex === index && (
-               <div className="px-6 pb-5">
+               <div id={`faq-answer-${index}`} className="px-6 pb-5">
                  <p className="text-content-secondary leading-relaxed">{faq.answer}</p>
                </div>
              )}
packages/kal-frontend/src/components/landing/Features.tsx (1)

4-59: Consider adding accessibility attributes to decorative SVGs.

All the feature icons are decorative and should include aria-hidden="true" to prevent them from being announced separately by screen readers, since the feature titles and descriptions already convey the meaning.

🔎 Example for one icon
    icon: (
-     <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+     <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
        <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={1.5} d="M13 10V3L4 14h7v7l9-11h-7z" />
      </svg>
    ),

Apply this pattern to all six feature icons in the array.

packages/kal-frontend/src/components/landing/Hero.tsx (1)

39-41: Consider adding accessibility attributes to the decorative SVG.

The arrow icon is decorative and should include aria-hidden="true" to prevent screen readers from announcing it separately from the button text.

🔎 Suggested improvement
-              <svg className="w-5 h-5 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+              <svg className="w-5 h-5 ml-2" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M17 8l4 4m0 0l-4 4m4-4H3" />
              </svg>
packages/kal-frontend/src/components/landing/Navbar.tsx (1)

48-59: Consider enhancing mobile menu accessibility.

The mobile menu toggle button should include ARIA attributes to properly convey its state and purpose to assistive technologies. The SVG icons should be marked as decorative.

🔎 Suggested improvements
          <button
            onClick={() => setMobileMenuOpen(!mobileMenuOpen)}
+           aria-expanded={mobileMenuOpen}
+           aria-label="Toggle navigation menu"
            className="md:hidden p-2 text-content-secondary hover:text-content-primary"
          >
-           <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24">
+           <svg className="w-6 h-6" fill="none" stroke="currentColor" viewBox="0 0 24 24" aria-hidden="true">
              {mobileMenuOpen ? (
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
              ) : (
                <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
              )}
            </svg>
          </button>
packages/kal-frontend/src/components/ui/SectionHeading.tsx (1)

19-23: Apply mx-auto conditionally based on centered prop.

The subtitle paragraph always applies mx-auto (line 20), which horizontally centers the element block even when centered={false}. This creates a layout inconsistency where left-aligned text is rendered within a centered container, potentially causing unexpected spacing or alignment issues.

🔎 Proposed fix
      {subtitle && (
-        <p className="text-content-secondary text-lg max-w-2xl mx-auto">
+        <p className={`text-content-secondary text-lg max-w-2xl ${centered ? 'mx-auto' : ''}`}>
          {subtitle}
        </p>
      )}
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between fe1b886 and f99b4c7.

📒 Files selected for processing (21)
  • README.md
  • packages/kal-backend/src/routers/food.ts
  • packages/kal-db/scripts/seed-safe.ts
  • packages/kal-db/scripts/seed.ts
  • packages/kal-frontend/src/app/globals.css
  • packages/kal-frontend/src/app/page.tsx
  • packages/kal-frontend/src/app/search/page.tsx
  • packages/kal-frontend/src/components/landing/CTA.tsx
  • packages/kal-frontend/src/components/landing/FAQ.tsx
  • packages/kal-frontend/src/components/landing/Features.tsx
  • packages/kal-frontend/src/components/landing/Footer.tsx
  • packages/kal-frontend/src/components/landing/Hero.tsx
  • packages/kal-frontend/src/components/landing/HowItWorks.tsx
  • packages/kal-frontend/src/components/landing/Navbar.tsx
  • packages/kal-frontend/src/components/landing/ProblemSolution.tsx
  • packages/kal-frontend/src/components/landing/SampleFoods.tsx
  • packages/kal-frontend/src/components/landing/Testimonials.tsx
  • packages/kal-frontend/src/components/ui/Button.tsx
  • packages/kal-frontend/src/components/ui/Container.tsx
  • packages/kal-frontend/src/components/ui/SectionHeading.tsx
  • packages/kal-frontend/tailwind.config.ts
🧰 Additional context used
🧬 Code graph analysis (12)
packages/kal-frontend/src/components/landing/Testimonials.tsx (2)
packages/kal-frontend/src/components/ui/Container.tsx (1)
  • Container (7-24)
packages/kal-frontend/src/components/ui/SectionHeading.tsx (1)
  • SectionHeading (8-26)
packages/kal-frontend/src/components/landing/Navbar.tsx (2)
packages/kal-frontend/src/components/ui/Container.tsx (1)
  • Container (7-24)
packages/kal-frontend/src/components/ui/Button.tsx (1)
  • Button (12-53)
packages/kal-frontend/src/components/landing/ProblemSolution.tsx (2)
packages/kal-frontend/src/components/ui/Container.tsx (1)
  • Container (7-24)
packages/kal-frontend/src/components/ui/SectionHeading.tsx (1)
  • SectionHeading (8-26)
packages/kal-frontend/src/components/landing/HowItWorks.tsx (2)
packages/kal-frontend/src/components/ui/Container.tsx (1)
  • Container (7-24)
packages/kal-frontend/src/components/ui/SectionHeading.tsx (1)
  • SectionHeading (8-26)
packages/kal-frontend/src/components/landing/FAQ.tsx (2)
packages/kal-frontend/src/components/ui/Container.tsx (1)
  • Container (7-24)
packages/kal-frontend/src/components/ui/SectionHeading.tsx (1)
  • SectionHeading (8-26)
packages/kal-frontend/src/app/page.tsx (10)
packages/kal-frontend/src/components/landing/Navbar.tsx (1)
  • Navbar (14-85)
packages/kal-frontend/src/components/landing/Hero.tsx (1)
  • Hero (10-65)
packages/kal-frontend/src/components/landing/ProblemSolution.tsx (1)
  • ProblemSolution (18-65)
packages/kal-frontend/src/components/landing/HowItWorks.tsx (1)
  • HowItWorks (37-77)
packages/kal-frontend/src/components/landing/Features.tsx (1)
  • Features (61-94)
packages/kal-frontend/src/components/landing/SampleFoods.tsx (1)
  • SampleFoods (14-64)
packages/kal-frontend/src/components/landing/Testimonials.tsx (1)
  • Testimonials (22-63)
packages/kal-frontend/src/components/landing/FAQ.tsx (1)
  • FAQ (26-70)
packages/kal-frontend/src/components/landing/CTA.tsx (1)
  • FinalCTA (4-25)
packages/kal-frontend/src/components/landing/Footer.tsx (1)
  • Footer (4-28)
packages/kal-frontend/src/components/landing/SampleFoods.tsx (3)
packages/kal-frontend/src/components/ui/Container.tsx (1)
  • Container (7-24)
packages/kal-frontend/src/components/ui/SectionHeading.tsx (1)
  • SectionHeading (8-26)
packages/kal-frontend/src/components/ui/Button.tsx (1)
  • Button (12-53)
packages/kal-frontend/src/app/search/page.tsx (1)
packages/kal-frontend/src/lib/trpc.ts (1)
  • trpc (5-5)
packages/kal-frontend/src/components/landing/Hero.tsx (2)
packages/kal-frontend/src/components/ui/Container.tsx (1)
  • Container (7-24)
packages/kal-frontend/src/components/ui/Button.tsx (1)
  • Button (12-53)
packages/kal-frontend/src/components/landing/CTA.tsx (2)
packages/kal-frontend/src/components/ui/Container.tsx (1)
  • Container (7-24)
packages/kal-frontend/src/components/ui/Button.tsx (1)
  • Button (12-53)
packages/kal-frontend/src/components/landing/Footer.tsx (1)
packages/kal-frontend/src/components/ui/Container.tsx (1)
  • Container (7-24)
packages/kal-backend/src/routers/food.ts (1)
packages/kal-backend/src/lib/trpc.ts (1)
  • publicProcedure (7-7)
🪛 LanguageTool
README.md

[grammar] ~3-~3: Use a hyphen to join words.
Context: ...lorie Tracker 🥗 A simple, fast calorie tracking app focused on Malaysian cuisin...

(QB_NEW_EN_HYPHEN)

🪛 markdownlint-cli2 (0.18.1)
README.md

32-32: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


221-221: Fenced code blocks should have a language specified

(MD040, fenced-code-language)


243-243: Emphasis used instead of a heading

(MD036, no-emphasis-as-heading)

🔇 Additional comments (16)
packages/kal-frontend/tailwind.config.ts (1)

11-48: LGTM! Well-structured design token system.

The semantic color palette (dark, accent, content) provides clear naming and good organization. The animations are standard and appropriate for modern UI transitions.

packages/kal-frontend/src/app/globals.css (1)

5-44: LGTM! Well-structured CSS with good accessibility practices.

The CSS variable system aligns well with the Tailwind configuration. Good inclusion of accessibility features like :focus-visible and thoughtful touches like selection styling and font smoothing.

packages/kal-db/scripts/seed.ts (3)

20-30: Verify URL parsing handles edge cases correctly.

The database name extraction from DATABASE_URL may not handle all edge cases properly. If the pathname is just / or empty, pathname.slice(1).split("?")[0] would return an empty string rather than falling back to "kal".

🔎 Suggested improvement
 const getDbName = () => {
   if (process.env.DATABASE_URL) {
     try {
       const url = new URL(process.env.DATABASE_URL);
-      return url.pathname.slice(1).split("?")[0];
+      const dbName = url.pathname.slice(1).split("?")[0];
+      return dbName || "kal";
     } catch {
       return "kal";
     }
   }
   return MONGODB_DATABASE || "kal";
 };

33-152: LGTM! Comprehensive and well-structured food database.

The Malaysian food dataset is extensive (97 items) with consistent schema across all entries. Category organization is logical and complete.


176-179: Good addition of category index for query performance.

Creating an index on the category field will optimize the category filter queries mentioned in the PR objectives.

packages/kal-db/scripts/seed-safe.ts (1)

187-189: Good consistency with seed.ts for category indexing.

The category index creation is consistent with the main seed script, ensuring query performance for category filtering in both seeding scenarios.

packages/kal-frontend/src/components/landing/Footer.tsx (1)

4-28: LGTM! Clean and well-structured Footer component.

The component uses semantic HTML, responsive layout, and dynamic copyright year generation. Good integration with the shared Container component.

packages/kal-frontend/src/components/landing/ProblemSolution.tsx (1)

18-65: LGTM!

The problem-solution comparison is well-structured with clear visual distinction between the two sections. The use of contrasting colors and icons effectively communicates the concept.

packages/kal-frontend/src/components/landing/SampleFoods.tsx (1)

38-51: LGTM! Good use of text labels alongside color indicators.

The macro breakdown uses colored dots alongside text labels (P:, C:, F:), which ensures the information is accessible even if colors cannot be perceived. This follows accessibility best practices.

packages/kal-frontend/src/app/page.tsx (1)

12-33: LGTM! Clean modular landing page composition.

The landing page effectively composes all section components with proper structure and decorative background effects. The modular approach makes the codebase maintainable and each section reusable.

packages/kal-backend/src/routers/food.ts (2)

43-82: LGTM! Pagination logic is correctly implemented.

The cursor-based pagination implementation is sound:

  • Parallel fetching of items and count optimizes performance
  • nextCursor calculation correctly handles edge cases
  • Optional category filtering integrates cleanly

84-90: Category field indexing is already in place.

Verification confirms that an index on the category field has been created in the foods collection. The seed scripts (packages/kal-db/scripts/seed.ts and seed-safe.ts) explicitly create this index with the comment "Create index on category for filtering", ensuring the distinct("category") query will perform efficiently as the dataset grows.

packages/kal-frontend/src/app/search/page.tsx (4)

18-42: LGTM! Search state management is well-implemented.

The debounced search with Enter-to-search functionality is correctly implemented:

  • Proper cleanup of debounce timer
  • useCallback optimization for the key handler
  • Reasonable 1.5s delay balances UX and server load

55-68: LGTM! Infinite query setup follows React Query best practices.

The infinite query configuration is correct:

  • Properly disabled during search mode
  • nextCursor correctly extracted from backend response
  • initialCursor: 0 aligns with backend pagination
  • Query automatically resets when category filter changes (React Query handles this via input changes)

70-85: LGTM! IntersectionObserver implementation is correct.

The infinite scroll logic properly:

  • Disconnects observer on cleanup
  • Checks all necessary conditions before fetching (intersection, hasNextPage, not already fetching)
  • Includes searchQuery in dependencies to recreate observer when switching modes

158-187: Well-designed category filter with proper mode isolation.

The category filter correctly:

  • Only displays in browse mode (not during search)
  • Uses proper active state styling
  • Integrates with the infinite query, which automatically resets pagination when category changes

Comment on lines +48 to +71
<div key={index} className="text-center">
{/* Step number */}
<div className="relative inline-flex items-center justify-center mb-6">
<div className="w-16 h-16 rounded-2xl bg-dark-surface border border-dark-border flex items-center justify-center text-accent">
{step.icon}
</div>
<span className="absolute -top-2 -right-2 w-6 h-6 rounded-full bg-accent text-dark text-sm font-bold flex items-center justify-center">
{step.number}
</span>
</div>

{/* Content */}
<h3 className="text-xl font-semibold text-content-primary mb-2">
{step.title}
</h3>
<p className="text-content-secondary">
{step.description}
</p>

{/* Connector line (not on last) */}
{index < steps.length - 1 && (
<div className="hidden md:block absolute top-8 left-full w-full h-px bg-dark-border -translate-y-1/2" />
)}
</div>

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix positioning context for connector lines.

The connector line at lines 68-70 uses absolute positioning with left-full, but the parent div at line 48 lacks position: relative. This causes the line to position relative to the nearest positioned ancestor (likely the section or Container), not the step card itself, breaking the visual connection between steps.

🔎 Proposed fix
-          {steps.map((step, index) => (
-            <div key={index} className="text-center">
+          {steps.map((step, index) => (
+            <div key={index} className="text-center relative">
              {/* Step number */}
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<div key={index} className="text-center">
{/* Step number */}
<div className="relative inline-flex items-center justify-center mb-6">
<div className="w-16 h-16 rounded-2xl bg-dark-surface border border-dark-border flex items-center justify-center text-accent">
{step.icon}
</div>
<span className="absolute -top-2 -right-2 w-6 h-6 rounded-full bg-accent text-dark text-sm font-bold flex items-center justify-center">
{step.number}
</span>
</div>
{/* Content */}
<h3 className="text-xl font-semibold text-content-primary mb-2">
{step.title}
</h3>
<p className="text-content-secondary">
{step.description}
</p>
{/* Connector line (not on last) */}
{index < steps.length - 1 && (
<div className="hidden md:block absolute top-8 left-full w-full h-px bg-dark-border -translate-y-1/2" />
)}
</div>
{steps.map((step, index) => (
<div key={index} className="text-center relative">
{/* Step number */}
<div className="relative inline-flex items-center justify-center mb-6">
<div className="w-16 h-16 rounded-2xl bg-dark-surface border border-dark-border flex items-center justify-center text-accent">
{step.icon}
</div>
<span className="absolute -top-2 -right-2 w-6 h-6 rounded-full bg-accent text-dark text-sm font-bold flex items-center justify-center">
{step.number}
</span>
</div>
{/* Content */}
<h3 className="text-xl font-semibold text-content-primary mb-2">
{step.title}
</h3>
<p className="text-content-secondary">
{step.description}
</p>
{/* Connector line (not on last) */}
{index < steps.length - 1 && (
<div className="hidden md:block absolute top-8 left-full w-full h-px bg-dark-border -translate-y-1/2" />
)}
</div>
))}
🤖 Prompt for AI Agents
In packages/kal-frontend/src/components/landing/HowItWorks.tsx around lines 48
to 71, the connector line uses absolute positioning but its parent container
isn't positioned, so the line is positioned relative to an ancestor instead of
the step card; fix this by adding a positioning context (e.g., apply relative
positioning) to the parent div at line 48 so the absolute connector is anchored
to the step element, and verify layout on mobile and md breakpoints so the
connector only shows where intended.

Comment on lines +48 to +52
return (
<button onClick={onClick} className={styles}>
{children}
</button>
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion | 🟠 Major

Add explicit type="button" to prevent unintended form submissions.

The button element should include type="button" to prevent it from acting as a submit button when used inside forms.

🔎 Proposed fix
   return (
-    <button onClick={onClick} className={styles}>
+    <button type="button" onClick={onClick} className={styles}>
       {children}
     </button>
   );
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
return (
<button onClick={onClick} className={styles}>
{children}
</button>
);
return (
<button type="button" onClick={onClick} className={styles}>
{children}
</button>
);
🤖 Prompt for AI Agents
In packages/kal-frontend/src/components/ui/Button.tsx around lines 48 to 52 the
rendered <button> lacks an explicit type, which causes it to act as a submit
button when placed inside forms; update the JSX to include type="button" on the
button element so it will not trigger form submission (keep existing onClick and
className props unchanged).

@rekabytes
rekabytes merged commit 6e72b42 into main Dec 24, 2025
1 check passed
This was referenced Jan 2, 2026
Merged
@coderabbitai coderabbitai Bot mentioned this pull request Mar 14, 2026
13 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant