Add ZXDB breadcrumbs and release places
Add ZXDB breadcrumbs on list/detail pages and group release magazine references by issue for clearer Places view. Signed-off-by: codex@lucy.xalior.com
This commit is contained in:
27
src/app/zxdb/components/ZxdbBreadcrumbs.tsx
Normal file
27
src/app/zxdb/components/ZxdbBreadcrumbs.tsx
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
type Crumb = {
|
||||||
|
label: string;
|
||||||
|
href?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function ZxdbBreadcrumbs({ items }: { items: Crumb[] }) {
|
||||||
|
if (items.length === 0) return null;
|
||||||
|
|
||||||
|
const lastIndex = items.length - 1;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<nav aria-label="breadcrumb">
|
||||||
|
<ol className="breadcrumb">
|
||||||
|
{items.map((item, index) => {
|
||||||
|
const isActive = index === lastIndex || !item.href;
|
||||||
|
return (
|
||||||
|
<li key={`${item.label}-${index}`} className={`breadcrumb-item${isActive ? " active" : ""}`} aria-current={isActive ? "page" : undefined}>
|
||||||
|
{isActive ? item.label : <Link href={item.href ?? "#"}>{item.label}</Link>}
|
||||||
|
</li>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</ol>
|
||||||
|
</nav>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@ import { useEffect, useMemo, useState } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import EntryLink from "../components/EntryLink";
|
import EntryLink from "../components/EntryLink";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
type Item = {
|
type Item = {
|
||||||
id: number;
|
id: number;
|
||||||
@@ -178,6 +179,13 @@ export default function EntriesExplorer({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ZxdbBreadcrumbs
|
||||||
|
items={[
|
||||||
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
|
{ label: "Entries" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<h1 className="mb-3">Entries</h1>
|
<h1 className="mb-3">Entries</h1>
|
||||||
<form className="row gy-2 gx-2 align-items-center" onSubmit={onSubmit}>
|
<form className="row gy-2 gx-2 align-items-center" onSubmit={onSubmit}>
|
||||||
<div className="col-sm-8 col-md-6 col-lg-4">
|
<div className="col-sm-8 col-md-6 col-lg-4">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
type Label = { id: number; name: string; labeltypeId: string | null };
|
type Label = { id: number; name: string; labeltypeId: string | null };
|
||||||
export type EntryDetailData = {
|
export type EntryDetailData = {
|
||||||
@@ -76,6 +77,14 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ZxdbBreadcrumbs
|
||||||
|
items={[
|
||||||
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
|
{ label: "Entries", href: "/zxdb/entries" },
|
||||||
|
{ label: data.title },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="d-flex align-items-center gap-2 flex-wrap">
|
<div className="d-flex align-items-center gap-2 flex-wrap">
|
||||||
<h1 className="mb-0">{data.title}</h1>
|
<h1 className="mb-0">{data.title}</h1>
|
||||||
{data.genre.name && (
|
{data.genre.name && (
|
||||||
@@ -243,7 +252,9 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
|
|||||||
<Link className="badge text-bg-primary text-decoration-none" href={`/zxdb/machinetypes/${d.machinetype.id}`}>{d.machinetype.name}</Link>
|
<Link className="badge text-bg-primary text-decoration-none" href={`/zxdb/machinetypes/${d.machinetype.id}`}>{d.machinetype.name}</Link>
|
||||||
)}
|
)}
|
||||||
{typeof d.year === "number" ? <span className="badge text-bg-dark">{d.year}</span> : null}
|
{typeof d.year === "number" ? <span className="badge text-bg-dark">{d.year}</span> : null}
|
||||||
<span className="badge text-bg-light">rel #{d.releaseSeq}</span>
|
<Link className="badge text-bg-light text-decoration-none" href={`/zxdb/releases/${data.id}/${d.releaseSeq}`}>
|
||||||
|
rel #{d.releaseSeq}
|
||||||
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
</td>
|
</td>
|
||||||
<td>{d.comments ?? ""}</td>
|
<td>{d.comments ?? ""}</td>
|
||||||
@@ -306,7 +317,9 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
|
|||||||
<tbody>
|
<tbody>
|
||||||
{data.aliases.map((a, idx) => (
|
{data.aliases.map((a, idx) => (
|
||||||
<tr key={`${a.releaseSeq}-${a.languageId}-${idx}`}>
|
<tr key={`${a.releaseSeq}-${a.languageId}-${idx}`}>
|
||||||
<td>#{a.releaseSeq}</td>
|
<td>
|
||||||
|
<Link href={`/zxdb/releases/${data.id}/${a.releaseSeq}`}>#{a.releaseSeq}</Link>
|
||||||
|
</td>
|
||||||
<td>{a.languageId}</td>
|
<td>{a.languageId}</td>
|
||||||
<td>{a.title}</td>
|
<td>{a.title}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
type Genre = { id: number; name: string };
|
type Genre = { id: number; name: string };
|
||||||
type Paged<T> = { items: T[]; page: number; pageSize: number; total: number };
|
type Paged<T> = { items: T[]; page: number; pageSize: number; total: number };
|
||||||
@@ -31,6 +32,13 @@ export default function GenresSearch({ initial, initialQ }: { initial?: Paged<Ge
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ZxdbBreadcrumbs
|
||||||
|
items={[
|
||||||
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
|
{ label: "Genres" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<h1>Genres</h1>
|
<h1>Genres</h1>
|
||||||
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
||||||
<div className="col-sm-8 col-md-6 col-lg-4">
|
<div className="col-sm-8 col-md-6 col-lg-4">
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import Link from "next/link";
|
|||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { getIssue } from "@/server/repo/zxdb";
|
import { getIssue } from "@/server/repo/zxdb";
|
||||||
import EntryLink from "@/app/zxdb/components/EntryLink";
|
import EntryLink from "@/app/zxdb/components/EntryLink";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
export const metadata = { title: "ZXDB Issue" };
|
export const metadata = { title: "ZXDB Issue" };
|
||||||
export const revalidate = 3600;
|
export const revalidate = 3600;
|
||||||
@@ -18,6 +19,15 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ZxdbBreadcrumbs
|
||||||
|
items={[
|
||||||
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
|
{ label: "Magazines", href: "/zxdb/magazines" },
|
||||||
|
{ label: issue.magazine.title, href: `/zxdb/magazines/${issue.magazine.id}` },
|
||||||
|
{ label: `Issue ${ym || issue.id}` },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<div className="mb-3 d-flex gap-2 flex-wrap">
|
<div className="mb-3 d-flex gap-2 flex-wrap">
|
||||||
<Link className="btn btn-outline-secondary btn-sm" href={`/zxdb/magazines/${issue.magazine.id}`}>← Back to magazine</Link>
|
<Link className="btn btn-outline-secondary btn-sm" href={`/zxdb/magazines/${issue.magazine.id}`}>← Back to magazine</Link>
|
||||||
<Link className="btn btn-outline-secondary btn-sm" href="/zxdb/magazines">All magazines</Link>
|
<Link className="btn btn-outline-secondary btn-sm" href="/zxdb/magazines">All magazines</Link>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
type Label = { id: number; name: string; labeltypeId: string | null };
|
type Label = { id: number; name: string; labeltypeId: string | null };
|
||||||
type Paged<T> = { items: T[]; page: number; pageSize: number; total: number };
|
type Paged<T> = { items: T[]; page: number; pageSize: number; total: number };
|
||||||
@@ -33,6 +34,13 @@ export default function LabelsSearch({ initial, initialQ }: { initial?: Paged<La
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ZxdbBreadcrumbs
|
||||||
|
items={[
|
||||||
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
|
{ label: "Labels" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<h1>Labels</h1>
|
<h1>Labels</h1>
|
||||||
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
||||||
<div className="col-sm-8 col-md-6 col-lg-4">
|
<div className="col-sm-8 col-md-6 col-lg-4">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
type Language = { id: string; name: string };
|
type Language = { id: string; name: string };
|
||||||
type Paged<T> = { items: T[]; page: number; pageSize: number; total: number };
|
type Paged<T> = { items: T[]; page: number; pageSize: number; total: number };
|
||||||
@@ -31,6 +32,13 @@ export default function LanguagesSearch({ initial, initialQ }: { initial?: Paged
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ZxdbBreadcrumbs
|
||||||
|
items={[
|
||||||
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
|
{ label: "Languages" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<h1>Languages</h1>
|
<h1>Languages</h1>
|
||||||
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
||||||
<div className="col-sm-8 col-md-6 col-lg-4">
|
<div className="col-sm-8 col-md-6 col-lg-4">
|
||||||
|
|||||||
@@ -3,6 +3,7 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
type MT = { id: number; name: string };
|
type MT = { id: number; name: string };
|
||||||
type Paged<T> = { items: T[]; page: number; pageSize: number; total: number };
|
type Paged<T> = { items: T[]; page: number; pageSize: number; total: number };
|
||||||
@@ -33,6 +34,13 @@ export default function MachineTypesSearch({ initial, initialQ }: { initial?: Pa
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ZxdbBreadcrumbs
|
||||||
|
items={[
|
||||||
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
|
{ label: "Machine Types" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<h1>Machine Types</h1>
|
<h1>Machine Types</h1>
|
||||||
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
<form className="row gy-2 gx-2 align-items-center" onSubmit={submit}>
|
||||||
<div className="col-sm-8 col-md-6 col-lg-4">
|
<div className="col-sm-8 col-md-6 col-lg-4">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { notFound } from "next/navigation";
|
import { notFound } from "next/navigation";
|
||||||
import { getMagazine } from "@/server/repo/zxdb";
|
import { getMagazine } from "@/server/repo/zxdb";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
export const metadata = { title: "ZXDB Magazine" };
|
export const metadata = { title: "ZXDB Magazine" };
|
||||||
export const revalidate = 3600;
|
export const revalidate = 3600;
|
||||||
@@ -15,6 +16,14 @@ export default async function Page({ params }: { params: Promise<{ id: string }>
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ZxdbBreadcrumbs
|
||||||
|
items={[
|
||||||
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
|
{ label: "Magazines", href: "/zxdb/magazines" },
|
||||||
|
{ label: mag.title },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<h1 className="mb-1">{mag.title}</h1>
|
<h1 className="mb-1">{mag.title}</h1>
|
||||||
<div className="text-secondary mb-3">Language: {mag.languageId}</div>
|
<div className="text-secondary mb-3">Language: {mag.languageId}</div>
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import { listMagazines } from "@/server/repo/zxdb";
|
import { listMagazines } from "@/server/repo/zxdb";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
export const metadata = { title: "ZXDB Magazines" };
|
export const metadata = { title: "ZXDB Magazines" };
|
||||||
|
|
||||||
@@ -19,6 +20,13 @@ export default async function Page({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ZxdbBreadcrumbs
|
||||||
|
items={[
|
||||||
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
|
{ label: "Magazines" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<h1 className="mb-3">Magazines</h1>
|
<h1 className="mb-3">Magazines</h1>
|
||||||
|
|
||||||
<form className="mb-3" action="/zxdb/magazines" method="get">
|
<form className="mb-3" action="/zxdb/magazines" method="get">
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ import { useEffect, useMemo, useRef, useState } from "react";
|
|||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
import EntryLink from "../components/EntryLink";
|
import EntryLink from "../components/EntryLink";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
type Item = {
|
type Item = {
|
||||||
entryId: number;
|
entryId: number;
|
||||||
@@ -229,6 +230,13 @@ export default function ReleasesExplorer({
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
<ZxdbBreadcrumbs
|
||||||
|
items={[
|
||||||
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
|
{ label: "Releases" },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
|
||||||
<h1 className="mb-3">Releases</h1>
|
<h1 className="mb-3">Releases</h1>
|
||||||
<form className="row gy-2 gx-2 align-items-center" onSubmit={onSubmit}>
|
<form className="row gy-2 gx-2 align-items-center" onSubmit={onSubmit}>
|
||||||
<div className="col-sm-8 col-md-6 col-lg-4">
|
<div className="col-sm-8 col-md-6 col-lg-4">
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import ZxdbBreadcrumbs from "@/app/zxdb/components/ZxdbBreadcrumbs";
|
||||||
|
|
||||||
type ReleaseDetailData = {
|
type ReleaseDetailData = {
|
||||||
entry: {
|
entry: {
|
||||||
@@ -114,25 +115,69 @@ function formatCurrency(value: number | null, currency: ReleaseDetailData["relea
|
|||||||
return String(value);
|
return String(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type MagazineGroup = {
|
||||||
|
magazineId: number | null;
|
||||||
|
magazineName: string | null;
|
||||||
|
items: ReleaseDetailData["magazineRefs"];
|
||||||
|
};
|
||||||
|
|
||||||
|
type IssueGroup = {
|
||||||
|
issueId: number;
|
||||||
|
issue: ReleaseDetailData["magazineRefs"][number]["issue"];
|
||||||
|
items: ReleaseDetailData["magazineRefs"];
|
||||||
|
};
|
||||||
|
|
||||||
|
function groupMagazineRefs(refs: ReleaseDetailData["magazineRefs"]) {
|
||||||
|
const groups: MagazineGroup[] = [];
|
||||||
|
const lookup = new Map<string, MagazineGroup>();
|
||||||
|
|
||||||
|
for (const ref of refs) {
|
||||||
|
const key = ref.magazineId != null ? `mag:${ref.magazineId}` : "mag:unknown";
|
||||||
|
let group = lookup.get(key);
|
||||||
|
if (!group) {
|
||||||
|
group = { magazineId: ref.magazineId, magazineName: ref.magazineName, items: [] };
|
||||||
|
lookup.set(key, group);
|
||||||
|
groups.push(group);
|
||||||
|
}
|
||||||
|
group.items.push(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
|
function groupIssueRefs(refs: ReleaseDetailData["magazineRefs"]) {
|
||||||
|
const groups: IssueGroup[] = [];
|
||||||
|
const lookup = new Map<number, IssueGroup>();
|
||||||
|
|
||||||
|
for (const ref of refs) {
|
||||||
|
const key = ref.issueId;
|
||||||
|
let group = lookup.get(key);
|
||||||
|
if (!group) {
|
||||||
|
group = { issueId: ref.issueId, issue: ref.issue, items: [] };
|
||||||
|
lookup.set(key, group);
|
||||||
|
groups.push(group);
|
||||||
|
}
|
||||||
|
group.items.push(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
return groups;
|
||||||
|
}
|
||||||
|
|
||||||
export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData | null }) {
|
export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData | null }) {
|
||||||
if (!data) return <div className="alert alert-warning">Not found</div>;
|
if (!data) return <div className="alert alert-warning">Not found</div>;
|
||||||
|
|
||||||
|
const magazineGroups = groupMagazineRefs(data.magazineRefs);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<nav aria-label="breadcrumb">
|
<ZxdbBreadcrumbs
|
||||||
<ol className="breadcrumb">
|
items={[
|
||||||
<li className="breadcrumb-item">
|
{ label: "ZXDB", href: "/zxdb" },
|
||||||
<Link href="/zxdb">ZXDB</Link>
|
{ label: "Releases", href: "/zxdb/releases" },
|
||||||
</li>
|
{ label: data.entry.title, href: `/zxdb/entries/${data.entry.id}` },
|
||||||
<li className="breadcrumb-item">
|
{ label: `Release #${data.release.releaseSeq}` },
|
||||||
<Link href="/zxdb/releases">Releases</Link>
|
]}
|
||||||
</li>
|
/>
|
||||||
<li className="breadcrumb-item">
|
|
||||||
<Link href={`/zxdb/entries/${data.entry.id}`}>{data.entry.title}</Link>
|
|
||||||
</li>
|
|
||||||
<li className="breadcrumb-item active" aria-current="page">Release #{data.release.releaseSeq}</li>
|
|
||||||
</ol>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
<div className="d-flex align-items-center gap-2 flex-wrap">
|
<div className="d-flex align-items-center gap-2 flex-wrap">
|
||||||
<h1 className="mb-0">Release #{data.release.releaseSeq}</h1>
|
<h1 className="mb-0">Release #{data.release.releaseSeq}</h1>
|
||||||
@@ -221,43 +266,61 @@ export default function ReleaseDetailClient({ data }: { data: ReleaseDetailData
|
|||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h5>Magazine References</h5>
|
<h5>Places (Magazines)</h5>
|
||||||
{data.magazineRefs.length === 0 && <div className="text-secondary">No magazine references</div>}
|
{magazineGroups.length === 0 && <div className="text-secondary">No magazine references</div>}
|
||||||
{data.magazineRefs.length > 0 && (
|
{magazineGroups.length > 0 && (
|
||||||
<div className="table-responsive">
|
<div className="d-flex flex-column gap-3">
|
||||||
<table className="table table-sm table-striped align-middle">
|
{magazineGroups.map((group) => (
|
||||||
<thead>
|
<div key={group.magazineId ?? "unknown"}>
|
||||||
<tr>
|
<div className="d-flex align-items-center justify-content-between">
|
||||||
<th>Magazine</th>
|
<div className="fw-semibold">
|
||||||
<th>Issue</th>
|
{group.magazineId != null ? (
|
||||||
<th style={{ width: 120 }}>Type</th>
|
<Link href={`/zxdb/magazines/${group.magazineId}`}>
|
||||||
<th style={{ width: 80 }}>Page</th>
|
{group.magazineName ?? `Magazine #${group.magazineId}`}
|
||||||
<th style={{ width: 100 }}>Original</th>
|
</Link>
|
||||||
<th>Notes</th>
|
) : (
|
||||||
</tr>
|
<span className="text-secondary">Unknown magazine</span>
|
||||||
</thead>
|
)}
|
||||||
<tbody>
|
</div>
|
||||||
{data.magazineRefs.map((m) => (
|
<div className="text-secondary small">{group.items.length} reference{group.items.length === 1 ? "" : "s"}</div>
|
||||||
<tr key={m.id}>
|
</div>
|
||||||
<td>
|
{groupIssueRefs(group.items).map((issueGroup) => (
|
||||||
{m.magazineId != null ? (
|
<div key={issueGroup.issueId} className="mt-2">
|
||||||
<Link href={`/zxdb/magazines/${m.magazineId}`}>{m.magazineName ?? `#${m.magazineId}`}</Link>
|
<div className="d-flex align-items-center justify-content-between">
|
||||||
) : (
|
<div>
|
||||||
<span className="text-secondary">-</span>
|
<Link href={`/zxdb/issues/${issueGroup.issueId}`}>Issue #{issueGroup.issueId}</Link>
|
||||||
)}
|
<div className="text-secondary small">{formatIssue(issueGroup.issue) || "-"}</div>
|
||||||
</td>
|
</div>
|
||||||
<td>
|
<div className="text-secondary small">
|
||||||
<Link href={`/zxdb/issues/${m.issueId}`}>#{m.issueId}</Link>
|
{issueGroup.items.length} reference{issueGroup.items.length === 1 ? "" : "s"}
|
||||||
<div className="text-secondary small">{formatIssue(m.issue) || "-"}</div>
|
</div>
|
||||||
</td>
|
</div>
|
||||||
<td>{m.referencetypeName ?? `#${m.referencetypeId}`}</td>
|
<div className="table-responsive mt-2">
|
||||||
<td>{m.page}</td>
|
<table className="table table-sm table-striped align-middle">
|
||||||
<td>{m.isOriginal ? "Yes" : "No"}</td>
|
<thead>
|
||||||
<td>{m.scoreGroup || "-"}</td>
|
<tr>
|
||||||
</tr>
|
<th style={{ width: 80 }}>Page</th>
|
||||||
|
<th style={{ width: 120 }}>Type</th>
|
||||||
|
<th style={{ width: 100 }}>Original</th>
|
||||||
|
<th>Notes</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{issueGroup.items.map((m) => (
|
||||||
|
<tr key={m.id}>
|
||||||
|
<td>{m.page}</td>
|
||||||
|
<td>{m.referencetypeName ?? `#${m.referencetypeId}`}</td>
|
||||||
|
<td>{m.isOriginal ? "Yes" : "No"}</td>
|
||||||
|
<td>{m.scoreGroup || "-"}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</tbody>
|
</div>
|
||||||
</table>
|
))}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user