feat: Drawer for Generation - #26
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This pull request adds a drawer component for generating unique book tracking codes, allowing users to register books they're donating to the Little Free Library network. The main addition is an interactive UI flow that searches for books, requests location permission, and generates tracking codes via API.
Key changes:
- New
AddSightingDrawercomponent with Google Books search integration and location-based code generation - Updated z-index values for toast and drawer components to ensure proper layering
- Fixed coordinate validation in map component and improved sightings feed with duplicate prevention
Reviewed changes
Copilot reviewed 12 out of 13 changed files in this pull request and generated 12 comments.
Show a summary per file
| File | Description |
|---|---|
| components/add-sighting-drawer.tsx | New drawer component implementing the complete book registration flow with location handling and API integration |
| components/ui/toast.tsx | Increased z-index from 100 to 5000 for proper layering above other UI elements |
| components/ui/drawer.tsx | Updated z-index values to 2000/2001 for drawer overlay and content |
| components/sightings-feed.tsx | Added duplicate prevention logic and removed dependency causing unnecessary re-renders |
| components/home-map.tsx | Enhanced coordinate validation with type checking before map navigation |
| components/hero.tsx | Integrated the new AddSightingDrawer component into the hero section |
| components/book-search.tsx | Updated input styling for improved visual consistency |
| components/ledger-list.tsx | Changed placeholder text from "Listening for drops..." to "Waiting for sightings..." |
| app/api/sightings/route.ts | Updated to handle async cookies() function with await |
| app/api/books/generate/route.ts | Updated to handle async cookies() function with await |
| tests/app/api/books/generate/route.test.ts | Restructured test suite with proper mocking for the generate endpoint |
| next-env.d.ts | Removed auto-generated Next.js TypeScript configuration file |
| comments.json | Removed previous code review comments file |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
|
||
| <Button | ||
| onClick={handleGenerate} | ||
| disabled={isGenerating || (!latitude && !locationError && !locationTimeout)} |
There was a problem hiding this comment.
The condition checks !latitude && !locationError && !locationTimeout but !latitude will be true when latitude is 0 (a valid coordinate on the equator). This could incorrectly disable the button for users at latitude 0. Consider using a nullish check like latitude == null instead.
| const cookieStore = await cookies() | ||
| const supabase = createRouteHandlerClient({ cookies: () => cookieStore } as any, { | ||
| supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL, | ||
| supabaseKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, | ||
| }) |
There was a problem hiding this comment.
The as any type assertion bypasses TypeScript's type safety. This could hide type mismatches between what createRouteHandlerClient expects and what's being provided. Consider properly typing the cookies parameter or reviewing if there's a better approach to handle the async cookies function.
| const cookieStore = await cookies() | |
| const supabase = createRouteHandlerClient({ cookies: () => cookieStore } as any, { | |
| supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL, | |
| supabaseKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, | |
| }) | |
| const cookieStore = cookies() | |
| const supabase = createRouteHandlerClient( | |
| { cookies: () => cookieStore }, | |
| { | |
| supabaseUrl: process.env.NEXT_PUBLIC_SUPABASE_URL, | |
| supabaseKey: process.env.NEXT_PUBLIC_SUPABASE_ANON_KEY, | |
| } | |
| ) |
| <Button | ||
| variant="ghost" | ||
| onClick={() => { | ||
| // Optional: reset state on close if desired, but user might want to keep it open | ||
| // For now, let's just close. | ||
| }} | ||
| > |
There was a problem hiding this comment.
The empty onClick handler with a comment suggests incomplete functionality. Either implement the reset state logic on close or remove this handler if it's truly not needed. Having an empty handler with explanatory comments creates confusion about the intended behavior.
| <Button | |
| variant="ghost" | |
| onClick={() => { | |
| // Optional: reset state on close if desired, but user might want to keep it open | |
| // For now, let's just close. | |
| }} | |
| > | |
| <Button variant="ghost"> |
| if (!getCookie("lfl_anonymous_id")) { | ||
| const newId = crypto.randomUUID() | ||
| // Set cookie for 1 year | ||
| document.cookie = `lfl_anonymous_id=${newId}; path=/; max-age=31536000; SameSite=Lax; Secure` |
There was a problem hiding this comment.
The Secure attribute is included in the cookie settings, but this will cause the cookie to fail in local development environments using HTTP. Consider making the Secure attribute conditional based on the environment (e.g., only set it in production).
| document.cookie = `lfl_anonymous_id=${newId}; path=/; max-age=31536000; SameSite=Lax; Secure` | |
| let cookie = `lfl_anonymous_id=${newId}; path=/; max-age=31536000; SameSite=Lax` | |
| if (typeof window !== "undefined" && window.location.protocol === "https:") { | |
| cookie += "; Secure" | |
| } | |
| document.cookie = cookie |
| <Loader2 className="mr-2 h-5 w-5 animate-spin" /> | ||
| Forging Code... | ||
| </> | ||
| ) : !latitude ? ( |
There was a problem hiding this comment.
The condition checks !latitude on line 220, but this will also be true when latitude is 0 (which is a valid coordinate on the equator). This could incorrectly show "Location Access Required" or "Locating you..." when the user is actually at latitude 0. Consider using latitude == null or latitude === undefined instead.
|
|
||
| return () => clearInterval(timer) | ||
| }, [currentIndex, initialSightings]) | ||
| }, [initialSightings]) |
There was a problem hiding this comment.
Removing currentIndex from the dependency array while still using it in the effect can lead to stale closure issues. The effect checks currentIndex >= initialSightings.length but if currentIndex isn't in the dependencies, the effect won't re-run when currentIndex changes, which could cause the interval to continue running after all items have been displayed.
| const cookieStore = await cookies() | ||
| const supabase = createRouteHandlerClient({ cookies: () => cookieStore } as any) |
There was a problem hiding this comment.
The as any type assertion bypasses TypeScript's type safety. This could hide type mismatches between what createRouteHandlerClient expects and what's being provided. Consider properly typing the cookies parameter or reviewing if there's a better approach to handle the async cookies function.
| const cookieStore = await cookies() | |
| const supabase = createRouteHandlerClient({ cookies: () => cookieStore } as any) | |
| const supabase = createRouteHandlerClient({ cookies }) |
| import { parseBookMetadata } from "@/lib/book-utils" | ||
| import { BookMetadata } from "@/lib/types" | ||
| import { POST } from "@/app/api/books/generate/route" | ||
| import { NextResponse } from "next/server" |
There was a problem hiding this comment.
Unused import NextResponse.
| import { NextResponse } from "next/server" |
| // Only start timeout if drawer is open and we don't have location yet | ||
| if (open && !latitude && !locationError) { |
There was a problem hiding this comment.
This negation always evaluates to true.
| // Only start timeout if drawer is open and we don't have location yet | |
| if (open && !latitude && !locationError) { | |
| // Only start timeout if drawer is open (at this point we know we don't have location or an error yet) | |
| if (open) { |
| } | ||
|
|
||
| // Only start timeout if drawer is open and we don't have location yet | ||
| if (open && !latitude && !locationError) { |
There was a problem hiding this comment.
This negation always evaluates to true.
| if (open && !latitude && !locationError) { | |
| if (open) { |
|
I have addressed the PR feedback:
|
No description provided.