fix: polish manuals staging rollout
This commit is contained in:
parent
d6403b6296
commit
e3b97f361b
4 changed files with 49 additions and 8 deletions
|
|
@ -7,6 +7,33 @@ import { getManualAssetFromStorage } from "@/lib/manuals-object-storage"
|
|||
|
||||
export const dynamic = "force-dynamic"
|
||||
|
||||
function buildPlaceholderSvg(label: string) {
|
||||
const safeLabel = label
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'")
|
||||
|
||||
return `
|
||||
<svg xmlns="http://www.w3.org/2000/svg" width="800" height="480" viewBox="0 0 800 480" fill="none">
|
||||
<rect width="800" height="480" rx="36" fill="#F5F1E6"/>
|
||||
<rect x="28" y="28" width="744" height="424" rx="28" fill="#FCFBF7" stroke="#DCC9A3" stroke-width="2"/>
|
||||
<circle cx="126" cy="124" r="54" fill="#E5D5B3"/>
|
||||
<path d="M102 124h48M126 100v48" stroke="#8A6730" stroke-width="10" stroke-linecap="round"/>
|
||||
<text x="400" y="184" text-anchor="middle" fill="#5C4320" font-size="40" font-family="Arial, sans-serif" font-weight="700">
|
||||
Manual Preview
|
||||
</text>
|
||||
<text x="400" y="236" text-anchor="middle" fill="#8A6730" font-size="24" font-family="Arial, sans-serif">
|
||||
Thumbnail unavailable
|
||||
</text>
|
||||
<text x="400" y="298" text-anchor="middle" fill="#7A6A4A" font-size="22" font-family="Arial, sans-serif">
|
||||
${safeLabel}
|
||||
</text>
|
||||
</svg>
|
||||
`.trim()
|
||||
}
|
||||
|
||||
function decodeSegments(pathArray: string[]) {
|
||||
return pathArray.map((segment) => {
|
||||
try {
|
||||
|
|
@ -72,7 +99,14 @@ export async function GET(
|
|||
const normalizedThumbnailsDir = thumbnailsDir.replace(/\\/g, "/")
|
||||
|
||||
if (!existsSync(normalizedFullPath) && !existsSync(fullPath)) {
|
||||
return new NextResponse("Thumbnail not found", { status: 404 })
|
||||
const label = decodedPath.at(-1)?.replace(/\.(jpg|jpeg|png|webp)$/i, "") || "Rocky Mountain Vending"
|
||||
return new NextResponse(buildPlaceholderSvg(label), {
|
||||
headers: {
|
||||
"Content-Type": "image/svg+xml; charset=utf-8",
|
||||
"Cache-Control": "public, max-age=3600, stale-while-revalidate=86400",
|
||||
"X-Content-Type-Options": "nosniff",
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
const fileToRead = existsSync(normalizedFullPath) ? normalizedFullPath : fullPath
|
||||
|
|
|
|||
|
|
@ -150,27 +150,27 @@ export function Footer() {
|
|||
<h3 className="font-semibold mb-5 text-base">Service Areas</h3>
|
||||
<ul className="space-y-3 text-sm text-muted-foreground">
|
||||
<li>
|
||||
<Link href="/vending-machines/salt-lake-city-utah" className="transition-colors inline-block py-0.5">
|
||||
<Link href="/vending-machines-salt-lake-city-utah" className="transition-colors inline-block py-0.5">
|
||||
Salt Lake City, UT
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/vending-machines/ogden-utah" className="transition-colors inline-block py-0.5">
|
||||
<Link href="/vending-machines-ogden-utah" className="transition-colors inline-block py-0.5">
|
||||
Ogden, UT
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/vending-machines/provo-utah" className="transition-colors inline-block py-0.5">
|
||||
<Link href="/vending-machines-provo-utah" className="transition-colors inline-block py-0.5">
|
||||
Provo, UT
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/vending-machines/sandy-utah" className="transition-colors inline-block py-0.5">
|
||||
<Link href="/vending-machines-sandy-utah" className="transition-colors inline-block py-0.5">
|
||||
Sandy, UT
|
||||
</Link>
|
||||
</li>
|
||||
<li>
|
||||
<Link href="/vending-machines/west-valley-city-utah" className="transition-colors inline-block py-0.5">
|
||||
<Link href="/vending-machines-west-valley-city-utah" className="transition-colors inline-block py-0.5">
|
||||
West Valley City, UT
|
||||
</Link>
|
||||
</li>
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import {
|
|||
Dialog,
|
||||
DialogContent,
|
||||
DialogHeader,
|
||||
DialogDescription,
|
||||
DialogTitle,
|
||||
DialogClose,
|
||||
} from '@/components/ui/dialog'
|
||||
|
|
@ -168,6 +169,10 @@ export function ManualViewer({ manualUrl, filename, isOpen, onClose }: ManualVie
|
|||
}}
|
||||
>
|
||||
<DialogHeader className="px-4 sm:px-6 pt-4 sm:pt-6 pb-3 sm:pb-4 border-b flex-shrink-0 bg-background">
|
||||
<DialogDescription className="sr-only">
|
||||
PDF viewer for {filename.replace(/\.pdf$/i, '')}. Use the actions to open the manual in a new tab,
|
||||
download it, or browse available parts.
|
||||
</DialogDescription>
|
||||
<div className="flex items-center justify-between gap-4">
|
||||
<DialogTitle className="text-base sm:text-lg font-semibold line-clamp-1 flex-1 min-w-0">
|
||||
{filename.replace(/\.pdf$/i, '')}
|
||||
|
|
@ -280,4 +285,3 @@ export function ManualViewer({ manualUrl, filename, isOpen, onClose }: ManualVie
|
|||
</Dialog>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -109,6 +109,10 @@ async function searchEBayForParts(partNumber: string, description?: string, manu
|
|||
* Enhance parts data with real-time eBay listings
|
||||
*/
|
||||
async function enhancePartsData(parts: PartForPage[]): Promise<PartForPage[]> {
|
||||
if (!ebayClient.isConfigured()) {
|
||||
return parts
|
||||
}
|
||||
|
||||
const enhancedParts = await Promise.all(parts.map(async (part) => {
|
||||
// Only search for parts without existing eBay listings
|
||||
if (part.ebayListings.length === 0) {
|
||||
|
|
@ -261,4 +265,3 @@ export function clearPartsCache(): void {
|
|||
manualPartsCache = null
|
||||
manualPagesPartsCache = null
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue