From e8927986d5847b22466df5510c7774d853bba190 Mon Sep 17 00:00:00 2001 From: Aidan Date: Wed, 5 Feb 2025 00:56:21 -0500 Subject: [PATCH] bump packages, improve dashboard design, add partial functionality to dashboard with api --- app/account/dashboard/page.tsx | 310 +++++++++----- app/globals.css | 16 + bun.lockb | Bin 203900 -> 207748 bytes components/ui/progress.tsx | 28 ++ components/ui/separator.tsx | 31 ++ components/ui/sheet.tsx | 140 ++++++ components/ui/sidebar.tsx | 763 +++++++++++++++++++++++++++++++++ components/ui/skeleton.tsx | 15 + components/ui/tooltip.tsx | 32 ++ hooks/use-mobile.tsx | 19 + next.config.ts | 10 + package.json | 22 +- tailwind.config.ts | 132 +++--- 13 files changed, 1354 insertions(+), 164 deletions(-) create mode 100644 components/ui/progress.tsx create mode 100644 components/ui/separator.tsx create mode 100644 components/ui/sheet.tsx create mode 100644 components/ui/sidebar.tsx create mode 100644 components/ui/skeleton.tsx create mode 100644 components/ui/tooltip.tsx create mode 100644 hooks/use-mobile.tsx diff --git a/app/account/dashboard/page.tsx b/app/account/dashboard/page.tsx index a119cd1..98a701c 100644 --- a/app/account/dashboard/page.tsx +++ b/app/account/dashboard/page.tsx @@ -1,106 +1,222 @@ "use client" -import { useState } from "react" -import { Flex, Text, Card, Progress, Grid, Box } from "@radix-ui/themes" -import { Mail, GitBranch, Music, Key, CheckCircle, XCircle } from "lucide-react" -import Sidebar from "../../components/account/Sidebar" +import Image from "next/image" +import Link from "next/link" +import { useEffect, useState } from "react" +import { Mail, GitBranch, Music, Key, CheckCircle, XCircle, User, LayoutDashboard, Settings, Briefcase, Loader } from "lucide-react" +import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" +import { Progress } from "@/components/ui/progress" +import { Sidebar, SidebarContent, SidebarFooter, SidebarHeader, SidebarMenu, SidebarMenuButton, SidebarMenuItem, SidebarProvider } from "@/components/ui/sidebar" +import { motion } from "framer-motion" +import { getCookie } from "cookies-next" + +const fadeIn = { + initial: { opacity: 0, y: 20 }, + animate: { opacity: 1, y: 0 }, + transition: { duration: 0.4 }, +} export default function Dashboard() { - const [diskUsage, setDiskUsage] = useState(75) + const [diskUsage, setDiskUsage] = useState(0) + const [isGitLinked, setIsGitLinked] = useState(false) + const [gitUser, setGitUser] = useState("Unlinked") + const [gitFollowerCt, setGitFollowerCt] = useState(0) + const [gitAvatar, setGitAvatar] = useState(null) + const [gitProfileCardLoading, setGitProfileCardLoading] = useState(true) + const [gitProfileCardError, setGitProfileCardError] = useState(false) + + useEffect(() => { + const checkGitLinkStatus = async () => { + try { + const key = getCookie("key") + const email = getCookie("email") + + const response = await fetch("http://localhost:3001/account/links", { + method: "POST", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ key, email, exi: true }), + }) + + if (response.ok) { + const data = await response.json() + setIsGitLinked(data.linked || false) + setGitUser(data.user || "Unlinked") + setGitAvatar(data.avatar || null) + setGitFollowerCt(data.followers || 0) + setGitProfileCardLoading(false) + } else { + setGitProfileCardError(true) + console.error("Failed to fetch Git link status") + } + } catch (error) { + setGitProfileCardError(true) + console.error("Error checking Git link status:", error) + } + } + + checkGitLinkStatus() + }, []) return ( - - - - - Dashboard - - - - - Disk Usage - - - - {diskUsage}% of 100GB used - - - - - - Account Security - - - - - Spam Protection - - - - Two-Factor Authentication - - - - - - - Services - - - - - Mail - - - - Git - - - - Music - - - - Password Manager - - - - - - - Linked Accounts - - - - - p0ntus mail - - - - LibreCloud Git - - - - - - - LibreCloud Git - - - - - - - - username - - 12 repositories - - - - - - + +
+ + +
+ + username +
+
+ + + + + + Dashboard + + + + + + Services + + + + + + My Account + + + + + + + + + + Settings + + + + +
+
+
+ +

