From 75fc85217ad7718283e420389fb7438f1fecd1e4 Mon Sep 17 00:00:00 2001 From: Vincent Sit Date: Thu, 15 Aug 2024 00:28:44 +1200 Subject: [PATCH 1/7] Initial addition of user menu items --- .../react/src/components/header/header.tsx | 46 ++++++++++++++++++- .../userMenu/components/UserMenuItem.tsx | 27 +++++++++++ .../src/components/header/userMenu/types.ts | 5 ++ 3 files changed, 76 insertions(+), 2 deletions(-) create mode 100644 packages/react/src/components/header/userMenu/components/UserMenuItem.tsx create mode 100644 packages/react/src/components/header/userMenu/types.ts diff --git a/packages/react/src/components/header/header.tsx b/packages/react/src/components/header/header.tsx index ffc340a08..ea5f19d82 100644 --- a/packages/react/src/components/header/header.tsx +++ b/packages/react/src/components/header/header.tsx @@ -16,12 +16,18 @@ import { Logo } from "./Logo"; import { DropdownMenu, DropdownMenuContent, - DropdownMenuItem, DropdownMenuTrigger, } from "@/shadcn/ui/dropdown-menu"; import { useAuth } from "@/hooks/useAuth"; import { userAtom } from "@/store/auth"; import { useState } from "react"; +import { UserMenuItem } from "./userMenu/components/UserMenuItem"; +import { + CalendarIcon, + ExitIcon, + GearIcon, + PersonIcon, +} from "@radix-ui/react-icons"; interface HeaderProps extends React.DetailedHTMLProps< React.HTMLAttributes, @@ -105,6 +111,28 @@ export function UserMenu() { const user = useAtomValue(userAtom); const { logout } = useAuth(); + const mockUserMenuItems = [ + { + key: "accountSettings", + value: "Account Settings", + fn: () => mockNavigate("accountSettings"), + icon: , + }, + { + key: "settings", + value: "Settings", + fn: () => mockNavigate("settings"), + icon: , + }, + { + key: "iCalFeed", + value: "ICal Feed", + fn: () => mockNavigate("iCalFeed"), + icon: , + }, + { key: "logout", value: "Logout", fn: logout, icon: }, + ]; + if (!user) { return ( - ); - } - - return ( - - - User avatar - - - {/* user profile */} - {mockUserMenuItems.map((item) => { - return ( - - ); - })} - - - ); -} - -function mockNavigate(destination: string) { - console.log(destination); -} diff --git a/packages/react/src/components/header/userMenu/components/UserMenu.tsx b/packages/react/src/components/header/userMenu/components/UserMenu.tsx new file mode 100644 index 000000000..d56551f72 --- /dev/null +++ b/packages/react/src/components/header/userMenu/components/UserMenu.tsx @@ -0,0 +1,107 @@ +import { useAuth } from "@/hooks/useAuth"; +import { userAtom } from "@/store/auth"; +import { + DropdownMenu, + DropdownMenuTrigger, + DropdownMenuContent, + DropdownMenuSeparator, +} from "@radix-ui/react-dropdown-menu"; +import { + PersonIcon, + GearIcon, + CalendarIcon, + ExitIcon, +} from "@radix-ui/react-icons"; +import { useAtomValue } from "jotai"; +import { useTranslation } from "react-i18next"; +import { Link } from "react-router-dom"; +import { UserMenuItem } from "./UserMenuItem"; +import { Button } from "@/shadcn/ui/button"; +import { DropdownMenuLabel } from "@/shadcn/ui/dropdown-menu"; + +export function UserMenu() { + const { t } = useTranslation(); + const user = useAtomValue(userAtom); + const { logout } = useAuth(); + + const mockUserMenuItems = [ + { + key: "accountSettings", + value: "Account Settings", + fn: () => mockNavigate("accountSettings"), + icon: , + }, + { + key: "settings", + value: "Settings", + fn: () => mockNavigate("settings"), + icon: , + }, + { + key: "iCalFeed", + value: "ICal Feed", + fn: () => mockNavigate("iCalFeed"), + icon: , + }, + { key: "logout", value: "Logout", fn: logout, icon: }, + ]; + + if (!user) { + return ( + + ); + } + + return ( + + + User avatar + + + {/* user profile */} +
+
+ {user.username} +
+
+
+
+
+
+
+
+
+
+
+
+
+ + {mockUserMenuItems.map((item) => { + return ( + + ); + })} + + + ); +} + +function mockNavigate(destination: string) { + console.log(destination); +} diff --git a/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx b/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx index 1f18a8595..7c2bf157c 100644 --- a/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx +++ b/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx @@ -3,12 +3,6 @@ import { DropdownMenuItem } from "@/shadcn/ui/dropdown-menu"; // import { useTranslation } from "react-i18next"; import { MenuItem } from "../types"; -// interface UserMenuItemProps -// extends React.ComponentPropsWithoutRef { -// item: QueryItem; -// onSelect: (_: string) => void; -// } - interface UserMenuItemProps { item: MenuItem; onClick: () => void; From 38103338fb2f62663728a35b0fbc931e5dfef4f2 Mon Sep 17 00:00:00 2001 From: Vincent Sit Date: Thu, 15 Aug 2024 11:05:06 +1200 Subject: [PATCH 3/7] Add avatar to shadcn collection --- packages/react/package-lock.json | 69 +++++++++++++++++-------- packages/react/package.json | 1 + packages/react/src/shadcn/ui/avatar.tsx | 48 +++++++++++++++++ 3 files changed, 97 insertions(+), 21 deletions(-) create mode 100644 packages/react/src/shadcn/ui/avatar.tsx diff --git a/packages/react/package-lock.json b/packages/react/package-lock.json index a7c78d9c6..62af3e70e 100644 --- a/packages/react/package-lock.json +++ b/packages/react/package-lock.json @@ -16,6 +16,7 @@ "@radix-ui/colors": "^3.0.0", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", + "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-collapsible": "^1.1.0", "@radix-ui/react-context-menu": "^2.2.1", @@ -2052,6 +2053,32 @@ } } }, + "node_modules/@radix-ui/react-avatar": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@radix-ui/react-avatar/-/react-avatar-1.1.0.tgz", + "integrity": "sha512-Q/PbuSMk/vyAd/UoIShVGZ7StHHeRFYU7wXmi5GV+8cLXflZAEpHL/F697H1klrzxKXNtZ97vWiC0q3RKUH8UA==", + "license": "MIT", + "dependencies": { + "@radix-ui/react-context": "1.1.0", + "@radix-ui/react-primitive": "2.0.0", + "@radix-ui/react-use-callback-ref": "1.1.0", + "@radix-ui/react-use-layout-effect": "1.1.0" + }, + "peerDependencies": { + "@types/react": "*", + "@types/react-dom": "*", + "react": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc", + "react-dom": "^16.8 || ^17.0 || ^18.0 || ^19.0 || ^19.0.0-rc" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + }, + "@types/react-dom": { + "optional": true + } + } + }, "node_modules/@radix-ui/react-checkbox": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-checkbox/-/react-checkbox-1.1.1.tgz", @@ -5066,11 +5093,11 @@ } }, "node_modules/braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dependencies": { - "fill-range": "^7.0.1" + "fill-range": "^7.1.1" }, "engines": { "node": ">=8" @@ -5975,27 +6002,27 @@ "dev": true }, "node_modules/engine.io-client": { - "version": "6.5.3", - "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.3.tgz", - "integrity": "sha512-9Z0qLB0NIisTRt1DZ/8U2k12RJn8yls/nXMZLn+/N8hANT3TcYjKFKcwbw5zFQiN4NTde3TSY9zb79e1ij6j9Q==", + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/engine.io-client/-/engine.io-client-6.5.4.tgz", + "integrity": "sha512-GeZeeRjpD2qf49cZQ0Wvh/8NJNfeXkXXcoGh+F77oEAgo9gUHwT1fCRxSNU+YEEaysOJTnsFHmM5oAcPy4ntvQ==", "dependencies": { "@socket.io/component-emitter": "~3.1.0", "debug": "~4.3.1", "engine.io-parser": "~5.2.1", - "ws": "~8.11.0", + "ws": "~8.17.1", "xmlhttprequest-ssl": "~2.0.0" } }, "node_modules/engine.io-client/node_modules/ws": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", - "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { @@ -6580,9 +6607,9 @@ "dev": true }, "node_modules/fast-loops": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.3.tgz", - "integrity": "sha512-8EZzEP0eKkEEVX+drtd9mtuQ+/QrlfW/5MlwcwK5Nds6EkZ/tRzEexkzUY2mIssnAyVLT+TKHuRXmFNNXYUd6g==" + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/fast-loops/-/fast-loops-1.1.4.tgz", + "integrity": "sha512-8dbd3XWoKCTms18ize6JmQF1SFnnfj5s0B7rRry22EofgMu7B6LKHVh+XfFqFGsqnbH54xgeO83PzpKI+ODhlg==" }, "node_modules/fast-shallow-equal": { "version": "1.0.0", @@ -6621,9 +6648,9 @@ } }, "node_modules/fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -11122,9 +11149,9 @@ "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, "node_modules/ws": { - "version": "7.5.9", - "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "version": "7.5.10", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.10.tgz", + "integrity": "sha512-+dbF1tHwZpXcbOJdVOkzLDxZP1ailvSxM6ZweXTegylPny803bFhA+vqBYw4s31NSAk4S2Qz+AKXK9a4wkdjcQ==", "dev": true, "engines": { "node": ">=8.3.0" diff --git a/packages/react/package.json b/packages/react/package.json index 2a00e0f9a..3a77eb319 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -23,6 +23,7 @@ "@radix-ui/colors": "^3.0.0", "@radix-ui/react-accordion": "^1.2.0", "@radix-ui/react-alert-dialog": "^1.1.1", + "@radix-ui/react-avatar": "^1.1.0", "@radix-ui/react-checkbox": "^1.1.1", "@radix-ui/react-collapsible": "^1.1.0", "@radix-ui/react-context-menu": "^2.2.1", diff --git a/packages/react/src/shadcn/ui/avatar.tsx b/packages/react/src/shadcn/ui/avatar.tsx new file mode 100644 index 000000000..991f56ecb --- /dev/null +++ b/packages/react/src/shadcn/ui/avatar.tsx @@ -0,0 +1,48 @@ +import * as React from "react" +import * as AvatarPrimitive from "@radix-ui/react-avatar" + +import { cn } from "@/lib/utils" + +const Avatar = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +Avatar.displayName = AvatarPrimitive.Root.displayName + +const AvatarImage = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarImage.displayName = AvatarPrimitive.Image.displayName + +const AvatarFallback = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName + +export { Avatar, AvatarImage, AvatarFallback } From d7276792615667bbd9da3a248e1c687e5634299c Mon Sep 17 00:00:00 2001 From: Vincent Sit Date: Thu, 15 Aug 2024 11:06:23 +1200 Subject: [PATCH 4/7] Remove type option --- packages/react/src/components/header/userMenu/types.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/react/src/components/header/userMenu/types.ts b/packages/react/src/components/header/userMenu/types.ts index a3111ff8c..8c01026e8 100644 --- a/packages/react/src/components/header/userMenu/types.ts +++ b/packages/react/src/components/header/userMenu/types.ts @@ -1,5 +1,5 @@ export type MenuItem = { key: string; value: string; - icon?: React.JSX.Element; + icon: React.JSX.Element; }; From 56bfa6a42727259e2dc763f7342474b25169ef90 Mon Sep 17 00:00:00 2001 From: Vincent Sit Date: Thu, 15 Aug 2024 11:53:48 +1200 Subject: [PATCH 5/7] Format user profile info section --- .../header/userMenu/components/UserMenu.tsx | 32 +++++++++---------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/packages/react/src/components/header/userMenu/components/UserMenu.tsx b/packages/react/src/components/header/userMenu/components/UserMenu.tsx index d56551f72..8a0c0a4c9 100644 --- a/packages/react/src/components/header/userMenu/components/UserMenu.tsx +++ b/packages/react/src/components/header/userMenu/components/UserMenu.tsx @@ -6,18 +6,14 @@ import { DropdownMenuContent, DropdownMenuSeparator, } from "@radix-ui/react-dropdown-menu"; -import { - PersonIcon, - GearIcon, - CalendarIcon, - ExitIcon, -} from "@radix-ui/react-icons"; +import { GearIcon, CalendarIcon, ExitIcon } from "@radix-ui/react-icons"; import { useAtomValue } from "jotai"; import { useTranslation } from "react-i18next"; import { Link } from "react-router-dom"; import { UserMenuItem } from "./UserMenuItem"; import { Button } from "@/shadcn/ui/button"; import { DropdownMenuLabel } from "@/shadcn/ui/dropdown-menu"; +import { Avatar, AvatarFallback, AvatarImage } from "@/shadcn/ui/avatar"; export function UserMenu() { const { t } = useTranslation(); @@ -25,12 +21,6 @@ export function UserMenu() { const { logout } = useAuth(); const mockUserMenuItems = [ - { - key: "accountSettings", - value: "Account Settings", - fn: () => mockNavigate("accountSettings"), - icon: , - }, { key: "settings", value: "Settings", @@ -64,10 +54,17 @@ export function UserMenu() { {/* user profile */} -
-
+
+ + + CN + +
{user.username} -
+
@@ -85,6 +82,10 @@ export function UserMenu() {
+ + {user.role} : {user.contribution_count} + {t("component.mainNav.points")} +
{mockUserMenuItems.map((item) => { @@ -93,7 +94,6 @@ export function UserMenu() { key={item.key + item.value} item={{ key: item.key, value: item.value, icon: item.icon }} onClick={item.fn} - // add specific icon /> ); })} From 4fcac611f2796ffd866e053b5c44a60968b79eaf Mon Sep 17 00:00:00 2001 From: Vincent Sit Date: Thu, 15 Aug 2024 11:55:13 +1200 Subject: [PATCH 6/7] Update user menu item format --- .../header/userMenu/components/UserMenuItem.tsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx b/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx index 7c2bf157c..c50bb02cc 100644 --- a/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx +++ b/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx @@ -12,10 +12,12 @@ export function UserMenuItem({ item, onClick }: UserMenuItemProps) { return ( onClick()} - className="flex h-full w-64 flex-row gap-2" + className="grid h-full w-full grid-cols-4 p-4" > -
{item.icon}
-
{item.value}
+
+ {item.icon} +
+
{item.value}
); } From d8df1ec06853b942883758d149d11efc0c527d34 Mon Sep 17 00:00:00 2001 From: Vincent Sit Date: Thu, 15 Aug 2024 12:09:35 +1200 Subject: [PATCH 7/7] Update format --- .../header/userMenu/components/UserMenu.tsx | 12 ++++-------- .../header/userMenu/components/UserMenuItem.tsx | 2 +- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/packages/react/src/components/header/userMenu/components/UserMenu.tsx b/packages/react/src/components/header/userMenu/components/UserMenu.tsx index 8a0c0a4c9..eb15b46dc 100644 --- a/packages/react/src/components/header/userMenu/components/UserMenu.tsx +++ b/packages/react/src/components/header/userMenu/components/UserMenu.tsx @@ -24,13 +24,13 @@ export function UserMenu() { { key: "settings", value: "Settings", - fn: () => mockNavigate("settings"), + fn: () => console.log("settings"), icon: , }, { key: "iCalFeed", value: "ICal Feed", - fn: () => mockNavigate("iCalFeed"), + fn: () => console.log("iCalFeed"), icon: , }, { key: "logout", value: "Logout", fn: logout, icon: }, @@ -52,7 +52,7 @@ export function UserMenu() { alt="User avatar" /> - + {/* user profile */}
@@ -87,7 +87,7 @@ export function UserMenu() { {t("component.mainNav.points")}
- + {mockUserMenuItems.map((item) => { return ( ); } - -function mockNavigate(destination: string) { - console.log(destination); -} diff --git a/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx b/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx index c50bb02cc..bea4ebd2a 100644 --- a/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx +++ b/packages/react/src/components/header/userMenu/components/UserMenuItem.tsx @@ -12,7 +12,7 @@ export function UserMenuItem({ item, onClick }: UserMenuItemProps) { return ( onClick()} - className="grid h-full w-full grid-cols-4 p-4" + className="grid h-full w-full cursor-pointer grid-cols-4 p-4" >
{item.icon}