source: frontend/src/pages/Diary.tsx

main
Last change on this file was 700e2f9, checked in by 186079 <matej.milevski@…>, 5 days ago

Init

  • Property mode set to 100644
File size: 7.4 KB
RevLine 
[700e2f9]1import {
2 type Component,
3 createEffect,
4 createResource,
5 createSignal,
6 Show,
7} from "solid-js";
8import { useNavigate } from "@solidjs/router";
9import { useAuth } from "@/context/AuthContext";
10import { diaryApi, type DiaryEntry } from "@/api/diary";
11import { therapistApi } from "@/api/therapist";
12import CalendarHeader from "@/components/CalendarHeader";
13import DayHeaders from "@/components/DayHeaders";
14import CalendarGrid from "@/components/CalendarGrid";
15import RatingLegend from "@/components/RatingLegend";
16import DiaryModal from "@/components/DiaryModal";
17import PatientSelector from "@/components/PatientSelector";
18import { dateToString, getMonthName, getTodayString } from "@/utils";
19import { UserType } from "@/enums/UserType";
20
21const Diary: Component = () => {
22 const { user, isAuthenticated } = useAuth();
23 const navigate = useNavigate();
24
25 const now = new Date();
26 const [currentYear, setCurrentYear] = createSignal(now.getFullYear());
27 const [currentMonth, setCurrentMonth] = createSignal(now.getMonth() + 1);
28 const [isModalOpen, setIsModalOpen] = createSignal(false);
29 const [selectedDateStr, setSelectedDateStr] = createSignal<string | null>(
30 null,
31 );
32 const [selectedEntry, setSelectedEntry] = createSignal<
33 DiaryEntry | undefined
34 >(undefined);
35
36 const [selectedPatientId, setSelectedPatientId] = createSignal<number | null>(
37 null,
38 );
39 const isTherapist = () => user()?.userType === UserType.THERAPIST;
40
41 const [patients] = createResource(
42 () => ({
43 authenticated: isAuthenticated(),
44 isTherapist: isTherapist(),
45 }),
46 async (params) => {
47 if (!params.authenticated || !params.isTherapist) return [];
48 return await therapistApi.getTherapistPatients();
49 },
50 );
51
52 createEffect(() => {
53 const patientList = patients();
54 if (
55 isTherapist() &&
56 patientList &&
57 patientList.length > 0 &&
58 selectedPatientId() === null
59 ) {
60 setSelectedPatientId(patientList[0].userId);
61 }
62 });
63
64 const [diaryEntries, { refetch }] = createResource(
65 () => ({
66 authenticated: isAuthenticated(),
67 userId: isTherapist() ? selectedPatientId() : user()?.userId,
68 year: currentYear(),
69 month: currentMonth(),
70 }),
71
72 async (params) => {
73 if (!params.authenticated || !params.userId) return [];
74 return await diaryApi.getDiaryEntries(
75 params.userId,
76 params.year,
77 params.month,
78 );
79 },
80 );
81
82 createEffect(() => {
83 if (!isAuthenticated()) {
84 navigate("/login", { replace: true });
85 return;
86 }
87 });
88
89 const goToPreviousMonth = () => {
90 if (currentMonth() === 1) {
91 setCurrentMonth(12);
92 setCurrentYear(currentYear() - 1);
93 } else {
94 setCurrentMonth(currentMonth() - 1);
95 }
96 };
97
98 const goToNextMonth = () => {
99 if (currentMonth() === 12) {
100 setCurrentMonth(1);
101 setCurrentYear(currentYear() + 1);
102 } else {
103 setCurrentMonth(currentMonth() + 1);
104 }
105 };
106
107 const generateCalendarDays = () => {
108 const year = currentYear();
109 const month = currentMonth();
110 const firstDay = new Date(year, month - 1, 1);
111 const lastDay = new Date(year, month, 0);
112 const daysInMonth = lastDay.getDate();
113 const startDayOfWeek = firstDay.getDay();
114
115 const mondayBasedStart = startDayOfWeek === 0 ? 6 : startDayOfWeek - 1;
116
117 const days: Array<{
118 day: number | null;
119 isCurrentMonth: boolean;
120 date: Date | null;
121 }> = [];
122
123 for (let i = 0; i < mondayBasedStart; i++) {
124 days.push({ day: null, isCurrentMonth: false, date: null });
125 }
126
127 for (let day = 1; day <= daysInMonth; day++) {
128 days.push({
129 day,
130 isCurrentMonth: true,
131 date: new Date(year, month - 1, day),
132 });
133 }
134
135 return days;
136 };
137
138 const getEntryForDate = (date: Date | null): DiaryEntry | undefined => {
139 if (!date || !diaryEntries()) return undefined;
140 const dateStr = dateToString(date);
141 return diaryEntries()!.find((entry) => entry.date === dateStr);
142 };
143
144 const isToday = (dateStr: string): boolean => dateStr === getTodayString();
145
146 const handleDayClick = (date: Date | null) => {
147 if (isTherapist() || !date) return;
148
149 const dateStr = dateToString(date);
150 const entry = getEntryForDate(date);
151
152 if (!entry && !isToday(dateStr)) {
153 return;
154 }
155
156 setSelectedDateStr(dateStr);
157 setSelectedEntry(entry);
158 setIsModalOpen(true);
159 };
160
161 const handleSaveEntry = async (rating: number, content: string) => {
162 const dateStr = selectedDateStr();
163 if (!dateStr) return;
164
165 const entry = selectedEntry();
166
167 if (entry) {
168 const updateData: { content: string; dailyRating?: number } = { content };
169
170 if (isToday(dateStr)) {
171 updateData.dailyRating = rating;
172 }
173
174 await diaryApi.updateDiaryEntry(entry.idDiary, updateData);
175 } else {
176 await diaryApi.createDiaryEntry({
177 dailyRating: rating,
178 content,
179 });
180 }
181
182 refetch();
183 setIsModalOpen(false);
184 setSelectedDateStr(null);
185 setSelectedEntry(undefined);
186 };
187
188 const handleDeleteEntry = async () => {
189 const entry = selectedEntry();
190 if (!entry) return;
191
192 await diaryApi.deleteDiaryEntry(entry.idDiary);
193
194 refetch();
195 setIsModalOpen(false);
196 setSelectedDateStr(null);
197 setSelectedEntry(undefined);
198 };
199
200 const handleCloseModal = () => {
201 setIsModalOpen(false);
202 setSelectedDateStr(null);
203 setSelectedEntry(undefined);
204 };
205
206 const getSelectedDate = (): Date | null => {
207 const dateStr = selectedDateStr();
208 if (!dateStr) return null;
209
210 const [year, month, day] = dateStr.split("-").map(Number);
211
212 return new Date(year, month - 1, day);
213 };
214
215 return (
216 <div class="container mx-auto px-4 py-8">
217 <div class="mb-8">
218 <h1 class="text-3xl font-bold text-gray-900 mb-2">
219 {isTherapist() ? "Patient Diaries" : "My Diary"}
220 </h1>
221 <p class="text-gray-600">
222 {isTherapist()
223 ? "View your patients' daily mood ratings"
224 : "Track your daily thoughts and feelings"}
225 </p>
226 </div>
227
228 <Show when={isTherapist()}>
229 <PatientSelector
230 patients={patients()}
231 loading={patients.loading}
232 selectedPatientId={selectedPatientId()}
233 onPatientChange={setSelectedPatientId}
234 />
235 </Show>
236
237 <div class="bg-white rounded-lg shadow-lg p-6">
238 <CalendarHeader
239 monthName={getMonthName(currentYear(), currentMonth())}
240 onPreviousMonth={goToPreviousMonth}
241 onNextMonth={goToNextMonth}
242 />
243
244 <DayHeaders />
245 <Show
246 when={!diaryEntries.loading}
247 fallback={
248 <div class="flex justify-center items-center py-12">
249 <div class="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-600" />
250 </div>
251 }
252 >
253 <CalendarGrid
254 days={generateCalendarDays()}
255 getEntryForDate={getEntryForDate}
256 onDayClick={handleDayClick}
257 isTherapistView={isTherapist()}
258 />
259 </Show>
260 <RatingLegend />
261 </div>
262
263 <Show when={selectedDateStr() && getSelectedDate()}>
264 <DiaryModal
265 isOpen={isModalOpen()}
266 onClose={handleCloseModal}
267 entry={selectedEntry()}
268 date={getSelectedDate()!}
269 onSave={handleSaveEntry}
270 onDelete={handleDeleteEntry}
271 isToday={isToday(selectedDateStr()!)}
272 />
273 </Show>
274 </div>
275 );
276};
277
278export default Diary;
Note: See TracBrowser for help on using the repository browser.