Skip to content

Commit 3b21b68

Browse files
committed
separate form from dialog
1 parent 6f0be1a commit 3b21b68

File tree

5 files changed

+168
-161
lines changed

5 files changed

+168
-161
lines changed

src/components/ui/coaching-session.tsx

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ import {
1616
} from "@/components/ui/dropdown-menu";
1717
import { MoreHorizontal } from "lucide-react";
1818
import { CoachingSessionDialog } from "@/components/ui/dashboard/coaching-session-dialog";
19+
import CoachingSessionForm, { CoachingSessionFormMode } from "@/components/ui/dashboard/coaching-session-form";
1920
import { DateTime } from "ts-luxon";
2021

2122
interface CoachingSessionProps {
@@ -35,6 +36,7 @@ const CoachingSession: React.FC<CoachingSessionProps> = ({
3536
(state) => state
3637
);
3738
const [updateDialogOpen, setUpdateDialogOpen] = useState(false);
39+
const mode: CoachingSessionFormMode = "update";
3840

3941
return (
4042
<>
@@ -78,13 +80,14 @@ const CoachingSession: React.FC<CoachingSessionProps> = ({
7880
<CoachingSessionDialog
7981
open={updateDialogOpen}
8082
onOpenChange={setUpdateDialogOpen}
81-
onCoachingSessionUpdated={() => {
82-
// Refresh the list of coaching sessions
83-
window.location.reload();
84-
}}
85-
existingSession={coachingSession}
86-
mode="update"
87-
/>
83+
mode={mode}
84+
>
85+
<CoachingSessionForm
86+
existingSession={coachingSession}
87+
mode={mode}
88+
onOpenChange={setUpdateDialogOpen}
89+
/>
90+
</CoachingSessionDialog>
8891
</>
8992
);
9093
};

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

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -2,21 +2,18 @@
22

33
import { useState } from "react";
44
import { CoachingSessionDialog } from "./coaching-session-dialog";
5-
import { AddCoachingSessionButton } from "./add-coaching-session-button";
65
import { AddMemberButton } from "./add-member-button";
76
import { useRouter } from "next/navigation";
87
import { useOrganizationStateStore } from "@/lib/providers/organization-state-store-provider";
98
import { useAuthStore } from "@/lib/providers/auth-store-provider";
9+
import CoachingSessionForm, { CoachingSessionFormMode } from "@/components/ui/dashboard/coaching-session-form";
1010

1111
export default function AddEntities() {
1212
const router = useRouter();
13-
const [open, setOpen] = useState(false);
1413
const { currentOrganizationId } = useOrganizationStateStore((state) => state);
1514
const { isCoach } = useAuthStore((state) => state);
16-
17-
const onCoachingSessionAdded = () => {
18-
setOpen(false);
19-
};
15+
const mode: CoachingSessionFormMode = "create";
16+
const [open, setOpen] = useState(false);
2017

2118
const onMemberButtonClicked = () => {
2219
router.push(`/organizations/${currentOrganizationId}/members`);
@@ -27,28 +24,25 @@ export default function AddEntities() {
2724
<h3 className="text-xl sm:text-2xl font-semibold tracking-tight">
2825
Add New
2926
</h3>
30-
<div className="grid grid-cols-1 gap-4 md:grid-cols-2">
31-
<CoachingSessionDialog
32-
mode="create"
33-
open={open}
27+
<CoachingSessionDialog
28+
open={open}
29+
onOpenChange={setOpen}
30+
mode={mode}
31+
>
32+
<CoachingSessionForm
33+
mode={mode}
3434
onOpenChange={setOpen}
35-
onCoachingSessionUpdated={onCoachingSessionAdded}
36-
dialogTrigger={
37-
<AddCoachingSessionButton
38-
disabled={!isCoach || !currentOrganizationId}
39-
/>
40-
}
4135
/>
36+
</CoachingSessionDialog>
4237

43-
{/* TODO: Refactor the AddMemberButton and AddMemberDialog to work just like
38+
{/* TODO: Refactor the AddMemberButton and AddMemberDialog to work just like
4439
AddCoachingSessionDialog does above, where the dialog is the parent container
4540
and it accepts a AddMemberButton as the dialogTrigger parameter.
4641
*/}
47-
<AddMemberButton
48-
disabled={!isCoach || !currentOrganizationId}
49-
onClick={onMemberButtonClicked}
50-
/>
51-
</div>
42+
<AddMemberButton
43+
disabled={!isCoach || !currentOrganizationId}
44+
onClick={onMemberButtonClicked}
45+
/>
5246
</div>
5347
);
5448
}

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

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

33
import React from "react";
4-
import { useState } from "react";
54
import { Button } from "@/components/ui/button";
65
import {
76
Dialog,
@@ -10,108 +9,41 @@ import {
109
DialogTitle,
1110
DialogTrigger,
1211
} from "@/components/ui/dialog";
13-
import { Label } from "@/components/ui/label";
14-
import { Calendar } from "@/components/ui/calendar";
15-
import { useCoachingRelationshipStateStore } from "@/lib/providers/coaching-relationship-state-store-provider";
16-
import { getDateTimeFromString } from "@/types/general";
17-
import {
18-
CoachingSession,
19-
defaultCoachingSession,
20-
} from "@/types/coaching-session";
21-
import {
22-
useCoachingSessionList,
23-
useCoachingSessionMutation,
24-
} from "@/lib/api/coaching-sessions";
25-
import { DateTime } from "ts-luxon";
12+
import { CoachingSessionFormMode } from "./coaching-session-form";
2613
import { useAuthStore } from "@/lib/providers/auth-store-provider";
27-
import { cn } from "@/components/lib/utils";
28-
import { format } from "date-fns";
14+
import { useCoachingRelationshipStateStore } from "@/lib/providers/coaching-relationship-state-store-provider";
2915

3016
interface CoachingSessionDialogProps {
3117
open: boolean;
3218
onOpenChange: (open: boolean) => void;
33-
onCoachingSessionUpdated: () => void;
34-
dialogTrigger?: React.ReactElement<React.HTMLAttributes<HTMLButtonElement>>;
35-
existingSession?: CoachingSession;
36-
mode: "create" | "update";
19+
mode: CoachingSessionFormMode;
20+
children: React.ReactNode;
3721
}
3822

3923
export function CoachingSessionDialog({
4024
open,
4125
onOpenChange,
42-
onCoachingSessionUpdated,
43-
dialogTrigger,
44-
existingSession,
4526
mode,
27+
children,
4628
}: CoachingSessionDialogProps) {
29+
30+
const { isCoach } = useAuthStore((state) => state);
4731
const { currentCoachingRelationshipId } = useCoachingRelationshipStateStore(
4832
(state) => state
4933
);
50-
const fromDate = DateTime.now().minus({ month: 1 });
51-
const toDate = DateTime.now().plus({ month: 1 });
52-
const { refresh } = useCoachingSessionList(
53-
currentCoachingRelationshipId,
54-
fromDate,
55-
toDate
56-
);
57-
const { create: createCoachingSession, update } =
58-
useCoachingSessionMutation();
59-
const [sessionDate, setSessionDate] = useState<Date | undefined>(
60-
existingSession ? new Date(existingSession.date) : undefined
61-
);
62-
const [sessionTime, setSessionTime] = useState<string>(
63-
existingSession ? format(new Date(existingSession.date), "HH:mm") : ""
64-
);
65-
const { isCoach } = useAuthStore((state) => state);
66-
67-
const handleSubmit = async (e: React.FormEvent) => {
68-
e.preventDefault();
69-
if (!sessionDate || !sessionTime) return;
70-
71-
const [hours, minutes] = sessionTime.split(":").map(Number);
72-
const dateTime = getDateTimeFromString(sessionDate.toISOString())
73-
.set({ hour: hours, minute: minutes })
74-
.toFormat("yyyy-MM-dd'T'HH:mm:ss");
75-
76-
try {
77-
if (mode === "create") {
78-
const newCoachingSession: CoachingSession = {
79-
...defaultCoachingSession(),
80-
coaching_relationship_id: currentCoachingRelationshipId,
81-
date: dateTime,
82-
};
83-
await createCoachingSession(newCoachingSession);
84-
} else if (existingSession) {
85-
await update(existingSession.id, {
86-
...existingSession,
87-
date: dateTime,
88-
updated_at: DateTime.now(),
89-
});
90-
}
91-
refresh();
92-
onCoachingSessionUpdated();
93-
setSessionDate(undefined);
94-
setSessionTime("");
95-
onOpenChange(false);
96-
} catch (error) {
97-
console.error(`Failed to ${mode} coaching session:`, error);
98-
}
99-
};
10034

10135
return (
10236
<Dialog open={open} onOpenChange={onOpenChange}>
103-
{dialogTrigger && (
104-
<DialogTrigger asChild>
105-
{React.cloneElement(dialogTrigger, {
106-
...(dialogTrigger.props as React.HTMLAttributes<HTMLButtonElement>),
107-
className: cn(
108-
(dialogTrigger.props as React.HTMLAttributes<HTMLButtonElement>)
109-
.className
110-
),
111-
})}
112-
</DialogTrigger>
113-
)}
114-
37+
<DialogTrigger asChild>
38+
<Button
39+
variant="outline"
40+
size="sm"
41+
className="w-full sm:w-auto"
42+
disabled={!isCoach || !currentCoachingRelationshipId}
43+
>
44+
Create New Coaching Session
45+
</Button>
46+
</DialogTrigger>
11547
<DialogContent>
11648
<DialogHeader>
11749
<DialogTitle>
@@ -120,30 +52,7 @@ export function CoachingSessionDialog({
12052
: "Update Coaching Session"}
12153
</DialogTitle>
12254
</DialogHeader>
123-
<form onSubmit={handleSubmit} className="space-y-4">
124-
<div className="space-y-2">
125-
<Label htmlFor="session-date">Session Date</Label>
126-
<Calendar
127-
mode="single"
128-
selected={sessionDate}
129-
onSelect={(date) => setSessionDate(date)}
130-
/>
131-
</div>
132-
<div className="space-y-2">
133-
<Label htmlFor="session-time">Session Time</Label>
134-
<input
135-
type="time"
136-
id="session-time"
137-
value={sessionTime}
138-
onChange={(e) => setSessionTime(e.target.value)}
139-
className="w-full border rounded p-2"
140-
required
141-
/>
142-
</div>
143-
<Button type="submit" disabled={!sessionDate || !sessionTime}>
144-
{mode === "create" ? "Create Session" : "Update Session"}
145-
</Button>
146-
</form>
55+
{children}
14756
</DialogContent>
14857
</Dialog>
14958
);
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
2+
3+
import { CoachingSession } from "@/types/coaching-session";
4+
import { Label } from "@/components/ui/label";
5+
import { Calendar } from "@/components/ui/calendar";
6+
import { Button } from "@/components/ui/button";
7+
import { useCoachingRelationshipStateStore } from "@/lib/providers/coaching-relationship-state-store-provider";
8+
import { useCoachingSessionList, useCoachingSessionMutation } from "@/lib/api/coaching-sessions";
9+
import { DateTime } from "ts-luxon";
10+
import { useAuthStore } from "@/lib/providers/auth-store-provider";
11+
import { format } from "date-fns";
12+
import { getDateTimeFromString } from "@/types/general";
13+
import { useState } from "react";
14+
import { defaultCoachingSession } from "@/types/coaching-session";
15+
16+
export type CoachingSessionFormMode = "create" | "update";
17+
18+
19+
interface CoachingSessionFormProps {
20+
existingSession?: CoachingSession;
21+
mode: CoachingSessionFormMode;
22+
onOpenChange: (open: boolean) => void;
23+
}
24+
25+
export default function CoachingSessionForm({
26+
existingSession,
27+
mode,
28+
onOpenChange
29+
}: CoachingSessionFormProps) {
30+
31+
const { currentCoachingRelationshipId } = useCoachingRelationshipStateStore(
32+
(state) => state
33+
);
34+
const fromDate = DateTime.now().minus({ month: 1 });
35+
const toDate = DateTime.now().plus({ month: 1 });
36+
const { refresh } = useCoachingSessionList(
37+
currentCoachingRelationshipId,
38+
fromDate,
39+
toDate
40+
);
41+
const { create: createCoachingSession, update } =
42+
useCoachingSessionMutation();
43+
const [sessionDate, setSessionDate] = useState<Date | undefined>(
44+
existingSession ? new Date(existingSession.date) : undefined
45+
);
46+
const [sessionTime, setSessionTime] = useState<string>(
47+
existingSession ? format(new Date(existingSession.date), "HH:mm") : ""
48+
);
49+
50+
const handleSubmit = async (e: React.FormEvent) => {
51+
e.preventDefault();
52+
if (!sessionDate || !sessionTime) return;
53+
54+
const [hours, minutes] = sessionTime.split(":").map(Number);
55+
const dateTime = getDateTimeFromString(sessionDate.toISOString())
56+
.set({ hour: hours, minute: minutes })
57+
.toFormat("yyyy-MM-dd'T'HH:mm:ss");
58+
59+
try {
60+
if (mode === "create") {
61+
const newCoachingSession: CoachingSession = {
62+
...defaultCoachingSession(),
63+
coaching_relationship_id: currentCoachingRelationshipId,
64+
date: dateTime,
65+
};
66+
await createCoachingSession(newCoachingSession);
67+
} else if (existingSession) {
68+
await update(existingSession.id, {
69+
...existingSession,
70+
date: dateTime,
71+
updated_at: DateTime.now(),
72+
});
73+
}
74+
refresh();
75+
setSessionDate(undefined);
76+
setSessionTime("");
77+
onOpenChange(false);
78+
} catch (error) {
79+
console.error(`Failed to ${mode} coaching session:`, error);
80+
}
81+
};
82+
83+
return (
84+
<div>
85+
<form onSubmit={handleSubmit} className="space-y-4">
86+
<div className="space-y-2">
87+
<Label htmlFor="session-date">Session Date</Label>
88+
<Calendar
89+
mode="single"
90+
selected={sessionDate}
91+
onSelect={(date) => setSessionDate(date)}
92+
/>
93+
</div>
94+
<div className="space-y-2">
95+
<Label htmlFor="session-time">Session Time</Label>
96+
<input
97+
type="time"
98+
id="session-time"
99+
value={sessionTime}
100+
onChange={(e) => setSessionTime(e.target.value)}
101+
className="w-full border rounded p-2"
102+
required
103+
/>
104+
</div>
105+
<Button type="submit" disabled={!sessionDate || !sessionTime}>
106+
{mode === "create" ? "Create Session" : "Update Session"}
107+
</Button>
108+
</form>
109+
</div>
110+
);
111+
}

0 commit comments

Comments
 (0)