Dashboard

+
+ + + + Disk Usage + + + +

{diskUsage}% of 100GB used

+
+
+
+ + + + Account Security + + +
+
+ + Spam Protection +
+
+ + Two-Factor Authentication +
+
+
+
+
+ + + + Linked Accounts + + +
+
+
+ p0ntus mail +
+
+
+ LibreCloud Git +
+
+ + + + + + + Services + + +
+
+ + Mail +
+
+ + Git +
+
+ + Music +
+
+ + Vaultwarden +
+
+
+
+
+ + + + LibreCloud Git + + +
+
+ {gitProfileCardLoading ? ( +
+ +
+ ) : gitAvatar ? ( + User Avatar + ) : gitProfileCardError ? ( + + ) : ( + + )} +
+
+

{gitUser}

+

{gitFollowerCt} {gitFollowerCt === 1 ? "follower" : "followers"}

+
+
+
+
+
+
+ +
+
+
+
) } diff --git a/app/globals.css b/app/globals.css index 23284cf..c455351 100644 --- a/app/globals.css +++ b/app/globals.css @@ -33,6 +33,14 @@ body { --chart-4: 43 74% 66%; --chart-5: 27 87% 67%; --radius: 0.5rem; + --sidebar-background: 0 0% 98%; + --sidebar-foreground: 240 5.3% 26.1%; + --sidebar-primary: 240 5.9% 10%; + --sidebar-primary-foreground: 0 0% 98%; + --sidebar-accent: 240 4.8% 95.9%; + --sidebar-accent-foreground: 240 5.9% 10%; + --sidebar-border: 220 13% 91%; + --sidebar-ring: 217.2 91.2% 59.8%; } .dark { --background: 222.2 84% 4.9%; @@ -59,6 +67,14 @@ body { --chart-3: 30 80% 55%; --chart-4: 280 65% 60%; --chart-5: 340 75% 55%; + --sidebar-background: 240 5.9% 10%; + --sidebar-foreground: 240 4.8% 95.9%; + --sidebar-primary: 224.3 76.3% 48%; + --sidebar-primary-foreground: 0 0% 100%; + --sidebar-accent: 240 3.7% 15.9%; + --sidebar-accent-foreground: 240 4.8% 95.9%; + --sidebar-border: 240 3.7% 15.9%; + --sidebar-ring: 217.2 91.2% 59.8%; } } diff --git a/bun.lockb b/bun.lockb index aaa1ec432960fd2fb5ff5774a2c7a4508afe0db0..fb0b7da2b27f6a9755e2466e6d35d65176c719e1 100755 GIT binary patch delta 42980 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 delta 40621 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 J-C}jM{{u9ag>?V` diff --git a/components/ui/progress.tsx b/components/ui/progress.tsx new file mode 100644 index 0000000..4fc3b47 --- /dev/null +++ b/components/ui/progress.tsx @@ -0,0 +1,28 @@ +"use client" + +import * as React from "react" +import * as ProgressPrimitive from "@radix-ui/react-progress" + +import { cn } from "@/lib/utils" + +const Progress = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, value, ...props }, ref) => ( + + + +)) +Progress.displayName = ProgressPrimitive.Root.displayName + +export { Progress } diff --git a/components/ui/separator.tsx b/components/ui/separator.tsx new file mode 100644 index 0000000..12d81c4 --- /dev/null +++ b/components/ui/separator.tsx @@ -0,0 +1,31 @@ +"use client" + +import * as React from "react" +import * as SeparatorPrimitive from "@radix-ui/react-separator" + +import { cn } from "@/lib/utils" + +const Separator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>( + ( + { className, orientation = "horizontal", decorative = true, ...props }, + ref + ) => ( + + ) +) +Separator.displayName = SeparatorPrimitive.Root.displayName + +export { Separator } diff --git a/components/ui/sheet.tsx b/components/ui/sheet.tsx new file mode 100644 index 0000000..272cb72 --- /dev/null +++ b/components/ui/sheet.tsx @@ -0,0 +1,140 @@ +"use client" + +import * as React from "react" +import * as SheetPrimitive from "@radix-ui/react-dialog" +import { cva, type VariantProps } from "class-variance-authority" +import { X } from "lucide-react" + +import { cn } from "@/lib/utils" + +const Sheet = SheetPrimitive.Root + +const SheetTrigger = SheetPrimitive.Trigger + +const SheetClose = SheetPrimitive.Close + +const SheetPortal = SheetPrimitive.Portal + +const SheetOverlay = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetOverlay.displayName = SheetPrimitive.Overlay.displayName + +const sheetVariants = cva( + "fixed z-50 gap-4 bg-background p-6 shadow-lg transition ease-in-out data-[state=closed]:duration-300 data-[state=open]:duration-500 data-[state=open]:animate-in data-[state=closed]:animate-out", + { + variants: { + side: { + top: "inset-x-0 top-0 border-b data-[state=closed]:slide-out-to-top data-[state=open]:slide-in-from-top", + bottom: + "inset-x-0 bottom-0 border-t data-[state=closed]:slide-out-to-bottom data-[state=open]:slide-in-from-bottom", + left: "inset-y-0 left-0 h-full w-3/4 border-r data-[state=closed]:slide-out-to-left data-[state=open]:slide-in-from-left sm:max-w-sm", + right: + "inset-y-0 right-0 h-full w-3/4 border-l data-[state=closed]:slide-out-to-right data-[state=open]:slide-in-from-right sm:max-w-sm", + }, + }, + defaultVariants: { + side: "right", + }, + } +) + +interface SheetContentProps + extends React.ComponentPropsWithoutRef, + VariantProps {} + +const SheetContent = React.forwardRef< + React.ElementRef, + SheetContentProps +>(({ side = "right", className, children, ...props }, ref) => ( + + + + + + Close + + {children} + + +)) +SheetContent.displayName = SheetPrimitive.Content.displayName + +const SheetHeader = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetHeader.displayName = "SheetHeader" + +const SheetFooter = ({ + className, + ...props +}: React.HTMLAttributes) => ( +
+) +SheetFooter.displayName = "SheetFooter" + +const SheetTitle = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetTitle.displayName = SheetPrimitive.Title.displayName + +const SheetDescription = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +SheetDescription.displayName = SheetPrimitive.Description.displayName + +export { + Sheet, + SheetPortal, + SheetOverlay, + SheetTrigger, + SheetClose, + SheetContent, + SheetHeader, + SheetFooter, + SheetTitle, + SheetDescription, +} diff --git a/components/ui/sidebar.tsx b/components/ui/sidebar.tsx new file mode 100644 index 0000000..a8a5314 --- /dev/null +++ b/components/ui/sidebar.tsx @@ -0,0 +1,763 @@ +"use client" + +import * as React from "react" +import { Slot } from "@radix-ui/react-slot" +import { VariantProps, cva } from "class-variance-authority" +import { PanelLeft } from "lucide-react" + +import { useIsMobile } from "@/hooks/use-mobile" +import { cn } from "@/lib/utils" +import { Button } from "@/components/ui/button" +import { Input } from "@/components/ui/input" +import { Separator } from "@/components/ui/separator" +import { Sheet, SheetContent } from "@/components/ui/sheet" +import { Skeleton } from "@/components/ui/skeleton" +import { + Tooltip, + TooltipContent, + TooltipProvider, + TooltipTrigger, +} from "@/components/ui/tooltip" + +const SIDEBAR_COOKIE_NAME = "sidebar:state" +const SIDEBAR_COOKIE_MAX_AGE = 60 * 60 * 24 * 7 +const SIDEBAR_WIDTH = "16rem" +const SIDEBAR_WIDTH_MOBILE = "18rem" +const SIDEBAR_WIDTH_ICON = "3rem" +const SIDEBAR_KEYBOARD_SHORTCUT = "b" + +type SidebarContext = { + state: "expanded" | "collapsed" + open: boolean + setOpen: (open: boolean) => void + openMobile: boolean + setOpenMobile: (open: boolean) => void + isMobile: boolean + toggleSidebar: () => void +} + +const SidebarContext = React.createContext(null) + +function useSidebar() { + const context = React.useContext(SidebarContext) + if (!context) { + throw new Error("useSidebar must be used within a SidebarProvider.") + } + + return context +} + +const SidebarProvider = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + defaultOpen?: boolean + open?: boolean + onOpenChange?: (open: boolean) => void + } +>( + ( + { + defaultOpen = true, + open: openProp, + onOpenChange: setOpenProp, + className, + style, + children, + ...props + }, + ref + ) => { + const isMobile = useIsMobile() + const [openMobile, setOpenMobile] = React.useState(false) + + // This is the internal state of the sidebar. + // We use openProp and setOpenProp for control from outside the component. + const [_open, _setOpen] = React.useState(defaultOpen) + const open = openProp ?? _open + const setOpen = React.useCallback( + (value: boolean | ((value: boolean) => boolean)) => { + const openState = typeof value === "function" ? value(open) : value + if (setOpenProp) { + setOpenProp(openState) + } else { + _setOpen(openState) + } + + // This sets the cookie to keep the sidebar state. + document.cookie = `${SIDEBAR_COOKIE_NAME}=${openState}; path=/; max-age=${SIDEBAR_COOKIE_MAX_AGE}` + }, + [setOpenProp, open] + ) + + // Helper to toggle the sidebar. + const toggleSidebar = React.useCallback(() => { + return isMobile + ? setOpenMobile((open) => !open) + : setOpen((open) => !open) + }, [isMobile, setOpen, setOpenMobile]) + + // Adds a keyboard shortcut to toggle the sidebar. + React.useEffect(() => { + const handleKeyDown = (event: KeyboardEvent) => { + if ( + event.key === SIDEBAR_KEYBOARD_SHORTCUT && + (event.metaKey || event.ctrlKey) + ) { + event.preventDefault() + toggleSidebar() + } + } + + window.addEventListener("keydown", handleKeyDown) + return () => window.removeEventListener("keydown", handleKeyDown) + }, [toggleSidebar]) + + // We add a state so that we can do data-state="expanded" or "collapsed". + // This makes it easier to style the sidebar with Tailwind classes. + const state = open ? "expanded" : "collapsed" + + const contextValue = React.useMemo( + () => ({ + state, + open, + setOpen, + isMobile, + openMobile, + setOpenMobile, + toggleSidebar, + }), + [state, open, setOpen, isMobile, openMobile, setOpenMobile, toggleSidebar] + ) + + return ( + + +
+ {children} +
+
+
+ ) + } +) +SidebarProvider.displayName = "SidebarProvider" + +const Sidebar = React.forwardRef< + HTMLDivElement, + React.ComponentProps<"div"> & { + side?: "left" | "right" + variant?: "sidebar" | "floating" | "inset" + collapsible?: "offcanvas" | "icon" | "none" + } +>( + ( + { + side = "left", + variant = "sidebar", + collapsible = "offcanvas", + className, + children, + ...props + }, + ref + ) => { + const { isMobile, state, openMobile, setOpenMobile } = useSidebar() + + if (collapsible === "none") { + return ( +
+ {children} +
+ ) + } + + if (isMobile) { + return ( + + +
{children}
+
+
+ ) + } + + return ( +
+ {/* This is what handles the sidebar gap on desktop */} +
+ +
+ ) + } +) +Sidebar.displayName = "Sidebar" + +const SidebarTrigger = React.forwardRef< + React.ElementRef, + React.ComponentProps +>(({ className, onClick, ...props }, ref) => { + const { toggleSidebar } = useSidebar() + + return ( + + ) +}) +SidebarTrigger.displayName = "SidebarTrigger" + +const SidebarRail = React.forwardRef< + HTMLButtonElement, + React.ComponentProps<"button"> +>(({ className, ...props }, ref) => { + const { toggleSidebar } = useSidebar() + + return ( +