api: allow to disable a medicine regimen
Parents:
afb7b645 file(s) changed
- api/src/controllers/medicineRegimens.ts +35 -0
- api/src/routes/medicineRegimens.ts +2 -0
- api/src/schemas/medicineRegimen.schema.ts +6 -0
- api/src/services/careAction.service.ts +15 -0
- api/src/services/medicineRegimen.service.ts +32 -0
api/src/controllers/medicineRegimens.ts
1 1
import { Response, NextFunction } from 'express';
2 2
import { AuthenticatedRequest } from '@app/middleware/auth';
3 3
import { CreateMedicineRegimenSchema, UpdateMedicineRegimenSchema } from '@app/schemas';
4 +
import { ToggleMedicineRegimenSchema } from '@app/schemas/medicineRegimen.schema';
4 5
import {
5 6
getMedicineRegimensByUserId,
7 +
getMedicineRegimenById,
6 8
createMedicineRegimen,
7 9
updateMedicineRegimen,
8 10
deactivateMedicineRegimen,
9 11
getTodayMedicineRegimens,
12 +
toggleMedicineRegimen,
10 13
} from '@app/services/medicineRegimen.service';
11 14
12 15
export const MedicineRegimensController = {
13 16
index: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
14 17
try {
▸ 13 unchanged lines
28 31
} catch (err) {
29 32
next(err);
30 33
}
31 34
},
32 35
36 +
show: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
37 +
try {
38 +
const regimen = await getMedicineRegimenById(req.userId!, Number(req.params.id));
39 +
40 +
if (!regimen) return res.status(404).json({ error: 'Not found' });
41 +
42 +
return res.status(200).json({ regimen });
43 +
} catch (err) {
44 +
next(err);
45 +
}
46 +
},
47 +
33 48
create: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
34 49
try {
35 50
const parsed = CreateMedicineRegimenSchema.safeParse(req.body.regimen);
36 51
37 52
if (!parsed.success) {
▸ 18 unchanged lines
56 71
57 72
const regimen = await updateMedicineRegimen(
58 73
req.userId!,
59 74
Number(req.params.id),
60 75
parsed.data,
76 +
);
77 +
78 +
return res.status(200).json({ regimen });
79 +
} catch (err) {
80 +
next(err);
81 +
}
82 +
},
83 +
84 +
toggle: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
85 +
try {
86 +
const parsed = ToggleMedicineRegimenSchema.safeParse(req.body);
87 +
88 +
if (!parsed.success) {
89 +
return res.status(400).json({ errors: parsed.error!.issues });
90 +
}
91 +
92 +
const regimen = await toggleMedicineRegimen(
93 +
req.userId!,
94 +
Number(req.params.id),
95 +
parsed.data.active,
61 96
);
62 97
63 98
return res.status(200).json({ regimen });
64 99
} catch (err) {
65 100
next(err);
▸ 8 unchanged lines
74 109
} catch (err) {
75 110
next(err);
76 111
}
77 112
},
78 113
};
api/src/routes/medicineRegimens.ts
1 1
import { Router } from 'express';
2 2
import { MedicineRegimensController } from '@app/controllers/medicineRegimens';
3 3
import { requireAuth } from '@app/middleware/auth';
4 4
5 5
const router = Router();
6 6
7 7
router.get('/', requireAuth, MedicineRegimensController.index);
8 8
router.get('/today', requireAuth, MedicineRegimensController.today);
9 9
router.post('/', requireAuth, MedicineRegimensController.create);
10 +
router.get('/:id', requireAuth, MedicineRegimensController.show);
11 +
router.patch('/:id/active', requireAuth, MedicineRegimensController.toggle);
10 12
router.patch('/:id', requireAuth, MedicineRegimensController.update);
11 13
router.delete('/:id', requireAuth, MedicineRegimensController.destroy);
12 14
13 15
export default router;
api/src/schemas/medicineRegimen.schema.ts
1 1
import { z } from 'zod';
2 2
import { MedicinePeriodicity } from '@prisma/client';
3 3
4 4
export const CreateMedicineRegimenSchema = z.object({
5 5
name: z.string().min(1),
▸ 11 unchanged lines
17 17
scheduledAt: z.array(z.string()).optional().default([]),
18 18
active: z.boolean().optional(),
19 19
});
20 20
21 21
export type UpdateMedicineRegimenInput = z.infer<typeof UpdateMedicineRegimenSchema>;
22 +
23 +
export const ToggleMedicineRegimenSchema = z.object({
24 +
active: z.boolean(),
25 +
});
26 +
27 +
export type ToggleMedicineRegimenInput = z.infer<typeof ToggleMedicineRegimenSchema>;
api/src/services/careAction.service.ts
1 1
import { prisma } from '@app/lib/prisma';
2 2
import { CareAction, CareActionType } from '@prisma/client';
3 3
import { CreateCareActionInput, PatchCareActionInput } from '@app/schemas';
4 +
import { syncMedicineReminderJobs } from '@app/services/medicineReminder.sync';
4 5
5 6
const careActionIncludes = {
6 7
medicineLog: { include: { regimen: true } },
7 8
appointment: true,
8 9
activity: true,
▸ 32 unchanged lines
41 42
include: careActionIncludes,
42 43
orderBy: { moment: 'desc' },
43 44
take: limit,
44 45
skip,
45 46
}),
47 +
46 48
prisma.careAction.count({ where }),
47 49
]);
48 50
49 51
const totalPages = Math.ceil(total / limit);
50 52
▸ 38 unchanged lines
89 91
input: CreateCareActionInput,
90 92
) => {
91 93
if (input.type === CareActionType.MEDICINE) {
92 94
if (input.regimenId) {
93 95
const regimenId = input.regimenId;
96 +
94 97
const [careAction] = await prisma.$transaction([
95 98
prisma.careAction.create({
96 99
data: {
97 100
userId,
98 101
type: input.type,
▸ 5 unchanged lines
104 107
},
105 108
},
106 109
include: careActionIncludes,
107 110
}),
108 111
]);
112 +
109 113
return careAction;
110 114
}
111 115
112 116
if (input.medicine) {
113 117
const med = input.medicine;
118 +
114 119
const [careAction] = await prisma.$transaction([
115 120
prisma.careAction.create({
116 121
data: {
117 122
userId,
118 123
type: input.type,
▸ 15 unchanged lines
134 139
},
135 140
},
136 141
include: careActionIncludes,
137 142
}),
138 143
]);
144 +
145 +
if (careAction.medicineLog?.regimen) {
146 +
await syncMedicineReminderJobs(careAction.medicineLog.regimen as any);
147 +
}
148 +
139 149
return careAction;
140 150
}
141 151
142 152
// MEDICINE with no regimen info: create action without log
143 153
const [careAction] = await prisma.$transaction([
▸ 6 unchanged lines
150 160
moodId: input.moodId,
151 161
},
152 162
include: careActionIncludes,
153 163
}),
154 164
]);
165 +
155 166
return careAction;
156 167
}
157 168
158 169
if (input.type === CareActionType.APPOINTMENT && input.appointment) {
159 170
const appt = input.appointment;
171 +
160 172
const [careAction] = await prisma.$transaction([
161 173
prisma.careAction.create({
162 174
data: {
163 175
userId,
164 176
type: input.type,
▸ 9 unchanged lines
174 186
},
175 187
},
176 188
include: careActionIncludes,
177 189
}),
178 190
]);
191 +
179 192
return careAction;
180 193
}
181 194
182 195
if (input.type === CareActionType.ACTIVITY && input.activity) {
183 196
const act = input.activity;
197 +
184 198
const [careAction] = await prisma.$transaction([
185 199
prisma.careAction.create({
186 200
data: {
187 201
userId,
188 202
type: input.type,
▸ 8 unchanged lines
197 211
},
198 212
},
199 213
include: careActionIncludes,
200 214
}),
201 215
]);
216 +
202 217
return careAction;
203 218
}
204 219
205 220
// Fallback: create without subtype
206 221
return prisma.careAction.create({
▸ 5 unchanged lines
212 227
moodId: input.moodId,
213 228
},
214 229
include: careActionIncludes,
215 230
});
216 231
};
api/src/services/medicineRegimen.service.ts
1 1
import { prisma } from '@app/lib/prisma';
2 2
import { MedicineRegimen } from '@prisma/client';
3 3
import { CreateMedicineRegimenInput, UpdateMedicineRegimenInput } from '@app/schemas';
4 4
import { syncMedicineReminderJobs } from '@app/services/medicineReminder.sync';
5 5
6 +
export const toggleMedicineRegimen = async (
7 +
userId: number,
8 +
id: number,
9 +
active: boolean,
10 +
): Promise<MedicineRegimen> => {
11 +
const regimen = await prisma.medicineRegimen.update({
12 +
where: { id, userId },
13 +
data: { active },
14 +
});
15 +
16 +
await syncMedicineReminderJobs(regimen);
17 +
18 +
return regimen;
19 +
};
20 +
6 21
type TodayEntry = {
7 22
regimen: Pick<MedicineRegimen, 'id' | 'name' | 'dosage' | 'scheduledAt'>;
8 23
logs: { id: number; takenAt: Date }[];
9 24
};
10 25
26 +
export const getMedicineRegimenById = async (
27 +
userId: number,
28 +
id: number,
29 +
): Promise<MedicineRegimen | null> => {
30 +
return prisma.medicineRegimen.findUnique({
31 +
where: { id, userId },
32 +
});
33 +
};
34 +
11 35
export const getMedicineRegimensByUserId = async (
12 36
userId: number,
13 37
): Promise<MedicineRegimen[]> => {
14 38
return prisma.medicineRegimen.findMany({
15 39
where: { userId },
▸ 6 unchanged lines
22 46
input: CreateMedicineRegimenInput,
23 47
): Promise<MedicineRegimen> => {
24 48
const regimen = await prisma.medicineRegimen.create({
25 49
data: { ...input, userId },
26 50
});
51 +
27 52
await syncMedicineReminderJobs(regimen);
53 +
28 54
return regimen;
29 55
};
30 56
31 57
export const updateMedicineRegimen = async (
32 58
userId: number,
▸ 2 unchanged lines
35 61
): Promise<MedicineRegimen> => {
36 62
const regimen = await prisma.medicineRegimen.update({
37 63
where: { id, userId },
38 64
data: input,
39 65
});
66 +
40 67
await syncMedicineReminderJobs(regimen);
68 +
41 69
return regimen;
42 70
};
43 71
44 72
export const getTodayMedicineRegimens = async (
45 73
userId: number,
46 74
): Promise<TodayEntry[]> => {
47 75
const now = new Date();
76 +
48 77
const startOfDay = new Date(now);
49 78
startOfDay.setUTCHours(0, 0, 0, 0);
79 +
50 80
const endOfDay = new Date(now);
51 81
endOfDay.setUTCHours(23, 59, 59, 999);
52 82
53 83
const regimens = await prisma.medicineRegimen.findMany({
54 84
where: { userId, active: true },
▸ 21 unchanged lines
76 106
): Promise<MedicineRegimen> => {
77 107
const regimen = await prisma.medicineRegimen.update({
78 108
where: { id, userId },
79 109
data: { active: false },
80 110
});
111 +
81 112
await syncMedicineReminderJobs(regimen);
113 +
82 114
return regimen;
83 115
};