Skip to content

Fix issue 17#116

Open
Aryan-The-Ghost wants to merge 17 commits intomonu808:mainfrom
Aryan-The-Ghost:fix-issue-17
Open

Fix issue 17#116
Aryan-The-Ghost wants to merge 17 commits intomonu808:mainfrom
Aryan-The-Ghost:fix-issue-17

Conversation

@Aryan-The-Ghost
Copy link
Copy Markdown

@Aryan-The-Ghost Aryan-The-Ghost commented Jan 28, 2026

Fixes: #17

Dark Theme Implementation - Changes Summary

Overview

Implemented a global light/dark theme toggle with consistent theming across all pages for the GreenPass Tourist Management System.

Files Modified

1. src/context/ThemeContext.tsx

  • Created theme context with ThemeProvider and useTheme hook
  • Added theme state management with theme (user preference) and resolvedTheme (actual applied theme)
  • Implemented toggleTheme function that properly toggles between light and dark modes
  • Added localStorage persistence to remember user's theme preference
  • Fixed toggle bug by using state callbacks and immediate DOM updates

2. src/app/layout.tsx

  • Added anti-flash script to prevent white flash on page load for dark mode users
  • Wrapped app in ThemeProvider for global theme access
  • Script properly handles light, dark, and system theme preferences

3. src/components/Navbar.tsx

  • Added theme toggle button with sun/moon icons
  • Integrated useTheme hook for theme switching functionality
  • Applied dark mode classes (dark:bg-gray-900, dark:text-white, etc.)

4. src/app/globals.css

  • Added CSS variables for light and dark theme colors
  • Configured Tailwind dark mode with .dark class selector
  • Defined color palette for backgrounds, text, borders, and components

5. tailwind.config.ts

  • Enabled class-based dark mode with darkMode: 'class'
  • Extended theme with CSS variable references

6. Component Updates (Dark Mode Classes Added)

  • src/app/page.tsx - Landing page
  • src/app/tourist/dashboard/page.tsx - Tourist dashboard
  • src/app/tourist/register/page.tsx - Registration form
  • src/components/TouristSidebar.tsx - Sidebar navigation
  • src/app/admin/login/page.tsx - Admin login page

Key Features

  • ✅ Global light/dark theme toggle in navbar
  • ✅ Theme persists across page refreshes (localStorage)
  • ✅ No flash of wrong theme on page load
  • ✅ System theme detection support
  • ✅ Consistent dark mode styling across all components
  • ✅ Smooth toggle transition

Usage

Users can click the sun/moon icon in the navbar to toggle between light and dark themes. The preference is saved and persists across sessions.

Related Issues

Will close issue #17

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Jan 28, 2026

@Aryan-The-Ghost is attempting to deploy a commit to the Narendra Singh's projects Team on Vercel.

A member of the Team first needs to authorize it.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Jan 28, 2026

📝 Walkthrough

Walkthrough

This PR introduces a comprehensive dark theme system with ThemeContext and ThemeProvider, refactors the dashboard page to a tourist-oriented interface with real-time data hooks and SSE integration, and adds OTP email verification to the signup flow. Dark mode styling is applied across all UI components and pages.

Changes

Cohort / File(s) Summary
Theme Infrastructure
src/contexts/ThemeContext.tsx, src/components/ThemeToggle.tsx, src/app/layout.tsx
New ThemeContext with theme persistence (localStorage key: "greenpass-theme"), system theme detection, and DOM class management. New ThemeToggle component with icon/dropdown/switch rendering variants. Root layout wrapped with ThemeProvider and hydration-safe inline script for theme initialization.
Global Theme Variables
src/app/globals.css
Extended CSS variables for light/dark themes with component-specific tokens (card, input, sidebar, header, dropdown, badges, overlays, scrollbars). Added system-preference fallback and theme-specific variants for backgrounds, borders, shadows, and states.
Dashboard Refactor
src/app/dashboard/page.tsx
Replaced DashboardPage with TouristDashboard; major rewrite (+425/-7) introducing useDestinations and useWeatherDataBatch hooks, SSE event listeners for real-time data refresh, computed batch-adjusted capacities, and richly structured hero/destination/metrics UI with TouristLayout wrapping.
Dark Mode Page Updates
src/app/home/page.tsx, src/app/login/page.tsx, src/app/signup/page.tsx, src/app/page.tsx
Added dark mode color variants (dark:text-, dark:bg-) across headings, inputs, containers, buttons, and helper text. Signup now includes OTP multi-step flow with handleVerifyOTP and handleResendOTP.
Dark Mode Component Updates
src/components/Header.tsx, src/components/Sidebar.tsx, src/components/Layout.tsx, src/components/OTPVerification.tsx, src/components/TouristHeader.tsx, src/components/TouristLayout.tsx, src/components/TouristSidebar.tsx
Added dark mode background, text, border, and hover variants across all components. OTPVerification updated with dark-aware input and message styling. TouristLayout adjusted with dark gradient overlays. All nav, dropdown, and interactive elements gain dark-theme support.

