Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 40 additions & 1 deletion components/Navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,16 @@
import Image from "next/image";
import Link from "next/link";
import { motion } from "framer-motion";
import { useState } from "react";
import { Menu, X } from "lucide-react";
import { useScrollHiddenNav } from "@/hooks/useScrollHiddenNav";
import ToggleButton from "@/components/ui/ToggleButton";
import ConnectBtn from "./ui/ConnectBtn";
import { SearchModal } from "./ui/SearchModal";

export default function Navbar() {
const isVisible = useScrollHiddenNav();
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false);

return (
<motion.div
Expand Down Expand Up @@ -37,11 +41,46 @@ export default function Navbar() {
dit
</span>
</Link>
<div className="flex items-center gap-3 md:gap-4">

<div className="hidden pe-3.5 md:flex-1 md:justify-end lg:flex">
<div className="w-full max-w-xs">
<SearchModal isOpen={false} onClose={() => {}} inline />
</div>
</div>
<div className="hidden items-center gap-3 md:gap-4 lg:flex">
<ToggleButton />
<ConnectBtn />
</div>

<div className="flex items-center gap-2 lg:hidden">
<ToggleButton />
<button
onClick={() => setIsMobileMenuOpen(!isMobileMenuOpen)}
className="rounded-full p-2 text-black transition-colors hover:bg-black/10 dark:text-white dark:hover:bg-white/10"
>
{isMobileMenuOpen ? <X size={20} /> : <Menu size={20} />}
</button>
</div>
</div>

{isMobileMenuOpen && (
<div className="border-t border-slate-200/10 bg-white/95 lg:hidden dark:border-slate-700/70 dark:bg-slate-950/95">
<div className="space-y-4 px-4 py-4">
<div className="flex items-center justify-end">
<div className="w-full max-w-xs">
<SearchModal
isOpen={false}
onClose={() => setIsMobileMenuOpen(false)}
mobile
/>
</div>
</div>
<div className="flex items-center justify-end gap-3">
<ConnectBtn />
</div>
</div>
</div>
)}
</motion.div>
);
}
180 changes: 180 additions & 0 deletions components/ui/SearchModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,180 @@
"use client";

import React, { useRef, useState, useCallback, useEffect } from "react";
import { X, Search, CornerDownLeft } from "lucide-react";
import { useRouter } from "next/navigation";

interface SearchModalProps {
isOpen: boolean;
onClose: () => void;
mobile?: boolean;
inline?: boolean;
}

