Skip to content

Commit 30bda35

Browse files
authored
Merge pull request #52 from refactor-group/coaching_session_fast_switch
Coaching session fast switch select component
2 parents 736e5cf + 5fef790 commit 30bda35

File tree

4 files changed

+215
-80
lines changed

4 files changed

+215
-80
lines changed

src/app/coaching-sessions/[id]/page.tsx

Lines changed: 75 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -36,66 +36,80 @@ import {
3636
CoachingNotes,
3737
EditorRef,
3838
} from "@/components/ui/coaching-sessions/coaching-notes";
39+
import { CoachingSessionSelector } from "@/components/ui/coaching-session-selector";
40+
import { CoachingSession } from "@/types/coaching-session";
41+
import { useRouter } from "next/navigation";
42+
import { fetchCoachingSessions } from "@/lib/api/coaching-sessions";
3943

4044
// export const metadata: Metadata = {
4145
// title: "Coaching Session",
4246
// description: "Coaching session main page, where the good stuff happens.",
4347
// };
4448

4549
export default function CoachingSessionsPage() {
50+
const router = useRouter();
4651
const [note, setNote] = useState<Note>(defaultNote());
4752
const [syncStatus, setSyncStatus] = useState<string>("");
4853
const { userId } = useAuthStore((state) => ({ userId: state.userId }));
4954
const { coachingSession } = useAppStateStore((state) => state);
55+
const { coachingSessionId, setCoachingSessionId } = useAppStateStore(
56+
(state) => state
57+
);
58+
const [coachingSessions, setCoachingSessions] = React.useState<
59+
CoachingSession[]
60+
>([]);
61+
const { relationshipId } = useAppStateStore((state) => state);
5062
const [isLoading, setIsLoading] = useState(false);
5163
const editorRef = useRef<EditorRef>(null);
5264

53-
async function fetchNote() {
54-
if (!coachingSession.id) {
55-
console.error(
56-
"Failed to fetch Note since coachingSession.id is not set."
57-
);
58-
return;
59-
}
60-
if (isLoading) {
61-
console.debug(
62-
"Not issuing a new Note fetch because a previous fetch is still in progress."
63-
);
64-
}
65+
const fetchNoteData = async () => {
66+
if (isLoading) return;
6567

6668
setIsLoading(true);
69+
try {
70+
const notes = await fetchNotesByCoachingSessionId(coachingSessionId);
71+
if (notes.length > 0) {
72+
setEditorContent(notes[0].body);
73+
setNote(notes[0]);
74+
setSyncStatus("Notes refreshed");
75+
setEditorFocussed();
76+
}
77+
} catch (err) {
78+
console.error(`Failed to fetch Note: ${err}`);
79+
} finally {
80+
setIsLoading(false);
81+
}
82+
};
6783

68-
await fetchNotesByCoachingSessionId(coachingSession.id)
69-
.then((notes) => {
70-
const note = notes[0];
71-
if (notes.length > 0) {
72-
console.trace("Fetched note: " + noteToString(note));
73-
setEditorContent(note.body);
74-
setNote(note);
75-
setSyncStatus("Notes refreshed");
76-
setEditorFocussed();
77-
} else {
78-
console.trace(
79-
"No Notes associated with this coachingSession.id: " +
80-
coachingSession.id
81-
);
82-
}
83-
})
84-
.catch((err) => {
85-
console.error(
86-
"Failed to fetch Note for current coaching session id: " +
87-
coachingSession.id +
88-
". Error: " +
89-
err
90-
);
91-
});
84+
useEffect(() => {
85+
if (!coachingSessionId) return;
9286

93-
setIsLoading(false);
94-
}
87+
fetchNoteData();
88+
}, [coachingSessionId]); // Remove isLoading from dependencies
9589

9690
useEffect(() => {
97-
fetchNote();
98-
}, [coachingSession.id, isLoading]);
91+
if (!relationshipId) return;
92+
93+
const loadCoachingSessions = async () => {
94+
if (isLoading) return;
95+
96+
setIsLoading(true);
97+
98+
try {
99+
const [coachingSessions] = await fetchCoachingSessions(relationshipId);
100+
console.debug(
101+
"setCoachingSessions: " + JSON.stringify(coachingSessions)
102+
);
103+
setCoachingSessions(coachingSessions);
104+
} catch (err) {
105+
console.error("Failed to fetch coaching sessions: " + err);
106+
} finally {
107+
setIsLoading(false);
108+
}
109+
};
110+
111+
loadCoachingSessions();
112+
}, [relationshipId]);
99113

