Skip to content

feat: search integration with backend catalogue#250

Merged
onerandomdevv merged 3 commits into
devfrom
feat/search-integration
Apr 6, 2026
Merged

feat: search integration with backend catalogue#250
onerandomdevv merged 3 commits into
devfrom
feat/search-integration

Conversation

@onerandomdevv
Copy link
Copy Markdown
Collaborator

@onerandomdevv onerandomdevv commented Apr 5, 2026

Summary by CodeRabbit

  • New Features

    • Interactive Explore page with searchable, category-filtered product listings, empty-state messaging, clear-filters action, and pagination.
    • Header search with debounced input and immediate Enter navigation.
    • Floating WhatsApp contact button for quick support.
  • Refactor

    • Pages now show improved loading fallbacks (skeletons/spinners) for smoother load transitions.
  • Style

    • Updated logo and wordmark visuals.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 5, 2026

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

Replaces the placeholder public explore page with a client-side ExplorePage that reads URL params, fetches a paginated catalogue via useQuery, and adds header, category bar, and WhatsApp FAB into a new public layout; also adds debounce hook and several new UI components. (≤50 words)

Changes

Cohort / File(s) Summary
Explore Page
apps/web/src/app/(public)/explore/page.tsx
Converted placeholder to client-side ExplorePage ("use client"). Reads search, category, page from URL, fetches catalogue via useQuery keyed on ["products","catalogue", search, category, page], renders loading/error/empty/product grid states, and adds pagination and clear-filters navigation.
Public Layout
apps/web/src/app/(public)/layout.tsx
Layout updated to a flex column; renders PublicHeader and CategoryBar inside Suspense fallbacks, uses main.className="flex-1" for content, and includes WhatsAppFab.
Header & Category UI
apps/web/src/components/layout/public-header.tsx, apps/web/src/components/layout/category-bar.tsx
Added PublicHeader (client) with debounced search input synchronized to search URL param and mobile expand behavior; added CategoryBar (client) rendering sticky, scrollable category pills that update query params/links.
Shared FAB & Logo
apps/web/src/components/shared/whatsapp-fab.tsx, apps/web/src/components/ui/logo.tsx
Added WhatsAppFab floating contact button with sanitized click-to-chat URL and animations; refactored Logo to use next/link, simplified sizing to a size map and swapped SVG for a text-based wordmark.
Hooks
apps/web/src/hooks/use-debounce.ts
Added useDebounce<T> hook (value + delay) returning debounced value with proper cleanup.
Suspense refactors (dashboard pages)
apps/web/src/app/(dashboard)/buyer/orders/confirmation/page.tsx, .../payment/callback/page.tsx, .../merchant/products/page.tsx
Wrapped existing page logic in React.Suspense and moved prior inline logic into new non-exported *Content components with loading fallbacks.
Package & tooling
package.json, apps/web/package.json, apps/backend/package.json, precommit.ps1
Added pnpm.overrides to root package.json; bumped next range in apps/web/package.json; narrowed ESLint target in backend package.json; changed precommit script directory handling and audit level.
Misc
apps/web/next-env.d.ts
Updated Next.js TypeScript doc comment URL (non-functional comment change).

Sequence Diagram

sequenceDiagram
    participant User
    participant PublicHeader
    participant useDebounce
    participant Router as BrowserRouter
    participant ExplorePage
    participant productApi
    participant UI as ProductGrid

    User->>PublicHeader: types/search input
    PublicHeader->>useDebounce: provide value
    Note over useDebounce: debounce (≈500ms)
    useDebounce->>Router: push/replace /explore?search=...
    Router->>ExplorePage: URL params update (search,category,page)
    ExplorePage->>productApi: useQuery(fetch catalogue with params)
    productApi-->>ExplorePage: returns paginated data
    ExplorePage->>UI: render grid / empty / error / pagination
    UI-->>User: display results / actions
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • Fix/e2e issues #200: Changes productApi.getCatalogue to return a paginated response and adds getPaginated — directly related to ExplorePage's catalogue fetching shape.

Poem

🐰
A rabbit sifts through search and page,
Filters hop along the cage,
Header hums, categories gleam,
WhatsApp waves in a friendly beam,
Twizrr blooms — the explore page sings! ✨

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The pull request description is entirely missing. The template requires key sections like 'What does this PR do?', 'Module(s) affected', 'Type of change', a checklist, and 'How to test'. Add a complete pull request description following the repository template, including the objective, affected modules, change type, checklist items, and testing instructions.
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: integrating search functionality with backend catalogue, matching the primary objective of the changeset.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/search-integration

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🧹 Nitpick comments (4)
apps/web/src/components/layout/public-header.tsx (1)

