Skip to content

Commit e9cb3e7

Browse files
committed
refactor coaching session creation/updates
1 parent 704b933 commit e9cb3e7

File tree

6 files changed

+136
-159
lines changed

6 files changed

+136
-159
lines changed
Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
"use client";
2+
3+
import { useState } from "react";
4+
import type * as React from "react";
5+
import { cn } from "@/components/lib/utils";
6+
import SelectCoachingRelationship from "@/components/ui/dashboard/select-coaching-relationship";
7+
import CoachingSessionList from "@/components/ui/dashboard/coaching-session-list";
8+
import AddEntities from "@/components/ui/dashboard/add-entities";
9+
import { CoachingSessionDialog } from "@/components/ui/dashboard/coaching-session-dialog";
10+
import type { CoachingSession } from "@/types/coaching-session";
11+
12+
function DashboardContainer({
13+
className,
14+
...props
15+
}: React.HTMLAttributes<HTMLDivElement>) {
16+
return (
17+
<div
18+
className={cn(
19+
// Base styles
20+
"p-4",
21+
// Mobile: stack vertically
22+
"flex flex-col gap-6",
23+
// Tablet and up (640px+): side by side
24+
"sm:grid sm:grid-cols-2",
25+
// Never grow wider than the site-header
26+
"max-w-screen-2xl",
27+
// Ensure full width for children
28+
"[&>*]:w-full",
29+
className
30+
)}
31+
{...props}
32+
/>
33+
);
34+
}
35+
36+
export function DashboardContent() {
37+
const [dialogOpen, setDialogOpen] = useState(false);
38+
const [sessionToEdit, setSessionToEdit] = useState<CoachingSession | undefined>();
39+
40+
const handleOpenDialog = (session?: CoachingSession) => {
41+
setSessionToEdit(session);
42+
setDialogOpen(true);
43+
};
44+
45+
const handleCloseDialog = () => {
46+
setDialogOpen(false);
47+
setSessionToEdit(undefined);
48+
};
49+
50+
return (
51+
<>
52+
<div className="p-4 max-w-screen-2xl">
53+
<div className="mb-8 w-full">
54+
<AddEntities onCreateSession={() => handleOpenDialog()} />
55+
</div>
56+
</div>
57+
<DashboardContainer>
58+
<SelectCoachingRelationship />
59+
<CoachingSessionList onUpdateSession={handleOpenDialog} />
60+
</DashboardContainer>
61+
62+
<CoachingSessionDialog
63+
open={dialogOpen}
64+
onOpenChange={handleCloseDialog}
65+
coachingSessionToEdit={sessionToEdit}
66+
/>
67+
</>
68+
);
69+
}

src/app/dashboard/page.tsx

Lines changed: 2 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,11 @@
11
import type { Metadata } from "next";
2-
import type * as React from "react";
3-
import { cn } from "@/components/lib/utils";
4-
import SelectCoachingRelationship from "@/components/ui/dashboard/select-coaching-relationship";
5-
import CoachingSessionList from "@/components/ui/dashboard/coaching-session-list";
6-
import AddEntities from "@/components/ui/dashboard/add-entities";
2+
import { DashboardContent } from "./dashboard-content";
73

84
export const metadata: Metadata = {
95
title: "Dashboard",
106
description: "Coaching dashboard",
117
};
128

13-
function DashboardContainer({
14-
className,
15-
...props
16-
}: React.HTMLAttributes<HTMLDivElement>) {
17-
return (
18-
<div
19-
className={cn(
20-
// Base styles
21-
"p-4",
22-
// Mobile: stack vertically
23-
"flex flex-col gap-6",
24-
// Tablet and up (640px+): side by side
25-
"sm:grid sm:grid-cols-2",
26-
// Never grow wider than the site-header
27-
"max-w-screen-2xl",
28-
// Ensure full width for children
29-
"[&>*]:w-full",
30-
className
31-
)}
32-
{...props}
33-
/>
34-
);
35-
}
36-
379
export default function DashboardPage() {
38-
return (
39-
<>
40-
<div className="p-4 max-w-screen-2xl">
41-
<div className="mb-8 w-full">
42-
<AddEntities />
43-
</div>
44-
</div>
45-
<DashboardContainer>
46-
<SelectCoachingRelationship />
47-
<CoachingSessionList />
48-
</DashboardContainer>
49-
</>
50-
);
10+
return <DashboardContent />;
5111
}

src/components/ui/coaching-session.tsx

