import { headers } from "next/headers"; import Image from "next/image"; import { Accordion, AccordionControl, AccordionItem, AccordionPanel, AspectRatio, Avatar, Card, Center, Flex, Group, Kbd, List, ListItem, Stack, Table, TableTbody, TableTd, TableTh, TableThead, TableTr, Text, Title, } from "@mantine/core"; import { IconKeyboard, IconLanguage, IconLibrary, IconUsers } from "@tabler/icons-react"; import { capitalize, objectEntries } from "@homarr/common"; import { hotkeys } from "@homarr/definitions"; import { getScopedI18n } from "@homarr/translation/server"; import { homarrLogoPath } from "~/components/layout/logo/homarr-logo"; import { DynamicBreadcrumb } from "~/components/navigation/dynamic-breadcrumb"; import { createMetaTitle } from "~/metadata"; import { getPackageAttributesAsync } from "~/versions/package-reader"; import type githubContributorsJson from "../../../../../../../static-data/contributors.json"; import type crowdinContributorsJson from "../../../../../../../static-data/translators.json"; import classes from "./about.module.css"; export async function generateMetadata() { const t = await getScopedI18n("management"); return { title: createMetaTitle(t("metaTitle")), }; } const getHostAsync = async () => { if (process.env.HOSTNAME) { return `${process.env.HOSTNAME}:3000`; } return (await headers()).get("host"); }; export default async function AboutPage() { const baseServerUrl = `http://${await getHostAsync()}`; const t = await getScopedI18n("management.page.about"); const attributes = await getPackageAttributesAsync(); const githubContributors = (await fetch(`${baseServerUrl}/api/about/contributors/github`).then((res) => res.json(), )) as typeof githubContributorsJson; const crowdinContributors = (await fetch(`${baseServerUrl}/api/about/contributors/crowdin`).then((res) => res.json(), )) as typeof crowdinContributorsJson; return (
Homarr {t("version", { version: attributes.version })}
{t("text")} }> {t("accordion.contributors.title")} {t("accordion.contributors.subtitle", { count: String(githubContributors.length), })} {githubContributors.map((contributor) => ( ))} }> {t("accordion.translators.title")} {t("accordion.translators.subtitle", { count: String(crowdinContributors.length), })} {crowdinContributors.map((translator) => ( ))} }> {t("accordion.libraries.title")} {t("accordion.libraries.subtitle", { count: String(Object.keys(attributes.dependencies).length), })} {Object.entries(attributes.dependencies) .sort(([key1], [key2]) => key1.localeCompare(key2)) .map(([key, value]) => ( {value.includes("workspace:") ? ( {key} ) : ( {key} )} ))} }> {t("accordion.hotkeys.title")} {t("accordion.hotkeys.subtitle")} {t("accordion.hotkeys.field.shortcut")} {t("accordion.hotkeys.field.action")} {objectEntries(hotkeys).map(([key, shortcut]) => ( {shortcut .split("+") .map((key) => capitalize(key.trim())) .join(" + ")} {t(`accordion.hotkeys.action.${key}`)} ))}
{t("accordion.hotkeys.note")}
); } interface GenericContributorLinkCardProps { name: string; link: string; image: string; } const GenericContributorLinkCard = ({ name, image, link }: GenericContributorLinkCardProps) => { return ( {name} ); };