90-96: Camera search button is non-functional.

The button has an aria-label="Search by image" but no onClick handler. Consider either removing it for now or adding a TODO/disabled state to clarify it's a future feature.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/layout/public-header.tsx` around lines 90 - 96, The
Camera search button in the PublicHeader is non-functional because the <button>
with aria-label "Search by image" and the Camera component inside has no onClick
handler or disabled/placeholder state; either remove the button, mark it
visually and semantically disabled (add disabled attribute and aria-disabled or
a TODO tooltip/aria-describedby) or add a temporary onClick stub that opens a
placeholder modal/toast indicating the feature is pending; update the element
around the Camera component (the <button> in public-header.tsx / PublicHeader)
accordingly so accessibility labels match the disabled/placeholder behavior.
apps/web/src/components/shared/whatsapp-fab.tsx (1)

7-9: Move placeholder phone number to configuration.

The hardcoded phone number with "Replace with actual business number" comment should be moved to environment variables or a configuration file to avoid deploying placeholder values to production.

-  const whatsappNumber = "+2348012345678"; // Replace with actual business number
+  const whatsappNumber = process.env.NEXT_PUBLIC_WHATSAPP_NUMBER || "+2348012345678";
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/shared/whatsapp-fab.tsx` around lines 7 - 9, The
whatsappNumber is hardcoded in the whatsapp-fab component (const whatsappNumber)
creating a risk of shipping placeholder values; move this value into
configuration by reading it from an environment/config variable (e.g.,
process.env.NEXT_PUBLIC_WHATSAPP_NUMBER or a config utility) and fall back to a
safe default or disable the FAB if not set, then update whatsappUrl construction
(const whatsappUrl) to use the configured number and keep message (const
message) as-is or also configurable if desired; ensure any build-time/public env
variable uses the NEXT_PUBLIC prefix for client-side access and add a short
comment documenting the new env var.
apps/web/src/app/(public)/explore/page.tsx (2)

105-128: Pagination doesn't scale well for many pages.

Rendering a button for every page becomes unwieldy with large catalogs. Consider truncating with ellipsis (e.g., 1 2 ... 5 6 7 ... 41 42) or using prev/next buttons with a page indicator.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/`(public)/explore/page.tsx around lines 105 - 128, The
pagination currently renders a button per page (Array.from({ length: totalPages
}) ...) which doesn't scale; replace that full list with a truncated pagination
UI: compute a visible window of page numbers around the current page (use
symbols page and totalPages) and render first/last pages with ellipses between
gaps, plus Prev/Next buttons that use the existing router.push logic and
searchParams updates; update the rendering in the same component (where
totalPages, page, searchParams, router are used) to map only the computed pages
and ellipsis tokens to buttons/elements so large catalogs show something like "1
2 ... 5 6 7 ... 41 42" while preserving the existing onClick behavior and active
styling.

38-40: Category slug formatting doesn't handle hyphenated names well.

For slugs like "home-kitchen", the current formatting produces "Home-kitchen" instead of a proper display name. Consider using a lookup map or proper title-casing.

+const formatCategoryName = (slug: string) => 
+  slug.split('-').map(word => word.charAt(0).toUpperCase() + word.slice(1)).join(' & ');
+
 <h1 className="text-2xl font-black tracking-tight text-foreground md:text-3xl">
