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"
|
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[]) {
|
function decodeSegments(pathArray: string[]) {
|
||||||
return pathArray.map((segment) => {
|
return pathArray.map((segment) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -72,7 +99,14 @@ export async function GET(
|
||||||
const normalizedThumbnailsDir = thumbnailsDir.replace(/\\/g, "/")
|
const normalizedThumbnailsDir = thumbnailsDir.replace(/\\/g, "/")
|
||||||
|
|
||||||
if (!existsSync(normalizedFullPath) && !existsSync(fullPath)) {
|
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
|
const fileToRead = existsSync(normalizedFullPath) ? normalizedFullPath : fullPath
|
||||||
|
|
|
||||||
|
|
@ -150,27 +150,27 @@ export function Footer() {
|
||||||
<h3 className="font-semibold mb-5 text-base">Service Areas</h3>
|
<h3 className="font-semibold mb-5 text-base">Service Areas</h3>
|
||||||
<ul className="space-y-3 text-sm text-muted-foreground">
|
<ul className="space-y-3 text-sm text-muted-foreground">
|
||||||
<li>
|
<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
|
Salt Lake City, UT
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<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
|
Ogden, UT
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<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
|
Provo, UT
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<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
|
Sandy, UT
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
<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
|
West Valley City, UT
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
|
|
|
||||||
|
|
@ -7,6 +7,7 @@ import {
|
||||||
Dialog,
|
Dialog,
|
||||||
DialogContent,
|
DialogContent,
|
||||||
DialogHeader,
|
DialogHeader,
|
||||||
|
DialogDescription,
|
||||||
DialogTitle,
|
DialogTitle,
|
||||||
DialogClose,
|
DialogClose,
|
||||||
} from '@/components/ui/dialog'
|
} 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">
|
<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">
|
<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">
|
<DialogTitle className="text-base sm:text-lg font-semibold line-clamp-1 flex-1 min-w-0">
|
||||||
{filename.replace(/\.pdf$/i, '')}
|
{filename.replace(/\.pdf$/i, '')}
|
||||||
|
|
@ -280,4 +285,3 @@ export function ManualViewer({ manualUrl, filename, isOpen, onClose }: ManualVie
|
||||||
</Dialog>
|
</Dialog>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -109,6 +109,10 @@ async function searchEBayForParts(partNumber: string, description?: string, manu
|
||||||
* Enhance parts data with real-time eBay listings
|
* Enhance parts data with real-time eBay listings
|
||||||
*/
|
*/
|
||||||
async function enhancePartsData(parts: PartForPage[]): Promise<PartForPage[]> {
|
async function enhancePartsData(parts: PartForPage[]): Promise<PartForPage[]> {
|
||||||
|
if (!ebayClient.isConfigured()) {
|
||||||
|
return parts
|
||||||
|
}
|
||||||
|
|
||||||
const enhancedParts = await Promise.all(parts.map(async (part) => {
|
const enhancedParts = await Promise.all(parts.map(async (part) => {
|
||||||
// Only search for parts without existing eBay listings
|
// Only search for parts without existing eBay listings
|
||||||
if (part.ebayListings.length === 0) {
|
if (part.ebayListings.length === 0) {
|
||||||
|
|
@ -261,4 +265,3 @@ export function clearPartsCache(): void {
|
||||||
manualPartsCache = null
|
manualPartsCache = null
|
||||||
manualPagesPartsCache = null
|
manualPagesPartsCache = null
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue