Skip to content

Commit

Permalink
feat: workshop modal initial setup
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphico committed Jun 17, 2024
1 parent 839d200 commit c27c0dd
Show file tree
Hide file tree
Showing 8 changed files with 169 additions and 6 deletions.
1 change: 0 additions & 1 deletion next.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ const nextConfig = {
locales: ["en"],
defaultLocale: "en",
},

// typescript errors & eslint complaints are checked in CI pipeline
eslint: {
ignoreDuringBuilds: true,
Expand Down
112 changes: 112 additions & 0 deletions src/app/(app)/@modal/(.)workshop/[workshopId]/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import * as React from "react"
import { notFound, redirect } from "next/navigation"

import { redirects } from "@/config/constants"
import { getUserSession } from "@/server/data/user"
import { getWorkshop } from "@/server/data/workshop"
import { getExactScheduled } from "@/utils/format-scheduled-date"
import { Button } from "@/components/ui/button"
import { Separator } from "@/components/ui/separator"
import { CopyButton } from "@/components/copy-button"
import { Icons } from "@/components/icons"
import { ModalShell } from "@/components/modal-shell"
import { PageHeader, PageHeaderHeading } from "@/components/page-header"
import { OrganizerSection } from "@/app/(app)/workshop/[workshopId]/_components/organizer-section"
import { OrganizerSectionSkeleton } from "@/app/(app)/workshop/[workshopId]/_components/organizer-section-skeleton"
import { WorkshopSettings } from "@/app/(app)/workshop/[workshopId]/_components/workshop-settings"

interface WorkshopModalProps {
params: {
workshopId: string
}
}

export default async function WorkshopModal({ params }: WorkshopModalProps) {
const workshopId = decodeURIComponent(params.workshopId)

const workshop = await getWorkshop(workshopId)

if (!workshop) {
return notFound()
}

const { user } = await getUserSession()

if (!user) {
redirect(redirects.toLogin)
}

const isCurrentUserWorkshop = workshop.organizerId === user.id

return (
<ModalShell>
<div className="flex w-full flex-col items-start space-y-1">
<div className="flex w-full items-start justify-between">
<PageHeader>
<PageHeaderHeading>{workshop.title}</PageHeaderHeading>
</PageHeader>

<div className="flex items-center gap-1">
<CopyButton
value={workshop.accessCode}
size="icon"
className="rounded-full"
/>
{isCurrentUserWorkshop && <WorkshopSettings workshop={workshop} />}
</div>
</div>

<div className="flex items-center gap-2">
<Icons.clock
className="size-4 text-muted-foreground"
aria-hidden="true"
/>
<p className="text-sm text-muted-foreground">
{getExactScheduled(workshop.scheduled)}
</p>
</div>
<div className="flex items-center gap-2">
<Icons.watch
className="size-4 text-muted-foreground"
aria-hidden="true"
/>
<p className="text-sm text-muted-foreground">
{workshop.duration} mins
</p>
</div>
</div>

<Separator />

<div className="flex flex-col items-start space-y-4">
<div className="space-y-1">
<h4 className="font-medium sm:text-lg">About</h4>

<p className="max-w-lg text-sm text-muted-foreground">
{workshop.description}
</p>
</div>

<React.Suspense fallback={<OrganizerSectionSkeleton />}>
<OrganizerSection organizerId={workshop.organizerId} />
</React.Suspense>

<div className="flex w-full justify-end">
{!isCurrentUserWorkshop ? (
<Button size="sm">
{/* {isPending && (
<Icons.spinner
className="mr-2 size-4 animate-spin"
aria-hidden="true"
/>
)} */}
Register
</Button>
) : (
<Button size="sm">Start</Button>
)}
</div>
</div>
</ModalShell>
)
}
3 changes: 3 additions & 0 deletions src/app/(app)/@modal/default.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export default function Default() {
return null
}
12 changes: 10 additions & 2 deletions src/app/(app)/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,12 @@ import { getUserSession } from "@/server/data/user"

import { Header } from "./_components/header"

export default async function AppLayout({ children }: React.PropsWithChildren) {
interface AppLayoutProps {
children: React.ReactNode
modal: React.ReactNode
}

export default async function AppLayout({ children, modal }: AppLayoutProps) {
const { user } = await getUserSession()

if (!user) {
Expand All @@ -15,7 +20,10 @@ export default async function AppLayout({ children }: React.PropsWithChildren) {
return (
<div className="relative flex min-h-screen flex-col">
<Header user={user} />
<main className="flex-1">{children}</main>
<main className="flex-1">
{children}
{modal}
</main>
</div>
)
}
2 changes: 1 addition & 1 deletion src/app/(app)/workshop/[workshopId]/loading.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { OrganizerSectionSkeleton } from "./_components/organizer-section-skelet

export default function WorkshopLoading() {
return (
<Shell className="max-w-xl gap-4 sm:mt-8">
<Shell className="max-w-xl gap-4">
<div className="flex w-full flex-col items-start space-y-2">
<div className="flex w-full items-start justify-between">
<Skeleton className="h-7 w-52" />
Expand Down
2 changes: 1 addition & 1 deletion src/app/(app)/workshop/[workshopId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,7 @@ export default async function WorkshopPage({ params }: WorkshopPageProps) {
const isCurrentUserWorkshop = workshop.organizerId === user.id

return (
<Shell className="max-w-xl gap-4 sm:mt-8">
<Shell className="max-w-xl gap-4">
<div className="flex w-full flex-col items-start space-y-1">
<div className="flex w-full items-start justify-between">
<PageHeader>
Expand Down
42 changes: 42 additions & 0 deletions src/components/modal-shell.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
"use client"

import { useRouter } from "next/navigation"

import { cn } from "@/lib/utils"
import { useMediaQuery } from "@/hooks/use-media-query"

import { Dialog, DialogContent, DialogOverlay } from "./ui/dialog"
import { Drawer, DrawerContent, DrawerOverlay } from "./ui/drawer"

interface ModalShellProps extends React.HTMLAttributes<HTMLDivElement> {}

export function ModalShell({ children, className, ...props }: ModalShellProps) {
const router = useRouter()
const isDesktop = useMediaQuery("(min-width: 768px)")

const onOpenChange = () => router.back()

if (isDesktop) {
return (
<Dialog open={true} onOpenChange={onOpenChange}>
<DialogOverlay />
<DialogContent>
<div className={cn(className)} {...props}>
{children}
</div>
</DialogContent>
</Dialog>
)
}

return (
<Drawer open={true} onOpenChange={onOpenChange}>
<DrawerOverlay />
<DrawerContent>
<div className={cn(className)} {...props}>
{children}
</div>
</DrawerContent>
</Drawer>
)
}
1 change: 0 additions & 1 deletion src/server/actions/workshop.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,6 @@ export async function deleteWorkshopAction(id: string) {
.where(and(eq(workshops.organizerId, user.id), eq(workshops.id, id)))

revalidateTag(`workshops-${user.id}`)
revalidateTag(`workshops`)

return {
error: null,
Expand Down

0 comments on commit c27c0dd

Please sign in to comment.