export function SearchModal({
isOpen,
onClose,
mobile = false,
inline = false,
}: SearchModalProps) {
// useRef for debounce timer — no re-renders
const debounceRef = useRef<ReturnType<typeof setTimeout> | null>(null);
// useRef for input element (uncontrolled) — no re-render on every keystroke
const inputRef = useRef<HTMLInputElement>(null);
// Only ONE piece of state: whether input has content (drives the arrow button visibility)
const [hasValue, setHasValue] = useState(false);
const router = useRouter();

// Clean up timer on unmount
useEffect(() => {
return () => {
if (debounceRef.current) clearTimeout(debounceRef.current);
};
}, []);

const handleChange = useCallback(() => {
const value = inputRef.current?.value ?? "";

// Update arrow button visibility (single boolean — cheap)
setHasValue(value.trim().length > 0);

// Debounce the search API call using ref, not state
if (debounceRef.current) clearTimeout(debounceRef.current);
debounceRef.current = setTimeout(() => {
const trimmed = value.trim();
if (trimmed) {
console.log("Debounced search term:", trimmed);
// TODO: Add elasticsearch fetch here
}
}, 300);
Comment on lines +43 to +49
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.

🛠️ Refactor suggestion | 🟠 Major

Remove console.log before production.

Debug logging should be removed or replaced with a proper logging utility that can be disabled in production builds.

♻️ Proposed fix
    debounceRef.current = setTimeout(() => {
      const trimmed = value.trim();
      if (trimmed) {
-       console.log("Debounced search term:", trimmed);
        // TODO: Add elasticsearch fetch here
      }
    }, 300);
📝 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
debounceRef.current = setTimeout(() => {
const trimmed = value.trim();
if (trimmed) {
console.log("Debounced search term:", trimmed);
// TODO: Add elasticsearch fetch here
}
}, 300);
debounceRef.current = setTimeout(() => {
const trimmed = value.trim();
if (trimmed) {
// TODO: Add elasticsearch fetch here
}
}, 300);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/SearchModal.tsx` around lines 43 - 49, The debug console.log
inside the debounce timeout should be removed or replaced with a proper logger
call that can be disabled in production: update the setTimeout callback (the
debounceRef.current block that trims value) to remove console.log("Debounced
search term:", trimmed) and either call your app logging utility (e.g.,
logger.debug or debugLog) or guard the log behind a NODE_ENV check so it’s
omitted in production; ensure you keep the trimmed truthy check and the
placeholder for the elasticsearch fetch intact.

}, []);

const handleSubmit = useCallback(
(e?: React.FormEvent) => {
e?.preventDefault();
const value = inputRef.current?.value.trim();
if (!value) return;
router.push(`/${value}`);
onClose();
if (inputRef.current) inputRef.current.value = "";
setHasValue(false);
},
[router, onClose]
);
Comment on lines +52 to +63
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

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Check how the username parameter is used in the destination page
ast-grep --pattern 'params.$_.$_'
rg -n 'username' --type ts --type tsx -g '!node_modules'
cat app/\[username\]/page.tsx 2>/dev/null || fd -t f 'page.tsx' -x grep -l 'username' {}

Repository: StabilityNexus/IdentityTokens-EVM-Frontend

Length of output: 525


🏁 Script executed:

#!/bin/bash
# Get full SearchModal.tsx content to verify all mentioned issues
fd -t f 'SearchModal.tsx' | head -1 | xargs wc -l

Repository: StabilityNexus/IdentityTokens-EVM-Frontend

Length of output: 119


🏁 Script executed:

#!/bin/bash
# Read SearchModal.tsx to check for console.log, inline styles, and i18n issues
fd -t f 'SearchModal.tsx' | head -1 | xargs cat -n

Repository: StabilityNexus/IdentityTokens-EVM-Frontend

Length of output: 7327


🏁 Script executed:

#!/bin/bash
# Check if there are i18n files in the project
fd -t d 'locales\|i18n\|translations' | head -10

Repository: StabilityNexus/IdentityTokens-EVM-Frontend

Length of output: 68


Remove console.log, externalize strings to i18n, and replace hardcoded inline styles with Tailwind classes.

The code has three issues:

  1. console.log on line 46: Remove the production debug statement.

  2. Hardcoded user-visible strings: Lines 85, 154–175 contain hard-coded strings ("Search by username or Token ID…", "Search Identity", "Press Enter", etc.). Externalize these to an i18n resource file per the coding guidelines.

  3. Hardcoded inline styles (lines 137–140): Replace the inline style object with Tailwind classes or CSS variables for theme consistency:

    • backgroundColor: "#18191D" → Use a theme-aware background class
    • border: "1px solid rgba(255,255,255,0.06)" → Use a theme-aware border class

Also consider using encodeURIComponent() when constructing the navigation path on line 57 to ensure special characters in usernames are safely encoded.

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

In `@components/ui/SearchModal.tsx` around lines 52 - 63, In SearchModal, remove
any stray console.log debug statements, externalize all user-facing strings
(e.g., placeholders and labels like "Search by username or Token ID…", "Search
Identity", "Press Enter") into the i18n resource and replace inline usages with
i18n lookups, replace the inline style object used for the modal
background/border with appropriate Tailwind (or theme-aware) utility classes
(e.g., use a dark background class and a semi-transparent border class instead
of backgroundColor:"#18191D" and border:"1px solid rgba(255,255,255,0.06)"), and
update the handleSubmit function so router.push uses an encoded path
(router.push(`/${encodeURIComponent(value)}`)) while still clearing inputRef,
calling onClose, and updating setHasValue as before.


const handleKeyDown = useCallback(
(e: React.KeyboardEvent<HTMLInputElement>) => {
if (e.key === "Escape") onClose();
if (e.key === "Enter") handleSubmit();
},
[onClose, handleSubmit]
);

const searchInput = (
<form onSubmit={handleSubmit} className="relative max-w-xs">
{/* Search icon */}
<Search
className="pointer-events-none absolute top-1/2 left-3 h-4 w-4 -translate-y-1/2 text-slate-400 dark:text-slate-500"
aria-hidden
/>

{/* Uncontrolled input — no value prop, no re-render per keystroke */}
<input
ref={inputRef}
type="text"
placeholder="Search by username or Token ID..."
onChange={handleChange}
onKeyDown={handleKeyDown}
autoFocus={!mobile && !inline}
className={[
"w-full rounded-full border py-2 pr-10 pl-10 text-base",
"bg-white text-slate-950 placeholder-slate-500",
"dark:bg-slate-950/95 dark:text-white dark:placeholder-slate-400",
"border-slate-300/80 dark:border-slate-800",
"outline-0 transition-all duration-150",
inline ? "max-w-sm" : "",
]
.filter(Boolean)
.join(" ")}
/>

{/* Enter / submit arrow button — visible only when input has content */}
<button
type="submit"
aria-label="Submit search"
className={[
"absolute top-1/2 right-3 -translate-y-1/2",
"flex items-center justify-center",
"h-8 w-8 rounded-full",
"bg-blue-600 text-white",
"transition-all duration-150",
"hover:bg-blue-700 focus:ring-2 focus:ring-blue-500 focus:ring-offset-2 focus:ring-offset-white focus:outline-none dark:focus:ring-offset-slate-950",
hasValue
? "pointer-events-auto scale-100 opacity-100"
: "pointer-events-none scale-75 opacity-0",
].join(" ")}
>
<CornerDownLeft size={12} strokeWidth={2.5} />
</button>
</form>
);

// ── Inline / mobile variant ──────────────────────────────────────────────
if (mobile || inline) {
return <div className="w-full">{searchInput}</div>;
}

if (!isOpen) return null;

// ── Modal variant ────────────────────────────────────────────────────────
return (
<div
className="animate-in fade-in fixed inset-0 z-[100] flex items-center justify-center bg-black/70 p-4 backdrop-blur-sm duration-200"
onClick={onClose}
>
<div
className="animate-in zoom-in-95 relative w-full max-w-md rounded-2xl p-6 shadow-2xl duration-200"
style={{
backgroundColor: "#18191D",
border: "1px solid rgba(255,255,255,0.06)",
}}
onClick={(e) => e.stopPropagation()}
Comment on lines +135 to +141
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.

🧹 Nitpick | 🔵 Trivial

Prefer theme-aware classes over hardcoded inline styles.

The modal container uses hardcoded colors (#18191D, rgba(255,255,255,0.06)) which bypass the dark/light theme system used elsewhere in the component. Consider using Tailwind classes for consistency.

♻️ Proposed fix using theme-aware classes
      <div
-       className="animate-in zoom-in-95 relative w-full max-w-md rounded-2xl p-6 shadow-2xl duration-200"
-       style={{
-         backgroundColor: "#18191D",
-         border: "1px solid rgba(255,255,255,0.06)",
-       }}
+       className="animate-in zoom-in-95 relative w-full max-w-md rounded-2xl bg-slate-900 p-6 shadow-2xl duration-200 dark:bg-slate-900 border border-white/5"
        onClick={(e) => e.stopPropagation()}
      >
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@components/ui/SearchModal.tsx` around lines 135 - 141, The modal container in
SearchModal uses hardcoded inline styles (backgroundColor "#18191D" and border
"rgba(255,255,255,0.06)"); replace the style prop on the div with theme-aware
Tailwind classes (e.g., use bg/rounded/shadow and border utilities with dark:
and light: variants to match the app theme) so the modal follows dark/light
themes consistently—update the div whose className starts with "animate-in
zoom-in-95 relative w-full max-w-md rounded-2xl p-6 shadow-2xl duration-200" by
removing the style prop and adding appropriate Tailwind classes (including dark:
and/or ring/opacity utilities) to represent the same visual appearance.

>
{/* Close button */}
<button
onClick={onClose}
aria-label="Close search"
className="absolute top-4 right-4 rounded-full p-2 text-[#6B7280] transition-colors hover:bg-white/10 hover:text-white focus:ring-2 focus:ring-white/20 focus:outline-none"
>
<X size={18} />
</button>

{/* Header */}
<div className="mb-5">
<h2 className="text-xl font-semibold tracking-tight text-white">
Search Identity
</h2>
<p className="mt-1 text-sm text-[#6B7280]">
Search by username or Token ID
</p>
</div>

{/* Search input (with built-in arrow submit button) */}
{searchInput}

{/* Hint */}
<p className="mt-3 text-center text-xs text-slate-500 dark:text-slate-400">
Press{" "}
<kbd className="rounded border border-slate-200/70 bg-slate-50 px-1.5 py-0.5 font-mono text-[10px] text-slate-700 dark:border-slate-700 dark:bg-slate-950 dark:text-slate-400">
Enter
</kbd>{" "}
or click{" "}
<span className="inline-flex items-center gap-0.5 text-blue-500">
<CornerDownLeft size={10} />
</span>{" "}
to search
</p>
</div>
</div>
);
}
Loading