aboutsummaryrefslogtreecommitdiffstats
path: root/apps/web/components/dashboard/admin
diff options
context:
space:
mode:
authorMohamed Bassem <me@mbassem.com>2024-11-17 00:33:28 +0000
committerGitHub <noreply@github.com>2024-11-17 00:33:28 +0000
commit4354ee7ba1c6ac9a9567944ae6169b1664e0ea8a (patch)
treee27c9070930514d77582bae00b3350274116179c /apps/web/components/dashboard/admin
parent9f2c7be23769bb0f4102736a683710b1a1939661 (diff)
downloadkarakeep-4354ee7ba1c6ac9a9567944ae6169b1664e0ea8a.tar.zst
feature: Add i18n support. Fixes #57 (#635)
* feature(web): Add basic scaffolding for i18n * refactor: Switch most of the app's strings to use i18n strings * fix: Remove unused i18next-resources-for-ts command * Add user setting * More translations * Drop the german translation for now
Diffstat (limited to 'apps/web/components/dashboard/admin')
-rw-r--r--apps/web/components/dashboard/admin/AdminActions.tsx19
-rw-r--r--apps/web/components/dashboard/admin/ServerStats.tsx38
-rw-r--r--apps/web/components/dashboard/admin/UserList.tsx30
3 files changed, 53 insertions, 34 deletions
diff --git a/apps/web/components/dashboard/admin/AdminActions.tsx b/apps/web/components/dashboard/admin/AdminActions.tsx
index a97552f8..3b95045c 100644
--- a/apps/web/components/dashboard/admin/AdminActions.tsx
+++ b/apps/web/components/dashboard/admin/AdminActions.tsx
@@ -2,9 +2,11 @@
import { ActionButton } from "@/components/ui/action-button";
import { toast } from "@/components/ui/use-toast";
+import { useTranslation } from "@/lib/i18n/client";
import { api } from "@/lib/trpc";
export default function AdminActions() {
+ const { t } = useTranslation();
const { mutate: recrawlLinks, isPending: isRecrawlPending } =
api.admin.recrawlLinks.useMutation({
onSuccess: () => {
@@ -69,7 +71,7 @@ export default function AdminActions() {
return (
<div>
- <div className="mb-2 mt-8 text-xl font-medium">Actions</div>
+ <div className="mb-2 mt-8 text-xl font-medium">{t("common.actions")}</div>
<div className="flex flex-col gap-2 sm:w-1/2">
<ActionButton
variant="destructive"
@@ -78,7 +80,7 @@ export default function AdminActions() {
recrawlLinks({ crawlStatus: "failure", runInference: true })
}
>
- Recrawl Failed Links Only
+ {t("admin.actions.recrawl_failed_links_only")}
</ActionButton>
<ActionButton
variant="destructive"
@@ -87,7 +89,7 @@ export default function AdminActions() {
recrawlLinks({ crawlStatus: "all", runInference: true })
}
>
- Recrawl All Links
+ {t("admin.actions.recrawl_all_links")}
</ActionButton>
<ActionButton
variant="destructive"
@@ -96,7 +98,8 @@ export default function AdminActions() {
recrawlLinks({ crawlStatus: "all", runInference: false })
}
>
- Recrawl All Links (Without Inference)
+ {t("admin.actions.recrawl_all_links")} (
+ {t("admin.actions.without_inference")})
</ActionButton>
<ActionButton
variant="destructive"
@@ -105,28 +108,28 @@ export default function AdminActions() {
reRunInferenceOnAllBookmarks({ taggingStatus: "failure" })
}
>
- Regenerate AI Tags for Failed Bookmarks Only
+ {t("admin.actions.regenerate_ai_tags_for_failed_bookmarks_only")}
</ActionButton>
<ActionButton
variant="destructive"
loading={isInferencePending}
onClick={() => reRunInferenceOnAllBookmarks({ taggingStatus: "all" })}
>
- Regenerate AI Tags for All Bookmarks
+ {t("admin.actions.regenerate_ai_tags_for_all_bookmarks")}
</ActionButton>
<ActionButton
variant="destructive"
loading={isReindexPending}
onClick={() => reindexBookmarks()}
>
- Reindex All Bookmarks
+ {t("admin.actions.reindex_all_bookmarks")}
</ActionButton>
<ActionButton
variant="destructive"
loading={isTidyAssetsPending}
onClick={() => tidyAssets()}
>
- Compact Assets
+ {t("admin.actions.compact_assets")}
</ActionButton>
</div>
</div>
diff --git a/apps/web/components/dashboard/admin/ServerStats.tsx b/apps/web/components/dashboard/admin/ServerStats.tsx
index f45d86c5..da69390b 100644
--- a/apps/web/components/dashboard/admin/ServerStats.tsx
+++ b/apps/web/components/dashboard/admin/ServerStats.tsx
@@ -10,6 +10,7 @@ import {
TableRow,
} from "@/components/ui/table";
import { useClientConfig } from "@/lib/clientConfig";
+import { useTranslation } from "@/lib/i18n/client";
import { api } from "@/lib/trpc";
import { keepPreviousData, useQuery } from "@tanstack/react-query";
@@ -61,6 +62,7 @@ function ReleaseInfo() {
}
export default function ServerStats() {
+ const { t } = useTranslation();
const { data: serverStats } = api.admin.stats.useQuery(undefined, {
refetchInterval: 1000,
placeholderData: keepPreviousData,
@@ -72,15 +74,19 @@ export default function ServerStats() {
return (
<>
- <div className="mb-2 text-xl font-medium">Server Stats</div>
+ <div className="mb-2 text-xl font-medium">
+ {t("admin.server_stats.server_stats")}
+ </div>
<div className="flex flex-col gap-4 sm:flex-row">
<div className="rounded-md border bg-background p-4 sm:w-1/4">
- <div className="text-sm font-medium text-gray-400">Total Users</div>
+ <div className="text-sm font-medium text-gray-400">
+ {t("admin.server_stats.total_users")}
+ </div>
<div className="text-3xl font-semibold">{serverStats.numUsers}</div>
</div>
<div className="rounded-md border bg-background p-4 sm:w-1/4">
<div className="text-sm font-medium text-gray-400">
- Total Bookmarks
+ {t("admin.server_stats.total_bookmarks")}
</div>
<div className="text-3xl font-semibold">
{serverStats.numBookmarks}
@@ -88,42 +94,48 @@ export default function ServerStats() {
</div>
<div className="rounded-md border bg-background p-4 sm:w-1/4">
<div className="text-sm font-medium text-gray-400">
- Server Version
+ {t("admin.server_stats.server_version")}
</div>
<ReleaseInfo />
</div>
</div>
<div className="sm:w-1/2">
- <div className="mb-2 mt-8 text-xl font-medium">Background Jobs</div>
+ <div className="mb-2 mt-8 text-xl font-medium">
+ {t("admin.background_jobs.background_jobs")}
+ </div>
<Table className="rounded-md border">
<TableHeader className="bg-gray-200">
- <TableHead>Job</TableHead>
- <TableHead>Queued</TableHead>
- <TableHead>Pending</TableHead>
- <TableHead>Failed</TableHead>
+ <TableHead>{t("admin.background_jobs.job")}</TableHead>
+ <TableHead>{t("admin.background_jobs.queued")}</TableHead>
+ <TableHead>{t("admin.background_jobs.pending")}</TableHead>
+ <TableHead>{t("admin.background_jobs.failed")}</TableHead>
</TableHeader>
<TableBody>
<TableRow>
- <TableCell className="lg:w-2/3">Crawling Jobs</TableCell>
+ <TableCell className="lg:w-2/3">
+ {t("admin.background_jobs.crawler_jobs")}
+ </TableCell>
<TableCell>{serverStats.crawlStats.queued}</TableCell>
<TableCell>{serverStats.crawlStats.pending}</TableCell>
<TableCell>{serverStats.crawlStats.failed}</TableCell>
</TableRow>
<TableRow>
- <TableCell>Indexing Jobs</TableCell>
+ <TableCell>{t("admin.background_jobs.indexing_jobs")}</TableCell>
<TableCell>{serverStats.indexingStats.queued}</TableCell>
<TableCell>-</TableCell>
<TableCell>-</TableCell>
</TableRow>
<TableRow>
- <TableCell>Inference Jobs</TableCell>
+ <TableCell>{t("admin.background_jobs.inference_jobs")}</TableCell>
<TableCell>{serverStats.inferenceStats.queued}</TableCell>
<TableCell>{serverStats.inferenceStats.pending}</TableCell>
<TableCell>{serverStats.inferenceStats.failed}</TableCell>
</TableRow>
<TableRow>
- <TableCell>Tidy Assets Jobs</TableCell>
+ <TableCell>
+ {t("admin.background_jobs.tidy_assets_jobs")}
+ </TableCell>
<TableCell>{serverStats.tidyAssetsStats.queued}</TableCell>
<TableCell>-</TableCell>
<TableCell>-</TableCell>
diff --git a/apps/web/components/dashboard/admin/UserList.tsx b/apps/web/components/dashboard/admin/UserList.tsx
index 2937df28..8c788ef4 100644
--- a/apps/web/components/dashboard/admin/UserList.tsx
+++ b/apps/web/components/dashboard/admin/UserList.tsx
@@ -12,6 +12,7 @@ import {
TableRow,
} from "@/components/ui/table";
import { toast } from "@/components/ui/use-toast";
+import { useTranslation } from "@/lib/i18n/client";
import { api } from "@/lib/trpc";
import { Check, KeyRound, Pencil, Trash, UserPlus, X } from "lucide-react";
import { useSession } from "next-auth/react";
@@ -28,6 +29,7 @@ function toHumanReadableSize(size: number) {
}
export default function UsersSection() {
+ const { t } = useTranslation();
const { data: session } = useSession();
const invalidateUserList = api.useUtils().users.list.invalidate;
const { data: users } = api.users.list.useQuery();
@@ -55,7 +57,7 @@ export default function UsersSection() {
return (
<>
<div className="mb-2 flex items-center justify-between text-xl font-medium">
- <span>Users List</span>
+ <span>{t("admin.users_list.users_list")}</span>
<AddUserDialog>
<ButtonWithTooltip tooltip="Create User" variant="outline">
<UserPlus size={16} />
@@ -65,13 +67,13 @@ export default function UsersSection() {
<Table>
<TableHeader className="bg-gray-200">
- <TableHead>Name</TableHead>
- <TableHead>Email</TableHead>
- <TableHead>Num Bookmarks</TableHead>
- <TableHead>Asset Sizes</TableHead>
- <TableHead>Role</TableHead>
- <TableHead>Local User</TableHead>
- <TableHead>Actions</TableHead>
+ <TableHead>{t("common.name")}</TableHead>
+ <TableHead>{t("common.email")}</TableHead>
+ <TableHead>{t("admin.users_list.num_bookmarks")}</TableHead>
+ <TableHead>{t("admin.users_list.asset_sizes")}</TableHead>
+ <TableHead>{t("common.role")}</TableHead>
+ <TableHead>{t("admin.users_list.local_user")}</TableHead>
+ <TableHead>{t("common.actions")}</TableHead>
</TableHeader>
<TableBody>
{users.users.map((u) => (
@@ -84,13 +86,15 @@ export default function UsersSection() {
<TableCell className="py-1">
{toHumanReadableSize(userStats[u.id].assetSizes)}
</TableCell>
- <TableCell className="py-1 capitalize">{u.role}</TableCell>
- <TableCell className="py-1 capitalize">
+ <TableCell className="py-1">
+ {u.role && t(`common.roles.${u.role}`)}
+ </TableCell>
+ <TableCell className="py-1">
{u.localUser ? <Check /> : <X />}
</TableCell>
<TableCell className="flex gap-1 py-1">
<ActionButtonWithTooltip
- tooltip="Delete user"
+ tooltip={t("admin.users_list.delete_user")}
variant="outline"
onClick={() => deleteUser({ userId: u.id })}
loading={isDeletionPending}
@@ -100,7 +104,7 @@ export default function UsersSection() {
</ActionButtonWithTooltip>
<ResetPasswordDialog userId={u.id}>
<ButtonWithTooltip
- tooltip="Reset password"
+ tooltip={t("admin.users_list.reset_password")}
variant="outline"
disabled={session!.user.id == u.id || !u.localUser}
>
@@ -109,7 +113,7 @@ export default function UsersSection() {
</ResetPasswordDialog>
<ChangeRoleDialog userId={u.id} currentRole={u.role!}>
<ButtonWithTooltip
- tooltip="Change role"
+ tooltip={t("admin.users_list.change_role")}
variant="outline"
disabled={session!.user.id == u.id}
>