2026-05-16 00:18:22 +08:00
|
|
|
import { Link } from "react-router-dom";
|
|
|
|
|
import type { Resource } from "../api";
|
|
|
|
|
import { CategoryIcon } from "./CategoryIcon";
|
|
|
|
|
import { useI18n } from "../i18n";
|
2026-06-01 16:35:40 +08:00
|
|
|
import { useLocalizedPath } from "../useLocalizedPath";
|
2026-05-16 00:18:22 +08:00
|
|
|
import { resourceTypeLabel } from "../resourceTypeLabels";
|
|
|
|
|
import { formatDateYmd } from "../utils/format";
|
2026-06-02 00:41:06 +08:00
|
|
|
import { FavoriteButton } from "../favorites/FavoriteButton";
|
2026-05-16 00:18:22 +08:00
|
|
|
|
|
|
|
|
const LATEST_CARD_CLASS =
|
2026-06-02 00:41:06 +08:00
|
|
|
"relative flex min-h-[106px] items-start gap-4 overflow-hidden rounded-xl border border-ark-line bg-ark-panel p-4 outline-none transition hover:border-ark-gold/45 focus-visible:ring-2 focus-visible:ring-ark-gold/80 focus-visible:ring-offset-2 focus-visible:ring-offset-ark-bg md:min-h-[138px] md:p-5";
|
2026-05-16 00:18:22 +08:00
|
|
|
|
|
|
|
|
export function LatestUpdateRow({
|
|
|
|
|
r,
|
|
|
|
|
iconKey,
|
|
|
|
|
}: {
|
|
|
|
|
r: Resource;
|
|
|
|
|
iconKey: string;
|
|
|
|
|
}) {
|
|
|
|
|
const { t } = useI18n();
|
2026-06-01 16:35:40 +08:00
|
|
|
const lp = useLocalizedPath();
|
2026-05-16 00:18:22 +08:00
|
|
|
const dateStr = formatDateYmd(r.updatedAt);
|
|
|
|
|
|
|
|
|
|
return (
|
2026-06-02 00:41:06 +08:00
|
|
|
<article className={LATEST_CARD_CLASS}>
|
|
|
|
|
<Link
|
|
|
|
|
to={lp(`/resource/${r.id}`)}
|
|
|
|
|
aria-label={r.title}
|
|
|
|
|
className="absolute inset-0 z-0 rounded-xl outline-none focus-visible:ring-2 focus-visible:ring-ark-gold/80"
|
|
|
|
|
/>
|
|
|
|
|
<div className="relative z-10 flex shrink-0 items-center justify-center pt-0.5">
|
2026-05-16 00:18:22 +08:00
|
|
|
<CategoryIcon
|
|
|
|
|
iconKey={iconKey}
|
|
|
|
|
categorySlug={r.categorySlug}
|
|
|
|
|
className="h-10 w-10 text-ark-gold"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-06-02 00:41:06 +08:00
|
|
|
<div className="pointer-events-none relative z-10 flex min-w-0 flex-1 self-stretch py-0.5 flex-col pr-11">
|
2026-05-16 00:18:22 +08:00
|
|
|
<div className="text-base font-bold leading-snug text-white line-clamp-2 md:text-lg">
|
|
|
|
|
{r.title}
|
|
|
|
|
</div>
|
2026-05-19 00:34:29 +08:00
|
|
|
<div className="mt-auto grid gap-1 text-sm text-[#9b9ca6]">
|
2026-05-16 00:18:22 +08:00
|
|
|
<span>{r.categoryName}</span>
|
|
|
|
|
<span>
|
|
|
|
|
{resourceTypeLabel(t, r.type)}
|
|
|
|
|
<time className="mx-2 text-ark-muted" dateTime={r.updatedAt}>
|
|
|
|
|
{dateStr}
|
|
|
|
|
</time>
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
2026-06-02 00:41:06 +08:00
|
|
|
<FavoriteButton
|
|
|
|
|
resourceId={r.id}
|
|
|
|
|
size="sm"
|
|
|
|
|
className="absolute right-3 top-3 z-20"
|
|
|
|
|
/>
|
|
|
|
|
</article>
|
2026-05-16 00:18:22 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const comingSoonIconKeys = ["image", "film", "book", "gift", "folder"];
|
|
|
|
|
|
|
|
|
|
export function ComingSoonLatestUpdateRow({ index = 0 }: { index?: number }) {
|
|
|
|
|
const iconKey = comingSoonIconKeys[index % comingSoonIconKeys.length];
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<article
|
|
|
|
|
className={`${LATEST_CARD_CLASS} cursor-default opacity-95 hover:border-ark-line`}
|
|
|
|
|
aria-label="即将到来"
|
|
|
|
|
>
|
|
|
|
|
<div className="flex shrink-0 items-center justify-center pt-0.5">
|
|
|
|
|
<CategoryIcon iconKey={iconKey} className="h-10 w-10 text-ark-gold" />
|
|
|
|
|
</div>
|
2026-05-19 00:34:29 +08:00
|
|
|
<div className="flex min-w-0 flex-1 self-stretch py-0.5 flex-col">
|
2026-05-16 00:18:22 +08:00
|
|
|
<div className="text-base font-bold leading-snug text-white line-clamp-2 md:text-lg">
|
|
|
|
|
即将到来
|
|
|
|
|
</div>
|
2026-05-19 00:34:29 +08:00
|
|
|
<div className="mt-auto grid gap-1 text-sm text-[#9b9ca6]">
|
2026-05-16 00:18:22 +08:00
|
|
|
<span>更多内容准备中</span>
|
|
|
|
|
<span>Coming soon</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
</article>
|
|
|
|
|
);
|
|
|
|
|
}
|