Skip to content

Commit 1314e49

Browse files
committed
Add fetch/create/update an OverarchingGoal tied into the backend
1 parent 4d044fe commit 1314e49

File tree

8 files changed

+753
-290
lines changed

8 files changed

+753
-290
lines changed

src/components/ui/coaching-sessions/actions-list.tsx

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@ import {
3434
} from "@/components/ui/dropdown-menu";
3535
import { MoreHorizontal, ArrowUpDown, Save, CalendarClock } from "lucide-react";
3636
import {
37-
ActionStatus,
37+
ItemStatus,
3838
actionStatusToString,
3939
Id,
4040
stringToActionStatus,
@@ -52,13 +52,13 @@ const ActionsList: React.FC<{
5252
locale: string | "us";
5353
onActionAdded: (
5454
body: string,
55-
status: ActionStatus,
55+
status: ItemStatus,
5656
dueBy: DateTime
5757
) => Promise<Action>;
5858
onActionEdited: (
5959
id: Id,
6060
body: string,
61-
status: ActionStatus,
61+
status: ItemStatus,
6262
dueBy: DateTime
6363
) => Promise<Action>;
6464
onActionDeleted: (id: Id) => Promise<Action>;
@@ -80,14 +80,12 @@ const ActionsList: React.FC<{
8080

8181
const [actions, setActions] = useState<Action[]>([]);
8282
const [newBody, setNewBody] = useState("");
83-
const [newStatus, setNewStatus] = useState<ActionStatus>(
84-
ActionStatus.NotStarted
85-
);
83+
const [newStatus, setNewStatus] = useState<ItemStatus>(ItemStatus.NotStarted);
8684
const [newDueBy, setNewDueBy] = useState<DateTime>(DateTime.now());
8785
const [editingId, setEditingId] = useState<Id | null>(null);
8886
const [editBody, setEditBody] = useState("");
89-
const [editStatus, setEditStatus] = useState<ActionStatus>(
90-
ActionStatus.NotStarted
87+
const [editStatus, setEditStatus] = useState<ItemStatus>(
88+
ItemStatus.NotStarted
9189
);
9290
const [editDueBy, setEditDueBy] = useState<DateTime>(DateTime.now());
9391
const [sortColumn, setSortColumn] = useState<keyof Action>(
@@ -113,14 +111,14 @@ const ActionsList: React.FC<{
113111
});
114112

115113
setNewBody("");
116-
setNewStatus(ActionStatus.NotStarted);
114+
setNewStatus(ItemStatus.NotStarted);
117115
setNewDueBy(DateTime.now());
118116
};
119117

120118
const updateAction = async (
121119
id: Id,
122120
newBody: string,
123-
newStatus: ActionStatus,
121+
newStatus: ItemStatus,
124122
newDueBy: DateTime
125123
) => {
126124
const body = newBody.trim();
@@ -155,7 +153,7 @@ const ActionsList: React.FC<{
155153
setActions(updatedActions);
156154
setEditingId(null);
157155
setEditBody("");
158-
setEditStatus(ActionStatus.NotStarted);
156+
setEditStatus(ItemStatus.NotStarted);
159157
setEditDueBy(DateTime.now());
160158
} catch (err) {
161159
console.error("Failed to update Action (id: " + id + "): ", err);
@@ -313,7 +311,7 @@ const ActionsList: React.FC<{
313311
<SelectValue placeholder="Select a status" />
314312
</SelectTrigger>
315313
<SelectContent>
316-
{Object.values(ActionStatus).map((s) => (
314+
{Object.values(ItemStatus).map((s) => (
317315
<SelectItem value={s} key={s}>
318316
{actionStatusToString(s)}
319317
</SelectItem>

src/components/ui/coaching-sessions/overarching-goal-container.tsx

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

3-
import { useState } from "react";
3+
import { useEffect, useState } from "react";
44
import { ActionsList } from "@/components/ui/coaching-sessions/actions-list";
5-
import { ActionStatus, Id } from "@/types/general";
5+
import { ItemStatus, Id } from "@/types/general";
66
import { Action } from "@/types/action";
77
import { AgreementsList } from "@/components/ui/coaching-sessions/agreements-list";
88
import { Agreement } from "@/types/agreement";
@@ -17,12 +17,24 @@ import { DateTime } from "ts-luxon";
1717
import { siteConfig } from "@/site.config";
1818
import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs";
1919
import { useAppStateStore } from "@/lib/providers/app-state-store-provider";
20-
import { OverarchingGoal } from "./overarching-goal";
20+
import { OverarchingGoalComponent } from "./overarching-goal";
21+
import {
22+
createOverarchingGoal,
23+
fetchOverarchingGoalsByCoachingSessionId,
24+
updateOverarchingGoal,
25+
} from "@/lib/api/overarching-goals";
26+
import {
27+
defaultOverarchingGoal,
28+
OverarchingGoal,
29+
overarchingGoalToString,
30+
} from "@/types/overarching-goal";
2131

2232
const OverarchingGoalContainer: React.FC<{
2333
userId: Id;
2434
}> = ({ userId }) => {
2535
const [isOpen, setIsOpen] = useState(false);
36+
const [goal, setGoal] = useState<OverarchingGoal>(defaultOverarchingGoal());
37+
const [goalId, setGoalId] = useState<Id>("");
2638
const { coachingSession, coachingRelationship } = useAppStateStore(
2739
(state) => ({
2840
coachingSession: state.coachingSession,
@@ -66,7 +78,7 @@ const OverarchingGoalContainer: React.FC<{
6678

6779
const handleActionAdded = (
6880
body: string,
69-
status: ActionStatus,
81+
status: ItemStatus,
7082
dueBy: DateTime
7183
): Promise<Action> => {
7284
// Calls the backend endpoint that creates and stores a full Action entity
@@ -83,7 +95,7 @@ const OverarchingGoalContainer: React.FC<{
8395
const handleActionEdited = (
8496
id: Id,
8597
body: string,
86-
status: ActionStatus,
98+
status: ItemStatus,
8799
dueBy: DateTime
88100
): Promise<Action> => {
89101
return updateAction(id, coachingSession.id, body, status, dueBy)
@@ -107,6 +119,84 @@ const OverarchingGoalContainer: React.FC<{
107119
});
108120
};
109121

122+
useEffect(() => {
123+
async function fetchOverarchingGoal() {
124+
if (!coachingSession.id) {
125+
console.error(
126+
"Failed to fetch Overarching Goal since coachingSession.id is not set."
127+
);
128+
return;
129+
}
130+
131+
await fetchOverarchingGoalsByCoachingSessionId(coachingSession.id)
132+
.then((goals) => {
133+
const goal = goals[0];
134+
if (goals.length > 0) {
135+
console.trace("Overarching Goal: " + overarchingGoalToString(goal));
136+
setGoalId(goal.id);
137+
setGoal(goal);
138+
} else {
139+
console.trace(
140+
"No Overarching Goals associated with this coachingSession.id"
141+
);
142+
}
143+
})
144+
.catch((err) => {
145+
console.error(
146+
"Failed to fetch Overarching Goal for current coaching session: " +
147+
err
148+
);
149+
});
150+
}
151+
fetchOverarchingGoal();
152+
}, [coachingSession.id, goalId]);
153+
154+
const handleGoalChange = async (newGoal: OverarchingGoal) => {
155+
console.trace("handleGoalChange (goal to set/update): " + newGoal.title);
156+
157+
if (goalId && coachingSession.id) {
158+
console.debug("Update existing Overarching Goal with id: " + goalId);
159+
updateOverarchingGoal(
160+
goalId,
161+
coachingSession.id,
162+
newGoal.title,
163+
newGoal.body,
164+
newGoal.status
165+
)
166+
.then((responseGoal) => {
167+
console.trace(
168+
"Updated Overarching Goal: " + overarchingGoalToString(responseGoal)
169+
);
170+
setGoal(responseGoal);
171+
})
172+
.catch((err) => {
173+
console.error("Failed to update Overarching Goal: " + err);
174+
});
175+
} else if (!goalId && coachingSession.id) {
176+
createOverarchingGoal(
177+
coachingSession.id,
178+
newGoal.title,
179+
newGoal.body,
180+
newGoal.status
181+
)
182+
.then((responseGoal) => {
183+
console.trace(
184+
"Newly created Overarching Goal: " +
185+
overarchingGoalToString(responseGoal)
186+
);
187+
setGoal(responseGoal);
188+
setGoalId(responseGoal.id);
189+
})
190+
.catch((err) => {
191+
console.error("Failed to create new Overarching Goal: " + err);
192+
});
193+
} else {
194+
console.error(
195+
"Could not update or create a Overarching Goal since coachingSession.id or userId are not set."
196+
);
197+
}
198+
};
199+
110200
return (
111201
<div className="grid grid-flow-row auto-rows-min gap-4">
112202
<div className="row-span-1 pt-4">
@@ -115,9 +205,11 @@ const OverarchingGoalContainer: React.FC<{
115205
onOpenChange={setIsOpen}
116206
className="w-full space-y-2"
117207
>
118-
<OverarchingGoal
208+
<OverarchingGoalComponent
209+
initialValue={goal}
119210
onOpenChange={(open: boolean) => setIsOpen(open)}
120-
></OverarchingGoal>
211+
onGoalChange={(goal: OverarchingGoal) => handleGoalChange(goal)}
212+
></OverarchingGoalComponent>
121213
<CollapsibleContent className="px-4">
122214
<div className="flex-col space-y-4 sm:flex">
123215
<div className="grid flex-1 items-start gap-4 sm:py-0 md:gap-8">

src/components/ui/coaching-sessions/overarching-goal.tsx

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -11,32 +11,37 @@ import {
1111
} from "@/components/ui/tooltip";
1212
import { useState, useEffect } from "react";
1313
import { cn } from "@/lib/utils";
14+
import {
15+
defaultOverarchingGoal,
16+
OverarchingGoal,
17+
} from "@/types/overarching-goal";
1418

15-
const OverarchingGoal: React.FC<{ onOpenChange: (open: boolean) => void }> = ({
16-
onOpenChange,
17-
}) => {
19+
const OverarchingGoalComponent: React.FC<{
20+
initialValue: OverarchingGoal;
21+
onOpenChange: (open: boolean) => void;
22+
onGoalChange: (goal: OverarchingGoal) => void;
23+
}> = ({ initialValue, onOpenChange, onGoalChange }) => {
1824
const [isOpen, setIsOpen] = useState<boolean>(false);
19-
const [goal, setGoal] = useState<string>("");
20-
const [tempGoal, setTempGoal] = useState(goal);
25+
const [tempGoalTitle, setTempGoalTitle] = useState<string>("");
26+
const [overarchingGoal, setOverarchingGoal] = useState<OverarchingGoal>(
27+
defaultOverarchingGoal()
28+
);
2129

2230
useEffect(() => {
23-
// Update tempGoal whenever goal changes
24-
setTempGoal(goal);
25-
}, [goal]);
31+
setOverarchingGoal(initialValue);
32+
setTempGoalTitle(initialValue.title);
33+
}, [initialValue]);
2634

2735
const toggleDrawer = () => {
28-
if (isOpen) {
29-
// When closing, reset tempGoal to match the current goal
30-
setTempGoal(goal);
31-
}
3236
onOpenChange(!isOpen);
3337
setIsOpen(!isOpen);
3438
};
3539

3640
const handleSetGoal = async () => {
37-
// TODO: make a new callback function for when the goal gets set so the parent can
38-
// call the backend and store the new overarching goal.
39-
setGoal(tempGoal); // Update the main goal state
41+
var tempGoal = overarchingGoal;
42+
tempGoal.title = tempGoalTitle;
43+
setOverarchingGoal(tempGoal);
44+
onGoalChange(tempGoal);
4045
toggleDrawer();
4146
};
4247

@@ -51,8 +56,8 @@ const OverarchingGoal: React.FC<{ onOpenChange: (open: boolean) => void }> = ({
5156
<div id="label" className="flex w-full mr-2 min-w-0">
5257
{isOpen ? (
5358
<Input
54-
value={tempGoal}
55-
onChange={(e) => setTempGoal(e.target.value)}
59+
value={tempGoalTitle}
60+
onChange={(e) => setTempGoalTitle(e.target.value)}
5661
onKeyDown={(e) => e.key === "Enter" && handleSetGoal()}
5762
className={cn("w-full h-6 bg-inherit border-0 p-1")}
5863
placeholder="Insert a new overarching goal"
@@ -61,9 +66,11 @@ const OverarchingGoal: React.FC<{ onOpenChange: (open: boolean) => void }> = ({
6166
<div>
6267
<span className="hidden md:inline-flex truncate">
6368
<div className="mr-1">{"Overarching goal: "}</div>
64-
<div>{goal}</div>
69+
<div>{overarchingGoal.title}</div>
70+
</span>
71+
<span className="inline-flex md:hidden">
72+
{overarchingGoal.title}
6573
</span>
66-
<span className="inline-flex md:hidden">{goal}</span>
6774
</div>
6875
)}
6976
</div>
@@ -112,4 +119,4 @@ const OverarchingGoal: React.FC<{ onOpenChange: (open: boolean) => void }> = ({
112119
);
113120
};
114121

115-
export { OverarchingGoal };
122+
export { OverarchingGoalComponent };

0 commit comments

Comments
 (0)