Add entry_id relationship links to Entries
- Introduce reusable EntryLink component - Use EntryLink in Releases and Label detail tables - Link both ID and title to /zxdb/entries/[id] for consistency Signed-off-by: Junie@MacOS
This commit is contained in:
225
.output.txt
Normal file
225
.output.txt
Normal file
@@ -0,0 +1,225 @@
|
|||||||
|
▲ Next.js 15.5.9 (Turbopack)
|
||||||
|
- Environments: .env
|
||||||
|
Creating an optimized production build ...
|
||||||
|
✓ Finished writing to disk in 48ms
|
||||||
|
Turbopack build encountered 21 warnings:
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: 311 repetitive deprecation warnings omitted.
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 0, column 8 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/bootstrap.scss:0:8:
|
||||||
|
Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
|
||||||
|
More info and automated migrator: https://sass-lang.com/d/import
|
||||||
|
0 | @import "mixins/banner";
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 1:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 10, column 29 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:10:29:
|
||||||
|
Global built-in functions are deprecated and will be removed in Dart Sass 3.0.0.
|
||||||
|
Use math.unit instead.
|
||||||
|
More info and automated migrator: https://sass-lang.com/d/import
|
||||||
|
10 | @if $prev-num == null or unit($num) == "%" or unit($prev-num) == "%" {
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 11:30 -assert-ascending()
|
||||||
|
node_modules/bootstrap/scss/_variables.scss 494:1 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 8:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 10, column 50 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:10:50:
|
||||||
|
Global built-in functions are deprecated and will be removed in Dart Sass 3.0.0.
|
||||||
|
Use math.unit instead.
|
||||||
|
More info and automated migrator: https://sass-lang.com/d/import
|
||||||
|
10 | @if $prev-num == null or unit($num) == "%" or unit($prev-num) == "%" {
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 11:51 -assert-ascending()
|
||||||
|
node_modules/bootstrap/scss/_variables.scss 494:1 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 8:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 10, column 8 of file:///Volumes/McFiver/u/GIT/next-explorer/src/scss/nbn.scss:10:8:
|
||||||
|
Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
|
||||||
|
More info and automated migrator: https://sass-lang.com/d/import
|
||||||
|
10 | @import "bootswatch";
|
||||||
|
src/scss/nbn.scss 11:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 12, column 8 of file:///Volumes/McFiver/u/GIT/next-explorer/src/scss/nbn.scss:12:8:
|
||||||
|
Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
|
||||||
|
More info and automated migrator: https://sass-lang.com/d/import
|
||||||
|
12 | @import "explorer";
|
||||||
|
src/scss/nbn.scss 13:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 176, column 10 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:176:10:
|
||||||
|
The Sass if() syntax is deprecated in favor of the modern CSS syntax.
|
||||||
|
Suggestion: if(sass($l1 > $l2): divide($l1 + 0.05, $l2 + 0.05); else: divide($l2 + 0.05, $l1 + 0.05))
|
||||||
|
More info: https://sass-lang.com/d/if-function
|
||||||
|
176 | @return if($l1 > $l2, divide($l1 + .05, $l2 + .05), divide($l2 + .05, $l1 + .05));
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 177:11 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 7:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 184, column 9 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:184:9:
|
||||||
|
red() is deprecated. Suggestion:
|
||||||
|
color.channel($color, "red", $space: rgb)
|
||||||
|
More info: https://sass-lang.com/d/color-functions
|
||||||
|
184 | "r": red($color),
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 185:10 luminance()
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 174:8 contrast-ratio()
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 159:22 color-contrast()
|
||||||
|
node_modules/bootstrap/scss/_variables.scss 846:42 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 8:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 185, column 9 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:185:9:
|
||||||
|
green() is deprecated. Suggestion:
|
||||||
|
color.channel($color, "green", $space: rgb)
|
||||||
|
More info: https://sass-lang.com/d/color-functions
|
||||||
|
185 | "g": green($color),
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 186:10 luminance()
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 174:8 contrast-ratio()
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 159:22 color-contrast()
|
||||||
|
node_modules/bootstrap/scss/_variables.scss 846:42 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 8:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 186, column 9 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:186:9:
|
||||||
|
blue() is deprecated. Suggestion:
|
||||||
|
color.channel($color, "blue", $space: rgb)
|
||||||
|
More info: https://sass-lang.com/d/color-functions
|
||||||
|
186 | "b": blue($color)
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 187:10 luminance()
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 174:8 contrast-ratio()
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 159:22 color-contrast()
|
||||||
|
node_modules/bootstrap/scss/_variables.scss 846:42 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 8:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 190, column 12 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:190:12:
|
||||||
|
The Sass if() syntax is deprecated in favor of the modern CSS syntax.
|
||||||
|
Suggestion: if(sass(divide($value, 255) < 0.04045): divide(divide($value, 255), 12.92); else: nth($_luminance-list, $value + 1))
|
||||||
|
More info: https://sass-lang.com/d/if-function
|
||||||
|
190 | $value: if(divide($value, 255) < .04045, divide(divide($value, 255), 12.92), nth($_luminance-list, $value + 1));
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 191:13 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 7:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 206, column 10 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:206:10:
|
||||||
|
Global built-in functions are deprecated and will be removed in Dart Sass 3.0.0.
|
||||||
|
Use color.mix instead.
|
||||||
|
More info and automated migrator: https://sass-lang.com/d/import
|
||||||
|
206 | @return mix(white, $color, $weight);
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 207:11 tint-color()
|
||||||
|
node_modules/bootstrap/scss/_variables.scss 79:12 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 8:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 211, column 10 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:211:10:
|
||||||
|
Global built-in functions are deprecated and will be removed in Dart Sass 3.0.0.
|
||||||
|
Use color.mix instead.
|
||||||
|
More info and automated migrator: https://sass-lang.com/d/import
|
||||||
|
211 | @return mix(black, $color, $weight);
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 212:11 shade-color()
|
||||||
|
node_modules/bootstrap/scss/_variables.scss 84:12 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 8:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 216, column 10 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:216:10:
|
||||||
|
The Sass if() syntax is deprecated in favor of the modern CSS syntax.
|
||||||
|
Suggestion: if(sass($weight > 0): shade-color($color, $weight); else: tint-color($color, -$weight))
|
||||||
|
More info: https://sass-lang.com/d/if-function
|
||||||
|
216 | @return if($weight > 0, shade-color($color, $weight), tint-color($color, -$weight));
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 217:11 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 7:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 341, column 26 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_variables.scss:341:26:
|
||||||
|
Global built-in functions are deprecated and will be removed in Dart Sass 3.0.0.
|
||||||
|
Use color.mix instead.
|
||||||
|
More info and automated migrator: https://sass-lang.com/d/import
|
||||||
|
341 | $light-bg-subtle: mix($gray-100, $white) !default;
|
||||||
|
node_modules/bootstrap/scss/_variables.scss 342:27 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 8:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 36, column 10 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:36:10:
|
||||||
|
red() is deprecated. Suggestion:
|
||||||
|
color.channel($color, "red", $space: rgb)
|
||||||
|
More info: https://sass-lang.com/d/color-functions
|
||||||
|
36 | @return red($value), green($value), blue($value);
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 37:11 to-rgb()
|
||||||
|
node_modules/bootstrap/scss/_variables.scss 846:31 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 8:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 36, column 23 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:36:23:
|
||||||
|
green() is deprecated. Suggestion:
|
||||||
|
color.channel($color, "green", $space: rgb)
|
||||||
|
More info: https://sass-lang.com/d/color-functions
|
||||||
|
36 | @return red($value), green($value), blue($value);
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 37:24 to-rgb()
|
||||||
|
node_modules/bootstrap/scss/_variables.scss 846:31 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 8:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 57, column 29 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:57:29:
|
||||||
|
The Sass if() syntax is deprecated in favor of the modern CSS syntax.
|
||||||
|
Suggestion: if(sass($arg == "$key"): $key; else: if($arg == "$value", $value, $arg))
|
||||||
|
More info: https://sass-lang.com/d/if-function
|
||||||
|
57 | $_args: append($_args, if($arg == "$key", $key, if($arg == "$value", $value, $arg)));
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 58:30 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 7:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 57, column 54 of file:///Volumes/McFiver/u/GIT/next-explorer/node_modules/bootstrap/scss/_functions.scss:57:54:
|
||||||
|
The Sass if() syntax is deprecated in favor of the modern CSS syntax.
|
||||||
|
Suggestion: if(sass($arg == "$value"): $value; else: $arg)
|
||||||
|
More info: https://sass-lang.com/d/if-function
|
||||||
|
57 | $_args: append($_args, if($arg == "$key", $key, if($arg == "$value", $value, $arg)));
|
||||||
|
node_modules/bootstrap/scss/_functions.scss 58:55 @import
|
||||||
|
node_modules/bootstrap/scss/bootstrap.scss 7:9 @import
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 6, column 8 of file:///Volumes/McFiver/u/GIT/next-explorer/src/scss/nbn.scss:6:8:
|
||||||
|
Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
|
||||||
|
More info and automated migrator: https://sass-lang.com/d/import
|
||||||
|
6 | @import "variables";
|
||||||
|
src/scss/nbn.scss 7:9 root stylesheet
|
||||||
|
./src/scss/nbn.scss
|
||||||
|
Issue while running loader
|
||||||
|
SassWarning: Deprecation Warning on line 8, column 8 of file:///Volumes/McFiver/u/GIT/next-explorer/src/scss/nbn.scss:8:8:
|
||||||
|
Sass @import rules are deprecated and will be removed in Dart Sass 3.0.0.
|
||||||
|
More info and automated migrator: https://sass-lang.com/d/import
|
||||||
|
8 | @import "../../node_modules/bootstrap/scss/bootstrap";
|
||||||
|
src/scss/nbn.scss 9:9 root stylesheet
|
||||||
|
✓ Compiled successfully in 3.7s
|
||||||
|
./src/app/zxdb/releases/ReleasesExplorer.tsx
|
||||||
|
142:6 Warning: React Hook useEffect has missing dependencies: 'fetchData', 'initial', 'initialUrlState?.casetypeId', 'initialUrlState?.dLanguageId', 'initialUrlState?.dMachinetypeId', 'initialUrlState?.filetypeId', 'initialUrlState?.isDemo', 'initialUrlState?.q', 'initialUrlState?.schemetypeId', 'initialUrlState?.sort', 'initialUrlState?.sourcetypeId', 'initialUrlState?.year', 'q', and 'updateUrl'. Either include them or remove the dependency array. react-hooks/exhaustive-deps
|
||||||
|
info - Need to disable some ESLint rules? Learn more here: https://nextjs.org/docs/app/api-reference/config/eslint#disabling-rules
|
||||||
|
Failed to compile.
|
||||||
|
./src/server/repo/zxdb.ts:491:17
|
||||||
|
Type error: Argument of type 'Name' is not assignable to parameter of type 'SQL<unknown> | Column<ColumnBaseConfig<ColumnDataType, string>, object, object> | Aliased<unknown>'.
|
||||||
|
Type 'Name' is missing the following properties from type 'Aliased<unknown>': sql, fieldAlias, _
|
||||||
|
489 | .select({ total: sql<number>`count(distinct ${sql.identifier("label_id")})` })
|
||||||
|
490 | .from(sql`search_by_names`)
|
||||||
|
> 491 | .where(like(sql.identifier("label_name"), pattern));
|
||||||
|
| ^
|
||||||
|
492 | const total = Number(countRows[0]?.total ?? 0);
|
||||||
|
493 |
|
||||||
|
494 | const items = await db
|
||||||
|
Next.js build worker exited with code: 1 and signal: null
|
||||||
7
COMMIT_EDITMSG
Normal file
7
COMMIT_EDITMSG
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
Add entry_id relationship links to Entries
|
||||||
|
|
||||||
|
- Introduce reusable EntryLink component
|
||||||
|
- Use EntryLink in Releases and Label detail tables
|
||||||
|
- Link both ID and title to /zxdb/entries/[id] for consistency
|
||||||
|
|
||||||
|
Signed-off-by: Junie@MacOS
|
||||||
18
src/app/zxdb/components/EntryLink.tsx
Normal file
18
src/app/zxdb/components/EntryLink.tsx
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
"use client";
|
||||||
|
|
||||||
|
import Link from "next/link";
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
id: number;
|
||||||
|
title?: string;
|
||||||
|
className?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function EntryLink({ id, title, className }: Props) {
|
||||||
|
const text = typeof title === "string" && title.length > 0 ? title : `#${id}`;
|
||||||
|
return (
|
||||||
|
<Link href={`/zxdb/entries/${id}`} className={className}>
|
||||||
|
{text}
|
||||||
|
</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import EntryLink from "../components/EntryLink";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
|
||||||
type Item = {
|
type Item = {
|
||||||
@@ -244,9 +245,9 @@ export default function EntriesExplorer({
|
|||||||
<tbody>
|
<tbody>
|
||||||
{data.items.map((it) => (
|
{data.items.map((it) => (
|
||||||
<tr key={it.id}>
|
<tr key={it.id}>
|
||||||
<td>{it.id}</td>
|
<td><EntryLink id={it.id} /></td>
|
||||||
<td>
|
<td>
|
||||||
<Link href={`/zxdb/entries/${it.id}`}>{it.title}</Link>
|
<EntryLink id={it.id} title={it.title} />
|
||||||
</td>
|
</td>
|
||||||
<td>
|
<td>
|
||||||
{it.machinetypeId != null ? (
|
{it.machinetypeId != null ? (
|
||||||
|
|||||||
@@ -66,6 +66,9 @@ export type EntryDetailData = {
|
|||||||
year: number | null;
|
year: number | null;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
|
// Additional relationships
|
||||||
|
aliases?: { releaseSeq: number; languageId: string; title: string }[];
|
||||||
|
webrefs?: { link: string; languageId: string; website: { id: number; name: string; link?: string | null } }[];
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function EntryDetailClient({ data }: { data: EntryDetailData | null }) {
|
export default function EntryDetailClient({ data }: { data: EntryDetailData | null }) {
|
||||||
@@ -286,6 +289,74 @@ export default function EntryDetailClient({ data }: { data: EntryDetailData | nu
|
|||||||
|
|
||||||
<hr />
|
<hr />
|
||||||
|
|
||||||
|
{/* Aliases (alternative titles) */}
|
||||||
|
<div>
|
||||||
|
<h5>Aliases</h5>
|
||||||
|
{(!data.aliases || data.aliases.length === 0) && <div className="text-secondary">No aliases</div>}
|
||||||
|
{data.aliases && data.aliases.length > 0 && (
|
||||||
|
<div className="table-responsive">
|
||||||
|
<table className="table table-sm table-striped align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th style={{ width: 90 }}>Release #</th>
|
||||||
|
<th style={{ width: 120 }}>Language</th>
|
||||||
|
<th>Title</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{data.aliases.map((a, idx) => (
|
||||||
|
<tr key={`${a.releaseSeq}-${a.languageId}-${idx}`}>
|
||||||
|
<td>#{a.releaseSeq}</td>
|
||||||
|
<td>{a.languageId}</td>
|
||||||
|
<td>{a.title}</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
|
{/* Web links (external references) */}
|
||||||
|
<div>
|
||||||
|
<h5>Web links</h5>
|
||||||
|
{(!data.webrefs || data.webrefs.length === 0) && <div className="text-secondary">No web links</div>}
|
||||||
|
{data.webrefs && data.webrefs.length > 0 && (
|
||||||
|
<div className="table-responsive">
|
||||||
|
<table className="table table-sm table-striped align-middle">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Website</th>
|
||||||
|
<th style={{ width: 120 }}>Language</th>
|
||||||
|
<th>URL</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{data.webrefs.map((w, idx) => (
|
||||||
|
<tr key={`${w.website.id}-${idx}`}>
|
||||||
|
<td>
|
||||||
|
{w.website.link ? (
|
||||||
|
<a href={w.website.link} target="_blank" rel="noopener noreferrer">{w.website.name}</a>
|
||||||
|
) : (
|
||||||
|
<span>{w.website.name}</span>
|
||||||
|
)}
|
||||||
|
</td>
|
||||||
|
<td>{w.languageId}</td>
|
||||||
|
<td>
|
||||||
|
<a href={w.link} target="_blank" rel="noopener noreferrer">{w.link}</a>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
))}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<hr />
|
||||||
|
|
||||||
<div>
|
<div>
|
||||||
<h5>Files</h5>
|
<h5>Files</h5>
|
||||||
{(!data.files || data.files.length === 0) && <div className="text-secondary">No files linked</div>}
|
{(!data.files || data.files.length === 0) && <div className="text-secondary">No files linked</div>}
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
"use client";
|
"use client";
|
||||||
|
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import EntryLink from "../../components/EntryLink";
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState } from "react";
|
||||||
import { useRouter } from "next/navigation";
|
import { useRouter } from "next/navigation";
|
||||||
|
|
||||||
@@ -69,8 +70,8 @@ export default function LabelDetailClient({ id, initial, initialTab, initialQ }:
|
|||||||
<tbody>
|
<tbody>
|
||||||
{current.items.map((it) => (
|
{current.items.map((it) => (
|
||||||
<tr key={it.id}>
|
<tr key={it.id}>
|
||||||
<td>{it.id}</td>
|
<td><EntryLink id={it.id} /></td>
|
||||||
<td><Link href={`/zxdb/entries/${it.id}`}>{it.title}</Link></td>
|
<td><EntryLink id={it.id} title={it.title} /></td>
|
||||||
<td>
|
<td>
|
||||||
{it.machinetypeId != null ? (
|
{it.machinetypeId != null ? (
|
||||||
it.machinetypeName ? (
|
it.machinetypeName ? (
|
||||||
|
|||||||
@@ -2,6 +2,7 @@
|
|||||||
|
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import Link from "next/link";
|
import Link from "next/link";
|
||||||
|
import EntryLink from "../components/EntryLink";
|
||||||
import { usePathname, useRouter } from "next/navigation";
|
import { usePathname, useRouter } from "next/navigation";
|
||||||
|
|
||||||
type Item = {
|
type Item = {
|
||||||
@@ -313,9 +314,11 @@ export default function ReleasesExplorer({
|
|||||||
<tbody>
|
<tbody>
|
||||||
{data.items.map((it) => (
|
{data.items.map((it) => (
|
||||||
<tr key={`${it.entryId}-${it.releaseSeq}`}>
|
<tr key={`${it.entryId}-${it.releaseSeq}`}>
|
||||||
<td>{it.entryId}</td>
|
|
||||||
<td>
|
<td>
|
||||||
<Link href={`/zxdb/entries/${it.entryId}`}>{it.entryTitle}</Link>
|
<EntryLink id={it.entryId} />
|
||||||
|
</td>
|
||||||
|
<td>
|
||||||
|
<EntryLink id={it.entryId} title={it.entryTitle} />
|
||||||
</td>
|
</td>
|
||||||
<td>#{it.releaseSeq}</td>
|
<td>#{it.releaseSeq}</td>
|
||||||
<td>{it.year ?? <span className="text-secondary">-</span>}</td>
|
<td>{it.year ?? <span className="text-secondary">-</span>}</td>
|
||||||
|
|||||||
@@ -20,6 +20,9 @@ import {
|
|||||||
availabletypes,
|
availabletypes,
|
||||||
currencies,
|
currencies,
|
||||||
roletypes,
|
roletypes,
|
||||||
|
aliases,
|
||||||
|
webrefs,
|
||||||
|
websites,
|
||||||
} from "@/server/schema/zxdb";
|
} from "@/server/schema/zxdb";
|
||||||
|
|
||||||
export interface SearchParams {
|
export interface SearchParams {
|
||||||
@@ -219,6 +222,9 @@ export interface EntryDetail {
|
|||||||
year: number | null;
|
year: number | null;
|
||||||
}[];
|
}[];
|
||||||
}[];
|
}[];
|
||||||
|
// Additional relationships surfaced on the entry detail page
|
||||||
|
aliases?: { releaseSeq: number; languageId: string; title: string }[];
|
||||||
|
webrefs?: { link: string; languageId: string; website: { id: number; name: string; link?: string | null } }[];
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
||||||
@@ -411,6 +417,24 @@ export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
|||||||
// Sort releases by sequence for stable UI order
|
// Sort releases by sequence for stable UI order
|
||||||
releasesData.sort((a, b) => a.releaseSeq - b.releaseSeq);
|
releasesData.sort((a, b) => a.releaseSeq - b.releaseSeq);
|
||||||
|
|
||||||
|
// Fetch extra relationships in parallel
|
||||||
|
let aliasRows: { releaseSeq: number | string; languageId: string; title: string }[] = [];
|
||||||
|
let webrefRows: { link: string; languageId: string; websiteId: number | string; websiteName: string; websiteLink: string | null }[] = [];
|
||||||
|
try {
|
||||||
|
aliasRows = await db
|
||||||
|
.select({ releaseSeq: aliases.releaseSeq, languageId: aliases.languageId, title: aliases.title })
|
||||||
|
.from(aliases)
|
||||||
|
.where(eq(aliases.entryId, id));
|
||||||
|
} catch {}
|
||||||
|
try {
|
||||||
|
const rows = await db
|
||||||
|
.select({ link: webrefs.link, languageId: webrefs.languageId, websiteId: websites.id, websiteName: websites.name, websiteLink: websites.link })
|
||||||
|
.from(webrefs)
|
||||||
|
.innerJoin(websites, eq(websites.id, webrefs.websiteId))
|
||||||
|
.where(eq(webrefs.entryId, id));
|
||||||
|
webrefRows = rows as typeof webrefRows;
|
||||||
|
} catch {}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
id: base.id,
|
id: base.id,
|
||||||
title: base.title,
|
title: base.title,
|
||||||
@@ -453,6 +477,8 @@ export async function getEntryById(id: number): Promise<EntryDetail | null> {
|
|||||||
year: d.year != null ? Number(d.year) : null,
|
year: d.year != null ? Number(d.year) : null,
|
||||||
releaseSeq: Number(d.releaseSeq),
|
releaseSeq: Number(d.releaseSeq),
|
||||||
})),
|
})),
|
||||||
|
aliases: aliasRows.map((a) => ({ releaseSeq: Number(a.releaseSeq), languageId: a.languageId, title: a.title })),
|
||||||
|
webrefs: webrefRows.map((w) => ({ link: w.link, languageId: w.languageId, website: { id: Number(w.websiteId), name: w.websiteName, link: w.websiteLink } })),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -143,6 +143,14 @@ export const hosts = mysqlTable("hosts", {
|
|||||||
magazineId: smallint("magazine_id"),
|
magazineId: smallint("magazine_id"),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---- Aliases (alternative titles per entry/release/language)
|
||||||
|
export const aliases = mysqlTable("aliases", {
|
||||||
|
entryId: int("entry_id").notNull(),
|
||||||
|
releaseSeq: smallint("release_seq").notNull().default(0),
|
||||||
|
languageId: char("language_id", { length: 2 }).notNull(),
|
||||||
|
title: varchar("title", { length: 250 }).notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
// `releases` are identified by (entry_id, release_seq)
|
// `releases` are identified by (entry_id, release_seq)
|
||||||
export const releases = mysqlTable("releases", {
|
export const releases = mysqlTable("releases", {
|
||||||
entryId: int("entry_id").notNull(),
|
entryId: int("entry_id").notNull(),
|
||||||
@@ -184,6 +192,22 @@ export const downloads = mysqlTable("downloads", {
|
|||||||
comments: varchar("comments", { length: 250 }),
|
comments: varchar("comments", { length: 250 }),
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ---- Web references (external links tied to entries)
|
||||||
|
export const webrefs = mysqlTable("webrefs", {
|
||||||
|
entryId: int("entry_id").notNull(),
|
||||||
|
link: varchar("link", { length: 200 }).notNull(),
|
||||||
|
websiteId: tinyint("website_id").notNull(),
|
||||||
|
languageId: char("language_id", { length: 2 }).notNull(),
|
||||||
|
});
|
||||||
|
|
||||||
|
export const websites = mysqlTable("websites", {
|
||||||
|
id: tinyint("id").notNull().primaryKey(),
|
||||||
|
name: varchar("name", { length: 100 }).notNull(),
|
||||||
|
comments: varchar("comments", { length: 100 }),
|
||||||
|
link: varchar("link", { length: 100 }),
|
||||||
|
linkMask: varchar("link_mask", { length: 100 }),
|
||||||
|
});
|
||||||
|
|
||||||
// Roles relation (composite PK in DB)
|
// Roles relation (composite PK in DB)
|
||||||
export const roles = mysqlTable("roles", {
|
export const roles = mysqlTable("roles", {
|
||||||
entryId: int("entry_id").notNull(),
|
entryId: int("entry_id").notNull(),
|
||||||
|
|||||||
Reference in New Issue
Block a user