@blackcode_sa/metaestetics-api 1.13.3 → 1.13.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/admin/index.d.mts +15 -28
- package/dist/admin/index.d.ts +15 -28
- package/dist/index.d.mts +18 -30
- package/dist/index.d.ts +18 -30
- package/dist/index.js +11 -3
- package/dist/index.mjs +11 -3
- package/package.json +121 -119
- package/src/__mocks__/firstore.ts +10 -10
- package/src/admin/aggregation/README.md +79 -79
- package/src/admin/aggregation/appointment/README.md +128 -128
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +1984 -1984
- package/src/admin/aggregation/appointment/index.ts +1 -1
- package/src/admin/aggregation/clinic/README.md +52 -52
- package/src/admin/aggregation/clinic/clinic.aggregation.service.ts +703 -703
- package/src/admin/aggregation/clinic/index.ts +1 -1
- package/src/admin/aggregation/forms/README.md +13 -13
- package/src/admin/aggregation/forms/filled-forms.aggregation.service.ts +322 -322
- package/src/admin/aggregation/forms/index.ts +1 -1
- package/src/admin/aggregation/index.ts +8 -8
- package/src/admin/aggregation/patient/README.md +27 -27
- package/src/admin/aggregation/patient/index.ts +1 -1
- package/src/admin/aggregation/patient/patient.aggregation.service.ts +141 -141
- package/src/admin/aggregation/practitioner/README.md +42 -42
- package/src/admin/aggregation/practitioner/index.ts +1 -1
- package/src/admin/aggregation/practitioner/practitioner.aggregation.service.ts +433 -433
- package/src/admin/aggregation/practitioner-invite/index.ts +1 -1
- package/src/admin/aggregation/practitioner-invite/practitioner-invite.aggregation.service.ts +961 -961
- package/src/admin/aggregation/procedure/README.md +43 -43
- package/src/admin/aggregation/procedure/index.ts +1 -1
- package/src/admin/aggregation/procedure/procedure.aggregation.service.ts +702 -702
- package/src/admin/aggregation/reviews/index.ts +1 -1
- package/src/admin/aggregation/reviews/reviews.aggregation.service.ts +689 -689
- package/src/admin/analytics/analytics.admin.service.ts +278 -278
- package/src/admin/analytics/index.ts +2 -2
- package/src/admin/booking/README.md +125 -125
- package/src/admin/booking/booking.admin.ts +1037 -1037
- package/src/admin/booking/booking.calculator.ts +712 -712
- package/src/admin/booking/booking.types.ts +59 -59
- package/src/admin/booking/index.ts +3 -3
- package/src/admin/booking/timezones-problem.md +185 -185
- package/src/admin/calendar/README.md +7 -7
- package/src/admin/calendar/calendar.admin.service.ts +345 -345
- package/src/admin/calendar/index.ts +1 -1
- package/src/admin/documentation-templates/document-manager.admin.ts +260 -260
- package/src/admin/documentation-templates/index.ts +1 -1
- package/src/admin/free-consultation/free-consultation-utils.admin.ts +148 -148
- package/src/admin/free-consultation/index.ts +1 -1
- package/src/admin/index.ts +81 -81
- package/src/admin/logger/index.ts +78 -78
- package/src/admin/mailing/README.md +95 -95
- package/src/admin/mailing/appointment/appointment.mailing.service.ts +732 -732
- package/src/admin/mailing/appointment/index.ts +1 -1
- package/src/admin/mailing/appointment/templates/patient/appointment-confirmed.html +40 -40
- package/src/admin/mailing/base.mailing.service.ts +208 -208
- package/src/admin/mailing/index.ts +3 -3
- package/src/admin/mailing/practitionerInvite/existing-practitioner-invite.mailing.ts +611 -611
- package/src/admin/mailing/practitionerInvite/index.ts +2 -2
- package/src/admin/mailing/practitionerInvite/practitionerInvite.mailing.ts +395 -395
- package/src/admin/mailing/practitionerInvite/templates/existing-practitioner-invitation.template.ts +155 -155
- package/src/admin/mailing/practitionerInvite/templates/invitation.template.ts +101 -101
- package/src/admin/mailing/practitionerInvite/templates/invite-accepted-notification.template.ts +228 -228
- package/src/admin/mailing/practitionerInvite/templates/invite-rejected-notification.template.ts +242 -242
- package/src/admin/notifications/index.ts +1 -1
- package/src/admin/notifications/notifications.admin.ts +710 -710
- package/src/admin/requirements/README.md +128 -128
- package/src/admin/requirements/index.ts +1 -1
- package/src/admin/requirements/patient-requirements.admin.service.ts +475 -475
- package/src/admin/users/index.ts +1 -1
- package/src/admin/users/user-profile.admin.ts +405 -405
- package/src/backoffice/constants/certification.constants.ts +13 -13
- package/src/backoffice/constants/index.ts +1 -1
- package/src/backoffice/errors/backoffice.errors.ts +181 -181
- package/src/backoffice/errors/index.ts +1 -1
- package/src/backoffice/expo-safe/README.md +26 -26
- package/src/backoffice/expo-safe/index.ts +41 -41
- package/src/backoffice/index.ts +5 -5
- package/src/backoffice/services/FIXES_README.md +102 -102
- package/src/backoffice/services/README.md +57 -57
- package/src/backoffice/services/analytics.service.proposal.md +863 -863
- package/src/backoffice/services/analytics.service.summary.md +143 -143
- package/src/backoffice/services/brand.service.ts +256 -256
- package/src/backoffice/services/category.service.ts +384 -384
- package/src/backoffice/services/constants.service.ts +385 -385
- package/src/backoffice/services/documentation-template.service.ts +202 -202
- package/src/backoffice/services/index.ts +10 -10
- package/src/backoffice/services/migrate-products.ts +116 -116
- package/src/backoffice/services/product.service.ts +553 -553
- package/src/backoffice/services/requirement.service.ts +235 -235
- package/src/backoffice/services/subcategory.service.ts +461 -461
- package/src/backoffice/services/technology.service.ts +1151 -1151
- package/src/backoffice/types/README.md +12 -12
- package/src/backoffice/types/admin-constants.types.ts +69 -69
- package/src/backoffice/types/brand.types.ts +29 -29
- package/src/backoffice/types/category.types.ts +67 -67
- package/src/backoffice/types/documentation-templates.types.ts +28 -28
- package/src/backoffice/types/index.ts +10 -10
- package/src/backoffice/types/procedure-product.types.ts +38 -38
- package/src/backoffice/types/product.types.ts +240 -240
- package/src/backoffice/types/requirement.types.ts +63 -63
- package/src/backoffice/types/static/README.md +18 -18
- package/src/backoffice/types/static/blocking-condition.types.ts +21 -21
- package/src/backoffice/types/static/certification.types.ts +37 -37
- package/src/backoffice/types/static/contraindication.types.ts +19 -19
- package/src/backoffice/types/static/index.ts +6 -6
- package/src/backoffice/types/static/pricing.types.ts +16 -16
- package/src/backoffice/types/static/procedure-family.types.ts +14 -14
- package/src/backoffice/types/static/treatment-benefit.types.ts +22 -22
- package/src/backoffice/types/subcategory.types.ts +34 -34
- package/src/backoffice/types/technology.types.ts +168 -168
- package/src/backoffice/validations/index.ts +1 -1
- package/src/backoffice/validations/schemas.ts +164 -164
- package/src/config/__mocks__/firebase.ts +99 -99
- package/src/config/firebase.ts +78 -78
- package/src/config/index.ts +9 -9
- package/src/errors/auth.error.ts +6 -6
- package/src/errors/auth.errors.ts +200 -200
- package/src/errors/clinic.errors.ts +32 -32
- package/src/errors/firebase.errors.ts +47 -47
- package/src/errors/user.errors.ts +99 -99
- package/src/index.backup.ts +407 -407
- package/src/index.ts +6 -6
- package/src/locales/en.ts +31 -31
- package/src/recommender/admin/index.ts +1 -1
- package/src/recommender/admin/services/recommender.service.admin.ts +5 -5
- package/src/recommender/front/index.ts +1 -1
- package/src/recommender/front/services/onboarding.service.ts +5 -5
- package/src/recommender/front/services/recommender.service.ts +3 -3
- package/src/recommender/index.ts +1 -1
- package/src/services/PATIENTAUTH.MD +197 -197
- package/src/services/README.md +106 -106
- package/src/services/__tests__/auth/auth.mock.test.ts +17 -17
- package/src/services/__tests__/auth/auth.setup.ts +293 -293
- package/src/services/__tests__/auth.service.test.ts +346 -346
- package/src/services/__tests__/base.service.test.ts +77 -77
- package/src/services/__tests__/user.service.test.ts +528 -528
- package/src/services/analytics/ARCHITECTURE.md +199 -199
- package/src/services/analytics/CLOUD_FUNCTIONS.md +225 -225
- package/src/services/analytics/GROUPED_ANALYTICS.md +501 -501
- package/src/services/analytics/QUICK_START.md +393 -393
- package/src/services/analytics/README.md +304 -304
- package/src/services/analytics/SUMMARY.md +141 -141
- package/src/services/analytics/TRENDS.md +380 -380
- package/src/services/analytics/USAGE_GUIDE.md +518 -518
- package/src/services/analytics/analytics-cloud.service.ts +222 -222
- package/src/services/analytics/analytics.service.ts +2142 -2142
- package/src/services/analytics/index.ts +4 -4
- package/src/services/analytics/review-analytics.service.ts +941 -941
- package/src/services/analytics/utils/appointment-filtering.utils.ts +138 -138
- package/src/services/analytics/utils/cost-calculation.utils.ts +182 -182
- package/src/services/analytics/utils/grouping.utils.ts +434 -434
- package/src/services/analytics/utils/stored-analytics.utils.ts +347 -347
- package/src/services/analytics/utils/time-calculation.utils.ts +186 -186
- package/src/services/analytics/utils/trend-calculation.utils.ts +200 -200
- package/src/services/appointment/README.md +17 -17
- package/src/services/appointment/appointment.service.ts +2558 -2558
- package/src/services/appointment/index.ts +1 -1
- package/src/services/appointment/utils/appointment.utils.ts +552 -552
- package/src/services/appointment/utils/extended-procedure.utils.ts +314 -314
- package/src/services/appointment/utils/form-initialization.utils.ts +225 -225
- package/src/services/appointment/utils/recommended-procedure.utils.ts +195 -195
- package/src/services/appointment/utils/zone-management.utils.ts +353 -353
- package/src/services/appointment/utils/zone-photo.utils.ts +152 -152
- package/src/services/auth/auth.service.ts +989 -989
- package/src/services/auth/auth.v2.service.ts +961 -961
- package/src/services/auth/index.ts +7 -7
- package/src/services/auth/utils/error.utils.ts +90 -90
- package/src/services/auth/utils/firebase.utils.ts +49 -49
- package/src/services/auth/utils/index.ts +21 -21
- package/src/services/auth/utils/practitioner.utils.ts +125 -125
- package/src/services/base.service.ts +41 -41
- package/src/services/calendar/calendar.service.ts +1077 -1077
- package/src/services/calendar/calendar.v2.service.ts +1683 -1683
- package/src/services/calendar/calendar.v3.service.ts +313 -313
- package/src/services/calendar/externalCalendar.service.ts +178 -178
- package/src/services/calendar/index.ts +5 -5
- package/src/services/calendar/synced-calendars.service.ts +743 -743
- package/src/services/calendar/utils/appointment.utils.ts +265 -265
- package/src/services/calendar/utils/calendar-event.utils.ts +646 -646
- package/src/services/calendar/utils/clinic.utils.ts +237 -237
- package/src/services/calendar/utils/docs.utils.ts +157 -157
- package/src/services/calendar/utils/google-calendar.utils.ts +697 -697
- package/src/services/calendar/utils/index.ts +8 -8
- package/src/services/calendar/utils/patient.utils.ts +198 -198
- package/src/services/calendar/utils/practitioner.utils.ts +221 -221
- package/src/services/calendar/utils/synced-calendar.utils.ts +472 -472
- package/src/services/clinic/README.md +204 -204
- package/src/services/clinic/__tests__/clinic-admin.service.test.ts +287 -287
- package/src/services/clinic/__tests__/clinic-group.service.test.ts +352 -352
- package/src/services/clinic/__tests__/clinic.service.test.ts +354 -354
- package/src/services/clinic/billing-transactions.service.ts +217 -217
- package/src/services/clinic/clinic-admin.service.ts +202 -202
- package/src/services/clinic/clinic-group.service.ts +310 -310
- package/src/services/clinic/clinic.service.ts +708 -708
- package/src/services/clinic/index.ts +5 -5
- package/src/services/clinic/practitioner-invite.service.ts +519 -519
- package/src/services/clinic/utils/admin.utils.ts +551 -551
- package/src/services/clinic/utils/clinic-group.utils.ts +646 -646
- package/src/services/clinic/utils/clinic.utils.ts +949 -949
- package/src/services/clinic/utils/filter.utils.d.ts +23 -23
- package/src/services/clinic/utils/filter.utils.ts +446 -446
- package/src/services/clinic/utils/index.ts +11 -11
- package/src/services/clinic/utils/photos.utils.ts +188 -188
- package/src/services/clinic/utils/search.utils.ts +84 -84
- package/src/services/clinic/utils/tag.utils.ts +124 -124
- package/src/services/documentation-templates/documentation-template.service.ts +537 -537
- package/src/services/documentation-templates/filled-document.service.ts +587 -587
- package/src/services/documentation-templates/index.ts +2 -2
- package/src/services/index.ts +14 -14
- package/src/services/media/index.ts +1 -1
- package/src/services/media/media.service.ts +418 -418
- package/src/services/notifications/__tests__/notification.service.test.ts +242 -242
- package/src/services/notifications/index.ts +1 -1
- package/src/services/notifications/notification.service.ts +215 -215
- package/src/services/patient/README.md +48 -48
- package/src/services/patient/To-Do.md +43 -43
- package/src/services/patient/__tests__/patient.service.test.ts +294 -294
- package/src/services/patient/index.ts +2 -2
- package/src/services/patient/patient.service.ts +883 -883
- package/src/services/patient/patientRequirements.service.ts +285 -285
- package/src/services/patient/utils/aesthetic-analysis.utils.ts +176 -176
- package/src/services/patient/utils/clinic.utils.ts +80 -80
- package/src/services/patient/utils/docs.utils.ts +142 -142
- package/src/services/patient/utils/index.ts +9 -9
- package/src/services/patient/utils/location.utils.ts +126 -126
- package/src/services/patient/utils/medical-stuff.utils.ts +143 -143
- package/src/services/patient/utils/medical.utils.ts +458 -458
- package/src/services/patient/utils/practitioner.utils.ts +260 -260
- package/src/services/patient/utils/profile.utils.ts +510 -510
- package/src/services/patient/utils/sensitive.utils.ts +260 -260
- package/src/services/patient/utils/token.utils.ts +211 -211
- package/src/services/practitioner/README.md +145 -145
- package/src/services/practitioner/index.ts +1 -1
- package/src/services/practitioner/practitioner.service.ts +1742 -1742
- package/src/services/procedure/README.md +163 -163
- package/src/services/procedure/index.ts +1 -1
- package/src/services/procedure/procedure.service.ts +2200 -2191
- package/src/services/reviews/index.ts +1 -1
- package/src/services/reviews/reviews.service.ts +734 -734
- package/src/services/user/index.ts +1 -1
- package/src/services/user/user.service.ts +489 -489
- package/src/services/user/user.v2.service.ts +466 -466
- package/src/types/analytics/analytics.types.ts +597 -597
- package/src/types/analytics/grouped-analytics.types.ts +173 -173
- package/src/types/analytics/index.ts +4 -4
- package/src/types/analytics/stored-analytics.types.ts +137 -137
- package/src/types/appointment/index.ts +480 -480
- package/src/types/calendar/index.ts +258 -258
- package/src/types/calendar/synced-calendar.types.ts +66 -66
- package/src/types/clinic/index.ts +498 -489
- package/src/types/clinic/practitioner-invite.types.ts +91 -91
- package/src/types/clinic/preferences.types.ts +159 -159
- package/src/types/clinic/to-do +3 -3
- package/src/types/documentation-templates/index.ts +308 -308
- package/src/types/index.ts +47 -47
- package/src/types/notifications/README.md +77 -77
- package/src/types/notifications/index.ts +286 -286
- package/src/types/patient/aesthetic-analysis.types.ts +66 -66
- package/src/types/patient/allergies.ts +58 -58
- package/src/types/patient/index.ts +275 -275
- package/src/types/patient/medical-info.types.ts +152 -152
- package/src/types/patient/patient-requirements.ts +92 -92
- package/src/types/patient/token.types.ts +61 -61
- package/src/types/practitioner/index.ts +206 -206
- package/src/types/procedure/index.ts +181 -181
- package/src/types/profile/index.ts +39 -39
- package/src/types/reviews/index.ts +132 -132
- package/src/types/tz-lookup.d.ts +4 -4
- package/src/types/user/index.ts +38 -38
- package/src/utils/TIMESTAMPS.md +176 -176
- package/src/utils/TimestampUtils.ts +241 -241
- package/src/utils/index.ts +1 -1
- package/src/validations/appointment.schema.ts +574 -574
- package/src/validations/calendar.schema.ts +225 -225
- package/src/validations/clinic.schema.ts +494 -493
- package/src/validations/common.schema.ts +25 -25
- package/src/validations/documentation-templates/index.ts +1 -1
- package/src/validations/documentation-templates/template.schema.ts +220 -220
- package/src/validations/documentation-templates.schema.ts +10 -10
- package/src/validations/index.ts +20 -20
- package/src/validations/media.schema.ts +10 -10
- package/src/validations/notification.schema.ts +90 -90
- package/src/validations/patient/aesthetic-analysis.schema.ts +55 -55
- package/src/validations/patient/medical-info.schema.ts +125 -125
- package/src/validations/patient/patient-requirements.schema.ts +84 -84
- package/src/validations/patient/token.schema.ts +29 -29
- package/src/validations/patient.schema.ts +217 -217
- package/src/validations/practitioner.schema.ts +222 -222
- package/src/validations/procedure-product.schema.ts +41 -41
- package/src/validations/procedure.schema.ts +124 -124
- package/src/validations/profile-info.schema.ts +41 -41
- package/src/validations/reviews.schema.ts +195 -195
- package/src/validations/schemas.ts +104 -104
- package/src/validations/shared.schema.ts +78 -78
|
@@ -1,200 +1,200 @@
|
|
|
1
|
-
import { Appointment } from '../../../types/appointment';
|
|
2
|
-
import { Timestamp } from 'firebase/firestore';
|
|
3
|
-
|
|
4
|
-
/**
|
|
5
|
-
* Trend period type
|
|
6
|
-
*/
|
|
7
|
-
export type TrendPeriod = 'week' | 'month' | 'quarter' | 'year';
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* Period information for trend grouping
|
|
11
|
-
*/
|
|
12
|
-
export interface PeriodInfo {
|
|
13
|
-
period: string; // e.g., "2024-W01", "2024-01", "2024-Q1", "2024"
|
|
14
|
-
startDate: Date;
|
|
15
|
-
endDate: Date;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* Calculates the start and end dates for a given period
|
|
20
|
-
*/
|
|
21
|
-
export function getPeriodDates(date: Date, period: TrendPeriod): PeriodInfo {
|
|
22
|
-
const year = date.getFullYear();
|
|
23
|
-
const month = date.getMonth();
|
|
24
|
-
const day = date.getDate();
|
|
25
|
-
|
|
26
|
-
let startDate: Date;
|
|
27
|
-
let endDate: Date;
|
|
28
|
-
let periodString: string;
|
|
29
|
-
|
|
30
|
-
switch (period) {
|
|
31
|
-
case 'week': {
|
|
32
|
-
// Get Monday of the week
|
|
33
|
-
const dayOfWeek = date.getDay();
|
|
34
|
-
const diff = date.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1); // Adjust when day is Sunday
|
|
35
|
-
startDate = new Date(year, month, diff);
|
|
36
|
-
startDate.setHours(0, 0, 0, 0);
|
|
37
|
-
|
|
38
|
-
endDate = new Date(startDate);
|
|
39
|
-
endDate.setDate(endDate.getDate() + 6);
|
|
40
|
-
endDate.setHours(23, 59, 59, 999);
|
|
41
|
-
|
|
42
|
-
// ISO week format: YYYY-Www
|
|
43
|
-
const weekNumber = getWeekNumber(date);
|
|
44
|
-
periodString = `${year}-W${weekNumber.toString().padStart(2, '0')}`;
|
|
45
|
-
break;
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
case 'month': {
|
|
49
|
-
startDate = new Date(year, month, 1);
|
|
50
|
-
startDate.setHours(0, 0, 0, 0);
|
|
51
|
-
|
|
52
|
-
endDate = new Date(year, month + 1, 0);
|
|
53
|
-
endDate.setHours(23, 59, 59, 999);
|
|
54
|
-
|
|
55
|
-
periodString = `${year}-${(month + 1).toString().padStart(2, '0')}`;
|
|
56
|
-
break;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
case 'quarter': {
|
|
60
|
-
const quarter = Math.floor(month / 3);
|
|
61
|
-
const quarterStartMonth = quarter * 3;
|
|
62
|
-
|
|
63
|
-
startDate = new Date(year, quarterStartMonth, 1);
|
|
64
|
-
startDate.setHours(0, 0, 0, 0);
|
|
65
|
-
|
|
66
|
-
endDate = new Date(year, quarterStartMonth + 3, 0);
|
|
67
|
-
endDate.setHours(23, 59, 59, 999);
|
|
68
|
-
|
|
69
|
-
periodString = `${year}-Q${quarter + 1}`;
|
|
70
|
-
break;
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
case 'year': {
|
|
74
|
-
startDate = new Date(year, 0, 1);
|
|
75
|
-
startDate.setHours(0, 0, 0, 0);
|
|
76
|
-
|
|
77
|
-
endDate = new Date(year, 11, 31);
|
|
78
|
-
endDate.setHours(23, 59, 59, 999);
|
|
79
|
-
|
|
80
|
-
periodString = `${year}`;
|
|
81
|
-
break;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
return {
|
|
86
|
-
period: periodString,
|
|
87
|
-
startDate,
|
|
88
|
-
endDate,
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
/**
|
|
93
|
-
* Gets ISO week number for a date
|
|
94
|
-
*/
|
|
95
|
-
function getWeekNumber(date: Date): number {
|
|
96
|
-
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
|
97
|
-
const dayNum = d.getUTCDay() || 7;
|
|
98
|
-
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
|
99
|
-
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
100
|
-
return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
/**
|
|
104
|
-
* Groups appointments by trend period
|
|
105
|
-
*/
|
|
106
|
-
export function groupAppointmentsByPeriod(
|
|
107
|
-
appointments: Appointment[],
|
|
108
|
-
period: TrendPeriod,
|
|
109
|
-
): Map<string, Appointment[]> {
|
|
110
|
-
const periodMap = new Map<string, Appointment[]>();
|
|
111
|
-
|
|
112
|
-
appointments.forEach(appointment => {
|
|
113
|
-
const appointmentDate = appointment.appointmentStartTime.toDate();
|
|
114
|
-
const periodInfo = getPeriodDates(appointmentDate, period);
|
|
115
|
-
const periodKey = periodInfo.period;
|
|
116
|
-
|
|
117
|
-
if (!periodMap.has(periodKey)) {
|
|
118
|
-
periodMap.set(periodKey, []);
|
|
119
|
-
}
|
|
120
|
-
periodMap.get(periodKey)!.push(appointment);
|
|
121
|
-
});
|
|
122
|
-
|
|
123
|
-
return periodMap;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Generates all periods between start and end date for a given period type
|
|
128
|
-
*/
|
|
129
|
-
export function generatePeriods(
|
|
130
|
-
startDate: Date,
|
|
131
|
-
endDate: Date,
|
|
132
|
-
period: TrendPeriod,
|
|
133
|
-
): PeriodInfo[] {
|
|
134
|
-
const periods: PeriodInfo[] = [];
|
|
135
|
-
const current = new Date(startDate);
|
|
136
|
-
|
|
137
|
-
while (current <= endDate) {
|
|
138
|
-
const periodInfo = getPeriodDates(current, period);
|
|
139
|
-
|
|
140
|
-
// Only add if period overlaps with our date range
|
|
141
|
-
if (periodInfo.endDate >= startDate && periodInfo.startDate <= endDate) {
|
|
142
|
-
periods.push(periodInfo);
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
// Move to next period
|
|
146
|
-
switch (period) {
|
|
147
|
-
case 'week':
|
|
148
|
-
current.setDate(current.getDate() + 7);
|
|
149
|
-
break;
|
|
150
|
-
case 'month':
|
|
151
|
-
current.setMonth(current.getMonth() + 1);
|
|
152
|
-
break;
|
|
153
|
-
case 'quarter':
|
|
154
|
-
current.setMonth(current.getMonth() + 3);
|
|
155
|
-
break;
|
|
156
|
-
case 'year':
|
|
157
|
-
current.setFullYear(current.getFullYear() + 1);
|
|
158
|
-
break;
|
|
159
|
-
}
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
return periods.sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
/**
|
|
166
|
-
* Calculates percentage change between two values
|
|
167
|
-
* @param current - Current value
|
|
168
|
-
* @param previous - Previous value
|
|
169
|
-
* @returns Percentage change (positive = increase, negative = decrease)
|
|
170
|
-
*/
|
|
171
|
-
export function calculatePercentageChange(current: number, previous: number): number {
|
|
172
|
-
if (previous === 0) {
|
|
173
|
-
return current > 0 ? 100 : 0;
|
|
174
|
-
}
|
|
175
|
-
return ((current - previous) / previous) * 100;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
/**
|
|
179
|
-
* Gets trend direction and percentage change
|
|
180
|
-
*/
|
|
181
|
-
export interface TrendChange {
|
|
182
|
-
value: number;
|
|
183
|
-
previousValue: number;
|
|
184
|
-
percentageChange: number;
|
|
185
|
-
direction: 'up' | 'down' | 'stable';
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export function getTrendChange(current: number, previous: number): TrendChange {
|
|
189
|
-
const percentageChange = calculatePercentageChange(current, previous);
|
|
190
|
-
const direction: 'up' | 'down' | 'stable' =
|
|
191
|
-
percentageChange > 0.01 ? 'up' : percentageChange < -0.01 ? 'down' : 'stable';
|
|
192
|
-
|
|
193
|
-
return {
|
|
194
|
-
value: current,
|
|
195
|
-
previousValue: previous,
|
|
196
|
-
percentageChange: Math.abs(percentageChange),
|
|
197
|
-
direction,
|
|
198
|
-
};
|
|
199
|
-
}
|
|
200
|
-
|
|
1
|
+
import { Appointment } from '../../../types/appointment';
|
|
2
|
+
import { Timestamp } from 'firebase/firestore';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Trend period type
|
|
6
|
+
*/
|
|
7
|
+
export type TrendPeriod = 'week' | 'month' | 'quarter' | 'year';
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Period information for trend grouping
|
|
11
|
+
*/
|
|
12
|
+
export interface PeriodInfo {
|
|
13
|
+
period: string; // e.g., "2024-W01", "2024-01", "2024-Q1", "2024"
|
|
14
|
+
startDate: Date;
|
|
15
|
+
endDate: Date;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* Calculates the start and end dates for a given period
|
|
20
|
+
*/
|
|
21
|
+
export function getPeriodDates(date: Date, period: TrendPeriod): PeriodInfo {
|
|
22
|
+
const year = date.getFullYear();
|
|
23
|
+
const month = date.getMonth();
|
|
24
|
+
const day = date.getDate();
|
|
25
|
+
|
|
26
|
+
let startDate: Date;
|
|
27
|
+
let endDate: Date;
|
|
28
|
+
let periodString: string;
|
|
29
|
+
|
|
30
|
+
switch (period) {
|
|
31
|
+
case 'week': {
|
|
32
|
+
// Get Monday of the week
|
|
33
|
+
const dayOfWeek = date.getDay();
|
|
34
|
+
const diff = date.getDate() - dayOfWeek + (dayOfWeek === 0 ? -6 : 1); // Adjust when day is Sunday
|
|
35
|
+
startDate = new Date(year, month, diff);
|
|
36
|
+
startDate.setHours(0, 0, 0, 0);
|
|
37
|
+
|
|
38
|
+
endDate = new Date(startDate);
|
|
39
|
+
endDate.setDate(endDate.getDate() + 6);
|
|
40
|
+
endDate.setHours(23, 59, 59, 999);
|
|
41
|
+
|
|
42
|
+
// ISO week format: YYYY-Www
|
|
43
|
+
const weekNumber = getWeekNumber(date);
|
|
44
|
+
periodString = `${year}-W${weekNumber.toString().padStart(2, '0')}`;
|
|
45
|
+
break;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
case 'month': {
|
|
49
|
+
startDate = new Date(year, month, 1);
|
|
50
|
+
startDate.setHours(0, 0, 0, 0);
|
|
51
|
+
|
|
52
|
+
endDate = new Date(year, month + 1, 0);
|
|
53
|
+
endDate.setHours(23, 59, 59, 999);
|
|
54
|
+
|
|
55
|
+
periodString = `${year}-${(month + 1).toString().padStart(2, '0')}`;
|
|
56
|
+
break;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
case 'quarter': {
|
|
60
|
+
const quarter = Math.floor(month / 3);
|
|
61
|
+
const quarterStartMonth = quarter * 3;
|
|
62
|
+
|
|
63
|
+
startDate = new Date(year, quarterStartMonth, 1);
|
|
64
|
+
startDate.setHours(0, 0, 0, 0);
|
|
65
|
+
|
|
66
|
+
endDate = new Date(year, quarterStartMonth + 3, 0);
|
|
67
|
+
endDate.setHours(23, 59, 59, 999);
|
|
68
|
+
|
|
69
|
+
periodString = `${year}-Q${quarter + 1}`;
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
case 'year': {
|
|
74
|
+
startDate = new Date(year, 0, 1);
|
|
75
|
+
startDate.setHours(0, 0, 0, 0);
|
|
76
|
+
|
|
77
|
+
endDate = new Date(year, 11, 31);
|
|
78
|
+
endDate.setHours(23, 59, 59, 999);
|
|
79
|
+
|
|
80
|
+
periodString = `${year}`;
|
|
81
|
+
break;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
period: periodString,
|
|
87
|
+
startDate,
|
|
88
|
+
endDate,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Gets ISO week number for a date
|
|
94
|
+
*/
|
|
95
|
+
function getWeekNumber(date: Date): number {
|
|
96
|
+
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()));
|
|
97
|
+
const dayNum = d.getUTCDay() || 7;
|
|
98
|
+
d.setUTCDate(d.getUTCDate() + 4 - dayNum);
|
|
99
|
+
const yearStart = new Date(Date.UTC(d.getUTCFullYear(), 0, 1));
|
|
100
|
+
return Math.ceil(((d.getTime() - yearStart.getTime()) / 86400000 + 1) / 7);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Groups appointments by trend period
|
|
105
|
+
*/
|
|
106
|
+
export function groupAppointmentsByPeriod(
|
|
107
|
+
appointments: Appointment[],
|
|
108
|
+
period: TrendPeriod,
|
|
109
|
+
): Map<string, Appointment[]> {
|
|
110
|
+
const periodMap = new Map<string, Appointment[]>();
|
|
111
|
+
|
|
112
|
+
appointments.forEach(appointment => {
|
|
113
|
+
const appointmentDate = appointment.appointmentStartTime.toDate();
|
|
114
|
+
const periodInfo = getPeriodDates(appointmentDate, period);
|
|
115
|
+
const periodKey = periodInfo.period;
|
|
116
|
+
|
|
117
|
+
if (!periodMap.has(periodKey)) {
|
|
118
|
+
periodMap.set(periodKey, []);
|
|
119
|
+
}
|
|
120
|
+
periodMap.get(periodKey)!.push(appointment);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
return periodMap;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Generates all periods between start and end date for a given period type
|
|
128
|
+
*/
|
|
129
|
+
export function generatePeriods(
|
|
130
|
+
startDate: Date,
|
|
131
|
+
endDate: Date,
|
|
132
|
+
period: TrendPeriod,
|
|
133
|
+
): PeriodInfo[] {
|
|
134
|
+
const periods: PeriodInfo[] = [];
|
|
135
|
+
const current = new Date(startDate);
|
|
136
|
+
|
|
137
|
+
while (current <= endDate) {
|
|
138
|
+
const periodInfo = getPeriodDates(current, period);
|
|
139
|
+
|
|
140
|
+
// Only add if period overlaps with our date range
|
|
141
|
+
if (periodInfo.endDate >= startDate && periodInfo.startDate <= endDate) {
|
|
142
|
+
periods.push(periodInfo);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// Move to next period
|
|
146
|
+
switch (period) {
|
|
147
|
+
case 'week':
|
|
148
|
+
current.setDate(current.getDate() + 7);
|
|
149
|
+
break;
|
|
150
|
+
case 'month':
|
|
151
|
+
current.setMonth(current.getMonth() + 1);
|
|
152
|
+
break;
|
|
153
|
+
case 'quarter':
|
|
154
|
+
current.setMonth(current.getMonth() + 3);
|
|
155
|
+
break;
|
|
156
|
+
case 'year':
|
|
157
|
+
current.setFullYear(current.getFullYear() + 1);
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
return periods.sort((a, b) => a.startDate.getTime() - b.startDate.getTime());
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Calculates percentage change between two values
|
|
167
|
+
* @param current - Current value
|
|
168
|
+
* @param previous - Previous value
|
|
169
|
+
* @returns Percentage change (positive = increase, negative = decrease)
|
|
170
|
+
*/
|
|
171
|
+
export function calculatePercentageChange(current: number, previous: number): number {
|
|
172
|
+
if (previous === 0) {
|
|
173
|
+
return current > 0 ? 100 : 0;
|
|
174
|
+
}
|
|
175
|
+
return ((current - previous) / previous) * 100;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/**
|
|
179
|
+
* Gets trend direction and percentage change
|
|
180
|
+
*/
|
|
181
|
+
export interface TrendChange {
|
|
182
|
+
value: number;
|
|
183
|
+
previousValue: number;
|
|
184
|
+
percentageChange: number;
|
|
185
|
+
direction: 'up' | 'down' | 'stable';
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export function getTrendChange(current: number, previous: number): TrendChange {
|
|
189
|
+
const percentageChange = calculatePercentageChange(current, previous);
|
|
190
|
+
const direction: 'up' | 'down' | 'stable' =
|
|
191
|
+
percentageChange > 0.01 ? 'up' : percentageChange < -0.01 ? 'down' : 'stable';
|
|
192
|
+
|
|
193
|
+
return {
|
|
194
|
+
value: current,
|
|
195
|
+
previousValue: previous,
|
|
196
|
+
percentageChange: Math.abs(percentageChange),
|
|
197
|
+
direction,
|
|
198
|
+
};
|
|
199
|
+
}
|
|
200
|
+
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
# Appointment Service
|
|
2
|
-
|
|
3
|
-
This service is responsible for managing appointments.
|
|
4
|
-
|
|
5
|
-
## Methods
|
|
6
|
-
|
|
7
|
-
### `countCompletedAppointments(patientId: string, clinicBranchId?: string, excludeClinic: boolean = true): Promise<number>`
|
|
8
|
-
|
|
9
|
-
Counts the number of completed appointments for a given patient.
|
|
10
|
-
|
|
11
|
-
- `patientId`: The ID of the patient.
|
|
12
|
-
- `clinicBranchId` (optional): The ID of a clinic branch to filter by.
|
|
13
|
-
- `excludeClinic` (optional, default: `true`):
|
|
14
|
-
- If `true`, it counts appointments _outside_ the specified `clinicBranchId`.
|
|
15
|
-
- If `false`, it counts appointments _only within_ the specified `clinicBranchId`.
|
|
16
|
-
|
|
17
|
-
If `clinicBranchId` is not provided, it counts all completed appointments for the patient across all clinics.
|
|
1
|
+
# Appointment Service
|
|
2
|
+
|
|
3
|
+
This service is responsible for managing appointments.
|
|
4
|
+
|
|
5
|
+
## Methods
|
|
6
|
+
|
|
7
|
+
### `countCompletedAppointments(patientId: string, clinicBranchId?: string, excludeClinic: boolean = true): Promise<number>`
|
|
8
|
+
|
|
9
|
+
Counts the number of completed appointments for a given patient.
|
|
10
|
+
|
|
11
|
+
- `patientId`: The ID of the patient.
|
|
12
|
+
- `clinicBranchId` (optional): The ID of a clinic branch to filter by.
|
|
13
|
+
- `excludeClinic` (optional, default: `true`):
|
|
14
|
+
- If `true`, it counts appointments _outside_ the specified `clinicBranchId`.
|
|
15
|
+
- If `false`, it counts appointments _only within_ the specified `clinicBranchId`.
|
|
16
|
+
|
|
17
|
+
If `clinicBranchId` is not provided, it counts all completed appointments for the patient across all clinics.
|