feat(import): bulk tool import (CSV/JSON/YAML) for admins; NetBox-style help button
Build & Push Docker Image / build (push) Successful in 2m49s
Build & Push Docker Image / build (push) Successful in 2m49s
- Add POST /admin/tools/import with format auto-detect, CSV delimiters
(comma/semicolon/tab), per-row validation via CreateToolBody, bulk insert,
audit log entries per imported tool; gated by new 'tool-import' feature
flag (premium/enterprise; admins always pass)
- Add tool-import-dialog UI (format tabs, delimiter select, textarea, file
upload, result/error list) behind hasFeature('tool-import')
- Replace FieldHelp question marks and bare GuideHelp links with a NetBox-style
'Hilfe/Help' outline button (HelpCircle + text) in form headers only
- Sync locales to 482 keys per language (de/en), update handbook docs
(administration import section, index/plaene feature tables), regenerate
API client + zod schemas, add yaml dependency
This commit is contained in:
@@ -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>
|
||||
<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>
|
||||
<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")}
|
||||
</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>
|
||||
|
||||
Reference in New Issue
Block a user