api: fetch and create care actions/medicines
Parents:
b4b88476 file(s) changed
- api/src/controllers/careActions.ts +74 -0
- api/src/controllers/medicineRegimens.ts +67 -0
- api/src/routes/careActions.ts +12 -0
- api/src/routes/medicineRegimens.ts +12 -0
- api/src/services/careAction.service.ts +204 -0
- api/src/services/medicineRegimen.service.ts +42 -0
api/src/controllers/careActions.ts
@@ -0,0 +1,74 @@
1 + import { Response, NextFunction } from 'express';
2 + import { AuthenticatedRequest } from '@app/middleware/auth';
3 + import { CareActionType } from '@prisma/client';
4 + import { CreateCareActionSchema } from '@app/schemas';
5 + import {
6 + getCareActionsByUserId,
7 + getCareActionById,
8 + createCareAction,
9 + destroyCareActionById,
10 + } from '@app/services/careAction.service';
11 +
12 + export const CareActionsController = {
13 + index: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
14 + try {
15 + const result = await getCareActionsByUserId(Number(req.userId), {
16 + type: req.query.type as CareActionType | undefined,
17 + from: req.query.from as string | undefined,
18 + to: req.query.to as string | undefined,
19 + limit: req.query.limit ? Number(req.query.limit) : undefined,
20 + page: req.query.page ? Number(req.query.page) : undefined,
21 + });
22 +
23 + return res.status(200).json(result);
24 + } catch (err) {
25 + next(err);
26 + }
27 + },
28 +
29 + show: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
30 + try {
31 + const careAction = await getCareActionById(
32 + Number(req.userId),
33 + Number(req.params.id),
34 + );
35 +
36 + if (!careAction) {
37 + return res.status(404).json({ errors: 'Care action was not found' });
38 + }
39 +
40 + return res.status(200).json({ careAction });
41 + } catch (err) {
42 + next(err);
43 + }
44 + },
45 +
46 + create: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
47 + try {
48 + const parsed = CreateCareActionSchema.safeParse(req.body.careAction);
49 +
50 + req.log.info("Params: %s", req.body.careAction)
51 + req.log.info("Care action: %s", parsed.error)
52 +
53 + if (!parsed.success) {
54 + return res.status(400).json({ errors: parsed.error!.issues });
55 + }
56 +
57 + const careAction = await createCareAction(req.userId!, parsed.data);
58 +
59 + return res.status(201).json({ careAction });
60 + } catch (err) {
61 + next(err);
62 + }
63 + },
64 +
65 + destroy: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
66 + try {
67 + await destroyCareActionById(req.userId!, Number(req.params.id));
68 +
69 + return res.status(200).json({});
70 + } catch (err) {
71 + next(err);
72 + }
73 + },
74 + };
api/src/controllers/medicineRegimens.ts
@@ -0,0 +1,67 @@
1 + import { Response, NextFunction } from 'express';
2 + import { AuthenticatedRequest } from '@app/middleware/auth';
3 + import { CreateMedicineRegimenSchema, UpdateMedicineRegimenSchema } from '@app/schemas';
4 + import {
5 + getMedicineRegimensByUserId,
6 + createMedicineRegimen,
7 + updateMedicineRegimen,
8 + deactivateMedicineRegimen,
9 + } from '@app/services/medicineRegimen.service';
10 +
11 + export const MedicineRegimensController = {
12 + index: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
13 + try {
14 + const regimens = await getMedicineRegimensByUserId(Number(req.userId));
15 +
16 + return res.status(200).json({ regimens });
17 + } catch (err) {
18 + next(err);
19 + }
20 + },
21 +
22 + create: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
23 + try {
24 + const parsed = CreateMedicineRegimenSchema.safeParse(req.body.regimen);
25 +
26 + if (!parsed.success) {
27 + return res.status(400).json({ errors: parsed.error!.issues });
28 + }
29 +
30 + const regimen = await createMedicineRegimen(req.userId!, parsed.data);
31 +
32 + return res.status(201).json({ regimen });
33 + } catch (err) {
34 + next(err);
35 + }
36 + },
37 +
38 + update: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
39 + try {
40 + const parsed = UpdateMedicineRegimenSchema.safeParse(req.body.regimen);
41 +
42 + if (!parsed.success) {
43 + return res.status(400).json({ errors: parsed.error!.issues });
44 + }
45 +
46 + const regimen = await updateMedicineRegimen(
47 + req.userId!,
48 + Number(req.params.id),
49 + parsed.data,
50 + );
51 +
52 + return res.status(200).json({ regimen });
53 + } catch (err) {
54 + next(err);
55 + }
56 + },
57 +
58 + destroy: async (req: AuthenticatedRequest, res: Response, next: NextFunction) => {
59 + try {
60 + await deactivateMedicineRegimen(req.userId!, Number(req.params.id));
61 +
62 + return res.status(200).json({});
63 + } catch (err) {
64 + next(err);
65 + }
66 + },
67 + };
api/src/routes/careActions.ts
@@ -0,0 +1,12 @@
1 + import { Router } from 'express';
2 + import { CareActionsController } from '@app/controllers/careActions';
3 + import { requireAuth } from '@app/middleware/auth';
4 +
5 + const router = Router();
6 +
7 + router.get('/', requireAuth, CareActionsController.index);
8 + router.post('/', requireAuth, CareActionsController.create);
9 + router.get('/:id', requireAuth, CareActionsController.show);
10 + router.delete('/:id', requireAuth, CareActionsController.destroy);
11 +
12 + export default router;
api/src/routes/medicineRegimens.ts
@@ -0,0 +1,12 @@
1 + import { Router } from 'express';
2 + import { MedicineRegimensController } from '@app/controllers/medicineRegimens';
3 + import { requireAuth } from '@app/middleware/auth';
4 +
5 + const router = Router();
6 +
7 + router.get('/', requireAuth, MedicineRegimensController.index);
8 + router.post('/', requireAuth, MedicineRegimensController.create);
9 + router.patch('/:id', requireAuth, MedicineRegimensController.update);
10 + router.delete('/:id', requireAuth, MedicineRegimensController.destroy);
11 +
12 + export default router;
api/src/services/careAction.service.ts
@@ -0,0 +1,204 @@
1 + import { prisma } from '@app/lib/prisma';
2 + import { CareAction, CareActionType } from '@prisma/client';
3 + import { CreateCareActionInput } from '@app/schemas';
4 +
5 + const careActionIncludes = {
6 + medicineLog: { include: { regimen: true } },
7 + appointment: true,
8 + activity: true,
9 + } as const;
10 +
11 + export const getCareActionsByUserId = async (
12 + userId: number,
13 + params?: {
14 + type?: CareActionType;
15 + from?: string;
16 + to?: string;
17 + page?: number;
18 + limit?: number;
19 + },
20 + ) => {
21 + const page = params?.page ?? 1;
22 + const limit = params?.limit ?? 20;
23 + const skip = (page - 1) * limit;
24 +
25 + const where = {
26 + userId,
27 + ...(params?.type ? { type: params.type } : {}),
28 + ...(params?.from || params?.to
29 + ? {
30 + moment: {
31 + ...(params?.from && { gte: new Date(params.from) }),
32 + ...(params?.to && { lte: new Date(params.to) }),
33 + },
34 + }
35 + : {}),
36 + };
37 +
38 + const [entries, total] = await Promise.all([
39 + prisma.careAction.findMany({
40 + where,
41 + include: careActionIncludes,
42 + orderBy: { moment: 'desc' },
43 + take: limit,
44 + skip,
45 + }),
46 + prisma.careAction.count({ where }),
47 + ]);
48 +
49 + const totalPages = Math.ceil(total / limit);
50 +
51 + return {
52 + entries,
53 + total,
54 + page,
55 + nextPage: page < totalPages ? page + 1 : null,
56 + };
57 + };
58 +
59 + export const getCareActionById = async (userId: number, id: number) => {
60 + return prisma.careAction.findUnique({
61 + where: { id, userId },
62 + include: careActionIncludes,
63 + });
64 + };
65 +
66 + export const destroyCareActionById = async (
67 + userId: number,
68 + id: number,
69 + ): Promise<CareAction> => {
70 + return prisma.careAction.delete({
71 + where: { id, userId },
72 + });
73 + };
74 +
75 + export const createCareAction = async (
76 + userId: number,
77 + input: CreateCareActionInput,
78 + ) => {
79 + if (input.type === CareActionType.MEDICINE) {
80 + if (input.regimenId) {
81 + const regimenId = input.regimenId;
82 + const [careAction] = await prisma.$transaction([
83 + prisma.careAction.create({
84 + data: {
85 + userId,
86 + type: input.type,
87 + moment: input.moment,
88 + triggerId: input.triggerId,
89 + moodId: input.moodId,
90 + medicineLog: {
91 + create: { regimenId },
92 + },
93 + },
94 + include: careActionIncludes,
95 + }),
96 + ]);
97 + return careAction;
98 + }
99 +
100 + if (input.medicine) {
101 + const med = input.medicine;
102 + const [careAction] = await prisma.$transaction([
103 + prisma.careAction.create({
104 + data: {
105 + userId,
106 + type: input.type,
107 + moment: input.moment,
108 + triggerId: input.triggerId,
109 + moodId: input.moodId,
110 + medicineLog: {
111 + create: {
112 + regimen: {
113 + create: {
114 + name: med.name,
115 + dosage: med.dosage,
116 + periodicity: med.periodicity,
117 + scheduledAt: med.scheduledAt,
118 + userId,
119 + },
120 + },
121 + },
122 + },
123 + },
124 + include: careActionIncludes,
125 + }),
126 + ]);
127 + return careAction;
128 + }
129 +
130 + // MEDICINE with no regimen info: create action without log
131 + const [careAction] = await prisma.$transaction([
132 + prisma.careAction.create({
133 + data: {
134 + userId,
135 + type: input.type,
136 + moment: input.moment,
137 + triggerId: input.triggerId,
138 + moodId: input.moodId,
139 + },
140 + include: careActionIncludes,
141 + }),
142 + ]);
143 + return careAction;
144 + }
145 +
146 + if (input.type === CareActionType.APPOINTMENT && input.appointment) {
147 + const appt = input.appointment;
148 + const [careAction] = await prisma.$transaction([
149 + prisma.careAction.create({
150 + data: {
151 + userId,
152 + type: input.type,
153 + moment: input.moment,
154 + triggerId: input.triggerId,
155 + moodId: input.moodId,
156 + appointment: {
157 + create: {
158 + type: appt.type,
159 + duration: appt.duration,
160 + note: appt.note,
161 + },
162 + },
163 + },
164 + include: careActionIncludes,
165 + }),
166 + ]);
167 + return careAction;
168 + }
169 +
170 + if (input.type === CareActionType.ACTIVITY && input.activity) {
171 + const act = input.activity;
172 + const [careAction] = await prisma.$transaction([
173 + prisma.careAction.create({
174 + data: {
175 + userId,
176 + type: input.type,
177 + moment: input.moment,
178 + triggerId: input.triggerId,
179 + moodId: input.moodId,
180 + activity: {
181 + create: {
182 + type: act.type,
183 + duration: act.duration,
184 + },
185 + },
186 + },
187 + include: careActionIncludes,
188 + }),
189 + ]);
190 + return careAction;
191 + }
192 +
193 + // Fallback: create without subtype
194 + return prisma.careAction.create({
195 + data: {
196 + userId,
197 + type: input.type,
198 + moment: input.moment,
199 + triggerId: input.triggerId,
200 + moodId: input.moodId,
201 + },
202 + include: careActionIncludes,
203 + });
204 + };
api/src/services/medicineRegimen.service.ts
@@ -0,0 +1,42 @@
1 + import { prisma } from '@app/lib/prisma';
2 + import { MedicineRegimen } from '@prisma/client';
3 + import { CreateMedicineRegimenInput, UpdateMedicineRegimenInput } from '@app/schemas';
4 +
5 + export const getMedicineRegimensByUserId = async (
6 + userId: number,
7 + ): Promise<MedicineRegimen[]> => {
8 + return prisma.medicineRegimen.findMany({
9 + where: { userId, active: true },
10 + orderBy: { createdAt: 'desc' },
11 + });
12 + };
13 +
14 + export const createMedicineRegimen = async (
15 + userId: number,
16 + input: CreateMedicineRegimenInput,
17 + ): Promise<MedicineRegimen> => {
18 + return prisma.medicineRegimen.create({
19 + data: { ...input, userId },
20 + });
21 + };
22 +
23 + export const updateMedicineRegimen = async (
24 + userId: number,
25 + id: number,
26 + input: UpdateMedicineRegimenInput,
27 + ): Promise<MedicineRegimen> => {
28 + return prisma.medicineRegimen.update({
29 + where: { id, userId },
30 + data: input,
31 + });
32 + };
33 +
34 + export const deactivateMedicineRegimen = async (
35 + userId: number,
36 + id: number,
37 + ): Promise<MedicineRegimen> => {
38 + return prisma.medicineRegimen.update({
39 + where: { id, userId },
40 + data: { active: false },
41 + });
42 + };