Conversation
|
@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. |
📝 WalkthroughWalkthroughThis 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
Sequence DiagramsequenceDiagram
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
Estimated code review effort🎯 4 (Complex) | ⏱️ ~55 minutes Possibly related PRs
Suggested reviewers
🚥 Pre-merge checks | ✅ 1 | ❌ 2❌ Failed checks (1 warning, 1 inconclusive)
✅ Passed checks (1 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing touches
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. Comment |
|
Please review the changes in this pull request. Thank you. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
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
useEffecthooks 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: UnusedweatherMapRefshould be removed.The
weatherMapRefis 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
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (16)
src/app/dashboard/page.tsxsrc/app/globals.csssrc/app/home/page.tsxsrc/app/layout.tsxsrc/app/login/page.tsxsrc/app/page.tsxsrc/app/signup/page.tsxsrc/components/Header.tsxsrc/components/Layout.tsxsrc/components/OTPVerification.tsxsrc/components/Sidebar.tsxsrc/components/ThemeToggle.tsxsrc/components/TouristHeader.tsxsrc/components/TouristLayout.tsxsrc/components/TouristSidebar.tsxsrc/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.tsxsrc/app/page.tsxsrc/components/Header.tsxsrc/components/ThemeToggle.tsxsrc/components/Sidebar.tsxsrc/app/signup/page.tsxsrc/components/Layout.tsxsrc/components/OTPVerification.tsxsrc/app/layout.tsxsrc/contexts/ThemeContext.tsxsrc/components/TouristSidebar.tsxsrc/app/dashboard/page.tsxsrc/components/TouristLayout.tsxsrc/app/login/page.tsxsrc/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.tsxsrc/app/page.tsxsrc/components/Header.tsxsrc/components/ThemeToggle.tsxsrc/components/Sidebar.tsxsrc/app/signup/page.tsxsrc/components/Layout.tsxsrc/components/OTPVerification.tsxsrc/app/layout.tsxsrc/contexts/ThemeContext.tsxsrc/components/TouristSidebar.tsxsrc/app/dashboard/page.tsxsrc/components/TouristLayout.tsxsrc/app/login/page.tsxsrc/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-950is 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/75for slightly more opacity in dark mode- Container uses consistent
dark:bg-slate-900anddark:border-slate-700matching 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-400for 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
localStorageandmatchMedia, both of which are safe operations- The storage key
'greenpass-theme'correctly matches thestorageKeyprop passed toThemeProvideron line 79The
suppressHydrationWarningon 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 detectionstorageKey="greenpass-theme"matches the inline script's localStorage keyThe 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
/20to/10in 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/30for icon backgrounds, anddark:text-*-400for 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-600todark:from-green-700 dark:to-blue-700maintains 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, anddark: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.
| const handleNavigation = (link: string): void => { | ||
| window.location.href = link; | ||
| }; |
There was a problem hiding this comment.
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.
| 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.
| 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> | ||
| ); | ||
| } |
There was a problem hiding this comment.
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.
| {[ | ||
| { 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) => ( |
There was a problem hiding this comment.
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:
- Adding a TODO comment indicating these need real data integration
- Using a hook to fetch actual user statistics
- 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.
| <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> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
find . -name "ThemeToggle.tsx" -o -name "ThemeToggle.ts" -o -name "ThemeToggle.jsx" -o -name "ThemeToggle.js" | head -20Repository: monu808/GreenPass
Length of output: 93
🏁 Script executed:
cat -n ./src/components/ThemeToggle.tsxRepository: 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.
| <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.
|
@Aryan-The-Ghost your pr has too many critical issues:
I request you to first test it on your local enironment and then raise a pr. |
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.tsxThemeProvideranduseThemehooktheme(user preference) andresolvedTheme(actual applied theme)toggleThemefunction that properly toggles between light and dark modes2.
src/app/layout.tsxThemeProviderfor global theme accesslight,dark, andsystemtheme preferences3.
src/components/Navbar.tsxuseThemehook for theme switching functionalitydark:bg-gray-900,dark:text-white, etc.)4.
src/app/globals.css.darkclass selector5.
tailwind.config.tsdarkMode: 'class'6. Component Updates (Dark Mode Classes Added)
src/app/page.tsx- Landing pagesrc/app/tourist/dashboard/page.tsx- Tourist dashboardsrc/app/tourist/register/page.tsx- Registration formsrc/components/TouristSidebar.tsx- Sidebar navigationsrc/app/admin/login/page.tsx- Admin login pageKey Features
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