fix(i18n): translate submit button, density/view toggles; rename watchlist guide to Merkliste; add guide help buttons
Build & Push Docker Image / build (push) Successful in 2m31s
Build & Push Docker Image / build (push) Successful in 2m31s
This commit is contained in:
@@ -15,6 +15,7 @@ import {
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card";
|
||||
@@ -105,7 +106,10 @@ export function AdminToolsTab() {
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between gap-4 flex-wrap">
|
||||
<div className="space-y-1.5">
|
||||
<CardTitle>{t("adminTools.allTools")}</CardTitle>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
{t("adminTools.allTools")}
|
||||
<GuideHelp guide="administration" label={t("adminTools.allTools")} />
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
{selectedIds.length > 0 ? t("adminTools.selected", { count: selectedIds.length }) : t("adminTools.toolCount", { count: allTools.length })}
|
||||
</CardDescription>
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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")}
|
||||
|
||||
@@ -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";
|
||||
@@ -164,7 +165,10 @@ export function UserMenu() {
|
||||
<Dialog open={pwOpen} onOpenChange={setPwOpen}>
|
||||
<DialogContent className="sm:max-w-md">
|
||||
<DialogHeader>
|
||||
<DialogTitle>{t("auth.changePassword")}</DialogTitle>
|
||||
<DialogTitle className="flex items-center gap-2">
|
||||
{t("auth.changePassword")}
|
||||
<GuideHelp guide="konto" label={t("auth.changePassword")} />
|
||||
</DialogTitle>
|
||||
</DialogHeader>
|
||||
{!user?.isLocal ? (
|
||||
<div className="space-y-4 py-2">
|
||||
@@ -193,7 +197,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, label, Icon }) => (
|
||||
<ToggleGroupItem key={v} value={v} aria-label={label} title={label} data-testid={`view-${v}`}>
|
||||
{OPTIONS.map(({ value: v, labelKey, Icon }) => (
|
||||
<ToggleGroupItem
|
||||
key={v}
|
||||
value={v}
|
||||
aria-label={t(labelKey)}
|
||||
title={t(labelKey)}
|
||||
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",
|
||||
@@ -54,8 +55,16 @@
|
||||
"all": "Alle",
|
||||
"none": "Keine",
|
||||
"language": "Sprache",
|
||||
"viewDetails": "Details ansehen",
|
||||
"guideTooltip": "Anleitung in der Dokumentation"
|
||||
"viewDetails": "Details ansehen",
|
||||
"guideTooltip": "Anleitung in der Dokumentation",
|
||||
"viewMode": "Ansichtsmodus",
|
||||
"viewGrid": "Raster",
|
||||
"viewTable": "Tabelle",
|
||||
"viewRows": "Zeilen",
|
||||
"listDensity": "Listendichte",
|
||||
"densityCompact": "Kompakt",
|
||||
"densityComfortable": "Komfortabel",
|
||||
"densityTitle": "Dichte: {{density}}"
|
||||
},
|
||||
"home": {
|
||||
"welcome": "Willkommen bei toolr",
|
||||
|
||||
@@ -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",
|
||||
@@ -54,8 +55,16 @@
|
||||
"all": "All",
|
||||
"none": "None",
|
||||
"language": "Language",
|
||||
"viewDetails": "View details",
|
||||
"guideTooltip": "Guide in the documentation"
|
||||
"viewDetails": "View details",
|
||||
"guideTooltip": "Guide in the documentation",
|
||||
"viewMode": "View mode",
|
||||
"viewGrid": "Grid",
|
||||
"viewTable": "Table",
|
||||
"viewRows": "Rows",
|
||||
"listDensity": "List density",
|
||||
"densityCompact": "Compact",
|
||||
"densityComfortable": "Comfortable",
|
||||
"densityTitle": "Density: {{density}}"
|
||||
},
|
||||
"home": {
|
||||
"welcome": "Welcome to toolr",
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
import { Card, CardHeader, CardTitle, CardDescription, CardContent } from "@/components/ui/card";
|
||||
import { Skeleton } from "@/components/ui/skeleton";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { ToolCard } from "@/components/tool-card";
|
||||
import { Star, Wrench, MessageSquare, ArrowRight, Plus } from "lucide-react";
|
||||
import { Link } from "wouter";
|
||||
@@ -24,7 +25,10 @@ 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 flex items-center gap-2">
|
||||
{t("home.welcome")}
|
||||
<GuideHelp guide="getting-started" label={t("home.welcome")} />
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("home.tagline")}</p>
|
||||
</div>
|
||||
<Button asChild>
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { Card, CardContent } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
@@ -46,7 +47,10 @@ export default function RedundancyPage() {
|
||||
<div className="space-y-6">
|
||||
<div className="flex items-center gap-3">
|
||||
<AlertTriangle className="w-6 h-6 text-amber-500" />
|
||||
<h1 className="text-3xl font-bold">{t("redundancy.title")}</h1>
|
||||
<h1 className="text-3xl font-bold flex items-center gap-2">
|
||||
{t("redundancy.title")}
|
||||
<GuideHelp guide="redundanz" label={t("redundancy.title")} />
|
||||
</h1>
|
||||
</div>
|
||||
<p className="text-muted-foreground">
|
||||
{t("redundancy.subtitle")}
|
||||
|
||||
@@ -369,7 +369,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>
|
||||
|
||||
@@ -6,6 +6,7 @@ import {
|
||||
} from "@workspace/api-client-react";
|
||||
import { useVirtualizer } from "@tanstack/react-virtual";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { ToolCard } from "@/components/tool-card";
|
||||
import { ToolCardWide } from "@/components/tool-card-wide";
|
||||
import { ToolRow, TABLE_GRID } from "@/components/tool-row";
|
||||
@@ -230,7 +231,10 @@ 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 flex items-center gap-2">
|
||||
{t("nav.browseTools")}
|
||||
<GuideHelp guide="tools-finden" label={t("browse.subtitle")} />
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("browse.subtitle")}</p>
|
||||
</div>
|
||||
<Button asChild>
|
||||
@@ -521,7 +525,10 @@ export default function ToolsBrowse() {
|
||||
<AlertDialog open={compareUpsellOpen} onOpenChange={setCompareUpsellOpen}>
|
||||
<AlertDialogContent>
|
||||
<AlertDialogHeader>
|
||||
<AlertDialogTitle>{t("browse.comparePremiumTitle")}</AlertDialogTitle>
|
||||
<AlertDialogTitle className="flex items-center gap-2">
|
||||
{t("browse.comparePremiumTitle")}
|
||||
<GuideHelp guide="plaene" label={t("browse.comparePremiumTitle")} />
|
||||
</AlertDialogTitle>
|
||||
<AlertDialogDescription>
|
||||
{t("browse.comparePremiumSub")}
|
||||
</AlertDialogDescription>
|
||||
|
||||
@@ -15,6 +15,7 @@ import {
|
||||
import { useQueryClient } from "@tanstack/react-query";
|
||||
import { useAuth } from "@/hooks/use-auth";
|
||||
import { Layout } from "@/components/layout";
|
||||
import { GuideHelp } from "@/components/guide-help";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Input } from "@/components/ui/input";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
@@ -156,7 +157,10 @@ 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 flex items-center gap-2">
|
||||
{t("trash.title")}
|
||||
<GuideHelp guide="papierkorb" label={t("trash.title")} />
|
||||
</h1>
|
||||
<p className="text-muted-foreground">{t("trash.subtitle")}</p>
|
||||
</div>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user