-  {search ? `Results for "${search}"` : category && category !== "all" ? `${category.charAt(0).toUpperCase() + category.slice(1)}` : "Explore Products"}
+  {search ? `Results for "${search}"` : category && category !== "all" ? formatCategoryName(category) : "Explore Products"}
 </h1>
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/app/`(public)/explore/page.tsx around lines 38 - 40, The heading
currently capitalizes only the first character of the category slug
(category.charAt(0).toUpperCase() + category.slice(1)), which leaves hyphenated
slugs like "home-kitchen" formatted as "Home-kitchen"; replace that logic with a
slug-to-title formatter: add a helper (e.g., formatCategorySlug) in page.tsx
that splits the category on hyphens, capitalizes each segment, and joins them
with spaces (or use a lookup map for custom display names), then use
formatCategorySlug(category) inside the <h1> instead of the existing one-char
capitalization.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@apps/web/src/app/`(public)/explore/page.tsx:
- Around line 1-14: The page component ExplorePage uses useSearchParams (and
useRouter) and must be rendered inside a React Suspense boundary to satisfy
Next.js App Router prerender rules; update ExplorePage's JSX to wrap the UI that
relies on useSearchParams (the return value of the ExplorePage function) in a
<React.Suspense fallback={...}> (use an appropriate fallback such as the
existing <Skeleton /> or simple placeholder) so that useSearchParams is executed
within the Suspense boundary; keep the existing import of React (import * as
React from "react") and reference React.Suspense when wrapping the content.

In `@apps/web/src/components/layout/category-bar.tsx`:
- Around line 8-19: Update the hardcoded CATEGORIES constant so its slug set
matches the backend seed data: replace/remove frontend-only slugs
("phones-gadgets", "sports-fitness", "baby-kids") and add the missing backend
slugs ("auto-parts", "agriculture", "office-stationery"), ensuring each entry in
the CATEGORIES array (symbol name: CATEGORIES) pairs the human-friendly name
with the exact slug used in backend seed.ts; verify spelling and casing against
apps/backend/src/prisma/seed.ts and adjust the display names if needed so UI
labels map correctly to backend categories.

In `@apps/web/src/components/layout/public-header.tsx`:
- Around line 23-29: The effect currently reads searchQuery but doesn't include
it in the dependency array, causing an ESLint warning; replace that dependency
pattern by tracking the last-seen URL value in a ref so the effect only depends
on searchParams: add a ref (e.g., const prevUrlRef = React.useRef<string>(''))
near the top of the component, then inside the React.useEffect that reads
searchParams compute const urlSearch = searchParams.get("search") || "" and
compare to prevUrlRef.current — if different call setSearchQuery(urlSearch) and
set prevUrlRef.current = urlSearch; keep the effect’s dependency array to
[searchParams] (no searchQuery) so the loop is avoided and the linter warning is
resolved.

In `@apps/web/src/components/shared/whatsapp-fab.tsx`:
- Around line 39-45: The tooltip never shows because the ancestor wrapper lacks
the required Tailwind "group" class and the tooltip relies on group-hover to
change opacity; update the outer wrapper element in the whatsapp-fab component
(the top-level container in WhatsAppFab / whatsapp-fab.tsx) to include the
"group" class, and adjust the tooltip div's classes: remove the conditional
"hidden" + "group-hover:block" pattern and instead use persistent layout (e.g.,
keep "lg:block") with "opacity-0 transition-opacity group-hover:opacity-100" so
the tooltip becomes visible on group hover.

In `@apps/web/src/components/ui/logo.tsx`:
- Line 8: The Logo component currently accepts a variant?: "light" | "dark" prop
(LogoProps / function Logo) but never uses it, causing wrong text colors; update
the Logo component to read variant (default "light") from props and apply
conditional classes when rendering the wordmark and subtitle (e.g., use
"text-foreground" for light and a light/white text class for dark) by switching
the className via your cn/classnames helper or a conditional template string
inside the Logo render; ensure both the main wordmark element and any small
subtitle/descriptor elements use the conditional class so callers passing
variant="dark" (e.g., login/register/admin pages) get proper contrast.

---

Nitpick comments:
In `@apps/web/src/app/`(public)/explore/page.tsx:
- Around line 105-128: The pagination currently renders a button per page
(Array.from({ length: totalPages }) ...) which doesn't scale; replace that full
list with a truncated pagination UI: compute a visible window of page numbers
around the current page (use symbols page and totalPages) and render first/last
pages with ellipses between gaps, plus Prev/Next buttons that use the existing
router.push logic and searchParams updates; update the rendering in the same
component (where totalPages, page, searchParams, router are used) to map only
the computed pages and ellipsis tokens to buttons/elements so large catalogs
show something like "1 2 ... 5 6 7 ... 41 42" while preserving the existing
onClick behavior and active styling.
- Around line 38-40: The heading currently capitalizes only the first character
of the category slug (category.charAt(0).toUpperCase() + category.slice(1)),
which leaves hyphenated slugs like "home-kitchen" formatted as "Home-kitchen";
replace that logic with a slug-to-title formatter: add a helper (e.g.,
formatCategorySlug) in page.tsx that splits the category on hyphens, capitalizes
each segment, and joins them with spaces (or use a lookup map for custom display
names), then use formatCategorySlug(category) inside the <h1> instead of the
existing one-char capitalization.