Sequence Diagram

sequenceDiagram
    actor User
    participant SignupPage as Signup Page
    participant AuthService as Auth Service
    participant Backend as API Backend
    participant OTPComponent as OTP Component

    User->>SignupPage: Enter name & email
    User->>SignupPage: Submit form
    SignupPage->>AuthService: signUpWithOTP(email)
    AuthService->>Backend: POST /auth/signup (send OTP)
    Backend-->>AuthService: OTP sent successfully
    AuthService-->>SignupPage: Success response
    SignupPage->>OTPComponent: Switch to OTP verification
    User->>OTPComponent: Enter 6-digit OTP
    OTPComponent->>AuthService: handleVerifyOTP(otp)
    AuthService->>Backend: POST /auth/verify-otp
    Backend-->>AuthService: Token & session
    AuthService-->>OTPComponent: Success
    OTPComponent-->>User: Redirect to dashboard
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~55 minutes

Possibly related PRs

  • weather fix #41 — Modifies dashboard page's real-time weather handling and SSE per-destination state updates, directly related to TouristDashboard's useWeatherDataBatch hook integration.
  • Mobile optimization and admin credentials | Fixes #42 and #40 #43 — Adjusts dashboard capacity calculations and policy-engine data flows; relates to TouristDashboard's batch-adjusted capacity computation logic.
  • Added OTP verification  #44 — Implements OTP email verification feature in signup/login pages and OTPVerification component, parallel to this PR's OTP flow additions.

Suggested reviewers

  • monu808

🐰 A theme toggled bright,
Dark and light dance through the night,
OTP's the key,
Tourists roam so free,
The dashboard shines with all its might!

🚥 Pre-merge checks | ✅ 1 | ❌ 2
❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Fix issue 17' is vague and generic, providing no meaningful information about the actual changes (dark theme implementation). Update the title to something more descriptive, such as 'Implement global dark theme toggle with persistent state' to clearly communicate the primary change.
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description comprehensively documents the dark theme implementation, listing affected files, key features, and implementation details related to the changeset.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@Aryan-The-Ghost
Copy link
Copy Markdown
Author

@monu808

Please review the changes in this pull request.

Thank you.

@vercel
Copy link
Copy Markdown
Contributor

vercel bot commented Jan 28, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Review Updated (UTC)
green-pass Ready Ready Preview, Comment Jan 28, 2026 6:39pm

@monu808 monu808 linked an issue Jan 28, 2026 that may be closed by this pull request
Copy link
Copy Markdown

@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: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
src/app/login/page.tsx (2)

170-175: Security concern: Hardcoded credentials exposed in client-side code.

The admin credentials (admin@tms-india.gov.in / TMS_Admin_2025!) are hardcoded in client-side JavaScript and will be visible to anyone inspecting the bundle. Even if these are demo/test credentials, this pattern poses a security risk if accidentally deployed to production.

Consider:

  • Using environment variables with server-side API for demo credential injection
  • Restricting this feature to development builds only
  • Removing hardcoded credentials entirely
🔒 Suggested approach: Development-only demo credentials
+  // Only show in development
+  const isDev = process.env.NODE_ENV === 'development';
+
   const fillAdminCredentials = () => {
-    setEmail('admin@tms-india.gov.in');
-    setPassword('TMS_Admin_2025!');
+    if (isDev) {
+      setEmail(process.env.NEXT_PUBLIC_DEMO_ADMIN_EMAIL || '');
+      setPassword(process.env.NEXT_PUBLIC_DEMO_ADMIN_PASSWORD || '');
+    }
     setLoginMethod('password');
   };

Then in your .env.development:

NEXT_PUBLIC_DEMO_ADMIN_EMAIL=admin@tms-india.gov.in
NEXT_PUBLIC_DEMO_ADMIN_PASSWORD=TMS_Admin_2025!

320-343: Demo credential buttons should be development-only.

Similar to the admin credentials, the tourist demo credentials and the Admin/Tourist quick-fill buttons should be conditionally rendered only in development environments to prevent accidental exposure in production.

💡 Suggested fix
             {/* Admin Login Button */}
