diff options
| author | Mohamed Bassem <me@mbassem.com> | 2024-12-21 21:35:35 +0000 |
|---|---|---|
| committer | Mohamed Bassem <me@mbassem.com> | 2024-12-21 21:35:35 +0000 |
| commit | 4bfb3b4250efa735aa265e924680a36a4b8bb1a7 (patch) | |
| tree | 29b972fcc2ffb9542a6a5baf3359434dfb281104 /apps/web | |
| parent | 22458873594fa1b157782afbbf1abca30259e7ab (diff) | |
| download | karakeep-4bfb3b4250efa735aa265e924680a36a4b8bb1a7.tar.zst | |
feature: Add an admin notice about the usage of the legacy container images
Diffstat (limited to 'apps/web')
| -rw-r--r-- | apps/web/app/dashboard/admin/page.tsx | 15 | ||||
| -rw-r--r-- | apps/web/components/dashboard/admin/AdminCard.tsx | 3 | ||||
| -rw-r--r-- | apps/web/components/dashboard/admin/AdminNotices.tsx | 71 | ||||
| -rw-r--r-- | apps/web/components/dashboard/header/ProfileOptions.tsx | 11 | ||||
| -rw-r--r-- | apps/web/components/ui/alert.tsx | 60 |
5 files changed, 151 insertions, 9 deletions
diff --git a/apps/web/app/dashboard/admin/page.tsx b/apps/web/app/dashboard/admin/page.tsx index 18efc889..cf97698b 100644 --- a/apps/web/app/dashboard/admin/page.tsx +++ b/apps/web/app/dashboard/admin/page.tsx @@ -1,5 +1,7 @@ import { redirect } from "next/navigation"; import AdminActions from "@/components/dashboard/admin/AdminActions"; +import { AdminCard } from "@/components/dashboard/admin/AdminCard"; +import { AdminNotices } from "@/components/dashboard/admin/AdminNotices"; import ServerStats from "@/components/dashboard/admin/ServerStats"; import UserList from "@/components/dashboard/admin/UserList"; import { getServerAuthSession } from "@/server/auth"; @@ -10,14 +12,15 @@ export default async function AdminPage() { redirect("/"); } return ( - <> - <div className="rounded-md border bg-background p-4"> + <div className="flex flex-col gap-4"> + <AdminNotices /> + <AdminCard> <ServerStats /> <AdminActions /> - </div> - <div className="mt-4 rounded-md border bg-background p-4"> + </AdminCard> + <AdminCard> <UserList /> - </div> - </> + </AdminCard> + </div> ); } diff --git a/apps/web/components/dashboard/admin/AdminCard.tsx b/apps/web/components/dashboard/admin/AdminCard.tsx new file mode 100644 index 00000000..3a52b5e5 --- /dev/null +++ b/apps/web/components/dashboard/admin/AdminCard.tsx @@ -0,0 +1,3 @@ +export function AdminCard({ children }: { children: React.ReactNode }) { + return <div className="rounded-md border bg-background p-4">{children}</div>; +} diff --git a/apps/web/components/dashboard/admin/AdminNotices.tsx b/apps/web/components/dashboard/admin/AdminNotices.tsx new file mode 100644 index 00000000..4977736f --- /dev/null +++ b/apps/web/components/dashboard/admin/AdminNotices.tsx @@ -0,0 +1,71 @@ +"use client"; + +import { Alert, AlertDescription, AlertTitle } from "@/components/ui/alert"; +import { Badge } from "@/components/ui/badge"; +import { api } from "@/lib/trpc"; +import { AlertCircle } from "lucide-react"; + +import { AdminCard } from "./AdminCard"; + +interface AdminNotice { + level: "info" | "warning" | "error"; + message: React.ReactNode; + title: string; +} + +function useAdminNotices() { + const { data } = api.admin.getAdminNoticies.useQuery(); + if (!data) { + return []; + } + const ret: AdminNotice[] = []; + if (data.legacyContainersNotice) { + ret.push({ + level: "warning", + message: ( + <p> + You're using the legacy docker container images. Those will stop + getting supported soon. Please follow{" "} + <a + href="https://docs.hoarder.app/next/Guides/legacy-container-upgrade" + className="underline" + > + this guide + </a>{" "} + to upgrade. + </p> + ), + title: "Legacy Container Images", + }); + } + return ret; +} + +export function AdminNotices() { + const notices = useAdminNotices(); + + if (notices.length === 0) { + return null; + } + return ( + <AdminCard> + <div className="flex flex-col gap-2"> + {notices.map((n, i) => ( + <Alert key={i} variant="destructive"> + <AlertCircle className="h-4 w-4" /> + <AlertTitle>{n.title}</AlertTitle> + <AlertDescription>{n.message}</AlertDescription> + </Alert> + ))} + </div> + </AdminCard> + ); +} + +export function AdminNoticeBadge() { + const notices = useAdminNotices(); + if (notices.length === 0) { + return null; + } + return <Badge variant="destructive">{notices.length}</Badge>; +} diff --git a/apps/web/components/dashboard/header/ProfileOptions.tsx b/apps/web/components/dashboard/header/ProfileOptions.tsx index 3dbfcf04..fc18e9d2 100644 --- a/apps/web/components/dashboard/header/ProfileOptions.tsx +++ b/apps/web/components/dashboard/header/ProfileOptions.tsx @@ -16,6 +16,8 @@ import { LogOut, Moon, Paintbrush, Settings, Shield, Sun } from "lucide-react"; import { signOut, useSession } from "next-auth/react"; import { useTheme } from "next-themes"; +import { AdminNoticeBadge } from "../admin/AdminNotices"; + function DarkModeToggle() { const { t } = useTranslation(); const { theme } = useTheme(); @@ -72,9 +74,12 @@ export default function SidebarProfileOptions() { </DropdownMenuItem> {session.user.role == "admin" && ( <DropdownMenuItem asChild> - <Link href="/dashboard/admin"> - <Shield className="mr-2 size-4" /> - {t("admin.admin_settings")} + <Link href="/dashboard/admin" className="flex justify-between"> + <div className="items-cente flex gap-2"> + <Shield className="size-4" /> + {t("admin.admin_settings")} + </div> + <AdminNoticeBadge /> </Link> </DropdownMenuItem> )} diff --git a/apps/web/components/ui/alert.tsx b/apps/web/components/ui/alert.tsx new file mode 100644 index 00000000..706b711e --- /dev/null +++ b/apps/web/components/ui/alert.tsx @@ -0,0 +1,60 @@ +import type { VariantProps } from "class-variance-authority"; +import * as React from "react"; +import { cn } from "@/lib/utils"; +import { cva } from "class-variance-authority"; + +const alertVariants = cva( + "relative w-full rounded-lg border p-4 [&>svg+div]:translate-y-[-3px] [&>svg]:absolute [&>svg]:left-4 [&>svg]:top-4 [&>svg]:text-foreground [&>svg~*]:pl-7", + { + variants: { + variant: { + default: "bg-background text-foreground", + destructive: + "border-destructive/50 text-destructive dark:border-destructive [&>svg]:text-destructive", + }, + }, + defaultVariants: { + variant: "default", + }, + }, +); + +const Alert = React.forwardRef< + HTMLDivElement, + React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof alertVariants> +>(({ className, variant, ...props }, ref) => ( + <div + ref={ref} + role="alert" + className={cn(alertVariants({ variant }), className)} + {...props} + /> +)); +Alert.displayName = "Alert"; + +const AlertTitle = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes<HTMLHeadingElement> +>(({ className, ...props }, ref) => ( + // eslint-disable-next-line jsx-a11y/heading-has-content + <h5 + ref={ref} + className={cn("mb-1 font-medium leading-none tracking-tight", className)} + {...props} + /> +)); +AlertTitle.displayName = "AlertTitle"; + +const AlertDescription = React.forwardRef< + HTMLParagraphElement, + React.HTMLAttributes<HTMLParagraphElement> +>(({ className, ...props }, ref) => ( + <div + ref={ref} + className={cn("text-sm [&_p]:leading-relaxed", className)} + {...props} + /> +)); +AlertDescription.displayName = "AlertDescription"; + +export { Alert, AlertTitle, AlertDescription }; |