In `@apps/web/src/components/layout/public-header.tsx`:
- Around line 90-96: The Camera search button in the PublicHeader is
non-functional because the <button> with aria-label "Search by image" and the
Camera component inside has no onClick handler or disabled/placeholder state;
either remove the button, mark it visually and semantically disabled (add
disabled attribute and aria-disabled or a TODO tooltip/aria-describedby) or add
a temporary onClick stub that opens a placeholder modal/toast indicating the
feature is pending; update the element around the Camera component (the <button>
in public-header.tsx / PublicHeader) accordingly so accessibility labels match
the disabled/placeholder behavior.

In `@apps/web/src/components/shared/whatsapp-fab.tsx`:
- Around line 7-9: The whatsappNumber is hardcoded in the whatsapp-fab component
(const whatsappNumber) creating a risk of shipping placeholder values; move this
value into configuration by reading it from an environment/config variable
(e.g., process.env.NEXT_PUBLIC_WHATSAPP_NUMBER or a config utility) and fall
back to a safe default or disable the FAB if not set, then update whatsappUrl
construction (const whatsappUrl) to use the configured number and keep message
(const message) as-is or also configurable if desired; ensure any
build-time/public env variable uses the NEXT_PUBLIC prefix for client-side
access and add a short comment documenting the new env var.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 70f92be7-8ac9-4314-9de1-72e1a407a145

📥 Commits

Reviewing files that changed from the base of the PR and between 673e594 and 4710228.

📒 Files selected for processing (7)
  • apps/web/src/app/(public)/explore/page.tsx
  • apps/web/src/app/(public)/layout.tsx
  • apps/web/src/components/layout/category-bar.tsx
  • apps/web/src/components/layout/public-header.tsx
  • apps/web/src/components/shared/whatsapp-fab.tsx
  • apps/web/src/components/ui/logo.tsx
  • apps/web/src/hooks/use-debounce.ts

Comment thread apps/web/src/app/(public)/explore/page.tsx
Comment on lines +8 to +19
const CATEGORIES = [
{ name: "All", slug: "all" },
{ name: "Electronics", slug: "electronics" },
{ name: "Fashion", slug: "fashion" },
{ name: "Home & Kitchen", slug: "home-kitchen" },
{ name: "Health & Beauty", slug: "health-beauty" },
{ name: "Food & Groceries", slug: "food-groceries" },
{ name: "Phones & Gadgets", slug: "phones-gadgets" },
{ name: "Sports & Fitness", slug: "sports-fitness" },
{ name: "Baby & Kids", slug: "baby-kids" },
{ name: "Other", slug: "other" },
];
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Category slugs mismatch with backend database.

The hardcoded CATEGORIES array contains slugs that don't exist in the backend database, and is missing slugs that do exist. Based on the backend seed data at apps/backend/src/prisma/seed.ts:

Frontend has but backend doesn't:

  • phones-gadgets
  • sports-fitness
  • baby-kids

Backend has but frontend doesn't:

  • auto-parts
  • agriculture
  • office-stationery

Users selecting mismatched categories will get no results, and some backend categories are completely inaccessible.

Proposed fix to align with backend
 const CATEGORIES = [
   { name: "All", slug: "all" },
   { name: "Electronics", slug: "electronics" },
   { name: "Fashion", slug: "fashion" },
   { name: "Home & Kitchen", slug: "home-kitchen" },
   { name: "Health & Beauty", slug: "health-beauty" },
+  { name: "Auto Parts", slug: "auto-parts" },
+  { name: "Agriculture", slug: "agriculture" },
+  { name: "Office & Stationery", slug: "office-stationery" },
   { name: "Food & Groceries", slug: "food-groceries" },
-  { name: "Phones & Gadgets", slug: "phones-gadgets" },
-  { name: "Sports & Fitness", slug: "sports-fitness" },
-  { name: "Baby & Kids", slug: "baby-kids" },
+  { name: "Books & Media", slug: "books-media" },
   { name: "Other", slug: "other" },
 ];
