Improve metadata info on most pages

This commit is contained in:
DrMint 2024-05-11 23:03:48 +02:00
parent cb3ba888dd
commit b1561c1b74
12 changed files with 265 additions and 214 deletions

View File

@ -31,11 +31,9 @@ const { getLocalizedMatch } = await getI18n(Astro.locals.currentLocale);
div {
display: grid;
gap: 2em;
margin-block: 2em;
@media (max-width: 35rem) {
gap: 3.5em;
margin-block: 3.5em;
gap: 3em;
}
}
</style>

View File

@ -5,12 +5,12 @@ import {
type PayloadImage,
type RichTextContent,
} from "src/shared/payload/payload-sdk";
import RichText from "./RichText/RichText.astro";
import TagGroups from "./TagGroups.astro";
import Credits from "./Credits.astro";
import DownloadButton from "./DownloadButton.astro";
import AppLayoutTitle from "./AppLayout/components/AppLayoutTitle.astro";
import type { ComponentProps } from "astro/types";
import AppLayoutDescription from "./AppLayout/components/AppLayoutDescription.astro";
interface Props {
previousImageHref?: string | undefined;
@ -65,15 +65,16 @@ const smallTitle = !subtitle && !pretitle;
complex:
(tagGroups && tagGroups.length > 0) || (credits && credits.length > 0) || description,
}}>
{
smallTitle ? (
<h1>{title}</h1>
) : (
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} />
)
}
{description && <RichText content={description} />}
<div>
{
smallTitle ? (
<h1>{title}</h1>
) : (
<AppLayoutTitle pretitle={pretitle} title={title} subtitle={subtitle} />
)
}
{description && <AppLayoutDescription description={description} />}
</div>
{tagGroups && tagGroups.length > 0 && <TagGroups tagGroups={tagGroups} />}
{credits && credits.length > 0 && <Credits credits={credits} />}
{filename && <DownloadButton href={url} filename={filename} />}
@ -113,12 +114,16 @@ const smallTitle = !subtitle && !pretitle;
& > #info {
display: flex;
flex-direction: column;
gap: 1em;
align-items: center;
gap: 4em;
align-items: start;
&.complex {
@media (max-width: 35rem) {
gap: 6em;
}
&:not(.complex) {
align-items: center;
gap: 2em;
align-items: start;
}
& > h1 {

View File

@ -41,13 +41,12 @@ const groups = tagGroups.map((group) => {
<style>
div {
display: grid;
display: flex;
flex-direction: column;
gap: 2em;
margin-block: 2em;
@media (max-width: 35rem) {
gap: 3.5em;
margin-block: 3.5em;
gap: 3em;
}
}
</style>

View File

@ -144,4 +144,7 @@ export type WordingKey =
| "global.media.attributes.duration"
| "global.media.attributes.filesize"
| "global.media.attributes.createdAt"
| "global.media.attributes.updatedAt";
| "global.media.attributes.updatedAt"
| "global.media.attributes.updatedBy"
| "collectibles.nature"
| "collectibles.languages";

View File

@ -22,13 +22,38 @@ interface Props {
const reqUrl = new URL(Astro.request.url);
const lang = Astro.props.lang ?? reqUrl.searchParams.get("lang")!;
const slug = Astro.props.slug ?? reqUrl.searchParams.get("slug")!;
const { translations, thumbnail, tagGroups } = Astro.props.page ?? (await payload.getPage(slug));
const { translations, thumbnail, tagGroups, createdAt, updatedAt, updatedBy } =
Astro.props.page ?? (await payload.getPage(slug));
const { getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
const { getLocalizedUrl, t, formatDate } = await getI18n(Astro.locals.currentLocale);
const { getLocalizedMatch } = await getI18n(lang);
const { pretitle, title, subtitle, summary, content, credits, toc, language, sourceLanguage } =
getLocalizedMatch(translations);
const attributes = [
{
title: t("global.media.attributes.createdAt"),
icon: "material-symbols:calendar-add-on-outline",
values: [formatDate(new Date(createdAt))],
withBorder: false,
},
{
title: t("global.media.attributes.updatedAt"),
icon: "material-symbols:edit-calendar",
values: [formatDate(new Date(updatedAt))],
withBorder: false,
},
];
if (updatedBy) {
attributes.push({
title: t("global.media.attributes.updatedBy"),
icon: "material-symbols:person-edit-outline",
values: [updatedBy?.username],
withBorder: true,
});
}
---
{/* ------------------------------------------- HTML ------------------------------------------- */}
@ -56,27 +81,37 @@ const { pretitle, title, subtitle, summary, content, credits, toc, language, sou
<Fragment slot="meta">
{summary && <AppLayoutDescription description={summary} />}
{tagGroups.length > 0 && <TagGroups tagGroups={tagGroups} />}
</Fragment>
<Fragment slot="aside">
{
translations.length > 1 && (
<LanguageOverride
currentLanguage={language}
currentSourceLanguage={sourceLanguage}
availableLanguages={translations.map(({ language }) => language)}
getPartialUrl={(lang) =>
getLocalizedUrl(`/api/pages/partial?lang=${lang}&slug=${slug}`)
}
/>
tagGroups.length > 0 && (
<div id="tags">
<TagGroups tagGroups={tagGroups} />
</div>
)
}
</Fragment>
{credits.length > 0 && <Credits credits={credits} />}
<div id="aside" slot="aside">
<div id="credits">
{
translations.length > 1 && (
<LanguageOverride
currentLanguage={language}
currentSourceLanguage={sourceLanguage}
availableLanguages={translations.map(({ language }) => language)}
getPartialUrl={(lang) =>
getLocalizedUrl(`/api/pages/partial?lang=${lang}&slug=${slug}`)
}
/>
)
}
{credits.length > 0 && <Credits credits={credits} />}
</div>
{attributes.length > 0 && <TagGroups tagGroups={attributes} />}
{toc.length > 0 && <TableOfContent toc={toc} />}
</Fragment>
</div>
<hr />
<div id="text">
@ -94,6 +129,39 @@ const { pretitle, title, subtitle, summary, content, credits, toc, language, sou
margin-block: 3em;
}
#aside {
display: flex;
flex-direction: column;
gap: 4em;
@media (max-width: 35rem) {
gap: 6em;
}
}
#tags {
margin-block: 2em;
@media (max-width: 1280.5px) {
margin-bottom: 4em;
}
@media (max-width: 35rem) {
margin-top: 4em;
margin-bottom: 6em;
}
}
#credits {
display: flex;
flex-direction: column;
gap: 1em;
@media (max-width: 35rem) {
gap: 2em;
}
}
#thumbnail {
max-width: 35rem;
max-height: 60vh;

View File

@ -36,8 +36,7 @@ const { pretitle, title, subtitle, description } = getLocalizedMatch(translation
const smallTitle = !subtitle && !pretitle;
const tagsAndAttributes = [
...tagGroups,
const attributes = [
...(filename && title !== filename
? [
{
@ -81,7 +80,7 @@ const tagsAndAttributes = [
<div id="container">
<AudioPlayer audio={audio} />
<div>
<div id="info">
{
smallTitle ? (
<h1>{title}</h1>
@ -90,10 +89,9 @@ const tagsAndAttributes = [
)
}
{description && <RichText content={description} />}
<div>
{tagsAndAttributes.length > 0 && <TagGroups tagGroups={tagsAndAttributes} />}
{credits.length > 0 && <Credits credits={credits} />}
</div>
{tagGroups.length > 0 && <TagGroups tagGroups={tagGroups} />}
{credits.length > 0 && <Credits credits={credits} />}
{attributes.length > 0 && <TagGroups tagGroups={attributes} />}
<DownloadButton href={url} filename={filename} />
</div>
</div>
@ -117,18 +115,14 @@ const tagsAndAttributes = [
max-width: 35em;
}
& > div {
& > #info {
display: flex;
flex-direction: column;
gap: 2em;
gap: 4em;
align-items: start;
& > div {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 2em 6em;
width: 100%;
@media (max-width: 35rem) {
gap: 6em;
}
}
}

View File

@ -1,70 +0,0 @@
---
import { Icon } from "astro-icon/components";
import { getI18n } from "src/i18n/i18n";
import { convert } from "src/utils/currencies";
interface Props {
price: {
amount: number;
currency: string;
};
}
const { price } = Astro.props;
const { formatPrice, t } = await getI18n(Astro.locals.currentLocale);
const preferredCurrency = Astro.locals.currentCurrency;
const convertedPrice: Props["price"] = {
amount: convert(price.currency, preferredCurrency, price.amount),
currency: preferredCurrency,
};
let priceText = price.amount === 0 ? t("collectibles.price.free") : formatPrice(price);
if (price.amount > 0 && price.currency !== convertedPrice.currency) {
priceText += ` (${formatPrice(convertedPrice)})`;
}
---
{/* ------------------------------------------- HTML ------------------------------------------- */}
<div id="container">
<div id="title">
<Icon name="material-symbols:sell-outline" width={24} height={24} />
<p>{t("collectibles.price")}</p>
</div>
{(<p>{priceText}</p>)}
</div>
{/* ------------------------------------------- CSS -------------------------------------------- */}
<style>
#container {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.5em 1em;
align-items: start;
@media (max-width: 35em) {
grid-template-columns: 1fr;
}
& > #title {
display: flex;
place-items: center;
gap: 8px;
& > p {
font-size: 1.5em;
font-weight: 600;
}
}
& > p {
margin-top: 0.5em;
}
}
</style>

View File

@ -1,53 +0,0 @@
---
import { Icon } from "astro-icon/components";
import { getI18n } from "src/i18n/i18n";
interface Props {
releaseDate: string;
}
const { releaseDate } = Astro.props;
const { formatDate, t } = await getI18n(Astro.locals.currentLocale);
---
{/* ------------------------------------------- HTML ------------------------------------------- */}
<div id="container">
<div id="title">
<Icon name="material-symbols:calendar-month-outline" width={24} height={24} />
<p>{t("collectibles.releaseDate")}</p>
</div>
<p>{formatDate(new Date(releaseDate))}</p>
</div>
{/* ------------------------------------------- CSS -------------------------------------------- */}
<style>
#container {
display: grid;
grid-template-columns: auto 1fr;
gap: 0.5em 1em;
align-items: center;
@media (max-width: 35em) {
grid-template-columns: 1fr;
}
& > #title {
display: flex;
place-items: center;
gap: 8px;
& > p {
font-size: 1.5em;
font-weight: 600;
}
}
& > p {
margin-top: 0.5em;
}
}
</style>

View File

@ -3,23 +3,24 @@ import AppLayout from "components/AppLayout/AppLayout.astro";
import AppLayoutTitle from "components/AppLayout/components/AppLayoutTitle.astro";
import TagGroups from "components/TagGroups.astro";
import { getI18n } from "src/i18n/i18n";
import { payload } from "src/shared/payload/payload-sdk";
import { CollectibleNature, payload } from "src/shared/payload/payload-sdk";
import { fetchOr404 } from "src/utils/responses";
import ImageTile from "./_components/ImageTile.astro";
import PriceInfo from "./_components/PriceInfo.astro";
import SizeInfo from "./_components/SizeInfo.astro";
import ReleaseDateInfo from "./_components/ReleaseDateInfo.astro";
import PageInfo from "./_components/PageInfo.astro";
import AvailabilityInfo from "./_components/AvailabilityInfo.astro";
import WeightInfo from "./_components/WeightInfo.astro";
import SubitemSection from "./_components/SubitemSection.astro";
import ContentsSection from "./_components/ContentsSection/ContentsSection.astro";
import { formatInlineTitle, formatRichTextToString } from "src/utils/format";
import { formatInlineTitle, formatLocale, formatRichTextToString } from "src/utils/format";
import AsideLayout from "components/AppLayout/AsideLayout.astro";
import AppLayoutDescription from "components/AppLayout/components/AppLayoutDescription.astro";
import { convert } from "src/utils/currencies";
const { slug } = Astro.params;
const { getLocalizedMatch, getLocalizedUrl, t } = await getI18n(Astro.locals.currentLocale);
const { getLocalizedMatch, getLocalizedUrl, t, formatDate, formatPrice } = await getI18n(
Astro.locals.currentLocale
);
const collectible = await fetchOr404(() => payload.getCollectible(slug!));
if (collectible instanceof Response) {
@ -42,9 +43,89 @@ const {
parentPages,
tagGroups,
contents,
createdAt,
updatedAt,
updatedBy,
languages,
nature,
} = collectible;
const translation = getLocalizedMatch(translations);
const { pretitle, title, subtitle, description } = translation;
const attributes = [
{
title: t("global.media.attributes.createdAt"),
icon: "material-symbols:calendar-add-on-outline",
values: [formatDate(new Date(createdAt))],
withBorder: false,
},
{
title: t("global.media.attributes.updatedAt"),
icon: "material-symbols:edit-calendar",
values: [formatDate(new Date(updatedAt))],
withBorder: false,
},
];
if (updatedBy) {
attributes.push({
title: t("global.media.attributes.updatedBy"),
icon: "material-symbols:person-edit-outline",
values: [updatedBy?.username],
withBorder: true,
});
}
const tagGroupWithAttributes = [
...tagGroups,
{
title: t("collectibles.nature"),
icon: "material-symbols:leaf-spark-outline",
values: [nature === CollectibleNature.Physical ? "Physical" : "Digital"],
withBorder: true,
},
];
if (releaseDate) {
tagGroupWithAttributes.push({
title: t("collectibles.releaseDate"),
icon: "material-symbols:calendar-month-outline",
values: [formatDate(new Date(releaseDate))],
withBorder: false,
});
}
if (languages.length > 0) {
tagGroupWithAttributes.push({
title: t("collectibles.languages"),
icon: "material-symbols:translate",
values: languages.map((lang) => formatLocale(lang)),
withBorder: true,
});
}
if (price) {
const preferredCurrency = Astro.locals.currentCurrency;
const convertedPrice = {
amount: convert(price.currency, preferredCurrency, price.amount),
currency: preferredCurrency,
};
let priceText = price.amount === 0 ? t("collectibles.price.free") : formatPrice(price);
if (price.amount > 0 && price.currency !== convertedPrice.currency) {
priceText += ` (${formatPrice(convertedPrice)})`;
}
tagGroupWithAttributes.push({
title: t("collectibles.price"),
icon: "material-symbols:sell-outline",
values: [priceText],
withBorder: false,
});
}
---
{/* ------------------------------------------- HTML ------------------------------------------- */}
@ -52,18 +133,14 @@ const translation = getLocalizedMatch(translations);
<AppLayout
openGraph={{
title: formatInlineTitle(translation),
description: translation.description && formatRichTextToString(translation.description),
description: description && formatRichTextToString(description),
thumbnail,
}}
parentPages={parentPages}
backgroundImage={backgroundImage ?? thumbnail}>
<AsideLayout>
<Fragment slot="header">
<AppLayoutTitle
title={translation.title}
pretitle={translation.pretitle}
subtitle={translation.subtitle}
/>
<AppLayoutTitle title={title} pretitle={pretitle} subtitle={subtitle} />
</Fragment>
<Fragment slot="header-aside">
@ -107,21 +184,31 @@ const translation = getLocalizedMatch(translations);
</div>
</Fragment>
{translation.description && <AppLayoutDescription description={translation.description} />}
<Fragment slot="aside">
{
attributes.length > 0 && (
<div id="attributes">
<TagGroups tagGroups={attributes} />
</div>
)
}
</Fragment>
<TagGroups tagGroups={tagGroups}>
{releaseDate && <ReleaseDateInfo releaseDate={releaseDate} />}
<Fragment slot="meta">
{description && <AppLayoutDescription description={description} />}
{price && <PriceInfo price={price} />}
<div id="tags">
<TagGroups tagGroups={tagGroupWithAttributes}>
<AvailabilityInfo urls={urls} price={price !== undefined} releaseDate={releaseDate} />
<AvailabilityInfo urls={urls} price={price !== undefined} releaseDate={releaseDate} />
{size && <SizeInfo size={size} />}
{size && <SizeInfo size={size} />}
{weight && <WeightInfo weight={weight} />}
{weight && <WeightInfo weight={weight} />}
{pageInfo && <PageInfo pageInfo={pageInfo} />}
</TagGroups>
{pageInfo && <PageInfo pageInfo={pageInfo} />}
</TagGroups>
</div>
</Fragment>
{subitems.length > 0 && <SubitemSection subitems={subitems} />}
@ -195,4 +282,24 @@ const translation = getLocalizedMatch(translations);
}
}
}
#tags {
margin-block: 2em;
@media (max-width: 1280.5px) {
margin-bottom: 4em;
}
@media (max-width: 35rem) {
margin-top: 4em;
margin-bottom: 6em;
}
}
#attributes {
@media (min-width: 1280.5px) {
margin-left: 12em;
margin-top: 3em;
}
}
</style>

View File

@ -33,7 +33,7 @@ const { t, getLocalizedUrl } = await getI18n(Astro.locals.currentLocale);
<AppLayoutDescription description={t("home.description")} />
<a href={getLocalizedUrl("/about")}>
<a href={getLocalizedUrl("/about")} class="DEV_TODO">
<Button title={t("home.aboutUsButton")} icon="material-symbols:left-click" />
</a>

View File

@ -35,8 +35,7 @@ const {
const { pretitle, title, subtitle, description } = getLocalizedMatch(translations);
const smallTitle = !subtitle && !pretitle;
const tagsAndAttributes = [
...tagGroups,
const attributes = [
...(filename && title !== filename
? [
{
@ -80,7 +79,7 @@ const tagsAndAttributes = [
<div id="container">
<VideoPlayer video={video} />
<div>
<div id="info">
{
smallTitle ? (
<h1>{title}</h1>
@ -89,10 +88,9 @@ const tagsAndAttributes = [
)
}
{description && <RichText content={description} />}
<div>
{tagsAndAttributes.length > 0 && <TagGroups tagGroups={tagsAndAttributes} />}
{credits.length > 0 && <Credits credits={credits} />}
</div>
{tagGroups.length > 0 && <TagGroups tagGroups={tagGroups} />}
{credits.length > 0 && <Credits credits={credits} />}
{attributes.length > 0 && <TagGroups tagGroups={attributes} />}
<DownloadButton href={url} filename={filename} />
</div>
</div>
@ -118,18 +116,14 @@ const tagsAndAttributes = [
max-width: 35em;
}
& > div {
& > #info {
display: flex;
flex-direction: column;
gap: 2em;
gap: 4em;
align-items: start;
& > div {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
gap: 2em 6em;
width: 100%;
@media (max-width: 35rem) {
gap: 6em;
}
}
}

View File

@ -1482,6 +1482,9 @@ export type EndpointPage = {
credits: EndpointCredit[];
toc: TableOfContentEntry[];
}[];
createdAt: string;
updatedAt: string;
updatedBy?: EndpointRecorder;
parentPages: EndpointSource[];
};
@ -1562,6 +1565,9 @@ export type EndpointCollectible = {
}[];
};
}[];
createdAt: string;
updatedAt: string;
updatedBy?: EndpointRecorder;
parentPages: EndpointSource[];
};