diff --git a/artifacts/toolrate/src/pages/docs.tsx b/artifacts/toolrate/src/pages/docs.tsx
index 4c622e2..1f446d1 100644
--- a/artifacts/toolrate/src/pages/docs.tsx
+++ b/artifacts/toolrate/src/pages/docs.tsx
@@ -8,6 +8,7 @@ import { Badge } from "@/components/ui/badge";
import { Input } from "@/components/ui/input";
import { Button } from "@/components/ui/button";
import { ThemeToggle } from "@/components/theme-toggle";
+import { LanguageSwitcher } from "@/components/language-switcher";
import {
Select,
SelectContent,
@@ -57,6 +58,20 @@ function docsFile(version: string | null, relPath: string) {
return version === null ? relPath : `versions/${version}/${relPath}`;
}
+// Prefer the English file/title variant when the active UI language is English.
+function useDocsLocale() {
+ const { i18n } = useTranslation();
+ return (i18n.language ?? "en").toLowerCase().startsWith("en") ? "en" : "de";
+}
+
+function localizedFile(file: string, fileEn: string | null, locale: "en" | "de"): string {
+ return locale === "en" && fileEn ? fileEn : file;
+}
+
+function localizedTitle(title: string, titleEn: string | null, locale: "en" | "de"): string {
+ return locale === "en" && titleEn ? titleEn : title;
+}
+
// ---------------------------------------------------------------------------
// Types
// ---------------------------------------------------------------------------
@@ -100,13 +115,22 @@ type Reference = { tags: TagGroup[]; schemas: SchemaModel[] };
type ReleaseDoc = {
version: string;
file: string;
+ fileEn: string | null;
title: string;
+ titleEn: string | null;
date: string | null;
hasReference: boolean;
hasHandbook: boolean;
};
-type HandbookPage = { slug: string; file: string; title: string; order: number };
+type HandbookPage = {
+ slug: string;
+ file: string;
+ fileEn: string | null;
+ title: string;
+ titleEn: string | null;
+ order: number;
+};
type SearchEntry = { title: string; href: string; kind: string; text: string };
@@ -339,6 +363,7 @@ function DocsNav({
}) {
const [location] = useLocation();
const { t } = useTranslation();
+ const locale = useDocsLocale();
const navLink = (href: string) => {
const active = location === href || (href !== "/docs" && location.startsWith(href));
@@ -353,7 +378,7 @@ function DocsNav({
icon: BookOpen,
items: handbook.map((p) => ({
href: docsHref(version, `handbook/${p.slug}`),
- label: p.title,
+ label: localizedTitle(p.title, p.titleEn, locale),
active: navLink(docsHref(version, `handbook/${p.slug}`)),
})),
});
@@ -536,7 +561,7 @@ function FieldTable({ fields }: { fields: Field[] }) {
{f.required ? (
- required
+ {t("docs.required")}
) : (
–
)}
@@ -611,8 +636,8 @@ function EndpointTagView({ tag }: { tag: TagGroup }) {
- Name
- In
+ {t("docs.name")}
+ {t("docs.in")}
{t("docs.type")}
{t("docs.required")}
{t("docs.description")}
@@ -625,7 +650,7 @@ function EndpointTagView({ tag }: { tag: TagGroup }) {
{p.in}
- {p.required ? req : "–"}
+ {p.required ? {t("docs.required")} : "–"}
{p.description}
@@ -642,7 +667,7 @@ function EndpointTagView({ tag }: { tag: TagGroup }) {
{ep.requestBody && (
- Request Body {ep.requestBody.required && required }
+ {t("docs.requestBody")} {ep.requestBody.required && {t("docs.required")} }
@@ -679,6 +704,7 @@ function EndpointTagView({ tag }: { tag: TagGroup }) {
function ReleasesView({ versions }: { versions: ReleaseDoc[] }) {
const [, setLocation] = useLocation();
const { t } = useTranslation();
+ const locale = useDocsLocale();
return (
@@ -697,7 +723,7 @@ function ReleasesView({ versions }: { versions: ReleaseDoc[] }) {
- {v.title}
+ {localizedTitle(v.title, v.titleEn, locale)}
{v.version}
{v.date && (
@@ -707,7 +733,7 @@ function ReleasesView({ versions }: { versions: ReleaseDoc[] }) {
)}
- {v.hasReference &&
API-Referenz }
+ {v.hasReference &&
{t("docs.reference")} }
))}
@@ -717,7 +743,9 @@ function ReleasesView({ versions }: { versions: ReleaseDoc[] }) {
);
}
-function ReleaseNoteView({ version }: { version: string }) {
+function ReleaseNoteView({ version, doc }: { version: string; doc: ReleaseDoc | null }) {
+ const locale = useDocsLocale();
+ const file = doc ? `releases/${localizedFile(doc.file, doc.fileEn, locale)}` : `releases/${version}.md`;
return (
@@ -733,16 +761,20 @@ function ReleaseNoteView({ version }: { version: string }) {
-
+
);
}
-function HandbookView({ slug }: { slug: string }) {
+function HandbookView({ slug, pages }: { slug: string; pages: HandbookPage[] | null }) {
const version = useDocsVersion();
- const handbookFile = `${slug}.md`;
- return ;
+ const locale = useDocsLocale();
+ const page = pages?.find((p) => p.slug === slug);
+ const file = page
+ ? docsFile(version, `handbook/${localizedFile(page.file, page.fileEn, locale)}`)
+ : docsFile(version, `handbook/${slug}.md`);
+ return ;
}
// ---------------------------------------------------------------------------
@@ -750,7 +782,9 @@ function HandbookView({ slug }: { slug: string }) {
// ---------------------------------------------------------------------------
function useDocsSearch(query: string) {
- const { data, error } = useJson(query ? `${DOCS_BASE}/search.json` : null);
+ const locale = useDocsLocale();
+ const indexFile = locale === "en" ? "search.en.json" : "search.json";
+ const { data, error } = useJson(query ? `${DOCS_BASE}/${indexFile}` : null);
const results = useMemo(() => {
if (!query.trim() || !data) return [];
const q = query.trim().toLowerCase();
@@ -879,21 +913,21 @@ export default function Docs() {
if (version !== null && path.length === 0) {
content =
handbook && handbook.length > 0 ? (
-
+
) : (
-
+
);
} else if (section === "home") {
content =
handbook && handbook.length > 0 ? (
-
+
) : releases && releases.length > 0 ? (
) : (
{t("docs.noDocs")}
);
} else if (section === "handbook" && param) {
- content = ;
+ content = ;
} else if (section === "reference" && param === "endpoints" && path[2]) {
const tag = reference?.tags.find(
(tg) => tg.name.toLowerCase() === path[2].toLowerCase(),
@@ -963,7 +997,7 @@ export default function Docs() {
);
} else if (section === "releases" && param) {
- content = ;
+ content = r.version === param) ?? null} />;
} else if (section === "releases") {
content = releases ? : ;
} else {
@@ -1014,6 +1048,7 @@ export default function Docs() {
+
diff --git a/artifacts/toolrate/src/pages/redundancy.tsx b/artifacts/toolrate/src/pages/redundancy.tsx
index 5beb3ef..14b067a 100644
--- a/artifacts/toolrate/src/pages/redundancy.tsx
+++ b/artifacts/toolrate/src/pages/redundancy.tsx
@@ -1,4 +1,5 @@
import { useState, useEffect } from "react";
+import { useTranslation } from "react-i18next";
import { Layout } from "@/components/layout";
import { Card, CardContent } from "@/components/ui/card";
import { Badge } from "@/components/ui/badge";
@@ -11,6 +12,7 @@ import { customFetch } from "@workspace/api-client-react";
import { useToast } from "@/hooks/use-toast";
export default function RedundancyPage() {
+ const { t } = useTranslation();
const [data, setData] = useState(null);
const [loading, setLoading] = useState(true);
const { toast } = useToast();
@@ -28,10 +30,10 @@ export default function RedundancyPage() {
method: "POST",
body: JSON.stringify({ toolId, relatedToolId, betterToolId }),
});
- toast({ title: "Evaluation saved" });
+ toast({ title: t("redundancy.toastEvalSaved") });
setData(await customFetch("/api/admin/redundancy"));
} catch {
- toast({ title: "Failed to save evaluation", variant: "destructive" });
+ toast({ title: t("redundancy.toastEvalFailed"), variant: "destructive" });
}
}
@@ -44,10 +46,10 @@ export default function RedundancyPage() {
-
Tool Analysis & Recommendations
+
{t("redundancy.title")}
- Automatic redundancy detection with cost and rating comparison. Admin can manually confirm which tool is the better choice.
+ {t("redundancy.subtitle")}
{loading ? (
@@ -61,12 +63,12 @@ export default function RedundancyPage() {
{group.category}
-
{group.tools.length} tools, {group.pairs.length} comparisons
+
{t("redundancy.toolsComparisons", { tools: group.tools.length, pairs: group.pairs.length })}
{group.totalMonthlyCost > 0 && (
- {group.totalMonthlyCost.toFixed(2)}/mo total
+ {group.totalMonthlyCost.toFixed(2)}{t("redundancy.totalMonthly")}
)}
@@ -82,12 +84,12 @@ export default function RedundancyPage() {
{tool.name}
{tool.costs?.length > 0 && tool.totalMonthly > 0 && (
- {tool.totalMonthly.toFixed(2)}/mo
+ {tool.totalMonthly.toFixed(2)}{t("redundancy.perMonth")}
)}
- {tool.ratingCount} reviews
+ {tool.ratingCount} {t("redundancy.reviews")}
{tool.avgCombined != null && (
<>
·
@@ -104,7 +106,7 @@ export default function RedundancyPage() {
))}
- {tool.features.length} features
+ {tool.features.length} {t("redundancy.features")}
@@ -114,7 +116,7 @@ export default function RedundancyPage() {
{group.pairs.length > 0 && (
-
Comparisons & Recommendations
+ {t("redundancy.comparisonsTitle")}
{group.pairs.map((pair: any, i: number) => (
@@ -126,11 +128,11 @@ export default function RedundancyPage() {
{pair.a.avgCombined != null ? pair.a.avgCombined.toFixed(1) : "N/A"} ★
- {pair.a.totalMonthly > 0 ? ` · ${pair.a.totalMonthly.toFixed(2)}/mo` : ""}
+ {pair.a.totalMonthly > 0 ? ` · ${pair.a.totalMonthly.toFixed(2)}${t("redundancy.perMonth")}` : ""}
-
vs
+
{t("redundancy.vs")}
{pair.overlap}%
@@ -142,7 +144,7 @@ export default function RedundancyPage() {
{pair.b.avgCombined != null ? pair.b.avgCombined.toFixed(1) : "N/A"} ★
- {pair.b.totalMonthly > 0 ? ` · ${pair.b.totalMonthly.toFixed(2)}/mo` : ""}
+ {pair.b.totalMonthly > 0 ? ` · ${pair.b.totalMonthly.toFixed(2)}${t("redundancy.perMonth")}` : ""}
@@ -188,7 +190,7 @@ export default function RedundancyPage() {
))}
) : (
- No tools found.
+ {t("redundancy.noTools")}
)}
diff --git a/artifacts/toolrate/src/pages/tool-detail.tsx b/artifacts/toolrate/src/pages/tool-detail.tsx
index f3448b5..6567d1d 100644
--- a/artifacts/toolrate/src/pages/tool-detail.tsx
+++ b/artifacts/toolrate/src/pages/tool-detail.tsx
@@ -121,24 +121,24 @@ export default function ToolDetail() {
method: "POST",
body: JSON.stringify({ relatedToolId: Number(linkToolId), relationType: linkType, notes: linkNotes || undefined }),
});
- toast({ title: "Relation created" });
+ toast({ title: t("detail.toastRelationCreated") });
setLinkDialogOpen(false);
setLinkToolId("");
setLinkNotes("");
setLinkType("similar");
setSimilarData(await customFetch(`/api/tools/${id}/similar`));
} catch (err: any) {
- toast({ title: "Failed to create relation", description: err.data?.error ?? err.message, variant: "destructive" });
+ toast({ title: t("detail.toastRelationFailed"), description: err.data?.error ?? err.message, variant: "destructive" });
}
}
async function handleDeleteRelation(relationId: number) {
try {
await customFetch(`/api/tools/relations/${relationId}`, { method: "DELETE" });
- toast({ title: "Relation deleted" });
+ toast({ title: t("detail.toastRelationDeleted") });
setSimilarData(await customFetch(`/api/tools/${id}/similar`));
} catch {
- toast({ title: "Failed to delete relation", variant: "destructive" });
+ toast({ title: t("detail.toastRelationDeleteFailed"), variant: "destructive" });
}
}
@@ -163,12 +163,12 @@ export default function ToolDetail() {
if (costAmount) body.cost = costAmount;
try {
await customFetch(url, { method, body: JSON.stringify(body) });
- toast({ title: editCost ? "Cost updated" : "Cost added" });
+ toast({ title: editCost ? t("detail.toastCostUpdated") : t("detail.toastCostAdded") });
setCostDialogOpen(false);
resetCostForm();
setCosts(await customFetch(`/api/tools/${id}/costs`));
} catch (err: any) {
- toast({ title: "Failed to save cost", description: err.data?.error ?? err.message, variant: "destructive" });
+ toast({ title: t("detail.toastCostFailed"), description: err.data?.error ?? err.message, variant: "destructive" });
}
}
@@ -194,10 +194,10 @@ export default function ToolDetail() {
async function handleDeleteCost(costId: number) {
try {
await customFetch(`/api/costs/${costId}`, { method: "DELETE" });
- toast({ title: "Cost deleted" });
+ toast({ title: t("detail.toastCostDeleted") });
setCosts(await customFetch(`/api/tools/${id}/costs`));
} catch {
- toast({ title: "Failed to delete cost", variant: "destructive" });
+ toast({ title: t("detail.toastCostDeleteFailed"), variant: "destructive" });
}
}
@@ -237,8 +237,8 @@ export default function ToolDetail() {
createRating.mutate({ id, data }, {
onSuccess: () => {
toast({
- title: "Rating submitted",
- description: "Thank you for your feedback!",
+ title: t("detail.toastRatingSubmitted"),
+ description: t("detail.toastRatingThanks"),
});
setIsReviewFormOpen(false);
form.reset();
@@ -252,8 +252,8 @@ export default function ToolDetail() {
},
onError: (error) => {
toast({
- title: "Failed to submit rating",
- description: error.data?.error || error.message || "An unexpected error occurred.",
+ title: t("detail.toastRatingFailed"),
+ description: error.data?.error || error.message || t("detail.unexpectedError"),
variant: "destructive"
});
}
@@ -264,9 +264,9 @@ export default function ToolDetail() {
return (
-
Invalid Tool ID
+
{t("detail.invalidToolId")}
- Back to tools
+ {t("detail.backToBrowse")}
@@ -288,7 +288,7 @@ export default function ToolDetail() {
{ id },
{
onSuccess: () => {
- toast({ title: "Tool deleted" });
+ toast({ title: t("detail.toastToolDeleted") });
queryClient.invalidateQueries({ queryKey: getListToolsQueryKey() });
queryClient.invalidateQueries({ queryKey: getListCategoriesQueryKey() });
queryClient.invalidateQueries({ queryKey: getListAllFeaturesQueryKey() });
@@ -297,7 +297,7 @@ export default function ToolDetail() {
setLocation("/tools");
},
onError: (err) => {
- toast({ title: "Failed to delete", description: err.data?.error || err.message, variant: "destructive" });
+ toast({ title: t("detail.toastDeleteFailed"), description: err.data?.error || err.message, variant: "destructive" });
setDeleteOpen(false);
},
},
@@ -309,7 +309,7 @@ export default function ToolDetail() {
- Back to browse
+ {t("detail.backToBrowse")}
{/* Header Section */}
@@ -408,7 +408,7 @@ export default function ToolDetail() {
{tool.features && tool.features.length > 0 && (
-
Key Features
+
{t("detail.keyFeatures")}
{tool.features.map((feature, i) => (
@@ -423,17 +423,17 @@ export default function ToolDetail() {
)}
) : (
-
Tool not found.
+
{t("detail.toolNotFound")}
)}
{/* Similar Tools Section */}
{tool && (
-
Similar Tools
+ {t("detail.similarTools")}
{isAdmin && (
setLinkDialogOpen(true)} className="gap-2">
- Link Tool
+ {t("detail.linkTool")}
)}
@@ -501,7 +501,7 @@ export default function ToolDetail() {
>
)}
·
-
Score: {item.score}
+
{t("detail.score")}: {item.score}
@@ -510,7 +510,7 @@ export default function ToolDetail() {
))}
) : similarData && similarData.manual.length === 0 && similarData.auto.length === 0 ? (
- No similar tools found.
+ {t("detail.noSimilarTools")}
) : null}
)}
@@ -519,44 +519,44 @@ export default function ToolDetail() {
- Link Similar Tool
- Manually link this tool to another tool.
+ {t("detail.linkSimilarTool")}
+ {t("detail.linkSimilarToolSub")}
- Tool ID
+ {t("detail.toolId")}
setLinkToolId(e.target.value)}
/>
- Relation Type
+ {t("detail.relationType")}
- Similar
- Replaces
- Superseded By
+ {t("detail.relationSimilar")}
+ {t("detail.relationReplaces")}
+ {t("detail.relationSupersededBy")}
- Notes (Optional)
+ {t("detail.notesOptional")}
- setLinkDialogOpen(false)}>Cancel
- Create Link
+ setLinkDialogOpen(false)}>{t("common.cancel")}
+ {t("detail.createLink")}
@@ -587,7 +587,7 @@ export default function ToolDetail() {
{c.billingPeriod && {c.billingPeriod} }
- {c.cost != null ? `${c.cost} ${c.currency ?? ""}` : "Free"}
+ {c.cost != null ? `${c.cost} ${c.currency ?? ""}` : t("detail.licenseFree")}
{c.notes && {c.notes}
}
@@ -607,7 +607,7 @@ export default function ToolDetail() {
))}
) : (
- No cost information added yet.
+ {t("detail.noCostInfo")}
)}
)}
@@ -620,41 +620,41 @@ export default function ToolDetail() {
{editCost ? t("detail.edit") + " " + t("detail.costs") : t("detail.addCost")}
- Manage license cost information for this tool.
+ {t("detail.costDialogSub")}
- License Type
+ {t("detail.licenseType")}
- Free
- Subscription
- One-Time
- Usage-Based
+ {t("detail.licenseFree")}
+ {t("detail.licenseSubscription")}
+ {t("detail.licenseOneTime")}
+ {t("detail.licenseUsageBased")}
{costLicenseType === "subscription" && (
- Billing Period
+ {t("detail.billingPeriod")}
- Monthly
- Quarterly
- Yearly
+ {t("detail.billingMonthly")}
+ {t("detail.billingQuarterly")}
+ {t("detail.billingYearly")}
)}
- Notes
-
- setCostDialogOpen(false)}>Cancel
- Save
+ setCostDialogOpen(false)}>{t("common.cancel")}
+ {t("common.save")}
@@ -744,9 +744,9 @@ export default function ToolDetail() {
(payload?.[0] ? format(new Date(payload[0].payload.date), "dd.MM.yyyy HH:mm") : "")} />
-
-
-
+
+
+
@@ -773,7 +773,7 @@ export default function ToolDetail() {
{t("detail.addReview")}
- Share your experience with {tool.name}
+ {t("detail.shareExperience", { name: tool.name })}
@@ -829,12 +829,12 @@ export default function ToolDetail() {
render={({ field }) => (
- Comment (Optional)
- Comment
+ {t("detail.commentOptional")}
+ {t("detail.commentLabel")}
@@ -850,11 +850,11 @@ export default function ToolDetail() {
render={({ field }) => (
- Name (Optional)
- Name
+ {t("detail.nameOptional")}
+ {t("detail.nameLabel")}
-
+
@@ -868,7 +868,7 @@ export default function ToolDetail() {
onClick={() => setIsReviewFormOpen(false)}
disabled={createRating.isPending}
>
- Cancel
+ {t("common.cancel")}
{createRating.isPending ? t("common.loading") : t("detail.submit")}
@@ -891,7 +891,7 @@ export default function ToolDetail() {
-
{rating.reviewerName || "Anonymous Engineer"}
+
{rating.reviewerName || t("detail.anonymousEngineer")}
{format(new Date(rating.createdAt), "MMM d, yyyy")}
@@ -921,7 +921,7 @@ export default function ToolDetail() {
{t("detail.noReviews")}
-
Be the first to share your thoughts on this tool.
+
{t("detail.beFirstToReview")}
)}
diff --git a/artifacts/toolrate/src/pages/tool-edit.tsx b/artifacts/toolrate/src/pages/tool-edit.tsx
index 1069298..400d5ee 100644
--- a/artifacts/toolrate/src/pages/tool-edit.tsx
+++ b/artifacts/toolrate/src/pages/tool-edit.tsx
@@ -32,28 +32,34 @@ import { TagInput } from "@/components/tag-input";
import { useAuth } from "@/hooks/use-auth";
import { FieldHelp } from "@/components/field-help";
import { GuideHelp } from "@/components/guide-help";
+import { useTranslation } from "react-i18next";
-const toolSchema = z.object({
- name: z.string().min(2, "Name must be at least 2 characters"),
- description: z.string().min(10, "Description must be at least 10 characters"),
- category: z.string().min(2, "Category is required"),
- websiteUrl: z.string().url("Must be a valid URL").optional().or(z.literal("")),
- iconUrl: z.string().url("Must be a valid URL").optional().or(z.literal("")),
- features: z.array(z.object({ value: z.string() })).optional(),
- tags: z.array(z.object({ value: z.string() })).optional(),
-});
+type ToolFormValues = z.infer
>;
-type ToolFormValues = z.infer;
+function buildToolSchema(t: (key: string) => string) {
+ return z.object({
+ name: z.string().min(2, t("toolForm.nameMin")),
+ description: z.string().min(10, t("toolForm.descriptionMin")),
+ category: z.string().min(2, t("toolForm.categoryRequired")),
+ websiteUrl: z.string().url(t("toolForm.invalidUrl")).optional().or(z.literal("")),
+ iconUrl: z.string().url(t("toolForm.invalidUrl")).optional().or(z.literal("")),
+ features: z.array(z.object({ value: z.string() })).optional(),
+ tags: z.array(z.object({ value: z.string() })).optional(),
+ });
+}
export default function ToolEdit() {
const [match, params] = useRoute("/tools/:id/edit");
const [, setLocation] = useLocation();
const id = parseInt(params?.id || "0", 10);
+ const { t } = useTranslation();
const { toast } = useToast();
const queryClient = useQueryClient();
const updateTool = useUpdateTool();
const { isAuthenticated, isAdmin } = useAuth();
+ const toolSchema = buildToolSchema(t);
+
const { data: tool, isLoading } = useGetTool(id, {
query: { enabled: !!id, queryKey: getGetToolQueryKey(id) },
});
@@ -108,7 +114,7 @@ export default function ToolEdit() {
{ id, data: payload },
{
onSuccess: () => {
- toast({ title: "Tool updated", description: "Changes saved successfully." });
+ toast({ title: t("toolForm.toastUpdated"), description: t("toolForm.toastUpdatedSub") });
queryClient.invalidateQueries({ queryKey: getGetToolQueryKey(id) });
queryClient.invalidateQueries({ queryKey: getListToolsQueryKey() });
queryClient.invalidateQueries({ queryKey: getListCategoriesQueryKey() });
@@ -120,8 +126,8 @@ export default function ToolEdit() {
},
onError: (err) => {
toast({
- title: "Failed to update tool",
- description: err.data?.error || err.message || "An unexpected error occurred.",
+ title: t("toolForm.toastUpdateFailed"),
+ description: err.data?.error || err.message || t("detail.unexpectedError"),
variant: "destructive",
});
},
@@ -138,13 +144,13 @@ export default function ToolEdit() {
- Back to tool
+ {t("toolForm.backToTool")}
-
Edit Tool
-
Update tool details and metadata.
+
{t("toolForm.editTitle")}
+
{t("toolForm.editSubtitle")}
{isLoading ? (
@@ -160,10 +166,10 @@ export default function ToolEdit() {
- Tool Details
-
+ {t("toolForm.toolDetails")}
+
- Modify the tool information below.
+ {t("toolForm.toolDetailsEditSub")}
@@ -175,11 +181,11 @@ export default function ToolEdit() {
render={({ field }) => (
- Name
- Name
+ {t("toolForm.name")}
+ {t("toolForm.name")}
-
+
@@ -191,8 +197,8 @@ export default function ToolEdit() {
render={({ field }) => (
- Category
- Category
+ {t("toolForm.category")}
+ {t("toolForm.category")}
@@ -209,11 +215,11 @@ export default function ToolEdit() {
render={({ field }) => (
- Website URL (Optional)
- Website URL
+ {t("toolForm.websiteUrlOptional")}
+ {t("toolForm.websiteUrl")}
-
+
@@ -226,8 +232,8 @@ export default function ToolEdit() {
render={({ field }) => (
- Icon / Logo URL (Optional)
- Icon / Logo URL
+ {t("toolForm.iconUrlOptional")}
+ {t("toolForm.iconUrl")}
@@ -235,16 +241,16 @@ export default function ToolEdit() {
{field.value ? (
{ (e.target as HTMLImageElement).style.display = "none"; }}
/>
) : (
-
img
+
{t("toolForm.name").charAt(0)}
)}
(
- Description
- Description
+ {t("toolForm.description")}
+ {t("toolForm.description")}
@@ -281,13 +287,13 @@ export default function ToolEdit() {
- Features
- Features
+ {t("toolForm.features")}
+ {t("toolForm.features")}
-
Key capabilities of this tool. Existing features from other tools are selectable.
+
{t("toolForm.featuresEditSub")}
appendFeature({ value: "" })}>
- Add Feature
+ {t("toolForm.addFeature")}
@@ -302,7 +308,7 @@ export default function ToolEdit() {
))}
{featureFields.length === 0 && (
-
No features added.
+
{t("toolForm.noFeatures")}
)}
@@ -328,13 +334,13 @@ export default function ToolEdit() {
- Tags
- Tags
+ {t("toolForm.tags")}
+ {t("toolForm.tags")}
-
Keywords for this tool. Existing tags from other tools are selectable.
+
{t("toolForm.tagsEditSub")}
appendTag({ value: "" })}>
- Add Tag
+ {t("toolForm.addTag")}
@@ -347,7 +353,7 @@ export default function ToolEdit() {
- Cancel
+ {t("common.cancel")}
- {updateTool.isPending ? "Saving…" : "Save Changes"}
+ {updateTool.isPending ? t("toolForm.saving") : t("toolForm.saveChanges")}
diff --git a/artifacts/toolrate/src/pages/tool-new.tsx b/artifacts/toolrate/src/pages/tool-new.tsx
index 65c15cd..4fb4623 100644
--- a/artifacts/toolrate/src/pages/tool-new.tsx
+++ b/artifacts/toolrate/src/pages/tool-new.tsx
@@ -20,26 +20,32 @@ import { TagInput } from "@/components/tag-input";
import { useAuth } from "@/hooks/use-auth";
import { FieldHelp } from "@/components/field-help";
import { GuideHelp } from "@/components/guide-help";
+import { useTranslation } from "react-i18next";
-const toolSchema = z.object({
- name: z.string().min(2, "Name must be at least 2 characters"),
- description: z.string().min(10, "Description must be at least 10 characters"),
- category: z.string().min(2, "Category is required"),
- websiteUrl: z.string().url("Must be a valid URL").optional().or(z.literal("")),
- iconUrl: z.string().url("Must be a valid URL").optional().or(z.literal("")),
- features: z.array(z.object({ value: z.string() })).optional(),
- tags: z.array(z.object({ value: z.string() })).optional(),
-});
+type ToolFormValues = z.infer>;
-type ToolFormValues = z.infer;
+function buildToolSchema(t: (key: string) => string) {
+ return z.object({
+ name: z.string().min(2, t("toolForm.nameMin")),
+ description: z.string().min(10, t("toolForm.descriptionMin")),
+ category: z.string().min(2, t("toolForm.categoryRequired")),
+ websiteUrl: z.string().url(t("toolForm.invalidUrl")).optional().or(z.literal("")),
+ iconUrl: z.string().url(t("toolForm.invalidUrl")).optional().or(z.literal("")),
+ features: z.array(z.object({ value: z.string() })).optional(),
+ tags: z.array(z.object({ value: z.string() })).optional(),
+ });
+}
export default function ToolNew() {
const [location, setLocation] = useLocation();
+ const { t } = useTranslation();
const { toast } = useToast();
const queryClient = useQueryClient();
const createTool = useCreateTool();
const { isAuthenticated, isLoading: authLoading, login } = useAuth();
+ const toolSchema = buildToolSchema(t);
+
const form = useForm({
resolver: zodResolver(toolSchema),
defaultValues: {
@@ -76,7 +82,7 @@ export default function ToolNew() {
{ data: payload },
{
onSuccess: (newTool) => {
- toast({ title: "Tool added successfully", description: "Your tool is now available for review." });
+ toast({ title: t("toolForm.toastAdded"), description: t("toolForm.toastAddedSub") });
queryClient.invalidateQueries({ queryKey: getListToolsQueryKey() });
queryClient.invalidateQueries({ queryKey: getListCategoriesQueryKey() });
queryClient.invalidateQueries({ queryKey: getListAllFeaturesQueryKey() });
@@ -86,7 +92,7 @@ export default function ToolNew() {
setLocation(`/tools/${newTool.id}`);
},
onError: () => {
- toast({ title: "Failed to add tool", description: "An unexpected error occurred.", variant: "destructive" });
+ toast({ title: t("toolForm.toastAddFailed"), description: t("detail.unexpectedError"), variant: "destructive" });
},
},
);
@@ -97,24 +103,24 @@ export default function ToolNew() {
- Back to browse
+ {t("toolForm.backToBrowse")}
-
Add a New Tool
-
Submit a tool you use to let the community rate and review it.
+
{t("toolForm.addTitle")}
+
{t("toolForm.addSubtitle")}
{!authLoading && !isAuthenticated && (
-
Sign in required
-
You must be signed in to submit a tool.
+
{t("toolForm.signInRequired")}
+
{t("toolForm.signInRequiredSub")}
login(location)} data-testid="button-login-prompt">
- Sign in
+ {t("auth.signIn")}
)}
@@ -123,10 +129,10 @@ export default function ToolNew() {
- Tool Details
-
+ {t("toolForm.toolDetails")}
+
- Provide the basic information about the tool.
+ {t("toolForm.toolDetailsNewSub")}
@@ -138,11 +144,11 @@ export default function ToolNew() {
render={({ field }) => (
- Name
- Name
+ {t("toolForm.name")}
+ {t("toolForm.name")}
-
+
@@ -155,8 +161,8 @@ export default function ToolNew() {
render={({ field }) => (
- Category
- Category
+ {t("toolForm.category")}
+ {t("toolForm.category")}
(
- Website URL (Optional)
- Website URL
+ {t("toolForm.websiteUrlOptional")}
+ {t("toolForm.websiteUrl")}
-
+
@@ -193,8 +199,8 @@ export default function ToolNew() {
render={({ field }) => (
- Icon / Logo URL (Optional)
- Icon / Logo URL
+ {t("toolForm.iconUrlOptional")}
+ {t("toolForm.iconUrl")}
@@ -202,16 +208,16 @@ export default function ToolNew() {
{field.value ? (
{ (e.target as HTMLImageElement).style.display = "none"; }}
/>
) : (
-
img
+
{t("toolForm.name").charAt(0)}
)}
(
- Description
- Description
+ {t("toolForm.description")}
+ {t("toolForm.description")}
- Features
- Features
+ {t("toolForm.features")}
+ {t("toolForm.features")}
-
List key capabilities. Existing features from other tools are selectable.
+
{t("toolForm.featuresNewSub")}
appendFeature({ value: "" })}
data-testid="button-add-feature"
>
- Add Feature
+ {t("toolForm.addFeature")}
@@ -278,7 +284,7 @@ export default function ToolNew() {
@@ -297,7 +303,7 @@ export default function ToolNew() {
/>
))}
{featureFields.length === 0 && (
- No features added.
+ {t("toolForm.noFeatures")}
)}
@@ -306,10 +312,10 @@ export default function ToolNew() {
- Tags
- Tags
+ {t("toolForm.tags")}
+ {t("toolForm.tags")}
-
Keywords to help find this tool. Existing tags from other tools are selectable.
+
{t("toolForm.tagsHelp")}
appendTag({ value: "" })}
data-testid="button-add-tag"
>
- Add Tag
+ {t("toolForm.addTag")}
@@ -332,7 +338,7 @@ export default function ToolNew() {
setFeatures(features.filter((x) => x !== f))}
className="rounded-sm hover:bg-muted p-0.5"
- aria-label={`Remove feature ${f}`}
+ aria-label={t("filter.removeFeature", { feature: f })}
>
@@ -374,14 +374,14 @@ export default function ToolsBrowse() {
setMinRating(null)}
className="rounded-sm hover:bg-muted p-0.5"
- aria-label="Remove min rating"
+ aria-label={t("filter.removeMinRating")}
>
)}
- Clear all
+ {t("common.clearAll")}
)}
@@ -521,15 +521,15 @@ export default function ToolsBrowse() {
- Compare is a Premium feature
+ {t("browse.comparePremiumTitle")}
- Comparing tools side-by-side is available to Premium and Enterprise users. Upgrade your plan to unlock it.
+ {t("browse.comparePremiumSub")}
- Not now
+ {t("browse.notNow")}
- Upgrade to Premium
+ {t("browse.upgradePremium")}
diff --git a/artifacts/toolrate/src/pages/trash.tsx b/artifacts/toolrate/src/pages/trash.tsx
index 3cb887b..a728df6 100644
--- a/artifacts/toolrate/src/pages/trash.tsx
+++ b/artifacts/toolrate/src/pages/trash.tsx
@@ -11,7 +11,6 @@ import {
getListAllTagsQueryKey,
getGetTopToolsQueryKey,
getGetAnalyticsSummaryQueryKey,
- type Tool,
} from "@workspace/api-client-react";
import { useQueryClient } from "@tanstack/react-query";
import { useAuth } from "@/hooks/use-auth";
@@ -91,12 +90,12 @@ export default function Trash() {
{ data: { ids } },
{
onSuccess: (res) => {
- toast({ title: "Tools restored", description: `${res.restored ?? ids.length} tool(s) restored.` });
+ toast({ title: t("trash.toastRestored"), description: t("trash.toastRestoredSub", { count: res.restored ?? ids.length }) });
setSelected(new Set());
invalidate();
},
onError: (err) => {
- toast({ title: "Failed to restore", description: err.data?.error ?? err.message, variant: "destructive" });
+ toast({ title: t("trash.toastRestoreFailed"), description: err.data?.error ?? err.message, variant: "destructive" });
},
},
);
@@ -107,13 +106,13 @@ export default function Trash() {
{ data: { ids } },
{
onSuccess: () => {
- toast({ title: "Tools deleted", description: `${ids.length} tool(s) permanently removed.` });
+ toast({ title: t("trash.toastDeleted"), description: t("trash.toastDeletedSub", { count: ids.length }) });
setSelected(new Set());
setConfirmDelete(false);
invalidate();
},
onError: (err) => {
- toast({ title: "Failed to delete", description: err.data?.error ?? err.message, variant: "destructive" });
+ toast({ title: t("trash.toastDeleteFailed"), description: err.data?.error ?? err.message, variant: "destructive" });
},
},
);
@@ -124,13 +123,13 @@ export default function Trash() {
undefined,
{
onSuccess: (res) => {
- toast({ title: "Trash emptied", description: `${res.deleted ?? 0} tool(s) permanently removed.` });
+ toast({ title: t("trash.toastEmptied"), description: t("trash.toastEmptiedSub", { count: res.deleted ?? 0 }) });
setSelected(new Set());
setConfirmEmpty(false);
invalidate();
},
onError: (err) => {
- toast({ title: "Failed to empty trash", description: err.data?.error ?? err.message, variant: "destructive" });
+ toast({ title: t("trash.toastEmptyFailed"), description: err.data?.error ?? err.message, variant: "destructive" });
},
},
);
@@ -227,48 +226,48 @@ export default function Trash() {
0}
onCheckedChange={toggleAll}
- aria-label="Select all"
+ aria-label={t("trash.selectAll")}
/>
- Name
- Category
+ {t("trash.name")}
+ {t("trash.category")}
{t("trash.deletedAt")}
{t("trash.deletedBy")}
- Actions
+ {t("trash.actions")}
- {trashed.map((t: Tool) => (
-
+ {trashed.map((tool) => (
+
toggle(t.id)}
- aria-label={`Select ${t.name}`}
+ checked={selected.has(tool.id)}
+ onCheckedChange={() => toggle(tool.id)}
+ aria-label={t("trash.selectName", { name: tool.name })}
/>
- {t.name}
+ {tool.name}
- {t.category}
+ {tool.category}
- {t.deletedAt ? format(new Date(t.deletedAt), "dd.MM.yyyy HH:mm") : "—"}
+ {tool.deletedAt ? format(new Date(tool.deletedAt), "dd.MM.yyyy HH:mm") : "—"}
- {t.deletedBy ?? "—"}
+ {tool.deletedBy ?? "—"}
- handleRestore([t.id])} disabled={restore.isPending}>
- Restore
+ handleRestore([tool.id])} disabled={restore.isPending}>
+ {t("trash.restoreAction")}
{isAdmin && (
handleDeletePermanent([t.id])}
+ onClick={() => handleDeletePermanent([tool.id])}
disabled={deletePermanent.isPending}
>
- Delete
+ {t("trash.deleteAction")}
)}
@@ -285,13 +284,13 @@ export default function Trash() {
- Delete {selectedIds.length} tool(s) permanently?
+ {t("trash.deleteConfirmTitle", { count: selectedIds.length })}
- This permanently removes the selected tools along with all their ratings, costs and relations. This cannot be undone.
+ {t("trash.deleteConfirmSub")}
- Cancel
+ {t("common.cancel")}
handleDeletePermanent(selectedIds)}
@@ -305,13 +304,13 @@ export default function Trash() {
- Empty the trash?
+ {t("trash.emptyConfirmTitle")}
- This permanently removes all {trashed.length} tool(s) in the trash along with their ratings, costs and relations. This cannot be undone.
+ {t("trash.emptyConfirmSub", { count: trashed.length })}
- Cancel
+ {t("common.cancel")}
- Browse tools
+ {t("common.browseTools")}
) : (
diff --git a/docs/handbook/administration.en.md b/docs/handbook/administration.en.md
new file mode 100644
index 0000000..ea649ad
--- /dev/null
+++ b/docs/handbook/administration.en.md
@@ -0,0 +1,65 @@
+---
+title: Administration
+order: 13
+---
+
+# Administration
+
+The **Admin** area (`/admin`) is exclusively accessible to admins.
+Without the admin role, access is denied.
+
+> At the top right, the **Redundancy dashboard** button leads to the automatic
+> duplicate detection (see [Redundancy](/docs/handbook/redundanz)).
+
+## "Users" tab
+
+Management of local accounts.
+
+- **Add user:** username (required), password (at least 6 characters),
+ email (optional), **role** (User/Admin), **plan** (Free/Premium/Enterprise).
+- **Edit user:** set role, plan and (for local accounts) a new password.
+ For OIDC accounts, password management is offered in the identity provider
+ (e.g. Keycloak).
+- **Delete user:** permanently removes the account (not for your own account).
+
+API reference:
+[`POST /users`](/docs/reference/endpoints/users#createUser),
+[`PATCH /users/{id}`](/docs/reference/endpoints/users#updateUser),
+[`DELETE /users/{id}`](/docs/reference/endpoints/users#deleteUser).
+
+## "Tools" tab
+
+Central access to the tool catalog.
+
+- **Search** for tools.
+- View, edit or move individual tools to the trash.
+- **Bulk action:** select multiple tools and move them to the trash
+ (confirmation dialog; soft-deleted tools are removed from all public views
+ and can be restored or permanently deleted).
+
+## "Audit log" tab
+
+Chronological log of all creation, change and deletion operations
+(max. 100 entries): action, entity + ID, timestamp, executing person and
+changed fields.
+
+API reference: [`GET /audit-logs`](/docs/reference/endpoints/audit#listAuditLogs).
+
+## "System" tab
+
+Version information of the running instance:
+
+- **Version** (e.g. `v0.8.1`),
+- **Commit** (7-digit SHA, linked to the repository),
+- **Build date**,
+- **Trash retention** ("N days" or "Forever").
+
+## Tool links (Admin)
+
+On the detail page of a tool you can manage **links** as an admin
+(own/"manual" as well as automatically detected ones):
+
+- **Link tool:** dialog with tool ID, **relationship type**
+ (Similar / Replaces / Superseded by) and optional notes.
+- Relationship types are displayed as badges on the detail page.
+- Manual links can be removed again via the trash icon.
diff --git a/docs/handbook/analytics.en.md b/docs/handbook/analytics.en.md
new file mode 100644
index 0000000..49c6f97
--- /dev/null
+++ b/docs/handbook/analytics.en.md
@@ -0,0 +1,33 @@
+---
+title: Analytics
+order: 10
+---
+
+# Analytics
+
+The **Analytics** area (`/analytics`) is a public dashboard with
+metrics and charts based on all tools and ratings.
+
+## Metrics (KPI cards)
+
+- **Number of tools** — how many tools are recorded in the catalog.
+- **Number of ratings** — how many ratings were submitted in total.
+- **Active categories** — how many categories exist.
+- **Average rating** — global combined value.
+
+## Charts
+
+| Chart | Content |
+| --- | --- |
+| **Top 8 tools** | Bar chart of the tools with the highest combined score (0–5) |
+| **Tools per category** | Radar chart of the number of tools per category |
+| **Score distribution** | Two horizontal bar charts (usefulness & usability) per star |
+
+The charts are interactive (tooltips on hover).
+
+## API
+
+- [`GET /analytics/summary`](/docs/reference/endpoints/analytics#getAnalyticsSummary)
+- [`GET /analytics/top-tools`](/docs/reference/endpoints/analytics#getTopTools)
+- [`GET /analytics/by-category`](/docs/reference/endpoints/analytics#getAnalyticsByCategory)
+- [`GET /analytics/rating-distribution`](/docs/reference/endpoints/analytics#getRatingDistribution)
diff --git a/docs/handbook/bewerten.en.md b/docs/handbook/bewerten.en.md
new file mode 100644
index 0000000..f5a2eec
--- /dev/null
+++ b/docs/handbook/bewerten.en.md
@@ -0,0 +1,43 @@
+---
+title: Rating
+order: 7
+---
+
+# Rating
+
+On the detail page of a tool you can share your experience. Click on
+**Submit a rating** (requires an account).
+
+## Form fields
+
+| Field | Required | Notes |
+| --- | --- | --- |
+| **Usefulness** | Yes | 1–5 stars |
+| **Usability** | Yes | 1–5 stars |
+| **Comment** | No | Free text |
+| **Name** | No | Defaults to "Anonymous" |
+
+Next to the fields, the **? icon** links directly to the associated field
+description in the [data model reference](/docs/reference/schemas/ratinginput).
+
+## What happens after submitting?
+
+- Your rating is saved immediately and appears in the **rating list** of the
+ detail page.
+- The **averages** (usefulness, usability, combined) and the **score
+ distribution** are updated.
+- The **statistics** in the [Analytics](/docs/handbook/analytics) section are
+ recalculated.
+
+## Statistic sections on the detail page
+
+- **Rating overview:** usefulness & usability as an average with progress
+ bars.
+- **Score distribution:** number of ratings per star (1★–5★).
+- **History:** line chart of combined/individual values over time
+ (only visible once there are several ratings).
+
+## API
+
+- [`POST /tools/{id}/ratings`](/docs/reference/endpoints/ratings#createRating) — Submit a rating
+- [`GET /tools/{id}/ratings`](/docs/reference/endpoints/ratings#listToolRatings) — Ratings of a tool
diff --git a/docs/handbook/datenmodell.en.md b/docs/handbook/datenmodell.en.md
new file mode 100644
index 0000000..8d0dfa3
--- /dev/null
+++ b/docs/handbook/datenmodell.en.md
@@ -0,0 +1,74 @@
+---
+title: Data model
+order: 17
+---
+
+# Data model
+
+This chapter explains the central data objects of toolr at the application
+level. The complete, automatically generated reference of all fields,
+types and constraints can be found in the
+[API reference](/docs/reference/schemas/tool).
+
+## Tool
+
+The heart of it all: a tool recorded in the catalog.
+
+| Property | Description |
+| --- | --- |
+| `id` | Unique identifier |
+| `name` | Display name |
+| `description` | Description (what does the tool do?) |
+| `category` | Category assignment |
+| `websiteUrl` | Official website (optional) |
+| `iconUrl` | Logo/icon URL (optional) |
+| `features` | List of capabilities |
+| `tags` | List of keywords |
+| `createdAt` / `updatedAt` | Timestamps |
+| `createdBy` | Person who created it |
+| `deletedAt` / `deletedBy` | Soft deletion (trash) |
+
+Input forms use the derived schemas
+[`ToolInput`](/docs/reference/schemas/toolinput) and
+[`ToolUpdate`](/docs/reference/schemas/toolupdate).
+Aggregated views are provided by [`ToolWithStats`](/docs/reference/schemas/toolwithstats)
+(e.g. with average rating).
+
+## Rating (Bewertung)
+
+A single rating for a tool:
+
+- `usefulness` (Nützlichkeit, 1–5) and `usability` (Bedienbarkeit, 1–5)
+- optional `comment` and a display name (`reviewerName`)
+- timestamp
+
+Input schema: [`RatingInput`](/docs/reference/schemas/ratinginput).
+
+## User & Auth
+
+- [`User`](/docs/reference/schemas/user) — user account with role (User/Admin)
+ and plan (Free/Premium/Enterprise).
+- [`AuthUser`](/docs/reference/schemas/authuser) — the logged-in profile
+ including `entitlements` (available features).
+- [`UserPreferences`](/docs/reference/schemas/userpreferences) — view and
+ density preferences as well as the `watchlist` (list of tool IDs).
+
+## Analytics
+
+The statistics endpoints provide aggregated data:
+
+- [`AnalyticsSummary`](/docs/reference/schemas/analyticssummary) — global
+ metrics (number of tools/ratings, categories, average).
+- [`TopToolEntry`](/docs/reference/schemas/TopToolEntry) — an entry of the
+ top tools.- [`CategoryStats`](/docs/reference/schemas/categorystats) — number of tools per
+ category.
+- [`RatingDistribution`](/docs/reference/schemas/ratingdistribution) —
+ score distribution (usefulness & usability).
+- [`ScoreBucket`](/docs/reference/schemas/scorebucket) — a value bucket.
+
+## Additional
+
+- [`VersionInfo`](/docs/reference/schemas/versioninfo) — version, commit SHA,
+ build date and trash retention of the running instance.
+- [`AuditLog`](/docs/reference/schemas/auditlog) — a log entry
+ (action, entity, timestamp, actor, changes).
diff --git a/docs/handbook/getting-started.en.md b/docs/handbook/getting-started.en.md
new file mode 100644
index 0000000..dbad9f2
--- /dev/null
+++ b/docs/handbook/getting-started.en.md
@@ -0,0 +1,57 @@
+---
+title: Getting Started
+order: 2
+---
+
+# Getting Started
+
+This page guides you through the most important workflows in toolr — from your
+first visit to creating and rating a tool.
+
+## 1. Sign in
+
+Most actions (create a tool, rate, watchlist, compare) require
+an account. Click **Sign in** in the bottom left corner. Depending on the
+instance configuration you have two options:
+
+- **Local accounts:** username + password. Access is created by an admin
+ (see [Administration](/docs/handbook/administration)).
+- **OIDC (SSO):** sign in with the configured identity provider (e.g.
+ Keycloak).
+
+Which mode is active is shown by the
+[`GET /auth/mode`](/docs/reference/endpoints/auth#getAuthMode) endpoint. Details
+can be found in the [Sign in & account](/docs/handbook/konto) section.
+
+## 2. Find tools
+
+Open the **Browse tools** section:
+
+- **Search** — full-text search across name & description (shortcut `/`).
+- **Filter** — by category, tags, features and minimum rating
+ (`minRating`).
+- **Sort** — by newest, top-rated, most rated, name
+ (ascending/descending) or last update.
+
+All options in detail: [Find & browse tools](/docs/handbook/tools-finden).
+
+## 3. Create a tool
+
+Go to **Add tool** and fill in the form. Details for each field can be found
+in the [Create a tool](/docs/handbook/tool-anlegen) section and in the
+[field reference](/docs/reference/schemas/toolinput).
+
+## 4. Rate
+
+On the detail page of a tool you can submit **usefulness** and **usability**
+(1–5 each) and optionally leave a comment. Your
+rating is immediately reflected in the statistics.
+See [Rating](/docs/handbook/bewerten).
+
+## 5. Further reading
+
+- [Compare tools](/docs/handbook/vergleichen)
+- [Watchlist](/docs/handbook/watchlist)
+- [Analytics](/docs/handbook/analytics)
+- [Plans & permissions](/docs/handbook/plaene)
+- [Administration](/docs/handbook/administration)
diff --git a/docs/handbook/index.en.md b/docs/handbook/index.en.md
new file mode 100644
index 0000000..8842610
--- /dev/null
+++ b/docs/handbook/index.en.md
@@ -0,0 +1,55 @@
+---
+title: Overview
+order: 1
+---
+
+# Welcome to toolr
+
+toolr is a platform for **discovering, rating and comparing development
+tools**. Users maintain a shared catalog of tools, submit ratings
+(usefulness & usability) and use statistics to make the right choice.
+
+## What can you do with toolr?
+
+| Function | Description | Visibility |
+| --- | --- | --- |
+| **Browse tools** | Filter, sort and search the catalog | Everyone |
+| **Create a tool** | Add a new tool with description, category, features & tags | Logged in |
+| **Edit/delete tools** | Maintain your own tools (creator or admin) | Logged in |
+| **Rate** | Submit usefulness & usability (1–5) plus a comment | Logged in |
+| **Watchlist** | Save tools as favorites | Premium |
+| **Compare** | View tools side by side | Premium |
+| **Record costs** | Enter license and cost models per tool | Premium |
+| **Analytics** | Statistics, top tools, distributions | Everyone |
+| **Trash** | Restore soft-deleted tools or delete them permanently | Premium |
+| **Admin** | User management, audit log, system information | Admin |
+| **Redundancy detection** | Automatic duplicate detection | Admin |
+
+## How this documentation is organized
+
+- **User Guide** (these pages): step-by-step instructions for all
+ functions — from the [Getting Started](/docs/handbook/getting-started) to
+ [Administration](/docs/handbook/administration).
+- **API Reference**: automatically generated from the OpenAPI specification —
+ all [endpoints](/docs/reference/endpoints/tools) and
+ [data fields](/docs/reference/schemas/toolinput) of the current version.
+- **Release Notes**: what is new in each [version](/docs/releases/v0.8.1).
+
+## Getting started
+
+The fastest way:
+
+1. **Sign in** — without an account you can only browse
+ (see [Getting Started](/docs/handbook/getting-started#1-anmelden)).
+2. **Find tools** — search, filters and sorting in the
+ [Browse tools](/docs/handbook/tools-finden) section.
+3. **Create a tool** — via "Add tool"
+ ([guide](/docs/handbook/tool-anlegen)).
+4. **Rate** — on the detail page of a tool
+ ([guide](/docs/handbook/bewerten)).
+
+## Contact & source code
+
+The source code is available at
+[git.kubebase.de/admin/tool-evaluator](https://git.kubebase.de/admin/tool-evaluator) —
+you can reach it at any time via the repository icon in the top right corner.
diff --git a/docs/handbook/konto.en.md b/docs/handbook/konto.en.md
new file mode 100644
index 0000000..c7ff15a
--- /dev/null
+++ b/docs/handbook/konto.en.md
@@ -0,0 +1,56 @@
+---
+title: Login & Account
+order: 3
+---
+
+# Login & Account
+
+## Logging in
+
+Click **Login** at the bottom left of the sidebar. Depending on the
+configuration of the instance:
+
+- **Local accounts:** enter username and password. The accounts are created
+ by an admin (see [Administration](/docs/handbook/administration)).
+- **OIDC (SSO):** you are redirected to the configured identity provider and
+ log in there.
+
+The active mode is available at the endpoint
+[`GET /auth/mode`](/docs/reference/endpoints/auth#getAuthMode).
+
+> You can reach the login page directly at `/login`. After a successful
+> login you are redirected back to the page you originally requested.
+
+## User profile
+
+You can see your profile (avatar, name, email, plan) at the bottom left in
+the user menu. There you have the following actions available:
+
+- **Watchlist** — your saved tools (only with the corresponding plan).
+- **Trash** — restorable, deleted tools (Premium/Enterprise).
+- **Change password** — directly in toolr for local accounts; for OIDC
+ accounts, password management is offered in the identity provider.
+- **Logout** — ends your session.
+
+## Changing your password (local account)
+
+1. Open the user menu at the bottom left.
+2. Select **Change password**.
+3. Enter the **current** and a **new** password (min. 6 characters) and
+ confirm it.
+4. Save — the password takes effect immediately.
+
+API reference: [`POST /auth/me/password`](/docs/reference/endpoints/auth#changeMyPassword).
+
+## Display settings
+
+Using the buttons at the top right you can:
+
+- switch the **language** (German / English),
+- toggle the **theme** (Light / Dark / System),
+- adjust the **list view** and **density** in the Browse tools section
+ (see [Finding & browsing tools](/docs/handbook/tools-finden)).
+
+Your preferences (incl. watchlist) are saved at the endpoint
+[`GET /auth/me/preferences`](/docs/reference/endpoints/auth#getMePreferences)
+and updated via [`PUT /auth/me/preferences`](/docs/reference/endpoints/auth#updateMePreferences).
diff --git a/docs/handbook/kosten.en.md b/docs/handbook/kosten.en.md
new file mode 100644
index 0000000..6a658e9
--- /dev/null
+++ b/docs/handbook/kosten.en.md
@@ -0,0 +1,39 @@
+---
+title: Recording Costs
+order: 12
+---
+
+# Recording Costs
+
+On the detail page of a tool you can enter cost and license models so that
+the total costs per tool become transparent.
+
+> Costs is a **premium feature** (`costs`, Premium/Enterprise). Admins always
+> have access.
+
+## Adding costs
+
+Click **Add costs** in the costs section of the detail page and fill out the
+form:
+
+| Field | Notes |
+| --- | --- |
+| **License type** | Free / Subscription / One-Time / Usage-Based |
+| **Billing period** | Only for "Subscription": Monthly / Quarterly / Annually |
+| **Costs** | Amount as a number |
+| **Currency** | EUR / USD / GBP / CHF |
+| **Notes** | Optional free text |
+
+Saving creates the entry. Each cost entry is displayed as a card with
+license badge, billing period, amount (`Amount Currency` or "Free") and
+notes.
+
+## Editing & deleting costs
+
+Hovering over a cost card shows the **Edit** (pencil) and **Delete** (trash)
+actions.
+
+## API
+
+The cost data is managed via the tool endpoints
+(see [API reference](/docs/reference/endpoints/tools)).
diff --git a/docs/handbook/papierkorb.en.md b/docs/handbook/papierkorb.en.md
new file mode 100644
index 0000000..c2e3a69
--- /dev/null
+++ b/docs/handbook/papierkorb.en.md
@@ -0,0 +1,44 @@
+---
+title: Trash
+order: 15
+---
+
+# Trash
+
+The **trash** (`/trash`) contains soft-deleted tools. With trash access
+they can be restored; permanent deletion is reserved for admins.
+
+> The trash is a **premium feature** (`trash`, Premium/Enterprise).
+> Admins always have access.
+
+## Access
+
+The trash can be reached via the user menu or the sidebar.
+Without the `trash` permission, a hint about changing the plan appears.
+
+## Restoring
+
+- Select one or more tools (checkboxes).
+- Click on **Restore (N)** — the tools appear again in all
+ public views.
+
+> Restoring is available to anyone with trash access.
+
+## Permanently delete (admin only)
+
+- **Delete (N)** **permanently** removes the selected tools — including
+ all ratings, costs and links. This cannot be undone.
+- **Empty trash** permanently removes all soft-deleted tools.
+
+## Table
+
+The trash lists: name, category, **Deleted on** (`tt.MM.jjjj HH:mm`),
+**Deleted by** as well as actions (Restore; Delete admin only). The search
+filters by name.
+
+## API
+
+- [`GET /tools/trash`](/docs/reference/endpoints/tools#listTrashedTools) — list
+- [`POST /tools/trash/restore`](/docs/reference/endpoints/tools#restoreTools) — restore
+- [`DELETE /tools/trash`](/docs/reference/endpoints/tools#deleteTrashedTools) — permanently delete (admin)
+- [`POST /tools/trash/empty`](/docs/reference/endpoints/tools#emptyTrash) — empty trash (admin)
diff --git a/docs/handbook/plaene.en.md b/docs/handbook/plaene.en.md
new file mode 100644
index 0000000..f3ad231
--- /dev/null
+++ b/docs/handbook/plaene.en.md
@@ -0,0 +1,45 @@
+---
+title: Plans & Permissions
+order: 11
+---
+
+# Plans & Permissions
+
+toolr distinguishes **plans** (tier) and **roles**. Admins bypass all feature
+restrictions.
+
+## Plans
+
+| Plan | Description |
+| --- | --- |
+| **Free** | Basic functions: search, filter, view, analytics |
+| **Premium** | Additionally watchlist, compare, trash, costs |
+| **Enterprise** | All premium features + extended support |
+
+### Feature permissions
+
+Premium/Enterprise unlock the following features:
+
+| Feature | Function | Learn more |
+| --- | --- | --- |
+| `compare` | Compare tools | [Compare](/docs/handbook/vergleichen) |
+| `watchlist` | Favorites list | [Watchlist](/docs/handbook/watchlist) |
+| `trash` | Trash (soft-deleted tools) | [Trash](/docs/handbook/papierkorb) |
+| `costs` | Record cost/license models | [Recording costs](/docs/handbook/kosten) |
+
+If you are missing a feature, the app shows an **upgrade notice** with a link
+to the plan management.
+
+## Roles
+
+| Role | Permissions |
+| --- | --- |
+| **User** | Standard account: create/rate tools, edit your own tools |
+| **Admin** | All user rights + administration, audit log, redundancy, empty trash, tool links |
+
+Admins pass **all** feature checks — even without a premium plan.
+
+## Plan/role management
+
+The assignment of role and plan is managed by admins in the
+[Administration](/docs/handbook/administration) section (tab "Users").
diff --git a/docs/handbook/redundanz.en.md b/docs/handbook/redundanz.en.md
new file mode 100644
index 0000000..691c3b0
--- /dev/null
+++ b/docs/handbook/redundanz.en.md
@@ -0,0 +1,42 @@
+---
+title: Redundancy dashboard
+order: 14
+---
+
+# Redundancy dashboard
+
+The **Redundancy dashboard** (`/admin/redundancy`) is an admin tool for
+automatic detection of duplicate or strongly overlapping tools — per
+category — including cost and rating comparison.
+
+> Access is reserved exclusively for admins (the API is
+> admin-protected).
+
+## Layout
+
+- **Per category** a group is shown: name of the category,
+ number of tools and comparisons as well as the **total monthly costs**
+ if applicable (e.g. `€X.XX/mo total`).
+- Each tool is displayed as a card: name, monthly costs, number of
+ ratings, combined rating, license badges and number of features.
+
+## Comparisons & recommendations
+
+For each tool pair the following appears:
+
+- Tool A vs. Tool B, each with rating (`X.X ★`) and monthly costs.
+- **Overlap** in percent (progress bar in the middle).
+- A **recommendation** with confidence color:
+ - **high** (green), **medium** (yellow), **low** (gray)
+- The recommended, better tool is marked with a "thumbs up" and justified.
+
+## Manual rating
+
+You can rate a pair manually: click on Tool A or Tool B to
+record which one is better. The selection is saved and the
+display is updated.
+
+## API
+
+- [`GET /api/admin/redundancy`](#) — load data (admin-protected)
+- [`POST /api/admin/redundancy/evaluate`](#) — save manual rating
diff --git a/docs/handbook/tastatur.en.md b/docs/handbook/tastatur.en.md
new file mode 100644
index 0000000..916df10
--- /dev/null
+++ b/docs/handbook/tastatur.en.md
@@ -0,0 +1,40 @@
+---
+title: Keyboard shortcuts & command palette
+order: 16
+---
+
+# Keyboard shortcuts & command palette
+
+## Command palette
+
+The command palette is the central quick navigation:
+
+- Open with **`⌘K`** (macOS) or **`Ctrl+K`** (Windows/Linux).
+- Alternatively via the search bar at the top right ("Search tools… ⌘K") or the
+ search icon on mobile devices.
+
+### Empty state
+
+Without input, the palette shows:
+
+- **Recently viewed** — the last 5 tools you visited.
+- **Navigation** — browse tools, add tool, analytics as well as
+ (depending on permissions) watchlist, trash and admin.
+
+### Search
+
+Type to search for tools live (max. 10 results, incl. rating
+`X.X★`).
+
+## Overview of keyboard shortcuts
+
+| Shortcut | Action |
+| --- | --- |
+| `⌘K` / `Ctrl+K` | Open command palette |
+| `/` | Focus search in the "Browse tools" area |
+
+## Additional notes
+
+- **Recently viewed** is stored locally in the browser (max. 5 entries).
+- The sidebar (left navigation) can be collapsed on desktop; the
+ breadcrumb at the top shows your current location.
diff --git a/docs/handbook/tool-anlegen.en.md b/docs/handbook/tool-anlegen.en.md
new file mode 100644
index 0000000..3af152f
--- /dev/null
+++ b/docs/handbook/tool-anlegen.en.md
@@ -0,0 +1,50 @@
+---
+title: Create a tool
+order: 5
+---
+
+# Create a tool
+
+To add a new tool to the catalog, click **Add tool**
+(`/tools/new`). Creating a tool requires an account — without being signed in
+a notice with a login button appears.
+
+## Form fields
+
+| Field | Required | Notes |
+| --- | --- | --- |
+| **Name** | Yes | At least 2 characters |
+| **Category** | Yes | Dropdown; new categories can be created directly |
+| **Website URL** | No | Valid URL (e.g. `https://...`) |
+| **Icon / Logo URL** | No | Valid URL; preview is shown live |
+| **Description** | Yes | At least 10 characters; describe what the tool does |
+| **Features** | No | Dynamic list with autocomplete (max. 6) |
+| **Tags** | No | Dynamic list with autocomplete |
+
+Next to each field, the **? icon** takes you directly to the corresponding
+field description in the [data model reference](/docs/reference/schemas/toolinput).
+
+### Category
+
+- Type to search for existing categories.
+- Select **+ Create "..."** to create a new category.
+
+### Features & tags
+
+- **Add feature** / **Add tag** appends a new row.
+- The input fields suggest existing features/tags
+ (autocomplete, max. 6 suggestions).
+- Use the **×** button to remove individual rows.
+- Features and tags help with filtering and finding tools again.
+
+## Save
+
+Click **Add tool**. After successful creation you will be redirected to the
+detail page of the new tool.
+
+## API
+
+- [`POST /tools`](/docs/reference/endpoints/tools#createTool) — create a tool
+- [`GET /categories`](/docs/reference/endpoints/tools#listCategories) — categories
+- [`GET /features/all`](/docs/reference/endpoints/tools#listAllFeatures) — features
+- [`GET /tags/all`](/docs/reference/endpoints/tools#listAllTags) — tags
diff --git a/docs/handbook/tool-bearbeiten.en.md b/docs/handbook/tool-bearbeiten.en.md
new file mode 100644
index 0000000..6245b21
--- /dev/null
+++ b/docs/handbook/tool-bearbeiten.en.md
@@ -0,0 +1,36 @@
+---
+title: Edit & delete tools
+order: 6
+---
+
+# Edit & delete tools
+
+## Editing
+
+On the detail page of a tool you will find the **Edit** button
+(only for the person who created the tool, as well as for admins).
+
+The edit page (`/tools/:id/edit`) contains the same fields as when
+creating (name, category, website/icon URL, description, features, tags) —
+already filled with the current values.
+
+- **Save** applies the changes.
+- **Cancel** takes you back to the detail page.
+
+API reference: [`PATCH /tools/{id}`](/docs/reference/endpoints/tools#updateTool).
+
+## Deleting
+
+Via **Delete** on the detail page the tool is removed. The behavior
+depends on your plan:
+
+- **With trash access** (Premium/Enterprise or Admin): the tool is
+ **soft deleted** — it disappears from all public views, but can
+ be restored or permanently deleted in the [trash](/docs/handbook/papierkorb).
+- **Without trash access:** the tool is **permanently** deleted and cannot
+ be restored.
+
+Deletion is only possible for the person who created the tool, as well as
+for admins.
+
+API reference: [`DELETE /tools/{id}`](/docs/reference/endpoints/tools#deleteTool).
diff --git a/docs/handbook/tools-finden.en.md b/docs/handbook/tools-finden.en.md
new file mode 100644
index 0000000..5740257
--- /dev/null
+++ b/docs/handbook/tools-finden.en.md
@@ -0,0 +1,70 @@
+---
+title: Find & browse tools
+order: 4
+---
+
+# Find & browse tools
+
+The **Browse tools** section (`/tools`) is your entry point to the catalog.
+Here you combine search, filters and sorting to find exactly the tools
+you are interested in.
+
+## Search
+
+- The **search bar** searches name and description (full text).
+- Shortcut: Press **`/`** to focus the search.
+- The input is debounced so that filtering happens immediately with each
+ keystroke.
+
+## Filter
+
+Via the **Filter** button (with a badge for the number of active filters)
+you open the filter popover with:
+
+- **Tags** — selection via checkboxes (scrollable list).
+- **Features** — selection via checkboxes.
+- **Minimum rating** — slider from 0 to 5 (in steps of 0.5); shows
+ e.g. "3.0+".
+
+Active filters appear as **removable chips** above the result list.
+Use **Reset filters** or **Remove all** to clear them again.
+
+## Sort
+
+The **Sort** dropdown offers the following options:
+
+| Sort | Description |
+| --- | --- |
+| Newest | New tools first |
+| Top rated | By combined rating |
+| Most rated | By number of ratings |
+| Name (A–Z) | Alphabetically ascending |
+| Name (Z–A) | Alphabetically descending |
+| Last updated | By last update |
+
+## View & density
+
+- **Switch view:** grid / table / rows.
+- **Density:** comfortable / compact (slider).
+
+Your selection is saved — locally in the browser and, for logged-in users,
+additionally on the server in the preferences. View, density, search, filters
+and sorting are reflected in the URL so you can share results.
+
+## Table view
+
+In the table view the columns **Tool**, **Rating** and **Number of
+ratings** are sortable. Hovering over a row shows a preview
+with rating details, tags and mini bars.
+
+## Selecting for comparison & watchlist
+
+- On every card/row you find a **compare icon** that lets you add tools to the
+ [compare bar](/docs/handbook/vergleichen).
+- The **bookmark icon** saves tools to your
+ [watchlist](/docs/handbook/watchlist) (only with the corresponding plan).
+
+## API
+
+All search, filter and sort parameters correspond to the query parameters of
+[`GET /tools`](/docs/reference/endpoints/tools#listTools).
diff --git a/docs/handbook/vergleichen.en.md b/docs/handbook/vergleichen.en.md
new file mode 100644
index 0000000..9f0256e
--- /dev/null
+++ b/docs/handbook/vergleichen.en.md
@@ -0,0 +1,46 @@
+---
+title: Compare
+order: 9
+---
+
+# Compare
+
+With the compare function you can put several tools **side by side** —
+ideal for making a well-informed decision.
+
+> Comparing is a **premium feature** (Premium/Enterprise) and is always
+> available to admins.
+
+## Selecting tools
+
+1. In the **Browse tools** section, click the **compare icon** (scales) on
+ each card/row.
+2. The **compare bar** appears at the bottom with the selected tools as
+ chips. You can remove individual tools (×) or clear the selection.
+3. Click **Compare (N)** to go to the compare view.
+
+> Without a premium plan the button is locked (lock icon). The dialog takes
+> you to the plan switch
+> (see [Plans & permissions](/docs/handbook/plaene)).
+
+## The compare view
+
+The view shows a table with one column per tool. Rows:
+
+| Row | Content |
+| --- | --- |
+| **Rating** | Stars + value (e.g. `4.2/5`) |
+| **Usefulness** | Value (X.X/5) |
+| **Usability** | Value (X.X/5) |
+| **Number of ratings** | Count |
+| **Description** | Text |
+| **Features** | Badges |
+| **Tags** | Badges |
+| **Last updated** | Date |
+
+The **best value** per row is highlighted (with trophy icon).
+
+## API
+
+The compare view reads the data via
+[`GET /compare`](/docs/reference/endpoints/tools#listCompareTools).
diff --git a/docs/handbook/watchlist.en.md b/docs/handbook/watchlist.en.md
new file mode 100644
index 0000000..dc2d40b
--- /dev/null
+++ b/docs/handbook/watchlist.en.md
@@ -0,0 +1,37 @@
+---
+title: Watchlist
+order: 8
+---
+
+# Watchlist
+
+The **watchlist** is a personal favorites list. You can open and compare the
+tools in it at any time with a click.
+
+> The watchlist is a **premium feature** (Premium/Enterprise) and is always
+> available to admins.
+
+## Prerequisite
+
+You need a plan with the `watchlist` permission. If it is missing, a note
+about switching plans appears at the bookmark
+(see [Plans & permissions](/docs/handbook/plaene)).
+
+## Saving a tool
+
+- On every card/row in the **Browse tools** section you will find the
+ **bookmark icon**.
+- A click saves the tool to your watchlist — the icon becomes filled.
+- Clicking it again removes it.
+
+## Viewing the watchlist
+
+Open the watchlist via the user menu or the sidebar. It shows all saved tools
+as cards. The filled bookmark on a card removes the tool from the list.
+
+## Where is the watchlist stored?
+
+The watchlist is a list of tool IDs in your **user preferences**. This way it
+is linked to your account across devices.
+
+API reference: [`GET /auth/me/watchlist`](/docs/reference/endpoints/auth#getMeWatchlist).
diff --git a/docs/releases/v0.6.0.en.md b/docs/releases/v0.6.0.en.md
new file mode 100644
index 0000000..8762f32
--- /dev/null
+++ b/docs/releases/v0.6.0.en.md
@@ -0,0 +1,37 @@
+# v0.6.0 — Release Notes
+
+**Date:** 2026-08-03 · **Tag:** [`v0.6.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.6.0)
+
+## New features
+
+- Complete modernization of all dependencies to the current major versions
+ (TypeScript 7, Vite 8, React 19.2, openid-client 6, zod 4, recharts 3, react-day-picker 10).
+
+## Fixes & improvements
+
+- CI build fixed via `allowBuilds` configuration for pnpm 11
+ (build scripts for esbuild & Co. are no longer blocked).
+- Image tagging simplified: only `latest` and `v*` tags, no more `nightly-*`/`sha-*` tags.
+- All dependencies pinned exactly; automatic updates via Renovate prepared
+ (`renovate.json`, `docs/dependency-policy.md`).
+
+## API changes
+
+- No breaking changes to the API. openid-client internally migrated to v6
+ (auth flow behaves identically).
+
+## Operations / upgrade
+
+- **Env vars:** unchanged. Node image pinned to `node:24.18.1-alpine`.
+- **Migration:** no database migration required.
+- **Breaking changes:** none.
+
+## Known limitations
+
+- `typedoc` (indirect orval dependency) shows a peer-dependency warning
+ (expects TypeScript 5.x/6.x, 7.x is installed) — harmless for build and runtime.
+
+## Links
+
+- Commit: [`2f66fff`](https://git.kubebase.de/admin/tool-evaluator/commit/2f66fff)
+- Tag: [`v0.6.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.6.0)
diff --git a/docs/releases/v0.7.0.en.md b/docs/releases/v0.7.0.en.md
new file mode 100644
index 0000000..2348c6e
--- /dev/null
+++ b/docs/releases/v0.7.0.en.md
@@ -0,0 +1,35 @@
+# v0.7.0 — Release Notes
+
+**Date:** 2026-08-03 · **Tag:** [`v0.7.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.7.0)
+
+## New features
+
+- Version-bound **release documentation** in the app under `/docs`
+ (index + detail page per version, Markdown from `docs/releases/`).
+- Generated release template (`docs/releases/TEMPLATE.md`) and
+ sync step for the frontend build.
+
+## Fixes & improvements
+
+- `tsx` bumped to 4.23.4 — last outdated dependency in the workspace
+ (`pnpm outdated -r` is now empty).
+
+## API changes
+
+- No breaking changes to the API.
+
+## Operations / upgrade
+
+- **Env vars:** unchanged.
+- **Migration:** none.
+- **Breaking changes:** none.
+
+## Known limitations
+
+- The documentation is currently limited to release notes; a complete
+ API/field reference will follow in v0.8.0.
+
+## Links
+
+- Commit: [`520f917`](https://git.kubebase.de/admin/tool-evaluator/commit/520f917)
+- Tag: [`v0.7.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.7.0)
diff --git a/docs/releases/v0.8.0.en.md b/docs/releases/v0.8.0.en.md
new file mode 100644
index 0000000..671391b
--- /dev/null
+++ b/docs/releases/v0.8.0.en.md
@@ -0,0 +1,46 @@
+# v0.8.0 — Release Notes
+
+**Date:** 2026-08-03 · **Tag:** [`v0.8.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.0)
+
+## New features
+
+- **Complete documentation site** in mkdocs look under `/docs`:
+ - **Manual** with understandable explanations of all features
+ (Getting started, creating a tool, evaluations, comparing, watchlist,
+ analytics, administration, data model).
+ - **Automatically generated reference** from `lib/api-spec/openapi.yaml`:
+ all endpoints and data fields (type, required status, constraints) —
+ this guarantees that *every* feature is documented.
+ - **Search** across the manual, endpoints, and fields.
+ - **Version dropdown**: older releases keep their full field/endpoint
+ reference as a snapshot.
+ - **Repo link** to the source in the top right.
+- **Help buttons (?) in forms** (NetBox style): next to each field,
+ an icon jumps directly to the field description in the docs.
+
+## Fixes & improvements
+
+- Docs generator `scripts/src/generate-docs.mjs` replaces the previous
+ `sync-release-docs.mjs` (OpenAPI parsing, manual, search index, snapshots).
+- Documentation for v0.7.0 backfilled.
+
+## API changes
+
+- No breaking changes to the API.
+
+## Operations / upgrade
+
+- **Env vars:** unchanged.
+- **Migration:** none.
+- **Breaking changes:** none.
+
+## Known limitations
+
+- Manual & reference apply to the current version; older versions
+ show their release notes and a reference snapshot, if generated at
+ release time (`node scripts/src/generate-docs.mjs --snapshot vX.Y.Z`).
+
+## Links
+
+- Commit: [`6c92b63`](https://git.kubebase.de/admin/tool-evaluator/commit/6c92b63)
+- Tag: [`v0.8.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.0)
diff --git a/docs/releases/v0.8.1.en.md b/docs/releases/v0.8.1.en.md
new file mode 100644
index 0000000..a865280
--- /dev/null
+++ b/docs/releases/v0.8.1.en.md
@@ -0,0 +1,47 @@
+# v0.8.1 — Release Notes
+
+**Date:** 2026-08-03 · **Tag:** [`v0.8.1`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.1)
+
+## New features
+
+- **Standalone docs page**: The documentation is now available as its own,
+ mkdocs-like page at `toolr.kubebase.de/docs` — without the app shell
+ (own header with repo link, version dropdown, search, theme toggle
+ and link back to the app).
+- **Navigation renamed**: The sidebar entry is now called **"Help"**
+ and leads to the standalone docs.
+- **User guide completely revised**: 17 manual pages with
+ step-by-step instructions for all features (finding tools, creating a
+ tool, evaluating, watchlist, comparing, costs, analytics, plans,
+ administration, redundancy, trash, keyboard shortcuts, data model).
+
+## Fixes & improvements
+
+- Reference links are now case-insensitive
+ (schema/endpoint slugs such as `toolinput` and `ToolInput` both work).
+- Manual links to endpoint anchors corrected (PascalCase operation IDs).
+- Outdated, broken manual links (`vergleichen`, `watchlist` …) replaced.
+- Documentation table headers and notice texts in the docs page
+ internationalized via i18n (de/en).
+
+## API changes
+
+- No changes to the API.
+
+## Operations / upgrade
+
+- **Env vars:** unchanged.
+- **Migration:** none.
+- **Breaking changes:** none. The docs page is reachable under `/docs` as
+ before; only the presentation is now standalone.
+
+## Known limitations
+
+- Manual & reference apply to the current version; older versions show
+ their release notes and a reference snapshot, if generated at release time
+ (`node scripts/src/generate-docs.mjs --snapshot vX.Y.Z`).
+
+## Links
+
+- Commit: [`fdf2d74`](https://git.kubebase.de/admin/tool-evaluator/commit/fdf2d74)
+- Tag: [`v0.8.1`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.1)
diff --git a/docs/releases/v0.8.1/handbook/administration.en.md b/docs/releases/v0.8.1/handbook/administration.en.md
new file mode 100644
index 0000000..9fe7806
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/administration.en.md
@@ -0,0 +1,61 @@
+
+# Administration
+
+The **Admin** area (`/admin`) is exclusively accessible to admins.
+Without the admin role, access is denied.
+
+> At the top right, the **Redundancy dashboard** button leads to the automatic
+> duplicate detection (see [Redundancy](/docs/handbook/redundanz)).
+
+## "Users" tab
+
+Management of local accounts.
+
+- **Add user:** username (required), password (at least 6 characters),
+ email (optional), **role** (User/Admin), **plan** (Free/Premium/Enterprise).
+- **Edit user:** set role, plan and (for local accounts) a new password.
+ For OIDC accounts, password management is offered in the identity provider
+ (e.g. Keycloak).
+- **Delete user:** permanently removes the account (not for your own account).
+
+API reference:
+[`POST /users`](/docs/reference/endpoints/users#createUser),
+[`PATCH /users/{id}`](/docs/reference/endpoints/users#updateUser),
+[`DELETE /users/{id}`](/docs/reference/endpoints/users#deleteUser).
+
+## "Tools" tab
+
+Central access to the tool catalog.
+
+- **Search** for tools.
+- View, edit or move individual tools to the trash.
+- **Bulk action:** select multiple tools and move them to the trash
+ (confirmation dialog; soft-deleted tools are removed from all public views
+ and can be restored or permanently deleted).
+
+## "Audit log" tab
+
+Chronological log of all creation, change and deletion operations
+(max. 100 entries): action, entity + ID, timestamp, executing person and
+changed fields.
+
+API reference: [`GET /audit-logs`](/docs/reference/endpoints/audit#listAuditLogs).
+
+## "System" tab
+
+Version information of the running instance:
+
+- **Version** (e.g. `v0.8.1`),
+- **Commit** (7-digit SHA, linked to the repository),
+- **Build date**,
+- **Trash retention** ("N days" or "Forever").
+
+## Tool links (Admin)
+
+On the detail page of a tool you can manage **links** as an admin
+(own/"manual" as well as automatically detected ones):
+
+- **Link tool:** dialog with tool ID, **relationship type**
+ (Similar / Replaces / Superseded by) and optional notes.
+- Relationship types are displayed as badges on the detail page.
+- Manual links can be removed again via the trash icon.
diff --git a/docs/releases/v0.8.1/handbook/analytics.en.md b/docs/releases/v0.8.1/handbook/analytics.en.md
new file mode 100644
index 0000000..7450a33
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/analytics.en.md
@@ -0,0 +1,29 @@
+
+# Analytics
+
+The **Analytics** area (`/analytics`) is a public dashboard with
+metrics and charts based on all tools and ratings.
+
+## Metrics (KPI cards)
+
+- **Number of tools** — how many tools are recorded in the catalog.
+- **Number of ratings** — how many ratings were submitted in total.
+- **Active categories** — how many categories exist.
+- **Average rating** — global combined value.
+
+## Charts
+
+| Chart | Content |
+| --- | --- |
+| **Top 8 tools** | Bar chart of the tools with the highest combined score (0–5) |
+| **Tools per category** | Radar chart of the number of tools per category |
+| **Score distribution** | Two horizontal bar charts (usefulness & usability) per star |
+
+The charts are interactive (tooltips on hover).
+
+## API
+
+- [`GET /analytics/summary`](/docs/reference/endpoints/analytics#getAnalyticsSummary)
+- [`GET /analytics/top-tools`](/docs/reference/endpoints/analytics#getTopTools)
+- [`GET /analytics/by-category`](/docs/reference/endpoints/analytics#getAnalyticsByCategory)
+- [`GET /analytics/rating-distribution`](/docs/reference/endpoints/analytics#getRatingDistribution)
diff --git a/docs/releases/v0.8.1/handbook/bewerten.en.md b/docs/releases/v0.8.1/handbook/bewerten.en.md
new file mode 100644
index 0000000..7ccd0c8
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/bewerten.en.md
@@ -0,0 +1,39 @@
+
+# Rating
+
+On the detail page of a tool you can share your experience. Click on
+**Submit a rating** (requires an account).
+
+## Form fields
+
+| Field | Required | Notes |
+| --- | --- | --- |
+| **Usefulness** | Yes | 1–5 stars |
+| **Usability** | Yes | 1–5 stars |
+| **Comment** | No | Free text |
+| **Name** | No | Defaults to "Anonymous" |
+
+Next to the fields, the **? icon** links directly to the associated field
+description in the [data model reference](/docs/reference/schemas/ratinginput).
+
+## What happens after submitting?
+
+- Your rating is saved immediately and appears in the **rating list** of the
+ detail page.
+- The **averages** (usefulness, usability, combined) and the **score
+ distribution** are updated.
+- The **statistics** in the [Analytics](/docs/handbook/analytics) section are
+ recalculated.
+
+## Statistic sections on the detail page
+
+- **Rating overview:** usefulness & usability as an average with progress
+ bars.
+- **Score distribution:** number of ratings per star (1★–5★).
+- **History:** line chart of combined/individual values over time
+ (only visible once there are several ratings).
+
+## API
+
+- [`POST /tools/{id}/ratings`](/docs/reference/endpoints/ratings#createRating) — Submit a rating
+- [`GET /tools/{id}/ratings`](/docs/reference/endpoints/ratings#listToolRatings) — Ratings of a tool
diff --git a/docs/releases/v0.8.1/handbook/datenmodell.en.md b/docs/releases/v0.8.1/handbook/datenmodell.en.md
new file mode 100644
index 0000000..99d602e
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/datenmodell.en.md
@@ -0,0 +1,70 @@
+
+# Data model
+
+This chapter explains the central data objects of toolr at the application
+level. The complete, automatically generated reference of all fields,
+types and constraints can be found in the
+[API reference](/docs/reference/schemas/tool).
+
+## Tool
+
+The heart of it all: a tool recorded in the catalog.
+
+| Property | Description |
+| --- | --- |
+| `id` | Unique identifier |
+| `name` | Display name |
+| `description` | Description (what does the tool do?) |
+| `category` | Category assignment |
+| `websiteUrl` | Official website (optional) |
+| `iconUrl` | Logo/icon URL (optional) |
+| `features` | List of capabilities |
+| `tags` | List of keywords |
+| `createdAt` / `updatedAt` | Timestamps |
+| `createdBy` | Person who created it |
+| `deletedAt` / `deletedBy` | Soft deletion (trash) |
+
+Input forms use the derived schemas
+[`ToolInput`](/docs/reference/schemas/toolinput) and
+[`ToolUpdate`](/docs/reference/schemas/toolupdate).
+Aggregated views are provided by [`ToolWithStats`](/docs/reference/schemas/toolwithstats)
+(e.g. with average rating).
+
+## Rating (Bewertung)
+
+A single rating for a tool:
+
+- `usefulness` (Nützlichkeit, 1–5) and `usability` (Bedienbarkeit, 1–5)
+- optional `comment` and a display name (`reviewerName`)
+- timestamp
+
+Input schema: [`RatingInput`](/docs/reference/schemas/ratinginput).
+
+## User & Auth
+
+- [`User`](/docs/reference/schemas/user) — user account with role (User/Admin)
+ and plan (Free/Premium/Enterprise).
+- [`AuthUser`](/docs/reference/schemas/authuser) — the logged-in profile
+ including `entitlements` (available features).
+- [`UserPreferences`](/docs/reference/schemas/userpreferences) — view and
+ density preferences as well as the `watchlist` (list of tool IDs).
+
+## Analytics
+
+The statistics endpoints provide aggregated data:
+
+- [`AnalyticsSummary`](/docs/reference/schemas/analyticssummary) — global
+ metrics (number of tools/ratings, categories, average).
+- [`TopToolEntry`](/docs/reference/schemas/TopToolEntry) — an entry of the
+ top tools.- [`CategoryStats`](/docs/reference/schemas/categorystats) — number of tools per
+ category.
+- [`RatingDistribution`](/docs/reference/schemas/ratingdistribution) —
+ score distribution (usefulness & usability).
+- [`ScoreBucket`](/docs/reference/schemas/scorebucket) — a value bucket.
+
+## Additional
+
+- [`VersionInfo`](/docs/reference/schemas/versioninfo) — version, commit SHA,
+ build date and trash retention of the running instance.
+- [`AuditLog`](/docs/reference/schemas/auditlog) — a log entry
+ (action, entity, timestamp, actor, changes).
diff --git a/docs/releases/v0.8.1/handbook/getting-started.en.md b/docs/releases/v0.8.1/handbook/getting-started.en.md
new file mode 100644
index 0000000..98344ef
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/getting-started.en.md
@@ -0,0 +1,53 @@
+
+# Getting Started
+
+This page guides you through the most important workflows in toolr — from your
+first visit to creating and rating a tool.
+
+## 1. Sign in
+
+Most actions (create a tool, rate, watchlist, compare) require
+an account. Click **Sign in** in the bottom left corner. Depending on the
+instance configuration you have two options:
+
+- **Local accounts:** username + password. Access is created by an admin
+ (see [Administration](/docs/handbook/administration)).
+- **OIDC (SSO):** sign in with the configured identity provider (e.g.
+ Keycloak).
+
+Which mode is active is shown by the
+[`GET /auth/mode`](/docs/reference/endpoints/auth#getAuthMode) endpoint. Details
+can be found in the [Sign in & account](/docs/handbook/konto) section.
+
+## 2. Find tools
+
+Open the **Browse tools** section:
+
+- **Search** — full-text search across name & description (shortcut `/`).
+- **Filter** — by category, tags, features and minimum rating
+ (`minRating`).
+- **Sort** — by newest, top-rated, most rated, name
+ (ascending/descending) or last update.
+
+All options in detail: [Find & browse tools](/docs/handbook/tools-finden).
+
+## 3. Create a tool
+
+Go to **Add tool** and fill in the form. Details for each field can be found
+in the [Create a tool](/docs/handbook/tool-anlegen) section and in the
+[field reference](/docs/reference/schemas/toolinput).
+
+## 4. Rate
+
+On the detail page of a tool you can submit **usefulness** and **usability**
+(1–5 each) and optionally leave a comment. Your
+rating is immediately reflected in the statistics.
+See [Rating](/docs/handbook/bewerten).
+
+## 5. Further reading
+
+- [Compare tools](/docs/handbook/vergleichen)
+- [Watchlist](/docs/handbook/watchlist)
+- [Analytics](/docs/handbook/analytics)
+- [Plans & permissions](/docs/handbook/plaene)
+- [Administration](/docs/handbook/administration)
diff --git a/docs/releases/v0.8.1/handbook/index.en.md b/docs/releases/v0.8.1/handbook/index.en.md
new file mode 100644
index 0000000..490c2c5
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/index.en.md
@@ -0,0 +1,51 @@
+
+# Welcome to toolr
+
+toolr is a platform for **discovering, rating and comparing development
+tools**. Users maintain a shared catalog of tools, submit ratings
+(usefulness & usability) and use statistics to make the right choice.
+
+## What can you do with toolr?
+
+| Function | Description | Visibility |
+| --- | --- | --- |
+| **Browse tools** | Filter, sort and search the catalog | Everyone |
+| **Create a tool** | Add a new tool with description, category, features & tags | Logged in |
+| **Edit/delete tools** | Maintain your own tools (creator or admin) | Logged in |
+| **Rate** | Submit usefulness & usability (1–5) plus a comment | Logged in |
+| **Watchlist** | Save tools as favorites | Premium |
+| **Compare** | View tools side by side | Premium |
+| **Record costs** | Enter license and cost models per tool | Premium |
+| **Analytics** | Statistics, top tools, distributions | Everyone |
+| **Trash** | Restore soft-deleted tools or delete them permanently | Premium |
+| **Admin** | User management, audit log, system information | Admin |
+| **Redundancy detection** | Automatic duplicate detection | Admin |
+
+## How this documentation is organized
+
+- **User Guide** (these pages): step-by-step instructions for all
+ functions — from the [Getting Started](/docs/handbook/getting-started) to
+ [Administration](/docs/handbook/administration).
+- **API Reference**: automatically generated from the OpenAPI specification —
+ all [endpoints](/docs/reference/endpoints/tools) and
+ [data fields](/docs/reference/schemas/toolinput) of the current version.
+- **Release Notes**: what is new in each [version](/docs/releases/v0.8.1).
+
+## Getting started
+
+The fastest way:
+
+1. **Sign in** — without an account you can only browse
+ (see [Getting Started](/docs/handbook/getting-started#1-anmelden)).
+2. **Find tools** — search, filters and sorting in the
+ [Browse tools](/docs/handbook/tools-finden) section.
+3. **Create a tool** — via "Add tool"
+ ([guide](/docs/handbook/tool-anlegen)).
+4. **Rate** — on the detail page of a tool
+ ([guide](/docs/handbook/bewerten)).
+
+## Contact & source code
+
+The source code is available at
+[git.kubebase.de/admin/tool-evaluator](https://git.kubebase.de/admin/tool-evaluator) —
+you can reach it at any time via the repository icon in the top right corner.
diff --git a/docs/releases/v0.8.1/handbook/index.json b/docs/releases/v0.8.1/handbook/index.json
index 9379656..4dc0aa7 100644
--- a/docs/releases/v0.8.1/handbook/index.json
+++ b/docs/releases/v0.8.1/handbook/index.json
@@ -3,102 +3,136 @@
"slug": "index",
"file": "index.md",
"title": "Überblick",
- "order": 1
+ "order": 1,
+ "fileEn": "index.en.md",
+ "titleEn": "Overview"
},
{
"slug": "getting-started",
"file": "getting-started.md",
"title": "Erste Schritte",
- "order": 2
+ "order": 2,
+ "fileEn": "getting-started.en.md",
+ "titleEn": "Getting Started"
},
{
"slug": "konto",
"file": "konto.md",
"title": "Anmelden & Konto",
- "order": 3
+ "order": 3,
+ "fileEn": "konto.en.md",
+ "titleEn": "Login & Account"
},
{
"slug": "tools-finden",
"file": "tools-finden.md",
"title": "Tools finden & durchsuchen",
- "order": 4
+ "order": 4,
+ "fileEn": "tools-finden.en.md",
+ "titleEn": "Find & browse tools"
},
{
"slug": "tool-anlegen",
"file": "tool-anlegen.md",
"title": "Tool anlegen",
- "order": 5
+ "order": 5,
+ "fileEn": "tool-anlegen.en.md",
+ "titleEn": "Create a tool"
},
{
"slug": "tool-bearbeiten",
"file": "tool-bearbeiten.md",
"title": "Tool bearbeiten & löschen",
- "order": 6
+ "order": 6,
+ "fileEn": "tool-bearbeiten.en.md",
+ "titleEn": "Edit & delete tools"
},
{
"slug": "bewerten",
"file": "bewerten.md",
"title": "Bewerten",
- "order": 7
+ "order": 7,
+ "fileEn": "bewerten.en.md",
+ "titleEn": "Rating"
},
{
"slug": "watchlist",
"file": "watchlist.md",
"title": "Watchlist",
- "order": 8
+ "order": 8,
+ "fileEn": "watchlist.en.md",
+ "titleEn": "Watchlist"
},
{
"slug": "vergleichen",
"file": "vergleichen.md",
"title": "Vergleichen",
- "order": 9
+ "order": 9,
+ "fileEn": "vergleichen.en.md",
+ "titleEn": "Compare"
},
{
"slug": "analytics",
"file": "analytics.md",
"title": "Analytics",
- "order": 10
+ "order": 10,
+ "fileEn": "analytics.en.md",
+ "titleEn": "Analytics"
},
{
"slug": "plaene",
"file": "plaene.md",
"title": "Pläne & Berechtigungen",
- "order": 11
+ "order": 11,
+ "fileEn": "plaene.en.md",
+ "titleEn": "Plans & Permissions"
},
{
"slug": "kosten",
"file": "kosten.md",
"title": "Kosten erfassen",
- "order": 12
+ "order": 12,
+ "fileEn": "kosten.en.md",
+ "titleEn": "Recording Costs"
},
{
"slug": "administration",
"file": "administration.md",
"title": "Administration",
- "order": 13
+ "order": 13,
+ "fileEn": "administration.en.md",
+ "titleEn": "Administration"
},
{
"slug": "redundanz",
"file": "redundanz.md",
"title": "Redundanz-Dashboard",
- "order": 14
+ "order": 14,
+ "fileEn": "redundanz.en.md",
+ "titleEn": "Redundancy dashboard"
},
{
"slug": "papierkorb",
"file": "papierkorb.md",
"title": "Papierkorb",
- "order": 15
+ "order": 15,
+ "fileEn": "papierkorb.en.md",
+ "titleEn": "Trash"
},
{
"slug": "tastatur",
"file": "tastatur.md",
"title": "Tastenkürzel & Kommandopalette",
- "order": 16
+ "order": 16,
+ "fileEn": "tastatur.en.md",
+ "titleEn": "Keyboard shortcuts & command palette"
},
{
"slug": "datenmodell",
"file": "datenmodell.md",
"title": "Datenmodell",
- "order": 17
+ "order": 17,
+ "fileEn": "datenmodell.en.md",
+ "titleEn": "Data model"
}
]
\ No newline at end of file
diff --git a/docs/releases/v0.8.1/handbook/konto.en.md b/docs/releases/v0.8.1/handbook/konto.en.md
new file mode 100644
index 0000000..22360df
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/konto.en.md
@@ -0,0 +1,52 @@
+
+# Login & Account
+
+## Logging in
+
+Click **Login** at the bottom left of the sidebar. Depending on the
+configuration of the instance:
+
+- **Local accounts:** enter username and password. The accounts are created
+ by an admin (see [Administration](/docs/handbook/administration)).
+- **OIDC (SSO):** you are redirected to the configured identity provider and
+ log in there.
+
+The active mode is available at the endpoint
+[`GET /auth/mode`](/docs/reference/endpoints/auth#getAuthMode).
+
+> You can reach the login page directly at `/login`. After a successful
+> login you are redirected back to the page you originally requested.
+
+## User profile
+
+You can see your profile (avatar, name, email, plan) at the bottom left in
+the user menu. There you have the following actions available:
+
+- **Watchlist** — your saved tools (only with the corresponding plan).
+- **Trash** — restorable, deleted tools (Premium/Enterprise).
+- **Change password** — directly in toolr for local accounts; for OIDC
+ accounts, password management is offered in the identity provider.
+- **Logout** — ends your session.
+
+## Changing your password (local account)
+
+1. Open the user menu at the bottom left.
+2. Select **Change password**.
+3. Enter the **current** and a **new** password (min. 6 characters) and
+ confirm it.
+4. Save — the password takes effect immediately.
+
+API reference: [`POST /auth/me/password`](/docs/reference/endpoints/auth#changeMyPassword).
+
+## Display settings
+
+Using the buttons at the top right you can:
+
+- switch the **language** (German / English),
+- toggle the **theme** (Light / Dark / System),
+- adjust the **list view** and **density** in the Browse tools section
+ (see [Finding & browsing tools](/docs/handbook/tools-finden)).
+
+Your preferences (incl. watchlist) are saved at the endpoint
+[`GET /auth/me/preferences`](/docs/reference/endpoints/auth#getMePreferences)
+and updated via [`PUT /auth/me/preferences`](/docs/reference/endpoints/auth#updateMePreferences).
diff --git a/docs/releases/v0.8.1/handbook/kosten.en.md b/docs/releases/v0.8.1/handbook/kosten.en.md
new file mode 100644
index 0000000..1cf7a3a
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/kosten.en.md
@@ -0,0 +1,35 @@
+
+# Recording Costs
+
+On the detail page of a tool you can enter cost and license models so that
+the total costs per tool become transparent.
+
+> Costs is a **premium feature** (`costs`, Premium/Enterprise). Admins always
+> have access.
+
+## Adding costs
+
+Click **Add costs** in the costs section of the detail page and fill out the
+form:
+
+| Field | Notes |
+| --- | --- |
+| **License type** | Free / Subscription / One-Time / Usage-Based |
+| **Billing period** | Only for "Subscription": Monthly / Quarterly / Annually |
+| **Costs** | Amount as a number |
+| **Currency** | EUR / USD / GBP / CHF |
+| **Notes** | Optional free text |
+
+Saving creates the entry. Each cost entry is displayed as a card with
+license badge, billing period, amount (`Amount Currency` or "Free") and
+notes.
+
+## Editing & deleting costs
+
+Hovering over a cost card shows the **Edit** (pencil) and **Delete** (trash)
+actions.
+
+## API
+
+The cost data is managed via the tool endpoints
+(see [API reference](/docs/reference/endpoints/tools)).
diff --git a/docs/releases/v0.8.1/handbook/papierkorb.en.md b/docs/releases/v0.8.1/handbook/papierkorb.en.md
new file mode 100644
index 0000000..7111def
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/papierkorb.en.md
@@ -0,0 +1,40 @@
+
+# Trash
+
+The **trash** (`/trash`) contains soft-deleted tools. With trash access
+they can be restored; permanent deletion is reserved for admins.
+
+> The trash is a **premium feature** (`trash`, Premium/Enterprise).
+> Admins always have access.
+
+## Access
+
+The trash can be reached via the user menu or the sidebar.
+Without the `trash` permission, a hint about changing the plan appears.
+
+## Restoring
+
+- Select one or more tools (checkboxes).
+- Click on **Restore (N)** — the tools appear again in all
+ public views.
+
+> Restoring is available to anyone with trash access.
+
+## Permanently delete (admin only)
+
+- **Delete (N)** **permanently** removes the selected tools — including
+ all ratings, costs and links. This cannot be undone.
+- **Empty trash** permanently removes all soft-deleted tools.
+
+## Table
+
+The trash lists: name, category, **Deleted on** (`tt.MM.jjjj HH:mm`),
+**Deleted by** as well as actions (Restore; Delete admin only). The search
+filters by name.
+
+## API
+
+- [`GET /tools/trash`](/docs/reference/endpoints/tools#listTrashedTools) — list
+- [`POST /tools/trash/restore`](/docs/reference/endpoints/tools#restoreTools) — restore
+- [`DELETE /tools/trash`](/docs/reference/endpoints/tools#deleteTrashedTools) — permanently delete (admin)
+- [`POST /tools/trash/empty`](/docs/reference/endpoints/tools#emptyTrash) — empty trash (admin)
diff --git a/docs/releases/v0.8.1/handbook/plaene.en.md b/docs/releases/v0.8.1/handbook/plaene.en.md
new file mode 100644
index 0000000..f73d6dc
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/plaene.en.md
@@ -0,0 +1,41 @@
+
+# Plans & Permissions
+
+toolr distinguishes **plans** (tier) and **roles**. Admins bypass all feature
+restrictions.
+
+## Plans
+
+| Plan | Description |
+| --- | --- |
+| **Free** | Basic functions: search, filter, view, analytics |
+| **Premium** | Additionally watchlist, compare, trash, costs |
+| **Enterprise** | All premium features + extended support |
+
+### Feature permissions
+
+Premium/Enterprise unlock the following features:
+
+| Feature | Function | Learn more |
+| --- | --- | --- |
+| `compare` | Compare tools | [Compare](/docs/handbook/vergleichen) |
+| `watchlist` | Favorites list | [Watchlist](/docs/handbook/watchlist) |
+| `trash` | Trash (soft-deleted tools) | [Trash](/docs/handbook/papierkorb) |
+| `costs` | Record cost/license models | [Recording costs](/docs/handbook/kosten) |
+
+If you are missing a feature, the app shows an **upgrade notice** with a link
+to the plan management.
+
+## Roles
+
+| Role | Permissions |
+| --- | --- |
+| **User** | Standard account: create/rate tools, edit your own tools |
+| **Admin** | All user rights + administration, audit log, redundancy, empty trash, tool links |
+
+Admins pass **all** feature checks — even without a premium plan.
+
+## Plan/role management
+
+The assignment of role and plan is managed by admins in the
+[Administration](/docs/handbook/administration) section (tab "Users").
diff --git a/docs/releases/v0.8.1/handbook/redundanz.en.md b/docs/releases/v0.8.1/handbook/redundanz.en.md
new file mode 100644
index 0000000..27825b3
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/redundanz.en.md
@@ -0,0 +1,38 @@
+
+# Redundancy dashboard
+
+The **Redundancy dashboard** (`/admin/redundancy`) is an admin tool for
+automatic detection of duplicate or strongly overlapping tools — per
+category — including cost and rating comparison.
+
+> Access is reserved exclusively for admins (the API is
+> admin-protected).
+
+## Layout
+
+- **Per category** a group is shown: name of the category,
+ number of tools and comparisons as well as the **total monthly costs**
+ if applicable (e.g. `€X.XX/mo total`).
+- Each tool is displayed as a card: name, monthly costs, number of
+ ratings, combined rating, license badges and number of features.
+
+## Comparisons & recommendations
+
+For each tool pair the following appears:
+
+- Tool A vs. Tool B, each with rating (`X.X ★`) and monthly costs.
+- **Overlap** in percent (progress bar in the middle).
+- A **recommendation** with confidence color:
+ - **high** (green), **medium** (yellow), **low** (gray)
+- The recommended, better tool is marked with a "thumbs up" and justified.
+
+## Manual rating
+
+You can rate a pair manually: click on Tool A or Tool B to
+record which one is better. The selection is saved and the
+display is updated.
+
+## API
+
+- [`GET /api/admin/redundancy`](#) — load data (admin-protected)
+- [`POST /api/admin/redundancy/evaluate`](#) — save manual rating
diff --git a/docs/releases/v0.8.1/handbook/tastatur.en.md b/docs/releases/v0.8.1/handbook/tastatur.en.md
new file mode 100644
index 0000000..2eddf47
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/tastatur.en.md
@@ -0,0 +1,36 @@
+
+# Keyboard shortcuts & command palette
+
+## Command palette
+
+The command palette is the central quick navigation:
+
+- Open with **`⌘K`** (macOS) or **`Ctrl+K`** (Windows/Linux).
+- Alternatively via the search bar at the top right ("Search tools… ⌘K") or the
+ search icon on mobile devices.
+
+### Empty state
+
+Without input, the palette shows:
+
+- **Recently viewed** — the last 5 tools you visited.
+- **Navigation** — browse tools, add tool, analytics as well as
+ (depending on permissions) watchlist, trash and admin.
+
+### Search
+
+Type to search for tools live (max. 10 results, incl. rating
+`X.X★`).
+
+## Overview of keyboard shortcuts
+
+| Shortcut | Action |
+| --- | --- |
+| `⌘K` / `Ctrl+K` | Open command palette |
+| `/` | Focus search in the "Browse tools" area |
+
+## Additional notes
+
+- **Recently viewed** is stored locally in the browser (max. 5 entries).
+- The sidebar (left navigation) can be collapsed on desktop; the
+ breadcrumb at the top shows your current location.
diff --git a/docs/releases/v0.8.1/handbook/tool-anlegen.en.md b/docs/releases/v0.8.1/handbook/tool-anlegen.en.md
new file mode 100644
index 0000000..45618cf
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/tool-anlegen.en.md
@@ -0,0 +1,46 @@
+
+# Create a tool
+
+To add a new tool to the catalog, click **Add tool**
+(`/tools/new`). Creating a tool requires an account — without being signed in
+a notice with a login button appears.
+
+## Form fields
+
+| Field | Required | Notes |
+| --- | --- | --- |
+| **Name** | Yes | At least 2 characters |
+| **Category** | Yes | Dropdown; new categories can be created directly |
+| **Website URL** | No | Valid URL (e.g. `https://...`) |
+| **Icon / Logo URL** | No | Valid URL; preview is shown live |
+| **Description** | Yes | At least 10 characters; describe what the tool does |
+| **Features** | No | Dynamic list with autocomplete (max. 6) |
+| **Tags** | No | Dynamic list with autocomplete |
+
+Next to each field, the **? icon** takes you directly to the corresponding
+field description in the [data model reference](/docs/reference/schemas/toolinput).
+
+### Category
+
+- Type to search for existing categories.
+- Select **+ Create "..."** to create a new category.
+
+### Features & tags
+
+- **Add feature** / **Add tag** appends a new row.
+- The input fields suggest existing features/tags
+ (autocomplete, max. 6 suggestions).
+- Use the **×** button to remove individual rows.
+- Features and tags help with filtering and finding tools again.
+
+## Save
+
+Click **Add tool**. After successful creation you will be redirected to the
+detail page of the new tool.
+
+## API
+
+- [`POST /tools`](/docs/reference/endpoints/tools#createTool) — create a tool
+- [`GET /categories`](/docs/reference/endpoints/tools#listCategories) — categories
+- [`GET /features/all`](/docs/reference/endpoints/tools#listAllFeatures) — features
+- [`GET /tags/all`](/docs/reference/endpoints/tools#listAllTags) — tags
diff --git a/docs/releases/v0.8.1/handbook/tool-bearbeiten.en.md b/docs/releases/v0.8.1/handbook/tool-bearbeiten.en.md
new file mode 100644
index 0000000..9921517
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/tool-bearbeiten.en.md
@@ -0,0 +1,32 @@
+
+# Edit & delete tools
+
+## Editing
+
+On the detail page of a tool you will find the **Edit** button
+(only for the person who created the tool, as well as for admins).
+
+The edit page (`/tools/:id/edit`) contains the same fields as when
+creating (name, category, website/icon URL, description, features, tags) —
+already filled with the current values.
+
+- **Save** applies the changes.
+- **Cancel** takes you back to the detail page.
+
+API reference: [`PATCH /tools/{id}`](/docs/reference/endpoints/tools#updateTool).
+
+## Deleting
+
+Via **Delete** on the detail page the tool is removed. The behavior
+depends on your plan:
+
+- **With trash access** (Premium/Enterprise or Admin): the tool is
+ **soft deleted** — it disappears from all public views, but can
+ be restored or permanently deleted in the [trash](/docs/handbook/papierkorb).
+- **Without trash access:** the tool is **permanently** deleted and cannot
+ be restored.
+
+Deletion is only possible for the person who created the tool, as well as
+for admins.
+
+API reference: [`DELETE /tools/{id}`](/docs/reference/endpoints/tools#deleteTool).
diff --git a/docs/releases/v0.8.1/handbook/tools-finden.en.md b/docs/releases/v0.8.1/handbook/tools-finden.en.md
new file mode 100644
index 0000000..6483d44
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/tools-finden.en.md
@@ -0,0 +1,66 @@
+
+# Find & browse tools
+
+The **Browse tools** section (`/tools`) is your entry point to the catalog.
+Here you combine search, filters and sorting to find exactly the tools
+you are interested in.
+
+## Search
+
+- The **search bar** searches name and description (full text).
+- Shortcut: Press **`/`** to focus the search.
+- The input is debounced so that filtering happens immediately with each
+ keystroke.
+
+## Filter
+
+Via the **Filter** button (with a badge for the number of active filters)
+you open the filter popover with:
+
+- **Tags** — selection via checkboxes (scrollable list).
+- **Features** — selection via checkboxes.
+- **Minimum rating** — slider from 0 to 5 (in steps of 0.5); shows
+ e.g. "3.0+".
+
+Active filters appear as **removable chips** above the result list.
+Use **Reset filters** or **Remove all** to clear them again.
+
+## Sort
+
+The **Sort** dropdown offers the following options:
+
+| Sort | Description |
+| --- | --- |
+| Newest | New tools first |
+| Top rated | By combined rating |
+| Most rated | By number of ratings |
+| Name (A–Z) | Alphabetically ascending |
+| Name (Z–A) | Alphabetically descending |
+| Last updated | By last update |
+
+## View & density
+
+- **Switch view:** grid / table / rows.
+- **Density:** comfortable / compact (slider).
+
+Your selection is saved — locally in the browser and, for logged-in users,
+additionally on the server in the preferences. View, density, search, filters
+and sorting are reflected in the URL so you can share results.
+
+## Table view
+
+In the table view the columns **Tool**, **Rating** and **Number of
+ratings** are sortable. Hovering over a row shows a preview
+with rating details, tags and mini bars.
+
+## Selecting for comparison & watchlist
+
+- On every card/row you find a **compare icon** that lets you add tools to the
+ [compare bar](/docs/handbook/vergleichen).
+- The **bookmark icon** saves tools to your
+ [watchlist](/docs/handbook/watchlist) (only with the corresponding plan).
+
+## API
+
+All search, filter and sort parameters correspond to the query parameters of
+[`GET /tools`](/docs/reference/endpoints/tools#listTools).
diff --git a/docs/releases/v0.8.1/handbook/vergleichen.en.md b/docs/releases/v0.8.1/handbook/vergleichen.en.md
new file mode 100644
index 0000000..b2e0635
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/vergleichen.en.md
@@ -0,0 +1,42 @@
+
+# Compare
+
+With the compare function you can put several tools **side by side** —
+ideal for making a well-informed decision.
+
+> Comparing is a **premium feature** (Premium/Enterprise) and is always
+> available to admins.
+
+## Selecting tools
+
+1. In the **Browse tools** section, click the **compare icon** (scales) on
+ each card/row.
+2. The **compare bar** appears at the bottom with the selected tools as
+ chips. You can remove individual tools (×) or clear the selection.
+3. Click **Compare (N)** to go to the compare view.
+
+> Without a premium plan the button is locked (lock icon). The dialog takes
+> you to the plan switch
+> (see [Plans & permissions](/docs/handbook/plaene)).
+
+## The compare view
+
+The view shows a table with one column per tool. Rows:
+
+| Row | Content |
+| --- | --- |
+| **Rating** | Stars + value (e.g. `4.2/5`) |
+| **Usefulness** | Value (X.X/5) |
+| **Usability** | Value (X.X/5) |
+| **Number of ratings** | Count |
+| **Description** | Text |
+| **Features** | Badges |
+| **Tags** | Badges |
+| **Last updated** | Date |
+
+The **best value** per row is highlighted (with trophy icon).
+
+## API
+
+The compare view reads the data via
+[`GET /compare`](/docs/reference/endpoints/tools#listCompareTools).
diff --git a/docs/releases/v0.8.1/handbook/watchlist.en.md b/docs/releases/v0.8.1/handbook/watchlist.en.md
new file mode 100644
index 0000000..55da963
--- /dev/null
+++ b/docs/releases/v0.8.1/handbook/watchlist.en.md
@@ -0,0 +1,33 @@
+
+# Watchlist
+
+The **watchlist** is a personal favorites list. You can open and compare the
+tools in it at any time with a click.
+
+> The watchlist is a **premium feature** (Premium/Enterprise) and is always
+> available to admins.
+
+## Prerequisite
+
+You need a plan with the `watchlist` permission. If it is missing, a note
+about switching plans appears at the bookmark
+(see [Plans & permissions](/docs/handbook/plaene)).
+
+## Saving a tool
+
+- On every card/row in the **Browse tools** section you will find the
+ **bookmark icon**.
+- A click saves the tool to your watchlist — the icon becomes filled.
+- Clicking it again removes it.
+
+## Viewing the watchlist
+
+Open the watchlist via the user menu or the sidebar. It shows all saved tools
+as cards. The filled bookmark on a card removes the tool from the list.
+
+## Where is the watchlist stored?
+
+The watchlist is a list of tool IDs in your **user preferences**. This way it
+is linked to your account across devices.
+
+API reference: [`GET /auth/me/watchlist`](/docs/reference/endpoints/auth#getMeWatchlist).
diff --git a/docs/releases/v0.8.2.en.md b/docs/releases/v0.8.2.en.md
new file mode 100644
index 0000000..d3b3224
--- /dev/null
+++ b/docs/releases/v0.8.2.en.md
@@ -0,0 +1,35 @@
+# v0.8.2 — Release Notes
+
+**Date:** 2026-08-04 · **Tag:** [`v0.8.2`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.2)
+
+## Fixes & improvements
+
+- **Mobile navigation in the docs**: The left navigation bar (manual,
+ endpoints, schemas) was completely hidden below `lg` (1024px).
+ There is now a menu button in the header that opens a side
+ navigation drawer with the same content — on small screens
+ the docs remain fully navigable.
+- **Version dropdown shows the selected version**: When switching to a
+ different release (`/docs/releases/vX.Y.Z`), the dropdown wrongly stayed on
+ "current". The displayed version is now also derived from the releases route.
+
+## API changes
+
+- No changes to the API.
+
+## Operations / upgrade
+
+- **Env vars:** unchanged.
+- **Migration:** none.
+- **Breaking changes:** none.
+
+## Known limitations
+
+- Manual & reference apply to the current version; older versions show
+ their release notes and a reference snapshot, if generated at release time
+ (`node scripts/src/generate-docs.mjs --snapshot vX.Y.Z`).
+
+## Links
+
+- Commit: [`33cf261`](https://git.kubebase.de/admin/tool-evaluator/commit/33cf261)
+- Tag: [`v0.8.2`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.2)
diff --git a/docs/releases/v0.8.2/handbook/administration.en.md b/docs/releases/v0.8.2/handbook/administration.en.md
new file mode 100644
index 0000000..9fe7806
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/administration.en.md
@@ -0,0 +1,61 @@
+
+# Administration
+
+The **Admin** area (`/admin`) is exclusively accessible to admins.
+Without the admin role, access is denied.
+
+> At the top right, the **Redundancy dashboard** button leads to the automatic
+> duplicate detection (see [Redundancy](/docs/handbook/redundanz)).
+
+## "Users" tab
+
+Management of local accounts.
+
+- **Add user:** username (required), password (at least 6 characters),
+ email (optional), **role** (User/Admin), **plan** (Free/Premium/Enterprise).
+- **Edit user:** set role, plan and (for local accounts) a new password.
+ For OIDC accounts, password management is offered in the identity provider
+ (e.g. Keycloak).
+- **Delete user:** permanently removes the account (not for your own account).
+
+API reference:
+[`POST /users`](/docs/reference/endpoints/users#createUser),
+[`PATCH /users/{id}`](/docs/reference/endpoints/users#updateUser),
+[`DELETE /users/{id}`](/docs/reference/endpoints/users#deleteUser).
+
+## "Tools" tab
+
+Central access to the tool catalog.
+
+- **Search** for tools.
+- View, edit or move individual tools to the trash.
+- **Bulk action:** select multiple tools and move them to the trash
+ (confirmation dialog; soft-deleted tools are removed from all public views
+ and can be restored or permanently deleted).
+
+## "Audit log" tab
+
+Chronological log of all creation, change and deletion operations
+(max. 100 entries): action, entity + ID, timestamp, executing person and
+changed fields.
+
+API reference: [`GET /audit-logs`](/docs/reference/endpoints/audit#listAuditLogs).
+
+## "System" tab
+
+Version information of the running instance:
+
+- **Version** (e.g. `v0.8.1`),
+- **Commit** (7-digit SHA, linked to the repository),
+- **Build date**,
+- **Trash retention** ("N days" or "Forever").
+
+## Tool links (Admin)
+
+On the detail page of a tool you can manage **links** as an admin
+(own/"manual" as well as automatically detected ones):
+
+- **Link tool:** dialog with tool ID, **relationship type**
+ (Similar / Replaces / Superseded by) and optional notes.
+- Relationship types are displayed as badges on the detail page.
+- Manual links can be removed again via the trash icon.
diff --git a/docs/releases/v0.8.2/handbook/analytics.en.md b/docs/releases/v0.8.2/handbook/analytics.en.md
new file mode 100644
index 0000000..7450a33
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/analytics.en.md
@@ -0,0 +1,29 @@
+
+# Analytics
+
+The **Analytics** area (`/analytics`) is a public dashboard with
+metrics and charts based on all tools and ratings.
+
+## Metrics (KPI cards)
+
+- **Number of tools** — how many tools are recorded in the catalog.
+- **Number of ratings** — how many ratings were submitted in total.
+- **Active categories** — how many categories exist.
+- **Average rating** — global combined value.
+
+## Charts
+
+| Chart | Content |
+| --- | --- |
+| **Top 8 tools** | Bar chart of the tools with the highest combined score (0–5) |
+| **Tools per category** | Radar chart of the number of tools per category |
+| **Score distribution** | Two horizontal bar charts (usefulness & usability) per star |
+
+The charts are interactive (tooltips on hover).
+
+## API
+
+- [`GET /analytics/summary`](/docs/reference/endpoints/analytics#getAnalyticsSummary)
+- [`GET /analytics/top-tools`](/docs/reference/endpoints/analytics#getTopTools)
+- [`GET /analytics/by-category`](/docs/reference/endpoints/analytics#getAnalyticsByCategory)
+- [`GET /analytics/rating-distribution`](/docs/reference/endpoints/analytics#getRatingDistribution)
diff --git a/docs/releases/v0.8.2/handbook/bewerten.en.md b/docs/releases/v0.8.2/handbook/bewerten.en.md
new file mode 100644
index 0000000..7ccd0c8
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/bewerten.en.md
@@ -0,0 +1,39 @@
+
+# Rating
+
+On the detail page of a tool you can share your experience. Click on
+**Submit a rating** (requires an account).
+
+## Form fields
+
+| Field | Required | Notes |
+| --- | --- | --- |
+| **Usefulness** | Yes | 1–5 stars |
+| **Usability** | Yes | 1–5 stars |
+| **Comment** | No | Free text |
+| **Name** | No | Defaults to "Anonymous" |
+
+Next to the fields, the **? icon** links directly to the associated field
+description in the [data model reference](/docs/reference/schemas/ratinginput).
+
+## What happens after submitting?
+
+- Your rating is saved immediately and appears in the **rating list** of the
+ detail page.
+- The **averages** (usefulness, usability, combined) and the **score
+ distribution** are updated.
+- The **statistics** in the [Analytics](/docs/handbook/analytics) section are
+ recalculated.
+
+## Statistic sections on the detail page
+
+- **Rating overview:** usefulness & usability as an average with progress
+ bars.
+- **Score distribution:** number of ratings per star (1★–5★).
+- **History:** line chart of combined/individual values over time
+ (only visible once there are several ratings).
+
+## API
+
+- [`POST /tools/{id}/ratings`](/docs/reference/endpoints/ratings#createRating) — Submit a rating
+- [`GET /tools/{id}/ratings`](/docs/reference/endpoints/ratings#listToolRatings) — Ratings of a tool
diff --git a/docs/releases/v0.8.2/handbook/datenmodell.en.md b/docs/releases/v0.8.2/handbook/datenmodell.en.md
new file mode 100644
index 0000000..99d602e
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/datenmodell.en.md
@@ -0,0 +1,70 @@
+
+# Data model
+
+This chapter explains the central data objects of toolr at the application
+level. The complete, automatically generated reference of all fields,
+types and constraints can be found in the
+[API reference](/docs/reference/schemas/tool).
+
+## Tool
+
+The heart of it all: a tool recorded in the catalog.
+
+| Property | Description |
+| --- | --- |
+| `id` | Unique identifier |
+| `name` | Display name |
+| `description` | Description (what does the tool do?) |
+| `category` | Category assignment |
+| `websiteUrl` | Official website (optional) |
+| `iconUrl` | Logo/icon URL (optional) |
+| `features` | List of capabilities |
+| `tags` | List of keywords |
+| `createdAt` / `updatedAt` | Timestamps |
+| `createdBy` | Person who created it |
+| `deletedAt` / `deletedBy` | Soft deletion (trash) |
+
+Input forms use the derived schemas
+[`ToolInput`](/docs/reference/schemas/toolinput) and
+[`ToolUpdate`](/docs/reference/schemas/toolupdate).
+Aggregated views are provided by [`ToolWithStats`](/docs/reference/schemas/toolwithstats)
+(e.g. with average rating).
+
+## Rating (Bewertung)
+
+A single rating for a tool:
+
+- `usefulness` (Nützlichkeit, 1–5) and `usability` (Bedienbarkeit, 1–5)
+- optional `comment` and a display name (`reviewerName`)
+- timestamp
+
+Input schema: [`RatingInput`](/docs/reference/schemas/ratinginput).
+
+## User & Auth
+
+- [`User`](/docs/reference/schemas/user) — user account with role (User/Admin)
+ and plan (Free/Premium/Enterprise).
+- [`AuthUser`](/docs/reference/schemas/authuser) — the logged-in profile
+ including `entitlements` (available features).
+- [`UserPreferences`](/docs/reference/schemas/userpreferences) — view and
+ density preferences as well as the `watchlist` (list of tool IDs).
+
+## Analytics
+
+The statistics endpoints provide aggregated data:
+
+- [`AnalyticsSummary`](/docs/reference/schemas/analyticssummary) — global
+ metrics (number of tools/ratings, categories, average).
+- [`TopToolEntry`](/docs/reference/schemas/TopToolEntry) — an entry of the
+ top tools.- [`CategoryStats`](/docs/reference/schemas/categorystats) — number of tools per
+ category.
+- [`RatingDistribution`](/docs/reference/schemas/ratingdistribution) —
+ score distribution (usefulness & usability).
+- [`ScoreBucket`](/docs/reference/schemas/scorebucket) — a value bucket.
+
+## Additional
+
+- [`VersionInfo`](/docs/reference/schemas/versioninfo) — version, commit SHA,
+ build date and trash retention of the running instance.
+- [`AuditLog`](/docs/reference/schemas/auditlog) — a log entry
+ (action, entity, timestamp, actor, changes).
diff --git a/docs/releases/v0.8.2/handbook/getting-started.en.md b/docs/releases/v0.8.2/handbook/getting-started.en.md
new file mode 100644
index 0000000..98344ef
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/getting-started.en.md
@@ -0,0 +1,53 @@
+
+# Getting Started
+
+This page guides you through the most important workflows in toolr — from your
+first visit to creating and rating a tool.
+
+## 1. Sign in
+
+Most actions (create a tool, rate, watchlist, compare) require
+an account. Click **Sign in** in the bottom left corner. Depending on the
+instance configuration you have two options:
+
+- **Local accounts:** username + password. Access is created by an admin
+ (see [Administration](/docs/handbook/administration)).
+- **OIDC (SSO):** sign in with the configured identity provider (e.g.
+ Keycloak).
+
+Which mode is active is shown by the
+[`GET /auth/mode`](/docs/reference/endpoints/auth#getAuthMode) endpoint. Details
+can be found in the [Sign in & account](/docs/handbook/konto) section.
+
+## 2. Find tools
+
+Open the **Browse tools** section:
+
+- **Search** — full-text search across name & description (shortcut `/`).
+- **Filter** — by category, tags, features and minimum rating
+ (`minRating`).
+- **Sort** — by newest, top-rated, most rated, name
+ (ascending/descending) or last update.
+
+All options in detail: [Find & browse tools](/docs/handbook/tools-finden).
+
+## 3. Create a tool
+
+Go to **Add tool** and fill in the form. Details for each field can be found
+in the [Create a tool](/docs/handbook/tool-anlegen) section and in the
+[field reference](/docs/reference/schemas/toolinput).
+
+## 4. Rate
+
+On the detail page of a tool you can submit **usefulness** and **usability**
+(1–5 each) and optionally leave a comment. Your
+rating is immediately reflected in the statistics.
+See [Rating](/docs/handbook/bewerten).
+
+## 5. Further reading
+
+- [Compare tools](/docs/handbook/vergleichen)
+- [Watchlist](/docs/handbook/watchlist)
+- [Analytics](/docs/handbook/analytics)
+- [Plans & permissions](/docs/handbook/plaene)
+- [Administration](/docs/handbook/administration)
diff --git a/docs/releases/v0.8.2/handbook/index.en.md b/docs/releases/v0.8.2/handbook/index.en.md
new file mode 100644
index 0000000..490c2c5
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/index.en.md
@@ -0,0 +1,51 @@
+
+# Welcome to toolr
+
+toolr is a platform for **discovering, rating and comparing development
+tools**. Users maintain a shared catalog of tools, submit ratings
+(usefulness & usability) and use statistics to make the right choice.
+
+## What can you do with toolr?
+
+| Function | Description | Visibility |
+| --- | --- | --- |
+| **Browse tools** | Filter, sort and search the catalog | Everyone |
+| **Create a tool** | Add a new tool with description, category, features & tags | Logged in |
+| **Edit/delete tools** | Maintain your own tools (creator or admin) | Logged in |
+| **Rate** | Submit usefulness & usability (1–5) plus a comment | Logged in |
+| **Watchlist** | Save tools as favorites | Premium |
+| **Compare** | View tools side by side | Premium |
+| **Record costs** | Enter license and cost models per tool | Premium |
+| **Analytics** | Statistics, top tools, distributions | Everyone |
+| **Trash** | Restore soft-deleted tools or delete them permanently | Premium |
+| **Admin** | User management, audit log, system information | Admin |
+| **Redundancy detection** | Automatic duplicate detection | Admin |
+
+## How this documentation is organized
+
+- **User Guide** (these pages): step-by-step instructions for all
+ functions — from the [Getting Started](/docs/handbook/getting-started) to
+ [Administration](/docs/handbook/administration).
+- **API Reference**: automatically generated from the OpenAPI specification —
+ all [endpoints](/docs/reference/endpoints/tools) and
+ [data fields](/docs/reference/schemas/toolinput) of the current version.
+- **Release Notes**: what is new in each [version](/docs/releases/v0.8.1).
+
+## Getting started
+
+The fastest way:
+
+1. **Sign in** — without an account you can only browse
+ (see [Getting Started](/docs/handbook/getting-started#1-anmelden)).
+2. **Find tools** — search, filters and sorting in the
+ [Browse tools](/docs/handbook/tools-finden) section.
+3. **Create a tool** — via "Add tool"
+ ([guide](/docs/handbook/tool-anlegen)).
+4. **Rate** — on the detail page of a tool
+ ([guide](/docs/handbook/bewerten)).
+
+## Contact & source code
+
+The source code is available at
+[git.kubebase.de/admin/tool-evaluator](https://git.kubebase.de/admin/tool-evaluator) —
+you can reach it at any time via the repository icon in the top right corner.
diff --git a/docs/releases/v0.8.2/handbook/index.json b/docs/releases/v0.8.2/handbook/index.json
index 9379656..4dc0aa7 100644
--- a/docs/releases/v0.8.2/handbook/index.json
+++ b/docs/releases/v0.8.2/handbook/index.json
@@ -3,102 +3,136 @@
"slug": "index",
"file": "index.md",
"title": "Überblick",
- "order": 1
+ "order": 1,
+ "fileEn": "index.en.md",
+ "titleEn": "Overview"
},
{
"slug": "getting-started",
"file": "getting-started.md",
"title": "Erste Schritte",
- "order": 2
+ "order": 2,
+ "fileEn": "getting-started.en.md",
+ "titleEn": "Getting Started"
},
{
"slug": "konto",
"file": "konto.md",
"title": "Anmelden & Konto",
- "order": 3
+ "order": 3,
+ "fileEn": "konto.en.md",
+ "titleEn": "Login & Account"
},
{
"slug": "tools-finden",
"file": "tools-finden.md",
"title": "Tools finden & durchsuchen",
- "order": 4
+ "order": 4,
+ "fileEn": "tools-finden.en.md",
+ "titleEn": "Find & browse tools"
},
{
"slug": "tool-anlegen",
"file": "tool-anlegen.md",
"title": "Tool anlegen",
- "order": 5
+ "order": 5,
+ "fileEn": "tool-anlegen.en.md",
+ "titleEn": "Create a tool"
},
{
"slug": "tool-bearbeiten",
"file": "tool-bearbeiten.md",
"title": "Tool bearbeiten & löschen",
- "order": 6
+ "order": 6,
+ "fileEn": "tool-bearbeiten.en.md",
+ "titleEn": "Edit & delete tools"
},
{
"slug": "bewerten",
"file": "bewerten.md",
"title": "Bewerten",
- "order": 7
+ "order": 7,
+ "fileEn": "bewerten.en.md",
+ "titleEn": "Rating"
},
{
"slug": "watchlist",
"file": "watchlist.md",
"title": "Watchlist",
- "order": 8
+ "order": 8,
+ "fileEn": "watchlist.en.md",
+ "titleEn": "Watchlist"
},
{
"slug": "vergleichen",
"file": "vergleichen.md",
"title": "Vergleichen",
- "order": 9
+ "order": 9,
+ "fileEn": "vergleichen.en.md",
+ "titleEn": "Compare"
},
{
"slug": "analytics",
"file": "analytics.md",
"title": "Analytics",
- "order": 10
+ "order": 10,
+ "fileEn": "analytics.en.md",
+ "titleEn": "Analytics"
},
{
"slug": "plaene",
"file": "plaene.md",
"title": "Pläne & Berechtigungen",
- "order": 11
+ "order": 11,
+ "fileEn": "plaene.en.md",
+ "titleEn": "Plans & Permissions"
},
{
"slug": "kosten",
"file": "kosten.md",
"title": "Kosten erfassen",
- "order": 12
+ "order": 12,
+ "fileEn": "kosten.en.md",
+ "titleEn": "Recording Costs"
},
{
"slug": "administration",
"file": "administration.md",
"title": "Administration",
- "order": 13
+ "order": 13,
+ "fileEn": "administration.en.md",
+ "titleEn": "Administration"
},
{
"slug": "redundanz",
"file": "redundanz.md",
"title": "Redundanz-Dashboard",
- "order": 14
+ "order": 14,
+ "fileEn": "redundanz.en.md",
+ "titleEn": "Redundancy dashboard"
},
{
"slug": "papierkorb",
"file": "papierkorb.md",
"title": "Papierkorb",
- "order": 15
+ "order": 15,
+ "fileEn": "papierkorb.en.md",
+ "titleEn": "Trash"
},
{
"slug": "tastatur",
"file": "tastatur.md",
"title": "Tastenkürzel & Kommandopalette",
- "order": 16
+ "order": 16,
+ "fileEn": "tastatur.en.md",
+ "titleEn": "Keyboard shortcuts & command palette"
},
{
"slug": "datenmodell",
"file": "datenmodell.md",
"title": "Datenmodell",
- "order": 17
+ "order": 17,
+ "fileEn": "datenmodell.en.md",
+ "titleEn": "Data model"
}
]
\ No newline at end of file
diff --git a/docs/releases/v0.8.2/handbook/konto.en.md b/docs/releases/v0.8.2/handbook/konto.en.md
new file mode 100644
index 0000000..22360df
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/konto.en.md
@@ -0,0 +1,52 @@
+
+# Login & Account
+
+## Logging in
+
+Click **Login** at the bottom left of the sidebar. Depending on the
+configuration of the instance:
+
+- **Local accounts:** enter username and password. The accounts are created
+ by an admin (see [Administration](/docs/handbook/administration)).
+- **OIDC (SSO):** you are redirected to the configured identity provider and
+ log in there.
+
+The active mode is available at the endpoint
+[`GET /auth/mode`](/docs/reference/endpoints/auth#getAuthMode).
+
+> You can reach the login page directly at `/login`. After a successful
+> login you are redirected back to the page you originally requested.
+
+## User profile
+
+You can see your profile (avatar, name, email, plan) at the bottom left in
+the user menu. There you have the following actions available:
+
+- **Watchlist** — your saved tools (only with the corresponding plan).
+- **Trash** — restorable, deleted tools (Premium/Enterprise).
+- **Change password** — directly in toolr for local accounts; for OIDC
+ accounts, password management is offered in the identity provider.
+- **Logout** — ends your session.
+
+## Changing your password (local account)
+
+1. Open the user menu at the bottom left.
+2. Select **Change password**.
+3. Enter the **current** and a **new** password (min. 6 characters) and
+ confirm it.
+4. Save — the password takes effect immediately.
+
+API reference: [`POST /auth/me/password`](/docs/reference/endpoints/auth#changeMyPassword).
+
+## Display settings
+
+Using the buttons at the top right you can:
+
+- switch the **language** (German / English),
+- toggle the **theme** (Light / Dark / System),
+- adjust the **list view** and **density** in the Browse tools section
+ (see [Finding & browsing tools](/docs/handbook/tools-finden)).
+
+Your preferences (incl. watchlist) are saved at the endpoint
+[`GET /auth/me/preferences`](/docs/reference/endpoints/auth#getMePreferences)
+and updated via [`PUT /auth/me/preferences`](/docs/reference/endpoints/auth#updateMePreferences).
diff --git a/docs/releases/v0.8.2/handbook/kosten.en.md b/docs/releases/v0.8.2/handbook/kosten.en.md
new file mode 100644
index 0000000..1cf7a3a
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/kosten.en.md
@@ -0,0 +1,35 @@
+
+# Recording Costs
+
+On the detail page of a tool you can enter cost and license models so that
+the total costs per tool become transparent.
+
+> Costs is a **premium feature** (`costs`, Premium/Enterprise). Admins always
+> have access.
+
+## Adding costs
+
+Click **Add costs** in the costs section of the detail page and fill out the
+form:
+
+| Field | Notes |
+| --- | --- |
+| **License type** | Free / Subscription / One-Time / Usage-Based |
+| **Billing period** | Only for "Subscription": Monthly / Quarterly / Annually |
+| **Costs** | Amount as a number |
+| **Currency** | EUR / USD / GBP / CHF |
+| **Notes** | Optional free text |
+
+Saving creates the entry. Each cost entry is displayed as a card with
+license badge, billing period, amount (`Amount Currency` or "Free") and
+notes.
+
+## Editing & deleting costs
+
+Hovering over a cost card shows the **Edit** (pencil) and **Delete** (trash)
+actions.
+
+## API
+
+The cost data is managed via the tool endpoints
+(see [API reference](/docs/reference/endpoints/tools)).
diff --git a/docs/releases/v0.8.2/handbook/papierkorb.en.md b/docs/releases/v0.8.2/handbook/papierkorb.en.md
new file mode 100644
index 0000000..7111def
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/papierkorb.en.md
@@ -0,0 +1,40 @@
+
+# Trash
+
+The **trash** (`/trash`) contains soft-deleted tools. With trash access
+they can be restored; permanent deletion is reserved for admins.
+
+> The trash is a **premium feature** (`trash`, Premium/Enterprise).
+> Admins always have access.
+
+## Access
+
+The trash can be reached via the user menu or the sidebar.
+Without the `trash` permission, a hint about changing the plan appears.
+
+## Restoring
+
+- Select one or more tools (checkboxes).
+- Click on **Restore (N)** — the tools appear again in all
+ public views.
+
+> Restoring is available to anyone with trash access.
+
+## Permanently delete (admin only)
+
+- **Delete (N)** **permanently** removes the selected tools — including
+ all ratings, costs and links. This cannot be undone.
+- **Empty trash** permanently removes all soft-deleted tools.
+
+## Table
+
+The trash lists: name, category, **Deleted on** (`tt.MM.jjjj HH:mm`),
+**Deleted by** as well as actions (Restore; Delete admin only). The search
+filters by name.
+
+## API
+
+- [`GET /tools/trash`](/docs/reference/endpoints/tools#listTrashedTools) — list
+- [`POST /tools/trash/restore`](/docs/reference/endpoints/tools#restoreTools) — restore
+- [`DELETE /tools/trash`](/docs/reference/endpoints/tools#deleteTrashedTools) — permanently delete (admin)
+- [`POST /tools/trash/empty`](/docs/reference/endpoints/tools#emptyTrash) — empty trash (admin)
diff --git a/docs/releases/v0.8.2/handbook/plaene.en.md b/docs/releases/v0.8.2/handbook/plaene.en.md
new file mode 100644
index 0000000..f73d6dc
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/plaene.en.md
@@ -0,0 +1,41 @@
+
+# Plans & Permissions
+
+toolr distinguishes **plans** (tier) and **roles**. Admins bypass all feature
+restrictions.
+
+## Plans
+
+| Plan | Description |
+| --- | --- |
+| **Free** | Basic functions: search, filter, view, analytics |
+| **Premium** | Additionally watchlist, compare, trash, costs |
+| **Enterprise** | All premium features + extended support |
+
+### Feature permissions
+
+Premium/Enterprise unlock the following features:
+
+| Feature | Function | Learn more |
+| --- | --- | --- |
+| `compare` | Compare tools | [Compare](/docs/handbook/vergleichen) |
+| `watchlist` | Favorites list | [Watchlist](/docs/handbook/watchlist) |
+| `trash` | Trash (soft-deleted tools) | [Trash](/docs/handbook/papierkorb) |
+| `costs` | Record cost/license models | [Recording costs](/docs/handbook/kosten) |
+
+If you are missing a feature, the app shows an **upgrade notice** with a link
+to the plan management.
+
+## Roles
+
+| Role | Permissions |
+| --- | --- |
+| **User** | Standard account: create/rate tools, edit your own tools |
+| **Admin** | All user rights + administration, audit log, redundancy, empty trash, tool links |
+
+Admins pass **all** feature checks — even without a premium plan.
+
+## Plan/role management
+
+The assignment of role and plan is managed by admins in the
+[Administration](/docs/handbook/administration) section (tab "Users").
diff --git a/docs/releases/v0.8.2/handbook/redundanz.en.md b/docs/releases/v0.8.2/handbook/redundanz.en.md
new file mode 100644
index 0000000..27825b3
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/redundanz.en.md
@@ -0,0 +1,38 @@
+
+# Redundancy dashboard
+
+The **Redundancy dashboard** (`/admin/redundancy`) is an admin tool for
+automatic detection of duplicate or strongly overlapping tools — per
+category — including cost and rating comparison.
+
+> Access is reserved exclusively for admins (the API is
+> admin-protected).
+
+## Layout
+
+- **Per category** a group is shown: name of the category,
+ number of tools and comparisons as well as the **total monthly costs**
+ if applicable (e.g. `€X.XX/mo total`).
+- Each tool is displayed as a card: name, monthly costs, number of
+ ratings, combined rating, license badges and number of features.
+
+## Comparisons & recommendations
+
+For each tool pair the following appears:
+
+- Tool A vs. Tool B, each with rating (`X.X ★`) and monthly costs.
+- **Overlap** in percent (progress bar in the middle).
+- A **recommendation** with confidence color:
+ - **high** (green), **medium** (yellow), **low** (gray)
+- The recommended, better tool is marked with a "thumbs up" and justified.
+
+## Manual rating
+
+You can rate a pair manually: click on Tool A or Tool B to
+record which one is better. The selection is saved and the
+display is updated.
+
+## API
+
+- [`GET /api/admin/redundancy`](#) — load data (admin-protected)
+- [`POST /api/admin/redundancy/evaluate`](#) — save manual rating
diff --git a/docs/releases/v0.8.2/handbook/tastatur.en.md b/docs/releases/v0.8.2/handbook/tastatur.en.md
new file mode 100644
index 0000000..2eddf47
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/tastatur.en.md
@@ -0,0 +1,36 @@
+
+# Keyboard shortcuts & command palette
+
+## Command palette
+
+The command palette is the central quick navigation:
+
+- Open with **`⌘K`** (macOS) or **`Ctrl+K`** (Windows/Linux).
+- Alternatively via the search bar at the top right ("Search tools… ⌘K") or the
+ search icon on mobile devices.
+
+### Empty state
+
+Without input, the palette shows:
+
+- **Recently viewed** — the last 5 tools you visited.
+- **Navigation** — browse tools, add tool, analytics as well as
+ (depending on permissions) watchlist, trash and admin.
+
+### Search
+
+Type to search for tools live (max. 10 results, incl. rating
+`X.X★`).
+
+## Overview of keyboard shortcuts
+
+| Shortcut | Action |
+| --- | --- |
+| `⌘K` / `Ctrl+K` | Open command palette |
+| `/` | Focus search in the "Browse tools" area |
+
+## Additional notes
+
+- **Recently viewed** is stored locally in the browser (max. 5 entries).
+- The sidebar (left navigation) can be collapsed on desktop; the
+ breadcrumb at the top shows your current location.
diff --git a/docs/releases/v0.8.2/handbook/tool-anlegen.en.md b/docs/releases/v0.8.2/handbook/tool-anlegen.en.md
new file mode 100644
index 0000000..45618cf
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/tool-anlegen.en.md
@@ -0,0 +1,46 @@
+
+# Create a tool
+
+To add a new tool to the catalog, click **Add tool**
+(`/tools/new`). Creating a tool requires an account — without being signed in
+a notice with a login button appears.
+
+## Form fields
+
+| Field | Required | Notes |
+| --- | --- | --- |
+| **Name** | Yes | At least 2 characters |
+| **Category** | Yes | Dropdown; new categories can be created directly |
+| **Website URL** | No | Valid URL (e.g. `https://...`) |
+| **Icon / Logo URL** | No | Valid URL; preview is shown live |
+| **Description** | Yes | At least 10 characters; describe what the tool does |
+| **Features** | No | Dynamic list with autocomplete (max. 6) |
+| **Tags** | No | Dynamic list with autocomplete |
+
+Next to each field, the **? icon** takes you directly to the corresponding
+field description in the [data model reference](/docs/reference/schemas/toolinput).
+
+### Category
+
+- Type to search for existing categories.
+- Select **+ Create "..."** to create a new category.
+
+### Features & tags
+
+- **Add feature** / **Add tag** appends a new row.
+- The input fields suggest existing features/tags
+ (autocomplete, max. 6 suggestions).
+- Use the **×** button to remove individual rows.
+- Features and tags help with filtering and finding tools again.
+
+## Save
+
+Click **Add tool**. After successful creation you will be redirected to the
+detail page of the new tool.
+
+## API
+
+- [`POST /tools`](/docs/reference/endpoints/tools#createTool) — create a tool
+- [`GET /categories`](/docs/reference/endpoints/tools#listCategories) — categories
+- [`GET /features/all`](/docs/reference/endpoints/tools#listAllFeatures) — features
+- [`GET /tags/all`](/docs/reference/endpoints/tools#listAllTags) — tags
diff --git a/docs/releases/v0.8.2/handbook/tool-bearbeiten.en.md b/docs/releases/v0.8.2/handbook/tool-bearbeiten.en.md
new file mode 100644
index 0000000..9921517
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/tool-bearbeiten.en.md
@@ -0,0 +1,32 @@
+
+# Edit & delete tools
+
+## Editing
+
+On the detail page of a tool you will find the **Edit** button
+(only for the person who created the tool, as well as for admins).
+
+The edit page (`/tools/:id/edit`) contains the same fields as when
+creating (name, category, website/icon URL, description, features, tags) —
+already filled with the current values.
+
+- **Save** applies the changes.
+- **Cancel** takes you back to the detail page.
+
+API reference: [`PATCH /tools/{id}`](/docs/reference/endpoints/tools#updateTool).
+
+## Deleting
+
+Via **Delete** on the detail page the tool is removed. The behavior
+depends on your plan:
+
+- **With trash access** (Premium/Enterprise or Admin): the tool is
+ **soft deleted** — it disappears from all public views, but can
+ be restored or permanently deleted in the [trash](/docs/handbook/papierkorb).
+- **Without trash access:** the tool is **permanently** deleted and cannot
+ be restored.
+
+Deletion is only possible for the person who created the tool, as well as
+for admins.
+
+API reference: [`DELETE /tools/{id}`](/docs/reference/endpoints/tools#deleteTool).
diff --git a/docs/releases/v0.8.2/handbook/tools-finden.en.md b/docs/releases/v0.8.2/handbook/tools-finden.en.md
new file mode 100644
index 0000000..6483d44
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/tools-finden.en.md
@@ -0,0 +1,66 @@
+
+# Find & browse tools
+
+The **Browse tools** section (`/tools`) is your entry point to the catalog.
+Here you combine search, filters and sorting to find exactly the tools
+you are interested in.
+
+## Search
+
+- The **search bar** searches name and description (full text).
+- Shortcut: Press **`/`** to focus the search.
+- The input is debounced so that filtering happens immediately with each
+ keystroke.
+
+## Filter
+
+Via the **Filter** button (with a badge for the number of active filters)
+you open the filter popover with:
+
+- **Tags** — selection via checkboxes (scrollable list).
+- **Features** — selection via checkboxes.
+- **Minimum rating** — slider from 0 to 5 (in steps of 0.5); shows
+ e.g. "3.0+".
+
+Active filters appear as **removable chips** above the result list.
+Use **Reset filters** or **Remove all** to clear them again.
+
+## Sort
+
+The **Sort** dropdown offers the following options:
+
+| Sort | Description |
+| --- | --- |
+| Newest | New tools first |
+| Top rated | By combined rating |
+| Most rated | By number of ratings |
+| Name (A–Z) | Alphabetically ascending |
+| Name (Z–A) | Alphabetically descending |
+| Last updated | By last update |
+
+## View & density
+
+- **Switch view:** grid / table / rows.
+- **Density:** comfortable / compact (slider).
+
+Your selection is saved — locally in the browser and, for logged-in users,
+additionally on the server in the preferences. View, density, search, filters
+and sorting are reflected in the URL so you can share results.
+
+## Table view
+
+In the table view the columns **Tool**, **Rating** and **Number of
+ratings** are sortable. Hovering over a row shows a preview
+with rating details, tags and mini bars.
+
+## Selecting for comparison & watchlist
+
+- On every card/row you find a **compare icon** that lets you add tools to the
+ [compare bar](/docs/handbook/vergleichen).
+- The **bookmark icon** saves tools to your
+ [watchlist](/docs/handbook/watchlist) (only with the corresponding plan).
+
+## API
+
+All search, filter and sort parameters correspond to the query parameters of
+[`GET /tools`](/docs/reference/endpoints/tools#listTools).
diff --git a/docs/releases/v0.8.2/handbook/vergleichen.en.md b/docs/releases/v0.8.2/handbook/vergleichen.en.md
new file mode 100644
index 0000000..b2e0635
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/vergleichen.en.md
@@ -0,0 +1,42 @@
+
+# Compare
+
+With the compare function you can put several tools **side by side** —
+ideal for making a well-informed decision.
+
+> Comparing is a **premium feature** (Premium/Enterprise) and is always
+> available to admins.
+
+## Selecting tools
+
+1. In the **Browse tools** section, click the **compare icon** (scales) on
+ each card/row.
+2. The **compare bar** appears at the bottom with the selected tools as
+ chips. You can remove individual tools (×) or clear the selection.
+3. Click **Compare (N)** to go to the compare view.
+
+> Without a premium plan the button is locked (lock icon). The dialog takes
+> you to the plan switch
+> (see [Plans & permissions](/docs/handbook/plaene)).
+
+## The compare view
+
+The view shows a table with one column per tool. Rows:
+
+| Row | Content |
+| --- | --- |
+| **Rating** | Stars + value (e.g. `4.2/5`) |
+| **Usefulness** | Value (X.X/5) |
+| **Usability** | Value (X.X/5) |
+| **Number of ratings** | Count |
+| **Description** | Text |
+| **Features** | Badges |
+| **Tags** | Badges |
+| **Last updated** | Date |
+
+The **best value** per row is highlighted (with trophy icon).
+
+## API
+
+The compare view reads the data via
+[`GET /compare`](/docs/reference/endpoints/tools#listCompareTools).
diff --git a/docs/releases/v0.8.2/handbook/watchlist.en.md b/docs/releases/v0.8.2/handbook/watchlist.en.md
new file mode 100644
index 0000000..55da963
--- /dev/null
+++ b/docs/releases/v0.8.2/handbook/watchlist.en.md
@@ -0,0 +1,33 @@
+
+# Watchlist
+
+The **watchlist** is a personal favorites list. You can open and compare the
+tools in it at any time with a click.
+
+> The watchlist is a **premium feature** (Premium/Enterprise) and is always
+> available to admins.
+
+## Prerequisite
+
+You need a plan with the `watchlist` permission. If it is missing, a note
+about switching plans appears at the bookmark
+(see [Plans & permissions](/docs/handbook/plaene)).
+
+## Saving a tool
+
+- On every card/row in the **Browse tools** section you will find the
+ **bookmark icon**.
+- A click saves the tool to your watchlist — the icon becomes filled.
+- Clicking it again removes it.
+
+## Viewing the watchlist
+
+Open the watchlist via the user menu or the sidebar. It shows all saved tools
+as cards. The filled bookmark on a card removes the tool from the list.
+
+## Where is the watchlist stored?
+
+The watchlist is a list of tool IDs in your **user preferences**. This way it
+is linked to your account across devices.
+
+API reference: [`GET /auth/me/watchlist`](/docs/reference/endpoints/auth#getMeWatchlist).
diff --git a/docs/releases/v0.8.3.en.md b/docs/releases/v0.8.3.en.md
new file mode 100644
index 0000000..8998402
--- /dev/null
+++ b/docs/releases/v0.8.3.en.md
@@ -0,0 +1,49 @@
+# v0.8.3 — Release Notes
+
+**Date:** 2026-08-04 · **Tag:** [`v0.8.3`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.3)
+
+## New features
+
+- **Version-based documentation**: Each release now carries a
+ complete docs snapshot (manual + API reference). Via the
+ version dropdown you can reach the docs of the respective version
+ (`/docs/vX.Y.Z/…`) — including the manual and reference as they
+ applied at release time. The last 7 versions remain available.
+- **Help buttons in forms (NetBox style)**: For evaluation, tool
+ create/edit, costs, comparing, watchlist and analytics there is now
+ a help icon that leads directly to the matching guide in the manual.
+
+## Fixes & improvements
+
+- **Gitea tag links fixed**: `…/tags/vX.Y.Z` was a 404 page; the
+ correct URL is `/releases/tag/vX.Y.Z` (in all release notes and the
+ docs page).
+- Version switching now leads to the docs of the selected version instead of
+ just the release page; old `/docs/vX.Y.Z` redirects are removed.
+- Docs generator: `--snapshot` creates manual + reference per version; on
+ build, the snapshots of the last 7 versions are included.
+
+## API changes
+
+- No changes to the API.
+
+## Operations / upgrade
+
+- **Env vars:** unchanged.
+- **Migration:** none.
+- **Breaking changes:** none. New release notes must use the correct
+ tag link (see `docs/releases/TEMPLATE.md`). For versions before
+ this release, snapshots can be created retroactively:
+ `node scripts/src/generate-docs.mjs --snapshot vX.Y.Z`.
+
+## Known limitations
+
+- Older releases without a snapshot (e.g. v0.6.0) only show their release notes,
+ no manual/reference. Backfill with `--snapshot`.
+- The docs version corresponds to the state of the docs at the time the
+ snapshot was created; manual changes apply from the respective next release.
+
+## Links
+
+- Commit: [`3bb4598`](https://git.kubebase.de/admin/tool-evaluator/commit/3bb4598)
+- Tag: [`v0.8.3`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.3)
diff --git a/scripts/src/generate-docs.mjs b/scripts/src/generate-docs.mjs
index 758cf8b..4bc6270 100644
--- a/scripts/src/generate-docs.mjs
+++ b/scripts/src/generate-docs.mjs
@@ -26,7 +26,12 @@ const releasesDir = resolve(root, "docs/releases");
const targetDir = resolve(root, "artifacts/toolrate/public/docs");
const isReleaseFile = (name) => /^v\d+\.\d+\.\d+\.md$/.test(name);
-const isHandbookFile = (name) => /\.md$/.test(name);
+const isEnReleaseFile = (name) => /^v\d+\.\d+\.\d+\.en\.md$/.test(name);
+const isHandbookFile = (name) => /\.md$/.test(name) && !name.endsWith(".en.md");
+const isEnHandbookFile = (name) => /\.en\.md$/.test(name);
+
+const enName = (file) => (file.endsWith(".md") ? `${file.slice(0, -3)}.en.md` : null);
+const deName = (file) => (file.endsWith(".en.md") ? `${file.slice(0, -6)}.md` : null);
// ---------------------------------------------------------------------------
// Version helpers
@@ -196,6 +201,15 @@ function slugify(name) {
return name.replace(/\.md$/, "").toLowerCase().replace(/[^a-z0-9-]+/g, "-");
}
+async function readFileOptional(path) {
+ try {
+ return await readFile(path, "utf8");
+ } catch (err) {
+ if (err.code === "ENOENT") return null;
+ throw err;
+ }
+}
+
async function readHandbookPages() {
let files;
try {
@@ -208,12 +222,18 @@ async function readHandbookPages() {
for (const file of files) {
const content = await readFile(join(handbookDir, file), "utf8");
const { frontmatter, body } = parseHandbook(content);
+ const enFile = enName(file);
+ const enContent = enFile ? await readFileOptional(join(handbookDir, enFile)) : null;
+ const en = enContent ? parseHandbook(enContent) : null;
pages.push({
slug: slugify(basename(file)),
file,
title: frontmatter.title ?? extractTitle(body) ?? basename(file),
order: typeof frontmatter.order === "number" ? frontmatter.order : 999,
body,
+ fileEn: en ? enFile : null,
+ titleEn: en ? en.frontmatter.title ?? extractTitle(en.body) ?? null : null,
+ bodyEn: en ? en.body : null,
});
}
pages.sort((a, b) => a.order - b.order || a.title.localeCompare(b.title));
@@ -225,11 +245,12 @@ async function writeHandbookTo(dir) {
await mkdir(dir, { recursive: true });
for (const page of pages) {
await writeFile(join(dir, page.file), page.body);
+ if (page.bodyEn) await writeFile(join(dir, page.fileEn), page.bodyEn);
}
await writeFile(
join(dir, "index.json"),
JSON.stringify(
- pages.map(({ slug, file, title, order }) => ({ slug, file, title, order })),
+ pages.map(({ slug, file, title, order, fileEn, titleEn }) => ({ slug, file, title, order, fileEn, titleEn })),
null,
2,
),
@@ -256,6 +277,7 @@ function stripMarkdown(md) {
async function buildSearchIndex(reference, handbookPages, releaseVersions) {
const entries = [];
+ const entriesEn = [];
for (const page of handbookPages) {
const file = join(handbookDir, page.file);
@@ -267,44 +289,58 @@ async function buildSearchIndex(reference, handbookPages, releaseVersions) {
kind: "guide",
text: stripMarkdown(body),
});
+ if (page.bodyEn) {
+ entriesEn.push({
+ title: page.titleEn ?? page.title,
+ href: `/docs/handbook/${page.slug}`,
+ kind: "guide",
+ text: stripMarkdown(page.bodyEn),
+ });
+ }
}
for (const tag of reference.tags) {
for (const ep of tag.endpoints) {
- entries.push({
+ const entry = {
title: `${ep.method} ${ep.path}`,
href: `/docs/reference/endpoints/${tag.name}#${ep.operationId}`,
kind: "endpoint",
text: `${ep.summary} ${ep.description} ${ep.parameters
.map((p) => `${p.name} ${p.description}`)
.join(" ")}`.trim(),
- });
+ };
+ entries.push(entry);
+ entriesEn.push(entry);
}
}
for (const schema of reference.schemas) {
for (const field of schema.fields) {
- entries.push({
+ const entry = {
title: `${schema.name}.${field.name}`,
href: `/docs/reference/schemas/${schema.name}#${field.name}`,
kind: "field",
text: `${field.description} ${field.constraints} ${field.type.value ?? ""}`.trim(),
- });
+ };
+ entries.push(entry);
+ entriesEn.push(entry);
}
}
for (const version of releaseVersions) {
const file = join(releasesDir, `${version}.md`);
const content = await readFile(file, "utf8");
- entries.push({
+ const base = {
title: version,
href: `/docs/releases/${version}`,
kind: "release",
- text: stripMarkdown(content),
- });
+ };
+ entries.push({ ...base, text: stripMarkdown(content) });
+ const enContent = await readFileOptional(join(releasesDir, `${version}.en.md`));
+ entriesEn.push({ ...base, text: stripMarkdown(enContent ?? content) });
}
- return entries;
+ return { entries, entriesEn };
}
// ---------------------------------------------------------------------------
@@ -348,7 +384,6 @@ async function main() {
throw err;
}
releaseNames.sort(cmp).reverse();
-
await rm(targetDir, { recursive: true, force: true });
await mkdir(join(targetDir, "handbook"), { recursive: true });
await mkdir(join(targetDir, "releases"), { recursive: true });
@@ -363,7 +398,18 @@ async function main() {
const handbookPages = await buildHandbookIndex();
await writeFile(
join(targetDir, "handbook/index.json"),
- JSON.stringify(handbookPages, null, 2),
+ JSON.stringify(
+ handbookPages.map(({ slug, file, title, order, fileEn, titleEn }) => ({
+ slug,
+ file,
+ title,
+ order,
+ fileEn,
+ titleEn,
+ })),
+ null,
+ 2,
+ ),
);
// 3. Release notes + versioned docs snapshots (last 7 versions)
@@ -372,6 +418,9 @@ async function main() {
const version = parseVersion(name);
const content = await readFile(join(releasesDir, name), "utf8");
await copyFile(join(releasesDir, name), join(targetDir, "releases", name));
+ const enName = `${version}.en.md`;
+ const enContent = await readFileOptional(join(releasesDir, enName));
+ if (enContent) await copyFile(join(releasesDir, enName), join(targetDir, "releases", enName));
const snapDir = join(releasesDir, version);
const hasReference = await stat(join(snapDir, "reference.json"))
.then(() => true)
@@ -399,12 +448,20 @@ async function main() {
join(snapDir, "handbook", page.file),
join(targetDir, "versions", version, "handbook", page.file),
);
+ if (page.fileEn) {
+ await copyFile(
+ join(snapDir, "handbook", page.fileEn),
+ join(targetDir, "versions", version, "handbook", page.fileEn),
+ );
+ }
}
}
versions.push({
version,
file: `releases/${name}`,
+ fileEn: enContent ? `releases/${enName}` : null,
title: extractTitle(content) ?? version,
+ titleEn: enContent ? extractTitle(enContent) ?? version : null,
date: extractDate(content) ?? null,
hasReference,
hasHandbook,
@@ -414,12 +471,13 @@ async function main() {
await writeFile(join(targetDir, "index.json"), JSON.stringify(versions, null, 2));
// 4. Search index
- const searchIndex = await buildSearchIndex(reference, handbookPages, versions.map((v) => v.version));
- await writeFile(join(targetDir, "search.json"), JSON.stringify(searchIndex, null, 2));
+ const { entries, entriesEn } = await buildSearchIndex(reference, handbookPages, versions.map((v) => v.version));
+ await writeFile(join(targetDir, "search.json"), JSON.stringify(entries, null, 2));
+ await writeFile(join(targetDir, "search.en.json"), JSON.stringify(entriesEn, null, 2));
console.log(
`[generate-docs] synced ${versions.length} release(s), ${handbookPages.length} handbook page(s), ` +
- `${reference.schemas.length} schema(s), ${searchIndex.length} search entries`,
+ `${reference.schemas.length} schema(s), ${entries.length} search entries`,
);
}