📝 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
const CATEGORIES = [
{ name: "All", slug: "all" },
{ name: "Electronics", slug: "electronics" },
{ name: "Fashion", slug: "fashion" },
{ name: "Home & Kitchen", slug: "home-kitchen" },
{ name: "Health & Beauty", slug: "health-beauty" },
{ name: "Food & Groceries", slug: "food-groceries" },
{ name: "Phones & Gadgets", slug: "phones-gadgets" },
{ name: "Sports & Fitness", slug: "sports-fitness" },
{ name: "Baby & Kids", slug: "baby-kids" },
{ name: "Other", slug: "other" },
];
const CATEGORIES = [
{ name: "All", slug: "all" },
{ name: "Electronics", slug: "electronics" },
{ name: "Fashion", slug: "fashion" },
{ name: "Home & Kitchen", slug: "home-kitchen" },
{ name: "Health & Beauty", slug: "health-beauty" },
{ name: "Auto Parts", slug: "auto-parts" },
{ name: "Agriculture", slug: "agriculture" },
{ name: "Office & Stationery", slug: "office-stationery" },
{ name: "Food & Groceries", slug: "food-groceries" },
{ name: "Books & Media", slug: "books-media" },
{ name: "Other", slug: "other" },
];
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/layout/category-bar.tsx` around lines 8 - 19, Update
the hardcoded CATEGORIES constant so its slug set matches the backend seed data:
replace/remove frontend-only slugs ("phones-gadgets", "sports-fitness",
"baby-kids") and add the missing backend slugs ("auto-parts", "agriculture",
"office-stationery"), ensuring each entry in the CATEGORIES array (symbol name:
CATEGORIES) pairs the human-friendly name with the exact slug used in backend
seed.ts; verify spelling and casing against apps/backend/src/prisma/seed.ts and
adjust the display names if needed so UI labels map correctly to backend
categories.

Comment on lines +23 to +29
// Sync state with URL when search param changes externally (e.g. back button)
React.useEffect(() => {
const urlSearch = searchParams.get("search") || "";
if (urlSearch !== searchQuery) {
setSearchQuery(urlSearch);
}
}, [searchParams]);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix missing dependency causing ESLint warning.

The pipeline flags searchQuery as a missing dependency. Adding it directly would create an infinite loop since the effect sets searchQuery. Use a ref to track the previous URL value and avoid the warning.

Proposed fix
+  const prevUrlSearchRef = React.useRef(searchParams.get("search") || "");
+
   // Sync state with URL when search param changes externally (e.g. back button)
   React.useEffect(() => {
     const urlSearch = searchParams.get("search") || "";
-    if (urlSearch !== searchQuery) {
+    if (urlSearch !== prevUrlSearchRef.current) {
+      prevUrlSearchRef.current = urlSearch;
       setSearchQuery(urlSearch);
     }
-  }, [searchParams]);
+  }, [searchParams]);

Alternatively, you can disable the lint rule for this specific line with an explanatory comment:

   React.useEffect(() => {
     const urlSearch = searchParams.get("search") || "";
     if (urlSearch !== searchQuery) {
       setSearchQuery(urlSearch);
     }
+    // eslint-disable-next-line react-hooks/exhaustive-deps -- Only sync when URL changes externally
   }, [searchParams]);
📝 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
// Sync state with URL when search param changes externally (e.g. back button)
React.useEffect(() => {
const urlSearch = searchParams.get("search") || "";
if (urlSearch !== searchQuery) {
setSearchQuery(urlSearch);
}
}, [searchParams]);
const prevUrlSearchRef = React.useRef(searchParams.get("search") || "");
// Sync state with URL when search param changes externally (e.g. back button)
React.useEffect(() => {
const urlSearch = searchParams.get("search") || "";
if (urlSearch !== prevUrlSearchRef.current) {
prevUrlSearchRef.current = urlSearch;
setSearchQuery(urlSearch);
}
}, [searchParams]);
🧰 Tools
🪛 GitHub Actions: CI

[warning] 29-29: React Hook React.useEffect has a missing dependency: 'searchQuery'. (react-hooks/exhaustive-deps)

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/layout/public-header.tsx` around lines 23 - 29, The
effect currently reads searchQuery but doesn't include it in the dependency
array, causing an ESLint warning; replace that dependency pattern by tracking
the last-seen URL value in a ref so the effect only depends on searchParams: add
a ref (e.g., const prevUrlRef = React.useRef<string>('')) near the top of the
component, then inside the React.useEffect that reads searchParams compute const
urlSearch = searchParams.get("search") || "" and compare to prevUrlRef.current —
if different call setSearchQuery(urlSearch) and set prevUrlRef.current =
urlSearch; keep the effect’s dependency array to [searchParams] (no searchQuery)
so the loop is avoided and the linter warning is resolved.

