Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

fix: Add validation and error handling for pin header rendering #616

Closed
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions bun-tests/parts-engine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,23 @@ test("findPart", async () => {

expect(supplierPartNumbers.jlcpcb!.length).toEqual(3)
})

test("findPart should return supplier part numbers for a resistor", async () => {
const result = await jlcPartsEngine.findPart({
sourceComponent: {
type: "source_component",
ftype: "simple_resistor",
source_component_id: "123",
name: "R1",
resistance: 1000,
},
footprinterString: "0402",
})

// Verify the structure of the response
expect(Array.isArray(result.jlcpcb)).toBe(true)
// Check that each part number starts with 'C'
result.jlcpcb?.forEach((partNumber) => {
expect(partNumber.startsWith("C")).toBe(true)
})
})
34 changes: 33 additions & 1 deletion src/components/PreviewContent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,39 @@ export const PreviewContent = ({
>
<ErrorBoundary FallbackComponent={ErrorFallback}>
{circuitJson ? (
<CadViewer soup={circuitJson as any} ref={threeJsObjectRef} />
<div className="relative w-full h-full">
{(() => {
try {
return (
<CadViewer
soup={circuitJson as any}
ref={threeJsObjectRef}
/>
)
} catch (error: any) {
// If it's the hull error, show a helpful message
if (
error?.message?.includes(
"Uncaught TypeError: Cannot read properties of undefined",
)
) {
return (
<div className="p-4 bg-red-50 border border-red-200 rounded-md">
<h3 className="text-red-800 font-medium mb-2">
3D Model Error
</h3>
<p className="text-sm text-red-700">
Unable to display 3D model: Missing geometry
data for one or more components.
</p>
</div>
)
}
// For other errors, re-throw to be caught by ErrorBoundary
throw error
}
})()}
</div>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be in a separate file <CadViewerWithErrorMessage />, we don't want to make giant files with custom handling and a ton of complexity, better to break things up

) : (
<PreviewEmptyState triggerRunTsx={triggerRunTsx} />
)}
Expand Down
56 changes: 41 additions & 15 deletions src/lib/jlc-parts-engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,25 +43,51 @@ export const jlcPartsEngine: PartsEngine = {
jlcpcb: capacitors.map((c: any) => `C${c.lcsc}`).slice(0, 3),
}
} else if (sourceComponent.ftype === "simple_pin_header") {
// 1. Default values
const defaultValues = {
gender: "male",
pin_count: undefined as number | undefined,
}

// 2. Extract pin count from footprint if available
if (!sourceComponent.pin_count && footprinterString) {
const pinCountMatch = footprinterString.match(/(\d+)/)
if (pinCountMatch) {
defaultValues.pin_count = parseInt(pinCountMatch[1], 10)
}
}

// 3. Use provided values or defaults
const pin_count = sourceComponent.pin_count || defaultValues.pin_count
const gender = sourceComponent.gender || defaultValues.gender

// 4. Early validation
if (!pin_count) {
console.warn(
`Pin count not found for pin header: ${sourceComponent.name}`,
)
return {}
}

// 5. Extract pitch if available
let pitch
if (footprinterString?.includes("_p")) {
pitch = footprinterString.split("_p")[1]
}
const { headers } = await getJlcPartsCached(
"headers",
pitch
? {
pitch: pitch,
num_pins: sourceComponent.pin_count,
gender: sourceComponent.gender,
}
: {
num_pins: sourceComponent.pin_count,
gender: sourceComponent.gender,
},
)
return {
jlcpcb: headers.map((h: any) => `C${h.lcsc}`).slice(0, 3),

// 6. Call API with validated parameters
try {
const { headers } = await getJlcPartsCached("headers", {
...(pitch ? { pitch } : {}),
num_pins: pin_count,
gender,
})
return {
jlcpcb: (headers || []).map((h: any) => `C${h.lcsc}`).slice(0, 3),
}
} catch (error: any) {
console.error(`Error fetching pin header parts: ${error.message}`)
return {}
}
}
return {}
Expand Down
Loading