Edit metadata

This commit is contained in:
Thomas Camlong 2025-04-22 10:06:07 +02:00
parent 36a355d3e2
commit 3bcbbf426c
No known key found for this signature in database
GPG Key ID: A678F374F428457B

View File

@ -1,48 +1,55 @@
import { IconDetails } from "@/components/icon-details" import { IconDetails } from "@/components/icon-details";
import { BASE_URL, WEB_URL } from "@/constants" import { BASE_URL, WEB_URL } from "@/constants";
import { getAllIcons, getAuthorData } from "@/lib/api" import { getAllIcons, getAuthorData } from "@/lib/api";
import type { Metadata, ResolvingMetadata } from "next" import type { Metadata, ResolvingMetadata } from "next";
import { notFound } from "next/navigation" import { notFound } from "next/navigation";
export const dynamicParams = false export const dynamicParams = false;
export async function generateStaticParams() { export async function generateStaticParams() {
const iconsData = await getAllIcons() const iconsData = await getAllIcons();
return Object.keys(iconsData).map((icon) => ({ return Object.keys(iconsData).map((icon) => ({
icon, icon,
})) }));
} }
export const dynamic = "force-static" export const dynamic = "force-static";
type Props = { type Props = {
params: Promise<{ icon: string }> params: Promise<{ icon: string }>;
searchParams: Promise<{ [key: string]: string | string[] | undefined }> searchParams: Promise<{ [key: string]: string | string[] | undefined }>;
} };
export async function generateMetadata({ params, searchParams }: Props, parent: ResolvingMetadata): Promise<Metadata> { export async function generateMetadata(
const { icon } = await params { params, searchParams }: Props,
const iconsData = await getAllIcons() parent: ResolvingMetadata,
): Promise<Metadata> {
const { icon } = await params;
const iconsData = await getAllIcons();
if (!iconsData[icon]) { if (!iconsData[icon]) {
notFound() notFound();
} }
const authorData = await getAuthorData(iconsData[icon].update.author.id) const authorData = await getAuthorData(iconsData[icon].update.author.id);
const authorName = authorData.name || authorData.login const authorName = authorData.name || authorData.login;
const updateDate = new Date(iconsData[icon].update.timestamp) const updateDate = new Date(iconsData[icon].update.timestamp);
const totalIcons = Object.keys(iconsData).length 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 iconImageUrl = `${BASE_URL}/png/${icon}.png`;
const pageUrl = `${WEB_URL}/icons/${icon}` const pageUrl = `${WEB_URL}/icons/${icon}`;
const formattedIconName = icon const formattedIconName = icon
.split("-") .split("-")
.map((word) => word.charAt(0).toUpperCase() + word.slice(1)) .map((word) => word.charAt(0).toUpperCase() + word.slice(1))
.join(" ") .join(" ");
return { return {
title: `${formattedIconName} Icon | Dashboard Icons`, 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.`, 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: [ keywords: [
`${formattedIconName} icon`, `${formattedIconName} icon`,
"dashboard icon", "dashboard icon",
@ -52,48 +59,69 @@ export async function generateMetadata({ params, searchParams }: Props, parent:
"web dashboard", "web dashboard",
"app directory", "app directory",
], ],
authors: [ icons: {
{ icon: iconImageUrl,
name: "homarr",
url: "https://homarr.dev",
}, },
{ 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.`,
name: authorName, robots: {
url: authorData.html_url, index: true,
follow: true,
}, },
],
openGraph: { openGraph: {
title: `${formattedIconName} Icon | Dashboard Icons`, 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.`, 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", type: "article",
url: pageUrl, url: pageUrl,
authors: [authorName, "homarr"], images: [iconImageUrl],
authors: [authorName],
publishedTime: updateDate.toISOString(), publishedTime: updateDate.toISOString(),
modifiedTime: updateDate.toISOString(), modifiedTime: updateDate.toISOString(),
section: "Icons",
tags: [
formattedIconName,
"dashboard icon",
"service icon",
"application icon",
"tool icon",
"web dashboard",
"app directory",
],
}, },
twitter: { twitter: {
card: "summary_large_image", card: "summary_large_image",
title: `${formattedIconName} Icon | Dashboard Icons`, 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.`, 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], images: [iconImageUrl],
creator: "@homarr_app",
}, },
alternates: { alternates: {
canonical: pageUrl, 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 }> }) { export default async function IconPage({
const { icon } = await params params,
const iconsData = await getAllIcons() }: { params: Promise<{ icon: string }> }) {
const originalIconData = iconsData[icon] const { icon } = await params;
const iconsData = await getAllIcons();
const originalIconData = iconsData[icon];
if (!originalIconData) { if (!originalIconData) {
notFound() notFound();
} }
const authorData = await getAuthorData(originalIconData.update.author.id) const authorData = await getAuthorData(originalIconData.update.author.id);
return <IconDetails icon={icon} iconData={originalIconData} authorData={authorData} /> return (
<IconDetails
icon={icon}
iconData={originalIconData}
authorData={authorData}
/>
);
} }