diff --git a/bun.lockb b/bun.lockb index b0b8fcb..1763b60 100755 Binary files a/bun.lockb and b/bun.lockb differ diff --git a/package.json b/package.json index 7d515be..6a6ffc5 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "@radix-ui/react-dialog": "^1.0.5", "@radix-ui/react-dropdown-menu": "^2.0.6", "@radix-ui/react-label": "^2.0.2", + "@radix-ui/react-navigation-menu": "^1.1.4", "@radix-ui/react-select": "^2.0.0", "@radix-ui/react-slot": "^1.0.2", "@radix-ui/react-switch": "^1.0.3", diff --git a/src/app/_auth/components/profile-menu.tsx b/src/app/_auth/components/profile-menu.tsx index 33aa752..c503305 100644 --- a/src/app/_auth/components/profile-menu.tsx +++ b/src/app/_auth/components/profile-menu.tsx @@ -30,13 +30,13 @@ export const ProfileMenu = () => { - {data.user.name} + {data.user.name} signOut({ callbackUrl: '/' })} > - Sign out + Sign out diff --git a/src/app/_shared/components/ui/navigation-menu.tsx b/src/app/_shared/components/ui/navigation-menu.tsx new file mode 100644 index 0000000..88fc1f6 --- /dev/null +++ b/src/app/_shared/components/ui/navigation-menu.tsx @@ -0,0 +1,129 @@ +import * as React from 'react' +import { cva } from 'class-variance-authority' +import { ChevronDown } from 'lucide-react' + +import * as NavigationMenuPrimitive from '@radix-ui/react-navigation-menu' + +import { cn } from '@/lib/utils' + +const NavigationMenu = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + {children} + + +)) +NavigationMenu.displayName = NavigationMenuPrimitive.Root.displayName + +const NavigationMenuList = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +NavigationMenuList.displayName = NavigationMenuPrimitive.List.displayName + +const NavigationMenuItem = NavigationMenuPrimitive.Item + +const navigationMenuTriggerStyle = cva( + 'group inline-flex h-10 w-max items-center justify-center rounded-md bg-background px-4 py-2 text-sm font-medium transition-colors hover:bg-accent hover:text-accent-foreground focus:bg-accent focus:text-accent-foreground focus:outline-none disabled:pointer-events-none disabled:opacity-50 data-[active]:bg-accent/50 data-[state=open]:bg-accent/50' +) + +const NavigationMenuTrigger = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, children, ...props }, ref) => ( + + {children}{' '} + +)) +NavigationMenuTrigger.displayName = NavigationMenuPrimitive.Trigger.displayName + +const NavigationMenuContent = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +)) +NavigationMenuContent.displayName = NavigationMenuPrimitive.Content.displayName + +const NavigationMenuLink = NavigationMenuPrimitive.Link + +const NavigationMenuViewport = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( +
+ +
+)) +NavigationMenuViewport.displayName = + NavigationMenuPrimitive.Viewport.displayName + +const NavigationMenuIndicator = React.forwardRef< + React.ElementRef, + React.ComponentPropsWithoutRef +>(({ className, ...props }, ref) => ( + +
+ +)) +NavigationMenuIndicator.displayName = + NavigationMenuPrimitive.Indicator.displayName + +export { + navigationMenuTriggerStyle, + NavigationMenu, + NavigationMenuList, + NavigationMenuItem, + NavigationMenuContent, + NavigationMenuTrigger, + NavigationMenuLink, + NavigationMenuIndicator, + NavigationMenuViewport, +} diff --git a/src/app/_spaces/components/attach-space.tsx b/src/app/_spaces/components/attach-space.tsx index 1561cee..e757356 100644 --- a/src/app/_spaces/components/attach-space.tsx +++ b/src/app/_spaces/components/attach-space.tsx @@ -103,7 +103,7 @@ export const AttachWhiteboardSpace = ({ loading={isLoading} fallback={} > -
+

Available whiteboards:

    diff --git a/src/app/_spaces/components/detach-space.tsx b/src/app/_spaces/components/detach-space.tsx index 8643e12..af9be03 100644 --- a/src/app/_spaces/components/detach-space.tsx +++ b/src/app/_spaces/components/detach-space.tsx @@ -34,11 +34,15 @@ export const DetachWhiteboardSpace = ({ return ( - Detach: {whiteboard.name} + Detach: {whiteboard.name} -

    - Are you sure to detach the whiteboard from the space? +

    + Are you sure to detach the whiteboard? +

    + +

    + the whiteboard will be detached from the space: "{whiteboard.space?.name}"

    diff --git a/src/app/_spaces/components/remove-space.tsx b/src/app/_spaces/components/remove-space.tsx index dbaeabb..374f7a9 100644 --- a/src/app/_spaces/components/remove-space.tsx +++ b/src/app/_spaces/components/remove-space.tsx @@ -35,10 +35,10 @@ export const RemoveSpace = ({ return ( - Delete: {space.name} + Delete: {space.name} -

    +

    Are you sure to delete the space?

    diff --git a/src/app/_whiteboards/components/delete-whiteboard.tsx b/src/app/_whiteboards/components/delete-whiteboard.tsx index 8a45333..b42eb97 100644 --- a/src/app/_whiteboards/components/delete-whiteboard.tsx +++ b/src/app/_whiteboards/components/delete-whiteboard.tsx @@ -24,10 +24,10 @@ export const DeleteWhiteboard = ({ return ( - Delete: {whiteboard.name} + Delete: {whiteboard.name} -

    +

    Are you sure to delete the whiteboard?

    diff --git a/src/app/_whiteboards/components/whiteboard-card.tsx b/src/app/_whiteboards/components/whiteboard-card.tsx index 8ae2316..4224d03 100644 --- a/src/app/_whiteboards/components/whiteboard-card.tsx +++ b/src/app/_whiteboards/components/whiteboard-card.tsx @@ -1,7 +1,7 @@ 'use client' import Link from 'next/link' -import { FilePenLine, Link as LinkIcon,Split, Trash2 } from 'lucide-react' +import { ExternalLink,FilePenLine, Split, Trash2 } from 'lucide-react' import { ContextMenu, @@ -65,8 +65,8 @@ export const WhiteboardCard = ({ whiteboard, children }: ListItemProps) => {
{ - whiteboard.isPublic && - Public + whiteboard.isPublic && + Public }
@@ -111,7 +111,7 @@ export const WhiteboardActions = ({ onClick={() => handleCopyClipboard(`${window.location.origin}/view-whiteboard/${whiteboard.id}`)} className="cursor-pointer flex justify-start gap-2" > - Copy public link + Copy public link ) } diff --git a/src/app/_whiteboards/components/whiteboard-header.tsx b/src/app/_whiteboards/components/whiteboard-header.tsx new file mode 100644 index 0000000..0af3817 --- /dev/null +++ b/src/app/_whiteboards/components/whiteboard-header.tsx @@ -0,0 +1,50 @@ +'use client' + +import React from 'react' + +import { Avatar, AvatarImage } from '@/app/_shared/components/ui/avatar' +import { + NavigationMenu, + NavigationMenuContent, + NavigationMenuItem, + NavigationMenuList, + NavigationMenuTrigger +} from '@/app/_shared/components/ui/navigation-menu' + + +interface Props { + title: string + description: string + + creator: { + name: string + avatarUrl?: string + } +} + + +export const WhiteboardHeader = ({ title, description, creator }: Props) => { + return ( + + + + + {title} + + + { description } + + + + + + +
+ { creator.name} + Author +
+
+
+
+ ) +} diff --git a/src/app/_whiteboards/components/whiteboard-navigation.tsx b/src/app/_whiteboards/components/whiteboard-navigation.tsx index 80046c1..b795292 100644 --- a/src/app/_whiteboards/components/whiteboard-navigation.tsx +++ b/src/app/_whiteboards/components/whiteboard-navigation.tsx @@ -2,11 +2,18 @@ import React from 'react' import Link from 'next/link' import { Group,Presentation } from 'lucide-react' -import { ProfileCard } from '@/app/_auth/components/profile-card' import { ProfileMenu } from '@/app/_auth/components/profile-menu' +import { + NavigationMenu, + NavigationMenuItem, + NavigationMenuLink, + NavigationMenuList, + navigationMenuTriggerStyle +} from '@/app/_shared/components/ui/navigation-menu' interface Props { whiteboardName: string + description?: string spaceId?: number | null } @@ -14,29 +21,33 @@ interface Props { export const WhiteboardNavigation = ({ whiteboardName, spaceId }: Props) => { return ( - + } + + + + + ) } diff --git a/src/app/_whiteboards/interfaces/whiteboard.ts b/src/app/_whiteboards/interfaces/whiteboard.ts index 3dd267a..6106624 100644 --- a/src/app/_whiteboards/interfaces/whiteboard.ts +++ b/src/app/_whiteboards/interfaces/whiteboard.ts @@ -8,4 +8,19 @@ export interface Whiteboard { isPublic: boolean content: unknown previewUrl?: string +} + +export interface PublicWhiteboard { + id: number + name: string + description?: string + space?: Space + isPublic: boolean + content: unknown + previewUrl?: string + createdBy: { + id: number + name: string + image: string + } } \ No newline at end of file diff --git a/src/app/view-whiteboard/[id]/page.tsx b/src/app/view-whiteboard/[id]/page.tsx index 70f00f8..ee5a990 100644 --- a/src/app/view-whiteboard/[id]/page.tsx +++ b/src/app/view-whiteboard/[id]/page.tsx @@ -4,7 +4,8 @@ import React from 'react' import { redirect } from 'next/navigation' import { type Content,Whiteboard } from '@/app/_whiteboards/components/whiteboard' -import { type Whiteboard as WitheboardType } from '@/app/_whiteboards/interfaces/whiteboard' +import { WhiteboardHeader } from '@/app/_whiteboards/components/whiteboard-header' +import { type PublicWhiteboard } from '@/app/_whiteboards/interfaces/whiteboard' import findPublicWhiteboardById from '@/server/api/whiteboard/usecases/find-public-whiteboard' import { db } from '@/server/db' @@ -16,7 +17,7 @@ const getWhiteboard = async (id: number) => { redirect('/not-found') } - return whiteboard as unknown as WitheboardType & {content: undefined | Content} + return whiteboard as unknown as PublicWhiteboard & {content: undefined | Content} } @@ -24,7 +25,7 @@ const getWhiteboard = async (id: number) => { const WhitebardViewPage = async ({ params }: {params: {id: string}}) => { const whiteboardId = Number(params.id) - const whiteboard: WitheboardType = await getWhiteboard(whiteboardId) + const whiteboard: PublicWhiteboard = await getWhiteboard(whiteboardId) return (
@@ -33,11 +34,17 @@ const WhitebardViewPage = async ({ params }: {params: {id: string}}) => { id={whiteboard?.id} initialContent={whiteboard?.content as Content} /> -
-

{whiteboard.name}

-
+
) } + export default WhitebardViewPage \ No newline at end of file diff --git a/src/server/api/whiteboard/usecases/find-public-whiteboard.ts b/src/server/api/whiteboard/usecases/find-public-whiteboard.ts index 35692d7..41b44f6 100644 --- a/src/server/api/whiteboard/usecases/find-public-whiteboard.ts +++ b/src/server/api/whiteboard/usecases/find-public-whiteboard.ts @@ -21,6 +21,14 @@ const findPublicWhiteboardById = async (db: PostgresJsDatabase, o createdAt: true, updatedAt: true, previewUrl: true, + }, + with: { + createdBy: { + columns: { + email: false, + emailVerified: false, + } + }, } }) diff --git a/src/server/db/schema.ts b/src/server/db/schema.ts index a078556..805fb74 100644 --- a/src/server/db/schema.ts +++ b/src/server/db/schema.ts @@ -145,6 +145,7 @@ export const whiteboards = createTable( export const whiteboardsRelations = relations(whiteboards, ({ one }) => ({ space: one(spaces, { fields: [whiteboards.spaceId], references: [spaces.id] }), + createdBy: one(users, { fields: [whiteboards.createdById], references: [users.id] }), })) diff --git a/src/styles/globals.css b/src/styles/globals.css index 4df1223..9ead26e 100644 --- a/src/styles/globals.css +++ b/src/styles/globals.css @@ -70,6 +70,12 @@ @apply grid grid-cols-1 md:grid-cols-3 lg:grid-cols-4 2xl:grid-cols-6 gap-4 w-full; } + + .center-fixed { + left: 50%; + transform: translate(-50%, 0); + } + header { animation: nav-header-shadown 1s linear both; animation-timeline: scroll();