Lines changed: 38 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import React, { useState } from "react";
3+
import React from "react";
44
import { format } from "date-fns";
55
import { Card, CardHeader } from "@/components/ui/card";
66
import { Button } from "@/components/ui/button";
@@ -15,63 +15,60 @@ import {
1515
DropdownMenuTrigger,
1616
} from "@/components/ui/dropdown-menu";
1717
import { MoreHorizontal } from "lucide-react";
18-
import { CoachingSessionFormMode } from "@/components/ui/dashboard/coaching-session-form";
1918
import { CoachingSession as CoachingSessionType } from "@/types/coaching-session";
2019

2120
interface CoachingSessionProps {
2221
coachingSession: CoachingSessionType;
22+
onUpdate: () => void;
2323
}
2424

2525
const CoachingSession: React.FC<CoachingSessionProps> = ({
2626
coachingSession,
27+
onUpdate,
2728
}) => {
2829
const { setCurrentCoachingSessionId } = useCoachingSessionStateStore(
2930
(state) => state
3031
);
31-
const [updateDialogOpen, setUpdateDialogOpen] = useState(false);
32-
const mode: CoachingSessionFormMode = "update";
3332

3433
return (
35-
<>
36-
<Card>
37-
<CardHeader className="p-4">
38-
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
39-
<div className="space-y-1">
40-
<OverarchingGoal coachingSessionId={coachingSession.id} />
41-
<div className="text-sm text-muted-foreground">
42-
{format(new Date(coachingSession.date), "MMMM d, yyyy h:mm a")}
43-
</div>
34+
<Card>
35+
<CardHeader className="p-4">
36+
<div className="flex flex-col sm:flex-row sm:items-center justify-between gap-2">
37+
<div className="space-y-1">
38+
<OverarchingGoal coachingSessionId={coachingSession.id} />
39+
<div className="text-sm text-muted-foreground">
40+
{format(new Date(coachingSession.date), "MMMM d, yyyy h:mm a")}
4441
</div>
45-
<div className="flex items-center gap-2">
46-
<Link href={`/coaching-sessions/${coachingSession.id}`} passHref>
47-
<Button
48-
size="sm"
49-
className="w-full sm:w-auto mt-2 sm:mt-0 text-sm px-3 py-1"
50-
onClick={() => setCurrentCoachingSessionId(coachingSession.id)}
51-
>
52-
Join Session
42+
</div>
43+
<div className="flex items-center gap-2">
44+
<Link href={`/coaching-sessions/${coachingSession.id}`} passHref>
45+
<Button
46+
size="sm"
47+
className="w-full sm:w-auto mt-2 sm:mt-0 text-sm px-3 py-1"
48+
onClick={() => setCurrentCoachingSessionId(coachingSession.id)}
49+
>
50+
Join Session
51+
</Button>
52+
</Link>
53+
<DropdownMenu>
54+
<DropdownMenuTrigger asChild>
55+
<Button variant="ghost" size="icon">
56+
<MoreHorizontal className="h-4 w-4" />
5357
</Button>
54-
</Link>
55-
<DropdownMenu>
56-
<DropdownMenuTrigger asChild>
57-
<Button variant="ghost" size="icon">
58-
<MoreHorizontal className="h-4 w-4" />
59-
</Button>
60-
</DropdownMenuTrigger>
61-
<DropdownMenuContent align="end">
62-
<DropdownMenuItem onClick={() => setUpdateDialogOpen(true)}>
63-
Update Session
64-
</DropdownMenuItem>
65-
<DropdownMenuItem className="text-destructive">
66-
Delete Session
67-
</DropdownMenuItem>
68-
</DropdownMenuContent>
69-
</DropdownMenu>
70-
</div>
58+
</DropdownMenuTrigger>
59+
<DropdownMenuContent align="end">
60+
<DropdownMenuItem onClick={onUpdate}>
61+
Update Session
62+
</DropdownMenuItem>
63+
<DropdownMenuItem className="text-destructive">
64+
Delete Session
65+
</DropdownMenuItem>
66+
</DropdownMenuContent>
67+
</DropdownMenu>
7168
</div>
72-
</CardHeader>
73-
</Card>
74-
</>
69+
</div>
70+
</CardHeader>
71+
</Card>
7572
);
7673
};
7774

src/components/ui/dashboard/add-entities.tsx

Lines changed: 9 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,52 +1,35 @@
11
"use client";
22

3-
import { useState } from "react";
4-
import { CoachingSessionDialog } from "./coaching-session-dialog";
5-
import CoachingSessionForm, { CoachingSessionFormMode } from "./coaching-session-form";
63
import { AddCoachingSessionButton } from "./add-coaching-session-button";
74
import { AddMemberButton } from "./add-member-button";
85
import { useRouter } from "next/navigation";
96
import { useOrganizationStateStore } from "@/lib/providers/organization-state-store-provider";
107
import { useAuthStore } from "@/lib/providers/auth-store-provider";
118

