@blackcode_sa/metaestetics-api 1.12.36 → 1.12.39
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 +13 -1
- package/dist/admin/index.d.ts +13 -1
- package/dist/admin/index.js +45 -18
- package/dist/admin/index.mjs +45 -18
- package/dist/index.d.mts +106 -6
- package/dist/index.d.ts +106 -6
- package/dist/index.js +1989 -1615
- package/dist/index.mjs +1185 -811
- package/package.json +1 -1
- package/src/admin/aggregation/appointment/appointment.aggregation.service.ts +56 -25
- package/src/admin/booking/booking.admin.ts +1 -0
- package/src/services/appointment/appointment.service.ts +279 -45
- package/src/services/appointment/utils/recommended-procedure.utils.ts +193 -0
- package/src/services/appointment/utils/zone-management.utils.ts +27 -23
- package/src/services/appointment/utils/zone-photo.utils.ts +152 -0
- package/src/services/procedure/procedure.service.ts +42 -2
- package/src/types/appointment/index.ts +16 -2
- package/src/validations/appointment.schema.ts +24 -4
package/package.json
CHANGED
|
@@ -1549,15 +1549,25 @@ export class AppointmentAggregationService {
|
|
|
1549
1549
|
return true;
|
|
1550
1550
|
}
|
|
1551
1551
|
|
|
1552
|
-
// Compare before and after photos
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
beforeZonePhotos.after !== afterZonePhotos.after ||
|
|
1556
|
-
beforeZonePhotos.beforeNote !== afterZonePhotos.beforeNote ||
|
|
1557
|
-
beforeZonePhotos.afterNote !== afterZonePhotos.afterNote
|
|
1558
|
-
) {
|
|
1552
|
+
// Compare before and after photos arrays
|
|
1553
|
+
// If array lengths differ or any entry differs, consider it changed
|
|
1554
|
+
if (beforeZonePhotos.length !== afterZonePhotos.length) {
|
|
1559
1555
|
return true;
|
|
1560
1556
|
}
|
|
1557
|
+
|
|
1558
|
+
// Compare each entry in the arrays
|
|
1559
|
+
for (let i = 0; i < beforeZonePhotos.length; i++) {
|
|
1560
|
+
const beforeEntry = beforeZonePhotos[i];
|
|
1561
|
+
const afterEntry = afterZonePhotos[i];
|
|
1562
|
+
if (
|
|
1563
|
+
beforeEntry.before !== afterEntry.before ||
|
|
1564
|
+
beforeEntry.after !== afterEntry.after ||
|
|
1565
|
+
beforeEntry.beforeNote !== afterEntry.beforeNote ||
|
|
1566
|
+
beforeEntry.afterNote !== afterEntry.afterNote
|
|
1567
|
+
) {
|
|
1568
|
+
return true;
|
|
1569
|
+
}
|
|
1570
|
+
}
|
|
1561
1571
|
}
|
|
1562
1572
|
|
|
1563
1573
|
return false;
|
|
@@ -1580,27 +1590,48 @@ export class AppointmentAggregationService {
|
|
|
1580
1590
|
const newPhotoTypes: { zoneId: string; photoType: 'before' | 'after' }[] = [];
|
|
1581
1591
|
|
|
1582
1592
|
for (const zoneId of Object.keys(afterPhotos)) {
|
|
1583
|
-
const beforeZonePhotos = beforePhotos[zoneId];
|
|
1584
|
-
const afterZonePhotos = afterPhotos[zoneId];
|
|
1593
|
+
const beforeZonePhotos = beforePhotos[zoneId] || [];
|
|
1594
|
+
const afterZonePhotos = afterPhotos[zoneId] || [];
|
|
1585
1595
|
|
|
1586
|
-
if (
|
|
1596
|
+
if (beforeZonePhotos.length === 0 && afterZonePhotos.length > 0) {
|
|
1587
1597
|
// New zone with photos
|
|
1588
1598
|
updatedZones.push(zoneId);
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1599
|
+
afterZonePhotos.forEach(entry => {
|
|
1600
|
+
if (entry.before) {
|
|
1601
|
+
newPhotoTypes.push({ zoneId, photoType: 'before' });
|
|
1602
|
+
}
|
|
1603
|
+
if (entry.after) {
|
|
1604
|
+
newPhotoTypes.push({ zoneId, photoType: 'after' });
|
|
1605
|
+
}
|
|
1606
|
+
});
|
|
1607
|
+
} else if (afterZonePhotos.length > beforeZonePhotos.length) {
|
|
1608
|
+
// New photos added to existing zone
|
|
1609
|
+
updatedZones.push(zoneId);
|
|
1610
|
+
const newEntries = afterZonePhotos.slice(beforeZonePhotos.length);
|
|
1611
|
+
newEntries.forEach(entry => {
|
|
1612
|
+
if (entry.before) {
|
|
1613
|
+
newPhotoTypes.push({ zoneId, photoType: 'before' });
|
|
1614
|
+
}
|
|
1615
|
+
if (entry.after) {
|
|
1616
|
+
newPhotoTypes.push({ zoneId, photoType: 'after' });
|
|
1617
|
+
}
|
|
1618
|
+
});
|
|
1595
1619
|
} else {
|
|
1596
|
-
// Check for
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1620
|
+
// Check for updated photos in existing entries
|
|
1621
|
+
for (let i = 0; i < afterZonePhotos.length; i++) {
|
|
1622
|
+
const beforeEntry = beforeZonePhotos[i];
|
|
1623
|
+
const afterEntry = afterZonePhotos[i];
|
|
1624
|
+
|
|
1625
|
+
if (beforeEntry && afterEntry) {
|
|
1626
|
+
if (beforeEntry.before !== afterEntry.before && afterEntry.before) {
|
|
1627
|
+
updatedZones.push(zoneId);
|
|
1628
|
+
newPhotoTypes.push({ zoneId, photoType: 'before' });
|
|
1629
|
+
}
|
|
1630
|
+
if (beforeEntry.after !== afterEntry.after && afterEntry.after) {
|
|
1631
|
+
updatedZones.push(zoneId);
|
|
1632
|
+
newPhotoTypes.push({ zoneId, photoType: 'after' });
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1604
1635
|
}
|
|
1605
1636
|
}
|
|
1606
1637
|
}
|
|
@@ -1629,7 +1660,7 @@ export class AppointmentAggregationService {
|
|
|
1629
1660
|
if (selectedZones.length > 0) {
|
|
1630
1661
|
const completedZones = selectedZones.filter(zoneId => {
|
|
1631
1662
|
const zonePhotos = afterPhotos[zoneId];
|
|
1632
|
-
return zonePhotos &&
|
|
1663
|
+
return zonePhotos && zonePhotos.length > 0 && zonePhotos.some(entry => entry.before || entry.after);
|
|
1633
1664
|
});
|
|
1634
1665
|
|
|
1635
1666
|
const completionPercentage = (completedZones.length / selectedZones.length) * 100;
|
|
@@ -33,6 +33,7 @@ import {
|
|
|
33
33
|
ZoneItemData,
|
|
34
34
|
ExtendedProcedureInfo,
|
|
35
35
|
AppointmentProductMetadata,
|
|
36
|
+
RecommendedProcedure,
|
|
36
37
|
APPOINTMENTS_COLLECTION,
|
|
37
38
|
} from '../../types/appointment';
|
|
38
39
|
import {
|
|
@@ -48,7 +49,7 @@ import { PatientService } from '../patient/patient.service';
|
|
|
48
49
|
import { PractitionerService } from '../practitioner/practitioner.service';
|
|
49
50
|
import { ClinicService } from '../clinic/clinic.service';
|
|
50
51
|
import { FilledDocumentService } from '../documentation-templates/filled-document.service';
|
|
51
|
-
import { MediaService, MediaAccessLevel, MediaMetadata } from '../media/media.service';
|
|
52
|
+
import { MediaService, MediaAccessLevel, MediaMetadata, MediaResource } from '../media/media.service';
|
|
52
53
|
|
|
53
54
|
// Import utility functions
|
|
54
55
|
import {
|
|
@@ -70,6 +71,18 @@ import {
|
|
|
70
71
|
getExtendedProceduresUtil,
|
|
71
72
|
getAppointmentProductsUtil,
|
|
72
73
|
} from './utils/extended-procedure.utils';
|
|
74
|
+
import {
|
|
75
|
+
addRecommendedProcedureUtil,
|
|
76
|
+
removeRecommendedProcedureUtil,
|
|
77
|
+
updateRecommendedProcedureUtil,
|
|
78
|
+
getRecommendedProceduresUtil,
|
|
79
|
+
} from './utils/recommended-procedure.utils';
|
|
80
|
+
import {
|
|
81
|
+
updateZonePhotoEntryUtil,
|
|
82
|
+
addAfterPhotoToEntryUtil,
|
|
83
|
+
updateZonePhotoNotesUtil,
|
|
84
|
+
getZonePhotoEntryUtil,
|
|
85
|
+
} from './utils/zone-photo.utils';
|
|
73
86
|
|
|
74
87
|
/**
|
|
75
88
|
* Interface for available booking slot
|
|
@@ -1274,35 +1287,34 @@ export class AppointmentService extends BaseService {
|
|
|
1274
1287
|
zonesData: null,
|
|
1275
1288
|
appointmentProducts: [],
|
|
1276
1289
|
extendedProcedures: [],
|
|
1290
|
+
recommendedProcedures: [],
|
|
1277
1291
|
zoneBilling: null,
|
|
1278
1292
|
finalbilling: null,
|
|
1279
1293
|
finalizationNotes: null,
|
|
1280
1294
|
};
|
|
1281
1295
|
|
|
1282
|
-
// Initialize zonePhotos if it doesn't exist
|
|
1283
|
-
const currentZonePhotos
|
|
1296
|
+
// Initialize zonePhotos if it doesn't exist (array model per zone)
|
|
1297
|
+
const currentZonePhotos: Record<string, BeforeAfterPerZone[]> =
|
|
1298
|
+
(currentMetadata.zonePhotos as Record<string, BeforeAfterPerZone[]>) || {};
|
|
1284
1299
|
|
|
1285
|
-
// Initialize the zone
|
|
1300
|
+
// Initialize the zone array if it doesn't exist
|
|
1286
1301
|
if (!currentZonePhotos[zoneId]) {
|
|
1287
|
-
currentZonePhotos[zoneId] =
|
|
1288
|
-
before: null,
|
|
1289
|
-
after: null,
|
|
1290
|
-
beforeNote: null,
|
|
1291
|
-
afterNote: null,
|
|
1292
|
-
};
|
|
1302
|
+
currentZonePhotos[zoneId] = [];
|
|
1293
1303
|
}
|
|
1294
1304
|
|
|
1295
|
-
//
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
}
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1305
|
+
// Create a new entry for this uploaded photo with per-photo notes
|
|
1306
|
+
const newEntry: BeforeAfterPerZone = {
|
|
1307
|
+
before: photoType === 'before' ? mediaMetadata.url : null,
|
|
1308
|
+
after: photoType === 'after' ? mediaMetadata.url : null,
|
|
1309
|
+
beforeNote: photoType === 'before' ? (notes || null) : null,
|
|
1310
|
+
afterNote: photoType === 'after' ? (notes || null) : null,
|
|
1311
|
+
};
|
|
1312
|
+
|
|
1313
|
+
// Append to the zone's photo list
|
|
1314
|
+
currentZonePhotos[zoneId] = [...currentZonePhotos[zoneId], newEntry];
|
|
1315
|
+
// Enforce max 10 photos per zone by keeping the most recent 10
|
|
1316
|
+
if (currentZonePhotos[zoneId].length > 10) {
|
|
1317
|
+
currentZonePhotos[zoneId] = currentZonePhotos[zoneId].slice(-10);
|
|
1306
1318
|
}
|
|
1307
1319
|
|
|
1308
1320
|
// Update the appointment with new metadata
|
|
@@ -1313,6 +1325,7 @@ export class AppointmentService extends BaseService {
|
|
|
1313
1325
|
zonesData: currentMetadata.zonesData || null,
|
|
1314
1326
|
appointmentProducts: currentMetadata.appointmentProducts || [],
|
|
1315
1327
|
extendedProcedures: currentMetadata.extendedProcedures || [],
|
|
1328
|
+
recommendedProcedures: currentMetadata.recommendedProcedures || [],
|
|
1316
1329
|
// Only include zoneBilling if it exists (avoid undefined values in Firestore)
|
|
1317
1330
|
...(currentMetadata.zoneBilling !== undefined && {
|
|
1318
1331
|
zoneBilling: currentMetadata.zoneBilling,
|
|
@@ -1349,7 +1362,7 @@ export class AppointmentService extends BaseService {
|
|
|
1349
1362
|
async getZonePhotos(
|
|
1350
1363
|
appointmentId: string,
|
|
1351
1364
|
zoneId?: string,
|
|
1352
|
-
): Promise<Record<string, BeforeAfterPerZone> | BeforeAfterPerZone | null> {
|
|
1365
|
+
): Promise<Record<string, BeforeAfterPerZone[]> | BeforeAfterPerZone[] | null> {
|
|
1353
1366
|
try {
|
|
1354
1367
|
console.log(`[APPOINTMENT_SERVICE] Getting zone photos for appointment ${appointmentId}`);
|
|
1355
1368
|
|
|
@@ -1358,7 +1371,10 @@ export class AppointmentService extends BaseService {
|
|
|
1358
1371
|
throw new Error(`Appointment with ID ${appointmentId} not found`);
|
|
1359
1372
|
}
|
|
1360
1373
|
|
|
1361
|
-
const zonePhotos = appointment.metadata?.zonePhotos
|
|
1374
|
+
const zonePhotos = appointment.metadata?.zonePhotos as
|
|
1375
|
+
| Record<string, BeforeAfterPerZone[]>
|
|
1376
|
+
| undefined
|
|
1377
|
+
| null;
|
|
1362
1378
|
if (!zonePhotos) {
|
|
1363
1379
|
return null;
|
|
1364
1380
|
}
|
|
@@ -1377,21 +1393,21 @@ export class AppointmentService extends BaseService {
|
|
|
1377
1393
|
}
|
|
1378
1394
|
|
|
1379
1395
|
/**
|
|
1380
|
-
* Deletes a zone photo and updates appointment metadata
|
|
1396
|
+
* Deletes a zone photo entry (by index) and updates appointment metadata
|
|
1381
1397
|
*
|
|
1382
1398
|
* @param appointmentId ID of the appointment
|
|
1383
1399
|
* @param zoneId ID of the zone
|
|
1384
|
-
* @param
|
|
1400
|
+
* @param photoIndex Index of the photo entry to delete in the zone array
|
|
1385
1401
|
* @returns The updated appointment
|
|
1386
1402
|
*/
|
|
1387
1403
|
async deleteZonePhoto(
|
|
1388
1404
|
appointmentId: string,
|
|
1389
1405
|
zoneId: string,
|
|
1390
|
-
|
|
1406
|
+
photoIndex: number,
|
|
1391
1407
|
): Promise<Appointment> {
|
|
1392
1408
|
try {
|
|
1393
1409
|
console.log(
|
|
1394
|
-
`[APPOINTMENT_SERVICE] Deleting ${
|
|
1410
|
+
`[APPOINTMENT_SERVICE] Deleting zone photo index ${photoIndex} for zone ${zoneId} in appointment ${appointmentId}`,
|
|
1395
1411
|
);
|
|
1396
1412
|
|
|
1397
1413
|
// Get current appointment
|
|
@@ -1400,15 +1416,23 @@ export class AppointmentService extends BaseService {
|
|
|
1400
1416
|
throw new Error(`Appointment with ID ${appointmentId} not found`);
|
|
1401
1417
|
}
|
|
1402
1418
|
|
|
1403
|
-
const zonePhotos = appointment.metadata?.zonePhotos
|
|
1404
|
-
|
|
1419
|
+
const zonePhotos = appointment.metadata?.zonePhotos as
|
|
1420
|
+
| Record<string, BeforeAfterPerZone[]>
|
|
1421
|
+
| undefined
|
|
1422
|
+
| null;
|
|
1423
|
+
if (!zonePhotos || !zonePhotos[zoneId] || !Array.isArray(zonePhotos[zoneId])) {
|
|
1405
1424
|
throw new Error(`No photos found for zone ${zoneId} in appointment ${appointmentId}`);
|
|
1406
1425
|
}
|
|
1407
1426
|
|
|
1408
|
-
const
|
|
1409
|
-
|
|
1427
|
+
const zoneArray = [...zonePhotos[zoneId]];
|
|
1428
|
+
if (photoIndex < 0 || photoIndex >= zoneArray.length) {
|
|
1429
|
+
throw new Error(`Invalid photo index ${photoIndex} for zone ${zoneId}`);
|
|
1430
|
+
}
|
|
1431
|
+
|
|
1432
|
+
const entry = zoneArray[photoIndex];
|
|
1433
|
+
const photoUrl = (entry.before || entry.after) as MediaResource | null;
|
|
1410
1434
|
if (!photoUrl) {
|
|
1411
|
-
throw new Error(`No
|
|
1435
|
+
throw new Error(`No photo URL found for index ${photoIndex} in zone ${zoneId}`);
|
|
1412
1436
|
}
|
|
1413
1437
|
|
|
1414
1438
|
// Try to find and delete the media from storage
|
|
@@ -1429,19 +1453,14 @@ export class AppointmentService extends BaseService {
|
|
|
1429
1453
|
// Continue with metadata update even if media deletion fails
|
|
1430
1454
|
}
|
|
1431
1455
|
|
|
1432
|
-
// Update appointment metadata to remove the photo
|
|
1433
|
-
const updatedZonePhotos = { ...zonePhotos };
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
} else {
|
|
1438
|
-
updatedZonePhotos[zoneId].after = null;
|
|
1439
|
-
updatedZonePhotos[zoneId].afterNote = null;
|
|
1440
|
-
}
|
|
1441
|
-
|
|
1442
|
-
// If both photos are null, we could optionally remove the zone entry entirely
|
|
1443
|
-
if (!updatedZonePhotos[zoneId].before && !updatedZonePhotos[zoneId].after) {
|
|
1456
|
+
// Update appointment metadata to remove the photo entry at the specified index
|
|
1457
|
+
const updatedZonePhotos: Record<string, BeforeAfterPerZone[]> = { ...zonePhotos } as any;
|
|
1458
|
+
const updatedZoneArray = [...zoneArray];
|
|
1459
|
+
updatedZoneArray.splice(photoIndex, 1);
|
|
1460
|
+
if (updatedZoneArray.length === 0) {
|
|
1444
1461
|
delete updatedZonePhotos[zoneId];
|
|
1462
|
+
} else {
|
|
1463
|
+
updatedZonePhotos[zoneId] = updatedZoneArray;
|
|
1445
1464
|
}
|
|
1446
1465
|
|
|
1447
1466
|
const updateData: UpdateAppointmentData = {
|
|
@@ -1451,6 +1470,7 @@ export class AppointmentService extends BaseService {
|
|
|
1451
1470
|
zonesData: appointment.metadata?.zonesData || null,
|
|
1452
1471
|
appointmentProducts: appointment.metadata?.appointmentProducts || [],
|
|
1453
1472
|
extendedProcedures: appointment.metadata?.extendedProcedures || [],
|
|
1473
|
+
recommendedProcedures: appointment.metadata?.recommendedProcedures || [],
|
|
1454
1474
|
// Only include zoneBilling if it exists (avoid undefined values in Firestore)
|
|
1455
1475
|
...(appointment.metadata?.zoneBilling !== undefined && {
|
|
1456
1476
|
zoneBilling: appointment.metadata.zoneBilling,
|
|
@@ -1464,7 +1484,7 @@ export class AppointmentService extends BaseService {
|
|
|
1464
1484
|
const updatedAppointment = await this.updateAppointment(appointmentId, updateData);
|
|
1465
1485
|
|
|
1466
1486
|
console.log(
|
|
1467
|
-
`[APPOINTMENT_SERVICE] Successfully deleted ${
|
|
1487
|
+
`[APPOINTMENT_SERVICE] Successfully deleted photo index ${photoIndex} for zone ${zoneId}`,
|
|
1468
1488
|
);
|
|
1469
1489
|
|
|
1470
1490
|
return updatedAppointment;
|
|
@@ -1714,6 +1734,7 @@ export class AppointmentService extends BaseService {
|
|
|
1714
1734
|
zonesData: null,
|
|
1715
1735
|
appointmentProducts: [],
|
|
1716
1736
|
extendedProcedures: [],
|
|
1737
|
+
recommendedProcedures: [],
|
|
1717
1738
|
finalbilling: null,
|
|
1718
1739
|
finalizationNotes: null,
|
|
1719
1740
|
};
|
|
@@ -1725,6 +1746,7 @@ export class AppointmentService extends BaseService {
|
|
|
1725
1746
|
zonesData: currentMetadata.zonesData,
|
|
1726
1747
|
appointmentProducts: currentMetadata.appointmentProducts || [],
|
|
1727
1748
|
extendedProcedures: currentMetadata.extendedProcedures || [],
|
|
1749
|
+
recommendedProcedures: currentMetadata.recommendedProcedures || [],
|
|
1728
1750
|
// Only include zoneBilling if it exists (avoid undefined values in Firestore)
|
|
1729
1751
|
...(currentMetadata.zoneBilling !== undefined && {
|
|
1730
1752
|
zoneBilling: currentMetadata.zoneBilling,
|
|
@@ -1741,4 +1763,216 @@ export class AppointmentService extends BaseService {
|
|
|
1741
1763
|
throw error;
|
|
1742
1764
|
}
|
|
1743
1765
|
}
|
|
1766
|
+
|
|
1767
|
+
/**
|
|
1768
|
+
* Adds a recommended procedure to an appointment
|
|
1769
|
+
* Multiple recommendations of the same procedure are allowed (e.g., touch-up in 2 weeks, full treatment in 3 months)
|
|
1770
|
+
*
|
|
1771
|
+
* @param appointmentId ID of the appointment
|
|
1772
|
+
* @param procedureId ID of the procedure to recommend
|
|
1773
|
+
* @param note Note explaining the recommendation
|
|
1774
|
+
* @param timeframe Suggested timeframe for the procedure
|
|
1775
|
+
* @returns The updated appointment
|
|
1776
|
+
*/
|
|
1777
|
+
async addRecommendedProcedure(
|
|
1778
|
+
appointmentId: string,
|
|
1779
|
+
procedureId: string,
|
|
1780
|
+
note: string,
|
|
1781
|
+
timeframe: { value: number; unit: 'day' | 'week' | 'month' | 'year' }
|
|
1782
|
+
): Promise<Appointment> {
|
|
1783
|
+
try {
|
|
1784
|
+
console.log(
|
|
1785
|
+
`[APPOINTMENT_SERVICE] Adding recommended procedure ${procedureId} to appointment ${appointmentId}`,
|
|
1786
|
+
);
|
|
1787
|
+
return await addRecommendedProcedureUtil(this.db, appointmentId, procedureId, note, timeframe);
|
|
1788
|
+
} catch (error) {
|
|
1789
|
+
console.error(`[APPOINTMENT_SERVICE] Error adding recommended procedure:`, error);
|
|
1790
|
+
throw error;
|
|
1791
|
+
}
|
|
1792
|
+
}
|
|
1793
|
+
|
|
1794
|
+
/**
|
|
1795
|
+
* Removes a recommended procedure from an appointment by index
|
|
1796
|
+
*
|
|
1797
|
+
* @param appointmentId ID of the appointment
|
|
1798
|
+
* @param recommendationIndex Index of the recommendation to remove
|
|
1799
|
+
* @returns The updated appointment
|
|
1800
|
+
*/
|
|
1801
|
+
async removeRecommendedProcedure(appointmentId: string, recommendationIndex: number): Promise<Appointment> {
|
|
1802
|
+
try {
|
|
1803
|
+
console.log(
|
|
1804
|
+
`[APPOINTMENT_SERVICE] Removing recommended procedure at index ${recommendationIndex} from appointment ${appointmentId}`,
|
|
1805
|
+
);
|
|
1806
|
+
return await removeRecommendedProcedureUtil(this.db, appointmentId, recommendationIndex);
|
|
1807
|
+
} catch (error) {
|
|
1808
|
+
console.error(`[APPOINTMENT_SERVICE] Error removing recommended procedure:`, error);
|
|
1809
|
+
throw error;
|
|
1810
|
+
}
|
|
1811
|
+
}
|
|
1812
|
+
|
|
1813
|
+
/**
|
|
1814
|
+
* Updates a recommended procedure in an appointment by index
|
|
1815
|
+
*
|
|
1816
|
+
* @param appointmentId ID of the appointment
|
|
1817
|
+
* @param recommendationIndex Index of the recommendation to update
|
|
1818
|
+
* @param updates Partial updates (note and/or timeframe)
|
|
1819
|
+
* @returns The updated appointment
|
|
1820
|
+
*/
|
|
1821
|
+
async updateRecommendedProcedure(
|
|
1822
|
+
appointmentId: string,
|
|
1823
|
+
recommendationIndex: number,
|
|
1824
|
+
updates: {
|
|
1825
|
+
note?: string;
|
|
1826
|
+
timeframe?: { value: number; unit: 'day' | 'week' | 'month' | 'year' };
|
|
1827
|
+
}
|
|
1828
|
+
): Promise<Appointment> {
|
|
1829
|
+
try {
|
|
1830
|
+
console.log(
|
|
1831
|
+
`[APPOINTMENT_SERVICE] Updating recommended procedure at index ${recommendationIndex} in appointment ${appointmentId}`,
|
|
1832
|
+
);
|
|
1833
|
+
return await updateRecommendedProcedureUtil(this.db, appointmentId, recommendationIndex, updates);
|
|
1834
|
+
} catch (error) {
|
|
1835
|
+
console.error(`[APPOINTMENT_SERVICE] Error updating recommended procedure:`, error);
|
|
1836
|
+
throw error;
|
|
1837
|
+
}
|
|
1838
|
+
}
|
|
1839
|
+
|
|
1840
|
+
/**
|
|
1841
|
+
* Gets all recommended procedures for an appointment
|
|
1842
|
+
*
|
|
1843
|
+
* @param appointmentId ID of the appointment
|
|
1844
|
+
* @returns Array of recommended procedures
|
|
1845
|
+
*/
|
|
1846
|
+
async getRecommendedProcedures(appointmentId: string): Promise<RecommendedProcedure[]> {
|
|
1847
|
+
try {
|
|
1848
|
+
console.log(
|
|
1849
|
+
`[APPOINTMENT_SERVICE] Getting recommended procedures for appointment ${appointmentId}`,
|
|
1850
|
+
);
|
|
1851
|
+
return await getRecommendedProceduresUtil(this.db, appointmentId);
|
|
1852
|
+
} catch (error) {
|
|
1853
|
+
console.error(`[APPOINTMENT_SERVICE] Error getting recommended procedures:`, error);
|
|
1854
|
+
throw error;
|
|
1855
|
+
}
|
|
1856
|
+
}
|
|
1857
|
+
|
|
1858
|
+
/**
|
|
1859
|
+
* Updates a specific photo entry in a zone by index
|
|
1860
|
+
* Can update before/after photos and their notes
|
|
1861
|
+
*
|
|
1862
|
+
* @param appointmentId ID of the appointment
|
|
1863
|
+
* @param zoneId Zone ID
|
|
1864
|
+
* @param photoIndex Index of the photo entry to update
|
|
1865
|
+
* @param updates Partial updates to apply (before, after, beforeNote, afterNote)
|
|
1866
|
+
* @returns The updated appointment
|
|
1867
|
+
*/
|
|
1868
|
+
async updateZonePhotoEntry(
|
|
1869
|
+
appointmentId: string,
|
|
1870
|
+
zoneId: string,
|
|
1871
|
+
photoIndex: number,
|
|
1872
|
+
updates: Partial<BeforeAfterPerZone>
|
|
1873
|
+
): Promise<Appointment> {
|
|
1874
|
+
try {
|
|
1875
|
+
console.log(
|
|
1876
|
+
`[APPOINTMENT_SERVICE] Updating photo entry at index ${photoIndex} for zone ${zoneId} in appointment ${appointmentId}`,
|
|
1877
|
+
);
|
|
1878
|
+
return await updateZonePhotoEntryUtil(this.db, appointmentId, zoneId, photoIndex, updates);
|
|
1879
|
+
} catch (error) {
|
|
1880
|
+
console.error(`[APPOINTMENT_SERVICE] Error updating zone photo entry:`, error);
|
|
1881
|
+
throw error;
|
|
1882
|
+
}
|
|
1883
|
+
}
|
|
1884
|
+
|
|
1885
|
+
/**
|
|
1886
|
+
* Adds an after photo to an existing before photo entry
|
|
1887
|
+
*
|
|
1888
|
+
* @param appointmentId ID of the appointment
|
|
1889
|
+
* @param zoneId Zone ID
|
|
1890
|
+
* @param photoIndex Index of the entry to add after photo to
|
|
1891
|
+
* @param afterPhotoUrl URL of the after photo
|
|
1892
|
+
* @param afterNote Optional note for the after photo
|
|
1893
|
+
* @returns The updated appointment
|
|
1894
|
+
*/
|
|
1895
|
+
async addAfterPhotoToEntry(
|
|
1896
|
+
appointmentId: string,
|
|
1897
|
+
zoneId: string,
|
|
1898
|
+
photoIndex: number,
|
|
1899
|
+
afterPhotoUrl: MediaResource,
|
|
1900
|
+
afterNote?: string
|
|
1901
|
+
): Promise<Appointment> {
|
|
1902
|
+
try {
|
|
1903
|
+
console.log(
|
|
1904
|
+
`[APPOINTMENT_SERVICE] Adding after photo to entry at index ${photoIndex} for zone ${zoneId}`,
|
|
1905
|
+
);
|
|
1906
|
+
return await addAfterPhotoToEntryUtil(
|
|
1907
|
+
this.db,
|
|
1908
|
+
appointmentId,
|
|
1909
|
+
zoneId,
|
|
1910
|
+
photoIndex,
|
|
1911
|
+
afterPhotoUrl,
|
|
1912
|
+
afterNote
|
|
1913
|
+
);
|
|
1914
|
+
} catch (error) {
|
|
1915
|
+
console.error(`[APPOINTMENT_SERVICE] Error adding after photo to entry:`, error);
|
|
1916
|
+
throw error;
|
|
1917
|
+
}
|
|
1918
|
+
}
|
|
1919
|
+
|
|
1920
|
+
/**
|
|
1921
|
+
* Updates notes for a photo entry
|
|
1922
|
+
*
|
|
1923
|
+
* @param appointmentId ID of the appointment
|
|
1924
|
+
* @param zoneId Zone ID
|
|
1925
|
+
* @param photoIndex Index of the entry
|
|
1926
|
+
* @param beforeNote Optional note for before photo
|
|
1927
|
+
* @param afterNote Optional note for after photo
|
|
1928
|
+
* @returns The updated appointment
|
|
1929
|
+
*/
|
|
1930
|
+
async updateZonePhotoNotes(
|
|
1931
|
+
appointmentId: string,
|
|
1932
|
+
zoneId: string,
|
|
1933
|
+
photoIndex: number,
|
|
1934
|
+
beforeNote?: string,
|
|
1935
|
+
afterNote?: string
|
|
1936
|
+
): Promise<Appointment> {
|
|
1937
|
+
try {
|
|
1938
|
+
console.log(
|
|
1939
|
+
`[APPOINTMENT_SERVICE] Updating notes for photo entry at index ${photoIndex} for zone ${zoneId}`,
|
|
1940
|
+
);
|
|
1941
|
+
return await updateZonePhotoNotesUtil(
|
|
1942
|
+
this.db,
|
|
1943
|
+
appointmentId,
|
|
1944
|
+
zoneId,
|
|
1945
|
+
photoIndex,
|
|
1946
|
+
beforeNote,
|
|
1947
|
+
afterNote
|
|
1948
|
+
);
|
|
1949
|
+
} catch (error) {
|
|
1950
|
+
console.error(`[APPOINTMENT_SERVICE] Error updating zone photo notes:`, error);
|
|
1951
|
+
throw error;
|
|
1952
|
+
}
|
|
1953
|
+
}
|
|
1954
|
+
|
|
1955
|
+
/**
|
|
1956
|
+
* Gets a specific photo entry from a zone
|
|
1957
|
+
*
|
|
1958
|
+
* @param appointmentId ID of the appointment
|
|
1959
|
+
* @param zoneId Zone ID
|
|
1960
|
+
* @param photoIndex Index of the entry
|
|
1961
|
+
* @returns Photo entry
|
|
1962
|
+
*/
|
|
1963
|
+
async getZonePhotoEntry(
|
|
1964
|
+
appointmentId: string,
|
|
1965
|
+
zoneId: string,
|
|
1966
|
+
photoIndex: number
|
|
1967
|
+
): Promise<BeforeAfterPerZone> {
|
|
1968
|
+
try {
|
|
1969
|
+
console.log(
|
|
1970
|
+
`[APPOINTMENT_SERVICE] Getting photo entry at index ${photoIndex} for zone ${zoneId}`,
|
|
1971
|
+
);
|
|
1972
|
+
return await getZonePhotoEntryUtil(this.db, appointmentId, zoneId, photoIndex);
|
|
1973
|
+
} catch (error) {
|
|
1974
|
+
console.error(`[APPOINTMENT_SERVICE] Error getting zone photo entry:`, error);
|
|
1975
|
+
throw error;
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1744
1978
|
}
|