feat(docs): version-based docs, correct Gitea tag links, form guide help
- each release now carries a full docs snapshot (reference + handbook) under docs/releases/<v>/; the docs frontend loads a version's snapshot for /docs/vX.Y.Z/* and shows the handbook/reference of that version - version dropdown navigates to /docs/<version> (docs home) instead of the release notes page; old /docs/vX.Y.Z redirect removed - generator: --snapshot writes handbook + reference; build copies versioned snapshots (last 7 versions) into /docs/versions/<v>/ - fix Gitea tag links: /admin/tool-evaluator/tags/<v> was 404, correct URL is /releases/tag/<v> - add GuideHelp button to forms/pages linking to the matching handbook guide (rating, tool add/edit, costs, compare, watchlist, analytics)
This commit is contained in:
@@ -0,0 +1,29 @@
|
||||
import { BookOpen } from "lucide-react";
|
||||
import { Link } from "wouter";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
|
||||
export function GuideHelp({
|
||||
guide,
|
||||
label,
|
||||
}: {
|
||||
guide: string;
|
||||
label: string;
|
||||
}) {
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Link
|
||||
href={`/docs/handbook/${guide}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-label={`Help: ${label}`}
|
||||
data-testid={`guide-${guide}`}
|
||||
className="inline-flex shrink-0 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<BookOpen className="h-3.5 w-3.5" />
|
||||
</Link>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{label} — Anleitung in der Dokumentation</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
GetTopToolsMetric
|
||||
} from "@workspace/api-client-react";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import {
|
||||
@@ -48,7 +49,10 @@ export default function Analytics() {
|
||||
<Layout>
|
||||
<div className="space-y-6 pb-10">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">Platform Analytics</h1>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2 flex items-center gap-2">
|
||||
Platform Analytics
|
||||
<GuideHelp guide="analytics" label="Platform Analytics" />
|
||||
</h1>
|
||||
<p className="text-muted-foreground">Macro-level insights into tool performance and community engagement.</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -8,6 +8,7 @@ import { Link, useSearch } from "wouter";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { RatingStars } from "@/components/rating-stars";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
@@ -101,7 +102,10 @@ export default function Compare() {
|
||||
<Layout>
|
||||
<div className="space-y-6 pb-10">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">{t("compare.title")}</h1>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2 flex items-center gap-2">
|
||||
{t("compare.title")}
|
||||
<GuideHelp guide="vergleichen" label={t("compare.title")} />
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("compare.subtitle", { count: list.length })}</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { useEffect, useMemo, useState } from "react";
|
||||
import { useEffect, useMemo, useState, createContext, useContext } from "react";
|
||||
import { Link, useLocation } from "wouter";
|
||||
import { Marked } from "marked";
|
||||
import DOMPurify from "dompurify";
|
||||
@@ -42,6 +42,21 @@ import {
|
||||
const DOCS_BASE = `${import.meta.env.BASE_URL.replace(/\/$/, "")}/docs`;
|
||||
const REPO_URL = "https://git.kubebase.de/admin/tool-evaluator";
|
||||
|
||||
// Active docs version (null = current docs). Shared via context so every
|
||||
// sub-view builds versioned links and file paths.
|
||||
const DocsVersionContext = createContext<string | null>(null);
|
||||
const useDocsVersion = () => useContext(DocsVersionContext);
|
||||
|
||||
// Full in-app URL for a docs path, prefixed with the active version.
|
||||
function docsHref(version: string | null, path: string) {
|
||||
return version === null ? `/docs/${path}` : `/docs/${version}/${path}`;
|
||||
}
|
||||
|
||||
// Static file path under /docs, prefixed with the versioned snapshot dir.
|
||||
function docsFile(version: string | null, relPath: string) {
|
||||
return version === null ? relPath : `versions/${version}/${relPath}`;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
// Types
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -88,6 +103,7 @@ type ReleaseDoc = {
|
||||
title: string;
|
||||
date: string | null;
|
||||
hasReference: boolean;
|
||||
hasHandbook: boolean;
|
||||
};
|
||||
|
||||
type HandbookPage = { slug: string; file: string; title: string; order: number };
|
||||
@@ -230,8 +246,11 @@ function isLinkableType(t: FieldType): boolean {
|
||||
}
|
||||
|
||||
function resolveTypeHref(t: FieldType): string | null {
|
||||
if (t.kind === "ref") return `/docs/reference/schemas/${t.value}`;
|
||||
if (t.kind === "array" && /^[A-Z]/.test(t.value)) return `/docs/reference/schemas/${t.value}`;
|
||||
const version = useDocsVersion();
|
||||
const href = (v: string | null) =>
|
||||
v === null ? `/docs/reference/schemas/${t.value}` : `/docs/${v}/reference/schemas/${t.value}`;
|
||||
if (t.kind === "ref") return href(version);
|
||||
if (t.kind === "array" && /^[A-Z]/.test(t.value)) return href(version);
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -328,14 +347,14 @@ function DocsNav({
|
||||
|
||||
const groups: { label: string; icon: LucideIcon; items: { href: string; label: string; active: boolean }[] }[] = [];
|
||||
|
||||
if (handbook && handbook.length > 0 && version === null) {
|
||||
if (handbook && handbook.length > 0) {
|
||||
groups.push({
|
||||
label: t("docs.guides"),
|
||||
icon: BookOpen,
|
||||
items: handbook.map((p) => ({
|
||||
href: `/docs/handbook/${p.slug}`,
|
||||
href: docsHref(version, `handbook/${p.slug}`),
|
||||
label: p.title,
|
||||
active: navLink(`/docs/handbook/${p.slug}`),
|
||||
active: navLink(docsHref(version, `handbook/${p.slug}`)),
|
||||
})),
|
||||
});
|
||||
}
|
||||
@@ -345,18 +364,18 @@ function DocsNav({
|
||||
label: t("docs.endpoints"),
|
||||
icon: Server,
|
||||
items: reference.tags.map((tag) => ({
|
||||
href: `/docs/reference/endpoints/${tag.name}`,
|
||||
href: docsHref(version, `reference/endpoints/${tag.name}`),
|
||||
label: tag.name,
|
||||
active: navLink(`/docs/reference/endpoints/${tag.name}`),
|
||||
active: navLink(docsHref(version, `reference/endpoints/${tag.name}`)),
|
||||
})),
|
||||
});
|
||||
groups.push({
|
||||
label: t("docs.schemas"),
|
||||
icon: Library,
|
||||
items: reference.schemas.map((s) => ({
|
||||
href: `/docs/reference/schemas/${s.name}`,
|
||||
href: docsHref(version, `reference/schemas/${s.name}`),
|
||||
label: s.name,
|
||||
active: navLink(`/docs/reference/schemas/${s.name}`),
|
||||
active: navLink(docsHref(version, `reference/schemas/${s.name}`)),
|
||||
})),
|
||||
});
|
||||
}
|
||||
@@ -365,9 +384,7 @@ function DocsNav({
|
||||
label: t("docs.releases"),
|
||||
icon: Tag,
|
||||
items: versions.map((v) => ({
|
||||
href: v.version === (version ?? versions[0]?.version) && version !== null
|
||||
? `/docs/releases/${v.version}`
|
||||
: `/docs/releases/${v.version}`,
|
||||
href: `/docs/releases/${v.version}`,
|
||||
label: v.version,
|
||||
active: navLink(`/docs/releases/${v.version}`),
|
||||
})),
|
||||
@@ -708,7 +725,7 @@ function ReleaseNoteView({ version }: { version: string }) {
|
||||
<Tag className="h-4 w-4" />
|
||||
<span className="font-mono">{version}</span>
|
||||
<a
|
||||
href={`${REPO_URL}/tags/${version}`}
|
||||
href={`${REPO_URL}/releases/tag/${version}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
className="inline-flex items-center gap-1 hover:text-foreground"
|
||||
@@ -723,8 +740,9 @@ function ReleaseNoteView({ version }: { version: string }) {
|
||||
}
|
||||
|
||||
function HandbookView({ slug }: { slug: string }) {
|
||||
const version = useDocsVersion();
|
||||
const handbookFile = `${slug}.md`;
|
||||
return <MarkdownView file={`handbook/${handbookFile}`} />;
|
||||
return <MarkdownView file={docsFile(version, `handbook/${handbookFile}`)} />;
|
||||
}
|
||||
|
||||
// ---------------------------------------------------------------------------
|
||||
@@ -803,9 +821,11 @@ export default function Docs() {
|
||||
});
|
||||
|
||||
const { data: releases, error: releasesError } = useJson<ReleaseDoc[]>(`${DOCS_BASE}/index.json`);
|
||||
const { data: handbook, error: handbookError } = useJson<HandbookPage[]>(
|
||||
version === null ? `${DOCS_BASE}/handbook/index.json` : null,
|
||||
);
|
||||
|
||||
const activeRelease = releases?.find((r) => r.version === version) ?? null;
|
||||
const handbookAvailable = version === null || activeRelease?.hasHandbook;
|
||||
const handbookUrl = handbookAvailable ? `${DOCS_BASE}/${docsFile(version, "handbook/index.json")}` : null;
|
||||
const { data: handbook, error: handbookError } = useJson<HandbookPage[]>(handbookUrl);
|
||||
|
||||
const isCurrentVersion =
|
||||
version === null ||
|
||||
@@ -814,7 +834,7 @@ export default function Docs() {
|
||||
|
||||
const refUrl = version === null
|
||||
? `${DOCS_BASE}/reference.json`
|
||||
: releases?.find((r) => r.version === version)?.hasReference
|
||||
: activeRelease?.hasReference
|
||||
? `${DOCS_BASE}/versions/${version}.json`
|
||||
: null;
|
||||
|
||||
@@ -832,20 +852,13 @@ export default function Docs() {
|
||||
return null;
|
||||
}, [releases, versionInfo]);
|
||||
|
||||
// Redirect old-style /docs/vX.Y.Z to /docs/releases/vX.Y.Z
|
||||
useEffect(() => {
|
||||
if (version !== null && path.length === 0) {
|
||||
setLocation(`/docs/releases/${version}`, { replace: true });
|
||||
}
|
||||
}, [version, path, setLocation]);
|
||||
|
||||
const handleVersionChange = (v: string | null) => {
|
||||
setSearchQuery("");
|
||||
if (v === null || v === currentVersion) {
|
||||
setLocation("/docs");
|
||||
return;
|
||||
}
|
||||
setLocation(`/docs/releases/${v}`);
|
||||
setLocation(`/docs/${v}`);
|
||||
};
|
||||
|
||||
// ---- route resolution ----
|
||||
@@ -864,7 +877,12 @@ export default function Docs() {
|
||||
let content: React.ReactNode = null;
|
||||
|
||||
if (version !== null && path.length === 0) {
|
||||
content = <ReleaseNoteView version={version} />;
|
||||
content =
|
||||
handbook && handbook.length > 0 ? (
|
||||
<HandbookView slug={handbook[0].slug} />
|
||||
) : (
|
||||
<ReleaseNoteView version={version} />
|
||||
);
|
||||
} else if (section === "home") {
|
||||
content =
|
||||
handbook && handbook.length > 0 ? (
|
||||
@@ -913,7 +931,7 @@ export default function Docs() {
|
||||
{reference.tags.map((tg) => (
|
||||
<Link
|
||||
key={tg.name}
|
||||
href={`/docs/reference/endpoints/${tg.name}`}
|
||||
href={docsHref(version, `reference/endpoints/${tg.name}`)}
|
||||
className="rounded-lg border p-3 text-sm hover:bg-accent/50"
|
||||
>
|
||||
<span className="font-medium">{tg.name}</span>
|
||||
@@ -930,7 +948,7 @@ export default function Docs() {
|
||||
{reference.schemas.map((s) => (
|
||||
<Link
|
||||
key={s.name}
|
||||
href={`/docs/reference/schemas/${s.name}`}
|
||||
href={docsHref(version, `reference/schemas/${s.name}`)}
|
||||
className="rounded-lg border p-3 text-sm font-mono hover:bg-accent/50"
|
||||
>
|
||||
{s.name}
|
||||
@@ -955,75 +973,77 @@ export default function Docs() {
|
||||
const showSearch = version === null && section !== "releases";
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background flex flex-col">
|
||||
<header className="border-b bg-card shrink-0">
|
||||
<div className="mx-auto max-w-6xl px-4 md:px-6 h-14 flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-1 min-w-0">
|
||||
<Sheet open={navOpen} onOpenChange={setNavOpen}>
|
||||
<SheetTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="lg:hidden -ml-1 shrink-0"
|
||||
title={t("docs.nav")}
|
||||
data-testid="button-docs-nav-mobile"
|
||||
>
|
||||
<Menu className="h-5 w-5" />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="left" className="w-80 overflow-y-auto p-4">
|
||||
<SheetTitle className="sr-only">{t("docs.nav")}</SheetTitle>
|
||||
<DocsVersionContext.Provider value={version}>
|
||||
<div className="min-h-screen bg-background flex flex-col">
|
||||
<header className="border-b bg-card shrink-0">
|
||||
<div className="mx-auto max-w-6xl px-4 md:px-6 h-14 flex items-center justify-between gap-3">
|
||||
<div className="flex items-center gap-1 min-w-0">
|
||||
<Sheet open={navOpen} onOpenChange={setNavOpen}>
|
||||
<SheetTrigger asChild>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="icon"
|
||||
className="lg:hidden -ml-1 shrink-0"
|
||||
title={t("docs.nav")}
|
||||
data-testid="button-docs-nav-mobile"
|
||||
>
|
||||
<Menu className="h-5 w-5" />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="left" className="w-80 overflow-y-auto p-4">
|
||||
<SheetTitle className="sr-only">{t("docs.nav")}</SheetTitle>
|
||||
<DocsNav
|
||||
version={version}
|
||||
handbook={handbook}
|
||||
reference={reference}
|
||||
versions={releases ?? []}
|
||||
/>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center gap-2 text-primary font-bold text-lg min-w-0"
|
||||
data-testid="link-docs-back"
|
||||
>
|
||||
<Wrench className="w-5 h-5 shrink-0" />
|
||||
<span className="truncate">toolr</span>
|
||||
<span className="hidden md:inline-flex items-center gap-1 text-xs font-normal text-muted-foreground border-l pl-2 ml-1">
|
||||
<ArrowLeft className="w-3.5 h-3.5" />
|
||||
{t("docs.backToApp")}
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 shrink-0">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="mx-auto max-w-6xl w-full flex-1 space-y-6 px-4 md:px-6 py-6 pb-12">
|
||||
<DocsHeader
|
||||
versions={releases ?? []}
|
||||
activeVersion={headerVersion}
|
||||
onVersionChange={handleVersionChange}
|
||||
onSearchChange={showSearch ? setSearchQuery : undefined}
|
||||
/>
|
||||
|
||||
{showSearch && <SearchOverlay query={searchQuery} onClose={() => setSearchQuery("")} />}
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-[240px_minmax(0,1fr)] gap-8">
|
||||
<aside className="hidden lg:block">
|
||||
<div className="sticky top-20 max-h-[calc(100vh-6rem)] overflow-auto">
|
||||
<DocsNav
|
||||
version={version}
|
||||
handbook={handbook}
|
||||
reference={reference}
|
||||
versions={releases ?? []}
|
||||
/>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
<Link
|
||||
href="/"
|
||||
className="inline-flex items-center gap-2 text-primary font-bold text-lg min-w-0"
|
||||
data-testid="link-docs-back"
|
||||
>
|
||||
<Wrench className="w-5 h-5 shrink-0" />
|
||||
<span className="truncate">toolr</span>
|
||||
<span className="hidden md:inline-flex items-center gap-1 text-xs font-normal text-muted-foreground border-l pl-2 ml-1">
|
||||
<ArrowLeft className="w-3.5 h-3.5" />
|
||||
{t("docs.backToApp")}
|
||||
</span>
|
||||
</Link>
|
||||
</div>
|
||||
</aside>
|
||||
<div className="min-w-0">{content}</div>
|
||||
</div>
|
||||
<div className="flex items-center gap-1.5 shrink-0">
|
||||
<ThemeToggle />
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="mx-auto max-w-6xl w-full flex-1 space-y-6 px-4 md:px-6 py-6 pb-12">
|
||||
<DocsHeader
|
||||
versions={releases ?? []}
|
||||
activeVersion={headerVersion}
|
||||
onVersionChange={handleVersionChange}
|
||||
onSearchChange={showSearch ? setSearchQuery : undefined}
|
||||
/>
|
||||
|
||||
{showSearch && <SearchOverlay query={searchQuery} onClose={() => setSearchQuery("")} />}
|
||||
|
||||
<div className="grid grid-cols-1 lg:grid-cols-[240px_minmax(0,1fr)] gap-8">
|
||||
<aside className="hidden lg:block">
|
||||
<div className="sticky top-20 max-h-[calc(100vh-6rem)] overflow-auto">
|
||||
<DocsNav
|
||||
version={version}
|
||||
handbook={handbook}
|
||||
reference={reference}
|
||||
versions={releases ?? []}
|
||||
/>
|
||||
</div>
|
||||
</aside>
|
||||
<div className="min-w-0">{content}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</DocsVersionContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -61,6 +61,7 @@ import {
|
||||
import { customFetch } from "@workspace/api-client-react";
|
||||
import { recordRecentTool } from "@/lib/recent-tools";
|
||||
import { FieldHelp } from "@/components/field-help";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
|
||||
const ratingSchema = z.object({
|
||||
usefulness: z.number().min(1).max(5),
|
||||
@@ -615,7 +616,10 @@ export default function ToolDetail() {
|
||||
<Dialog open={costDialogOpen} onOpenChange={(o) => { if (!o) setCostDialogOpen(false); }}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle>{editCost ? t("detail.edit") + " " + t("detail.costs") : t("detail.addCost")}</DialogTitle>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
{editCost ? t("detail.edit") + " " + t("detail.costs") : t("detail.addCost")}
|
||||
<GuideHelp guide="kosten" label={t("detail.costs")} />
|
||||
</DialogTitle>
|
||||
<DialogDescription>Manage license cost information for this tool.</DialogDescription>
|
||||
</DialogHeader>
|
||||
<div className="space-y-4 py-2">
|
||||
@@ -765,7 +769,10 @@ export default function ToolDetail() {
|
||||
{isReviewFormOpen && (
|
||||
<Card className="border-primary shadow-sm">
|
||||
<CardHeader>
|
||||
<CardTitle>{t("detail.addReview")}</CardTitle>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
{t("detail.addReview")}
|
||||
<GuideHelp guide="bewerten" label={t("detail.addReview")} />
|
||||
</CardTitle>
|
||||
<CardDescription>Share your experience with {tool.name}</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
|
||||
@@ -31,6 +31,7 @@ import { FeatureInput } from "@/components/feature-input";
|
||||
import { TagInput } from "@/components/tag-input";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { FieldHelp } from "@/components/field-help";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
|
||||
const toolSchema = z.object({
|
||||
name: z.string().min(2, "Name must be at least 2 characters"),
|
||||
@@ -160,6 +161,7 @@ export default function ToolEdit() {
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Pencil className="w-5 h-5 text-primary" />
|
||||
Tool Details
|
||||
<GuideHelp guide="tool-bearbeiten" label="Tool Details" />
|
||||
</CardTitle>
|
||||
<CardDescription>Modify the tool information below.</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
@@ -19,6 +19,7 @@ import { FeatureInput } from "@/components/feature-input";
|
||||
import { TagInput } from "@/components/tag-input";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { FieldHelp } from "@/components/field-help";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
|
||||
const toolSchema = z.object({
|
||||
name: z.string().min(2, "Name must be at least 2 characters"),
|
||||
@@ -123,6 +124,7 @@ export default function ToolNew() {
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Wrench className="w-5 h-5 text-primary" />
|
||||
Tool Details
|
||||
<GuideHelp guide="tool-anlegen" label="Tool Details" />
|
||||
</CardTitle>
|
||||
<CardDescription>Provide the basic information about the tool.</CardDescription>
|
||||
</CardHeader>
|
||||
|
||||
@@ -6,6 +6,7 @@ import { useAuth } from "@/hooks/use-auth";
|
||||
import { useWatchlist } from "@/hooks/use-watchlist";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { ToolCard } from "@/components/tool-card";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { ShieldAlert, Bookmark } from "lucide-react";
|
||||
@@ -42,7 +43,10 @@ export default function Watchlist() {
|
||||
<Layout>
|
||||
<div className="space-y-6 pb-10">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">{t("watchlist.title")}</h1>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2 flex items-center gap-2">
|
||||
{t("watchlist.title")}
|
||||
<GuideHelp guide="watchlist" label={t("watchlist.title")} />
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("watchlist.subtitle")}</p>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user