diff options
Diffstat (limited to 'apps/web/components/dashboard')
7 files changed, 148 insertions, 65 deletions
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/bookmarks/BookmarkMarkdownComponent.tsx b/apps/web/components/dashboard/bookmarks/BookmarkMarkdownComponent.tsx new file mode 100644 index 00000000..74eb0868 --- /dev/null +++ b/apps/web/components/dashboard/bookmarks/BookmarkMarkdownComponent.tsx @@ -0,0 +1,43 @@ +import MarkdownEditor from "@/components/ui/markdown/markdown-editor";
+import { MarkdownReadonly } from "@/components/ui/markdown/markdown-readonly";
+import { toast } from "@/components/ui/use-toast";
+
+import type { ZBookmarkTypeText } from "@hoarder/shared/types/bookmarks";
+import { useUpdateBookmarkText } from "@hoarder/shared-react/hooks/bookmarks";
+
+export function BookmarkMarkdownComponent({
+ children: bookmark,
+ readOnly = true,
+}: {
+ children: ZBookmarkTypeText;
+ readOnly?: boolean;
+}) {
+ const { mutate: updateBookmarkMutator, isPending } = useUpdateBookmarkText({
+ onSuccess: () => {
+ toast({
+ description: "Note updated!",
+ });
+ },
+ onError: () => {
+ toast({ description: "Something went wrong", variant: "destructive" });
+ },
+ });
+
+ const onSave = (text: string) => {
+ updateBookmarkMutator({
+ bookmarkId: bookmark.id,
+ text,
+ });
+ };
+ return (
+ <div className="h-full overflow-hidden">
+ {readOnly ? (
+ <MarkdownReadonly>{bookmark.content.text}</MarkdownReadonly>
+ ) : (
+ <MarkdownEditor onSave={onSave} isSaving={isPending}>
+ {bookmark.content.text}
+ </MarkdownEditor>
+ )}
+ </div>
+ );
+}
diff --git a/apps/web/components/dashboard/bookmarks/BookmarkedTextEditor.tsx b/apps/web/components/dashboard/bookmarks/BookmarkedTextEditor.tsx index e0434943..b2c27c7e 100644 --- a/apps/web/components/dashboard/bookmarks/BookmarkedTextEditor.tsx +++ b/apps/web/components/dashboard/bookmarks/BookmarkedTextEditor.tsx @@ -1,20 +1,12 @@ -import { useState } from "react"; -import { ActionButton } from "@/components/ui/action-button"; -import { Button } from "@/components/ui/button"; +import { BookmarkMarkdownComponent } from "@/components/dashboard/bookmarks/BookmarkMarkdownComponent"; import { Dialog, - DialogClose, DialogContent, - DialogDescription, - DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; -import { Textarea } from "@/components/ui/textarea"; -import { toast } from "@/components/ui/use-toast"; -import { useUpdateBookmarkText } from "@hoarder/shared-react/hooks/bookmarks"; -import { BookmarkTypes, ZBookmark } from "@hoarder/shared/types/bookmarks"; +import { ZBookmark, ZBookmarkTypeText } from "@hoarder/shared/types/bookmarks"; export function BookmarkedTextEditor({ bookmark, @@ -26,55 +18,20 @@ export function BookmarkedTextEditor({ setOpen: (open: boolean) => void; }) { const isNewBookmark = bookmark === undefined; - const [noteText, setNoteText] = useState( - bookmark && bookmark.content.type == BookmarkTypes.TEXT - ? bookmark.content.text - : "", - ); - - const { mutate: updateBookmarkMutator, isPending } = useUpdateBookmarkText({ - onSuccess: () => { - toast({ - description: "Note updated!", - }); - setOpen(false); - }, - onError: () => { - toast({ description: "Something went wrong", variant: "destructive" }); - }, - }); - - const onSave = () => { - updateBookmarkMutator({ - bookmarkId: bookmark.id, - text: noteText, - }); - }; return ( <Dialog open={open} onOpenChange={setOpen}> - <DialogContent> - <DialogHeader> - <DialogTitle>{isNewBookmark ? "New Note" : "Edit Note"}</DialogTitle> - <DialogDescription> - Write your note with markdown support - </DialogDescription> + <DialogContent className="max-w-[80%]"> + <DialogHeader className="flex"> + <DialogTitle className="w-fit"> + {isNewBookmark ? "New Note" : "Edit Note"} + </DialogTitle> </DialogHeader> - <Textarea - value={noteText} - onChange={(e) => setNoteText(e.target.value)} - className="h-52 grow" - /> - <DialogFooter className="flex-shrink gap-1 sm:justify-end"> - <DialogClose asChild> - <Button type="button" variant="secondary"> - Close - </Button> - </DialogClose> - <ActionButton type="button" loading={isPending} onClick={onSave}> - Save - </ActionButton> - </DialogFooter> + <div className="h-[80vh]"> + <BookmarkMarkdownComponent readOnly={false}> + {bookmark as ZBookmarkTypeText} + </BookmarkMarkdownComponent> + </div> </DialogContent> </Dialog> ); diff --git a/apps/web/components/dashboard/bookmarks/TextCard.tsx b/apps/web/components/dashboard/bookmarks/TextCard.tsx index 14a4f905..9d168910 100644 --- a/apps/web/components/dashboard/bookmarks/TextCard.tsx +++ b/apps/web/components/dashboard/bookmarks/TextCard.tsx @@ -2,7 +2,7 @@ import Image from "next/image"; import Link from "next/link"; -import { MarkdownComponent } from "@/components/ui/markdown-component"; +import { BookmarkMarkdownComponent } from "@/components/dashboard/bookmarks/BookmarkMarkdownComponent"; import { bookmarkLayoutSwitch } from "@/lib/userLocalSettings/bookmarksLayout"; import { cn } from "@/lib/utils"; @@ -20,15 +20,16 @@ export default function TextCard({ bookmark: ZBookmarkTypeText; className?: string; }) { - const bookmarkedText = bookmark.content; - const banner = bookmark.assets.find((a) => a.assetType == "bannerImage"); - return ( <> <BookmarkLayoutAdaptingCard title={bookmark.title} - content={<MarkdownComponent>{bookmarkedText.text}</MarkdownComponent>} + content={ + <BookmarkMarkdownComponent readOnly={true}> + {bookmark} + </BookmarkMarkdownComponent> + } footer={ getSourceUrl(bookmark) && ( <FooterLinkURL url={getSourceUrl(bookmark)} /> 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/dashboard/preview/TextContentSection.tsx b/apps/web/components/dashboard/preview/TextContentSection.tsx index 327436c6..a58bc717 100644 --- a/apps/web/components/dashboard/preview/TextContentSection.tsx +++ b/apps/web/components/dashboard/preview/TextContentSection.tsx @@ -1,7 +1,8 @@ import Image from "next/image"; -import { MarkdownComponent } from "@/components/ui/markdown-component"; +import { BookmarkMarkdownComponent } from "@/components/dashboard/bookmarks/BookmarkMarkdownComponent"; import { ScrollArea } from "@radix-ui/react-scroll-area"; +import type { ZBookmarkTypeText } from "@hoarder/shared/types/bookmarks"; import { getAssetUrl } from "@hoarder/shared-react/utils/assetUtils"; import { BookmarkTypes, ZBookmark } from "@hoarder/shared/types/bookmarks"; @@ -27,7 +28,9 @@ export function TextContentSection({ bookmark }: { bookmark: ZBookmark }) { /> </div> )} - <MarkdownComponent>{bookmark.content.text}</MarkdownComponent> + <BookmarkMarkdownComponent> + {bookmark as ZBookmarkTypeText} + </BookmarkMarkdownComponent> </ScrollArea> ); } |
