@7365admin1/layer-common 1.10.0 → 1.10.1

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.
Files changed (81) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/components/AcceptDialog.vue +44 -0
  3. package/components/AccessCardAddForm.vue +101 -13
  4. package/components/AccessManagement.vue +130 -47
  5. package/components/AddSupplyForm.vue +165 -0
  6. package/components/AreaChecklistHistoryLogs.vue +235 -0
  7. package/components/AreaChecklistHistoryMain.vue +176 -0
  8. package/components/AreaFormDialog.vue +266 -0
  9. package/components/AreaMain.vue +841 -0
  10. package/components/AttendanceCheckInOutDialog.vue +416 -0
  11. package/components/AttendanceDetailsDialog.vue +184 -0
  12. package/components/AttendanceMain.vue +155 -0
  13. package/components/AttendanceMapSearchDialog.vue +393 -0
  14. package/components/AttendanceSettingsDialog.vue +398 -0
  15. package/components/BuildingManagement/buildings.vue +5 -5
  16. package/components/BuildingManagement/units.vue +5 -5
  17. package/components/ChecklistItemRow.vue +54 -0
  18. package/components/CheckoutItemMain.vue +705 -0
  19. package/components/CleaningScheduleMain.vue +271 -0
  20. package/components/DocumentManagement.vue +4 -0
  21. package/components/EntryPass/QrTemplatePreview.vue +104 -0
  22. package/components/EntryPassMain.vue +252 -200
  23. package/components/HygieneUpdateMoreAction.vue +238 -0
  24. package/components/ManageChecklistMain.vue +384 -0
  25. package/components/MemberMain.vue +48 -20
  26. package/components/MyAttendanceMain.vue +224 -0
  27. package/components/OnlineFormsConfiguration.vue +9 -2
  28. package/components/PhotoUpload.vue +410 -0
  29. package/components/ScheduleAreaMain.vue +313 -0
  30. package/components/ScheduleTaskAreaFormDialog.vue +144 -0
  31. package/components/ScheduleTaskAreaUpdateMoreAction.vue +109 -0
  32. package/components/ScheduleTaskForm.vue +471 -0
  33. package/components/ScheduleTaskMain.vue +345 -0
  34. package/components/ScheduleTastTicketMain.vue +182 -0
  35. package/components/StockCard.vue +191 -0
  36. package/components/SupplyManagementMain.vue +557 -0
  37. package/components/TableHygiene.vue +617 -0
  38. package/components/UnitMain.vue +451 -0
  39. package/components/VisitorManagement.vue +28 -15
  40. package/composables/useAccessManagement.ts +90 -0
  41. package/composables/useAreaPermission.ts +51 -0
  42. package/composables/useAreas.ts +99 -0
  43. package/composables/useAttendance.ts +89 -0
  44. package/composables/useAttendancePermission.ts +68 -0
  45. package/composables/useBuilding.ts +2 -2
  46. package/composables/useBuildingUnit.ts +2 -2
  47. package/composables/useCard.ts +2 -0
  48. package/composables/useCheckout.ts +61 -0
  49. package/composables/useCheckoutPermission.ts +80 -0
  50. package/composables/useCleaningPermission.ts +229 -0
  51. package/composables/useCleaningSchedulePermission.ts +58 -0
  52. package/composables/useCleaningSchedules.ts +233 -0
  53. package/composables/useCountry.ts +8 -0
  54. package/composables/useDashboardData.ts +2 -2
  55. package/composables/useFeedback.ts +1 -1
  56. package/composables/useLocation.ts +78 -0
  57. package/composables/useOnlineForm.ts +16 -9
  58. package/composables/usePeople.ts +87 -72
  59. package/composables/useQR.ts +29 -0
  60. package/composables/useScheduleTask.ts +89 -0
  61. package/composables/useScheduleTaskArea.ts +85 -0
  62. package/composables/useScheduleTaskPermission.ts +68 -0
  63. package/composables/useSiteEntryPassSettings.ts +4 -15
  64. package/composables/useStock.ts +45 -0
  65. package/composables/useSupply.ts +63 -0
  66. package/composables/useSupplyPermission.ts +92 -0
  67. package/composables/useUnitPermission.ts +51 -0
  68. package/composables/useUnits.ts +82 -0
  69. package/composables/useWebUsb.ts +389 -0
  70. package/composables/useWorkOrder.ts +1 -1
  71. package/nuxt.config.ts +3 -0
  72. package/package.json +4 -1
  73. package/types/area.d.ts +22 -0
  74. package/types/attendance.d.ts +38 -0
  75. package/types/checkout-item.d.ts +27 -0
  76. package/types/cleaner-schedule.d.ts +54 -0
  77. package/types/location.d.ts +42 -0
  78. package/types/schedule-task.d.ts +18 -0
  79. package/types/stock.d.ts +16 -0
  80. package/types/supply.d.ts +11 -0
  81. package/utils/acm-crypto.ts +30 -0