Comment on lines +39 to +45
{/* Tooltip */}
<div className="absolute bottom-full right-0 mb-3 hidden lg:block">
<div className="rounded-lg bg-foreground px-3 py-1.5 text-sm font-medium text-background opacity-0 shadow-xl transition-opacity hover:opacity-100 group-hover:block">
Need help? Chat with us
<div className="absolute left-1/2 top-full -translate-x-1/2 border-8 border-transparent border-t-foreground"></div>
</div>
</div>
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Tooltip hover is broken - missing group class.

The tooltip uses group-hover:block but no ancestor element has the group class, so the tooltip will never become visible on hover.

Proposed fix
-    <div className="fixed bottom-6 right-6 z-50 flex items-center justify-center">
+    <div className="group fixed bottom-6 right-6 z-50 flex items-center justify-center">

And fix the tooltip opacity transition:

-        <div className="rounded-lg bg-foreground px-3 py-1.5 text-sm font-medium text-background opacity-0 shadow-xl transition-opacity hover:opacity-100 group-hover:block">
+        <div className="rounded-lg bg-foreground px-3 py-1.5 text-sm font-medium text-background opacity-0 shadow-xl transition-opacity group-hover:opacity-100">
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/shared/whatsapp-fab.tsx` around lines 39 - 45, The
tooltip never shows because the ancestor wrapper lacks the required Tailwind
"group" class and the tooltip relies on group-hover to change opacity; update
the outer wrapper element in the whatsapp-fab component (the top-level container
in WhatsAppFab / whatsapp-fab.tsx) to include the "group" class, and adjust the
tooltip div's classes: remove the conditional "hidden" + "group-hover:block"
pattern and instead use persistent layout (e.g., keep "lg:block") with
"opacity-0 transition-opacity group-hover:opacity-100" so the tooltip becomes
visible on group hover.

size?: "xs" | "sm" | "md" | "lg";
showWordmark?: boolean;
className?: string;
variant?: "light" | "dark";
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

variant prop is declared but never used, causing visual regression.

The variant prop exists in the interface and is defaulted to "light", but it's completely ignored in the rendering logic. Multiple callers pass variant="dark" expecting white/light-colored text on dark backgrounds:

  • apps/web/src/app/(auth)/register/page.tsx (dark gradient panel)
  • apps/web/src/app/(auth)/login/page.tsx (dark gradient panel)
  • apps/web/src/app/(auth)/admin/login/page.tsx (dark panel)

The wordmark always renders with text-foreground, which will have poor contrast on these dark backgrounds.

Proposed fix to implement variant styling
       {showWordmark && (
         <span
           className={cn(
             font,
-            "font-bold tracking-tight text-foreground font-inter"
+            "font-bold tracking-tight font-inter",
+            variant === "dark" ? "text-white" : "text-foreground"
           )}
         >
           Twizrr
         </span>
       )}

Also applies to: 15-15, 43-50

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@apps/web/src/components/ui/logo.tsx` at line 8, The Logo component currently
accepts a variant?: "light" | "dark" prop (LogoProps / function Logo) but never
uses it, causing wrong text colors; update the Logo component to read variant
(default "light") from props and apply conditional classes when rendering the
wordmark and subtitle (e.g., use "text-foreground" for light and a light/white
text class for dark) by switching the className via your cn/classnames helper or
a conditional template string inside the Logo render; ensure both the main
wordmark element and any small subtitle/descriptor elements use the conditional
class so callers passing variant="dark" (e.g., login/register/admin pages) get
proper contrast.

@socket-security
Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addednext@​14.2.355781949770
Addednext-themes@​0.4.61001009882100
Updatedmulter@​2.0.2 ⏵ 2.1.1100 +1100 +31100 +190100
Addednestjs-pino@​4.6.09910010093100

View full report

@onerandomdevv onerandomdevv merged commit be5802e into dev Apr 6, 2026
3 of 4 checks passed
@onerandomdevv onerandomdevv deleted the feat/search-integration branch April 6, 2026 02:53
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