diff --git a/web/src/app/icons/[icon]/page.tsx b/web/src/app/icons/[icon]/page.tsx index 894a6c0d..7f1ab712 100644 --- a/web/src/app/icons/[icon]/page.tsx +++ b/web/src/app/icons/[icon]/page.tsx @@ -1,48 +1,55 @@ -import { IconDetails } from "@/components/icon-details" -import { BASE_URL, WEB_URL } from "@/constants" -import { getAllIcons, getAuthorData } from "@/lib/api" -import type { Metadata, ResolvingMetadata } from "next" -import { notFound } from "next/navigation" +import { IconDetails } from "@/components/icon-details"; +import { BASE_URL, WEB_URL } from "@/constants"; +import { getAllIcons, getAuthorData } from "@/lib/api"; +import type { Metadata, ResolvingMetadata } from "next"; +import { notFound } from "next/navigation"; -export const dynamicParams = false +export const dynamicParams = false; export async function generateStaticParams() { - const iconsData = await getAllIcons() + const iconsData = await getAllIcons(); return Object.keys(iconsData).map((icon) => ({ icon, - })) + })); } -export const dynamic = "force-static" +export const dynamic = "force-static"; type Props = { - params: Promise<{ icon: string }> - searchParams: Promise<{ [key: string]: string | string[] | undefined }> -} + params: Promise<{ icon: string }>; + searchParams: Promise<{ [key: string]: string | string[] | undefined }>; +}; -export async function generateMetadata({ params, searchParams }: Props, parent: ResolvingMetadata): Promise { - const { icon } = await params - const iconsData = await getAllIcons() +export async function generateMetadata( + { params, searchParams }: Props, + parent: ResolvingMetadata, +): Promise { + const { icon } = await params; + const iconsData = await getAllIcons(); if (!iconsData[icon]) { - notFound() + notFound(); } - const authorData = await getAuthorData(iconsData[icon].update.author.id) - const authorName = authorData.name || authorData.login - const updateDate = new Date(iconsData[icon].update.timestamp) - const totalIcons = Object.keys(iconsData).length + const authorData = await getAuthorData(iconsData[icon].update.author.id); + const authorName = authorData.name || authorData.login; + const updateDate = new Date(iconsData[icon].update.timestamp); + const totalIcons = Object.keys(iconsData).length; - console.debug(`Generated metadata for ${icon} by ${authorName} (${authorData.html_url}) updated at ${updateDate.toLocaleString()}`) + console.debug( + `Generated metadata for ${icon} by ${authorName} (${authorData.html_url}) updated at ${updateDate.toLocaleString()}`, + ); - const iconImageUrl = `${BASE_URL}/png/${icon}.png` - const pageUrl = `${WEB_URL}/icons/${icon}` + const iconImageUrl = `${BASE_URL}/png/${icon}.png`; + const pageUrl = `${WEB_URL}/icons/${icon}`; const formattedIconName = icon .split("-") .map((word) => word.charAt(0).toUpperCase() + word.slice(1)) - .join(" ") + .join(" "); return { title: `${formattedIconName} Icon | Dashboard Icons`, description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats for FREE. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, + assets: [iconImageUrl], + category: "icons", keywords: [ `${formattedIconName} icon`, "dashboard icon", @@ -52,48 +59,69 @@ export async function generateMetadata({ params, searchParams }: Props, parent: "web dashboard", "app directory", ], - authors: [ - { - name: "homarr", - url: "https://homarr.dev", - }, - { - name: authorName, - url: authorData.html_url, - }, - ], + icons: { + icon: iconImageUrl, + }, + abstract: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats for FREE. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, + robots: { + index: true, + follow: true, + }, openGraph: { title: `${formattedIconName} Icon | Dashboard Icons`, description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats for FREE. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, type: "article", url: pageUrl, - authors: [authorName, "homarr"], + images: [iconImageUrl], + authors: [authorName], publishedTime: updateDate.toISOString(), modifiedTime: updateDate.toISOString(), + section: "Icons", + tags: [ + formattedIconName, + "dashboard icon", + "service icon", + "application icon", + "tool icon", + "web dashboard", + "app directory", + ], }, twitter: { card: "summary_large_image", title: `${formattedIconName} Icon | Dashboard Icons`, description: `Download the ${formattedIconName} icon in SVG, PNG, and WEBP formats for FREE. Part of a collection of ${totalIcons} curated icons for services, applications and tools, designed specifically for dashboards and app directories.`, images: [iconImageUrl], - creator: "@homarr_app", }, alternates: { canonical: pageUrl, + media: { + png: iconImageUrl, + svg: `${BASE_URL}/svg/${icon}.svg`, + webp: `${BASE_URL}/webp/${icon}.webp`, + }, }, - } + }; } -export default async function IconPage({ params }: { params: Promise<{ icon: string }> }) { - const { icon } = await params - const iconsData = await getAllIcons() - const originalIconData = iconsData[icon] +export default async function IconPage({ + params, +}: { params: Promise<{ icon: string }> }) { + const { icon } = await params; + const iconsData = await getAllIcons(); + const originalIconData = iconsData[icon]; if (!originalIconData) { - notFound() + notFound(); } - const authorData = await getAuthorData(originalIconData.update.author.id) + const authorData = await getAuthorData(originalIconData.update.author.id); - return + return ( + + ); }