Skip to content
Open
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
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ A Next.js application that demonstrates the VisualDx API's ability to analyze sk
- Error handling for various API states
- Responsive design
- Results display with lesion identification and therapy recommendations

- **Product recommendations with images** - Display therapy-recommended skincare products with thumbnails
## Getting Started

### Prerequisites
Expand Down Expand Up @@ -99,6 +99,11 @@ The application handles various error states:

Update the entries in `src/lib/lesions-data.ts` (and regenerate any helpers in `src/lib/lesion-info.ts` if needed) to add new lesion types and their corresponding therapy messages.

### Adding New Products

1. Add product entry to `src/lib/products-data.ts`
2. Add product image to `public/images/products/` as `product-{id}.jpg`

### Result Messaging

Known lesions render as:
Expand Down
9 changes: 9 additions & 0 deletions public/images/products/placeholder.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-1.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-10.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-11.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-12.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-13.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-14.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-15.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-16.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-17.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-18.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-19.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-2.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-20.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-21.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-22.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-23.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-24.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-25.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-26.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-27.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-28.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-29.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-3.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-30.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added public/images/products/product-31.jpg
Binary file added public/images/products/product-32.jpg
Binary file added public/images/products/product-33.jpg
Binary file added public/images/products/product-34.jpg
Binary file added public/images/products/product-35.jpg
Binary file added public/images/products/product-36.jpg
Binary file added public/images/products/product-37.jpg
Binary file added public/images/products/product-38.jpg
Binary file added public/images/products/product-39.jpg
Binary file added public/images/products/product-4.jpg
Binary file added public/images/products/product-40.jpg
Binary file added public/images/products/product-41.jpg
Binary file added public/images/products/product-43.jpg
Binary file added public/images/products/product-44.jpg
Binary file added public/images/products/product-45.jpg
Binary file added public/images/products/product-46.jpg
Binary file added public/images/products/product-47.jpg
Binary file added public/images/products/product-48.jpg
Binary file added public/images/products/product-49.jpg
Binary file added public/images/products/product-5.jpg
Binary file added public/images/products/product-50.jpg
Binary file added public/images/products/product-51.jpg
Binary file added public/images/products/product-52.jpg
Binary file added public/images/products/product-53.jpg
Binary file added public/images/products/product-54.jpg
Binary file added public/images/products/product-55.jpg
Binary file added public/images/products/product-56.jpg
Binary file added public/images/products/product-57.jpg
Binary file added public/images/products/product-58.jpg
Binary file added public/images/products/product-59.jpg
Binary file added public/images/products/product-6.jpg
Binary file added public/images/products/product-60.jpg
Binary file added public/images/products/product-61.jpg
Binary file added public/images/products/product-62.jpg
Binary file added public/images/products/product-63.jpg
Binary file added public/images/products/product-64.jpg
Binary file added public/images/products/product-65.jpg
Binary file added public/images/products/product-66.jpg
Binary file added public/images/products/product-67.jpg
Binary file added public/images/products/product-69.jpg
Binary file added public/images/products/product-7.jpg
Binary file added public/images/products/product-71.jpg
Binary file added public/images/products/product-72.jpg
Binary file added public/images/products/product-73.jpg
Binary file added public/images/products/product-74.jpg
Binary file added public/images/products/product-75.jpg
Binary file added public/images/products/product-76.jpg
Binary file added public/images/products/product-77.jpg
Binary file added public/images/products/product-78.jpg
Binary file added public/images/products/product-79.jpg
Binary file added public/images/products/product-8.jpg
Binary file added public/images/products/product-80.jpg
Binary file added public/images/products/product-81.jpg
Binary file added public/images/products/product-82.jpg
Binary file added public/images/products/product-83.jpg
Binary file added public/images/products/product-9.jpg
9 changes: 9 additions & 0 deletions src/app/page.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -191,6 +191,15 @@
margin-bottom: 0.75rem;
}

.productImage {
width: 100%;
height: 200px;
object-fit: contain;
border-radius: 4px;
margin-bottom: 0.75rem;
background: #f9f9f9;
}

.productName {
display: block;
font-weight: 600;
Expand Down
45 changes: 36 additions & 9 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,13 +24,15 @@ export default function Home() {
// Validate file type and size
if (!file.type.match(/^image\/(png|jpeg|jpg)$/)) {
setError('Only .png and .jpg images smaller than 10MB are supported');
setSelectedFile(null); // Clear selected file
setPreviewUrl('');
setResult(null); // Clear previous results
return;
}

if (file.size > 10 * 1024 * 1024) { // 10MB
setError('Only .png and .jpg images smaller than 10MB are supported');
setSelectedFile(null); // Clear selected file
setPreviewUrl('');
setResult(null); // Clear previous results
return;
Expand All @@ -57,6 +59,17 @@ export default function Home() {
return;
}

// Double-check file validation before submission
if (!selectedFile.type.match(/^image\/(png|jpeg|jpg)$/)) {
setError('Only .png and .jpg images smaller than 10MB are supported');
return;
}

if (selectedFile.size > 10 * 1024 * 1024) {
setError('Only .png and .jpg images smaller than 10MB are supported');
return;
}

setIsAnalyzing(true);
setError('');
setResult(null);
Expand Down Expand Up @@ -168,15 +181,29 @@ export default function Home() {
<div className={styles.productsContainer}>
<h3>Recommended Products</h3>
<div className={styles.productsGrid}>
{result.products.map((product) => (
<div key={product.productId} className={styles.productCard}>
<a href={product.purchaseLink} target="_blank" rel="noopener noreferrer">
<div className={styles.productImagePlaceholder}></div>
<span className={styles.productName}>{product.productName}</span>
</a>
<p className={styles.productDescription}>{product.description}</p>
</div>
))}
{result.products.map((product) => {
const imageSrc = product.imageFileName
? `/images/products/${product.imageFileName}`
: '/images/products/placeholder.svg';

return (
<div key={product.productId} className={styles.productCard}>
<a href={product.purchaseLink} target="_blank" rel="noopener noreferrer">
<img
src={imageSrc}
alt={product.productName}
className={styles.productImage}
onError={(e) => {
// Fallback to placeholder if image fails to load
(e.target as HTMLImageElement).src = '/images/products/placeholder.svg';
}}
/>
<span className={styles.productName}>{product.productName}</span>
</a>
<p className={styles.productDescription}>{product.description}</p>
</div>
);
})}
</div>
</div>
)}
Expand Down
3 changes: 2 additions & 1 deletion src/lib/lesion-info.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ function getProductsFromTherapyIds(therapyIdsString: any): ProductInfo[] {
productId: product.productId,
productName: product.productName,
description: product.description,
purchaseLink: product.purchaseLink
purchaseLink: product.purchaseLink,
imageFileName: (product as any).imageFileName || undefined
});
}
});
Expand Down
2 changes: 1 addition & 1 deletion src/lib/lesions-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -465,7 +465,7 @@ export const lesionsData = [
"lesionMessage": NaN,
"therapynames": "Exfoliating creams with urea, lactic acid, or salicylic acid",
"therapyMessage": "We recommend trying exfoliating creams with urea, lactic acid, or salicylic acid.",
"therapyIds": NaN
"therapyIds": "1,2,3,4,31"
},
{
"findingid": 3096,
Expand Down
Loading