100114
const setEditorContent = (content: string) => {
101115
editorRef.current?.setContent(`${content}`);
@@ -108,14 +122,14 @@ export default function CoachingSessionsPage() {
108122
const handleOnChange = (value: string) => {
109123
console.debug("isLoading (before update/create): " + isLoading);
110124
console.debug(
111-
"coachingSession.id (before update/create): " + coachingSession.id
125+
"coachingSessionId (before update/create): " + coachingSessionId
112126
);
113127
console.debug("userId (before update/create): " + userId);
114128
console.debug("value (before update/create): " + value);
115129
console.debug("--------------------------------");
116130

117-
if (!isLoading && note.id && coachingSession.id && userId) {
118-
updateNote(note.id, coachingSession.id, userId, value)
131+
if (!isLoading && note.id && coachingSessionId && userId) {
132+
updateNote(note.id, coachingSessionId, userId, value)
119133
.then((updatedNote) => {
120134
setNote(updatedNote);
121135
console.trace("Updated Note: " + noteToString(updatedNote));
@@ -126,7 +140,7 @@ export default function CoachingSessionsPage() {
126140
console.error("Failed to update Note: " + err);
127141
});
128142
} else if (!isLoading && !note.id && coachingSession.id && userId) {
129-
createNote(coachingSession.id, userId, value)
143+
createNote(coachingSessionId, userId, value)
130144
.then((createdNote) => {
131145
setNote(createdNote);
132146
console.trace("Newly created Note: " + noteToString(createdNote));
@@ -151,6 +165,12 @@ export default function CoachingSessionsPage() {
151165
document.title = sessionTitle;
152166
};
153167

168+
const handleCoachingSessionSelect = (coachingSessionId: string) => {
169+
setCoachingSessionId(coachingSessionId);
170+
console.debug("coachingSessionId selected: " + coachingSessionId);
171+
router.push(`/coaching-sessions/${coachingSessionId}`);
172+
};
173+
154174
return (
155175
<div className="max-w-screen-2xl">
156176
<div className="flex-col h-full md:flex ">
@@ -160,12 +180,12 @@ export default function CoachingSessionsPage() {
160180
style={siteConfig.titleStyle}
161181
onRender={handleTitleRender}
162182
></CoachingSessionTitle>
163-
<div className="ml-auto flex w-full space-x-2 sm:justify-end">
164-
<PresetSelector current={current} future={future} past={past} />
165-
{/* Hidden for MVP */}
166-
<div className="hidden">
167-
<PresetActions />
168-
</div>
183+
<div className="ml-auto flex w-[28rem] space-x-2 sm:justify-end">
184+
<CoachingSessionSelector
185+
sessions={coachingSessions}
186+
placeholder="Select a coaching session"
187+
onSelect={handleCoachingSessionSelect}
188+
></CoachingSessionSelector>
169189
</div>
170190
</div>
171191
</div>
@@ -224,7 +244,11 @@ export default function CoachingSessionsPage() {
224244
<div className="flex items-center justify-between">
225245
<Label htmlFor="refresh">Notes Actions</Label>
226246
</div>
227-
<Button id="refresh" variant="outline" onClick={fetchNote}>
247+
<Button
248+
id="refresh"
249+
variant="outline"
250+
onClick={fetchNoteData}
251+
>
228252
<SymbolIcon className="mr-2 h-4 w-4" /> Refresh Notes
229253
</Button>
230254
</div>
Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
"use client";
2+
3+
import * as React from "react";
4+
import { PopoverProps } from "@radix-ui/react-popover";
5+
6+
import {
7+
Select,
8+
SelectContent,
9+
SelectGroup,
10+
SelectItem,
11+
SelectLabel,
12+
SelectTrigger,
13+
SelectValue,
14+
} from "@/components/ui/select";
15+
16+
import "@/styles/code-block.scss";
17+
import {
18+
CoachingSession,
19+
coachingSessionToString,
20+
getCoachingSessionById,
21+
} from "@/types/coaching-session";
22+
import { getDateTimeFromString, Id } from "@/types/general";
23+
import { DateTime } from "ts-luxon";
24+
import { useAppStateStore } from "@/lib/providers/app-state-store-provider";
25+
26+
interface CoachingSessionSelectorProps extends PopoverProps {
27+
sessions: CoachingSession[];
28+
placeholder: string;
29+
onSelect: (session: Id) => void;
30+
}
31+
32+
export function CoachingSessionSelector({
33+
sessions,
34+
placeholder,
35+
onSelect,
36+
...props
37+
}: CoachingSessionSelectorProps) {
38+
const { coachingSessionId, setCoachingSessionId } = useAppStateStore(
39+
(state) => state
40+
);
41+
const { setCoachingSession } = useAppStateStore((state) => state);
42+
43+
const handleSetCoachingSession = (coachingSessionId: string) => {
44+
setCoachingSessionId(coachingSessionId);
45+
const coachingSession = getCoachingSessionById(coachingSessionId, sessions);
46+
console.debug(
47+
"coachingSession: " + coachingSessionToString(coachingSession)
48+
);
49+
setCoachingSession(coachingSession);
50+
onSelect(coachingSessionId);
51+
};
52+
53+
return (
54+
<Select
55+
defaultValue="today"
56+
value={coachingSessionId}
57+
onValueChange={handleSetCoachingSession}
58+
>
59+
<SelectTrigger id="session">
60+
<SelectValue placeholder={placeholder} />
61+
</SelectTrigger>
62+
<SelectContent>
63+
{sessions.some(
64+
(session) => getDateTimeFromString(session.date) < DateTime.now()
65+
) && (
66+
<SelectGroup>
67+
<SelectLabel>Previous Sessions</SelectLabel>
68+
{sessions
69+
.filter(
70+
(session) =>
71+
getDateTimeFromString(session.date) < DateTime.now()
72+
)
73+
.map((session) => (
74+
<SelectItem value={session.id} key={session.id}>
75+
{getDateTimeFromString(session.date).toLocaleString(
76+
DateTime.DATETIME_FULL
77+
)}
78+
</SelectItem>
79+
))}
80+
</SelectGroup>
81+
)}
82+
{sessions.some(
83+
(session) => getDateTimeFromString(session.date) >= DateTime.now()
84+
) && (
85+
<SelectGroup>
86+
<SelectLabel>Upcoming Sessions</SelectLabel>
87+
{sessions
88+
.filter(
89+
(session) =>
90+
getDateTimeFromString(session.date) >= DateTime.now()
91+
)
92+
.map((session) => (
93+
<SelectItem value={session.id} key={session.id}>
94+
{getDateTimeFromString(session.date).toLocaleString(
95+
DateTime.DATETIME_FULL
96+
)}
97+
</SelectItem>
98+
))}
99+
</SelectGroup>
100+
)}
101+
{sessions.length == 0 && (
102+
<SelectItem disabled={true} value="none">
103+
None found
104+
</SelectItem>
105+
)}
106+
</SelectContent>
107+
</Select>
108+
);
109+
}

0 commit comments

Comments
 (0)