feat: compare limit 9, format-aware import placeholder, help links

- Raise compare limit from 8 to 9 (server + client, toast on overflow)
- Make import textarea placeholder match selected format (CSV/JSON/YAML/auto)
- Add GuideHelp links to admin create/edit user dialogs and tool import dialog
This commit is contained in:
opencode
2026-08-05 07:40:47 +02:00
parent e66a332270
commit e164d51574
82 changed files with 9381 additions and 7 deletions
+5 -2
View File
@@ -29,6 +29,7 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
import { Users, ScrollText, ShieldAlert, Plus, Pencil, Trash2, Clock, AlertTriangle, Wrench, Server } from "lucide-react";
import { format } from "date-fns";
import { AdminToolsTab } from "@/components/admin-tools-tab";
import { GuideHelp } from "@/components/guide-help";
import { useTranslation } from "react-i18next";
export default function Admin() {
@@ -359,8 +360,9 @@ export default function Admin() {
<Dialog open={createOpen} onOpenChange={setCreateOpen}>
<DialogContent className="sm:max-w-md">
<DialogHeader>
<DialogHeader className="flex flex-row items-center justify-between gap-2">
<DialogTitle>{t("admin.createNewUser")}</DialogTitle>
<GuideHelp guide="administration" label={t("admin.createNewUser")} />
</DialogHeader>
<div className="space-y-4 py-2">
<div className="space-y-2">
@@ -412,8 +414,9 @@ export default function Admin() {
<Dialog open={!!editUser} onOpenChange={(open) => { if (!open) { setEditUser(null); setEditPassword(""); } }}>
<DialogContent className="sm:max-w-sm">
<DialogHeader>
<DialogHeader className="flex flex-row items-center justify-between gap-2">
<DialogTitle>{t("admin.editUser", { username: editUser?.username })}</DialogTitle>
<GuideHelp guide="administration" label={t("admin.editUser", { username: editUser?.username })} />
</DialogHeader>
<div className="space-y-4 py-2">
<div className="space-y-2">
+10 -1
View File
@@ -18,6 +18,7 @@ import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent,
import { useBrowsePreferences, isViewMode, isDensity } from "@/hooks/use-browse-preferences";
import { useAuth } from "@/hooks/use-auth";
import { useWatchlist } from "@/hooks/use-watchlist";
import { useToast } from "@/hooks/use-toast";
import { CompareBar } from "@/components/compare-bar";
import { useTranslation } from "react-i18next";
import { Input } from "@/components/ui/input";
@@ -91,12 +92,20 @@ export default function ToolsBrowse() {
const { serverView, serverDensity, localView, localDensity, persist } = useBrowsePreferences();
const { hasFeature } = useAuth();
const { isWatched, toggle: toggleWatchlist, canWatchlist } = useWatchlist();
const { toast } = useToast();
const [compareIds, setCompareIds] = useState<number[]>([]);
const [compareUpsellOpen, setCompareUpsellOpen] = useState(false);
function toggleCompare(id: number) {
setCompareIds((prev) => (prev.includes(id) ? prev.filter((x) => x !== id) : [...prev, id]));
setCompareIds((prev) => {
if (prev.includes(id)) return prev.filter((x) => x !== id);
if (prev.length >= 9) {
toast({ title: t("browse.compareLimitReached"), variant: "destructive" });
return prev;
}
return [...prev, id];
});
}
const [search, setSearch] = useState(initialSearch);