Compare commits
6 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 48d25b668d | |||
| 6eb3dfd8f9 | |||
| ca980fce09 | |||
| 8f2fd89847 | |||
| d47db6d386 | |||
| 70e72dac71 |
@@ -23,6 +23,7 @@
|
||||
"openid-client": "6.8.4",
|
||||
"pino": "10.3.1",
|
||||
"pino-http": "11.0.0",
|
||||
"yaml": "catalog:",
|
||||
"zod": "catalog:"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -2,8 +2,8 @@ import { type Request, type Response, type NextFunction } from "express";
|
||||
|
||||
const TIER_FEATURES: Record<string, string[]> = {
|
||||
free: ["browse", "rate", "search"],
|
||||
premium: ["browse", "rate", "search", "similar-tools", "costs", "redundancy", "analytics-advanced", "trash", "compare", "watchlist"],
|
||||
enterprise: ["browse", "rate", "search", "similar-tools", "costs", "redundancy", "analytics-advanced", "trash", "compare", "watchlist", "sso", "audit-export", "api-access"],
|
||||
premium: ["browse", "rate", "search", "similar-tools", "costs", "redundancy", "analytics-advanced", "trash", "compare", "watchlist", "tool-import"],
|
||||
enterprise: ["browse", "rate", "search", "similar-tools", "costs", "redundancy", "analytics-advanced", "trash", "compare", "watchlist", "tool-import", "sso", "audit-export", "api-access"],
|
||||
};
|
||||
|
||||
export function getEntitlements(tier: string | undefined, role: string | undefined): string[] {
|
||||
|
||||
@@ -1,9 +1,12 @@
|
||||
import { Router, type IRouter } from "express";
|
||||
import { eq, and, sql } from "drizzle-orm";
|
||||
import { z } from "zod";
|
||||
import { parse as parseYaml } from "yaml";
|
||||
import { db, toolsTable, ratingsTable, toolCostsTable, toolRelationsTable } from "@workspace/db";
|
||||
import { requireAdmin } from "../middleware/auth";
|
||||
import { requireFeature } from "../middleware/feature";
|
||||
import { writeAuditLog } from "../lib/audit";
|
||||
import { CreateToolBody } from "@workspace/api-zod";
|
||||
|
||||
const EvaluateBody = z.object({
|
||||
toolId: z.coerce.number().int().positive(),
|
||||
@@ -219,4 +222,184 @@ router.post("/admin/redundancy/evaluate", requireAdmin, async (req, res): Promis
|
||||
res.json({ ok: true });
|
||||
});
|
||||
|
||||
const ToolImportBody = z.object({
|
||||
format: z.enum(["auto", "csv", "json", "yaml"]).default("auto"),
|
||||
delimiter: z.enum(["auto", "comma", "semicolon", "tab"]).default("auto"),
|
||||
data: z.string().min(1),
|
||||
});
|
||||
|
||||
const TOOL_FIELDS = ["name", "description", "category", "websiteUrl", "iconUrl", "features", "tags"] as const;
|
||||
|
||||
function splitList(value: unknown): string[] {
|
||||
if (Array.isArray(value)) {
|
||||
return value.map((v) => String(v).trim()).filter(Boolean);
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
return value
|
||||
.split(/[|;,]/)
|
||||
.map((v) => v.trim())
|
||||
.filter(Boolean);
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
function normalizeTool(raw: Record<string, unknown>): Record<string, unknown> {
|
||||
const out: Record<string, unknown> = {};
|
||||
for (const key of TOOL_FIELDS) {
|
||||
if (key in raw) out[key] = raw[key];
|
||||
}
|
||||
if ("features" in out) out.features = splitList(out.features);
|
||||
if ("tags" in out) out.tags = splitList(out.tags);
|
||||
return out;
|
||||
}
|
||||
|
||||
function parseCsv(data: string, delimiter: string): Record<string, unknown>[] {
|
||||
let sep = ",";
|
||||
if (delimiter === "semicolon") sep = ";";
|
||||
else if (delimiter === "tab") sep = "\t";
|
||||
else if (delimiter === "auto") {
|
||||
const firstLine = data.split(/\r?\n/)[0] ?? "";
|
||||
const candidates = [",", ";", "\t"];
|
||||
const counts = candidates.map((c) => (firstLine.match(new RegExp(`\\${c === "\t" ? "\\t" : c}`, "g")) ?? []).length);
|
||||
const max = Math.max(...counts);
|
||||
if (max > 0) sep = candidates[counts.indexOf(max)];
|
||||
}
|
||||
|
||||
const lines = data.split(/\r?\n/).filter((l) => l.trim().length > 0);
|
||||
if (lines.length === 0) return [];
|
||||
const header = parseCsvLine(lines[0], sep);
|
||||
const rows: Record<string, unknown>[] = [];
|
||||
for (let i = 1; i < lines.length; i++) {
|
||||
const cells = parseCsvLine(lines[i], sep);
|
||||
const row: Record<string, unknown> = {};
|
||||
header.forEach((h, idx) => {
|
||||
const key = h.trim();
|
||||
if (key) row[key] = cells[idx] ?? "";
|
||||
});
|
||||
rows.push(row);
|
||||
}
|
||||
return rows;
|
||||
}
|
||||
|
||||
function parseCsvLine(line: string, sep: string): string[] {
|
||||
const cells: string[] = [];
|
||||
let cur = "";
|
||||
let inQuotes = false;
|
||||
for (let i = 0; i < line.length; i++) {
|
||||
const ch = line[i];
|
||||
if (inQuotes) {
|
||||
if (ch === '"') {
|
||||
if (line[i + 1] === '"') {
|
||||
cur += '"';
|
||||
i++;
|
||||
} else {
|
||||
inQuotes = false;
|
||||
}
|
||||
} else {
|
||||
cur += ch;
|
||||
}
|
||||
} else if (ch === '"') {
|
||||
inQuotes = true;
|
||||
} else if (ch === sep) {
|
||||
cells.push(cur);
|
||||
cur = "";
|
||||
} else {
|
||||
cur += ch;
|
||||
}
|
||||
}
|
||||
cells.push(cur);
|
||||
return cells;
|
||||
}
|
||||
|
||||
router.post(
|
||||
"/admin/tools/import",
|
||||
requireAdmin,
|
||||
requireFeature("tool-import"),
|
||||
async (req, res): Promise<void> => {
|
||||
const parsed = ToolImportBody.safeParse(req.body);
|
||||
if (!parsed.success) {
|
||||
res.status(400).json({ error: parsed.error.message });
|
||||
return;
|
||||
}
|
||||
const { format, delimiter, data } = parsed.data;
|
||||
|
||||
let rawRows: Record<string, unknown>[] = [];
|
||||
let effectiveFormat = format;
|
||||
if (format === "auto") {
|
||||
const trimmed = data.trimStart();
|
||||
if (trimmed.startsWith("[") || trimmed.startsWith("{")) {
|
||||
effectiveFormat = "json";
|
||||
} else if (trimmed.includes(":") && !trimmed.startsWith("\"")) {
|
||||
effectiveFormat = "yaml";
|
||||
} else {
|
||||
effectiveFormat = "csv";
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
if (effectiveFormat === "json") {
|
||||
const json = JSON.parse(data);
|
||||
if (Array.isArray(json)) {
|
||||
rawRows = json.filter((r) => r && typeof r === "object") as Record<string, unknown>[];
|
||||
} else if (json && typeof json === "object") {
|
||||
rawRows = [json as Record<string, unknown>];
|
||||
} else {
|
||||
res.status(400).json({ error: "JSON data must be an object or an array of objects" });
|
||||
return;
|
||||
}
|
||||
} else if (effectiveFormat === "yaml") {
|
||||
const yaml = parseYaml(data);
|
||||
if (Array.isArray(yaml)) {
|
||||
rawRows = yaml.filter((r) => r && typeof r === "object") as Record<string, unknown>[];
|
||||
} else if (yaml && typeof yaml === "object") {
|
||||
rawRows = [yaml as Record<string, unknown>];
|
||||
} else {
|
||||
res.status(400).json({ error: "YAML data must be an object or a list of objects" });
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
rawRows = parseCsv(data, delimiter);
|
||||
}
|
||||
} catch (err) {
|
||||
res.status(400).json({ error: `Failed to parse ${effectiveFormat} data: ${(err as Error).message}` });
|
||||
return;
|
||||
}
|
||||
|
||||
const errors: { row: number; error: string }[] = [];
|
||||
const validRows: { name: string; description: string; category: string; websiteUrl?: string; iconUrl?: string; features: string[]; tags: string[]; createdBy: string }[] = [];
|
||||
const user = req.session.user!;
|
||||
const createdBy = user.preferred_username || user.name || user.sub;
|
||||
|
||||
rawRows.forEach((raw, idx) => {
|
||||
const normalized = normalizeTool(raw);
|
||||
const check = CreateToolBody.safeParse(normalized);
|
||||
if (!check.success) {
|
||||
errors.push({ row: idx + 1, error: check.error.message });
|
||||
return;
|
||||
}
|
||||
validRows.push({
|
||||
name: check.data.name,
|
||||
description: check.data.description,
|
||||
category: check.data.category,
|
||||
websiteUrl: check.data.websiteUrl,
|
||||
iconUrl: check.data.iconUrl,
|
||||
features: check.data.features ?? [],
|
||||
tags: check.data.tags ?? [],
|
||||
createdBy,
|
||||
});
|
||||
});
|
||||
|
||||
let imported = 0;
|
||||
if (validRows.length > 0) {
|
||||
const inserted = await db.insert(toolsTable).values(validRows).returning({ id: toolsTable.id, name: toolsTable.name, category: toolsTable.category });
|
||||
imported = inserted.length;
|
||||
for (const tool of inserted) {
|
||||
await writeAuditLog(req, "tool", tool.id, "create", { name: tool.name, category: tool.category, source: "import" });
|
||||
}
|
||||
}
|
||||
|
||||
res.json({ imported, total: rawRows.length, errors });
|
||||
},
|
||||
);
|
||||
|
||||
export default router;
|
||||
|
||||
@@ -23,12 +23,13 @@ import { Checkbox } from "@/components/ui/checkbox";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog";
|
||||
import { Search, Trash2, ExternalLink, Pencil, Star } from "lucide-react";
|
||||
import { ToolImportDialog } from "@/components/tool-import-dialog";
|
||||
import { Search, Trash2, ExternalLink, Pencil, Star, Upload } from "lucide-react";
|
||||
import { format } from "date-fns";
|
||||
|
||||
export function AdminToolsTab() {
|
||||
const { t } = useTranslation();
|
||||
const { isAdmin, isLoading: authLoading } = useAuth();
|
||||
const { isAdmin, hasFeature, isLoading: authLoading } = useAuth();
|
||||
const { toast } = useToast();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
@@ -36,6 +37,7 @@ export function AdminToolsTab() {
|
||||
const [search, setSearch] = useState("");
|
||||
const [selected, setSelected] = useState<Set<number>>(new Set());
|
||||
const [confirmTrash, setConfirmTrash] = useState(false);
|
||||
const [importOpen, setImportOpen] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
const t = setTimeout(() => setSearch(searchInput), 300);
|
||||
@@ -111,6 +113,16 @@ export function AdminToolsTab() {
|
||||
</CardDescription>
|
||||
</div>
|
||||
<div className="flex items-center gap-2 flex-wrap">
|
||||
{hasFeature("tool-import") && (
|
||||
<Button
|
||||
size="sm"
|
||||
variant="outline"
|
||||
onClick={() => setImportOpen(true)}
|
||||
data-testid="button-tool-import"
|
||||
>
|
||||
<Upload className="w-4 h-4 mr-2" /> {t("adminTools.importButton")}
|
||||
</Button>
|
||||
)}
|
||||
<div className="relative">
|
||||
<Search className="w-4 h-4 absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground" />
|
||||
<Input
|
||||
@@ -229,6 +241,15 @@ export function AdminToolsTab() {
|
||||
</AlertDialogFooter>
|
||||
</AlertDialogContent>
|
||||
</AlertDialog>
|
||||
|
||||
<ToolImportDialog
|
||||
open={importOpen}
|
||||
onOpenChange={setImportOpen}
|
||||
onImported={() => {
|
||||
toast({ title: t("adminTools.toastImportDone") });
|
||||
invalidate();
|
||||
}}
|
||||
/>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { Slider } from "@/components/ui/slider";
|
||||
import { Maximize2, Minimize2 } from "lucide-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export type Density = "cozy" | "compact";
|
||||
|
||||
@@ -10,8 +11,13 @@ export function DensityToggle({
|
||||
value: Density;
|
||||
onValueChange: (d: Density) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const densityLabel = value === "compact" ? t("common.densityCompact") : t("common.densityComfortable");
|
||||
return (
|
||||
<div className="flex items-center gap-2" title={`Density: ${value === "compact" ? "Compact" : "Comfortable"}`}>
|
||||
<div
|
||||
className="flex items-center gap-2"
|
||||
title={t("common.densityTitle", { density: densityLabel })}
|
||||
>
|
||||
<Maximize2 className="w-4 h-4 text-muted-foreground" aria-hidden />
|
||||
<Slider
|
||||
className="w-16"
|
||||
@@ -20,7 +26,7 @@ export function DensityToggle({
|
||||
step={1}
|
||||
value={[value === "compact" ? 1 : 0]}
|
||||
onValueChange={([v]) => onValueChange(v === 1 ? "compact" : "cozy")}
|
||||
aria-label="List density"
|
||||
aria-label={t("common.listDensity")}
|
||||
/>
|
||||
<Minimize2 className="w-4 h-4 text-muted-foreground" aria-hidden />
|
||||
</div>
|
||||
|
||||
@@ -1,32 +0,0 @@
|
||||
import { HelpCircle } from "lucide-react";
|
||||
import { Link } from "wouter";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
|
||||
export function FieldHelp({
|
||||
schema,
|
||||
field,
|
||||
children,
|
||||
}: {
|
||||
schema: string;
|
||||
field: string;
|
||||
children?: React.ReactNode;
|
||||
}) {
|
||||
const label = children ?? field;
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Link
|
||||
href={`/docs/reference/schemas/${schema}#${field}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-label={`Help: ${label}`}
|
||||
data-testid={`help-${schema}-${field}`}
|
||||
className="inline-flex shrink-0 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<HelpCircle className="h-3.5 w-3.5" />
|
||||
</Link>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{label} — Details in der Dokumentation</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
@@ -105,7 +105,7 @@ export function FilterPopover({
|
||||
step={0.5}
|
||||
value={[minRating ?? 0]}
|
||||
onValueChange={([v]) => onChange({ minRating: v })}
|
||||
aria-label="Minimum rating"
|
||||
aria-label={t("filter.minRating")}
|
||||
/>
|
||||
<span className="text-sm tabular-nums w-8 text-right text-muted-foreground">
|
||||
{minRating != null ? `${minRating.toFixed(1)}+` : t("filter.any")}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
import { BookOpen } from "lucide-react";
|
||||
import { HelpCircle } from "lucide-react";
|
||||
import { Link } from "wouter";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
|
||||
|
||||
export function GuideHelp({
|
||||
@@ -9,21 +11,24 @@ export function GuideHelp({
|
||||
guide: string;
|
||||
label: string;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<Tooltip>
|
||||
<TooltipTrigger asChild>
|
||||
<Button asChild variant="outline" size="sm" className="gap-1.5 text-muted-foreground">
|
||||
<Link
|
||||
href={`/docs/handbook/${guide}`}
|
||||
target="_blank"
|
||||
rel="noreferrer"
|
||||
aria-label={`Help: ${label}`}
|
||||
aria-label={`${t("common.help")}: ${label}`}
|
||||
data-testid={`guide-${guide}`}
|
||||
className="inline-flex shrink-0 text-muted-foreground hover:text-foreground transition-colors"
|
||||
>
|
||||
<BookOpen className="h-3.5 w-3.5" />
|
||||
<HelpCircle className="h-4 w-4" />
|
||||
{t("common.help")}
|
||||
</Link>
|
||||
</Button>
|
||||
</TooltipTrigger>
|
||||
<TooltipContent>{label} — Anleitung in der Dokumentation</TooltipContent>
|
||||
<TooltipContent>{label} — {t("common.guideTooltip")}</TooltipContent>
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,185 @@
|
||||
import { useState } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useImportTools, type ToolImportResponse } from "@workspace/api-client-react";
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Textarea } from "@/components/ui/textarea";
|
||||
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
|
||||
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { Upload, FileUp, CheckCircle2, AlertCircle } from "lucide-react";
|
||||
|
||||
type Format = "auto" | "csv" | "json" | "yaml";
|
||||
type Delimiter = "auto" | "comma" | "semicolon" | "tab";
|
||||
|
||||
export function ToolImportDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
onImported,
|
||||
}: {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onImported: () => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
const importTools = useImportTools();
|
||||
const [format, setFormat] = useState<Format>("auto");
|
||||
const [delimiter, setDelimiter] = useState<Delimiter>("auto");
|
||||
const [data, setData] = useState("");
|
||||
const [result, setResult] = useState<ToolImportResponse | null>(null);
|
||||
|
||||
function handleFile(file: File) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
setData(String(reader.result ?? ""));
|
||||
};
|
||||
reader.readAsText(file);
|
||||
}
|
||||
|
||||
function handleImport() {
|
||||
importTools.mutate(
|
||||
{ data: { format, delimiter, data } },
|
||||
{
|
||||
onSuccess: (res) => {
|
||||
setResult(res);
|
||||
if (res.imported > 0) onImported();
|
||||
},
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
function handleClose(open: boolean) {
|
||||
if (!importTools.isPending) {
|
||||
onOpenChange(open);
|
||||
if (!open) {
|
||||
setData("");
|
||||
setResult(null);
|
||||
setFormat("auto");
|
||||
setDelimiter("auto");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const canSubmit = data.trim().length > 0 && !importTools.isPending;
|
||||
|
||||
return (
|
||||
<Dialog open={open} onOpenChange={handleClose}>
|
||||
<DialogContent className="sm:max-w-2xl">
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<Upload className="w-4 h-4" />
|
||||
{t("adminTools.importTitle")}
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
|
||||
<div className="space-y-4 py-2">
|
||||
<p className="text-sm text-muted-foreground">{t("adminTools.importSub")}</p>
|
||||
|
||||
<Tabs value={format} onValueChange={(v) => setFormat(v as Format)}>
|
||||
<TabsList className="w-full grid grid-cols-4">
|
||||
<TabsTrigger value="auto">{t("adminTools.importFormatAuto")}</TabsTrigger>
|
||||
<TabsTrigger value="csv">CSV</TabsTrigger>
|
||||
<TabsTrigger value="json">JSON</TabsTrigger>
|
||||
<TabsTrigger value="yaml">YAML</TabsTrigger>
|
||||
</TabsList>
|
||||
<TabsContent value="auto" className="text-xs text-muted-foreground pt-2">
|
||||
{t("adminTools.importFormatAutoSub")}
|
||||
</TabsContent>
|
||||
<TabsContent value="csv" className="pt-2 space-y-3">
|
||||
<div className="space-y-2">
|
||||
<Label>{t("adminTools.importDelimiter")}</Label>
|
||||
<Select value={delimiter} onValueChange={(v) => setDelimiter(v as Delimiter)}>
|
||||
<SelectTrigger className="w-52">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="auto">{t("adminTools.importDelimiterAuto")}</SelectItem>
|
||||
<SelectItem value="comma">{t("adminTools.importDelimiterComma")}</SelectItem>
|
||||
<SelectItem value="semicolon">{t("adminTools.importDelimiterSemicolon")}</SelectItem>
|
||||
<SelectItem value="tab">{t("adminTools.importDelimiterTab")}</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">{t("adminTools.importCsvSub")}</p>
|
||||
</TabsContent>
|
||||
<TabsContent value="json" className="pt-2 space-y-3">
|
||||
<p className="text-xs text-muted-foreground">{t("adminTools.importJsonSub")}</p>
|
||||
</TabsContent>
|
||||
<TabsContent value="yaml" className="pt-2 space-y-3">
|
||||
<p className="text-xs text-muted-foreground">{t("adminTools.importYamlSub")}</p>
|
||||
</TabsContent>
|
||||
</Tabs>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
<Button type="button" variant="outline" size="sm" className="gap-2" onClick={() => document.getElementById("tool-import-file")?.click()}>
|
||||
<FileUp className="w-4 h-4" />
|
||||
{t("adminTools.importFile")}
|
||||
</Button>
|
||||
<input
|
||||
id="tool-import-file"
|
||||
type="file"
|
||||
accept=".csv,.json,.yaml,.yml,.txt,text/plain,application/json"
|
||||
className="hidden"
|
||||
onChange={(e) => {
|
||||
const f = e.target.files?.[0];
|
||||
if (f) handleFile(f);
|
||||
e.target.value = "";
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<Textarea
|
||||
value={data}
|
||||
onChange={(e) => setData(e.target.value)}
|
||||
placeholder={t("adminTools.importTextareaPlaceholder")}
|
||||
className="min-h-[220px] font-mono text-sm"
|
||||
data-testid="input-tool-import-data"
|
||||
/>
|
||||
|
||||
{importTools.isError && (
|
||||
<div className="flex items-start gap-2 rounded-md border border-destructive/30 bg-destructive/5 px-3 py-2 text-sm text-destructive">
|
||||
<AlertCircle className="w-4 h-4 mt-0.5 shrink-0" />
|
||||
<span>{importTools.error?.data?.error ?? importTools.error?.message}</span>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{result && (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-2 rounded-md border border-primary/20 bg-primary/5 px-3 py-2 text-sm">
|
||||
<CheckCircle2 className="w-4 h-4 text-primary shrink-0" />
|
||||
<span>{t("adminTools.importDone", { count: result.imported, total: result.total })}</span>
|
||||
</div>
|
||||
{result.errors.length > 0 && (
|
||||
<div className="space-y-1">
|
||||
<p className="text-sm font-medium flex items-center gap-1.5">
|
||||
<AlertCircle className="w-4 h-4 text-amber-500" />
|
||||
{t("adminTools.importErrors", { count: result.errors.length })}
|
||||
</p>
|
||||
<div className="max-h-40 overflow-y-auto rounded-md border bg-muted/40 divide-y divide-border">
|
||||
{result.errors.map((err, i) => (
|
||||
<div key={i} className="flex items-start gap-2 px-3 py-1.5 text-xs">
|
||||
<span className="font-mono text-muted-foreground shrink-0">
|
||||
{t("adminTools.importErrorRow", { row: err.row })}:
|
||||
</span>
|
||||
<span className="text-foreground break-words">{err.error}</span>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<DialogFooter>
|
||||
<Button variant="outline" onClick={() => handleClose(false)} disabled={importTools.isPending}>
|
||||
{t("common.cancel")}
|
||||
</Button>
|
||||
<Button onClick={handleImport} disabled={!canSubmit}>
|
||||
{importTools.isPending ? t("adminTools.importing") : t("adminTools.importButton")}
|
||||
</Button>
|
||||
</DialogFooter>
|
||||
</DialogContent>
|
||||
</Dialog>
|
||||
);
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import {
|
||||
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog";
|
||||
import { Label } from "@/components/ui/label";
|
||||
import { PasswordInput } from "@/components/password-input";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { useToast } from "@/hooks/use-toast";
|
||||
import { useChangeMyPassword, useGetPasswordRedirect, getGetPasswordRedirectQueryKey } from "@workspace/api-client-react";
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
@@ -163,8 +164,9 @@ export function UserMenu() {
|
||||
|
||||
<Dialog open={pwOpen} onOpenChange={setPwOpen}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogHeader className="flex flex-row items-start justify-between gap-4 space-y-0">
|
||||
<DialogTitle>{t("auth.changePassword")}</DialogTitle>
|
||||
<GuideHelp guide="konto" label={t("auth.changePassword")} />
|
||||
</DialogHeader>
|
||||
{!user?.isLocal ? (
|
||||
<div className="space-y-4 py-2">
|
||||
@@ -193,7 +195,7 @@ export function UserMenu() {
|
||||
<PasswordInput
|
||||
value={newPassword}
|
||||
onChange={(e) => setNewPassword(e.target.value)}
|
||||
placeholder="min. 6 characters"
|
||||
placeholder={t("auth.passwordMinLength")}
|
||||
data-testid="input-new-password"
|
||||
/>
|
||||
</div>
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { LayoutGrid, List, Rows3 } from "lucide-react";
|
||||
import { ToggleGroup, ToggleGroupItem } from "@/components/ui/toggle-group";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
export type ViewMode = "grid" | "table" | "rows";
|
||||
|
||||
const OPTIONS: { value: ViewMode; label: string; Icon: typeof LayoutGrid }[] = [
|
||||
{ value: "grid", label: "Grid", Icon: LayoutGrid },
|
||||
{ value: "table", label: "Table", Icon: List },
|
||||
{ value: "rows", label: "Rows", Icon: Rows3 },
|
||||
const OPTIONS: { value: ViewMode; labelKey: string; Icon: typeof LayoutGrid }[] = [
|
||||
{ value: "grid", labelKey: "common.viewGrid", Icon: LayoutGrid },
|
||||
{ value: "table", labelKey: "common.viewTable", Icon: List },
|
||||
{ value: "rows", labelKey: "common.viewRows", Icon: Rows3 },
|
||||
];
|
||||
|
||||
export function ViewToggle({
|
||||
@@ -16,6 +17,7 @@ export function ViewToggle({
|
||||
value: ViewMode;
|
||||
onValueChange: (v: ViewMode) => void;
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
return (
|
||||
<ToggleGroup
|
||||
type="single"
|
||||
@@ -25,10 +27,16 @@ export function ViewToggle({
|
||||
}}
|
||||
variant="outline"
|
||||
size="sm"
|
||||
aria-label="View mode"
|
||||
aria-label={t("common.viewMode")}
|
||||
>
|
||||
{OPTIONS.map(({ value: v, labelKey, Icon }) => (
|
||||
<ToggleGroupItem
|
||||
key={v}
|
||||
value={v}
|
||||
aria-label={t(labelKey)}
|
||||
title={t(labelKey)}
|
||||
data-testid={`view-${v}`}
|
||||
>
|
||||
{OPTIONS.map(({ value: v, label, Icon }) => (
|
||||
<ToggleGroupItem key={v} value={v} aria-label={label} title={label} data-testid={`view-${v}`}>
|
||||
<Icon className="w-4 h-4" />
|
||||
</ToggleGroupItem>
|
||||
))}
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"currentPassword": "Aktuelles Passwort",
|
||||
"newPassword": "Neues Passwort",
|
||||
"confirmPassword": "Passwort bestätigen",
|
||||
"passwordMinLength": "mind. 6 Zeichen",
|
||||
"pwMismatch": "Passwörter stimmen nicht überein",
|
||||
"pwTooShort": "Passwort muss mindestens 8 Zeichen haben",
|
||||
"pwChanged": "Passwort geändert",
|
||||
@@ -55,11 +56,20 @@
|
||||
"none": "Keine",
|
||||
"language": "Sprache",
|
||||
"viewDetails": "Details ansehen",
|
||||
"guideTooltip": "Anleitung in der Dokumentation"
|
||||
"guideTooltip": "Anleitung in der Dokumentation",
|
||||
"help": "Hilfe",
|
||||
"viewMode": "Ansichtsmodus",
|
||||
"viewGrid": "Raster",
|
||||
"viewTable": "Tabelle",
|
||||
"viewRows": "Zeilen",
|
||||
"listDensity": "Listendichte",
|
||||
"densityCompact": "Kompakt",
|
||||
"densityComfortable": "Komfortabel",
|
||||
"densityTitle": "Dichte: {{density}}"
|
||||
},
|
||||
"home": {
|
||||
"welcome": "Willkommen bei toolr",
|
||||
"tagline": "Die Community-Plattform, auf der Ingenieure die Tools, die sie täglich nutzen, ehrlich bewerten.",
|
||||
"tagline": "Bewerte dein Tool.",
|
||||
"totalTools": "Tools gesamt",
|
||||
"totalRatings": "Bewertungen gesamt",
|
||||
"avgRating": "Ø Bewertung",
|
||||
@@ -179,7 +189,7 @@
|
||||
"nameOptional": "Name (optional)",
|
||||
"nameLabel": "Name",
|
||||
"anonymousPlaceholder": "Anonym",
|
||||
"anonymousEngineer": "Anonymer Ingenieur",
|
||||
"anonymousEngineer": "Anonymer Benutzer",
|
||||
"beFirstToReview": "Sei der Erste, der seine Gedanken zu diesem Tool teilt.",
|
||||
"shareExperience": "Teile deine Erfahrung mit {{name}}",
|
||||
"score": "Punktestand",
|
||||
@@ -295,7 +305,6 @@
|
||||
"parameter": "Parameter",
|
||||
"noResults": "Keine Treffer",
|
||||
"fields": "Felder",
|
||||
"fieldHelpHint": "Hinweis: Formular-Felder verlinken per ?-Icon direkt zu den jeweiligen Zeilen dieser Tabelle.",
|
||||
"name": "Name",
|
||||
"in": "In",
|
||||
"requestBody": "Request-Body"
|
||||
@@ -393,7 +402,27 @@
|
||||
"moveToTrashAction": "In den Papierkorb verschieben",
|
||||
"toastMoved": "Tools in den Papierkorb verschoben",
|
||||
"toastMovedSub": "{{count}} Tool(s) in den Papierkorb verschoben.",
|
||||
"toastMoveFailed": "Verschieben in den Papierkorb fehlgeschlagen"
|
||||
"toastMoveFailed": "Verschieben in den Papierkorb fehlgeschlagen",
|
||||
"importTitle": "Tools importieren",
|
||||
"importSub": "Mehrere Tools auf einmal importieren — als CSV, JSON oder YAML (Premium-Feature).",
|
||||
"importButton": "Importieren",
|
||||
"importFormatAuto": "Auto",
|
||||
"importFormatAutoSub": "Das Format wird automatisch erkannt (CSV, JSON oder YAML).",
|
||||
"importDelimiter": "CSV-Trenner",
|
||||
"importDelimiterAuto": "Automatisch",
|
||||
"importDelimiterComma": "Komma",
|
||||
"importDelimiterSemicolon": "Semikolon",
|
||||
"importDelimiterTab": "Tabulator",
|
||||
"importCsvSub": "Spalten: name, description, category, websiteUrl, iconUrl, features, tags. Features/Tags durch | getrennt. Erste Zeile enthält die Spaltennamen.",
|
||||
"importJsonSub": "JSON-Objekt oder Array von Objekten mit den Feldern name, description, category, websiteUrl, iconUrl, features, tags.",
|
||||
"importYamlSub": "YAML-Liste von Objekten (oder ein einzelnes Objekt) mit den Feldern name, description, category, websiteUrl, iconUrl, features, tags.",
|
||||
"importFile": "Datei laden",
|
||||
"importTextareaPlaceholder": "name;description;category;features;tags\nMein Tool;Eine tolle Beschreibung;Monitoring;Deployment|Open Source;devops",
|
||||
"importing": "Wird importiert…",
|
||||
"importDone": "{{count}} von {{total}} Tools erfolgreich importiert.",
|
||||
"importErrors": "{{count}} Zeilen mit Fehlern:",
|
||||
"importErrorRow": "Zeile {{row}}",
|
||||
"toastImportDone": "Tools erfolgreich importiert"
|
||||
},
|
||||
"analytics": {
|
||||
"title": "Plattform-Analysen",
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
"currentPassword": "Current password",
|
||||
"newPassword": "New password",
|
||||
"confirmPassword": "Confirm password",
|
||||
"passwordMinLength": "min. 6 characters",
|
||||
"pwMismatch": "Passwords do not match",
|
||||
"pwTooShort": "Password must be at least 8 characters",
|
||||
"pwChanged": "Password changed",
|
||||
@@ -55,11 +56,20 @@
|
||||
"none": "None",
|
||||
"language": "Language",
|
||||
"viewDetails": "View details",
|
||||
"guideTooltip": "Guide in the documentation"
|
||||
"guideTooltip": "Guide in the documentation",
|
||||
"help": "Help",
|
||||
"viewMode": "View mode",
|
||||
"viewGrid": "Grid",
|
||||
"viewTable": "Table",
|
||||
"viewRows": "Rows",
|
||||
"listDensity": "List density",
|
||||
"densityCompact": "Compact",
|
||||
"densityComfortable": "Comfortable",
|
||||
"densityTitle": "Density: {{density}}"
|
||||
},
|
||||
"home": {
|
||||
"welcome": "Welcome to toolr",
|
||||
"tagline": "The community hub where engineers honestly rate the tools they use daily.",
|
||||
"tagline": "Rate your Tool.",
|
||||
"totalTools": "Total Tools",
|
||||
"totalRatings": "Total Ratings",
|
||||
"avgRating": "Avg Rating",
|
||||
@@ -179,7 +189,7 @@
|
||||
"nameOptional": "Name (Optional)",
|
||||
"nameLabel": "Name",
|
||||
"anonymousPlaceholder": "Anonymous",
|
||||
"anonymousEngineer": "Anonymous Engineer",
|
||||
"anonymousEngineer": "Anonymous User",
|
||||
"beFirstToReview": "Be the first to share your thoughts on this tool.",
|
||||
"shareExperience": "Share your experience with {{name}}",
|
||||
"score": "Score",
|
||||
@@ -295,7 +305,6 @@
|
||||
"parameter": "Parameter",
|
||||
"noResults": "No results",
|
||||
"fields": "Fields",
|
||||
"fieldHelpHint": "Note: form fields link via the ? icon directly to the respective rows of this table.",
|
||||
"name": "Name",
|
||||
"in": "In",
|
||||
"requestBody": "Request Body"
|
||||
@@ -393,7 +402,27 @@
|
||||
"moveToTrashAction": "Move to trash",
|
||||
"toastMoved": "Tools moved to trash",
|
||||
"toastMovedSub": "{{count}} tool(s) moved to trash.",
|
||||
"toastMoveFailed": "Failed to move to trash"
|
||||
"toastMoveFailed": "Failed to move to trash",
|
||||
"importTitle": "Import tools",
|
||||
"importSub": "Import multiple tools at once — as CSV, JSON or YAML (premium feature).",
|
||||
"importButton": "Import",
|
||||
"importFormatAuto": "Auto",
|
||||
"importFormatAutoSub": "The format is detected automatically (CSV, JSON or YAML).",
|
||||
"importDelimiter": "CSV delimiter",
|
||||
"importDelimiterAuto": "Auto-detect",
|
||||
"importDelimiterComma": "Comma",
|
||||
"importDelimiterSemicolon": "Semicolon",
|
||||
"importDelimiterTab": "Tab",
|
||||
"importCsvSub": "Columns: name, description, category, websiteUrl, iconUrl, features, tags. Separate features/tags with |. The first line holds the column names.",
|
||||
"importJsonSub": "A JSON object or array of objects with the fields name, description, category, websiteUrl, iconUrl, features, tags.",
|
||||
"importYamlSub": "A YAML list of objects (or a single object) with the fields name, description, category, websiteUrl, iconUrl, features, tags.",
|
||||
"importFile": "Upload file",
|
||||
"importTextareaPlaceholder": "name;description;category;features;tags\nMy Tool;A great description;Monitoring;Deployment|Open Source;devops",
|
||||
"importing": "Importing…",
|
||||
"importDone": "{{count}} of {{total}} tools imported successfully.",
|
||||
"importErrors": "{{count}} rows with errors:",
|
||||
"importErrorRow": "Row {{row}}",
|
||||
"toastImportDone": "Tools imported successfully"
|
||||
},
|
||||
"analytics": {
|
||||
"title": "Platform Analytics",
|
||||
|
||||
@@ -7,7 +7,6 @@ import {
|
||||
} from "@workspace/api-client-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import {
|
||||
@@ -51,9 +50,8 @@ export default function Analytics() {
|
||||
<Layout>
|
||||
<div className="space-y-6 pb-10">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2 flex items-center gap-2">
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">
|
||||
{t("analytics.title")}
|
||||
<GuideHelp guide="analytics" label={t("analytics.title")} />
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("analytics.subtitle")}</p>
|
||||
</div>
|
||||
|
||||
@@ -8,7 +8,6 @@ import { Link, useSearch } from "wouter";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { RatingStars } from "@/components/rating-stars";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
@@ -102,9 +101,8 @@ export default function Compare() {
|
||||
<Layout>
|
||||
<div className="space-y-6 pb-10">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2 flex items-center gap-2">
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">
|
||||
{t("compare.title")}
|
||||
<GuideHelp guide="vergleichen" label={t("compare.title")} />
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("compare.subtitle", { count: list.length })}</p>
|
||||
</div>
|
||||
|
||||
@@ -30,7 +30,6 @@ import {
|
||||
ExternalLink,
|
||||
FileText,
|
||||
GitBranch,
|
||||
HelpCircle,
|
||||
Library,
|
||||
Menu,
|
||||
Search,
|
||||
@@ -591,10 +590,6 @@ function SchemaView({ schema }: { schema: SchemaModel }) {
|
||||
{schema.description && <p className="text-muted-foreground">{schema.description}</p>}
|
||||
</div>
|
||||
<FieldTable fields={schema.fields} />
|
||||
<p className="text-xs text-muted-foreground">
|
||||
<HelpCircle className="h-3.5 w-3.5 inline mr-1" />
|
||||
{t("docs.fieldHelpHint")}
|
||||
</p>
|
||||
</div>
|
||||
<Toc headings={headings} title={t("docs.fields")} />
|
||||
</div>
|
||||
@@ -745,7 +740,7 @@ function ReleasesView({ versions }: { versions: ReleaseDoc[] }) {
|
||||
|
||||
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`;
|
||||
const file = doc ? `releases/${localizedFile(doc.file, doc.fileEn, locale)}` : `releases/${version}.de.md`;
|
||||
return (
|
||||
<div className="flex flex-col lg:flex-row gap-8">
|
||||
<div className="flex-1 min-w-0 max-w-3xl">
|
||||
|
||||
@@ -24,7 +24,9 @@ export default function Home() {
|
||||
<div className="space-y-8 pb-8">
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-end gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">{t("home.welcome")}</h1>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">
|
||||
{t("home.welcome")}
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("home.tagline")}</p>
|
||||
</div>
|
||||
<Button asChild>
|
||||
|
||||
@@ -60,7 +60,6 @@ import {
|
||||
} from "@/components/ui/alert-dialog";
|
||||
import { customFetch } from "@workspace/api-client-react";
|
||||
import { recordRecentTool } from "@/lib/recent-tools";
|
||||
import { FieldHelp } from "@/components/field-help";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
|
||||
const ratingSchema = z.object({
|
||||
@@ -616,9 +615,8 @@ export default function ToolDetail() {
|
||||
<Dialog open={costDialogOpen} onOpenChange={(o) => { if (!o) setCostDialogOpen(false); }}>
|
||||
<DialogContent>
|
||||
<DialogHeader>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
<DialogTitle>
|
||||
{editCost ? t("detail.edit") + " " + t("detail.costs") : t("detail.addCost")}
|
||||
<GuideHelp guide="kosten" label={t("detail.costs")} />
|
||||
</DialogTitle>
|
||||
<DialogDescription>{t("detail.costDialogSub")}</DialogDescription>
|
||||
</DialogHeader>
|
||||
@@ -768,12 +766,12 @@ export default function ToolDetail() {
|
||||
|
||||
{isReviewFormOpen && (
|
||||
<Card className="border-primary shadow-sm">
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
{t("detail.addReview")}
|
||||
<GuideHelp guide="bewerten" label={t("detail.addReview")} />
|
||||
</CardTitle>
|
||||
<CardHeader className="flex flex-row items-start justify-between gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<CardTitle>{t("detail.addReview")}</CardTitle>
|
||||
<CardDescription>{t("detail.shareExperience", { name: tool.name })}</CardDescription>
|
||||
</div>
|
||||
<GuideHelp guide="bewerten" label={t("detail.addReview")} />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
@@ -786,7 +784,6 @@ export default function ToolDetail() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("detail.usefulness")}
|
||||
<FieldHelp schema="RatingInput" field="usefulness">{t("detail.usefulness")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<div className="py-2">
|
||||
<RatingStars
|
||||
@@ -807,7 +804,6 @@ export default function ToolDetail() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("detail.usability")}
|
||||
<FieldHelp schema="RatingInput" field="usability">{t("detail.usability")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<div className="py-2">
|
||||
<RatingStars
|
||||
@@ -830,7 +826,6 @@ export default function ToolDetail() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("detail.commentOptional")}
|
||||
<FieldHelp schema="RatingInput" field="comment">{t("detail.commentLabel")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
@@ -851,7 +846,6 @@ export default function ToolDetail() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("detail.nameOptional")}
|
||||
<FieldHelp schema="RatingInput" field="reviewerName">{t("detail.nameLabel")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("detail.anonymousPlaceholder")} {...field} />
|
||||
|
||||
@@ -30,7 +30,6 @@ import { CategoryCombobox } from "@/components/category-combobox";
|
||||
import { FeatureInput } from "@/components/feature-input";
|
||||
import { TagInput } from "@/components/tag-input";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { FieldHelp } from "@/components/field-help";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -163,13 +162,15 @@ export default function ToolEdit() {
|
||||
</Card>
|
||||
) : (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardHeader className="flex flex-row items-start justify-between gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Pencil className="w-5 h-5 text-primary" />
|
||||
{t("toolForm.toolDetails")}
|
||||
<GuideHelp guide="tool-bearbeiten" label={t("toolForm.toolDetails")} />
|
||||
</CardTitle>
|
||||
<CardDescription>{t("toolForm.toolDetailsEditSub")}</CardDescription>
|
||||
</div>
|
||||
<GuideHelp guide="tool-bearbeiten" label={t("toolForm.toolDetails")} />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
@@ -182,7 +183,6 @@ export default function ToolEdit() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("toolForm.name")}
|
||||
<FieldHelp schema="ToolInput" field="name">{t("toolForm.name")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("toolForm.name")} {...field} />
|
||||
@@ -198,7 +198,6 @@ export default function ToolEdit() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("toolForm.category")}
|
||||
<FieldHelp schema="ToolInput" field="category">{t("toolForm.category")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<CategoryCombobox value={field.value} onChange={field.onChange} />
|
||||
@@ -216,7 +215,6 @@ export default function ToolEdit() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("toolForm.websiteUrlOptional")}
|
||||
<FieldHelp schema="ToolInput" field="websiteUrl">{t("toolForm.websiteUrl")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("toolForm.urlPlaceholder")} type="url" {...field} />
|
||||
@@ -233,7 +231,6 @@ export default function ToolEdit() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("toolForm.iconUrlOptional")}
|
||||
<FieldHelp schema="ToolInput" field="iconUrl">{t("toolForm.iconUrl")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -269,7 +266,6 @@ export default function ToolEdit() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("toolForm.description")}
|
||||
<FieldHelp schema="ToolInput" field="description">{t("toolForm.description")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
@@ -288,7 +284,6 @@ export default function ToolEdit() {
|
||||
<div>
|
||||
<h3 className="text-lg font-medium inline-flex items-center gap-1.5">
|
||||
{t("toolForm.features")}
|
||||
<FieldHelp schema="ToolInput" field="features">{t("toolForm.features")}</FieldHelp>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">{t("toolForm.featuresEditSub")}</p>
|
||||
</div>
|
||||
@@ -335,7 +330,6 @@ export default function ToolEdit() {
|
||||
<div>
|
||||
<h3 className="text-lg font-medium inline-flex items-center gap-1.5">
|
||||
{t("toolForm.tags")}
|
||||
<FieldHelp schema="ToolInput" field="tags">{t("toolForm.tags")}</FieldHelp>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">{t("toolForm.tagsEditSub")}</p>
|
||||
</div>
|
||||
|
||||
@@ -18,7 +18,6 @@ import { CategoryCombobox } from "@/components/category-combobox";
|
||||
import { FeatureInput } from "@/components/feature-input";
|
||||
import { TagInput } from "@/components/tag-input";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { FieldHelp } from "@/components/field-help";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
@@ -126,13 +125,15 @@ export default function ToolNew() {
|
||||
)}
|
||||
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardHeader className="flex flex-row items-start justify-between gap-4">
|
||||
<div className="space-y-1.5">
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Wrench className="w-5 h-5 text-primary" />
|
||||
{t("toolForm.toolDetails")}
|
||||
<GuideHelp guide="tool-anlegen" label={t("toolForm.toolDetails")} />
|
||||
</CardTitle>
|
||||
<CardDescription>{t("toolForm.toolDetailsNewSub")}</CardDescription>
|
||||
</div>
|
||||
<GuideHelp guide="tool-anlegen" label={t("toolForm.toolDetails")} />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<Form {...form}>
|
||||
@@ -145,7 +146,6 @@ export default function ToolNew() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("toolForm.name")}
|
||||
<FieldHelp schema="ToolInput" field="name">{t("toolForm.name")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("toolForm.namePlaceholder")} {...field} data-testid="input-tool-name" />
|
||||
@@ -162,7 +162,6 @@ export default function ToolNew() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("toolForm.category")}
|
||||
<FieldHelp schema="ToolInput" field="category">{t("toolForm.category")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<CategoryCombobox
|
||||
@@ -183,7 +182,6 @@ export default function ToolNew() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("toolForm.websiteUrlOptional")}
|
||||
<FieldHelp schema="ToolInput" field="websiteUrl">{t("toolForm.websiteUrl")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Input placeholder={t("toolForm.urlPlaceholder")} type="url" {...field} data-testid="input-tool-url" />
|
||||
@@ -200,7 +198,6 @@ export default function ToolNew() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("toolForm.iconUrlOptional")}
|
||||
<FieldHelp schema="ToolInput" field="iconUrl">{t("toolForm.iconUrl")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -237,7 +234,6 @@ export default function ToolNew() {
|
||||
<FormItem>
|
||||
<FormLabel className="inline-flex items-center gap-1.5">
|
||||
{t("toolForm.description")}
|
||||
<FieldHelp schema="ToolInput" field="description">{t("toolForm.description")}</FieldHelp>
|
||||
</FormLabel>
|
||||
<FormControl>
|
||||
<Textarea
|
||||
@@ -257,7 +253,6 @@ export default function ToolNew() {
|
||||
<div>
|
||||
<h3 className="text-lg font-medium inline-flex items-center gap-1.5">
|
||||
{t("toolForm.features")}
|
||||
<FieldHelp schema="ToolInput" field="features">{t("toolForm.features")}</FieldHelp>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">{t("toolForm.featuresNewSub")}</p>
|
||||
</div>
|
||||
@@ -313,7 +308,6 @@ export default function ToolNew() {
|
||||
<div>
|
||||
<h3 className="text-lg font-medium inline-flex items-center gap-1.5">
|
||||
{t("toolForm.tags")}
|
||||
<FieldHelp schema="ToolInput" field="tags">{t("toolForm.tags")}</FieldHelp>
|
||||
</h3>
|
||||
<p className="text-sm text-muted-foreground">{t("toolForm.tagsHelp")}</p>
|
||||
</div>
|
||||
@@ -369,7 +363,7 @@ export default function ToolNew() {
|
||||
className="w-full sm:w-auto"
|
||||
data-testid="button-submit-tool"
|
||||
>
|
||||
{createTool.isPending ? "Adding Tool..." : "Submit Tool"}
|
||||
{createTool.isPending ? t("toolForm.addingTool") : t("toolForm.submitTool")}
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
|
||||
@@ -230,7 +230,9 @@ export default function ToolsBrowse() {
|
||||
<div className="space-y-6 pb-8">
|
||||
<div className="flex flex-col sm:flex-row justify-between items-start sm:items-end gap-4">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">{t("nav.browseTools")}</h1>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">
|
||||
{t("nav.browseTools")}
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("browse.subtitle")}</p>
|
||||
</div>
|
||||
<Button asChild>
|
||||
|
||||
@@ -156,7 +156,9 @@ export default function Trash() {
|
||||
<Layout>
|
||||
<div className="space-y-6 pb-10">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">{t("trash.title")}</h1>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">
|
||||
{t("trash.title")}
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("trash.subtitle")}</p>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -6,7 +6,6 @@ import { useAuth } from "@/hooks/use-auth";
|
||||
import { useWatchlist } from "@/hooks/use-watchlist";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { ToolCard } from "@/components/tool-card";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { ShieldAlert, Bookmark } from "lucide-react";
|
||||
@@ -43,9 +42,8 @@ export default function Watchlist() {
|
||||
<Layout>
|
||||
<div className="space-y-6 pb-10">
|
||||
<div>
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2 flex items-center gap-2">
|
||||
<h1 className="text-3xl font-bold tracking-tight mb-2">
|
||||
{t("watchlist.title")}
|
||||
<GuideHelp guide="watchlist" label={t("watchlist.title")} />
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("watchlist.subtitle")}</p>
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
# Beispiel-YAML zum Import in den Admin-Bereich (Tab „Tools" -> Importieren).
|
||||
# Erwartetes Format pro Eintrag:
|
||||
# name (Pflicht)
|
||||
# description, category, websiteUrl, iconUrl (optional)
|
||||
# features, tags (Listen oder | -getrennte Strings)
|
||||
# Das Format wird automatisch erkannt; bei CSV den Trenner wählen.
|
||||
# Fehlerhafte Zeilen werden übersprungen und mit Zeilennummer aufgelistet.
|
||||
|
||||
- name: Slack
|
||||
description: Team-Kommunikation mit Channels, Direktnachrichten und App-Integrationen
|
||||
category: Kommunikation
|
||||
websiteUrl: https://slack.com
|
||||
features:
|
||||
- Channels
|
||||
- Videoanrufe
|
||||
- App-Integrationen
|
||||
tags:
|
||||
- communication
|
||||
- teamwork
|
||||
|
||||
- name: Microsoft Teams
|
||||
description: Chat, Besprechungen, Dateien und Zusammenarbeit in einer Plattform
|
||||
category: Kommunikation
|
||||
websiteUrl: https://www.microsoft.com/de-de/microsoft-365/microsoft-teams
|
||||
features:
|
||||
- Videokonferenzen
|
||||
- Teamkanäle
|
||||
- Office-Integration
|
||||
tags:
|
||||
- communication
|
||||
- meetings
|
||||
|
||||
- name: Zoom
|
||||
description: Videokonferenzen und Webinare für Teams und externe Partner
|
||||
category: Kommunikation
|
||||
websiteUrl: https://zoom.us
|
||||
features:
|
||||
- Videokonferenzen
|
||||
- Webinare
|
||||
- Bildschirmfreigabe
|
||||
tags:
|
||||
- video
|
||||
- meetings
|
||||
|
||||
- name: Jira
|
||||
description: Agile Projekt- und Issue-Verfolgung für Entwicklung und Betrieb
|
||||
category: Projektmanagement
|
||||
websiteUrl: https://www.atlassian.com/software/jira
|
||||
features:
|
||||
- Backlog
|
||||
- Sprints
|
||||
- Kanban-Boards
|
||||
tags:
|
||||
- agile
|
||||
- project-management
|
||||
|
||||
- name: Confluence
|
||||
description: Kollaborative Wissensdatenbank und Dokumentation für Teams
|
||||
category: Projektmanagement
|
||||
websiteUrl: https://www.atlassian.com/software/confluence
|
||||
features:
|
||||
- Wikis
|
||||
- Seitenbausteine
|
||||
- Berechtigungen
|
||||
tags:
|
||||
- documentation
|
||||
- knowledge-base
|
||||
|
||||
- name: GitLab
|
||||
description: DevOps-Plattform mit Git-Repositorys, CI/CD und Code-Review
|
||||
category: Entwicklung
|
||||
websiteUrl: https://about.gitlab.com
|
||||
features:
|
||||
- Git-Repositorys
|
||||
- CI/CD
|
||||
- Merge Requests
|
||||
tags:
|
||||
- devops
|
||||
- git
|
||||
|
||||
- name: GitHub
|
||||
description: Versionsverwaltung, Code-Review und Collaboration auf Git-Basis
|
||||
category: Entwicklung
|
||||
websiteUrl: https://github.com
|
||||
features:
|
||||
- Repositories
|
||||
- Pull Requests
|
||||
- Actions
|
||||
tags:
|
||||
- git
|
||||
- collaboration
|
||||
|
||||
- name: Jenkins
|
||||
description: Automatisierte Build- und Deploy-Pipelines für Softwareprojekte
|
||||
category: CI/CD
|
||||
websiteUrl: https://www.jenkins.io
|
||||
features:
|
||||
- Pipelines
|
||||
- Plugins
|
||||
- Job-Automatisierung
|
||||
tags:
|
||||
- ci
|
||||
- cd
|
||||
|
||||
- name: Docker
|
||||
description: Container-Plattform zum Paketieren und Ausführen von Anwendungen
|
||||
category: Entwicklung
|
||||
websiteUrl: https://www.docker.com
|
||||
features:
|
||||
- Container
|
||||
- Images
|
||||
- Compose
|
||||
tags:
|
||||
- containers
|
||||
- virtualization
|
||||
|
||||
- name: Grafana
|
||||
description: Observability- und Dashboard-Plattform für Metriken, Logs und Alerts
|
||||
category: Monitoring
|
||||
websiteUrl: https://grafana.com
|
||||
features:
|
||||
- Dashboards
|
||||
- Alerts
|
||||
- Datenquellen
|
||||
tags:
|
||||
- observability
|
||||
- monitoring
|
||||
|
||||
- name: Prometheus
|
||||
description: Open-Source-Systeme zur Zeitreihen-Metrikerfassung und -Auswertung
|
||||
category: Monitoring
|
||||
websiteUrl: https://prometheus.io
|
||||
features:
|
||||
- Metriken
|
||||
- Alerting
|
||||
- Time-Series-Datenbank
|
||||
tags:
|
||||
- monitoring
|
||||
- metrics
|
||||
|
||||
- name: Datadog
|
||||
description: Cloud-basierte Observability für Infrastruktur, APM und Logs
|
||||
category: Monitoring
|
||||
websiteUrl: https://www.datadoghq.com
|
||||
features:
|
||||
- Infrastruktur-Monitoring
|
||||
- APM
|
||||
- Log-Management
|
||||
tags:
|
||||
- observability
|
||||
- saas
|
||||
|
||||
- name: PagerDuty
|
||||
description: Incident-Management und On-Call-Benachrichtigungen für Teams
|
||||
category: Betrieb
|
||||
websiteUrl: https://www.pagerduty.com
|
||||
features:
|
||||
- Alerting
|
||||
- On-Call-Pläne
|
||||
- Incident-Response
|
||||
tags:
|
||||
- incident-management
|
||||
- on-call
|
||||
|
||||
- name: Sentry
|
||||
description: Fehlerüberwachung und Performance-Monitoring für Anwendungen
|
||||
category: Monitoring
|
||||
websiteUrl: https://sentry.io
|
||||
features:
|
||||
- Error-Tracking
|
||||
- Releases
|
||||
- Performance
|
||||
tags:
|
||||
- error-monitoring
|
||||
- debugging
|
||||
|
||||
- name: Terraform
|
||||
description: Infrastructure-as-Code zur deklarativen Bereitstellung von Ressourcen
|
||||
category: Infrastruktur
|
||||
websiteUrl: https://www.terraform.io
|
||||
features:
|
||||
- Modules
|
||||
- State-Management
|
||||
- Provider
|
||||
tags:
|
||||
- iac
|
||||
- infrastructure
|
||||
|
||||
- name: Ansible
|
||||
description: Agentenlose Automatisierung für Konfiguration, Deploy und Orchestrierung
|
||||
category: Infrastruktur
|
||||
websiteUrl: https://www.ansible.com
|
||||
features:
|
||||
- Playbooks
|
||||
- Inventories
|
||||
- Modules
|
||||
tags:
|
||||
- automation
|
||||
- configuration
|
||||
|
||||
- name: Elasticsearch
|
||||
description: Verteilter Such- und Analytics-Server für Logs, Metriken und Daten
|
||||
category: Datenbank
|
||||
websiteUrl: https://www.elastic.co
|
||||
features:
|
||||
- Volltextsuche
|
||||
- Aggregationen
|
||||
- Skalierung
|
||||
tags:
|
||||
- search
|
||||
- analytics
|
||||
|
||||
- name: Kubernetes
|
||||
description: Container-Orchestrierung für Deployment, Skalierung und Betrieb
|
||||
category: Infrastruktur
|
||||
websiteUrl: https://kubernetes.io
|
||||
features:
|
||||
- Pods
|
||||
- Services
|
||||
- Autoskalierung
|
||||
tags:
|
||||
- containers
|
||||
- orchestration
|
||||
|
||||
- name: Sonarqube
|
||||
description: Statische Code-Analyse und Qualitätsmessung für Softwareprojekte
|
||||
category: Entwicklung
|
||||
websiteUrl: https://www.sonarsource.com/products/sonarqube
|
||||
features:
|
||||
- Code-Qualität
|
||||
- Security-Scans
|
||||
- Quality Gates
|
||||
tags:
|
||||
- code-quality
|
||||
- static-analysis
|
||||
|
||||
- name: Power BI
|
||||
description: Business-Intelligence-Tool für Dashboards, Berichte und Datenanalyse
|
||||
category: Analytics
|
||||
websiteUrl: https://powerbi.microsoft.com
|
||||
features:
|
||||
- Dashboards
|
||||
- Berichte
|
||||
- Datenmodelle
|
||||
tags:
|
||||
- bi
|
||||
- analytics
|
||||
|
||||
- name: Google Workspace
|
||||
description: Produktivitäts-Suite mit E-Mail, Docs, Drive und Meet
|
||||
category: Produktivität
|
||||
websiteUrl: https://workspace.google.com
|
||||
features:
|
||||
- Gmail
|
||||
- Google Docs
|
||||
- Google Drive
|
||||
tags:
|
||||
- suite
|
||||
- productivity
|
||||
|
||||
- name: Microsoft 365
|
||||
description: Office-Anwendungen, E-Mail und Cloud-Dienste für Unternehmen
|
||||
category: Produktivität
|
||||
websiteUrl: https://www.microsoft.com/microsoft-365
|
||||
features:
|
||||
- Word
|
||||
- Excel
|
||||
- Outlook
|
||||
tags:
|
||||
- suite
|
||||
- productivity
|
||||
|
||||
- name: Slack Enterprise Grid
|
||||
description: Slack-Verwaltung für große Organisationen mit erweiterten Sicherheitsfunktionen
|
||||
category: Kommunikation
|
||||
websiteUrl: https://slack.com
|
||||
features:
|
||||
- Enterprise-Admin
|
||||
- Compliance
|
||||
- Multi-Workspace
|
||||
tags:
|
||||
- communication
|
||||
- enterprise
|
||||
|
||||
- name: Atlassian Rovo
|
||||
description: KI-Assistent für Suche und Zusammenarbeit über Atlassian-Produkte
|
||||
category: Projektmanagement
|
||||
websiteUrl: https://www.atlassian.com/software/rovo
|
||||
features:
|
||||
- KI-Suche
|
||||
- Agenten
|
||||
- Integrationen
|
||||
tags:
|
||||
- ai
|
||||
- search
|
||||
@@ -36,6 +36,49 @@ Zentraler Zugriff auf den Tool-Katalog.
|
||||
- **Massenaktion:** mehrere Tools auswählen und in den Papierkorb verschieben
|
||||
(Bestätigungsdialog; soft gelöschte Tools sind aus allen öffentlichen Ansichten
|
||||
entfernt und können wiederhergestellt oder endgültig gelöscht werden).
|
||||
- **Importieren** (Premium): über die Schaltfläche **Importieren** oben rechts
|
||||
können mehrere Tools gleichzeitig angelegt werden. Das Format wird automatisch
|
||||
erkannt (CSV, JSON oder YAML); für CSV lässt sich der Trenner wählen
|
||||
(automatisch, Komma, Semikolon oder Tabulator). Die Daten lassen sich direkt
|
||||
ins Formular einfügen oder als Datei hochladen.
|
||||
|
||||
Unterstützte Felder: `name` (Pflicht), `description`, `category`, `websiteUrl`,
|
||||
`iconUrl`, `features`, `tags` (`features`/`tags` sind mit `|` getrennte Listen).
|
||||
|
||||
**CSV-Beispiel:**
|
||||
|
||||
```csv
|
||||
name;description;category;features;tags
|
||||
Grafana;Observability- und Dashboard-Plattform;Monitoring;Dashboards|Alerts;devops
|
||||
Prometheus;Zeitreihen-Datenbank;Monitoring;Metrics;devops
|
||||
```
|
||||
|
||||
**JSON-Beispiel:**
|
||||
|
||||
```json
|
||||
[
|
||||
{ "name": "Grafana", "category": "Monitoring", "features": ["Dashboards"] },
|
||||
{ "name": "Prometheus", "category": "Monitoring" }
|
||||
]
|
||||
```
|
||||
|
||||
**YAML-Beispiel:**
|
||||
|
||||
```yaml
|
||||
- name: Grafana
|
||||
category: Monitoring
|
||||
features:
|
||||
- Dashboards
|
||||
- name: Prometheus
|
||||
category: Monitoring
|
||||
```
|
||||
|
||||
Eine umfangreichere, fertig importierbare Beispieldatei liegt unter
|
||||
`docs/examples/import-tools.yaml` (24 gängige Enterprise-Tools).
|
||||
|
||||
Beim Import werden einzelne fehlerhafte Zeilen übersprungen und mit Zeilennummer
|
||||
aufgelistet; korrekte Zeilen werden trotzdem angelegt. Jeder Import erzeugt
|
||||
Audit-Log-Einträge.
|
||||
|
||||
## Tab „Audit-Log"
|
||||
|
||||
@@ -36,6 +36,49 @@ Central access to the tool catalog.
|
||||
- **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).
|
||||
- **Import** (premium): via the **Import** button at the top right, multiple
|
||||
tools can be created at once. The format is detected automatically (CSV,
|
||||
JSON or YAML); for CSV the delimiter can be chosen (auto-detect, comma,
|
||||
semicolon or tab). Data can be pasted directly into the form or uploaded
|
||||
as a file.
|
||||
|
||||
Supported fields: `name` (required), `description`, `category`, `websiteUrl`,
|
||||
`iconUrl`, `features`, `tags` (`features`/`tags` are `|`-separated lists).
|
||||
|
||||
**CSV example:**
|
||||
|
||||
```csv
|
||||
name;description;category;features;tags
|
||||
Grafana;Observability and dashboard platform;Monitoring;Dashboards|Alerts;devops
|
||||
Prometheus;Time-series database;Monitoring;Metrics;devops
|
||||
```
|
||||
|
||||
**JSON example:**
|
||||
|
||||
```json
|
||||
[
|
||||
{ "name": "Grafana", "category": "Monitoring", "features": ["Dashboards"] },
|
||||
{ "name": "Prometheus", "category": "Monitoring" }
|
||||
]
|
||||
```
|
||||
|
||||
**YAML example:**
|
||||
|
||||
```yaml
|
||||
- name: Grafana
|
||||
category: Monitoring
|
||||
features:
|
||||
- Dashboards
|
||||
- name: Prometheus
|
||||
category: Monitoring
|
||||
```
|
||||
|
||||
A larger, ready-to-import example file is located at
|
||||
`docs/examples/import-tools.yaml` (24 common enterprise tools).
|
||||
|
||||
During import, individual faulty rows are skipped and listed with their row
|
||||
number; correct rows are still created. Every import produces audit-log
|
||||
entries.
|
||||
|
||||
## "Audit log" tab
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ Besuch bis zum Anlegen und Bewerten eines Tools.
|
||||
|
||||
## 1. Anmelden
|
||||
|
||||
Die meisten Aktionen (Tool anlegen, bewerten, Watchlist, Vergleichen) erfordern
|
||||
Die meisten Aktionen (Tool anlegen, bewerten, Merkliste, Vergleichen) erfordern
|
||||
ein Konto. Klicke unten links auf **Anmelden**. Je nach Konfiguration der
|
||||
Instanz hast du zwei Möglichkeiten:
|
||||
|
||||
@@ -51,7 +51,7 @@ Siehe [Bewerten](/docs/handbook/bewerten).
|
||||
## 5. Weiterführend
|
||||
|
||||
- [Tools vergleichen](/docs/handbook/vergleichen)
|
||||
- [Watchlist](/docs/handbook/watchlist)
|
||||
- [Merkliste](/docs/handbook/watchlist)
|
||||
- [Analytics](/docs/handbook/analytics)
|
||||
- [Pläne & Berechtigungen](/docs/handbook/plaene)
|
||||
- [Administration](/docs/handbook/administration)
|
||||
@@ -6,7 +6,7 @@ order: 1
|
||||
# Willkommen bei toolr
|
||||
|
||||
toolr ist eine Plattform zum **Entdecken, Bewerten und Vergleichen von
|
||||
Entwicklungstools**. Nutzer:innen pflegen einen gemeinsamen Katalog von Tools,
|
||||
Entwicklungstools**. Nutzer pflegen einen gemeinsamen Katalog von Tools,
|
||||
vergeben Bewertungen (Nützlichkeit & Bedienbarkeit) und nutzen Statistiken, um
|
||||
die richtige Wahl zu treffen.
|
||||
|
||||
@@ -16,13 +16,14 @@ die richtige Wahl zu treffen.
|
||||
| --- | --- | --- |
|
||||
| **Tools durchsuchen** | Katalog filtern, sortieren und durchsuchen | Alle |
|
||||
| **Tool anlegen** | Neues Tool mit Beschreibung, Kategorie, Features & Tags eintragen | Angemeldet |
|
||||
| **Tool bearbeiten/löschen** | Eigene Tools pflegen (Ersteller:in oder Admin) | Angemeldet |
|
||||
| **Tool bearbeiten/löschen** | Eigene Tools pflegen (Ersteller oder Admin) | Angemeldet |
|
||||
| **Bewerten** | Nützlichkeit & Bedienbarkeit (1–5) plus Kommentar vergeben | Angemeldet |
|
||||
| **Watchlist** | Tools als Favoriten speichern | Premium |
|
||||
| **Merkliste** | Tools als Favoriten speichern | Premium |
|
||||
| **Vergleichen** | Tools nebeneinander gegenüberstellen | Premium |
|
||||
| **Kosten erfassen** | Lizenz- und Kostenmodelle je Tool eintragen | Premium |
|
||||
| **Analytics** | Statistiken, Top-Tools, Verteilungen | Alle |
|
||||
| **Papierkorb** | Soft-gelöschte Tools wiederherstellen oder endgültig löschen | Premium |
|
||||
| **Import** | Mehrere Tools per CSV, JSON oder YAML anlegen | Admin (Premium) |
|
||||
| **Admin** | Nutzerverwaltung, Audit-Log, Systeminformationen | Admin |
|
||||
| **Redundanz** | Automatische Doppelungs-Erkennung | Admin |
|
||||
|
||||
@@ -22,6 +22,7 @@ tools**. Users maintain a shared catalog of tools, submit ratings
|
||||
| **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 |
|
||||
| **Import** | Create multiple tools via CSV, JSON or YAML | Admin (premium) |
|
||||
| **Admin** | User management, audit log, system information | Admin |
|
||||
| **Redundancy detection** | Automatic duplicate detection | Admin |
|
||||
|
||||
|
||||
@@ -26,7 +26,7 @@ Der aktive Modus steht im Endpunkt
|
||||
Dein Profil (Avatar, Name, E-Mail, Tarif) siehst du unten links im
|
||||
Benutzermenü. Dort stehen dir folgende Aktionen zur Verfügung:
|
||||
|
||||
- **Watchlist** — deine gespeicherten Tools (nur mit dem entsprechenden Tarif).
|
||||
- **Merkliste** — deine gespeicherten Tools (nur mit dem entsprechenden Tarif).
|
||||
- **Papierkorb** — wiederherstellbare, gelöschte Tools (Premium/Enterprise).
|
||||
- **Passwort ändern** — für lokale Konten direkt in toolr; für OIDC-Konten wird
|
||||
die Passwortverwaltung im Identitätsanbieter angeboten.
|
||||
@@ -51,7 +51,7 @@ API-Referenz: [`POST /auth/me/password`](/docs/reference/endpoints/auth#changeMy
|
||||
- die **Listenansicht** und **Dichte** im Bereich Tools durchsuchen anpassen
|
||||
(siehe [Tools finden & durchsuchen](/docs/handbook/tools-finden)).
|
||||
|
||||
Deine Präferenzen (inkl. Watchlist) werden im Endpunkt
|
||||
Deine Präferenzen (inkl. Merkliste) werden im Endpunkt
|
||||
[`GET /auth/me/preferences`](/docs/reference/endpoints/auth#getMePreferences)
|
||||
gespeichert und über [`PUT /auth/me/preferences`](/docs/reference/endpoints/auth#updateMePreferences)
|
||||
aktualisiert.
|
||||
@@ -13,7 +13,7 @@ Feature-Beschränkungen.
|
||||
| Tarif | Beschreibung |
|
||||
| --- | --- |
|
||||
| **Free** | Grundfunktionen: suchen, filtern, ansehen, Analytics |
|
||||
| **Premium** | Zusätzlich Watchlist, Vergleichen, Papierkorb, Kosten |
|
||||
| **Premium** | Zusätzlich Merkliste, Vergleichen, Papierkorb, Kosten |
|
||||
| **Enterprise** | Alle Premium-Features + erweiterter Support |
|
||||
|
||||
### Feature-Berechtigungen
|
||||
@@ -23,9 +23,10 @@ Premium/Enterprise schalten folgende Features frei:
|
||||
| Feature | Funktion | Mehr erfahren |
|
||||
| --- | --- | --- |
|
||||
| `compare` | Tools vergleichen | [Vergleichen](/docs/handbook/vergleichen) |
|
||||
| `watchlist` | Favoritenliste | [Watchlist](/docs/handbook/watchlist) |
|
||||
| `watchlist` | Favoritenliste | [Merkliste](/docs/handbook/watchlist) |
|
||||
| `trash` | Papierkorb (soft gelöschte Tools) | [Papierkorb](/docs/handbook/papierkorb) |
|
||||
| `costs` | Kosten-/Lizenzmodelle erfassen | [Kosten erfassen](/docs/handbook/kosten) |
|
||||
| `tool-import` | Tools per CSV/JSON/YAML importieren (Admin) | [Administration](/docs/handbook/administration) |
|
||||
|
||||
Fehlt dir ein Feature, zeigt die App einen **Upgrade-Hinweis** mit Link zur
|
||||
Tarifverwaltung.
|
||||
@@ -26,6 +26,7 @@ Premium/Enterprise unlock the following features:
|
||||
| `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) |
|
||||
| `tool-import` | Import tools via CSV/JSON/YAML (admin) | [Administration](/docs/handbook/administration) |
|
||||
|
||||
If you are missing a feature, the app shows an **upgrade notice** with a link
|
||||
to the plan management.
|
||||
|
||||
@@ -19,7 +19,7 @@ Ohne Eingabe zeigt die Palette:
|
||||
|
||||
- **Zuletzt angesehen** — die letzten 5 Tools, die du besucht hast.
|
||||
- **Navigation** — Tools durchsuchen, Tool hinzufügen, Analytics sowie
|
||||
(abhängig von Berechtigungen) Watchlist, Papierkorb und Admin.
|
||||
(abhängig von Berechtigungen) Merkliste, Papierkorb und Admin.
|
||||
|
||||
### Suche
|
||||
|
||||
@@ -47,7 +47,7 @@ Mit **Filter zurücksetzen** bzw. **Alle entfernen** räumst du sie wieder auf.
|
||||
- **Ansicht wechseln:** Raster / Tabelle / Zeilen.
|
||||
- **Dichte:** gemütlich / kompakt (Schieberegler).
|
||||
|
||||
Deine Auswahl wird gespeichert — lokal im Browser und für angemeldete Nutzer:innen
|
||||
Deine Auswahl wird gespeichert — lokal im Browser und für angemeldete Nutzer
|
||||
zusätzlich serverseitig in den Präferenzen. Ansicht, Dichte, Suche, Filter und
|
||||
Sortierung werden dabei in die URL übernommen, sodass du Ergebnisse teilen
|
||||
kannst.
|
||||
@@ -58,12 +58,12 @@ In der Tabellenansicht sind die Spalten **Tool**, **Bewertung** und **Anzahl
|
||||
Bewertungen** sortierbar. Beim Überfahren einer Zeile erscheint eine Vorschau
|
||||
mit Bewertungsdetails, Tags und Mini-Balken.
|
||||
|
||||
## Auswählen für Vergleich & Watchlist
|
||||
## Auswählen für Vergleich & Merkliste
|
||||
|
||||
- Auf jeder Karte/Zeile findest du ein **Vergleichs-Icon**, mit dem du Tools zur
|
||||
[Vergleichsleiste](/docs/handbook/vergleichen) hinzufügst.
|
||||
- Das **Lesezeichen-Icon** speichert Tools in deiner
|
||||
[Watchlist](/docs/handbook/watchlist) (nur mit dem entsprechenden Tarif).
|
||||
[Merkliste](/docs/handbook/watchlist) (nur mit dem entsprechenden Tarif).
|
||||
|
||||
## API
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
---
|
||||
title: Watchlist
|
||||
title: Merkliste
|
||||
order: 8
|
||||
---
|
||||
|
||||
# Watchlist
|
||||
# Merkliste
|
||||
|
||||
Die **Watchlist** ist eine persönliche Favoritenliste. Tools darin kannst du
|
||||
Die **Merkliste** ist eine persönliche Favoritenliste. Tools darin kannst du
|
||||
jederzeit per Klick wieder aufrufen und vergleichen.
|
||||
|
||||
> Die Watchlist ist ein **Premium-Feature** (Premium/Enterprise) und steht
|
||||
> Die Merkliste ist ein **Premium-Feature** (Premium/Enterprise) und steht
|
||||
> Admins immer zur Verfügung.
|
||||
|
||||
## Voraussetzung
|
||||
@@ -21,18 +21,18 @@ beim Lesezeichen ein Hinweis auf den Tarifwechsel
|
||||
|
||||
- Auf jeder Karte/Zeile im Bereich **Tools durchsuchen** findest du das
|
||||
**Lesezeichen-Icon**.
|
||||
- Ein Klick speichert das Tool in deiner Watchlist — das Icon wird gefüllt.
|
||||
- Ein Klick speichert das Tool in deiner Merkliste — das Icon wird gefüllt.
|
||||
- Ein erneuter Klick entfernt es wieder.
|
||||
|
||||
## Watchlist ansehen
|
||||
## Merkliste ansehen
|
||||
|
||||
Öffne die Watchlist über das Benutzermenü oder die Seitenleiste. Sie zeigt alle
|
||||
Öffne die Merkliste über das Benutzermenü oder die Seitenleiste. Sie zeigt alle
|
||||
gespeicherten Tools als Karten. Das gefüllte Lesezeichen auf einer Karte
|
||||
entfernt das Tool aus der Liste.
|
||||
|
||||
## Wo wird die Watchlist gespeichert?
|
||||
## Wo wird die Merkliste gespeichert?
|
||||
|
||||
Die Watchlist ist eine Liste von Tool-IDs in deinen **Benutzerpräferenzen**.
|
||||
Die Merkliste ist eine Liste von Tool-IDs in deinen **Benutzerpräferenzen**.
|
||||
Damit ist sie geräteübergreifend mit deinem Konto verbunden.
|
||||
|
||||
API-Referenz: [`GET /auth/me/watchlist`](/docs/reference/endpoints/auth#getMeWatchlist).
|
||||
API-Referenz: [`GET /auth/me/watchlist`](/docs/reference/endpoints/auth#getMeMerkliste).
|
||||
@@ -1,37 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,37 +0,0 @@
|
||||
# v0.6.0 — Release Notes
|
||||
|
||||
**Datum:** 2026-08-03 · **Tag:** [`v0.6.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.6.0)
|
||||
|
||||
## Neue Features
|
||||
|
||||
- Vollständige Modernisierung aller Abhängigkeiten auf die aktuellen Hauptversionen
|
||||
(TypeScript 7, Vite 8, React 19.2, openid-client 6, zod 4, recharts 3, react-day-picker 10).
|
||||
|
||||
## Fixes & Verbesserungen
|
||||
|
||||
- CI-Build durch `allowBuilds`-Konfiguration für pnpm 11 repariert
|
||||
(Build-Scripts für esbuild & Co. werden nicht mehr blockiert).
|
||||
- Image-Tagging vereinfacht: nur noch `latest` und `v*`-Tags, keine `nightly-*`/`sha-*`-Tags.
|
||||
- Alle Dependencies exakt gepinnt; automatische Updates via Renovate vorbereitet
|
||||
(`renovate.json`, `docs/dependency-policy.md`).
|
||||
|
||||
## API-Änderungen
|
||||
|
||||
- Keine Breaking Changes an der API. openid-client intern auf v6 migriert
|
||||
(auth-Fluss verhält sich identisch).
|
||||
|
||||
## Betrieb / Upgrade
|
||||
|
||||
- **Env-Vars:** unverändert. Node-Image auf `node:24.18.1-alpine` gepinnt.
|
||||
- **Migration:** keine Datenbank-Migration erforderlich.
|
||||
- **Breaking Changes:** keine.
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
- `typedoc` (indirekte orval-Abhängigkeit) zeigt eine Peer-Dependency-Warnung
|
||||
(erwartet TypeScript 5.x/6.x, installiert ist 7.x) — harmlos für Build & Laufzeit.
|
||||
|
||||
## 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)
|
||||
@@ -1,35 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,35 +0,0 @@
|
||||
# v0.7.0 — Release Notes
|
||||
|
||||
**Datum:** 2026-08-03 · **Tag:** [`v0.7.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.7.0)
|
||||
|
||||
## Neue Features
|
||||
|
||||
- Version-gebundene **Release-Dokumentation** in der App unter `/docs`
|
||||
(Index + Detailseite je Version, Markdown aus `docs/releases/`).
|
||||
- Generiertes Release-Vorlage (`docs/releases/TEMPLATE.md`) und
|
||||
Sync-Schritt für den Frontend-Build.
|
||||
|
||||
## Fixes & Verbesserungen
|
||||
|
||||
- `tsx` auf 4.23.4 angehoben — letzte veraltete Abhängigkeit im Workspace
|
||||
(`pnpm outdated -r` ist jetzt leer).
|
||||
|
||||
## API-Änderungen
|
||||
|
||||
- Keine Breaking Changes an der API.
|
||||
|
||||
## Betrieb / Upgrade
|
||||
|
||||
- **Env-Vars:** unverändert.
|
||||
- **Migration:** keine.
|
||||
- **Breaking Changes:** keine.
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
- Die Doku ist bisher auf Release-Notes beschränkt; eine vollständige
|
||||
API-/Feld-Referenz folgt 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)
|
||||
@@ -1,46 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,46 +0,0 @@
|
||||
# v0.8.0 — Release Notes
|
||||
|
||||
**Datum:** 2026-08-03 · **Tag:** [`v0.8.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.0)
|
||||
|
||||
## Neue Features
|
||||
|
||||
- **Vollständige Dokumentations-Site** in mkdocs-Optik unter `/docs`:
|
||||
- **Handbuch** mit verständlichen Erklärungen zu allen Features
|
||||
(Erste Schritte, Tool anlegen, Bewertungen, Vergleichen, Watchlist,
|
||||
Analytics, Administration, Datenmodell).
|
||||
- **Automatisch generierte Referenz** aus `lib/api-spec/openapi.yaml`:
|
||||
alle Endpunkte und Datenfelder (Typ, Pflichtstatus, Constraints) —
|
||||
damit ist garantiert, dass *jedes* Feature dokumentiert ist.
|
||||
- **Suche** über Handbuch, Endpunkte und Felder.
|
||||
- **Versions-Dropdown**: ältere Releases behalten ihre vollständige
|
||||
Feld-/Endpunkt-Referenz als Snapshot.
|
||||
- **Repo-Link** oben rechts zur Quelle.
|
||||
- **Hilfe-Buttons (?) in Formularen** (NetBox-Stil): neben jedem Feld
|
||||
springt ein Icon direkt zur Feldbeschreibung in der Doku.
|
||||
|
||||
## Fixes & Verbesserungen
|
||||
|
||||
- Doku-Generator `scripts/src/generate-docs.mjs` ersetzt den bisherigen
|
||||
`sync-release-docs.mjs` (OpenAPI-Parsing, Handbuch, Suchindex, Snapshots).
|
||||
- Dokumentation für v0.7.0 nachgezogen.
|
||||
|
||||
## API-Änderungen
|
||||
|
||||
- Keine Breaking Changes an der API.
|
||||
|
||||
## Betrieb / Upgrade
|
||||
|
||||
- **Env-Vars:** unverändert.
|
||||
- **Migration:** keine.
|
||||
- **Breaking Changes:** keine.
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
- Handbuch & Referenz gelten für die aktuelle Version; ältere Versionen
|
||||
zeigen ihre Release-Notes und einen Referenz-Snapshot, sofern beim Release
|
||||
erzeugt (`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)
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,47 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,47 +0,0 @@
|
||||
# v0.8.1 — Release Notes
|
||||
|
||||
**Datum:** 2026-08-03 · **Tag:** [`v0.8.1`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.1)
|
||||
|
||||
## Neue Features
|
||||
|
||||
- **Standalone-Doku-Seite**: Die Dokumentation steht jetzt als eigene,
|
||||
mkdocs-artige Seite unter `toolr.kubebase.de/docs` — ohne die App-Shell
|
||||
(eigene Kopfzeile mit Repo-Link, Versions-Dropdown, Suche, Theme-Umschalter
|
||||
und Link zurück zur App).
|
||||
- **Navigation umbenannt**: Der Seitenleisten-Eintrag heißt jetzt **„Hilfe"**
|
||||
und führt zur Standalone-Doku.
|
||||
- **User Guide komplett überarbeitet**: 17 Handbuch-Seiten mit
|
||||
Schritt-für-Schritt-Anleitungen für alle Funktionen (Tools finden, Tool
|
||||
anlegen, Bewerten, Watchlist, Vergleichen, Kosten, Analytics, Pläne,
|
||||
Administration, Redundanz, Papierkorb, Tastenkürzel, Datenmodell).
|
||||
|
||||
## Fixes & Verbesserungen
|
||||
|
||||
- Referenz-Links sind jetzt unabhängig von Groß-/Kleinschreibung
|
||||
(Schema-/Endpoint-Slugs wie `toolinput` und `ToolInput` funktionieren beide).
|
||||
- Handbuch-Links auf Endpunkt-Anker korrigiert (PascalCase-OperationIds).
|
||||
- Veraltete, kaputte Handbuch-Links (`vergleichen`, `watchlist` …) ersetzt.
|
||||
- Dokumentations-Tabellenkopfzeilen und Hinweistexte in der Doku-Seite über
|
||||
i18n internationalisiert (de/en).
|
||||
|
||||
## API-Änderungen
|
||||
|
||||
- Keine Änderungen an der API.
|
||||
|
||||
## Betrieb / Upgrade
|
||||
|
||||
- **Env-Vars:** unverändert.
|
||||
- **Migration:** keine.
|
||||
- **Breaking Changes:** keine. Die Doku-Seite ist unter `/docs` erreichbar wie
|
||||
bisher; lediglich die Darstellung ist nun eigenständig.
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
- Handbuch & Referenz gelten für die aktuelle Version; ältere Versionen zeigen
|
||||
ihre Release-Notes und einen Referenz-Snapshot, sofern beim Release erzeugt
|
||||
(`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)
|
||||
@@ -1,53 +0,0 @@
|
||||
|
||||
# Erste Schritte
|
||||
|
||||
Diese Seite führt dich durch die wichtigsten Abläufe in toolr — vom ersten
|
||||
Besuch bis zum Anlegen und Bewerten eines Tools.
|
||||
|
||||
## 1. Anmelden
|
||||
|
||||
Die meisten Aktionen (Tool anlegen, bewerten, Watchlist, Vergleichen) erfordern
|
||||
ein Konto. Klicke unten links auf **Anmelden**. Je nach Konfiguration der
|
||||
Instanz hast du zwei Möglichkeiten:
|
||||
|
||||
- **Lokale Konten:** Benutzername + Passwort. Der Zugang wird von einem Admin
|
||||
angelegt (siehe [Administration](/docs/handbook/administration)).
|
||||
- **OIDC (SSO):** Anmelden mit dem konfigurierten Identitätsanbieter (z. B.
|
||||
Keycloak).
|
||||
|
||||
Welcher Modus aktiv ist, steht im Endpunkt
|
||||
[`GET /auth/mode`](/docs/reference/endpoints/auth#getAuthMode). Details findest
|
||||
du im Abschnitt [Anmelden & Konto](/docs/handbook/konto).
|
||||
|
||||
## 2. Tools finden
|
||||
|
||||
Öffne den Bereich **Tools durchsuchen**:
|
||||
|
||||
- **Suchen** — Volltextsuche über Name & Beschreibung (Tastenkürzel `/`).
|
||||
- **Filtern** — nach Kategorie, Tags, Features und Mindestbewertung
|
||||
(`minRating`).
|
||||
- **Sortieren** — nach Aktualität, Top-Bewertung, meistbewertet, Name
|
||||
(auf-/absteigend) oder letztem Update.
|
||||
|
||||
Alle Optionen im Detail: [Tools finden & durchsuchen](/docs/handbook/tools-finden).
|
||||
|
||||
## 3. Tool anlegen
|
||||
|
||||
Gehe auf **Tool hinzufügen** und fülle das Formular aus. Details zu jedem Feld
|
||||
findest du im Abschnitt [Tool anlegen](/docs/handbook/tool-anlegen) und in der
|
||||
[Feld-Referenz](/docs/reference/schemas/toolinput).
|
||||
|
||||
## 4. Bewerten
|
||||
|
||||
Auf der Detailseite eines Tools kannst du **Nützlichkeit** und **Bedienbarkeit**
|
||||
(jeweils 1–5) vergeben und optional einen Kommentar hinterlassen. Deine
|
||||
Bewertung fließt sofort in die Statistiken ein.
|
||||
Siehe [Bewerten](/docs/handbook/bewerten).
|
||||
|
||||
## 5. Weiterführend
|
||||
|
||||
- [Tools vergleichen](/docs/handbook/vergleichen)
|
||||
- [Watchlist](/docs/handbook/watchlist)
|
||||
- [Analytics](/docs/handbook/analytics)
|
||||
- [Pläne & Berechtigungen](/docs/handbook/plaene)
|
||||
- [Administration](/docs/handbook/administration)
|
||||
@@ -1,51 +0,0 @@
|
||||
|
||||
# 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.
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
# Willkommen bei toolr
|
||||
|
||||
toolr ist eine Plattform zum **Entdecken, Bewerten und Vergleichen von
|
||||
Entwicklungstools**. Nutzer:innen pflegen einen gemeinsamen Katalog von Tools,
|
||||
vergeben Bewertungen (Nützlichkeit & Bedienbarkeit) und nutzen Statistiken, um
|
||||
die richtige Wahl zu treffen.
|
||||
|
||||
## Was kannst du mit toolr tun?
|
||||
|
||||
| Funktion | Beschreibung | Sichtbarkeit |
|
||||
| --- | --- | --- |
|
||||
| **Tools durchsuchen** | Katalog filtern, sortieren und durchsuchen | Alle |
|
||||
| **Tool anlegen** | Neues Tool mit Beschreibung, Kategorie, Features & Tags eintragen | Angemeldet |
|
||||
| **Tool bearbeiten/löschen** | Eigene Tools pflegen (Ersteller:in oder Admin) | Angemeldet |
|
||||
| **Bewerten** | Nützlichkeit & Bedienbarkeit (1–5) plus Kommentar vergeben | Angemeldet |
|
||||
| **Watchlist** | Tools als Favoriten speichern | Premium |
|
||||
| **Vergleichen** | Tools nebeneinander gegenüberstellen | Premium |
|
||||
| **Kosten erfassen** | Lizenz- und Kostenmodelle je Tool eintragen | Premium |
|
||||
| **Analytics** | Statistiken, Top-Tools, Verteilungen | Alle |
|
||||
| **Papierkorb** | Soft-gelöschte Tools wiederherstellen oder endgültig löschen | Premium |
|
||||
| **Admin** | Nutzerverwaltung, Audit-Log, Systeminformationen | Admin |
|
||||
| **Redundanz** | Automatische Doppelungs-Erkennung | Admin |
|
||||
|
||||
## Wie diese Doku aufgebaut ist
|
||||
|
||||
- **User Guide** (diese Seiten): Schritt-für-Schritt-Anleitungen für alle
|
||||
Funktionen — von den [Ersten Schritten](/docs/handbook/getting-started) bis
|
||||
zur [Administration](/docs/handbook/administration).
|
||||
- **API-Referenz**: automatisch aus der OpenAPI-Spezifikation generiert — alle
|
||||
[Endpunkte](/docs/reference/endpoints/tools) und
|
||||
[Datenfelder](/docs/reference/schemas/toolinput) der aktuellen Version.
|
||||
- **Release-Notes**: Was ist in welcher [Version](/docs/releases/v0.8.1) neu.
|
||||
|
||||
## Der Einstieg
|
||||
|
||||
Der schnellste Weg:
|
||||
|
||||
1. **Anmelden** — ohne Konto kannst du nur stöbern
|
||||
(siehe [Erste Schritte](/docs/handbook/getting-started#1-anmelden)).
|
||||
2. **Tools finden** — Suche, Filter und Sortierung im Bereich
|
||||
[Tools durchsuchen](/docs/handbook/tools-finden).
|
||||
3. **Tool anlegen** — über „Tool hinzufügen"
|
||||
([Anleitung](/docs/handbook/tool-anlegen)).
|
||||
4. **Bewerten** — auf der Detailseite eines Tools
|
||||
([Anleitung](/docs/handbook/bewerten)).
|
||||
|
||||
## Kontakt & Quellcode
|
||||
|
||||
Der Quellcode liegt unter
|
||||
[git.kubebase.de/admin/tool-evaluator](https://git.kubebase.de/admin/tool-evaluator) —
|
||||
über das Repository-Icon oben rechts erreichst du ihn jederzeit.
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
# 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").
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
# Tastenkürzel & Kommandopalette
|
||||
|
||||
## Kommandopalette
|
||||
|
||||
Die Kommandopalette ist die zentrale Schnellnavigation:
|
||||
|
||||
- Öffnen mit **`⌘K`** (macOS) bzw. **`Ctrl+K`** (Windows/Linux).
|
||||
- Alternativ über die Suchleiste oben rechts („Tools suchen… ⌘K") oder das
|
||||
Such-Icon auf Mobilgeräten.
|
||||
|
||||
### Leerer Zustand
|
||||
|
||||
Ohne Eingabe zeigt die Palette:
|
||||
|
||||
- **Zuletzt angesehen** — die letzten 5 Tools, die du besucht hast.
|
||||
- **Navigation** — Tools durchsuchen, Tool hinzufügen, Analytics sowie
|
||||
(abhängig von Berechtigungen) Watchlist, Papierkorb und Admin.
|
||||
|
||||
### Suche
|
||||
|
||||
Tippe, um live nach Tools zu suchen (max. 10 Ergebnisse, inkl. Bewertung
|
||||
`X.X★`).
|
||||
|
||||
## Tastenkürzel im Überblick
|
||||
|
||||
| Kürzel | Aktion |
|
||||
| --- | --- |
|
||||
| `⌘K` / `Ctrl+K` | Kommandopalette öffnen |
|
||||
| `/` | Suche im Bereich „Tools durchsuchen" fokussieren |
|
||||
|
||||
## Weitere Hinweise
|
||||
|
||||
- **Zuletzt angesehen** wird lokal im Browser gespeichert (max. 5 Einträge).
|
||||
- Die Seitenleiste (linke Navigation) ist auf Desktop einklappbar; der
|
||||
Breadcrumb oben zeigt deinen aktuellen Ort.
|
||||
@@ -1,34 +0,0 @@
|
||||
|
||||
# Watchlist
|
||||
|
||||
Die **Watchlist** ist eine persönliche Favoritenliste. Tools darin kannst du
|
||||
jederzeit per Klick wieder aufrufen und vergleichen.
|
||||
|
||||
> Die Watchlist ist ein **Premium-Feature** (Premium/Enterprise) und steht
|
||||
> Admins immer zur Verfügung.
|
||||
|
||||
## Voraussetzung
|
||||
|
||||
Du benötigst einen Tarif mit `watchlist`-Berechtigung. Fehlt diese, erscheint
|
||||
beim Lesezeichen ein Hinweis auf den Tarifwechsel
|
||||
(siehe [Pläne & Berechtigungen](/docs/handbook/plaene)).
|
||||
|
||||
## Tool speichern
|
||||
|
||||
- Auf jeder Karte/Zeile im Bereich **Tools durchsuchen** findest du das
|
||||
**Lesezeichen-Icon**.
|
||||
- Ein Klick speichert das Tool in deiner Watchlist — das Icon wird gefüllt.
|
||||
- Ein erneuter Klick entfernt es wieder.
|
||||
|
||||
## Watchlist ansehen
|
||||
|
||||
Öffne die Watchlist über das Benutzermenü oder die Seitenleiste. Sie zeigt alle
|
||||
gespeicherten Tools als Karten. Das gefüllte Lesezeichen auf einer Karte
|
||||
entfernt das Tool aus der Liste.
|
||||
|
||||
## Wo wird die Watchlist gespeichert?
|
||||
|
||||
Die Watchlist ist eine Liste von Tool-IDs in deinen **Benutzerpräferenzen**.
|
||||
Damit ist sie geräteübergreifend mit deinem Konto verbunden.
|
||||
|
||||
API-Referenz: [`GET /auth/me/watchlist`](/docs/reference/endpoints/auth#getMeWatchlist).
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,35 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,36 +0,0 @@
|
||||
# v0.8.2 — Release Notes
|
||||
|
||||
**Datum:** 2026-08-04 · **Tag:** [`v0.8.2`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.2)
|
||||
|
||||
## Fixes & Verbesserungen
|
||||
|
||||
- **Mobile Navigation in der Doku**: Die linke Navigationsleiste (Handbuch,
|
||||
Endpunkte, Schemas) wurde unterhalb von `lg` (1024px) komplett ausgeblendet.
|
||||
Es gibt jetzt einen Menü-Button im Kopfbereich, der ein seitliches
|
||||
Navigations-Drawer mit demselben Inhalt öffnet — auf kleinen Bildschirmen
|
||||
bleibt die Doku voll navigierbar.
|
||||
- **Versions-Dropdown zeigt die gewählte Version an**: Beim Wechsel zu einem
|
||||
anderen Release (`/docs/releases/vX.Y.Z`) blieb das Dropdown fälschlich auf
|
||||
„aktuell". Die angezeigte Version wird jetzt auch aus der releases-Route
|
||||
abgeleitet.
|
||||
|
||||
## API-Änderungen
|
||||
|
||||
- Keine Änderungen an der API.
|
||||
|
||||
## Betrieb / Upgrade
|
||||
|
||||
- **Env-Vars:** unverändert.
|
||||
- **Migration:** keine.
|
||||
- **Breaking Changes:** keine.
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
- Handbuch & Referenz gelten für die aktuelle Version; ältere Versionen zeigen
|
||||
ihre Release-Notes und einen Referenz-Snapshot, sofern beim Release erzeugt
|
||||
(`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)
|
||||
@@ -1,61 +0,0 @@
|
||||
|
||||
# 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.
|
||||
@@ -1,61 +0,0 @@
|
||||
|
||||
# Administration
|
||||
|
||||
Der Bereich **Admin** (`/admin`) ist ausschließlich für Admins zugänglich.
|
||||
Ohne Admin-Rolle erscheint eine Zugriffsverweigerung.
|
||||
|
||||
> Oben rechts führt die Schaltfläche **Redundanz-Dashboard** zur automatischen
|
||||
> Doppelungs-Erkennung (siehe [Redundanz](/docs/handbook/redundanz)).
|
||||
|
||||
## Tab „Nutzer"
|
||||
|
||||
Verwaltung der lokalen Konten.
|
||||
|
||||
- **Nutzer hinzufügen:** Benutzername (Pflicht), Passwort (mind. 6 Zeichen),
|
||||
E-Mail (optional), **Rolle** (User/Admin), **Tarif** (Free/Premium/Enterprise).
|
||||
- **Nutzer bearbeiten:** Rolle, Tarif und (für lokale Konten) ein neues Passwort
|
||||
setzen. Für OIDC-Konten wird die Passwortverwaltung im Identitätsanbieter
|
||||
(z. B. Keycloak) angeboten.
|
||||
- **Nutzer löschen:** Entfernt das Konto endgültig (nicht für das eigene Konto).
|
||||
|
||||
API-Referenz:
|
||||
[`POST /users`](/docs/reference/endpoints/users#createUser),
|
||||
[`PATCH /users/{id}`](/docs/reference/endpoints/users#updateUser),
|
||||
[`DELETE /users/{id}`](/docs/reference/endpoints/users#deleteUser).
|
||||
|
||||
## Tab „Tools"
|
||||
|
||||
Zentraler Zugriff auf den Tool-Katalog.
|
||||
|
||||
- **Suchen** nach Tools.
|
||||
- Tools einzeln ansehen, bearbeiten oder in den Papierkorb verschieben.
|
||||
- **Massenaktion:** mehrere Tools auswählen und in den Papierkorb verschieben
|
||||
(Bestätigungsdialog; soft gelöschte Tools sind aus allen öffentlichen Ansichten
|
||||
entfernt und können wiederhergestellt oder endgültig gelöscht werden).
|
||||
|
||||
## Tab „Audit-Log"
|
||||
|
||||
Chronologisches Protokoll aller Anlage-, Änderungs- und Löschvorgänge
|
||||
(max. 100 Einträge): Aktion, Entität + ID, Zeitstempel, ausführende Person und
|
||||
geänderte Felder.
|
||||
|
||||
API-Referenz: [`GET /audit-logs`](/docs/reference/endpoints/audit#listAuditLogs).
|
||||
|
||||
## Tab „System"
|
||||
|
||||
Versionsinformationen der laufenden Instanz:
|
||||
|
||||
- **Version** (z. B. `v0.8.1`),
|
||||
- **Commit** (7-stelliger SHA, verlinkt zum Repository),
|
||||
- **Build-Datum**,
|
||||
- **Papierkorb-Aufbewahrung** („N Tage" oder „Für immer").
|
||||
|
||||
## Tool-Verknüpfungen (Admin)
|
||||
|
||||
Auf der Detailseite eines Tools kannst du als Admin **Verknüpfungen**
|
||||
(eigene/„manual" sowie automatisch erkannte) verwalten:
|
||||
|
||||
- **Tool verknüpfen:** Dialog mit Tool-ID, **Beziehungstyp**
|
||||
(Ähnlich / Ersetzt / Abgelöst durch) und optionalen Notizen.
|
||||
- Beziehungstypen werden als Badges auf der Detailseite angezeigt.
|
||||
- Manuelle Verknüpfungen lassen sich per Papierkorb-Icon wieder entfernen.
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
# 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)
|
||||
@@ -1,29 +0,0 @@
|
||||
|
||||
# Analytics
|
||||
|
||||
Der Bereich **Analytics** (`/analytics`) ist ein öffentliches Dashboard mit
|
||||
Kennzahlen und Diagrammen auf Basis aller Tools und Bewertungen.
|
||||
|
||||
## Kennzahlen (KPI-Karten)
|
||||
|
||||
- **Anzahl Tools** — wie viele Tools sind im Katalog erfasst.
|
||||
- **Anzahl Bewertungen** — wie viele Bewertungen wurden insgesamt abgegeben.
|
||||
- **Aktive Kategorien** — wie viele Kategorien existieren.
|
||||
- **Durchschnittliche Bewertung** — globaler kombinierter Wert.
|
||||
|
||||
## Diagramme
|
||||
|
||||
| Diagramm | Inhalt |
|
||||
| --- | --- |
|
||||
| **Top 8 Tools** | Balkendiagramm der Tools mit der höchsten kombinierten Punktzahl (0–5) |
|
||||
| **Tools je Kategorie** | Radar-Diagramm der Tool-Anzahl pro Kategorie |
|
||||
| **Punkteverteilung** | Zwei horizontale Balken-Diagramme (Nützlichkeit & Bedienbarkeit) pro Stern |
|
||||
|
||||
Die Diagramme sind interaktiv (Tooltips beim Überfahren).
|
||||
|
||||
## 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)
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
# 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
|
||||
@@ -1,39 +0,0 @@
|
||||
|
||||
# Bewerten
|
||||
|
||||
Auf der Detailseite eines Tools kannst du deine Erfahrung teilen. Klicke auf
|
||||
**Bewertung abgeben** (erfordert ein Konto).
|
||||
|
||||
## Formularfelder
|
||||
|
||||
| Feld | Pflicht | Hinweise |
|
||||
| --- | --- | --- |
|
||||
| **Nützlichkeit** | Ja | 1–5 Sterne |
|
||||
| **Bedienbarkeit** | Ja | 1–5 Sterne |
|
||||
| **Kommentar** | Nein | Freitext |
|
||||
| **Name** | Nein | Standard „Anonym" |
|
||||
|
||||
Neben den Feldern führt das **?‑Icon** direkt zur zugehörigen Feldbeschreibung
|
||||
in der [Datenmodell-Referenz](/docs/reference/schemas/ratinginput).
|
||||
|
||||
## Was passiert nach dem Abgeben?
|
||||
|
||||
- Deine Bewertung wird sofort gespeichert und erscheint in der
|
||||
**Bewertungsliste** der Detailseite.
|
||||
- Die **Durchschnittswerte** (Nützlichkeit, Bedienbarkeit, Kombiniert) und die
|
||||
**Punkteverteilung** werden aktualisiert.
|
||||
- Die **Statistiken** im Bereich [Analytics](/docs/handbook/analytics) werden
|
||||
neu berechnet.
|
||||
|
||||
## Statistik-Bereiche auf der Detailseite
|
||||
|
||||
- **Bewertungsübersicht:** Nützlichkeit & Bedienbarkeit als Durchschnitt mit
|
||||
Fortschrittsbalken.
|
||||
- **Punkteverteilung:** Anzahl der Bewertungen pro Stern (1★–5★).
|
||||
- **Verlauf:** Linienchart der kombinierten/Teilwerte über die Zeit
|
||||
(erst ab mehreren Bewertungen sichtbar).
|
||||
|
||||
## API
|
||||
|
||||
- [`POST /tools/{id}/ratings`](/docs/reference/endpoints/ratings#createRating) — Bewertung abgeben
|
||||
- [`GET /tools/{id}/ratings`](/docs/reference/endpoints/ratings#listToolRatings) — Bewertungen eines Tools
|
||||
@@ -1,70 +0,0 @@
|
||||
|
||||
# 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).
|
||||
@@ -1,70 +0,0 @@
|
||||
|
||||
# Datenmodell
|
||||
|
||||
Dieses Kapitel erklärt die zentralen Datenobjekte von toolr auf Ebene der
|
||||
Anwendung. Die vollständige, automatisch generierte Referenz aller Felder,
|
||||
Typen und Constraints findest du in der
|
||||
[API-Referenz](/docs/reference/schemas/tool).
|
||||
|
||||
## Tool
|
||||
|
||||
Das Herzstück: ein im Katalog erfasstes Werkzeug.
|
||||
|
||||
| Eigenschaft | Beschreibung |
|
||||
| --- | --- |
|
||||
| `id` | Eindeutige Kennung |
|
||||
| `name` | Anzeigename |
|
||||
| `description` | Beschreibung (Was macht das Tool?) |
|
||||
| `category` | Kategorie-Zuordnung |
|
||||
| `websiteUrl` | Offizielle Website (optional) |
|
||||
| `iconUrl` | Logo-/Icon-URL (optional) |
|
||||
| `features` | Liste von Fähigkeiten |
|
||||
| `tags` | Liste von Schlagwörtern |
|
||||
| `createdAt` / `updatedAt` | Zeitstempel |
|
||||
| `createdBy` | Erstellende Person |
|
||||
| `deletedAt` / `deletedBy` | Soft-Löschung (Papierkorb) |
|
||||
|
||||
Eingabe-Formulare verwenden die abgeleiteten Schemas
|
||||
[`ToolInput`](/docs/reference/schemas/toolinput) und
|
||||
[`ToolUpdate`](/docs/reference/schemas/toolupdate).
|
||||
Aggregierte Ansichten liefert [`ToolWithStats`](/docs/reference/schemas/toolwithstats)
|
||||
(z. B. mit Durchschnittsbewertung).
|
||||
|
||||
## Rating (Bewertung)
|
||||
|
||||
Eine einzelne Bewertung zu einem Tool:
|
||||
|
||||
- `usefulness` (Nützlichkeit, 1–5) und `usability` (Bedienbarkeit, 1–5)
|
||||
- optional `comment` und ein Anzeigename (`reviewerName`)
|
||||
- Zeitstempel
|
||||
|
||||
Eingabe-Schema: [`RatingInput`](/docs/reference/schemas/ratinginput).
|
||||
|
||||
## User & Auth
|
||||
|
||||
- [`User`](/docs/reference/schemas/user) — Benutzerkonto mit Rolle (User/Admin)
|
||||
und Tarif (Free/Premium/Enterprise).
|
||||
- [`AuthUser`](/docs/reference/schemas/authuser) — das angemeldete Profil
|
||||
inklusive `entitlements` (verfügbare Features).
|
||||
- [`UserPreferences`](/docs/reference/schemas/userpreferences) — Ansichts- und
|
||||
Dichte-Präferenzen sowie die `watchlist` (Liste von Tool-IDs).
|
||||
|
||||
## Analytics
|
||||
|
||||
Die Statistik-Endpunkte liefern aggregierte Daten:
|
||||
|
||||
- [`AnalyticsSummary`](/docs/reference/schemas/analyticssummary) — globale
|
||||
Kennzahlen (Anzahl Tools/Bewertungen, Kategorien, Durchschnitt).
|
||||
- [`TopToolEntry`](/docs/reference/schemas/TopToolEntry) — ein Eintrag der
|
||||
Top-Tools.- [`CategoryStats`](/docs/reference/schemas/categorystats) — Tool-Anzahl je
|
||||
Kategorie.
|
||||
- [`RatingDistribution`](/docs/reference/schemas/ratingdistribution) —
|
||||
Punkteverteilung (Nützlichkeit & Bedienbarkeit).
|
||||
- [`ScoreBucket`](/docs/reference/schemas/scorebucket) — ein Werte-Bucket.
|
||||
|
||||
## Weitere
|
||||
|
||||
- [`VersionInfo`](/docs/reference/schemas/versioninfo) — Version, Commit-SHA,
|
||||
Build-Datum und Papierkorb-Aufbewahrung der laufenden Instanz.
|
||||
- [`AuditLog`](/docs/reference/schemas/auditlog) — ein Protokolleintrag
|
||||
(Aktion, Entität, Zeitstempel, Akteur, Änderungen).
|
||||
@@ -1,53 +0,0 @@
|
||||
|
||||
# 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)
|
||||
@@ -1,138 +0,0 @@
|
||||
[
|
||||
{
|
||||
"slug": "index",
|
||||
"file": "index.md",
|
||||
"title": "Überblick",
|
||||
"order": 1,
|
||||
"fileEn": "index.en.md",
|
||||
"titleEn": "Overview"
|
||||
},
|
||||
{
|
||||
"slug": "getting-started",
|
||||
"file": "getting-started.md",
|
||||
"title": "Erste Schritte",
|
||||
"order": 2,
|
||||
"fileEn": "getting-started.en.md",
|
||||
"titleEn": "Getting Started"
|
||||
},
|
||||
{
|
||||
"slug": "konto",
|
||||
"file": "konto.md",
|
||||
"title": "Anmelden & Konto",
|
||||
"order": 3,
|
||||
"fileEn": "konto.en.md",
|
||||
"titleEn": "Login & Account"
|
||||
},
|
||||
{
|
||||
"slug": "tools-finden",
|
||||
"file": "tools-finden.md",
|
||||
"title": "Tools finden & durchsuchen",
|
||||
"order": 4,
|
||||
"fileEn": "tools-finden.en.md",
|
||||
"titleEn": "Find & browse tools"
|
||||
},
|
||||
{
|
||||
"slug": "tool-anlegen",
|
||||
"file": "tool-anlegen.md",
|
||||
"title": "Tool anlegen",
|
||||
"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,
|
||||
"fileEn": "tool-bearbeiten.en.md",
|
||||
"titleEn": "Edit & delete tools"
|
||||
},
|
||||
{
|
||||
"slug": "bewerten",
|
||||
"file": "bewerten.md",
|
||||
"title": "Bewerten",
|
||||
"order": 7,
|
||||
"fileEn": "bewerten.en.md",
|
||||
"titleEn": "Rating"
|
||||
},
|
||||
{
|
||||
"slug": "watchlist",
|
||||
"file": "watchlist.md",
|
||||
"title": "Watchlist",
|
||||
"order": 8,
|
||||
"fileEn": "watchlist.en.md",
|
||||
"titleEn": "Watchlist"
|
||||
},
|
||||
{
|
||||
"slug": "vergleichen",
|
||||
"file": "vergleichen.md",
|
||||
"title": "Vergleichen",
|
||||
"order": 9,
|
||||
"fileEn": "vergleichen.en.md",
|
||||
"titleEn": "Compare"
|
||||
},
|
||||
{
|
||||
"slug": "analytics",
|
||||
"file": "analytics.md",
|
||||
"title": "Analytics",
|
||||
"order": 10,
|
||||
"fileEn": "analytics.en.md",
|
||||
"titleEn": "Analytics"
|
||||
},
|
||||
{
|
||||
"slug": "plaene",
|
||||
"file": "plaene.md",
|
||||
"title": "Pläne & Berechtigungen",
|
||||
"order": 11,
|
||||
"fileEn": "plaene.en.md",
|
||||
"titleEn": "Plans & Permissions"
|
||||
},
|
||||
{
|
||||
"slug": "kosten",
|
||||
"file": "kosten.md",
|
||||
"title": "Kosten erfassen",
|
||||
"order": 12,
|
||||
"fileEn": "kosten.en.md",
|
||||
"titleEn": "Recording Costs"
|
||||
},
|
||||
{
|
||||
"slug": "administration",
|
||||
"file": "administration.md",
|
||||
"title": "Administration",
|
||||
"order": 13,
|
||||
"fileEn": "administration.en.md",
|
||||
"titleEn": "Administration"
|
||||
},
|
||||
{
|
||||
"slug": "redundanz",
|
||||
"file": "redundanz.md",
|
||||
"title": "Redundanz-Dashboard",
|
||||
"order": 14,
|
||||
"fileEn": "redundanz.en.md",
|
||||
"titleEn": "Redundancy dashboard"
|
||||
},
|
||||
{
|
||||
"slug": "papierkorb",
|
||||
"file": "papierkorb.md",
|
||||
"title": "Papierkorb",
|
||||
"order": 15,
|
||||
"fileEn": "papierkorb.en.md",
|
||||
"titleEn": "Trash"
|
||||
},
|
||||
{
|
||||
"slug": "tastatur",
|
||||
"file": "tastatur.md",
|
||||
"title": "Tastenkürzel & Kommandopalette",
|
||||
"order": 16,
|
||||
"fileEn": "tastatur.en.md",
|
||||
"titleEn": "Keyboard shortcuts & command palette"
|
||||
},
|
||||
{
|
||||
"slug": "datenmodell",
|
||||
"file": "datenmodell.md",
|
||||
"title": "Datenmodell",
|
||||
"order": 17,
|
||||
"fileEn": "datenmodell.en.md",
|
||||
"titleEn": "Data model"
|
||||
}
|
||||
]
|
||||
@@ -1,52 +0,0 @@
|
||||
|
||||
# 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).
|
||||
@@ -1,53 +0,0 @@
|
||||
|
||||
# Anmelden & Konto
|
||||
|
||||
## Anmelden
|
||||
|
||||
Klicke unten links in der Seitenleiste auf **Anmelden**. Je nach Konfiguration
|
||||
der Instanz:
|
||||
|
||||
- **Lokale Konten:** Benutzername und Passwort eingeben. Die Konten werden von
|
||||
einem Admin angelegt (siehe [Administration](/docs/handbook/administration)).
|
||||
- **OIDC (SSO):** Du wirst an den konfigurierten Identitätsanbieter
|
||||
weitergeleitet und meldest dich dort an.
|
||||
|
||||
Der aktive Modus steht im Endpunkt
|
||||
[`GET /auth/mode`](/docs/reference/endpoints/auth#getAuthMode).
|
||||
|
||||
> Die Login-Seite erreichst du direkt unter `/login`. Nach erfolgreicher
|
||||
> Anmeldung wirst du zur ursprünglich aufgerufenen Seite zurückgeleitet.
|
||||
|
||||
## Benutzerprofil
|
||||
|
||||
Dein Profil (Avatar, Name, E-Mail, Tarif) siehst du unten links im
|
||||
Benutzermenü. Dort stehen dir folgende Aktionen zur Verfügung:
|
||||
|
||||
- **Watchlist** — deine gespeicherten Tools (nur mit dem entsprechenden Tarif).
|
||||
- **Papierkorb** — wiederherstellbare, gelöschte Tools (Premium/Enterprise).
|
||||
- **Passwort ändern** — für lokale Konten direkt in toolr; für OIDC-Konten wird
|
||||
die Passwortverwaltung im Identitätsanbieter angeboten.
|
||||
- **Abmelden** — beendet deine Sitzung.
|
||||
|
||||
## Passwort ändern (lokales Konto)
|
||||
|
||||
1. Öffne das Benutzermenü unten links.
|
||||
2. Wähle **Passwort ändern**.
|
||||
3. Gib das **aktuelle** sowie ein **neues** Passwort ein (mind. 6 Zeichen) und
|
||||
bestätige es.
|
||||
4. Speichern — das Passwort wird sofort übernommen.
|
||||
|
||||
API-Referenz: [`POST /auth/me/password`](/docs/reference/endpoints/auth#changeMyPassword).
|
||||
|
||||
## Anzeigeeinstellungen
|
||||
|
||||
Über die Schaltflächen oben rechts kannst du:
|
||||
|
||||
- **Sprache** wechseln (Deutsch / Englisch),
|
||||
- **Theme** umschalten (Hell / Dunkel / System),
|
||||
- die **Listenansicht** und **Dichte** im Bereich Tools durchsuchen anpassen
|
||||
(siehe [Tools finden & durchsuchen](/docs/handbook/tools-finden)).
|
||||
|
||||
Deine Präferenzen (inkl. Watchlist) werden im Endpunkt
|
||||
[`GET /auth/me/preferences`](/docs/reference/endpoints/auth#getMePreferences)
|
||||
gespeichert und über [`PUT /auth/me/preferences`](/docs/reference/endpoints/auth#updateMePreferences)
|
||||
aktualisiert.
|
||||
@@ -1,35 +0,0 @@
|
||||
|
||||
# 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)).
|
||||
@@ -1,35 +0,0 @@
|
||||
|
||||
# Kosten erfassen
|
||||
|
||||
Auf der Detailseite eines Tools kannst du Kosten- und Lizenzmodelle eintragen,
|
||||
damit die Gesamtkosten je Tool transparent werden.
|
||||
|
||||
> Kosten ist ein **Premium-Feature** (`costs`, Premium/Enterprise). Admins
|
||||
> haben immer Zugriff.
|
||||
|
||||
## Kosten hinzufügen
|
||||
|
||||
Klicke auf **Kosten hinzufügen** im Kosten-Bereich der Detailseite und fülle
|
||||
das Formular aus:
|
||||
|
||||
| Feld | Hinweise |
|
||||
| --- | --- |
|
||||
| **Lizenztyp** | Free / Subscription / One-Time / Usage-Based |
|
||||
| **Abrechnungszeitraum** | Nur für „Subscription": Monatlich / Quartalsweise / Jährlich |
|
||||
| **Kosten** | Betrag als Zahl |
|
||||
| **Währung** | EUR / USD / GBP / CHF |
|
||||
| **Notizen** | Optionaler Freitext |
|
||||
|
||||
Speichern legt den Eintrag an. Jeder Kosten-Eintrag wird als Karte mit
|
||||
Lizenz-Badge, Abrechnungszeitraum, Betrag (`Betrag Währung` bzw. „Free") und
|
||||
Notizen angezeigt.
|
||||
|
||||
## Kosten bearbeiten & löschen
|
||||
|
||||
Beim Überfahren einer Kosten-Karte erscheinen die Aktionen **Bearbeiten**
|
||||
(Bleistift) und **Löschen** (Papierkorb).
|
||||
|
||||
## API
|
||||
|
||||
Die Kosten-Daten werden über die Tool-Endpunkte verwaltet
|
||||
(siehe [API-Referenz](/docs/reference/endpoints/tools)).
|
||||
@@ -1,40 +0,0 @@
|
||||
|
||||
# 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)
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
# Papierkorb
|
||||
|
||||
Der **Papierkorb** (`/trash`) enthält soft gelöschte Tools. Mit Papierkorb-Zugang
|
||||
können sie wiederhergestellt werden; endgültiges Löschen ist Admins vorbehalten.
|
||||
|
||||
> Der Papierkorb ist ein **Premium-Feature** (`trash`, Premium/Enterprise).
|
||||
> Admins haben immer Zugriff.
|
||||
|
||||
## Zugang
|
||||
|
||||
Der Papierkorb ist über das Benutzermenü oder die Seitenleiste erreichbar.
|
||||
Ohne `trash`-Berechtigung erscheint ein Hinweis auf den Tarifwechsel.
|
||||
|
||||
## Wiederherstellen
|
||||
|
||||
- Markiere ein oder mehrere Tools (Checkboxen).
|
||||
- Klicke auf **Wiederherstellen (N)** — die Tools erscheinen wieder in allen
|
||||
öffentlichen Ansichten.
|
||||
|
||||
> Wiederherstellen steht jeder Person mit Papierkorb-Zugang zur Verfügung.
|
||||
|
||||
## Endgültig löschen (nur Admin)
|
||||
|
||||
- **Löschen (N)** entfernt die ausgewählten Tools **endgültig** — inklusive
|
||||
aller Bewertungen, Kosten und Verknüpfungen. Das kann nicht rückgängig
|
||||
gemacht werden.
|
||||
- **Papierkorb leeren** entfernt alle soft gelöschten Tools endgültig.
|
||||
|
||||
## Tabelle
|
||||
|
||||
Der Papierkorb listet: Name, Kategorie, **Gelöscht am** (`tt.MM.jjjj HH:mm`),
|
||||
**Gelöscht von** sowie Aktionen (Wiederherstellen; Löschen nur Admin). Die Suche
|
||||
filtert nach Namen.
|
||||
|
||||
## API
|
||||
|
||||
- [`GET /tools/trash`](/docs/reference/endpoints/tools#listTrashedTools) — Liste
|
||||
- [`POST /tools/trash/restore`](/docs/reference/endpoints/tools#restoreTools) — Wiederherstellen
|
||||
- [`DELETE /tools/trash`](/docs/reference/endpoints/tools#deleteTrashedTools) — Endgültig löschen (Admin)
|
||||
- [`POST /tools/trash/empty`](/docs/reference/endpoints/tools#emptyTrash) — Papierkorb leeren (Admin)
|
||||
@@ -1,41 +0,0 @@
|
||||
|
||||
# Pläne & Berechtigungen
|
||||
|
||||
toolr unterscheidet **Tarife** (Tier) und **Rollen**. Admins umgehen alle
|
||||
Feature-Beschränkungen.
|
||||
|
||||
## Tarife
|
||||
|
||||
| Tarif | Beschreibung |
|
||||
| --- | --- |
|
||||
| **Free** | Grundfunktionen: suchen, filtern, ansehen, Analytics |
|
||||
| **Premium** | Zusätzlich Watchlist, Vergleichen, Papierkorb, Kosten |
|
||||
| **Enterprise** | Alle Premium-Features + erweiterter Support |
|
||||
|
||||
### Feature-Berechtigungen
|
||||
|
||||
Premium/Enterprise schalten folgende Features frei:
|
||||
|
||||
| Feature | Funktion | Mehr erfahren |
|
||||
| --- | --- | --- |
|
||||
| `compare` | Tools vergleichen | [Vergleichen](/docs/handbook/vergleichen) |
|
||||
| `watchlist` | Favoritenliste | [Watchlist](/docs/handbook/watchlist) |
|
||||
| `trash` | Papierkorb (soft gelöschte Tools) | [Papierkorb](/docs/handbook/papierkorb) |
|
||||
| `costs` | Kosten-/Lizenzmodelle erfassen | [Kosten erfassen](/docs/handbook/kosten) |
|
||||
|
||||
Fehlt dir ein Feature, zeigt die App einen **Upgrade-Hinweis** mit Link zur
|
||||
Tarifverwaltung.
|
||||
|
||||
## Rollen
|
||||
|
||||
| Rolle | Berechtigungen |
|
||||
| --- | --- |
|
||||
| **User** | Standard-Konto: Tools anlegen/bewerten, eigene Tools bearbeiten |
|
||||
| **Admin** | Alle User-Rechte + Verwaltung, Audit-Log, Redundanz, Papierkorb leeren, Tool-Verknüpfungen |
|
||||
|
||||
Admins passieren **alle** Feature-Checks — auch ohne Premium-Tarif.
|
||||
|
||||
## Tarif-/Rollenverwaltung
|
||||
|
||||
Die Zuordnung von Rolle und Tarif wird durch Admins im Bereich
|
||||
[Administration](/docs/handbook/administration) (Tab „Nutzer") verwaltet.
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
# 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
|
||||
@@ -1,38 +0,0 @@
|
||||
|
||||
# Redundanz-Dashboard
|
||||
|
||||
Das **Redundanz-Dashboard** (`/admin/redundancy`) ist ein Admin-Werkzeug zur
|
||||
automatischen Erkennung doppelter oder stark überlappender Tools — jeweils
|
||||
pro Kategorie — inklusive Kosten- und Bewertungsvergleich.
|
||||
|
||||
> Der Zugriff ist ausschließlich Admins vorbehalten (die API ist
|
||||
> admin-geschützt).
|
||||
|
||||
## Aufbau
|
||||
|
||||
- **Pro Kategorie** wird eine Gruppe angezeigt: Name der Kategorie,
|
||||
Anzahl Tools und Vergleiche sowie ggf. die **gesamten monatlichen Kosten**
|
||||
(z. B. `€X.XX/mo gesamt`).
|
||||
- Jedes Tool wird als Karte dargestellt: Name, monatliche Kosten, Anzahl der
|
||||
Bewertungen, kombinierte Bewertung, Lizenz-Badges und Feature-Anzahl.
|
||||
|
||||
## Vergleiche & Empfehlungen
|
||||
|
||||
Für jedes Tool-Paar erscheint:
|
||||
|
||||
- Tool A vs. Tool B, jeweils mit Bewertung (`X.X ★`) und monatlichen Kosten.
|
||||
- **Überlappung** in Prozent (Fortschrittsbalken in der Mitte).
|
||||
- Eine **Empfehlung** mit Konfidenz-Farbe:
|
||||
- **hoch** (grün), **mittel** (gelb), **niedrig** (grau)
|
||||
- Das empfohlene, bessere Tool wird mit „Daumen hoch" markiert und begründet.
|
||||
|
||||
## Manuelle Bewertung
|
||||
|
||||
Du kannst ein Paar manuell bewerten: Klicke auf Tool A oder Tool B, um
|
||||
festzuhalten, welches besser ist. Die Auswahl wird gespeichert und die
|
||||
Darstellung aktualisiert.
|
||||
|
||||
## API
|
||||
|
||||
- [`GET /api/admin/redundancy`](#) — Daten laden (admin-geschützt)
|
||||
- [`POST /api/admin/redundancy/evaluate`](#) — manuelle Bewertung speichern
|
||||
@@ -1,36 +0,0 @@
|
||||
|
||||
# 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.
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
# 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
|
||||
@@ -1,46 +0,0 @@
|
||||
|
||||
# Tool anlegen
|
||||
|
||||
Um ein neues Tool zum Katalog hinzuzufügen, klicke auf **Tool hinzufügen**
|
||||
(`/tools/new`). Das Anlegen erfordert ein Konto — ohne Anmeldung erscheint ein
|
||||
Hinweis mit Login-Button.
|
||||
|
||||
## Formularfelder
|
||||
|
||||
| Feld | Pflicht | Hinweise |
|
||||
| --- | --- | --- |
|
||||
| **Name** | Ja | Mind. 2 Zeichen |
|
||||
| **Kategorie** | Ja | Auswahlliste; neue Kategorien lassen sich direkt anlegen |
|
||||
| **Website URL** | Nein | Gültige URL (z. B. `https://...`) |
|
||||
| **Icon / Logo URL** | Nein | Gültige URL; Vorschau wird live angezeigt |
|
||||
| **Beschreibung** | Ja | Mind. 10 Zeichen; beschreibe, was das Tool tut |
|
||||
| **Features** | Nein | Dynamische Liste mit Autovervollständigung (max. 6) |
|
||||
| **Tags** | Nein | Dynamische Liste mit Autovervollständigung |
|
||||
|
||||
Neben jedem Feld führt das **?‑Icon** direkt zur zugehörigen Feldbeschreibung
|
||||
in der [Datenmodell-Referenz](/docs/reference/schemas/toolinput).
|
||||
|
||||
### Kategorie
|
||||
|
||||
- Tippe, um nach bestehenden Kategorien zu suchen.
|
||||
- Wähle **+ Erstelle „..."**, um eine neue Kategorie anzulegen.
|
||||
|
||||
### Features & Tags
|
||||
|
||||
- **Feature hinzufügen** / **Tag hinzufügen** hängt eine neue Zeile an.
|
||||
- Die Eingabefelder schlagen bestehende Features/Tags vor
|
||||
(Autovervollständigung, max. 6 Vorschläge).
|
||||
- Mit dem **×**‑Button entfernst du einzelne Zeilen.
|
||||
- Features und Tags helfen beim Filtern und Wiederfinden.
|
||||
|
||||
## Speichern
|
||||
|
||||
Klicke auf **Tool hinzufügen**. Nach erfolgreicher Anlage wirst du auf die
|
||||
Detailseite des neuen Tools weitergeleitet.
|
||||
|
||||
## API
|
||||
|
||||
- [`POST /tools`](/docs/reference/endpoints/tools#createTool) — Tool anlegen
|
||||
- [`GET /categories`](/docs/reference/endpoints/tools#listCategories) — Kategorien
|
||||
- [`GET /features/all`](/docs/reference/endpoints/tools#listAllFeatures) — Features
|
||||
- [`GET /tags/all`](/docs/reference/endpoints/tools#listAllTags) — Tags
|
||||
@@ -1,32 +0,0 @@
|
||||
|
||||
# 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).
|
||||
@@ -1,33 +0,0 @@
|
||||
|
||||
# Tool bearbeiten & löschen
|
||||
|
||||
## Bearbeiten
|
||||
|
||||
Auf der Detailseite eines Tools findest du die Schaltfläche **Bearbeiten**
|
||||
(nur für die Person, die das Tool angelegt hat, sowie für Admins).
|
||||
|
||||
Die Bearbeitungsseite (`/tools/:id/edit`) enthält dieselben Felder wie beim
|
||||
Anlegen (Name, Kategorie, Website/Icon-URL, Beschreibung, Features, Tags) —
|
||||
bereits mit den aktuellen Werten befüllt.
|
||||
|
||||
- **Speichern** übernimmt die Änderungen.
|
||||
- **Abbrechen** führt zurück zur Detailseite.
|
||||
|
||||
API-Referenz: [`PATCH /tools/{id}`](/docs/reference/endpoints/tools#updateTool).
|
||||
|
||||
## Löschen
|
||||
|
||||
Über **Löschen** auf der Detailseite wird das Tool entfernt. Das Verhalten
|
||||
hängt von deinem Tarif ab:
|
||||
|
||||
- **Mit Papierkorb-Zugang** (Premium/Enterprise oder Admin): Das Tool wird
|
||||
**soft gelöscht** — es verschwindet aus allen öffentlichen Ansichten, kann
|
||||
aber im [Papierkorb](/docs/handbook/papierkorb) wiederhergestellt oder
|
||||
endgültig gelöscht werden.
|
||||
- **Ohne Papierkorb-Zugang:** Das Tool wird **endgültig** gelöscht und kann
|
||||
nicht wiederhergestellt werden.
|
||||
|
||||
Die Löschung ist nur für die Person, die das Tool angelegt hat, sowie für
|
||||
Admins möglich.
|
||||
|
||||
API-Referenz: [`DELETE /tools/{id}`](/docs/reference/endpoints/tools#deleteTool).
|
||||
@@ -1,66 +0,0 @@
|
||||
|
||||
# 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).
|
||||
@@ -1,67 +0,0 @@
|
||||
|
||||
# Tools finden & durchsuchen
|
||||
|
||||
Der Bereich **Tools durchsuchen** (`/tools`) ist der Einstieg in den Katalog.
|
||||
Hier kombinierst du Suche, Filter und Sortierung, um genau die Tools zu finden,
|
||||
die dich interessieren.
|
||||
|
||||
## Suche
|
||||
|
||||
- Die **Suchleiste** durchsucht Name und Beschreibung (Volltext).
|
||||
- Tastenkürzel: Drücke **`/`**, um die Suche zu fokussieren.
|
||||
- Die Eingabe ist deaktiviert (Debounce), damit bei jedem Tastendruck sofort
|
||||
nachgefiltert wird.
|
||||
|
||||
## Filtern
|
||||
|
||||
Über die Schaltfläche **Filter** (mit Badge für die Anzahl aktiver Filter)
|
||||
öffnest du den Filter-Popover mit:
|
||||
|
||||
- **Tags** — Auswahl über Checkboxen (scrollbare Liste).
|
||||
- **Features** — Auswahl über Checkboxen.
|
||||
- **Mindestbewertung** — Schieberegler von 0 bis 5 (Schritte von 0,5); zeigt
|
||||
z. B. „3.0+" an.
|
||||
|
||||
Aktive Filter erscheinen als **entfernbare Chips** über der Ergebnisliste.
|
||||
Mit **Filter zurücksetzen** bzw. **Alle entfernen** räumst du sie wieder auf.
|
||||
|
||||
## Sortieren
|
||||
|
||||
Über das Dropdown **Sortieren** stehen folgende Optionen zur Verfügung:
|
||||
|
||||
| Sortierung | Beschreibung |
|
||||
| --- | --- |
|
||||
| Neueste | Neue Tools zuerst |
|
||||
| Top bewertet | Nach kombinierter Bewertung |
|
||||
| Meistbewertet | Nach Anzahl der Bewertungen |
|
||||
| Name (A–Z) | Alphabetisch aufsteigend |
|
||||
| Name (Z–A) | Alphabetisch absteigend |
|
||||
| Zuletzt aktualisiert | Nach letztem Update |
|
||||
|
||||
## Ansicht & Dichte
|
||||
|
||||
- **Ansicht wechseln:** Raster / Tabelle / Zeilen.
|
||||
- **Dichte:** gemütlich / kompakt (Schieberegler).
|
||||
|
||||
Deine Auswahl wird gespeichert — lokal im Browser und für angemeldete Nutzer:innen
|
||||
zusätzlich serverseitig in den Präferenzen. Ansicht, Dichte, Suche, Filter und
|
||||
Sortierung werden dabei in die URL übernommen, sodass du Ergebnisse teilen
|
||||
kannst.
|
||||
|
||||
## Tabellenansicht
|
||||
|
||||
In der Tabellenansicht sind die Spalten **Tool**, **Bewertung** und **Anzahl
|
||||
Bewertungen** sortierbar. Beim Überfahren einer Zeile erscheint eine Vorschau
|
||||
mit Bewertungsdetails, Tags und Mini-Balken.
|
||||
|
||||
## Auswählen für Vergleich & Watchlist
|
||||
|
||||
- Auf jeder Karte/Zeile findest du ein **Vergleichs-Icon**, mit dem du Tools zur
|
||||
[Vergleichsleiste](/docs/handbook/vergleichen) hinzufügst.
|
||||
- Das **Lesezeichen-Icon** speichert Tools in deiner
|
||||
[Watchlist](/docs/handbook/watchlist) (nur mit dem entsprechenden Tarif).
|
||||
|
||||
## API
|
||||
|
||||
Alle Such-, Filter- und Sortierparameter entsprechen den Query-Parametern von
|
||||
[`GET /tools`](/docs/reference/endpoints/tools#listTools).
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
# 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).
|
||||
@@ -1,42 +0,0 @@
|
||||
|
||||
# Vergleichen
|
||||
|
||||
Mit der Vergleichsfunktion stellst du mehrere Tools **nebeneinander** gegenüber —
|
||||
ideal, um eine fundierte Entscheidung zu treffen.
|
||||
|
||||
> Vergleichen ist ein **Premium-Feature** (Premium/Enterprise) und steht Admins
|
||||
> immer zur Verfügung.
|
||||
|
||||
## Tools auswählen
|
||||
|
||||
1. Im Bereich **Tools durchsuchen** klickst du auf jeder Karte/Zeile auf das
|
||||
**Vergleichs-Icon** (Waage).
|
||||
2. Unten erscheint die **Vergleichsleiste** mit den ausgewählten Tools als
|
||||
Chips. Du kannst einzelne Tools entfernen (×) oder die Auswahl leeren.
|
||||
3. Klicke auf **Vergleichen (N)**, um zur Vergleichsansicht zu gelangen.
|
||||
|
||||
> Ohne Premium-Tarif ist der Button gesperrt (Schloss-Icon). Über den
|
||||
> Dialog gelangst du zum Tarifwechsel
|
||||
> (siehe [Pläne & Berechtigungen](/docs/handbook/plaene)).
|
||||
|
||||
## Die Vergleichsansicht
|
||||
|
||||
Die Ansicht zeigt eine Tabelle mit einer Spalte pro Tool. Zeilen:
|
||||
|
||||
| Zeile | Inhalt |
|
||||
| --- | --- |
|
||||
| **Bewertung** | Sterne + Wert (z. B. `4.2/5`) |
|
||||
| **Nützlichkeit** | Wert (X.X/5) |
|
||||
| **Bedienbarkeit** | Wert (X.X/5) |
|
||||
| **Anzahl Bewertungen** | Anzahl |
|
||||
| **Beschreibung** | Text |
|
||||
| **Features** | Badges |
|
||||
| **Tags** | Badges |
|
||||
| **Zuletzt aktualisiert** | Datum |
|
||||
|
||||
Der **beste Wert** pro Zeile wird hervorgehoben (mit Trophäen-Icon).
|
||||
|
||||
## API
|
||||
|
||||
Die Vergleichsansicht liest die Daten über
|
||||
[`GET /compare`](/docs/reference/endpoints/tools#listCompareTools).
|
||||
@@ -1,33 +0,0 @@
|
||||
|
||||
# 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).
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,49 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,49 +0,0 @@
|
||||
# v0.8.3 — Release Notes
|
||||
|
||||
**Datum:** 2026-08-04 · **Tag:** [`v0.8.3`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.3)
|
||||
|
||||
## Neue Features
|
||||
|
||||
- **Versionsbasierte Dokumentation**: Jedes Release trägt jetzt einen
|
||||
vollständigen Doku-Snapshot (Handbuch + API-Referenz). Über das
|
||||
Versions-Dropdown gelangst du zur Doku der jeweiligen Version
|
||||
(`/docs/vX.Y.Z/…`) — inklusive Handbuch und Referenz, wie sie zum
|
||||
Release-Zeitpunkt galten. Die letzten 7 Versionen bleiben verfügbar.
|
||||
- **Hilfe-Buttons in Formularen (NetBox-Stil)**: Bei Bewertung, Tool
|
||||
anlegen/bearbeiten, Kosten, Vergleichen, Watchlist und Analytics gibt es
|
||||
jetzt ein Hilfe-Icon, das direkt zur passenden Anleitung im Handbuch führt.
|
||||
|
||||
## Fixes & Verbesserungen
|
||||
|
||||
- **Gitea-Tag-Links korrigiert**: `…/tags/vX.Y.Z` war eine 404-Seite; die
|
||||
korrekte URL lautet `/releases/tag/vX.Y.Z` (in allen Release-Notes und der
|
||||
Doku-Seite).
|
||||
- Versions-Wechsel führt jetzt zur Doku der gewählten Version statt direkt
|
||||
nur zur Release-Seite; alte `/docs/vX.Y.Z`-Redirects entfallen.
|
||||
- Doku-Generator: `--snapshot` erzeugt Handbuch + Referenz pro Version; beim
|
||||
Build werden die Snapshots der letzten 7 Versionen eingespielt.
|
||||
|
||||
## API-Änderungen
|
||||
|
||||
- Keine Änderungen an der API.
|
||||
|
||||
## Betrieb / Upgrade
|
||||
|
||||
- **Env-Vars:** unverändert.
|
||||
- **Migration:** keine.
|
||||
- **Breaking Changes:** keine. Neue Release-Notes müssen den korrekten
|
||||
Tag-Link verwenden (siehe `docs/releases/TEMPLATE.md`). Für Versionen vor
|
||||
diesem Release können Snapshots nachträglich erzeugt werden:
|
||||
`node scripts/src/generate-docs.mjs --snapshot vX.Y.Z`.
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
- Ältere Releases ohne Snapshot (z. B. v0.6.0) zeigen nur ihre Release-Notes,
|
||||
kein Handbuch/keine Referenz. Mit `--snapshot` nachpflegen.
|
||||
- Die Doku-Version entspricht dem Doku-Stand zum Zeitpunkt der Erstellung des
|
||||
Snapshots; Änderungen am Handbuch gelten ab dem jeweils nächsten 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)
|
||||
@@ -1,45 +0,0 @@
|
||||
# v0.8.4 — Release Notes
|
||||
|
||||
**Date:** 2026-08-04 · **Tag:** [`v0.8.4`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.4)
|
||||
|
||||
## New features
|
||||
|
||||
- **Full app localization (German/English)**: All pages and components are
|
||||
now translated — tool detail page, admin area, trash, redundancy dashboard,
|
||||
compare, watchlist, analytics, create and edit tool, admin tools tab,
|
||||
category combobox and theme toggle. The language picker in the top right
|
||||
switches the whole UI.
|
||||
- **Bilingual documentation**: The manual (all 17 pages) and the release
|
||||
notes are now available in German and English. The docs page has a
|
||||
language switcher that switches content, navigation titles and search.
|
||||
Versioned docs snapshots (v0.8.1, v0.8.2) also contain the English pages.
|
||||
|
||||
## Fixes & improvements
|
||||
|
||||
- All remaining hardcoded UI strings replaced with `t()` calls; the locales
|
||||
are in sync (453 keys per language, 0 missing).
|
||||
- API reference: table headers (Name/In), required badges and request body
|
||||
labels in the docs are translated.
|
||||
- Docs search respects the active language (separate index per language).
|
||||
|
||||
## API changes
|
||||
|
||||
- No changes to the API.
|
||||
|
||||
## Operations / upgrade
|
||||
|
||||
- **Env vars:** unchanged.
|
||||
- **Migration:** none.
|
||||
- **Breaking changes:** none. The docs generator output now includes
|
||||
additional English files (`*.en.md`) and `search.en.json`; these are
|
||||
generated automatically during the build.
|
||||
|
||||
## Known limitations
|
||||
|
||||
- Versioned docs snapshots before v0.8.1 (e.g. v0.6.0) do not contain
|
||||
English manual pages; the German version is used as fallback there.
|
||||
|
||||
## Links
|
||||
|
||||
- Commit: [`3de59cc`](https://git.kubebase.de/admin/tool-evaluator/commit/3de59cc)
|
||||
- Tag: [`v0.8.4`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.4)
|
||||
@@ -1,48 +0,0 @@
|
||||
# v0.8.4 — Release Notes
|
||||
|
||||
**Datum:** 2026-08-04 · **Tag:** [`v0.8.4`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.4)
|
||||
|
||||
## Neue Features
|
||||
|
||||
- **Komplette Lokalisierung der App (Deutsch/Englisch)**: Alle Seiten und
|
||||
Komponenten sind jetzt übersetzt — Tool-Detailseite, Verwaltung, Papierkorb,
|
||||
Redundanz-Dashboard, Vergleichen, Watchlist, Analytics, Tool anlegen und
|
||||
bearbeiten, Admin-Bereich inkl. Tools-Tab, Kategorie-Combobox und
|
||||
Theme-Umschalter. Die Sprachauswahl oben rechts wechselt die gesamte
|
||||
Oberfläche.
|
||||
- **Dokumentation zweisprachig**: Handbuch (alle 17 Seiten) und Release-Notes
|
||||
sind jetzt in Deutsch und Englisch verfügbar. In der Doku gibt es einen
|
||||
Sprach-Umschalter, der Inhalte, Navigationstitel und Suche umschaltet.
|
||||
Auch versionierte Doku-Snapshots (v0.8.1, v0.8.2) enthalten die englischen
|
||||
Seiten.
|
||||
|
||||
## Fixes & Verbesserungen
|
||||
|
||||
- Alle verbliebenen hartkodierten UI-Strings durch `t()`-Aufrufe ersetzt;
|
||||
die Locales sind synchron (453 Keys pro Sprache, 0 fehlend).
|
||||
- API-Referenz: Tabellen-Header (Name/In), Pflicht-Badges und
|
||||
Request-Body-Label der Doku übersetzt.
|
||||
- Suche in der Doku berücksichtigt die aktive Sprache (eigener Index pro
|
||||
Sprache).
|
||||
|
||||
## API-Änderungen
|
||||
|
||||
- Keine Änderungen an der API.
|
||||
|
||||
## Betrieb / Upgrade
|
||||
|
||||
- **Env-Vars:** unverändert.
|
||||
- **Migration:** keine.
|
||||
- **Breaking Changes:** keine. Die Doku-Generator-Ausgabe umfasst jetzt
|
||||
zusätzliche englische Dateien (`*.en.md`) und `search.en.json`; beim Build
|
||||
werden diese automatisch erzeugt.
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
- Versionierte Doku-Snapshots vor v0.8.1 (z. B. v0.6.0) enthalten keine
|
||||
englischen Handbuch-Seiten; dort greift die deutsche Fassung als Fallback.
|
||||
|
||||
## Links
|
||||
|
||||
- Commit: [`3de59cc`](https://git.kubebase.de/admin/tool-evaluator/commit/3de59cc)
|
||||
- Tag: [`v0.8.4`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.8.4)
|
||||
@@ -0,0 +1,46 @@
|
||||
# v0.9.0 — Release Notes
|
||||
|
||||
**Datum:** 2026-08-05 · **Tag:** [`v0.9.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.9.0)
|
||||
|
||||
## Neue Features
|
||||
|
||||
- **Neuausrichtung der Versionierung:** Die Dokumentation startet mit dieser
|
||||
Version neu bei `v0.9.0`. Ältere Release-Notizen und Versionen-Snapshots
|
||||
(v0.6.0–v0.8.6) wurden entfernt. Das Handbuch und die Referenz (API/Admin)
|
||||
sind jetzt wieder für jede Version verfügbar — auch für neuere Versionen.
|
||||
- **Sprachsuffixe:** Alle Dokumentationsdateien verwenden jetzt einheitlich die
|
||||
Suffixe `.de.md` und `.en.md`. Damit lassen sich später weitere Sprachen
|
||||
ergänzen; die App liefert automatisch die passende Sprachfassung.
|
||||
- **Import — fertige Beispieldatei:** Unter `docs/examples/import-tools.yaml`
|
||||
liegt eine sofort importierbare YAML-Beispieldatei mit 24 gängigen
|
||||
Enterprise-Tools (Kategorien wie Kommunikation, Monitoring, CI/CD,
|
||||
Infrastruktur). Die Datei ist in der Administration-Dokumentation verlinkt.
|
||||
|
||||
## Fixes & Verbesserungen
|
||||
|
||||
- Der Versionsumschalter zeigt die Dokumentation jetzt auch für neuere
|
||||
Versionen korrekt an (vorher fehlende Versionen-Snapshots machten Handbuch und
|
||||
Referenz nicht verfügbar).
|
||||
- Wording bereinigt: Die Tagline und Platzhaltertexte ohne Berufsbezeichnung
|
||||
(„Bewerte dein Tool." / „Rate your Tool.", „Anonymer Benutzer" /
|
||||
„Anonymous User"). Es werden keine Gender-Sonderformen mehr verwendet
|
||||
(`Nutzer:innen` → `Nutzer`, `Ersteller:in` → `Ersteller`).
|
||||
|
||||
## API-Änderungen
|
||||
|
||||
- Keine Änderungen gegenüber v0.8.6 (siehe `lib/api-spec/openapi.yaml`).
|
||||
|
||||
## Betrieb / Upgrade
|
||||
|
||||
- **Migration:** keine. Bereits vorhandene Tools-, Bewertungs- und Konto-Daten
|
||||
bleiben unverändert.
|
||||
- **Breaking Changes:** keine.
|
||||
|
||||
## Bekannte Einschränkungen
|
||||
|
||||
- Keine im Rahmen dieses Releases.
|
||||
|
||||
## Links
|
||||
|
||||
- Commit: [`6eb3dfd`](https://git.kubebase.de/admin/tool-evaluator/commit/6eb3dfd)
|
||||
- Tag: [`v0.9.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.9.0)
|
||||
@@ -0,0 +1,46 @@
|
||||
# v0.9.0 — Release Notes
|
||||
|
||||
**Date:** 2026-08-05 · **Tag:** [`v0.9.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.9.0)
|
||||
|
||||
## New Features
|
||||
|
||||
- **Versioning reset:** Documentation starts fresh at `v0.9.0` with this
|
||||
release. Older release notes and version snapshots (v0.6.0–v0.8.6) have been
|
||||
removed. The handbook and reference (API/admin) are now available again for
|
||||
every version — including newer versions.
|
||||
- **Language suffixes:** All documentation files now consistently use the
|
||||
`.de.md` and `.en.md` suffixes. This makes it easy to add more languages
|
||||
later; the app automatically serves the matching language variant.
|
||||
- **Import — ready-to-use example file:** A ready-to-import YAML example file
|
||||
with 24 common enterprise tools is provided at
|
||||
`docs/examples/import-tools.yaml` (categories such as communication,
|
||||
monitoring, CI/CD, infrastructure). The file is linked from the
|
||||
administration documentation.
|
||||
|
||||
## Fixes & Polish
|
||||
|
||||
- The version switcher now displays documentation correctly for newer versions
|
||||
as well (previously, missing version snapshots made the handbook and
|
||||
reference unavailable).
|
||||
- Wording cleanup: tagline and placeholder texts no longer reference a specific
|
||||
job title ("Rate your Tool.", "Anonymous User"). No gender-specific forms are
|
||||
used anymore.
|
||||
|
||||
## API Changes
|
||||
|
||||
- No changes compared to v0.8.6 (see `lib/api-spec/openapi.yaml`).
|
||||
|
||||
## Operations / Upgrade
|
||||
|
||||
- **Migration:** none. Existing tool, rating, and account data remain
|
||||
unchanged.
|
||||
- **Breaking Changes:** none.
|
||||
|
||||
## Known Limitations
|
||||
|
||||
- None within the scope of this release.
|
||||
|
||||
## Links
|
||||
|
||||
- Commit: [`6eb3dfd`](https://git.kubebase.de/admin/tool-evaluator/commit/6eb3dfd)
|
||||
- Tag: [`v0.9.0`](https://git.kubebase.de/admin/tool-evaluator/releases/tag/v0.9.0)
|
||||
+43
@@ -32,6 +32,49 @@ Zentraler Zugriff auf den Tool-Katalog.
|
||||
- **Massenaktion:** mehrere Tools auswählen und in den Papierkorb verschieben
|
||||
(Bestätigungsdialog; soft gelöschte Tools sind aus allen öffentlichen Ansichten
|
||||
entfernt und können wiederhergestellt oder endgültig gelöscht werden).
|
||||
- **Importieren** (Premium): über die Schaltfläche **Importieren** oben rechts
|
||||
können mehrere Tools gleichzeitig angelegt werden. Das Format wird automatisch
|
||||
erkannt (CSV, JSON oder YAML); für CSV lässt sich der Trenner wählen
|
||||
(automatisch, Komma, Semikolon oder Tabulator). Die Daten lassen sich direkt
|
||||
ins Formular einfügen oder als Datei hochladen.
|
||||
|
||||
Unterstützte Felder: `name` (Pflicht), `description`, `category`, `websiteUrl`,
|
||||
`iconUrl`, `features`, `tags` (`features`/`tags` sind mit `|` getrennte Listen).
|
||||
|
||||
**CSV-Beispiel:**
|
||||
|
||||
```csv
|
||||
name;description;category;features;tags
|
||||
Grafana;Observability- und Dashboard-Plattform;Monitoring;Dashboards|Alerts;devops
|
||||
Prometheus;Zeitreihen-Datenbank;Monitoring;Metrics;devops
|
||||
```
|
||||
|
||||
**JSON-Beispiel:**
|
||||
|
||||
```json
|
||||
[
|
||||
{ "name": "Grafana", "category": "Monitoring", "features": ["Dashboards"] },
|
||||
{ "name": "Prometheus", "category": "Monitoring" }
|
||||
]
|
||||
```
|
||||
|
||||
**YAML-Beispiel:**
|
||||
|
||||
```yaml
|
||||
- name: Grafana
|
||||
category: Monitoring
|
||||
features:
|
||||
- Dashboards
|
||||
- name: Prometheus
|
||||
category: Monitoring
|
||||
```
|
||||
|
||||
Eine umfangreichere, fertig importierbare Beispieldatei liegt unter
|
||||
`docs/examples/import-tools.yaml` (24 gängige Enterprise-Tools).
|
||||
|
||||
Beim Import werden einzelne fehlerhafte Zeilen übersprungen und mit Zeilennummer
|
||||
aufgelistet; korrekte Zeilen werden trotzdem angelegt. Jeder Import erzeugt
|
||||
Audit-Log-Einträge.
|
||||
|
||||
## Tab „Audit-Log"
|
||||
|
||||
+43
@@ -32,6 +32,49 @@ Central access to the tool catalog.
|
||||
- **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).
|
||||
- **Import** (premium): via the **Import** button at the top right, multiple
|
||||
tools can be created at once. The format is detected automatically (CSV,
|
||||
JSON or YAML); for CSV the delimiter can be chosen (auto-detect, comma,
|
||||
semicolon or tab). Data can be pasted directly into the form or uploaded
|
||||
as a file.
|
||||
|
||||
Supported fields: `name` (required), `description`, `category`, `websiteUrl`,
|
||||
`iconUrl`, `features`, `tags` (`features`/`tags` are `|`-separated lists).
|
||||
|
||||
**CSV example:**
|
||||
|
||||
```csv
|
||||
name;description;category;features;tags
|
||||
Grafana;Observability and dashboard platform;Monitoring;Dashboards|Alerts;devops
|
||||
Prometheus;Time-series database;Monitoring;Metrics;devops
|
||||
```
|
||||
|
||||
**JSON example:**
|
||||
|
||||
```json
|
||||
[
|
||||
{ "name": "Grafana", "category": "Monitoring", "features": ["Dashboards"] },
|
||||
{ "name": "Prometheus", "category": "Monitoring" }
|
||||
]
|
||||
```
|
||||
|
||||
**YAML example:**
|
||||
|
||||
```yaml
|
||||
- name: Grafana
|
||||
category: Monitoring
|
||||
features:
|
||||
- Dashboards
|
||||
- name: Prometheus
|
||||
category: Monitoring
|
||||
```
|
||||
|
||||
A larger, ready-to-import example file is located at
|
||||
`docs/examples/import-tools.yaml` (24 common enterprise tools).
|
||||
|
||||
During import, individual faulty rows are skipped and listed with their row
|
||||
number; correct rows are still created. Every import produces audit-log
|
||||
entries.
|
||||
|
||||
## "Audit log" tab
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user