@@ -0,0 +1,389 @@
1
+ import QRCode from "qrcode";
2
+
3
+ // Web USB type definitions
4
+ interface USBDevice {
5
+ vendorId: number;
6
+ productId: number;
7
+ manufacturerName?: string;
8
+ productName?: string;
9
+ serialNumber?: string;
10
+ open(): Promise<void>;
11
+ close(): Promise<void>;
12
+ selectConfiguration(configurationValue: number): Promise<void>;
13
+ claimInterface(interfaceNumber: number): Promise<void>;
14
+ releaseInterface(interfaceNumber: number): Promise<void>;
15
+ transferOut(
16
+ endpointNumber: number,
17
+ data: BufferSource,
18
+ ): Promise<USBOutTransferResult>;
19
+ configuration: {
20
+ interfaces: Array<{
21
+ interfaceNumber: number;
22
+ alternates: Array<{
23
+ endpoints: Array<{
24
+ endpointNumber: number;
25
+ direction: "in" | "out";
26
+ }>;
27
+ }>;
28
+ }>;
29
+ };
30
+ }
31
+
32
+ interface USBOutTransferResult {
33
+ status: "ok" | "stall" | "babble";
34
+ bytesWritten: number;
35
+ }
36
+
37
+ declare global {
38
+ interface Navigator {
39
+ usb: {
40
+ getDevices(): Promise<USBDevice[]>;
41
+ requestDevice(options: {
42
+ filters: Array<{ vendorId?: number; productId?: number }>;
43
+ }): Promise<USBDevice>;
44
+ };
45
+ }
46
+ }
47
+
48
+ export default function useWebUsb() {
49
+ const isWebUsbSupported = computed(() => {
50
+ return typeof navigator !== "undefined" && "usb" in navigator;
51
+ });
52
+
53
+ const getAvailableDevices = async () => {
54
+ if (!isWebUsbSupported.value) {
55
+ throw new Error("Web USB is not supported in this browser");
56
+ }
57
+ try {
58
+ const devices = await navigator.usb.getDevices();
59
+ return devices.map((device: USBDevice) => ({
60
+ vendorId: device.vendorId,
61
+ productId: device.productId,
62
+ manufacturerName: device.manufacturerName || "Unknown",
63
+ productName: device.productName || "Unknown",
64
+ serialNumber: device.serialNumber || "Unknown",
65
+ deviceId: `${device.vendorId}:${device.productId}`,
66
+ }));
67
+ } catch (error) {
68
+ console.error("Error getting USB devices:", error);
69
+ throw new Error("Failed to get USB devices");
70
+ }
71
+ };
72
+
73
+ const requestDevice = async () => {
74
+ if (!isWebUsbSupported.value) {
75
+ throw new Error("Web USB is not supported in this browser");
76
+ }
77
+ try {
78
+ const device = await navigator.usb.requestDevice({ filters: [] });
79
+ return {
80
+ vendorId: device.vendorId,
81
+ productId: device.productId,
82
+ manufacturerName: device.manufacturerName || "Unknown",
83
+ productName: device.productName || "Unknown",
84
+ serialNumber: device.serialNumber || "Unknown",
85
+ deviceId: `${device.vendorId}:${device.productId}`,
86
+ };
87
+ } catch (error: any) {
88
+ if (error.name === "NotFoundError") {
89
+ throw new Error("No device selected");
90
+ }
91
+ console.error("Error requesting USB device:", error);
92
+ throw new Error("Failed to access USB device");
93
+ }
94
+ };
95
+
96
+ const connectToDevice = async (vendorId: number, productId: number) => {
97
+ if (!isWebUsbSupported.value) {
98
+ throw new Error("Web USB is not supported in this browser");
99
+ }
100
+ try {
101
+ let devices = await navigator.usb.getDevices();
102
+ let device = devices.find(
103
+ (d: USBDevice) => d.vendorId === vendorId && d.productId === productId,
104
+ );
105
+ if (!device) {
106
+ device = await navigator.usb.requestDevice({
107
+ filters: [{ vendorId, productId }],
108
+ });
109
+ }
110
+ await device.open();
111
+ await device.selectConfiguration(1);
112
+ await device.claimInterface(0);
113
+ return device;
114
+ } catch (error) {
115
+ console.error("Error connecting to USB device:", error);
116
+ throw new Error("Failed to connect to USB device");
117
+ }
118
+ };
119
+
120
+ const testConnection = async (
121
+ vendorId: number,
122
+ productId: number,
123
+ forQr?: boolean,
124
+ urlImage?: string,
125
+ doorLevel?: number | string | null,
126
+ liftLevel?: number | string | null,
127
+ companyName?: string,
128
+ address?: string,
129
+ qrHeader?: string,
130
+ qrSubText?: string,
131
+ ) => {
132
+ if (!isWebUsbSupported.value) {
133
+ throw new Error("Web USB is not supported in this browser");
134
+ }
135
+
136
+ let device: USBDevice | null = null;
137
+ try {
138
+ device = await connectToDevice(vendorId, productId);
139
+
140
+ const deviceInfo = {
141
+ vendorId: device.vendorId,
142
+ productId: device.productId,
143
+ manufacturerName: device.manufacturerName || "Unknown",
144
+ productName: device.productName || "Unknown",
145
+ serialNumber: device.serialNumber || "Unknown",
146
+ };
147
+
148
+ let printTestResult;
149
+ if (forQr && urlImage) {
150
+ printTestResult = await printQrCode(
151
+ device,
152
+ urlImage,
153
+ doorLevel ?? null,
154
+ liftLevel ?? null,
155
+ companyName,
156
+ address,
157
+ qrHeader,
158
+ qrSubText,
159
+ );
160
+ } else {
161
+ printTestResult = await testPrint(device);
162
+ }
163
+
164
+ return {
165
+ success: true,
166
+ deviceInfo,
167
+ printTest: printTestResult,
168
+ message: printTestResult.success
169
+ ? "Connection and print test successful"
170
+ : "Connection successful but print test failed",
171
+ };
172
+ } catch (error: any) {
173
+ return {
174
+ success: false,
175
+ error: error.message,
176
+ message: "Connection failed",
177
+ };
178
+ } finally {
179
+ if (device) {
180
+ try {
181
+ await device.close();
182
+ } catch (closeError) {
183
+ console.error("Error closing device:", closeError);
184
+ }
185
+ }
186
+ }
187
+ };
188
+
189
+ const testPrint = async (device: USBDevice) => {
190
+ try {
191
+ const testCommands = [
192
+ 0x1b, 0x40,
193
+ 0x1b, 0x61, 0x01,
194
+ 0x1b, 0x21, 0x30,
195
+ ...new TextEncoder().encode("TEST PRINT\n"),
196
+ 0x1b, 0x21, 0x00,
197
+ ...new TextEncoder().encode("Printer connection test successful!\n"),
198
+ ...new TextEncoder().encode("Date: " + new Date().toLocaleString() + "\n"),
199
+ 0x1d, 0x56, 0x00,
200
+ 0x0a, 0x0a, 0x0a, 0x0a,
201
+ ];
202
+
203
+ const testData = new Uint8Array(testCommands);
204
+ const usbInterface = device.configuration.interfaces[0];
205
+ const alternate = usbInterface.alternates[0];
206
+ const outputEndpoint = alternate.endpoints.find(
207
+ (ep: any) => ep.direction === "out",
208
+ );
209
+
210
+ if (!outputEndpoint) {
211
+ throw new Error("No output endpoint found");
212
+ }
213
+
214
+ await device.transferOut(outputEndpoint.endpointNumber, testData);
215
+ return { success: true, message: "Test print sent successfully" };
216
+ } catch (error: any) {
217
+ console.error("Test print error:", error);
218
+ return { success: false, error: error.message, message: "Test print failed" };
219
+ }
220
+ };
221
+
222
+ async function createReceiptLayout(
223
+ qrUrlImage: string,
224
+ doorLevel: number | string | null,
225
+ liftLevel: number | string | null,
226
+ companyName?: string,
227
+ address?: string,
228
+ qrHeader?: string,
229
+ qrSubText?: string,
230
+ ): Promise<HTMLCanvasElement> {
231
+ const canvas = document.createElement("canvas");
232
+ canvas.width = 576;
233
+ canvas.height = 620;
234
+ const ctx = canvas.getContext("2d")!;
235
+ ctx.fillStyle = "#fff";
236
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
237
+ ctx.fillStyle = "#000";
238
+ ctx.textAlign = "center";
239
+
240
+ ctx.font = "bold 30px Arial";
241
+ ctx.fillText(qrHeader || "Welcome", canvas.width / 2, 40);
242
+
243
+ ctx.font = "20px Arial";
244
+ ctx.fillText(
245
+ qrSubText || "Proceed to Security Desk to check-out after visit",
246
+ canvas.width / 2,
247
+ 70,
248
+ );
249
+
250
+ const qrSmall = 140;
251
+ const qrCenter = 250;
252
+ const margin = 20;
253
+ const qrCanvas = await QRCode.toCanvas(qrUrlImage, { width: qrCenter });
254
+
255
+ ctx.drawImage(qrCanvas, margin, 90, qrSmall, qrSmall);
256
+ ctx.drawImage(qrCanvas, canvas.width - qrSmall - margin, 90, qrSmall, qrSmall);
257
+ ctx.drawImage(qrCanvas, (canvas.width - qrCenter) / 2, 110, qrCenter, qrCenter);
258
+ ctx.drawImage(qrCanvas, margin, 300, qrSmall, qrSmall);
259
+ ctx.drawImage(qrCanvas, canvas.width - qrSmall - margin, 300, qrSmall, qrSmall);
260
+
261
+ ctx.font = "25px Arial";
262
+ ctx.fillText(companyName || "", canvas.width / 2, 470);
263
+
264
+ ctx.font = "25px Arial";
265
+ ctx.fillText(address || "", canvas.width / 2, 505);
266
+
267
+ const now = new Date();
268
+ const pad = (n: number) => n.toString().padStart(2, "0");
269
+ const dateStr = `${now.getFullYear()}-${pad(now.getMonth() + 1)}-${pad(now.getDate())}`;
270
+ const timeStr = `${pad(now.getHours())}:${pad(now.getMinutes())}:${pad(now.getSeconds())}`;
271
+
272
+ ctx.font = "20px Arial";
273
+ ctx.fillText(`Date: ${dateStr} ${timeStr}`, canvas.width / 2, 540);
274
+
275
+ let accessText = "Access: ";
276
+ if (doorLevel && liftLevel) {
277
+ accessText += "Door / Elevator";
278
+ } else if (doorLevel) {
279
+ accessText += "Door";
280
+ } else if (liftLevel) {
281
+ accessText += "Elevator";
282
+ } else {
283
+ accessText += "N/A";
284
+ }
285
+ ctx.fillText(accessText, canvas.width / 2, 570);
286
+
287
+ ctx.font = "italic 20px Arial";
288
+ let instructionText = "";
289
+ if (doorLevel && liftLevel) {
290
+ instructionText = `Scan ${doorLevel}. Scan and press Level ${liftLevel}. One time use.`;
291
+ } else if (doorLevel) {
292
+ instructionText = `Scan ${doorLevel}. One time use.`;
293
+ } else if (liftLevel) {
294
+ instructionText = `Scan and press Level ${liftLevel}. One time use.`;
295
+ } else {
296
+ instructionText = "One time use.";
297
+ }
298
+ ctx.fillText(instructionText, canvas.width / 2, 600);
299
+
300
+ return canvas;
301
+ }
302
+
303
+ function canvasToRaster(canvas: HTMLCanvasElement): Uint8Array {
304
+ const ctx = canvas.getContext("2d");
305
+ if (!ctx) throw new Error("Canvas 2D context not available");
306
+
307
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
308
+ const pixels = imageData.data;
309
+ const width = canvas.width;
310
+ const height = canvas.height;
311
+ const rowBytes = Math.ceil(width / 8);
312
+ const bytes: number[] = [];
313
+
314
+ bytes.push(0x1d, 0x76, 0x30, 0x00);
315
+ bytes.push(rowBytes & 0xff, (rowBytes >> 8) & 0xff);
316
+ bytes.push(height & 0xff, (height >> 8) & 0xff);
317
+
318
+ for (let y = 0; y < height; y++) {
319
+ for (let xByte = 0; xByte < rowBytes; xByte++) {
320
+ let byte = 0;
321
+ for (let bit = 0; bit < 8; bit++) {
322
+ const x = xByte * 8 + bit;
323
+ const idx = (y * width + x) * 4;
324
+ if (x < width) {
325
+ const r = pixels[idx];
326
+ const g = pixels[idx + 1];
327
+ const b = pixels[idx + 2];
328
+ const avg = (r + g + b) / 3;
329
+ if (avg < 128) {
330
+ byte |= 0x80 >> bit;
331
+ }
332
+ }
333
+ }
334
+ bytes.push(byte);
335
+ }
336
+ }
337
+
338
+ return new Uint8Array(bytes);
339
+ }
340
+
341
+ const printQrCode = async (
342
+ device: USBDevice,
343
+ urlImage: string,
344
+ doorLevel: number | string | null,
345
+ liftLevel: number | string | null,
346
+ companyName?: string,
347
+ address?: string,
348
+ qrHeader?: string,
349
+ qrSubText?: string,
350
+ ) => {
351
+ try {
352
+ const usbInterface = device.configuration.interfaces[0];
353
+ const alternate = usbInterface.alternates[0];
354
+ const outputEndpoint = alternate.endpoints.find(
355
+ (ep: any) => ep.direction === "out",
356
+ );
357
+ if (!outputEndpoint) throw new Error("No output endpoint found");
358
+
359
+ const canvas = await createReceiptLayout(
360
+ urlImage,
361
+ doorLevel,
362
+ liftLevel,
363
+ companyName,
364
+ address,
365
+ qrHeader,
366
+ qrSubText,
367
+ );
368
+ const rasterData = canvasToRaster(canvas);
369
+
370
+ await device.transferOut(outputEndpoint.endpointNumber, rasterData.buffer);
371
+
372
+ const CUT = new Uint8Array([0x1d, 0x56, 0x41, 0x10]);
373
+ await device.transferOut(outputEndpoint.endpointNumber, CUT.buffer);
374
+
375
+ return { success: true, message: "QR print sent successfully" };
376
+ } catch (error: any) {
377
+ console.error("Print QR code error:", error);
378
+ return { success: false, error: error.message };
379
+ }
380
+ };
381
+
382
+ return {
383
+ isWebUsbSupported,
384
+ getAvailableDevices,
385
+ requestDevice,
386
+ connectToDevice,
387
+ testConnection,
388
+ };
389
+ }
@@ -73,7 +73,7 @@ export default function useWorkOrder() {
73
73
 
74
74
  function deleteWorkOrder(id: string) {
75
75
  return useNuxtApp().$api<Record<string, any>>(`/api/work-orders/deleted/work-order`, {
76
- method: "DELETE",
76
+ method: "PUT",
77
77
  query: { id },
78
78
  });
79
79
  }
package/nuxt.config.ts CHANGED
@@ -32,6 +32,9 @@ export default defineNuxtConfig({
32
32
  APP_PEST_CONTROL: (process.env.APP_PEST_CONTROL as string) ?? "",
33
33
  APP_LANDSCAPING: (process.env.APP_LANDSCAPING as string) ?? "",
34
34
  APP_POOL_MAINTENANCE: (process.env.APP_POOL_MAINTENANCE as string) ?? "",
35
+ ENTRYPASS_ACM_URL:
36
+ (process.env.ENTRYPASS_ACM_URL as string) ??
37
+ "https://entrypass-xsocket.iservice365.app/",
35
38
  },
36
39
  },
37
40
 
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "@7365admin1/layer-common",
3
3
  "license": "MIT",
4
4
  "type": "module",
5
- "version": "1.10.0",
5
+ "version": "1.10.1",
6
6
  "author": "7365admin1",
7
7
  "main": "./nuxt.config.ts",
8
8
  "publishConfig": {
@@ -30,8 +30,11 @@
30
30
  "dependencies": {
31
31
  "@ckeditor/ckeditor5-vue": "^7.3.0",
32
32
  "@iconify/vue": "^5.0.0",
33
+ "qrcode.vue": "^3.4.1",
33
34
  "@mdi/font": "^7.4.47",
35
+ "@types/qrcode": "^1.5.6",
34
36
  "ckeditor5": "^47.2.0",
37
+ "qrcode": "^1.5.4",
35
38
  "sass": "^1.80.6",
36
39
  "vue3-signature": "^0.2.4",
37
40
  "zod": "^3.24.2"
@@ -0,0 +1,22 @@
1
+ declare type TArea = {
2
+ _id: string;
3
+ name: string;
4
+ checklist: TAreaChecklist[];
5
+ createdAt: string;
6
+ updatedAt: string;
7
+ };
8
+
9
+ declare type TAreaChecklist = {
10
+ _id: string;
11
+ name: string;
12
+ };
13
+
14
+ declare type TAreaCreate = {
15
+ name: string;
16
+ set: number;
17
+ type: string;
18
+ units: Array<{
19
+ unit: string;
20
+ name: string;
21
+ }>;
22
+ };
@@ -0,0 +1,38 @@
1
+ declare type TAttendance = {
2
+ _id: string;
3
+ teamMember: string;
4
+ checkIn?: string;
5
+ checkOut?: string;
6
+ totalHours?: string;
7
+ createdAt: string;
8
+ updatedAt: string;
9
+ };
10
+
11
+ declare type TAttendanceCheckIn = {
12
+ checkIn: {
13
+ timestamp: string;
14
+ location: TAttendanceLocation;
15
+ img?: string;
16
+ };
17
+ };
18
+
19
+ declare type TAttendanceCheckOut = {
20
+ checkOut: {
21
+ timestamp: string;
22
+ location: TAttendanceLocation;
23
+ img?: string;
24
+ };
25
+ };
26
+
27
+ declare type TAttendanceLocation = {
28
+ latitude: number;
29
+ longitude: number;
30
+ };
31
+
32
+ declare type TAttendanceSettings = {
33
+ site: string;
34
+ isLocationEnabled: boolean;
35
+ location?: TAttendanceLocation;
36
+ isGeofencingEnabled?: boolean;
37
+ mile?: number;
38
+ };
@@ -0,0 +1,27 @@
1
+ declare type TCheckoutItemApi = {
2
+ _id: string;
3
+ createdAt: string;
4
+ name: string;
5
+ status: string;
6
+ supply: string;
7
+ qty: number;
8
+ unitOfMeasurement: string;
9
+ remarks?: string;
10
+ attachment?: string[];
11
+ };
12
+
13
+ declare type TCheckoutItemCreate = {
14
+ items: Array<{
15
+ supply: string;
16
+ qty: number;
17
+ attachment?: string[];
18
+ }>;
19
+ };
20
+
21
+ declare type TCheckoutItem = {
22
+ supply: string;
23
+ supplyName: string;
24
+ qty: number;
25
+ photos: string[];
26
+ photoIds: string[];
27
+ };
@@ -0,0 +1,54 @@
1
+ declare type TCleanerChecklist = {
2
+ _id: string;
3
+ date: string;
4
+ status: TCleanerChecklistStatus[];
5
+ createdAt: string;
6
+ updatedAt: string;
7
+ attachments: string[];
8
+ };
9
+
10
+ declare type TCleanerChecklistStatus = {
11
+ type: string;
12
+ site: string;
13
+ status: string;
14
+ completedAt: string;
15
+ createdByName: string;
16
+ };
17
+
18
+ declare type TAreaRejectChecklist = {
19
+ attachments: string[];
20
+ remarks: string;
21
+ };
22
+
23
+ declare type TAreaChecklistHistory = {
24
+ _id: string;
25
+ name: string;
26
+ status: string;
27
+ createdAt: string;
28
+ endedAt: string;
29
+ acceptedByName: string;
30
+ };
31
+
32
+ declare type TUnitChecklistItem = {
33
+ unit: string;
34
+ name: string;
35
+ status: string;
36
+ remarks: string;
37
+ completedByName?: string;
38
+ timestamp?: string;
39
+ };
40
+
41
+ declare type TChecklistSet = {
42
+ units: TUnitChecklistItem[];
43
+ set: number;
44
+ remarks?: string;
45
+ attachments?: string[];
46
+ completedByName?: string;
47
+ };
48
+
49
+ declare type TFlattenedUnitItem = TUnitChecklistItem & {
50
+ set: number;
51
+ };
52
+
53
+ declare type TScheduleAreaStatus = "All" | "Ready" | "Ongoing" | "Completed";
54
+ declare type TAreaType = "all" | "common" | "toilet";
@@ -0,0 +1,42 @@
1
+ declare type TLocationData = {
2
+ latitude: number;
3
+ longitude: number;
4
+ address?: string;
5
+ city?: string;
6
+ country?: string;
7
+ postalCode?: string;
8
+ };
9
+
10
+ declare type TLocationFormData = {
11
+ country: string;
12
+ postalCode: string;
13
+ city: string;
14
+ address: string;
15
+ };
16
+
17
+ declare type TLocationFieldState = {
18
+ country: boolean;
19
+ city: boolean;
20
+ address: boolean;
21
+ };
22
+
23
+ declare type TNominatimResult = {
24
+ lat: string;
25
+ lon: string;
26
+ display_name: string;
27
+ address?: {
28
+ city?: string;
29
+ town?: string;
30
+ village?: string;
31
+ state?: string;
32
+ country?: string;
33
+ postcode?: string;
34
+ };
35
+ };
36
+
37
+ declare type TLocationSearchParams = {
38
+ postalCode: string;
39
+ city?: string;
40
+ address?: string;
41
+ country?: string;
42
+ };
@@ -0,0 +1,18 @@
1
+ declare type TScheduleTask = {
2
+ _id: string;
3
+ title: string;
4
+ // replaced frequency-based recurrence with explicit date range
5
+ startDate?: string; // YYYY-MM-DD
6
+ endDate?: string; // YYYY-MM-DD
7
+ time?: string;
8
+ description?: string;
9
+ areas: { name: string; value: string }[];
10
+ site: string;
11
+ createdAt: string;
12
+ updatedAt: string;
13
+ };
14
+
15
+ declare type TScheduleTaskForm = Pick<
16
+ TScheduleTask,
17
+ "title" | "startDate" | "endDate" | "time" | "description" | "areas"
18
+ >;
@@ -0,0 +1,16 @@
1
+ declare type TStock = {
2
+ _id?: string;
3
+ site: string;
4
+ supply: string;
5
+ in?: number;
6
+ out?: number;
7
+ balance: number;
8
+ qty: number;
9
+ remarks?: string;
10
+ status?: string;
11
+ createdAt?: string;
12
+ updatedAt?: string;
13
+ deletedAt?: string;
14
+ };
15
+
16
+ declare type TStockCreate = Pick<TStock, "qty" | "remarks">;
@@ -0,0 +1,11 @@
1
+ declare type TSupply = {
2
+ _id: string;
3
+ name: string;
4
+ qty: number;
5
+ status: string;
6
+ unitOfMeasurement: string;
7
+ createdAt: string;
8
+ updatedAt: string;
9
+ };
10
+
11
+ declare type TSupplyCreate = Pick<TSupply, "name" | "unitOfMeasurement">;
@@ -0,0 +1,30 @@
1
+ import crypto from "crypto";
2
+
3
+ const ALGORITHM = "aes-256-gcm";
4
+ const IV_LENGTH = 12;
5
+ const SECRET_KEY = crypto.createSecretKey(
6
+ new Uint8Array(crypto.createHash("sha256").update("acm-secret-key").digest())
7
+ );
8
+
9
+ // encrypting entrypass url for security purpose
10
+ export function encryptAcmUrl(entrypassUrl: string) {
11
+ const iv = crypto.randomBytes(IV_LENGTH);
12
+ const cipher = crypto.createCipheriv(
13
+ ALGORITHM,
14
+ SECRET_KEY,
15
+ new Uint8Array(iv)
16
+ );
17
+
18
+ const encrypted = Buffer.concat([
19
+ cipher.update(entrypassUrl, "utf8"),
20
+ cipher.final() as any,
21
+ ]);
22
+
23
+ const authTag = cipher.getAuthTag();
24
+
25
+ return [
26
+ iv.toString("base64"),
27
+ authTag.toString("base64"),
28
+ encrypted.toString("base64"),
29
+ ].join(":");
30
+ }