← back to Trademarks Copyright
src/app/item/[id]/page.tsx
128 lines
import { notFound } from "next/navigation";
import Link from "next/link";
import { query } from "@/lib/db";
import NewsFeed from "@/components/NewsFeed";
import SwotPanel from "@/components/SwotPanel";
import Disclaimer from "@/components/Disclaimer";
import type { Item } from "@/lib/types";
type Params = { id: string };
export default async function ItemPage({ params }: { params: Promise<Params> }) {
const { id } = await params;
const numId = Number(id);
if (!Number.isFinite(numId)) notFound();
const { rows } = await query<Item & { years_expired: number; follow_up_date: string }>(
`SELECT *,
EXTRACT(YEAR FROM AGE(NOW(), expired_date))::int AS years_expired,
(CURRENT_DATE + INTERVAL '6 months')::date AS follow_up_date
FROM items WHERE id = $1`,
[numId]
);
if (!rows.length) notFound();
const item = rows[0];
return (
<div>
<Disclaimer />
<Link href="/" className="text-sm text-ink/60 hover:text-brass">← All items</Link>
<div className="mt-2 flex flex-wrap items-baseline gap-3">
<h1 className="text-3xl font-semibold tracking-tight">{item.name}</h1>
<span className="tag">{item.kind}</span>
<span className="tag">{item.status}</span>
<span className="text-ink/60">
Expired {item.expired_date.toString().slice(0, 10)} ({item.years_expired} yrs ago)
{" · "}
<span title="Revisit date — 6 months from today">Follow-up {item.follow_up_date.toString().slice(0, 10)}</span>
</span>
{item.composite_score !== null && (
<span className="ml-auto rounded bg-ledger px-2 py-1 font-semibold text-parchment">
Score {Number(item.composite_score).toFixed(1)}
</span>
)}
</div>
<div className="mt-6 grid grid-cols-1 gap-6 lg:grid-cols-3">
<section className="card p-4 lg:col-span-2">
<h2 className="mb-2 text-lg font-semibold">Details</h2>
<dl className="grid grid-cols-2 gap-y-2 text-sm">
<dt className="text-ink/60">Original owner</dt><dd>{item.original_owner ?? "—"}</dd>
<dt className="text-ink/60">Registration #</dt><dd>{item.registration_number ?? "—"}</dd>
<dt className="text-ink/60">NICE class</dt><dd>{item.nice_class ?? "—"}</dd>
<dt className="text-ink/60">Goods / services</dt><dd>{item.goods_services ?? "—"}</dd>
<dt className="text-ink/60">Filed</dt><dd>{item.filed_date?.toString().slice(0, 10) ?? "—"}</dd>
<dt className="text-ink/60">Registered</dt><dd>{item.registered_date?.toString().slice(0, 10) ?? "—"}</dd>
<dt className="text-ink/60">Source</dt>
<dd>
{item.source_url ? (
<a href={item.source_url} target="_blank" rel="noreferrer" className="text-brass hover:underline">{item.source}</a>
) : (item.source)}
</dd>
</dl>
{item.notes && (
<>
<h3 className="mt-4 text-sm font-semibold">Notes</h3>
<p className="text-sm text-ink/80">{item.notes}</p>
</>
)}
</section>
<aside className="card p-4">
<h2 className="mb-2 text-lg font-semibold">Scoring breakdown</h2>
<ScoreBar label="Distinctiveness" v={item.distinctiveness_score} w={25} />
<ScoreBar label="Recognition" v={item.recognition_score} w={15} />
<ScoreBar label="Monetization breadth" v={item.monetization_breadth} w={10} />
<div className="mt-2 text-sm">
<span className="text-ink/60">.com available: </span>
<b>{item.domain_available === true ? "Yes" : item.domain_available === false ? "No" : "Unknown"}</b>
</div>
<div className="text-sm">
<span className="text-ink/60">Competing active marks: </span>
<b>{item.competing_active_marks ?? "—"}</b>
</div>
</aside>
<section className="card p-4 lg:col-span-3">
<h2 className="mb-2 text-lg font-semibold">Monetization ideas</h2>
{item.monetization_ideas?.length ? (
<ul className="grid grid-cols-1 gap-3 md:grid-cols-2 lg:grid-cols-3">
{item.monetization_ideas.map((idea, i) => (
<li key={i} className="rounded border border-ink/10 bg-white p-3">
<div className="font-semibold">{idea.title}</div>
<div className="text-sm text-ink/70">{idea.description}</div>
</li>
))}
</ul>
) : <div className="text-sm text-ink/50">None recorded.</div>}
</section>
<section className="card p-4 lg:col-span-2">
<h2 className="mb-2 text-lg font-semibold">SWOT analysis</h2>
<SwotPanel itemId={item.id} />
</section>
<section className="card p-4">
<h2 className="mb-2 text-lg font-semibold">News</h2>
<NewsFeed query={item.name} />
</section>
</div>
</div>
);
}
function ScoreBar({ label, v, w }: { label: string; v: number | null; w: number }) {
const pct = v ?? 0;
return (
<div className="mb-2">
<div className="mb-0.5 flex justify-between text-xs">
<span className="text-ink/70">{label} <span className="text-ink/40">(wt {w}%)</span></span>
<span className="font-semibold">{v ?? "—"}</span>
</div>
<div className="h-2 rounded bg-ink/10">
<div className="h-2 rounded bg-brass" style={{ width: `${pct}%` }} />
</div>
</div>
);
}