washday-sdk 1.6.62 → 1.6.63

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.
@@ -9,9 +9,52 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
9
9
  };
10
10
  const ATTENDANCE_API = 'api/attendance';
11
11
  const ATTENDANCE_SOURCES = ["web_pos", "pos_v2", "unknown"];
12
+ const LOCATION_PERMISSION_STATUSES = [
13
+ "granted",
14
+ "denied",
15
+ "unavailable",
16
+ "not_requested",
17
+ "error",
18
+ ];
12
19
  const stringValue = (value) => (typeof value === "string" && value.trim() ? value.trim() : undefined);
13
20
  const numberValue = (value) => (typeof value === "number" && Number.isFinite(value) ? value : undefined);
14
21
  const sourceValue = (value) => (ATTENDANCE_SOURCES.includes(value) ? value : undefined);
22
+ const permissionStatusValue = (value) => (LOCATION_PERMISSION_STATUSES.includes(value)
23
+ ? value
24
+ : undefined);
25
+ const dateValue = (value) => {
26
+ if (value instanceof Date && !Number.isNaN(value.getTime()))
27
+ return value;
28
+ if (typeof value !== "string")
29
+ return undefined;
30
+ return Number.isNaN(new Date(value).getTime()) ? undefined : value;
31
+ };
32
+ const buildSafeAttendanceLocation = (location) => {
33
+ if (!location)
34
+ return undefined;
35
+ const permissionStatus = permissionStatusValue(location.permissionStatus);
36
+ if (!permissionStatus)
37
+ return undefined;
38
+ const safeLocation = {
39
+ permissionStatus,
40
+ capturedAt: dateValue(location.capturedAt),
41
+ errorCode: stringValue(location.errorCode),
42
+ errorMessage: stringValue(location.errorMessage),
43
+ };
44
+ if (permissionStatus === "granted") {
45
+ safeLocation.latitude = numberValue(location.latitude);
46
+ safeLocation.longitude = numberValue(location.longitude);
47
+ safeLocation.accuracy = numberValue(location.accuracy);
48
+ safeLocation.altitude = numberValue(location.altitude);
49
+ safeLocation.altitudeAccuracy = numberValue(location.altitudeAccuracy);
50
+ safeLocation.heading = numberValue(location.heading);
51
+ safeLocation.speed = numberValue(location.speed);
52
+ }
53
+ const filteredEntries = Object.entries(safeLocation).filter(([, value]) => value !== undefined);
54
+ return filteredEntries.length
55
+ ? Object.fromEntries(filteredEntries)
56
+ : undefined;
57
+ };
15
58
  const buildSafeAttendanceMetadata = (metadata) => {
16
59
  if (!metadata)
17
60
  return undefined;
@@ -24,6 +67,7 @@ const buildSafeAttendanceMetadata = (metadata) => {
24
67
  timezone: stringValue(metadata.timezone),
25
68
  screenWidth: numberValue(metadata.screenWidth),
26
69
  screenHeight: numberValue(metadata.screenHeight),
70
+ location: buildSafeAttendanceLocation(metadata.location),
27
71
  };
28
72
  const filteredEntries = Object.entries(safeMetadata).filter(([, value]) => value !== undefined);
29
73
  return filteredEntries.length
@@ -14,7 +14,8 @@ export const updateById = function (id, data) {
14
14
  const config = {
15
15
  headers: { Authorization: `Bearer ${this.apiToken}` }
16
16
  };
17
- return yield this.axiosInstance.put(`${ATTENDANCE_API}/${id}`, data, config);
17
+ const { timestamp, notes, reason } = data;
18
+ return yield this.axiosInstance.put(`${ATTENDANCE_API}/${id}`, Object.assign(Object.assign(Object.assign({}, (timestamp !== undefined ? { timestamp } : {})), (notes !== undefined ? { notes } : {})), { reason }), config);
18
19
  }
19
20
  catch (error) {
20
21
  console.error('Error updating attendance entry:', error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "washday-sdk",
3
- "version": "1.6.62",
3
+ "version": "1.6.63",
4
4
  "description": "Washday utilities functions and API",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -1,6 +1,8 @@
1
1
  import { WashdayClientInstance } from "../../interfaces/Api";
2
2
  import type {
3
3
  AttendanceClientMetadata,
4
+ AttendanceLocationMetadata,
5
+ AttendanceLocationPermissionStatus,
4
6
  AttendanceSource,
5
7
  ClockInRequest,
6
8
  ClockOutRequest,
@@ -8,6 +10,13 @@ import type {
8
10
 
9
11
  const ATTENDANCE_API = 'api/attendance';
10
12
  const ATTENDANCE_SOURCES: AttendanceSource[] = ["web_pos", "pos_v2", "unknown"];
13
+ const LOCATION_PERMISSION_STATUSES: AttendanceLocationPermissionStatus[] = [
14
+ "granted",
15
+ "denied",
16
+ "unavailable",
17
+ "not_requested",
18
+ "error",
19
+ ];
11
20
 
12
21
  const stringValue = (value: unknown): string | undefined => (
13
22
  typeof value === "string" && value.trim() ? value.trim() : undefined
@@ -21,6 +30,52 @@ const sourceValue = (value: unknown): AttendanceSource | undefined => (
21
30
  ATTENDANCE_SOURCES.includes(value as AttendanceSource) ? value as AttendanceSource : undefined
22
31
  );
23
32
 
33
+ const permissionStatusValue = (value: unknown): AttendanceLocationPermissionStatus | undefined => (
34
+ LOCATION_PERMISSION_STATUSES.includes(value as AttendanceLocationPermissionStatus)
35
+ ? value as AttendanceLocationPermissionStatus
36
+ : undefined
37
+ );
38
+
39
+ const dateValue = (value: unknown): string | Date | undefined => {
40
+ if (value instanceof Date && !Number.isNaN(value.getTime())) return value;
41
+ if (typeof value !== "string") return undefined;
42
+ return Number.isNaN(new Date(value).getTime()) ? undefined : value;
43
+ };
44
+
45
+ const buildSafeAttendanceLocation = (
46
+ location?: AttendanceLocationMetadata & Record<string, unknown>
47
+ ): AttendanceLocationMetadata | undefined => {
48
+ if (!location) return undefined;
49
+
50
+ const permissionStatus = permissionStatusValue(location.permissionStatus);
51
+ if (!permissionStatus) return undefined;
52
+
53
+ const safeLocation: AttendanceLocationMetadata = {
54
+ permissionStatus,
55
+ capturedAt: dateValue(location.capturedAt),
56
+ errorCode: stringValue(location.errorCode),
57
+ errorMessage: stringValue(location.errorMessage),
58
+ };
59
+
60
+ if (permissionStatus === "granted") {
61
+ safeLocation.latitude = numberValue(location.latitude);
62
+ safeLocation.longitude = numberValue(location.longitude);
63
+ safeLocation.accuracy = numberValue(location.accuracy);
64
+ safeLocation.altitude = numberValue(location.altitude);
65
+ safeLocation.altitudeAccuracy = numberValue(location.altitudeAccuracy);
66
+ safeLocation.heading = numberValue(location.heading);
67
+ safeLocation.speed = numberValue(location.speed);
68
+ }
69
+
70
+ const filteredEntries = Object.entries(safeLocation).filter(
71
+ ([, value]) => value !== undefined
72
+ );
73
+
74
+ return filteredEntries.length
75
+ ? Object.fromEntries(filteredEntries) as AttendanceLocationMetadata
76
+ : undefined;
77
+ };
78
+
24
79
  const buildSafeAttendanceMetadata = (
25
80
  metadata?: AttendanceClientMetadata & Record<string, unknown>
26
81
  ): AttendanceClientMetadata | undefined => {
@@ -35,6 +90,7 @@ const buildSafeAttendanceMetadata = (
35
90
  timezone: stringValue(metadata.timezone),
36
91
  screenWidth: numberValue(metadata.screenWidth),
37
92
  screenHeight: numberValue(metadata.screenHeight),
93
+ location: buildSafeAttendanceLocation(metadata.location as any),
38
94
  };
39
95
 
40
96
  const filteredEntries = Object.entries(safeMetadata).filter(
@@ -1,20 +1,27 @@
1
1
  import { WashdayClientInstance } from "../../interfaces/Api";
2
- import axiosInstance from "../axiosInstance";
3
2
 
4
3
  const ATTENDANCE_API = 'api/attendance';
5
4
 
6
- export const updateById = async function (this: WashdayClientInstance, id: string, data: {
5
+ export interface UpdateAttendanceEntryRequest {
7
6
  timestamp?: string;
8
7
  notes?: string;
8
+ reason: string;
9
9
  editorId?: string;
10
- }): Promise<any> {
10
+ }
11
+
12
+ export const updateById = async function (this: WashdayClientInstance, id: string, data: UpdateAttendanceEntryRequest): Promise<any> {
11
13
  try {
12
14
  const config = {
13
15
  headers: { Authorization: `Bearer ${this.apiToken}` }
14
16
  };
15
- return await this.axiosInstance.put(`${ATTENDANCE_API}/${id}`, data, config);
17
+ const { timestamp, notes, reason } = data;
18
+ return await this.axiosInstance.put(`${ATTENDANCE_API}/${id}`, {
19
+ ...(timestamp !== undefined ? { timestamp } : {}),
20
+ ...(notes !== undefined ? { notes } : {}),
21
+ reason,
22
+ }, config);
16
23
  } catch (error) {
17
24
  console.error('Error updating attendance entry:', error);
18
25
  throw error;
19
26
  }
20
- };
27
+ };
@@ -3,6 +3,27 @@ import { IStore } from "./Store";
3
3
 
4
4
  export type AttendanceSource = "web_pos" | "pos_v2" | "unknown";
5
5
 
6
+ export type AttendanceLocationPermissionStatus =
7
+ | "granted"
8
+ | "denied"
9
+ | "unavailable"
10
+ | "not_requested"
11
+ | "error";
12
+
13
+ export interface AttendanceLocationMetadata {
14
+ latitude?: number;
15
+ longitude?: number;
16
+ accuracy?: number;
17
+ altitude?: number;
18
+ altitudeAccuracy?: number;
19
+ heading?: number;
20
+ speed?: number;
21
+ permissionStatus?: AttendanceLocationPermissionStatus;
22
+ capturedAt?: string | Date;
23
+ errorCode?: string;
24
+ errorMessage?: string;
25
+ }
26
+
6
27
  export interface AttendanceClientMetadata {
7
28
  platform?: string;
8
29
  appVersion?: string;
@@ -12,6 +33,7 @@ export interface AttendanceClientMetadata {
12
33
  timezone?: string;
13
34
  screenWidth?: number;
14
35
  screenHeight?: number;
36
+ location?: AttendanceLocationMetadata;
15
37
  }
16
38
 
17
39
  export interface AttendanceServerMetadata extends AttendanceClientMetadata {
@@ -46,6 +68,8 @@ export interface IAttendanceRecord {
46
68
  notes?: string;
47
69
  editedBy?: IUser | string;
48
70
  editedAt?: Date;
71
+ lastEditReason?: string;
72
+ revision?: number;
49
73
  autoClosed?: boolean;
50
74
  createdAt?: Date;
51
75
  updatedAt?: Date;
@@ -64,6 +88,36 @@ export interface IAttendanceStatus {
64
88
  clockInStore?: IStore | string;
65
89
  }
66
90
 
91
+ export interface AttendanceShiftDuration {
92
+ hours: number;
93
+ minutes: number;
94
+ totalMinutes: number;
95
+ }
96
+
97
+ export interface AttendanceReportTableInfoRow {
98
+ _id?: string;
99
+ employee: IUser | string | {
100
+ _id?: string;
101
+ name?: string;
102
+ email?: string;
103
+ };
104
+ clockInTime?: Date | string;
105
+ clockOutTime?: Date | string | null;
106
+ shiftDuration?: AttendanceShiftDuration | null;
107
+ clockInStore?: string;
108
+ clockOutStore?: string | null;
109
+ notes?: string;
110
+ clockInIpAddress?: string;
111
+ clockOutIpAddress?: string;
112
+ clockInMetadata?: AttendanceServerMetadata | null;
113
+ clockOutMetadata?: AttendanceServerMetadata | null;
114
+ clockInLocation?: AttendanceLocationMetadata | null;
115
+ clockOutLocation?: AttendanceLocationMetadata | null;
116
+ clockInLocationSummary?: string;
117
+ clockOutLocationSummary?: string;
118
+ autoClosed?: boolean;
119
+ }
120
+
67
121
  export interface IAttendanceReport {
68
122
  storeId: string;
69
123
  storeName?: string;
@@ -77,4 +131,12 @@ export interface IAttendanceReport {
77
131
  startDate: Date;
78
132
  endDate: Date;
79
133
  };
134
+ tableInfo?: AttendanceReportTableInfoRow[];
135
+ totalRows?: number;
136
+ pagination?: {
137
+ currentPage: number;
138
+ totalPages: number;
139
+ totalRecords: number;
140
+ limit?: number;
141
+ };
80
142
  }
@@ -13,6 +13,7 @@ export interface IPermissionMutationType {
13
13
 
14
14
  export interface IPermission {
15
15
  canEditOrders: IPermissionMutationType,
16
+ canManageAttendance: IPermissionMutationType,
16
17
  mutateOrderPayments: IPermissionMutationType,
17
18
  markOrderAsCollectedWithPendingAmount: IPermissionMutationType,
18
19
  markOrderAsUncollected: IPermissionMutationType,
@@ -18,6 +18,14 @@ describe("attendance metadata requests", () => {
18
18
  timezone: "America/Mexico_City",
19
19
  screenWidth: 1440,
20
20
  screenHeight: 900,
21
+ location: {
22
+ permissionStatus: "granted",
23
+ latitude: 19.4326,
24
+ longitude: -99.1332,
25
+ accuracy: 24,
26
+ capturedAt: "2026-05-26T14:00:00.000Z",
27
+ unknown: "drop-me",
28
+ },
21
29
  ipAddress: "1.1.1.1",
22
30
  cfConnectingIp: "1.1.1.2",
23
31
  xForwardedFor: "1.1.1.3",
@@ -36,6 +44,13 @@ describe("attendance metadata requests", () => {
36
44
  timezone: "America/Mexico_City",
37
45
  screenWidth: 1440,
38
46
  screenHeight: 900,
47
+ location: {
48
+ permissionStatus: "granted",
49
+ latitude: 19.4326,
50
+ longitude: -99.1332,
51
+ accuracy: 24,
52
+ capturedAt: "2026-05-26T14:00:00.000Z",
53
+ },
39
54
  },
40
55
  },
41
56
  {
@@ -59,6 +74,15 @@ describe("attendance metadata requests", () => {
59
74
  platform: "ios",
60
75
  appVersion: "2.5.0",
61
76
  deviceId: "device-1",
77
+ location: {
78
+ permissionStatus: "denied",
79
+ latitude: 19.4326,
80
+ longitude: -99.1332,
81
+ accuracy: 24,
82
+ capturedAt: "2026-05-26T14:00:00.000Z",
83
+ errorCode: "1",
84
+ errorMessage: "Permission denied",
85
+ },
62
86
  ipAddress: "1.1.1.1",
63
87
  },
64
88
  } as any);
@@ -73,6 +97,12 @@ describe("attendance metadata requests", () => {
73
97
  platform: "ios",
74
98
  appVersion: "2.5.0",
75
99
  deviceId: "device-1",
100
+ location: {
101
+ permissionStatus: "denied",
102
+ capturedAt: "2026-05-26T14:00:00.000Z",
103
+ errorCode: "1",
104
+ errorMessage: "Permission denied",
105
+ },
76
106
  },
77
107
  },
78
108
  {
@@ -0,0 +1,30 @@
1
+ import { updateById } from "../src/api/attendance/put";
2
+
3
+ describe("attendance update requests", () => {
4
+ it("sends reason with editable attendance fields", async () => {
5
+ const put = jest.fn().mockResolvedValue({ data: { data: {} } });
6
+ const client = {
7
+ apiToken: "token-123",
8
+ axiosInstance: { put },
9
+ } as any;
10
+
11
+ await updateById.call(client, "entry-1", {
12
+ timestamp: "2026-05-26T14:00:00.000Z",
13
+ notes: "Corrected note",
14
+ reason: "El empleado olvidó marcar a tiempo",
15
+ editorId: "ignored-by-backend",
16
+ } as any);
17
+
18
+ expect(put).toHaveBeenCalledWith(
19
+ "api/attendance/entry-1",
20
+ {
21
+ timestamp: "2026-05-26T14:00:00.000Z",
22
+ notes: "Corrected note",
23
+ reason: "El empleado olvidó marcar a tiempo",
24
+ },
25
+ {
26
+ headers: { Authorization: "Bearer token-123" },
27
+ },
28
+ );
29
+ });
30
+ });