Skip to content

Commit

Permalink
feat: complete workshop session
Browse files Browse the repository at this point in the history
  • Loading branch information
Raphico committed Jul 4, 2024
1 parent 421e72a commit f66f805
Show file tree
Hide file tree
Showing 10 changed files with 258 additions and 93 deletions.
12 changes: 9 additions & 3 deletions src/app/(app)/session/[workshopId]/_components/session-call.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ import { type User } from "lucia"
import "@stream-io/video-react-sdk/dist/css/styles.css"

import { generateStreamTokenAction } from "@/server/actions/stream"
import { MarkWorkshopHasCompleted } from "@/server/actions/workshop"
import { Icons } from "@/components/icons"

interface SessionCallProps {
Expand Down Expand Up @@ -81,10 +82,15 @@ export function SessionCall({
<StreamCall call={call}>
<div className="grid w-full space-x-10 lg:grid-cols-[1fr_20rem]">
<div className="flex flex-col items-center justify-center space-y-4">
<SpeakerLayout participantsBarPosition="left" />
<SpeakerLayout />
<CallControls
onLeave={async () => {
if (isOrganizer) await call.endCall()
onLeave={() => {
if (isOrganizer) {
MarkWorkshopHasCompleted(workshopId).then(() =>
call.endCall()
)
}

router.push("/dashboard")
}}
/>
Expand Down
19 changes: 9 additions & 10 deletions src/app/(app)/session/[workshopId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,22 +30,21 @@ export default async function SessionPage({ params }: SessionPageProps) {
}

const isOrganizer = workshop.organizerId === user.id
const registrants = await getWorkshopRegistrants(workshop.id)
const isRegistrant = registrants.some(
(registrant) => registrant.id === user.id
)

if (workshop.hasCompleted) {
redirect(`/workshop/${workshop.id}`)
if (!isOrganizer && !isRegistrant) {
redirect(`/dashboard`)
}

if (!workshop.hasStarted && !isOrganizer) {
if (workshop.hasCompleted) {
redirect(`/workshop/${workshop.id}`)
}

const registrants = await getWorkshopRegistrants(workshop.id)
const isCurrentUserARegistrant = registrants.some(
(registrant) => registrant.id === user.id
)

if (workshop.hasStarted && !isCurrentUserARegistrant) {
redirect(`/dashboard`)
if (!workshop.hasStarted) {
redirect(`/workshop/${workshop.id}`)
}

return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,14 @@ interface RegisterButtonProps {
workshopId: string
workshopTitle: string
isCurrentUserRegistered: boolean
isDisabled: boolean
}

export function RegisterButton({
userId,
workshopId,
isCurrentUserRegistered,
isDisabled,
}: RegisterButtonProps) {
const [isPending, startTransition] = React.useTransition()

Expand Down Expand Up @@ -51,10 +53,9 @@ export function RegisterButton({

return (
<Button
type="submit"
onClick={handleOnclickRegisterButton}
size="sm"
disabled={isPending}
disabled={isPending || isDisabled}
>
{isPending && (
<Icons.spinner
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,50 @@
"use client"

import Link from "next/link"
import * as React from "react"
import { useRouter } from "next/navigation"

import { cn } from "@/lib/utils"
import { buttonVariants } from "@/components/ui/button"
import { startWorkshopAction } from "@/server/actions/workshop"
import { showErrorToast } from "@/utils/handle-error"
import { Button } from "@/components/ui/button"
import { Icons } from "@/components/icons"

interface StartWorkshopButtonProps {
interface RegisterButtonProps {
workshopId: string
isDisabled: boolean
}

export function StartWorkshopButton({ workshopId }: StartWorkshopButtonProps) {
export function StartWorkshopButton({
workshopId,
isDisabled,
}: RegisterButtonProps) {
const router = useRouter()
const [isPending, startTransition] = React.useTransition()

const handleStartWorkshop = () => {
startTransition(async () => {
const { error } = await startWorkshopAction(workshopId)

if (error) {
showErrorToast(error)
}

router.push(`/session/${workshopId}`)
})
}

return (
<Link
href={`/session/${workshopId}`}
className={cn(buttonVariants({ size: "sm" }))}
<Button
onClick={handleStartWorkshop}
size="sm"
disabled={isPending || isDisabled}
>
Start Workshop
</Link>
{isPending && (
<Icons.spinner
className="mr-2 size-4 animate-spin"
aria-hidden={true}
/>
)}
Start
</Button>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import Link from "next/link"

import { cn } from "@/lib/utils"
import { Button, buttonVariants } from "@/components/ui/button"

import { RegisterButton } from "./register-button"
import { StartWorkshopButton } from "./start-workshop-button"

interface WorkshopButtonProps {
userId: string
workshopId: string
workshopTitle: string
isUserOrganizer: boolean
isUserRegistered: boolean
hasWorkshopStarted: boolean
hasWorkshopCompleted: boolean
}

export function WorkshopButton({
hasWorkshopStarted,
isUserOrganizer,
workshopTitle,
isUserRegistered,
workshopId,
userId,
hasWorkshopCompleted,
}: WorkshopButtonProps) {
if (hasWorkshopCompleted) {
return (
<Button disabled size="sm">
Ended
</Button>
)
}

if (isUserOrganizer) {
return (
<StartWorkshopButton
workshopId={workshopId}
isDisabled={hasWorkshopStarted}
/>
)
}

if (!hasWorkshopStarted) {
return (
<RegisterButton
isDisabled={hasWorkshopStarted}
userId={userId}
workshopId={workshopId}
isCurrentUserRegistered={isUserRegistered}
workshopTitle={workshopTitle}
/>
)
}

return (
<Link
className={cn(buttonVariants({ size: "sm" }))}
href={`/session/${workshopId}`}
>
Join
</Link>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export function WorkshopSettings({ workshop }: WorkshopSettingsProps) {
<DeleteWorkshopAlert />
<DropdownMenu open={open} onOpenChange={setOpen}>
<DropdownMenuTrigger asChild>
<Button size="icon" variant="ghost">
<Button size="icon" variant="ghost" className="size-8">
<Icons.more className="size-5" aria-hidden="true" />
<span className="sr-only">Workshop Settings Dropdown</span>
</Button>
Expand Down
63 changes: 36 additions & 27 deletions src/app/(app)/workshop/[workshopId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { getWorkshopRegistrants } from "@/server/data/registration"
import { getUserSession } from "@/server/data/user"
import { getWorkshop, getWorkshopMetadata } from "@/server/data/workshop"
import { getExactScheduled } from "@/utils/format-scheduled-date"
import { Badge } from "@/components/ui/badge"
import { Separator } from "@/components/ui/separator"
import { CopyButton } from "@/components/copy-button"
import { Icons } from "@/components/icons"
Expand All @@ -16,8 +17,7 @@ import { Shell } from "@/components/shell"

import { OrganizerSection } from "./_components/organizer-section"
import { OrganizerSectionSkeleton } from "./_components/organizer-section-skeleton"
import { RegisterButton } from "./_components/register-button"
import { StartWorkshopButton } from "./_components/start-workshop-button"
import { WorkshopButton } from "./_components/workshop-button"
import { WorkshopRegistrants } from "./_components/workshop-registrants"
import { WorkshopSettings } from "./_components/workshop-settings"

Expand Down Expand Up @@ -62,27 +62,29 @@ export default async function WorkshopPage({ params }: WorkshopPageProps) {

const registrants = await getWorkshopRegistrants(workshop.id)

const isCurrentUserWorkshop = workshop.organizerId === user.id
const isCurrentUserRegistered = registrants.some(
const isUserOrganizer = workshop.organizerId === user.id
const isUserRegistered = registrants.some(
(registrant) => registrant.id === user.id
)

return (
<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>
<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 className="flex w-full items-center justify-between">
<div className="flex items-center gap-2">
<PageHeader>
<PageHeaderHeading>{workshop.title}</PageHeaderHeading>
</PageHeader>
<Badge>
{workshop.hasCompleted
? "Ended"
: workshop.hasStarted
? "Ongoing"
: "Open"}
</Badge>
</div>

{isUserOrganizer && <WorkshopSettings workshop={workshop} />}
</div>

<div className="flex items-center gap-2">
Expand Down Expand Up @@ -123,17 +125,24 @@ export default async function WorkshopPage({ params }: WorkshopPageProps) {
<OrganizerSection organizerId={workshop.organizerId} />
</React.Suspense>

<div className="flex w-full justify-end">
{!isCurrentUserWorkshop ? (
<RegisterButton
userId={user.id}
workshopId={workshop.id}
isCurrentUserRegistered={isCurrentUserRegistered}
workshopTitle={workshop.title}
/>
) : (
<StartWorkshopButton workshopId={workshop.id} />
)}
<div className="flex w-full justify-end space-x-4">
<CopyButton
variant="secondary"
className="gap-2 text-secondary-foreground"
value={workshop.accessCode}
>
Copy code
</CopyButton>

<WorkshopButton
userId={user.id}
workshopId={workshop.id}
isUserOrganizer={isUserOrganizer}
isUserRegistered={isUserRegistered}
workshopTitle={workshop.title}
hasWorkshopStarted={workshop.hasStarted}
hasWorkshopCompleted={workshop.hasCompleted}
/>
</div>
</div>
</Shell>
Expand Down
10 changes: 8 additions & 2 deletions src/components/copy-button.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,20 @@ import { cn } from "@/lib/utils"
import { Icons } from "./icons"
import { Button, type ButtonProps } from "./ui/button"

export function CopyButton({ value, className, ...props }: ButtonProps) {
export function CopyButton({
value,
className,
children,
...props
}: ButtonProps) {
const [isCopied, setIsCopied] = React.useState(false)

return (
<Button
type="button"
variant="ghost"
size="sm"
className={cn("text-muted-foreground hover:bg-transparent", className)}
className={cn("text-muted-foreground", className)}
onClick={() => {
if (typeof window === "undefined") return
setIsCopied(true)
Expand All @@ -29,6 +34,7 @@ export function CopyButton({ value, className, ...props }: ButtonProps) {
) : (
<Icons.copy className="size-4" aria-hidden="true" />
)}
{children}
</Button>
)
}
Loading

0 comments on commit f66f805

Please sign in to comment.