-            {loginMethod === 'password' && (
+            {loginMethod === 'password' && process.env.NODE_ENV === 'development' && (
               <div className="grid grid-cols-2 gap-4">
                 {/* ... buttons ... */}
               </div>
             )}
src/components/OTPVerification.tsx (1)

27-47: Duplicate auto-focus logic detected.

There are two useEffect hooks that focus the first OTP input on mount (lines 28-34 and 45-47). This is redundant and the second one is simpler but will execute immediately without the timeout, potentially causing a race condition.

🔧 Suggested fix: Remove the duplicate useEffect
   // Focus trap for modal-like behavior if needed, though this is usually a full page component
   useEffect(() => {
     // Focus first input on mount
     const timer = setTimeout(() => {
         inputRefs.current[0]?.focus();
     }, 100);
     return () => clearTimeout(timer);
   }, []);

   // Cooldown timer for resend
   useEffect(() => {
     if (resendCooldown > 0) {
       const timer = setTimeout(() => setResendCooldown(resendCooldown - 1), 1000);
       return () => clearTimeout(timer);
     }
   }, [resendCooldown]);

-  // Auto-focus first input on mount
-  useEffect(() => {
-    inputRefs.current[0]?.focus();
-  }, []);
🤖 Fix all issues with AI agents
In `@src/app/dashboard/page.tsx`:
- Around line 363-368: The stats array in page.tsx that maps over [{ title:
"Destinations", value: destinations.length, ... }, { title: "Bookings", value:
"12", ... }, { title: "Score", value: "4.8", ... }, { title: "Adventures",
value: "24", ... }] contains hardcoded values for Bookings, Score, and
Adventures; replace these with real data (e.g., derive from a user/stats hook or
props like
useUserStats()/userStats.bookings/userStats.score/userStats.adventures) or, if
still placeholder, add a clear TODO and a visible "mock" marker in the UI;
update the mapping where .map((stat, i) => ...) uses stat.value so it consumes
the new source and ensure destinations.length remains unchanged.
- Around line 147-156: The loading branch currently returns TouristLayout
directly and can expose the spinner to unauthenticated users; wrap the loading
UI in the same ProtectedRoute wrapper used for the main content so
authentication gating is consistent. Locate the loading conditional that checks
the loading variable in page.tsx and change the return to render ProtectedRoute
(the same component wrapper used around the main dashboard) with TouristLayout
and the spinner markup as its child so the spinner is only shown to
authenticated users.
- Around line 143-145: Replace the direct navigation in handleNavigation to use
Next.js router: call useRouter() (e.g., const router = useRouter()) inside the
component and change handleNavigation to call router.push(link) (or
router.replace if you want no history entry) instead of setting
window.location.href; update any references to the handleNavigation function
accordingly so client-side navigation preserves React state.

In `@src/components/ThemeToggle.tsx`:
- Around line 37-61: The ThemeToggle component's button(s) lack an explicit
type, causing them to default to type="submit" and potentially submit parent
forms; update the main toggle button (the element with onClick={toggleTheme}
that uses resolvedTheme, cn, Moon and Sun) and any other <button> elements in
this file to include type="button" so they do not trigger form submission.
🧹 Nitpick comments (3)
src/app/dashboard/page.tsx (2)

40-41: Unused weatherMapRef should be removed.

The weatherMapRef is declared but never read or used anywhere in the component. This is dead code.

🧹 Remove unused ref
   // Use the interface here instead of 'any'
   const [weatherMap, setWeatherMap] = useState<Record<string, WeatherData>>({});
-  const weatherMapRef = useRef<Record<string, WeatherData>>({});

305-349: Consider extracting eco-alternatives IIFE into a separate component.

The inline IIFE for rendering eco-friendly alternatives is complex and reduces readability. Extracting it into a dedicated component would improve maintainability and testability.

♻️ Suggested refactor

Create a new component:

// src/components/EcoAlternatives.tsx
interface EcoAlternativesProps {
  destination: Destination;
  allDestinations: Destination[];
  adjustedCapacities: Record<string, number>;
  onNavigate: (link: string) => void;
}

export function EcoAlternatives({ 
  destination, 
  allDestinations, 
  adjustedCapacities,
  onNavigate 
}: EcoAlternativesProps) {
  const adjCap = adjustedCapacities[destination.id] ?? destination.maxCapacity;
  const occupancyRate = adjCap === 0 ? 0 : destination.currentOccupancy / adjCap;
  
  if (occupancyRate < 0.7) return null;

  const alternatives = getEcoFriendlyAlternatives(destination, allDestinations, adjustedCapacities);
  if (alternatives.length === 0) return null;

  return (
    <div className="bg-emerald-50/50 dark:bg-emerald-900/20 ...">
      {/* ... existing JSX ... */}
    </div>
  );
}

Then use it in the dashboard:

<EcoAlternatives
  destination={dest}
  allDestinations={destinations}
  adjustedCapacities={adjustedCapacities}
  onNavigate={handleNavigation}
/>
src/contexts/ThemeContext.tsx (1)

59-118: Guard localStorage access to avoid runtime errors in restricted contexts.

Some environments (privacy modes, disabled storage) can throw on getItem/setItem. A lightweight try/catch keeps theme switching resilient.

🔧 Suggested hardening
-    const saved = localStorage.getItem(storageKey) as Theme | null;
+    let saved: Theme | null = null;
+    try {
+      saved = localStorage.getItem(storageKey) as Theme | null;
+    } catch {
+      // Ignore storage access errors (e.g., disabled storage)
+    }
@@
-    localStorage.setItem(storageKey, newTheme);
+    try {
+      localStorage.setItem(storageKey, newTheme);
+    } catch {
+      // Ignore storage write errors
+    }
@@
-      localStorage.setItem(storageKey, newTheme);
+      try {
+        localStorage.setItem(storageKey, newTheme);
+      } catch {
+        // Ignore storage write errors
+      }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2da2277 and 545dac6.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (16)
  • src/app/dashboard/page.tsx
  • src/app/globals.css
  • src/app/home/page.tsx
  • src/app/layout.tsx
  • src/app/login/page.tsx
  • src/app/page.tsx
  • src/app/signup/page.tsx
  • src/components/Header.tsx
  • src/components/Layout.tsx
  • src/components/OTPVerification.tsx
  • src/components/Sidebar.tsx
  • src/components/ThemeToggle.tsx
  • src/components/TouristHeader.tsx
  • src/components/TouristLayout.tsx
  • src/components/TouristSidebar.tsx
  • src/contexts/ThemeContext.tsx
🧰 Additional context used
📓 Path-based instructions (2)
**/*.{ts,tsx,js,jsx,json}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Use Next.js with TypeScript, Tailwind CSS, ESLint, App Router, and src directory structure for project scaffolding

Files:

  • src/app/home/page.tsx
  • src/app/page.tsx
  • src/components/Header.tsx
  • src/components/ThemeToggle.tsx
  • src/components/Sidebar.tsx
  • src/app/signup/page.tsx
  • src/components/Layout.tsx
  • src/components/OTPVerification.tsx
  • src/app/layout.tsx
  • src/contexts/ThemeContext.tsx
  • src/components/TouristSidebar.tsx
  • src/app/dashboard/page.tsx
  • src/components/TouristLayout.tsx
  • src/app/login/page.tsx
  • src/components/TouristHeader.tsx
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.github/copilot-instructions.md)

Ensure all TypeScript errors are resolved and the project compiles successfully with Next.js build

Files:

  • src/app/home/page.tsx
  • src/app/page.tsx
  • src/components/Header.tsx
  • src/components/ThemeToggle.tsx
  • src/components/Sidebar.tsx
  • src/app/signup/page.tsx
  • src/components/Layout.tsx
  • src/components/OTPVerification.tsx
  • src/app/layout.tsx
  • src/contexts/ThemeContext.tsx
  • src/components/TouristSidebar.tsx
  • src/app/dashboard/page.tsx
  • src/components/TouristLayout.tsx
  • src/app/login/page.tsx
  • src/components/TouristHeader.tsx
🧠 Learnings (1)
📚 Learning: 2026-01-11T19:30:09.586Z
Learnt from: CR
Repo: monu808/GreenPass PR: 0
File: .github/copilot-instructions.md:0-0
Timestamp: 2026-01-11T19:30:09.586Z
Learning: Implement Tourist Management System features including dashboard, registration form, destinations management, real-time monitoring, capacity management (1000 tourists max), and ecological sensitivity tracking

Applied to files:

  • src/app/dashboard/page.tsx
🧬 Code graph analysis (6)
src/app/home/page.tsx (1)
src/components/ThemeToggle.tsx (1)
  • ThemeToggle (14-167)
src/components/Header.tsx (1)
src/components/ThemeToggle.tsx (1)
  • ThemeToggle (14-167)
src/components/ThemeToggle.tsx (2)
src/contexts/ThemeContext.tsx (1)
  • useTheme (141-147)
src/lib/utils.ts (1)
  • cn (4-6)
src/app/layout.tsx (1)
src/contexts/ThemeContext.tsx (1)
  • ThemeProvider (49-139)
src/components/TouristSidebar.tsx (2)
src/lib/utils.ts (1)
  • cn (4-6)
src/lib/tomorrowWeatherService.ts (1)
  • getWeatherIcon (402-414)
src/components/TouristHeader.tsx (1)
src/components/ThemeToggle.tsx (1)
  • ThemeToggle (14-167)
🪛 ast-grep (0.40.5)
src/app/layout.tsx

[warning] 53-53: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html

(react-unsafe-html-injection)

🪛 Biome (2.1.2)
src/app/layout.tsx

[error] 54-54: Avoid passing content using the dangerouslySetInnerHTML prop.

Setting content using code can expose users to cross-site scripting (XSS) attacks

(lint/security/noDangerouslySetInnerHtml)

🔇 Additional comments (27)
src/components/Layout.tsx (1)

29-29: LGTM!

The dark mode background class dark:bg-slate-950 is correctly applied to the admin layout container, maintaining consistency with the dark mode styling pattern used across other components in this PR.

src/components/TouristSidebar.tsx (3)

170-185: LGTM!

The dark mode styling for the mobile overlay and sidebar container is well-implemented:

  • Overlay uses dark:bg-black/75 for slightly more opacity in dark mode
  • Container uses consistent dark:bg-slate-900 and dark:border-slate-700 matching other components

228-236: LGTM!

Navigation item styling correctly handles both active and inactive states in dark mode with appropriate color contrasts:

  • Active: dark:bg-green-900/20, dark:text-green-400, dark:border-green-500
  • Inactive: dark:text-gray-300, dark:hover:text-green-400, dark:hover:bg-slate-800

247-285: LGTM!

The weather widget and footer sections have comprehensive dark mode styling with appropriate color adjustments for backgrounds, text, and borders.

src/components/Sidebar.tsx (2)

87-101: LGTM!

The admin Sidebar component correctly mirrors the dark mode styling patterns from TouristSidebar, ensuring visual consistency across the application.


143-145: LGTM!

Navigation item styling is consistent with TouristSidebar, using the same dark mode color palette for active and inactive states.

src/app/page.tsx (1)

23-39: LGTM!

Dark mode styling for loading states is correctly applied. The spinner border color changes to dark:border-green-400 for better visibility against the dark background.

src/app/layout.tsx (2)

50-75: Theme flash prevention script is correctly implemented.

The inline script is a standard pattern to prevent flash of incorrect theme (FOUC) during page load. Regarding the static analysis warnings about dangerouslySetInnerHTML:

  • This is a false positive in this context — the script content is entirely hardcoded with no user-controlled input
  • The script only reads from localStorage and matchMedia, both of which are safe operations
  • The storage key 'greenpass-theme' correctly matches the storageKey prop passed to ThemeProvider on line 79

The suppressHydrationWarning on the <html> element is appropriate since the script modifies the DOM before React hydration.


79-87: LGTM!

ThemeProvider correctly wraps all other providers with matching configuration:

  • defaultTheme="system" enables system preference detection
  • storageKey="greenpass-theme" matches the inline script's localStorage key

The provider nesting order is appropriate with ThemeProvider as the outermost wrapper.

src/components/TouristLayout.tsx (2)

21-21: LGTM!

The main container's dark gradient (dark:from-slate-950 dark:via-slate-900 dark:to-slate-950) provides a smooth, consistent dark background that matches the overall theming system.


30-32: Nice attention to detail.

The floating glass elements correctly reduce opacity from /20 to /10 in dark mode. This prevents the decorative elements from being too prominent against dark backgrounds while maintaining the visual effect.

src/app/login/page.tsx (1)

189-402: Dark mode styling applied consistently across login UI.

The dark mode classes follow a consistent pattern (dark:from-slate-950, dark:bg-slate-800, dark:text-gray-400, etc.) and align well with the global theming system. The Suspense fallback at line 402 also includes appropriate dark mode styling.

src/components/TouristHeader.tsx (3)

9-9: ThemeToggle integration looks good.

The import and usage align with the ThemeToggle component's API as documented in the relevant code snippets.


94-96: ThemeToggle placement and variant are appropriate.

Using variant="icon" for the header provides a compact toggle that fits well in the user actions area. The placement between other action buttons maintains visual consistency.


43-299: Comprehensive dark mode styling with good accessibility.

The dark mode classes are applied consistently throughout the header, dropdown menus, and all interactive elements. The component maintains proper ARIA attributes, focus management, and keyboard navigation support.

src/components/Header.tsx (2)

81-83: Appropriate use of dropdown variant for admin header.

Using variant="dropdown" for the admin header provides more granular theme control (Light/Dark/System options) which is suitable for an admin interface where users may prefer explicit control.


47-279: Dark mode styling consistently applied across admin header.

The dark mode classes follow the established pattern and cover all UI elements including the search input, user menu, admin panel dropdown, and authentication submenu. The styling maintains visual hierarchy and accessibility in both themes.

src/components/OTPVerification.tsx (1)

147-263: Dark mode styling thoroughly applied to OTP verification UI.

The dark mode classes are consistently applied across all visual elements including the header, form container, OTP inputs, error/success messages, resend section, and tip banner. The input states (filled, error, disabled) also properly adapt to dark mode.

src/app/home/page.tsx (3)

6-6: ThemeToggle properly integrated in home page navigation.

The variant="icon" provides a compact toggle suitable for the navigation bar, consistent with the TouristHeader implementation.

Also applies to: 24-25


71-149: Feature cards have consistent dark mode styling.

Each feature card follows the same pattern: dark:bg-slate-800, dark:border-slate-700, dark:bg-*-900/30 for icon backgrounds, and dark:text-*-400 for icons. This ensures visual consistency across the feature grid.


152-165: CTA section dark mode gradient is well-designed.

The gradient shift from from-green-600 to-blue-600 to dark:from-green-700 dark:to-blue-700 maintains vibrancy while reducing brightness for dark mode. The button text color adjustment (dark:text-green-700) ensures readability on the white background.

src/app/dashboard/page.tsx (1)

213-355: Dashboard dark mode styling is comprehensive and well-implemented.

The tourist dashboard applies dark mode consistently across all sections: destination cards, weather displays, eco-alternatives, and metric stats. The use of dark:bg-slate-800, dark:border-slate-700, and dark:text-* classes aligns with the project's theming patterns.

src/app/globals.css (2)

4-110: Solid theme variable taxonomy with system fallback.

Light/dark parity plus the prefers-color-scheme fallback should keep theme resolution consistent across the app.


164-175: Dark-mode component overrides and scrollbar theming look cohesive.

Good use of variables and targeted dark overrides for hero/glass/buttons/shimmer to keep visuals consistent.

Also applies to: 205-312

src/app/signup/page.tsx (1)

126-226: Dark‑mode styling updates are consistent across the signup flow.

Backgrounds, borders, text, and error/info states all have sensible dark variants.

src/components/ThemeToggle.tsx (1)

23-33: Click‑outside handler is clean and scoped.

This keeps the dropdown interaction tidy without extra state plumbing.

src/contexts/ThemeContext.tsx (1)

22-47: Nice separation of system resolution and DOM application.

Keeping resolve/apply isolated and updating meta theme-color is tidy and reusable.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Comment on lines +143 to +145
const handleNavigation = (link: string): void => {
window.location.href = link;
};
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

Use Next.js router instead of window.location.href for client-side navigation.

Using window.location.href causes a full page reload, losing React state and defeating the purpose of a SPA. Next.js provides useRouter which is already imported but not used for navigation.

🔧 Suggested fix
-  // BOT FIX: Standardized navigation handler
-  const handleNavigation = (link: string): void => {
-    window.location.href = link;
-  };
+  // Use Next.js router for client-side navigation
+  const handleNavigation = (link: string): void => {
+    router.push(link);
+  };

You'll need to add useRouter:

+import { useRouter } from 'next/navigation';

 export default function TouristDashboard() {
+  const router = useRouter();
   const [destinations, setDestinations] = useState<Destination[]>([]);
📝 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 handleNavigation = (link: string): void => {
window.location.href = link;
};
// Use Next.js router for client-side navigation
const handleNavigation = (link: string): void => {
router.push(link);
};
🤖 Prompt for AI Agents
In `@src/app/dashboard/page.tsx` around lines 143 - 145, Replace the direct
navigation in handleNavigation to use Next.js router: call useRouter() (e.g.,
const router = useRouter()) inside the component and change handleNavigation to
call router.push(link) (or router.replace if you want no history entry) instead
of setting window.location.href; update any references to the handleNavigation
function accordingly so client-side navigation preserves React state.

Comment on lines +147 to +156
if (loading) {
return (
<TouristLayout>
<div className="flex flex-col items-center justify-center min-h-[60vh] space-y-4" role="status" aria-live="polite">
<RefreshCw className="h-12 w-12 text-emerald-500 animate-spin" aria-hidden="true" />
<p className="text-[10px] font-black uppercase tracking-widest text-emerald-600">Syncing Nature Data...</p>
</div>
</TouristLayout>
);
}
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 | 🟡 Minor

Loading state bypasses ProtectedRoute wrapper.

The loading state returns a TouristLayout without being wrapped in ProtectedRoute, which means unauthenticated users could briefly see the loading spinner before being redirected. The main content at line 159 is properly wrapped.

🔧 Suggested fix: Wrap loading state in ProtectedRoute
   if (loading) {
     return (
+      <ProtectedRoute>
         <TouristLayout>
           <div className="flex flex-col items-center justify-center min-h-[60vh] space-y-4" role="status" aria-live="polite">
             <RefreshCw className="h-12 w-12 text-emerald-500 animate-spin" aria-hidden="true" />
             <p className="text-[10px] font-black uppercase tracking-widest text-emerald-600">Syncing Nature Data...</p>
           </div>
         </TouristLayout>
+      </ProtectedRoute>
     );
   }
🤖 Prompt for AI Agents
In `@src/app/dashboard/page.tsx` around lines 147 - 156, The loading branch
currently returns TouristLayout directly and can expose the spinner to
unauthenticated users; wrap the loading UI in the same ProtectedRoute wrapper
used for the main content so authentication gating is consistent. Locate the
loading conditional that checks the loading variable in page.tsx and change the
return to render ProtectedRoute (the same component wrapper used around the main
dashboard) with TouristLayout and the spinner markup as its child so the spinner
is only shown to authenticated users.

Comment on lines +363 to +368
{[
{ title: "Destinations", value: destinations.length, icon: MapPin, color: "bg-emerald-500", sub: "Available locations" },
{ title: "Bookings", value: "12", icon: Calendar, color: "bg-blue-500", sub: "Current trips" },
{ title: "Score", value: "4.8", icon: Star, color: "bg-amber-500", sub: "Average rating" },
{ title: "Adventures", value: "24", icon: Award, color: "bg-purple-500", sub: "Completed" }
].map((stat, i) => (
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 | 🟡 Minor

Hardcoded metric values should be replaced with actual data.

The stats for "Bookings" (12), "Score" (4.8), and "Adventures" (24) are hardcoded strings. These should be fetched from actual user data or clearly marked as placeholder/mock data.

💡 Suggested approach

If these are placeholders, consider:

  1. Adding a TODO comment indicating these need real data integration
  2. Using a hook to fetch actual user statistics
  3. At minimum, marking them clearly as mock data in the UI or code
+            // TODO: Replace with actual user statistics from useUserStats hook
             {[
-              { title: "Destinations", value: destinations.length, icon: MapPin, color: "bg-emerald-500", sub: "Available locations" },
-              { title: "Bookings", value: "12", icon: Calendar, color: "bg-blue-500", sub: "Current trips" },
-              { title: "Score", value: "4.8", icon: Star, color: "bg-amber-500", sub: "Average rating" },
-              { title: "Adventures", value: "24", icon: Award, color: "bg-purple-500", sub: "Completed" }
+              { title: "Destinations", value: destinations.length, icon: MapPin, color: "bg-emerald-500", sub: "Available locations" },
+              { title: "Bookings", value: userStats?.bookings ?? "—", icon: Calendar, color: "bg-blue-500", sub: "Current trips" },
+              { title: "Score", value: userStats?.rating ?? "—", icon: Star, color: "bg-amber-500", sub: "Average rating" },
+              { title: "Adventures", value: userStats?.completed ?? "—", icon: Award, color: "bg-purple-500", sub: "Completed" }
             ].map((stat, i) => (
🤖 Prompt for AI Agents
In `@src/app/dashboard/page.tsx` around lines 363 - 368, The stats array in
page.tsx that maps over [{ title: "Destinations", value: destinations.length,
... }, { title: "Bookings", value: "12", ... }, { title: "Score", value: "4.8",
... }, { title: "Adventures", value: "24", ... }] contains hardcoded values for
Bookings, Score, and Adventures; replace these with real data (e.g., derive from
a user/stats hook or props like
useUserStats()/userStats.bookings/userStats.score/userStats.adventures) or, if
still placeholder, add a clear TODO and a visible "mock" marker in the UI;
update the mapping where .map((stat, i) => ...) uses stat.value so it consumes
the new source and ensure destinations.length remains unchanged.

Comment on lines +37 to +61
<button
onClick={toggleTheme}
className={cn(
"relative inline-flex h-8 w-14 items-center rounded-full transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 dark:focus:ring-offset-slate-900",
resolvedTheme === 'dark' ? 'bg-slate-700' : 'bg-gray-200',
className
)}
role="switch"
aria-checked={resolvedTheme === 'dark'}
aria-label={`Switch to ${resolvedTheme === 'dark' ? 'light' : 'dark'} mode`}
>
<span className="sr-only">Toggle theme</span>
<span
className={cn(
"inline-flex h-6 w-6 transform items-center justify-center rounded-full bg-white shadow-lg transition-transform duration-300",
resolvedTheme === 'dark' ? 'translate-x-7' : 'translate-x-1'
)}
>
{resolvedTheme === 'dark' ? (
<Moon className="h-4 w-4 text-slate-700" aria-hidden="true" />
) : (
<Sun className="h-4 w-4 text-amber-500" aria-hidden="true" />
)}
</span>
</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.

⚠️ Potential issue | 🟡 Minor

🧩 Analysis chain

🏁 Script executed:

find . -name "ThemeToggle.tsx" -o -name "ThemeToggle.ts" -o -name "ThemeToggle.jsx" -o -name "ThemeToggle.js" | head -20

Repository: monu808/GreenPass

Length of output: 93


🏁 Script executed:

cat -n ./src/components/ThemeToggle.tsx

Repository: monu808/GreenPass

Length of output: 7506


Add type="button" to all button elements to prevent unintended form submissions.

Buttons without an explicit type attribute default to type="submit" in HTML. If this component is placed inside a <form>, clicking any button will unexpectedly submit the form. This affects the buttons at lines 37, 68, 95, 111, 127, and 151.

🔧 Proposed fix
-      <button
+      <button
+        type="button"
         onClick={toggleTheme}
📝 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
<button
onClick={toggleTheme}
className={cn(
"relative inline-flex h-8 w-14 items-center rounded-full transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 dark:focus:ring-offset-slate-900",
resolvedTheme === 'dark' ? 'bg-slate-700' : 'bg-gray-200',
className
)}
role="switch"
aria-checked={resolvedTheme === 'dark'}
aria-label={`Switch to ${resolvedTheme === 'dark' ? 'light' : 'dark'} mode`}
>
<span className="sr-only">Toggle theme</span>
<span
className={cn(
"inline-flex h-6 w-6 transform items-center justify-center rounded-full bg-white shadow-lg transition-transform duration-300",
resolvedTheme === 'dark' ? 'translate-x-7' : 'translate-x-1'
)}
>
{resolvedTheme === 'dark' ? (
<Moon className="h-4 w-4 text-slate-700" aria-hidden="true" />
) : (
<Sun className="h-4 w-4 text-amber-500" aria-hidden="true" />
)}
</span>
</button>
<button
type="button"
onClick={toggleTheme}
className={cn(
"relative inline-flex h-8 w-14 items-center rounded-full transition-colors duration-300 focus:outline-none focus:ring-2 focus:ring-green-500 focus:ring-offset-2 dark:focus:ring-offset-slate-900",
resolvedTheme === 'dark' ? 'bg-slate-700' : 'bg-gray-200',
className
)}
role="switch"
aria-checked={resolvedTheme === 'dark'}
aria-label={`Switch to ${resolvedTheme === 'dark' ? 'light' : 'dark'} mode`}
>
<span className="sr-only">Toggle theme</span>
<span
className={cn(
"inline-flex h-6 w-6 transform items-center justify-center rounded-full bg-white shadow-lg transition-transform duration-300",
resolvedTheme === 'dark' ? 'translate-x-7' : 'translate-x-1'
)}
>
{resolvedTheme === 'dark' ? (
<Moon className="h-4 w-4 text-slate-700" aria-hidden="true" />
) : (
<Sun className="h-4 w-4 text-amber-500" aria-hidden="true" />
)}
</span>
</button>
🤖 Prompt for AI Agents
In `@src/components/ThemeToggle.tsx` around lines 37 - 61, The ThemeToggle
component's button(s) lack an explicit type, causing them to default to
type="submit" and potentially submit parent forms; update the main toggle button
(the element with onClick={toggleTheme} that uses resolvedTheme, cn, Moon and
Sun) and any other <button> elements in this file to include type="button" so
they do not trigger form submission.

@monu808
Copy link
Copy Markdown
Owner

monu808 commented Jan 28, 2026

@Aryan-The-Ghost your pr has too many critical issues:

  1. the theme doesn't go back to white.
  2. most importantly on logging using admin's credential the application logged in to tourist side of portal.
  3. coderabbit quoted issues.

I request you to first test it on your local enironment and then raise a pr.

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.

Add Dark Theme Support

2 participants