12-
export default function AddEntities() {
9+
interface AddEntitiesProps {
10+
onCreateSession: () => void;
11+
}
12+
13+
export default function AddEntities({ onCreateSession }: AddEntitiesProps) {
1314
const router = useRouter();
1415
const { currentOrganizationId } = useOrganizationStateStore((state) => state);
1516
const { isCoach } = useAuthStore((state) => state);
16-
const [open, setOpen] = useState(false);
1717

1818
const onMemberButtonClicked = () => {
1919
router.push(`/organizations/${currentOrganizationId}/members`);
2020
};
2121

22-
const mode: CoachingSessionFormMode = "create";
23-
2422
return (
2523
<div className="space-y-4">
2624
<h3 className="text-xl sm:text-2xl font-semibold tracking-tight">
2725
Add New
2826
</h3>
2927
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
30-
<CoachingSessionDialog
31-
open={open}
32-
onOpenChange={setOpen}
33-
mode={mode}
34-
dialogTrigger={
35-
<AddCoachingSessionButton
36-
disabled={!isCoach || !currentOrganizationId}
37-
/>
38-
}
39-
>
40-
<CoachingSessionForm
41-
mode={mode}
42-
onOpenChange={setOpen}
43-
/>
44-
</CoachingSessionDialog>
28+
<AddCoachingSessionButton
29+
disabled={!isCoach || !currentOrganizationId}
30+
onClick={onCreateSession}
31+
/>
4532

46-
{/* TODO: Refactor the AddMemberButton and AddMemberDialog to work just like
47-
AddCoachingSessionDialog does above, where the dialog is the parent container
48-
and it accepts a AddMemberButton as the dialogTrigger parameter.
49-
*/}
5033
<AddMemberButton
5134
disabled={!isCoach || !currentOrganizationId}
5235
onClick={onMemberButtonClicked}

src/components/ui/dashboard/coaching-session-dialog.tsx

Lines changed: 11 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,62 +1,41 @@
11
"use client";
22

33
import React from "react";
4-
import { Button } from "@/components/ui/button";
54
import {
65
Dialog,
76
DialogContent,
87
DialogHeader,
98
DialogTitle,
10-
DialogTrigger,
119
} from "@/components/ui/dialog";
12-
import { CoachingSessionFormMode } from "./coaching-session-form";
13-
import { useAuthStore } from "@/lib/providers/auth-store-provider";
14-
import { useCoachingRelationshipStateStore } from "@/lib/providers/coaching-relationship-state-store-provider";
10+
import CoachingSessionForm, { CoachingSessionFormMode } from "./coaching-session-form";
11+
import type { CoachingSession } from "@/types/coaching-session";
1512

1613
interface CoachingSessionDialogProps {
1714
open: boolean;
1815
onOpenChange: (open: boolean) => void;
19-
mode: CoachingSessionFormMode;
20-
dialogTrigger?: React.ReactElement<React.HTMLAttributes<HTMLButtonElement>>;
21-
children: React.ReactNode;
16+
coachingSessionToEdit?: CoachingSession;
2217
}
2318

2419
export function CoachingSessionDialog({
2520
open,
2621
onOpenChange,
27-
mode,
28-
dialogTrigger,
29-
children,
22+
coachingSessionToEdit,
3023
}: CoachingSessionDialogProps) {
31-
32-
const { isCoach } = useAuthStore((state) => state);
33-
const { currentCoachingRelationshipId } = useCoachingRelationshipStateStore(
34-
(state) => state
35-
);
24+
const mode: CoachingSessionFormMode = coachingSessionToEdit ? "update" : "create";
3625

3726
return (
3827
<Dialog open={open} onOpenChange={onOpenChange}>
39-
<DialogTrigger asChild>
40-
{dialogTrigger ?? (
41-
<Button
42-
variant="outline"
43-
size="sm"
44-
className="w-full sm:w-auto"
45-
disabled={!isCoach || !currentCoachingRelationshipId}
46-
>
47-
Create New Coaching Session
48-
</Button>
49-
)}
50-
</DialogTrigger>
5128
<DialogContent>
5229
<DialogHeader>
5330
<DialogTitle>
54-
{mode === "create"
55-
? "Create New Coaching Session"
56-
: "Update Coaching Session"}
31+
{mode === "create" ? "Create New Coaching Session" : "Update Coaching Session"}
5732
</DialogTitle>
5833
</DialogHeader>
59-
{children}
34+
<CoachingSessionForm
35+
mode={mode}
36+
existingSession={coachingSessionToEdit}
37+
onOpenChange={onOpenChange}
38+
/>
6039
</DialogContent>
6140
</Dialog>
6241
);

0 commit comments

Comments
 (0)