Implement PDF Report Generation#602
Conversation
- Added `jspdf` and `markdown-to-txt` dependencies. - Created `lib/utils/report-generator.ts` for PDF generation logic. - Created `components/report-button.tsx` as a UI entry point. - Integrated `ReportButton` into the desktop header. - Implemented robust pagination and markdown-to-text conversion for AI responses in the report. - Added drawings appendix to the report. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
|
👋 Jules, reporting for duty! I'm here to lend a hand with this pull request. When you start a review, I'll add a 👀 emoji to each comment to let you know I've read it. I'll focus on feedback directed at me and will do my best to stay out of conversations between you and other bots or reviewers to keep the noise down. I'll push a commit with your requested changes shortly after. Please note there might be a delay between these steps, but rest assured I'm on the job! For more direct control, you can switch me to Reactive Mode. When this mode is on, I will only act on comments where you specifically mention me with New to Jules? Learn more at jules.google/docs. For security, I will only act on instructions from the user who triggered this task. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
ⓘ You've reached your Qodo monthly free-tier limit. Reviews pause until next month — upgrade your plan to continue now, or link your paid account if you already have one. |
|
|
WalkthroughThis PR introduces PDF report generation, refactors tool registration and AI provider selection, and restructures geospatial and resolution-search schemas with enhanced runtime validation. The ReportButton component captures map snapshots and generates multi-page PDFs, while search tool availability is now gated on TAVILY_API_KEY and Exa provider support is removed. AI model fallback now prioritizes Grok over Gemini, and geospatial queries are validated at runtime with flattened schema constraints. ChangesPDF Report Generation
Search Tool & Provider Configuration
Schema Refactoring & GeoJSON Handling
Sequence Diagram(s)sequenceDiagram
participant User as User
participant Chat as Chat UI
participant ReportButton as ReportButton
participant Map as Map Canvas
participant generateReport as generateReport
participant jsPDF as jsPDF
participant FileSystem as File System
User->>Chat: Click "Generate Report"
Chat->>ReportButton: Render button
User->>ReportButton: Click download
ReportButton->>Map: Extract canvas.toDataURL(PNG)
ReportButton->>generateReport: Call with messages, features, snapshot, chatTitle
generateReport->>jsPDF: Create document, add cover page
generateReport->>jsPDF: Embed map snapshot (with paging)
generateReport->>jsPDF: Render conversation history<br/>(markdown→plain text)
generateReport->>jsPDF: Embed resolution search result images
generateReport->>jsPDF: Append drawings measurements section
generateReport->>FileSystem: Save PDF (chatTitle-based filename)
FileSystem-->>User: PDF downloaded
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related issues
Possibly related PRs
Suggested labels
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
⚔️ Resolve merge conflicts
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 |
There was a problem hiding this comment.
Actionable comments posted: 6
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@components/report-button.tsx`:
- Around line 3-11: Remove the redundant top-level "import React from 'react'"
(the JSX runtime no longer needs it) and keep the existing named "useState"
import from 'react'; update the imports near Button, FileDown, Loader2,
useAIState, useMapData, useMap, generateReport, and toast so there are no
duplicate or unused React namespace imports, and verify no code references the
React identifier (e.g., React.something) in this file before committing.
In `@lib/utils/report-generator.ts`:
- Around line 90-94: The code parses userMsg.content into json and directly
reads json.input or json.related_query; add validation to ensure the parsed
value is an object and contains the expected keys before using them. In the try
block where JSON.parse(userMsg.content) is assigned to json, verify typeof json
=== 'object' && json !== null and then use checks like 'input' in json or
'related_query' in json (or optional chaining with fallbacks) to set userContent
safely; keep the existing catch fallback to userMsg.content. Update the logic
around userMsg, json, and userContent to avoid runtime errors when the parsed
structure is unexpected.
- Line 127: The call to doc.addImage(images[i], 'JPEG', ...) hardcodes JPEG but
images (mapboxImage, googleImage, image) may be PNG/WebP/etc.; update the image
handling around the images array and the doc.addImage call to detect each
image's format from its data URL prefix (inspect the "data:*/*;base64," mime
type), map the mime type to jsPDF's expected format string (e.g., 'PNG' for
image/png, 'JPEG' for image/jpeg, etc.), and pass that variable format into
doc.addImage for each image, with a sensible fallback (e.g., 'PNG') if the mime
type is missing or unsupported. Ensure detection logic is colocated with the
images array construction and used when calling doc.addImage so each image is
added with the correct format.
- Around line 162-163: The coordinate string is being naively truncated with
substring(0, 100) which can cut a number or coordinate pair mid-value; modify
the logic that builds coords (using feature.geometry.coordinates) to serialize
coordinates element-by-element and append whole coordinate entries until a
character limit (e.g., 100) would be exceeded, then append an ellipsis, and use
that result in the existing doc.text call (the variable coords and the
doc.text(`Coordinates: ${coords}`, margin + 5, yOffset) should remain but
receive the safely truncated string).
- Around line 112-133: Ensure the parsed JSON from searchResult is an object and
has the expected properties before using them: after const data =
JSON.parse(...), check that data is a non-null object and that summary is a
string before calling addTextWithAutoPageBreak, and verify
mapboxImage/googleImage/image are valid non-empty strings (or valid image
payloads) before adding them to the images array and calling doc.addImage; use
optional chaining/typeof checks and provide safe defaults, only call
checkPageBreak/doc.addImage when image entries pass validation, and keep error
handling around parsing/image rendering (refer to searchResult, data,
addTextWithAutoPageBreak, checkPageBreak, doc.addImage, and yOffset).
In `@package.json`:
- Line 72: Remove the unused "html2canvas" dependency from package.json (delete
the "html2canvas": "^1.4.1" entry) and then update the lockfile by running your
package manager install (npm install or yarn install) so the dependency is
actually removed from node_modules; verify there are no imports of html2canvas
in the codebase (e.g., check report-button.tsx which uses
map.getCanvas().toDataURL('image/png')) and run the test/build to ensure bundle
size and functionality remain intact.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 02755013-44cd-47ec-a497-1ec5561414f1
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (4)
components/header.tsxcomponents/report-button.tsxlib/utils/report-generator.tspackage.json
📜 Review details
🔇 Additional comments (6)
lib/utils/report-generator.ts (2)
26-50: LGTM!
105-105: ⚡ Quick winNo action required. The
markdownToTxt()function call correctly implements the markdown-to-txt v2.0.1 API, which accepts a markdown string as the first argument (with an optional MarkedOptions second argument). The code usage is appropriate and matches the documented API signature.components/report-button.tsx (2)
19-43: LGTM!
27-27:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd null safety for chatId before calling substring.
The code constructs
chatTitlefromaiState.chatId.substring(0, 8), but while the ternary checks for the existence ofchatId, it doesn't guard againstchatIdbeing an empty string or other edge cases before calling.substring().🛡️ Suggested fix for safer chatId handling
- const chatTitle = aiState.chatId ? `Chat-${aiState.chatId.substring(0, 8)}` : 'QCX-Analysis' + const chatTitle = aiState.chatId && aiState.chatId.length > 0 + ? `Chat-${aiState.chatId.substring(0, 8)}` + : 'QCX-Analysis'> Likely an incorrect or invalid review comment.components/header.tsx (1)
18-18: LGTM!Also applies to: 75-75
package.json (1)
73-73: ⚡ Quick winNo action required—jsPDF version 4.2.1 is correct.
The specified version is the latest stable release of jsPDF as of May 2026 (published Mar 17, 2026).
> Likely an incorrect or invalid review comment.
| import React from 'react' | ||
| import { Button } from '@/components/ui/button' | ||
| import { FileDown, Loader2 } from 'lucide-react' | ||
| import { useAIState } from 'ai/rsc' | ||
| import { useMapData } from '@/components/map/map-data-context' | ||
| import { useMap } from '@/components/map/map-context' | ||
| import { generateReport } from '@/lib/utils/report-generator' | ||
| import { useState } from 'react' | ||
| import { toast } from 'sonner' |
There was a problem hiding this comment.
🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win
Remove redundant React import.
In React 19, the React import on line 3 is no longer needed for JSX. Additionally, useState is imported on line 10, making the React namespace import on line 3 unnecessary.
🧹 Suggested cleanup
-import React from 'react'
import { Button } from '@/components/ui/button'
import { FileDown, Loader2 } from 'lucide-react'
import { useAIState } from 'ai/rsc'
import { useMapData } from '@/components/map/map-data-context'
import { useMap } from '@/components/map/map-context'
import { generateReport } from '@/lib/utils/report-generator'
import { useState } from 'react'
import { toast } from 'sonner'As per coding guidelines, React 19 no longer requires the React import for JSX transformation, as noted in the React 19 upgrade documentation: "Enable the modern JSX Transform (2020+)."
📝 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.
| import React from 'react' | |
| import { Button } from '@/components/ui/button' | |
| import { FileDown, Loader2 } from 'lucide-react' | |
| import { useAIState } from 'ai/rsc' | |
| import { useMapData } from '@/components/map/map-data-context' | |
| import { useMap } from '@/components/map/map-context' | |
| import { generateReport } from '@/lib/utils/report-generator' | |
| import { useState } from 'react' | |
| import { toast } from 'sonner' | |
| import { Button } from '@/components/ui/button' | |
| import { FileDown, Loader2 } from 'lucide-react' | |
| import { useAIState } from 'ai/rsc' | |
| import { useMapData } from '@/components/map/map-data-context' | |
| import { useMap } from '@/components/map/map-context' | |
| import { generateReport } from '@/lib/utils/report-generator' | |
| import { useState } from 'react' | |
| import { toast } from 'sonner' |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@components/report-button.tsx` around lines 3 - 11, Remove the redundant
top-level "import React from 'react'" (the JSX runtime no longer needs it) and
keep the existing named "useState" import from 'react'; update the imports near
Button, FileDown, Loader2, useAIState, useMapData, useMap, generateReport, and
toast so there are no duplicate or unused React namespace imports, and verify no
code references the React identifier (e.g., React.something) in this file before
committing.
| const json = JSON.parse(userMsg.content as string); | ||
| userContent = userMsg.type === 'input' ? json.input : json.related_query; | ||
| } catch (e) { | ||
| userContent = userMsg.content as string; | ||
| } |
There was a problem hiding this comment.
Add validation for parsed JSON structure.
The parsed JSON object is not validated before accessing json.input or json.related_query. If the structure is unexpected, this could fail silently or throw runtime errors.
🛡️ Suggested fix with validation
let userContent = '';
try {
const json = JSON.parse(userMsg.content as string);
- userContent = userMsg.type === 'input' ? json.input : json.related_query;
+ userContent = userMsg.type === 'input'
+ ? (json.input || 'No input content')
+ : (json.related_query || 'No related query');
} catch (e) {
userContent = userMsg.content as string;
}📝 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 json = JSON.parse(userMsg.content as string); | |
| userContent = userMsg.type === 'input' ? json.input : json.related_query; | |
| } catch (e) { | |
| userContent = userMsg.content as string; | |
| } | |
| const json = JSON.parse(userMsg.content as string); | |
| userContent = userMsg.type === 'input' | |
| ? (json.input || 'No input content') | |
| : (json.related_query || 'No related query'); | |
| } catch (e) { | |
| userContent = userMsg.content as string; | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/utils/report-generator.ts` around lines 90 - 94, The code parses
userMsg.content into json and directly reads json.input or json.related_query;
add validation to ensure the parsed value is an object and contains the expected
keys before using them. In the try block where JSON.parse(userMsg.content) is
assigned to json, verify typeof json === 'object' && json !== null and then use
checks like 'input' in json or 'related_query' in json (or optional chaining
with fallbacks) to set userContent safely; keep the existing catch fallback to
userMsg.content. Update the logic around userMsg, json, and userContent to avoid
runtime errors when the parsed structure is unexpected.
| const data = JSON.parse(searchResult.content as string); | ||
|
|
||
| // GeoJSON Summary | ||
| if (data.summary) { | ||
| addTextWithAutoPageBreak(`Analysis Summary: ${data.summary}`, 11, 'normal', [80, 80, 80]); | ||
| } | ||
|
|
||
| const images = [data.mapboxImage, data.googleImage, data.image].filter(Boolean); | ||
| if (images.length > 0) { | ||
| const imgWidth = (contentWidth - 10) / 2; | ||
| const imgHeight = (imgWidth * 3) / 4; | ||
|
|
||
| checkPageBreak(imgHeight + 10); | ||
|
|
||
| for (let i = 0; i < Math.min(images.length, 2); i++) { | ||
| doc.addImage(images[i], 'JPEG', margin + (i * (imgWidth + 10)), yOffset, imgWidth, imgHeight); | ||
| } | ||
| yOffset += imgHeight + 10; | ||
| } | ||
| } catch (e) { | ||
| console.error('Error parsing resolution search result for PDF:', e); | ||
| } |
There was a problem hiding this comment.
Validate parsed search result structure before accessing properties.
The code accesses data.summary, data.mapboxImage, data.googleImage, and data.image without validating that these properties exist in the parsed object. This could cause runtime errors if the structure is unexpected.
🛡️ Suggested fix with proper validation
try {
const data = JSON.parse(searchResult.content as string);
// GeoJSON Summary
- if (data.summary) {
+ if (data && typeof data === 'object' && data.summary) {
addTextWithAutoPageBreak(`Analysis Summary: ${data.summary}`, 11, 'normal', [80, 80, 80]);
}
- const images = [data.mapboxImage, data.googleImage, data.image].filter(Boolean);
+ const images = data && typeof data === 'object'
+ ? [data.mapboxImage, data.googleImage, data.image].filter(Boolean)
+ : [];
if (images.length > 0) {🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/utils/report-generator.ts` around lines 112 - 133, Ensure the parsed JSON
from searchResult is an object and has the expected properties before using
them: after const data = JSON.parse(...), check that data is a non-null object
and that summary is a string before calling addTextWithAutoPageBreak, and verify
mapboxImage/googleImage/image are valid non-empty strings (or valid image
payloads) before adding them to the images array and calling doc.addImage; use
optional chaining/typeof checks and provide safe defaults, only call
checkPageBreak/doc.addImage when image entries pass validation, and keep error
handling around parsing/image rendering (refer to searchResult, data,
addTextWithAutoPageBreak, checkPageBreak, doc.addImage, and yOffset).
| checkPageBreak(imgHeight + 10); | ||
|
|
||
| for (let i = 0; i < Math.min(images.length, 2); i++) { | ||
| doc.addImage(images[i], 'JPEG', margin + (i * (imgWidth + 10)), yOffset, imgWidth, imgHeight); |
There was a problem hiding this comment.
Verify image format before hardcoding 'JPEG'.
The code assumes all images are JPEG format, but the images from different sources (mapboxImage, googleImage, image) may be in different formats (PNG, WebP, etc.). jsPDF may fail or produce corrupted output if the format doesn't match.
Consider detecting the format from the data URL prefix or use a more flexible format like 'PNG' that supports transparency, or handle each image's actual format:
🔍 Suggested improvement
for (let i = 0; i < Math.min(images.length, 2); i++) {
- doc.addImage(images[i], 'JPEG', margin + (i * (imgWidth + 10)), yOffset, imgWidth, imgHeight);
+ // Detect format from data URL or default to JPEG
+ const format = images[i].startsWith('data:image/png') ? 'PNG' : 'JPEG';
+ doc.addImage(images[i], format, margin + (i * (imgWidth + 10)), yOffset, imgWidth, imgHeight);
}📝 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.
| doc.addImage(images[i], 'JPEG', margin + (i * (imgWidth + 10)), yOffset, imgWidth, imgHeight); | |
| for (let i = 0; i < Math.min(images.length, 2); i++) { | |
| // Detect format from data URL or default to JPEG | |
| const format = images[i].startsWith('data:image/png') ? 'PNG' : 'JPEG'; | |
| doc.addImage(images[i], format, margin + (i * (imgWidth + 10)), yOffset, imgWidth, imgHeight); | |
| } |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/utils/report-generator.ts` at line 127, The call to
doc.addImage(images[i], 'JPEG', ...) hardcodes JPEG but images (mapboxImage,
googleImage, image) may be PNG/WebP/etc.; update the image handling around the
images array and the doc.addImage call to detect each image's format from its
data URL prefix (inspect the "data:*/*;base64," mime type), map the mime type to
jsPDF's expected format string (e.g., 'PNG' for image/png, 'JPEG' for
image/jpeg, etc.), and pass that variable format into doc.addImage for each
image, with a sensible fallback (e.g., 'PNG') if the mime type is missing or
unsupported. Ensure detection logic is colocated with the images array
construction and used when calling doc.addImage so each image is added with the
correct format.
| const coords = JSON.stringify(feature.geometry.coordinates).substring(0, 100) + '...'; | ||
| doc.text(`Coordinates: ${coords}`, margin + 5, yOffset); |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
Improve coordinate truncation to avoid cutting mid-value.
The current implementation uses substring(0, 100) which can truncate in the middle of a coordinate pair or number, producing confusing output like [[-122.4, 37.7], [-122.3, 3....
♻️ Suggested improvement for cleaner truncation
- const coords = JSON.stringify(feature.geometry.coordinates).substring(0, 100) + '...';
- doc.text(`Coordinates: ${coords}`, margin + 5, yOffset);
+ // Show first few coordinate pairs cleanly
+ const coords = feature.geometry.coordinates;
+ const coordsArray = Array.isArray(coords[0]) ? coords.slice(0, 3) : [coords];
+ const coordsStr = JSON.stringify(coordsArray) + (coords.length > 3 ? '... (truncated)' : '');
+ doc.text(`Coordinates: ${coordsStr}`, margin + 5, yOffset);📝 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 coords = JSON.stringify(feature.geometry.coordinates).substring(0, 100) + '...'; | |
| doc.text(`Coordinates: ${coords}`, margin + 5, yOffset); | |
| // Show first few coordinate pairs cleanly | |
| const coords = feature.geometry.coordinates; | |
| const coordsArray = Array.isArray(coords[0]) ? coords.slice(0, 3) : [coords]; | |
| const coordsStr = JSON.stringify(coordsArray) + (coords.length > 3 ? '... (truncated)' : ''); | |
| doc.text(`Coordinates: ${coordsStr}`, margin + 5, yOffset); |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/utils/report-generator.ts` around lines 162 - 163, The coordinate string
is being naively truncated with substring(0, 100) which can cut a number or
coordinate pair mid-value; modify the logic that builds coords (using
feature.geometry.coordinates) to serialize coordinates element-by-element and
append whole coordinate entries until a character limit (e.g., 100) would be
exceeded, then append an ellipsis, and use that result in the existing doc.text
call (the variable coords and the doc.text(`Coordinates: ${coords}`, margin + 5,
yOffset) should remain but receive the safely truncated string).
- Implemented `generateReport` utility with `jsPDF` for comprehensive PDF export. - Added automatic page breaking and markdown-to-text conversion for long AI responses. - Included map snapshots, analysis summaries, and a drawings appendix. - Created `ReportButton` component using React Portals to ensure safe rendering in the global Header. - Verified successful production build after addressing SSR/prerendering constraints. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
|
@coderabbitai the front end of the map settings should be replaced with the report generation feature, place the button their an name that the title of the setting reports. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: Length of output: 5342
1.
|
I have updated the Settings view as requested. The 'Map' tab has been renamed to 'Reports' and now features a dedicated section for Report Generation with a prominent button to generate and download the PDF report. I've also moved the map provider settings into this new 'Reports' tab to keep the interface clean. |
- Renamed 'Map' tab to 'Reports' in Settings. - Added 'Report Generation' section to the Reports tab. - Updated `ReportButton` to support an inline display mode with a label. - Moved map provider settings into the new Reports tab. - Maintained the existing 'Download Report' button in the header via portals. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
There was a problem hiding this comment.
Actionable comments posted: 1
♻️ Duplicate comments (1)
components/report-button.tsx (1)
3-3: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winRemove redundant React namespace import.
The React namespace import is not used anywhere in this file and is unnecessary in React 19 with the modern JSX transform. Only the named imports (
useState,useEffect) are needed.🧹 Suggested cleanup
-import React, { useState, useEffect } from 'react' +import { useState, useEffect } from 'react'As per coding guidelines, React 19 no longer requires the React import for JSX transformation.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/report-button.tsx` at line 3, Remove the unused default React namespace from the import in components/report-button.tsx; keep only the named hooks by changing the import that currently reads like "import React, { useState, useEffect } from 'react'" to import only "{ useState, useEffect }" so the file uses the modern JSX transform without an unused React symbol.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@package.json`:
- Line 51: The dependency entry "@types/jspdf" may not match the jsPDF runtime
(4.2.1); audit all places using jsPDF types (search for jsPDF, JsPDF, and
relevant constructors/methods) to verify they are covered by `@types/jspdf`@2.0.0,
and then either replace/update the type source: remove `@types/jspdf` if jsPDF 4.x
includes its own types, upgrade to a community/official types package that lists
4.x support, or add missing type declarations (ambient module augmentations or
specific d.ts shims) for any methods/classes used but missing in `@types/jspdf`;
ensure package.json reflects the chosen approach and run TypeScript type-check
to confirm no new errors.
---
Duplicate comments:
In `@components/report-button.tsx`:
- Line 3: Remove the unused default React namespace from the import in
components/report-button.tsx; keep only the named hooks by changing the import
that currently reads like "import React, { useState, useEffect } from 'react'"
to import only "{ useState, useEffect }" so the file uses the modern JSX
transform without an unused React symbol.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4c62848b-aa28-41bd-87eb-351a5d794237
⛔ Files ignored due to path filters (1)
bun.lockis excluded by!**/*.lock
📒 Files selected for processing (5)
components/chat.tsxcomponents/header.tsxcomponents/report-button.tsxcomponents/settings/components/settings.tsxpackage.json
📜 Review details
🔇 Additional comments (6)
components/header.tsx (1)
73-74: LGTM!components/report-button.tsx (2)
30-53: LGTM!
55-77: LGTM!components/settings/components/settings.tsx (1)
26-26: LGTM!Also applies to: 170-170, 207-240
components/chat.tsx (1)
24-24: LGTM!Also applies to: 143-143, 190-190
package.json (1)
78-78: ⚡ Quick winmarkdown-to-txt@2.0.1 is available and current. Version 2.0.1 exists on npm and is the latest available version. The
^2.0.1constraint is valid and stable.
| "@tailwindcss/typography": "^0.5.16", | ||
| "@tavily/core": "^0.6.4", | ||
| "@turf/turf": "^7.2.0", | ||
| "@types/jspdf": "^2.0.0", |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Description: Check jspdf package versions and compatibility
# Check latest available version of jspdf
echo "=== Latest jspdf version ==="
npm view jspdf version
# Check if version 4.2.1 exists
echo -e "\n=== Checking if jspdf@4.2.1 exists ==="
npm view jspdf@4.2.1 version 2>&1 || echo "Version 4.2.1 not found"
# Check available versions of jspdf
echo -e "\n=== Recent jspdf versions ==="
npm view jspdf versions --json | jq '.[-10:]'
# Check latest `@types/jspdf` version
echo -e "\n=== Latest `@types/jspdf` version ==="
npm view `@types/jspdf` version
# Check for security advisories
echo -e "\n=== Security advisories for jspdf ==="
npm audit --package-lock-only --json | jq '.vulnerabilities.jspdf // "No vulnerabilities found"'Repository: QueueLab/QCX
Length of output: 757
Verify @types/jspdf type coverage for jsPDF 4.2.1.
jsPDF 4.2.1 is the current stable version, not an outdated pre-release. However, @types/jspdf@2.0.0 (the latest available types) may lack type definitions for features introduced in jsPDF 3.x and 4.x. Check that your codebase only uses types from the 2.x definitions or consider if a newer types package is available with proper 4.x support.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@package.json` at line 51, The dependency entry "@types/jspdf" may not match
the jsPDF runtime (4.2.1); audit all places using jsPDF types (search for jsPDF,
JsPDF, and relevant constructors/methods) to verify they are covered by
`@types/jspdf`@2.0.0, and then either replace/update the type source: remove
`@types/jspdf` if jsPDF 4.x includes its own types, upgrade to a
community/official types package that lists 4.x support, or add missing type
declarations (ambient module augmentations or specific d.ts shims) for any
methods/classes used but missing in `@types/jspdf`; ensure package.json reflects
the chosen approach and run TypeScript type-check to confirm no new errors.
|
@coderabbitai can you remove the icon at the top bar and do not notify the user when you've generated it. |
|
Tip For best results, initiate chat on the files or code changes.
The requested changes have been implemented and a pull request has been created: View PR |
I have removed the report icon from the header bar and disabled the success notification after report generation, as requested. The report generation is now primarily accessible through the 'Reports' tab in Settings. |
- Removed `ReportButton` from the header. - Disabled success toast notification after successful report generation. - Cleaned up unused portal target for the report button in the header. - Kept the feature accessible via the 'Reports' tab in Settings. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
|
@coderabbitai I need to synchronize this branch with the main branch and retain all the code whilst maintain functionality. |
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
# Check how far behind the PR branch is from main
gh api repos/QueueLab/QCX/compare/main...feature/pdf-report-generation-10179943642823521584 \
--jq '{ahead_by: .ahead_by, behind_by: .behind_by, status: .status}'
echo "---"
# Check if there are any open merge conflicts or blocking status checks
gh pr view 602 --repo QueueLab/QCX --json mergeable,mergeStateStatus,baseRefName,headRefNameLength of output: 646
Option A — I'll do it for you (reply "yes, go ahead"): Option B — Do it locally yourself: git checkout feature/pdf-report-generation-10179943642823521584
git fetch origin
git merge origin/main
# resolve any conflicts if prompted (GitHub says there are none, so it should be clean)
git push origin feature/pdf-report-generation-10179943642823521584Option C — Do it via the GitHub UI: A note on |
I have successfully merged the latest changes from the main branch into this feature branch. All code for the PDF report generation has been retained, and I've verified that the application still builds successfully and maintains its full functionality. |
- Merged the latest changes from the main branch. - Verified that PDF report generation functionality is preserved. - Confirmed successful production build. Co-authored-by: ngoiyaeric <115367894+ngoiyaeric@users.noreply.github.com>
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 (2)
.env.local.example (1)
19-25:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winDocument
XAI_API_KEYin the sample env file.
getModel()now prefers xAI before Gemini by default, but this example still only shows Gemini and Tavily keys. New deployments following.env.local.examplewon't see how to configure the new primary provider without reading source. Add anXAI_API_KEYentry alongside the other AI provider variables.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In @.env.local.example around lines 19 - 25, Add an XAI_API_KEY entry to the sample environment file so deployments can configure xAI as the new preferred provider; update the block that currently lists GEMINI_3_PRO_API_KEY and TAVILY_API_KEY to include a clearly named XAI_API_KEY=your_xai_api_key_here entry (matching the key name expected by getModel()/configuration), with the same comment style as the other provider keys.components/report-button.tsx (1)
48-61:⚠️ Potential issue | 🟡 Minor | ⚡ Quick winAdd an accessible name to the icon-only button.
When
inlineisfalse, this control renders without visible text.titlealone is not a reliable accessible name, so add anaria-labelfor the icon-only variant.Suggested change
<Button variant={inline ? "default" : "ghost"} size={inline ? "default" : "icon"} onClick={handleDownloadReport} title="Download PDF Report" + aria-label={inline ? undefined : "Download PDF Report"} disabled={isGenerating} className={inline ? "w-full" : ""} >🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@components/report-button.tsx` around lines 48 - 61, The icon-only Button rendered in ReportButton (props/vars: inline, isGenerating, Button, handleDownloadReport) lacks a reliable accessible name; add an aria-label to the Button when inline is false (the icon-only variant) — e.g. conditionally pass aria-label={inline ? undefined : isGenerating ? "Generating report" : "Generate report"} so screen readers receive a clear name while keeping the existing title and behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/agents/resolution-search.tsx`:
- Line 130: The prompt text in lib/agents/resolution-search.tsx asks for "news
context" which doesn't match the schema; update the prompt (the string that
contains "7. **Structured Output:**") to use the exact schema field names:
replace "news context" with the concrete fields hasRecentNews and newsItems and
instruct the model to populate those keys (hasRecentNews: boolean, newsItems:
array of objects) so the output aligns with the expected schema and validation
(locate the prompt string in the resolution-search.tsx prompt builder /
generatePrompt function and update it accordingly).
In `@lib/agents/tools/geospatial.tsx`:
- Around line 347-368: The parameter validation and toolArgs construction
(switch on queryType that references params, includeMap, etc.) must be executed
before establishing the MCP connection and calling getConnectedMcpClient() /
listTools(); move the guard logic that builds toolArgs (the switch block
handling 'directions', 'distance', 'reverse', 'search', 'geocode', 'map') to run
first, throw early on malformed params (missing origin/destination, coordinates,
query, or location), and only after toolArgs is successfully created proceed to
call getConnectedMcpClient() and listTools() so invalid requests short-circuit
without opening external connections.
In `@lib/schema/geospatial.tsx`:
- Around line 42-45: The geospatial schema currently allows an unsupported
'transit' routing mode which can be passed through to Mapbox and cause runtime
failures; update the zod enum in lib/schema/geospatial.tsx (the field defined as
mode: z.enum([...]).optional().default('driving').describe(...)) to only include
'driving', 'walking', and 'cycling' (remove 'transit'), keep the default
'driving', and ensure any validation or docs tied to that schema reflect the
three supported modes; also verify callers in lib/agents/tools/geospatial.tsx
that pass params.mode (used as profile/mode) will only receive those three
values.
In `@lib/schema/resolution-search.ts`:
- Around line 12-24: The geoJson shape currently lets any geometryType string
and only supports up to Polygon coordinate nesting; update the geoJson schema
(the geoJson object in lib/schema/resolution-search.ts) to validate known
GeoJSON geometry types and their matching coordinate structures: replace
geometryType: z.string() with a z.enum listing
"Point","MultiPoint","LineString","MultiLineString","Polygon","MultiPolygon","GeometryCollection","Feature","FeatureCollection"
and make coordinates a discriminated union (or use a z.discriminatedUnion keyed
on geometryType) that enforces 1D arrays for Point/MultiPoint, 2D for
LineString/MultiLineString, 3D for Polygon/MultiPolygon, and supports nested
arrays for Multi* types so valid MultiPolygon coordinates are accepted and
unsupported shapes like "Circle" are rejected; ensure this aligns with how
app/actions.tsx and GeoJsonLayer expect GeoJSON so rendered payloads match valid
geometry.
---
Outside diff comments:
In @.env.local.example:
- Around line 19-25: Add an XAI_API_KEY entry to the sample environment file so
deployments can configure xAI as the new preferred provider; update the block
that currently lists GEMINI_3_PRO_API_KEY and TAVILY_API_KEY to include a
clearly named XAI_API_KEY=your_xai_api_key_here entry (matching the key name
expected by getModel()/configuration), with the same comment style as the other
provider keys.
In `@components/report-button.tsx`:
- Around line 48-61: The icon-only Button rendered in ReportButton (props/vars:
inline, isGenerating, Button, handleDownloadReport) lacks a reliable accessible
name; add an aria-label to the Button when inline is false (the icon-only
variant) — e.g. conditionally pass aria-label={inline ? undefined : isGenerating
? "Generating report" : "Generate report"} so screen readers receive a clear
name while keeping the existing title and behavior.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: ASSERTIVE
Plan: Pro
Run ID: 0504f067-de43-4f88-b42c-908246134f6b
📒 Files selected for processing (12)
.env.local.exampleGEMINI_3.1_PRO_INTEGRATION.mdapp/actions.tsxcomponents/chat.tsxcomponents/report-button.tsxlib/agents/resolution-search.tsxlib/agents/tools/geospatial.tsxlib/agents/tools/index.tsxlib/agents/tools/search.tsxlib/schema/geospatial.tsxlib/schema/resolution-search.tslib/utils/index.ts
💤 Files with no reviewable changes (1)
- components/chat.tsx
📜 Review details
🔇 Additional comments (7)
lib/agents/tools/index.tsx (1)
27-32: LGTM!lib/agents/tools/search.tsx (1)
42-52: LGTM!lib/utils/index.ts (1)
82-104: LGTM!GEMINI_3.1_PRO_INTEGRATION.md (1)
13-14: LGTM!Also applies to: 64-65
lib/schema/geospatial.tsx (1)
3-41: LGTM!Also applies to: 46-61
components/report-button.tsx (2)
3-3: Drop the unused defaultReactimport.Already raised on an earlier revision;
Reactis not referenced in this file, and the JSX runtime does not need the default import here.
27-35: ⚡ Quick winMake map snapshot export best-effort in
components/report-button.tsxWrap
map.getCanvas().toDataURL('image/png')so a canvas export failure doesn’t abortgenerateReport; fall back to''and continue generating the PDF.Suggested change
- const mapSnapshot = map ? map.getCanvas().toDataURL('image/png') : '' + let mapSnapshot = '' + if (map) { + try { + mapSnapshot = map.getCanvas().toDataURL('image/png') + } catch (error) { + console.warn('Map snapshot unavailable for report:', error) + } + }
| 5. **COG Applicability:** Determine if this location would benefit from Cloud Optimized GeoTIFF (COG) analysis for high-precision temporal or spectral data. | ||
| 6. **News Integration:** Reference any recent news or events that may be relevant to the current state of the location. | ||
| 7. **Structured Output:** Return your findings in a structured JSON format including summary, geoJson, and newsContext. | ||
| 7. **Structured Output:** Return your findings in a structured JSON format including summary, geoJson (if any), news context, and any extracted coordinates or COG information. Use the provided schema. |
There was a problem hiding this comment.
Use the exact schema field names in the prompt.
Line 130 asks for “news context”, but the schema only accepts hasRecentNews and newsItems. That mismatch makes the model more likely to emit the wrong key or bury the data in summary, which then gets lost at validation time. Please name the concrete fields the schema expects.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/agents/resolution-search.tsx` at line 130, The prompt text in
lib/agents/resolution-search.tsx asks for "news context" which doesn't match the
schema; update the prompt (the string that contains "7. **Structured Output:**")
to use the exact schema field names: replace "news context" with the concrete
fields hasRecentNews and newsItems and instruct the model to populate those keys
(hasRecentNews: boolean, newsItems: array of objects) so the output aligns with
the expected schema and validation (locate the prompt string in the
resolution-search.tsx prompt builder / generatePrompt function and update it
accordingly).
| const toolArgs = (() => { | ||
| switch (queryType) { | ||
| case 'directions': return { waypoints: [params.origin, params.destination], includeMapPreview: includeMap, profile: params.mode }; | ||
| case 'distance': return { places: [params.origin, params.destination], includeMapPreview: includeMap, mode: params.mode || 'driving' }; | ||
| case 'reverse': return { searchText: `${params.coordinates.latitude},${params.coordinates.longitude}`, includeMapPreview: includeMap, maxResults: params.maxResults || 5 }; | ||
| case 'search': return { searchText: params.query, includeMapPreview: includeMap, maxResults: params.maxResults || 5, ...(params.coordinates && { proximity: `${params.coordinates.latitude},${params.coordinates.longitude}` }), ...(params.radius && { radius: params.radius }) }; | ||
| case 'geocode': | ||
| case 'map': return { searchText: params.location, includeMapPreview: includeMap, maxResults: queryType === 'geocode' ? params.maxResults || 5 : undefined }; | ||
| case 'directions': { | ||
| if (!params.origin || !params.destination) throw new Error("'directions' query requires origin and destination"); | ||
| return { waypoints: [params.origin, params.destination], includeMapPreview: includeMap, profile: params.mode }; | ||
| } | ||
| case 'distance': { | ||
| if (!params.origin || !params.destination) throw new Error("'distance' query requires origin and destination"); | ||
| return { places: [params.origin, params.destination], includeMapPreview: includeMap, mode: params.mode || 'driving' }; | ||
| } | ||
| case 'reverse': { | ||
| if (!params.coordinates) throw new Error("'reverse' query requires coordinates"); | ||
| return { searchText: `${params.coordinates.latitude},${params.coordinates.longitude}`, includeMapPreview: includeMap, maxResults: params.maxResults || 5 }; | ||
| } | ||
| case 'search': { | ||
| if (!params.query) throw new Error("'search' query requires query"); | ||
| return { searchText: params.query, includeMapPreview: includeMap, maxResults: params.maxResults || 5, ...(params.coordinates && { proximity: `${params.coordinates.latitude},${params.coordinates.longitude}` }), ...(params.radius && { radius: params.radius }) }; | ||
| } | ||
| case 'geocode': | ||
| case 'map': { | ||
| if (!params.location) throw new Error(`'${queryType}' query requires location`); | ||
| return { searchText: params.location, includeMapPreview: includeMap, maxResults: queryType === 'geocode' ? params.maxResults || 5 : undefined }; |
There was a problem hiding this comment.
Validate params before opening the MCP connection.
These guards run after getConnectedMcpClient() and the listTools() probe. With the flatter schema, malformed inputs now still pay for an outbound connection and tool discovery before we reject them locally. Move the argument-building/validation step before Line 314 so bad requests short-circuit without external I/O.
Suggested direction
- const mcpClient = await getConnectedMcpClient();
+ const toolArgs = buildToolArgs(params, includeMap);
+ const mcpClient = await getConnectedMcpClient();
if (!mcpClient) {
feedbackMessage = 'Geospatial functionality is unavailable. Please check configuration.';
uiFeedbackStream.update(feedbackMessage);
uiFeedbackStream.done();
uiStream.update(<BotMessage content={uiFeedbackStream.value} />);
return { type: 'MAP_QUERY_TRIGGER', originalUserInput: JSON.stringify(params), timestamp: new Date().toISOString(), mcp_response: null, error: 'MCP client initialization failed' };
}
@@
- const toolArgs = (() => {
- switch (queryType) {
- ...
- }
- })();🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/agents/tools/geospatial.tsx` around lines 347 - 368, The parameter
validation and toolArgs construction (switch on queryType that references
params, includeMap, etc.) must be executed before establishing the MCP
connection and calling getConnectedMcpClient() / listTools(); move the guard
logic that builds toolArgs (the switch block handling 'directions', 'distance',
'reverse', 'search', 'geocode', 'map') to run first, throw early on malformed
params (missing origin/destination, coordinates, query, or location), and only
after toolArgs is successfully created proceed to call getConnectedMcpClient()
and listTools() so invalid requests short-circuit without opening external
connections.
| mode: z.enum(['driving', 'walking', 'cycling', 'transit']) | ||
| .optional() | ||
| .default('driving') | ||
| .describe("Transportation mode (used by 'directions' and 'distance')"), |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
Does the Mapbox Directions API or Matrix API support a "transit" routing profile? What routing profiles are officially supported?
💡 Result:
No, neither the Mapbox Directions API nor the Matrix API supports a transit routing profile [1][2][3]. The Mapbox Directions and Matrix APIs officially support the following four routing profiles [1][2][3]: 1. mapbox/driving: For automotive routing, prioritizing the fastest routes [1][2][3]. 2. mapbox/driving-traffic: For automotive routing, factoring in current and historic traffic conditions [1][2][3]. 3. mapbox/walking: For pedestrian and hiking routing [1][2][3]. 4. mapbox/cycling: For bicycle routing, preferring paths with bike lanes and avoiding highways [1][2][3]. While Mapbox provides tools and data that can be used within transit applications—such as geocoding, base maps, and navigation SDKs for vehicle-based transit services—it does not offer a native API profile for multi-modal public transit journey planning [4][5].
Citations:
- 1: https://docs.mapbox.com/api/navigation/directions/
- 2: https://docs.mapbox.com/help/glossary/routing-profile/
- 3: https://docs.mapbox.com/api/navigation/matrix/
- 4: https://www.reddit.com/r/mapbox/comments/bmhx9t/public_transit_for_mapbox/
- 5: https://www.mapbox.com/showcase/blaise-transit
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Locate the exact schema and tool mapping for "mode"
rg -n "mode:\s*z\.enum\(" lib/schema/geospatial.tsx || true
rg -n "'driving'|'walking'|'cycling'|'transit'" lib/schema/geospatial.tsx || true
rg -n "geospatial\.tsx" -S lib/agents/tools || true
rg -n "directions|distance|matrix|routing profile|routing_profile|profile" lib/agents/tools/geospatial.tsx || true
# Print the parts of the tool that mention mode/profile mapping
sed -n '1,220p' lib/agents/tools/geospatial.tsx | cat -n
sed -n '1,120p' lib/schema/geospatial.tsx | cat -nRepository: QueueLab/QCX
Length of output: 14984
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Show the execute branch around where profile: params.mode is set
sed -n '300,420p' lib/agents/tools/geospatial.tsx | cat -n
# Show how "profile" is used in downstream request construction
rg -n "profile:\s*params\.mode|profile\W*:" lib/agents/tools/geospatial.tsx
rg -n "Directions|Matrix|matrix|directions" lib/agents/tools/geospatial.tsx
# If profile is transformed anywhere, capture those lines
rg -n "params\.mode|mode\s*===" lib/agents/tools/geospatial.tsxRepository: QueueLab/QCX
Length of output: 8421
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Find any mention of transit in the repo
rg -n "transit" lib || true
# Find any explicit Mapbox routing profile strings
rg -n "mapbox/(driving|driving-traffic|walking|cycling)" lib || true
# Find where "profile" or "mode" are translated/mapped for Mapbox requests (heuristic)
rg -n "profile:\s*|routing profile|routing_profile|driving-traffic|driving_traffic" lib/agents/tools lib/schema || trueRepository: QueueLab/QCX
Length of output: 554
Remove the unsupported transit routing mode from the geospatial schema.
lib/agents/tools/geospatial.tsx passes params.mode straight through to the downstream MCP/Mapbox calls (profile: params.mode for directions, mode: params.mode for distance), while the tool description only advertises driving, walking, and cycling. Mapbox Directions/Matrix routing profiles do not include transit, so allowing it in the schema can lead to runtime failures.
- mode: z.enum(['driving', 'walking', 'cycling', 'transit'])
+ mode: z.enum(['driving', 'walking', 'cycling'])🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/schema/geospatial.tsx` around lines 42 - 45, The geospatial schema
currently allows an unsupported 'transit' routing mode which can be passed
through to Mapbox and cause runtime failures; update the zod enum in
lib/schema/geospatial.tsx (the field defined as mode:
z.enum([...]).optional().default('driving').describe(...)) to only include
'driving', 'walking', and 'cycling' (remove 'transit'), keep the default
'driving', and ensure any validation or docs tied to that schema reflect the
three supported modes; also verify callers in lib/agents/tools/geospatial.tsx
that pass params.mode (used as profile/mode) will only receive those three
values.
| geoJson: z.object({ | ||
| type: z.string().describe("Must be 'FeatureCollection'"), | ||
| features: z.array(z.object({ | ||
| type: z.string().describe("Must be 'Feature'"), | ||
| geometryType: z.string().describe("The type of geometry, e.g., 'Point', 'Polygon'"), | ||
| coordinates: z.array(z.number()) | ||
| .or(z.array(z.array(z.number()))) | ||
| .or(z.array(z.array(z.array(z.number())))) | ||
| .describe('Coordinates for the geometry'), | ||
| name: z.string().describe('Name of the feature or point of interest'), | ||
| description: z.string().optional().describe('Description of the feature') | ||
| })) | ||
| }).optional().describe('A collection of points of interest and classified land features to be overlaid on the map.'), |
There was a problem hiding this comment.
Tighten the GeoJSON shape contract before it reaches the map layer.
This schema accepts any geometryType string, but coordinates only support up to Polygon nesting. That means invalid values like Circle can pass validation, while valid GeoJSON shapes like MultiPolygon are rejected. Since app/actions.tsx reconstructs and renders this payload as GeoJSON, one bad model output will either fail the structured response or reach GeoJsonLayer as invalid geometry.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@lib/schema/resolution-search.ts` around lines 12 - 24, The geoJson shape
currently lets any geometryType string and only supports up to Polygon
coordinate nesting; update the geoJson schema (the geoJson object in
lib/schema/resolution-search.ts) to validate known GeoJSON geometry types and
their matching coordinate structures: replace geometryType: z.string() with a
z.enum listing
"Point","MultiPoint","LineString","MultiLineString","Polygon","MultiPolygon","GeometryCollection","Feature","FeatureCollection"
and make coordinates a discriminated union (or use a z.discriminatedUnion keyed
on geometryType) that enforces 1D arrays for Point/MultiPoint, 2D for
LineString/MultiLineString, 3D for Polygon/MultiPolygon, and supports nested
arrays for Multi* types so valid MultiPolygon coordinates are accepted and
unsupported shapes like "Circle" are rejected; ensure this aligns with how
app/actions.tsx and GeoJsonLayer expect GeoJSON so rendered payloads match valid
geometry.
This PR implements a PDF report generation feature for QCX.
Key features:
useAIState,useMapData, anduseMapto gather all necessary context for the report.PR created automatically by Jules for task 10179943642823521584 started by @ngoiyaeric
Summary by CodeRabbit
New Features
Improvements
Chores