@passgage/sdk-react-native 1.0.4 → 1.0.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/README.md +409 -147
- package/dist/index.d.mts +133 -423
- package/dist/index.d.ts +133 -423
- package/dist/index.js +929 -1196
- package/dist/index.mjs +867 -1131
- package/package.json +8 -4
package/dist/index.mjs
CHANGED
|
@@ -1,11 +1,62 @@
|
|
|
1
|
+
import axios from 'axios';
|
|
2
|
+
import React, { createContext, useEffect, useContext, useState, useCallback } from 'react';
|
|
3
|
+
import { create } from 'zustand';
|
|
4
|
+
import * as Keychain from 'react-native-keychain';
|
|
5
|
+
import Geolocation2 from 'react-native-geolocation-service';
|
|
6
|
+
import RNPermissions, { PERMISSIONS, RESULTS, request } from 'react-native-permissions';
|
|
7
|
+
import { Platform } from 'react-native';
|
|
8
|
+
import NfcManager, { NfcTech } from 'react-native-nfc-manager';
|
|
9
|
+
|
|
1
10
|
var __defProp = Object.defineProperty;
|
|
11
|
+
var __require = /* @__PURE__ */ ((x) => typeof require !== "undefined" ? require : typeof Proxy !== "undefined" ? new Proxy(x, {
|
|
12
|
+
get: (a, b) => (typeof require !== "undefined" ? require : a)[b]
|
|
13
|
+
}) : x)(function(x) {
|
|
14
|
+
if (typeof require !== "undefined") return require.apply(this, arguments);
|
|
15
|
+
throw Error('Dynamic require of "' + x + '" is not supported');
|
|
16
|
+
});
|
|
2
17
|
var __export = (target, all) => {
|
|
3
18
|
for (var name in all)
|
|
4
19
|
__defProp(target, name, { get: all[name], enumerable: true });
|
|
5
20
|
};
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
21
|
+
var httpClient = axios.create();
|
|
22
|
+
var createHttpClient = ({
|
|
23
|
+
baseURL,
|
|
24
|
+
apiVersion,
|
|
25
|
+
timeout = 3e3
|
|
26
|
+
}) => {
|
|
27
|
+
httpClient = axios.create({
|
|
28
|
+
baseURL: `${baseURL}/api/${apiVersion}`,
|
|
29
|
+
timeout,
|
|
30
|
+
headers: {
|
|
31
|
+
"Content-Type": "application/json",
|
|
32
|
+
Accept: "application/json"
|
|
33
|
+
}
|
|
34
|
+
});
|
|
35
|
+
httpClient.interceptors.request.use(
|
|
36
|
+
(config) => {
|
|
37
|
+
return config;
|
|
38
|
+
},
|
|
39
|
+
(error) => {
|
|
40
|
+
return Promise.reject(error);
|
|
41
|
+
}
|
|
42
|
+
);
|
|
43
|
+
httpClient.interceptors.response.use(
|
|
44
|
+
(response) => {
|
|
45
|
+
const originalRequest = response.config;
|
|
46
|
+
if (response.status === 401 && originalRequest) {
|
|
47
|
+
return new Promise(async (resolve, _reject) => {
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
return response.data;
|
|
51
|
+
},
|
|
52
|
+
(err) => {
|
|
53
|
+
return Promise.reject(err);
|
|
54
|
+
}
|
|
55
|
+
);
|
|
56
|
+
};
|
|
57
|
+
var setTokenToHttpClient = (token) => {
|
|
58
|
+
httpClient.defaults.headers["Authorization"] = token ? `Bearer ${token}` : "";
|
|
59
|
+
};
|
|
9
60
|
var ApiClient = class {
|
|
10
61
|
constructor(config) {
|
|
11
62
|
this.isRefreshing = false;
|
|
@@ -166,32 +217,24 @@ var ApiClient = class {
|
|
|
166
217
|
* Generic PUT request
|
|
167
218
|
*/
|
|
168
219
|
async put(options) {
|
|
169
|
-
const response = await this.axiosInstance.put(
|
|
170
|
-
options.
|
|
171
|
-
options.
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
...options.config,
|
|
176
|
-
skipAuth: options.skipAuth
|
|
177
|
-
}
|
|
178
|
-
);
|
|
220
|
+
const response = await this.axiosInstance.put(options.endpoint, options.data, {
|
|
221
|
+
params: options.params,
|
|
222
|
+
headers: options.headers,
|
|
223
|
+
...options.config,
|
|
224
|
+
skipAuth: options.skipAuth
|
|
225
|
+
});
|
|
179
226
|
return response.data;
|
|
180
227
|
}
|
|
181
228
|
/**
|
|
182
229
|
* Generic PATCH request
|
|
183
230
|
*/
|
|
184
231
|
async patch(options) {
|
|
185
|
-
const response = await this.axiosInstance.patch(
|
|
186
|
-
options.
|
|
187
|
-
options.
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
...options.config,
|
|
192
|
-
skipAuth: options.skipAuth
|
|
193
|
-
}
|
|
194
|
-
);
|
|
232
|
+
const response = await this.axiosInstance.patch(options.endpoint, options.data, {
|
|
233
|
+
params: options.params,
|
|
234
|
+
headers: options.headers,
|
|
235
|
+
...options.config,
|
|
236
|
+
skipAuth: options.skipAuth
|
|
237
|
+
});
|
|
195
238
|
return response.data;
|
|
196
239
|
}
|
|
197
240
|
/**
|
|
@@ -214,6 +257,7 @@ function createApiClient(config) {
|
|
|
214
257
|
// src/api/endpoints.ts
|
|
215
258
|
var endpoints_exports = {};
|
|
216
259
|
__export(endpoints_exports, {
|
|
260
|
+
EP_AZURE_AUTH: () => EP_AZURE_AUTH,
|
|
217
261
|
EP_BRANCHES: () => EP_BRANCHES,
|
|
218
262
|
EP_CREATE_QR: () => EP_CREATE_QR,
|
|
219
263
|
EP_DEVICES: () => EP_DEVICES,
|
|
@@ -231,6 +275,7 @@ __export(endpoints_exports, {
|
|
|
231
275
|
});
|
|
232
276
|
var EP_LOGIN = "users/sign_in";
|
|
233
277
|
var EP_TOKEN = "token";
|
|
278
|
+
var EP_AZURE_AUTH = "auth/azure";
|
|
234
279
|
var EP_DEVICES = "devices";
|
|
235
280
|
var EP_QR_DEVICES = "devices/qr_devices";
|
|
236
281
|
var EP_QR_ACCESS = "qr_access/accessible_qrs";
|
|
@@ -264,17 +309,7 @@ var DeviceDirection = /* @__PURE__ */ ((DeviceDirection2) => {
|
|
|
264
309
|
})(DeviceDirection || {});
|
|
265
310
|
|
|
266
311
|
// src/services/AuthService.ts
|
|
267
|
-
var AuthService =
|
|
268
|
-
constructor(apiClient) {
|
|
269
|
-
this.apiClient = apiClient;
|
|
270
|
-
}
|
|
271
|
-
/**
|
|
272
|
-
* Set token storage implementation
|
|
273
|
-
* This is used by platform-specific implementations (React Native, Android, iOS)
|
|
274
|
-
*/
|
|
275
|
-
setTokenStorage(storage) {
|
|
276
|
-
this.tokenStorage = storage;
|
|
277
|
-
}
|
|
312
|
+
var AuthService = {
|
|
278
313
|
/**
|
|
279
314
|
* Login with credentials
|
|
280
315
|
*
|
|
@@ -295,7 +330,7 @@ var AuthService = class {
|
|
|
295
330
|
* }
|
|
296
331
|
* ```
|
|
297
332
|
*/
|
|
298
|
-
async
|
|
333
|
+
login: async (credentials) => {
|
|
299
334
|
try {
|
|
300
335
|
const requestBody = {
|
|
301
336
|
user: {
|
|
@@ -303,12 +338,7 @@ var AuthService = class {
|
|
|
303
338
|
password: credentials.password
|
|
304
339
|
}
|
|
305
340
|
};
|
|
306
|
-
const response = await
|
|
307
|
-
endpoint: "/api/v2/users/sign_in",
|
|
308
|
-
data: requestBody,
|
|
309
|
-
skipAuth: true
|
|
310
|
-
// Don't send auth header for login
|
|
311
|
-
});
|
|
341
|
+
const response = await httpClient.post(EP_LOGIN, requestBody);
|
|
312
342
|
if (!response.success || !response.data?.tokens) {
|
|
313
343
|
return {
|
|
314
344
|
success: false,
|
|
@@ -317,18 +347,12 @@ var AuthService = class {
|
|
|
317
347
|
};
|
|
318
348
|
}
|
|
319
349
|
const tokens = response.data.tokens;
|
|
320
|
-
|
|
321
|
-
await this.tokenStorage.saveTokens(tokens);
|
|
322
|
-
}
|
|
323
|
-
this.apiClient.setToken(tokens.access.token);
|
|
350
|
+
setTokenToHttpClient(tokens.access.token);
|
|
324
351
|
let user;
|
|
325
352
|
try {
|
|
326
|
-
const userResult = await
|
|
353
|
+
const userResult = await AuthService.getCurrentUser();
|
|
327
354
|
if (userResult.success) {
|
|
328
355
|
user = userResult.user;
|
|
329
|
-
if (this.tokenStorage) {
|
|
330
|
-
await this.tokenStorage.saveUser(user);
|
|
331
|
-
}
|
|
332
356
|
}
|
|
333
357
|
} catch (error) {
|
|
334
358
|
console.warn("Failed to fetch user info after login:", error);
|
|
@@ -345,7 +369,68 @@ var AuthService = class {
|
|
|
345
369
|
code: error.code || "NETWORK_ERROR"
|
|
346
370
|
};
|
|
347
371
|
}
|
|
348
|
-
}
|
|
372
|
+
},
|
|
373
|
+
/**
|
|
374
|
+
* Login with Azure AD token
|
|
375
|
+
*
|
|
376
|
+
* @param credentials - Azure AD login credentials (id_token and optional device_type)
|
|
377
|
+
* @returns Login result with tokens on success
|
|
378
|
+
*
|
|
379
|
+
* @example
|
|
380
|
+
* ```typescript
|
|
381
|
+
* const result = await authService.loginWithAzure({
|
|
382
|
+
* id_token: 'azure_id_token_here',
|
|
383
|
+
* device_type: 'ios'
|
|
384
|
+
* });
|
|
385
|
+
*
|
|
386
|
+
* if (result.success) {
|
|
387
|
+
* console.log('Access token:', result.tokens.access.token);
|
|
388
|
+
* } else {
|
|
389
|
+
* console.error('Azure login failed:', result.error);
|
|
390
|
+
* }
|
|
391
|
+
* ```
|
|
392
|
+
*/
|
|
393
|
+
loginWithAzure: async (credentials) => {
|
|
394
|
+
try {
|
|
395
|
+
const response = await httpClient.post(
|
|
396
|
+
EP_AZURE_AUTH,
|
|
397
|
+
{
|
|
398
|
+
id_token: credentials.id_token,
|
|
399
|
+
device_type: credentials.device_type || "android"
|
|
400
|
+
},
|
|
401
|
+
{ skipAuth: true }
|
|
402
|
+
);
|
|
403
|
+
if (!response.success || !response.data?.tokens) {
|
|
404
|
+
return {
|
|
405
|
+
success: false,
|
|
406
|
+
error: response.message || "Azure login failed",
|
|
407
|
+
code: "AZURE_LOGIN_FAILED"
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
const tokens = response.data.tokens;
|
|
411
|
+
setTokenToHttpClient(tokens.access.token);
|
|
412
|
+
let user;
|
|
413
|
+
try {
|
|
414
|
+
const userResult = await AuthService.getCurrentUser();
|
|
415
|
+
if (userResult.success) {
|
|
416
|
+
user = userResult.user;
|
|
417
|
+
}
|
|
418
|
+
} catch (error) {
|
|
419
|
+
console.warn("Failed to fetch user info after Azure login:", error);
|
|
420
|
+
}
|
|
421
|
+
return {
|
|
422
|
+
success: true,
|
|
423
|
+
tokens,
|
|
424
|
+
user
|
|
425
|
+
};
|
|
426
|
+
} catch (error) {
|
|
427
|
+
return {
|
|
428
|
+
success: false,
|
|
429
|
+
error: error.message || "An error occurred during Azure login",
|
|
430
|
+
code: error.code || "NETWORK_ERROR"
|
|
431
|
+
};
|
|
432
|
+
}
|
|
433
|
+
},
|
|
349
434
|
/**
|
|
350
435
|
* Refresh access token using refresh token
|
|
351
436
|
*
|
|
@@ -361,16 +446,18 @@ var AuthService = class {
|
|
|
361
446
|
* }
|
|
362
447
|
* ```
|
|
363
448
|
*/
|
|
364
|
-
async
|
|
449
|
+
refreshToken: async (refreshToken) => {
|
|
365
450
|
try {
|
|
366
451
|
const requestBody = {
|
|
367
452
|
refresh_token: refreshToken
|
|
368
453
|
};
|
|
369
|
-
const response = await
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
454
|
+
const response = await httpClient.post(
|
|
455
|
+
"/token",
|
|
456
|
+
{
|
|
457
|
+
data: requestBody
|
|
458
|
+
},
|
|
459
|
+
{ skipAuth: true }
|
|
460
|
+
);
|
|
374
461
|
if (!response.success || !response.data?.token || !response.data?.refresh_token) {
|
|
375
462
|
return {
|
|
376
463
|
success: false,
|
|
@@ -380,18 +467,14 @@ var AuthService = class {
|
|
|
380
467
|
}
|
|
381
468
|
const tokens = {
|
|
382
469
|
access: {
|
|
383
|
-
token: response.data.token,
|
|
470
|
+
token: response.data.token ?? "",
|
|
384
471
|
expiresIn: "30 days"
|
|
385
472
|
},
|
|
386
473
|
refresh: {
|
|
387
|
-
token: response.data.refresh_token,
|
|
474
|
+
token: response.data.refresh_token ?? "",
|
|
388
475
|
expiresIn: "30 days"
|
|
389
476
|
}
|
|
390
477
|
};
|
|
391
|
-
if (this.tokenStorage) {
|
|
392
|
-
await this.tokenStorage.saveTokens(tokens);
|
|
393
|
-
}
|
|
394
|
-
this.apiClient.setToken(tokens.access.token);
|
|
395
478
|
return {
|
|
396
479
|
success: true,
|
|
397
480
|
tokens
|
|
@@ -403,7 +486,7 @@ var AuthService = class {
|
|
|
403
486
|
code: error.code || "NETWORK_ERROR"
|
|
404
487
|
};
|
|
405
488
|
}
|
|
406
|
-
}
|
|
489
|
+
},
|
|
407
490
|
/**
|
|
408
491
|
* Get current user information
|
|
409
492
|
*
|
|
@@ -418,12 +501,10 @@ var AuthService = class {
|
|
|
418
501
|
* }
|
|
419
502
|
* ```
|
|
420
503
|
*/
|
|
421
|
-
async
|
|
504
|
+
getCurrentUser: async () => {
|
|
422
505
|
try {
|
|
423
|
-
const response = await
|
|
424
|
-
|
|
425
|
-
});
|
|
426
|
-
if (!response.success || !response.data) {
|
|
506
|
+
const response = await httpClient.get(EP_ME);
|
|
507
|
+
if (!response || !response.data) {
|
|
427
508
|
return {
|
|
428
509
|
success: false,
|
|
429
510
|
error: response.message || "Failed to fetch user information"
|
|
@@ -457,69 +538,40 @@ var AuthService = class {
|
|
|
457
538
|
};
|
|
458
539
|
}
|
|
459
540
|
}
|
|
541
|
+
};
|
|
542
|
+
|
|
543
|
+
// src/services/QRAccessService.ts
|
|
544
|
+
var QRAccessService = {
|
|
460
545
|
/**
|
|
461
|
-
*
|
|
462
|
-
* Clears tokens from storage and API client
|
|
463
|
-
*
|
|
464
|
-
* @example
|
|
465
|
-
* ```typescript
|
|
466
|
-
* await authService.logout();
|
|
467
|
-
* console.log('User logged out');
|
|
468
|
-
* ```
|
|
469
|
-
*/
|
|
470
|
-
async logout() {
|
|
471
|
-
if (this.tokenStorage) {
|
|
472
|
-
await this.tokenStorage.clearTokens();
|
|
473
|
-
await this.tokenStorage.clearUser();
|
|
474
|
-
}
|
|
475
|
-
this.apiClient.setToken(null);
|
|
476
|
-
}
|
|
477
|
-
/**
|
|
478
|
-
* Check if user is authenticated
|
|
479
|
-
*
|
|
480
|
-
* @returns True if user has valid tokens
|
|
481
|
-
*/
|
|
482
|
-
async isAuthenticated() {
|
|
483
|
-
if (!this.tokenStorage) {
|
|
484
|
-
return false;
|
|
485
|
-
}
|
|
486
|
-
const tokens = await this.tokenStorage.getTokens();
|
|
487
|
-
return tokens !== null && tokens.access.token.length > 0;
|
|
488
|
-
}
|
|
489
|
-
/**
|
|
490
|
-
* Get stored tokens
|
|
491
|
-
*
|
|
492
|
-
* @returns Stored tokens or null
|
|
546
|
+
* Trigger IoT device
|
|
493
547
|
*/
|
|
494
|
-
async
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
}
|
|
548
|
+
triggerIoTDevice: async (deviceId, config) => {
|
|
549
|
+
return httpClient.post(
|
|
550
|
+
`${EP_TRIGGER_IOT}/${deviceId}`,
|
|
551
|
+
config
|
|
552
|
+
);
|
|
553
|
+
},
|
|
500
554
|
/**
|
|
501
|
-
*
|
|
502
|
-
*
|
|
503
|
-
* @returns Stored user or null
|
|
555
|
+
* Create entrance from QR code
|
|
504
556
|
*/
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
557
|
+
createEntranceFromQR: (request2) => {
|
|
558
|
+
return httpClient.post(
|
|
559
|
+
EP_CREATE_QR,
|
|
560
|
+
request2
|
|
561
|
+
);
|
|
510
562
|
}
|
|
511
563
|
};
|
|
512
564
|
|
|
513
565
|
// src/utils/location.ts
|
|
514
566
|
function calculateDistance(lat1, lon1, lat2, lon2) {
|
|
515
|
-
|
|
516
|
-
const
|
|
517
|
-
const
|
|
518
|
-
const
|
|
519
|
-
const
|
|
520
|
-
const a = Math.sin(
|
|
567
|
+
let earthRadiusKm = 6371;
|
|
568
|
+
const lat1Rad = lat1 * Math.PI / 180;
|
|
569
|
+
const lat2Rad = lat2 * Math.PI / 180;
|
|
570
|
+
const dLat = (lat2 - lat1) * Math.PI / 180;
|
|
571
|
+
const dLon = (lon2 - lon1) * Math.PI / 180;
|
|
572
|
+
const a = Math.sin(dLat / 2) * Math.sin(dLat / 2) + Math.cos(lat1Rad) * Math.cos(lat2Rad) * Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
|
521
573
|
const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|
522
|
-
return
|
|
574
|
+
return earthRadiusKm * c;
|
|
523
575
|
}
|
|
524
576
|
function checkOnLocation(targetLat, targetLon, allowedRange, userPosition) {
|
|
525
577
|
if (!userPosition) {
|
|
@@ -545,20 +597,27 @@ function validateCoordinates(coords) {
|
|
|
545
597
|
}
|
|
546
598
|
|
|
547
599
|
// src/utils/validation.ts
|
|
548
|
-
var READ_TIMEOUT =
|
|
600
|
+
var READ_TIMEOUT = 3e4;
|
|
549
601
|
var readRecords = [];
|
|
550
602
|
function checkRepetitiveRead(code) {
|
|
551
603
|
const now = Date.now();
|
|
552
|
-
readRecords = readRecords.filter(
|
|
604
|
+
readRecords = readRecords.filter(
|
|
605
|
+
(record) => now - record.timestamp < READ_TIMEOUT
|
|
606
|
+
);
|
|
553
607
|
const recentRead = readRecords.find(
|
|
554
608
|
(record) => record.code === code && now - record.timestamp < READ_TIMEOUT
|
|
555
609
|
);
|
|
556
610
|
if (recentRead) {
|
|
557
611
|
return false;
|
|
558
612
|
}
|
|
559
|
-
readRecords.push({ code, timestamp: now });
|
|
560
613
|
return true;
|
|
561
614
|
}
|
|
615
|
+
var addQrReadRecord = (code) => {
|
|
616
|
+
const now = Date.now();
|
|
617
|
+
if (code) {
|
|
618
|
+
readRecords.push({ code, timestamp: now });
|
|
619
|
+
}
|
|
620
|
+
};
|
|
562
621
|
function clearReadRecords() {
|
|
563
622
|
readRecords = [];
|
|
564
623
|
}
|
|
@@ -572,6 +631,27 @@ function validateDeviceId(id) {
|
|
|
572
631
|
const uuidRegex = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
573
632
|
return uuidRegex.test(id);
|
|
574
633
|
}
|
|
634
|
+
var shouldValidateLocation = (device) => {
|
|
635
|
+
return device.range_matter === "1" && !!device.latitude && !!device.longitude;
|
|
636
|
+
};
|
|
637
|
+
var validateLocation = (device, userLocation) => {
|
|
638
|
+
if (!shouldValidateLocation(device)) {
|
|
639
|
+
return { valid: true };
|
|
640
|
+
}
|
|
641
|
+
if (!userLocation) {
|
|
642
|
+
return { valid: false };
|
|
643
|
+
}
|
|
644
|
+
const locationCheck = checkOnLocation(
|
|
645
|
+
device.latitude,
|
|
646
|
+
device.longitude,
|
|
647
|
+
device.range || 100,
|
|
648
|
+
userLocation
|
|
649
|
+
);
|
|
650
|
+
return {
|
|
651
|
+
valid: locationCheck.ok,
|
|
652
|
+
distance: locationCheck.distance
|
|
653
|
+
};
|
|
654
|
+
};
|
|
575
655
|
|
|
576
656
|
// src/utils/date.ts
|
|
577
657
|
function formatISO(date = /* @__PURE__ */ new Date()) {
|
|
@@ -598,442 +678,162 @@ function parseISO(isoString) {
|
|
|
598
678
|
return new Date(isoString);
|
|
599
679
|
}
|
|
600
680
|
|
|
601
|
-
// src/services/
|
|
602
|
-
var
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
}
|
|
612
|
-
/**
|
|
613
|
-
* Check if location validation is required
|
|
614
|
-
*/
|
|
615
|
-
shouldValidateLocation(device) {
|
|
616
|
-
return device.range_matter === "1" && !!device.latitude && !!device.longitude;
|
|
617
|
-
}
|
|
618
|
-
/**
|
|
619
|
-
* Validate user location against device location
|
|
620
|
-
*/
|
|
621
|
-
validateLocation(device, userLocation) {
|
|
622
|
-
if (!this.shouldValidateLocation(device)) {
|
|
681
|
+
// src/services/NFCAccessService.ts
|
|
682
|
+
var NfcAccessService = {
|
|
683
|
+
validateNFCFormat: (code) => {
|
|
684
|
+
return validateNFCCode(code);
|
|
685
|
+
},
|
|
686
|
+
shouldValidateLocation: (device) => {
|
|
687
|
+
return device?.nfc_range_matter === "1" && !!device?.latitude && !!device.longitude;
|
|
688
|
+
},
|
|
689
|
+
validateLocation: (device, userLocation) => {
|
|
690
|
+
if (!NfcAccessService.shouldValidateLocation(device)) {
|
|
623
691
|
return { valid: true };
|
|
624
692
|
}
|
|
625
693
|
if (!userLocation) {
|
|
626
694
|
return { valid: false };
|
|
627
695
|
}
|
|
628
696
|
const locationCheck = checkOnLocation(
|
|
629
|
-
device
|
|
630
|
-
device
|
|
631
|
-
device
|
|
697
|
+
device?.latitude,
|
|
698
|
+
device?.longitude,
|
|
699
|
+
device?.nfc_range || 100,
|
|
632
700
|
userLocation
|
|
633
701
|
);
|
|
634
702
|
return {
|
|
635
703
|
valid: locationCheck.ok,
|
|
636
704
|
distance: locationCheck.distance
|
|
637
705
|
};
|
|
638
|
-
}
|
|
706
|
+
},
|
|
639
707
|
/**
|
|
640
|
-
* Validate
|
|
708
|
+
* Validate NFC code with full checks
|
|
641
709
|
*/
|
|
642
|
-
async
|
|
643
|
-
const {
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
if (!skipLocationCheck) {
|
|
659
|
-
const locationValidation = this.validateLocation(device, userLocation);
|
|
660
|
-
if (!locationValidation.valid) {
|
|
710
|
+
async validateNFC(options) {
|
|
711
|
+
const { device } = options;
|
|
712
|
+
const onlineRequestBody = {
|
|
713
|
+
device_id: device.id
|
|
714
|
+
};
|
|
715
|
+
if (device.is_iot) {
|
|
716
|
+
try {
|
|
717
|
+
const enteranceQrRes = await QRAccessService.createEntranceFromQR(
|
|
718
|
+
onlineRequestBody
|
|
719
|
+
);
|
|
720
|
+
return {
|
|
721
|
+
success: !!enteranceQrRes?.success,
|
|
722
|
+
message: "QR code validated successfully",
|
|
723
|
+
entrance: enteranceQrRes.data
|
|
724
|
+
};
|
|
725
|
+
} catch (error) {
|
|
661
726
|
return {
|
|
662
727
|
success: false,
|
|
663
|
-
message:
|
|
664
|
-
error: {
|
|
665
|
-
code: "LOCATION_OUT_OF_RANGE",
|
|
666
|
-
details: locationValidation.distance ? `Distance: ${locationValidation.distance.toFixed(2)}m` : void 0
|
|
667
|
-
}
|
|
728
|
+
message: error.message || "Failed to validate QR code",
|
|
729
|
+
error: { code: "NETWORK_ERROR" }
|
|
668
730
|
};
|
|
669
731
|
}
|
|
670
|
-
}
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
732
|
+
} else {
|
|
733
|
+
try {
|
|
734
|
+
const enteranceQrRes = await QRAccessService.createEntranceFromQR(
|
|
735
|
+
onlineRequestBody
|
|
736
|
+
);
|
|
737
|
+
return {
|
|
738
|
+
success: !!enteranceQrRes?.success,
|
|
739
|
+
message: "QR code validated successfully",
|
|
740
|
+
entrance: enteranceQrRes.data
|
|
741
|
+
};
|
|
742
|
+
} catch (error) {
|
|
677
743
|
return {
|
|
678
744
|
success: false,
|
|
679
|
-
message:
|
|
680
|
-
error: { code: "
|
|
745
|
+
message: error.message || "Failed to validate QR code",
|
|
746
|
+
error: { code: "NETWORK_ERROR" }
|
|
681
747
|
};
|
|
682
748
|
}
|
|
683
|
-
return {
|
|
684
|
-
success: true,
|
|
685
|
-
message: "QR code validated successfully",
|
|
686
|
-
entrance: response.data
|
|
687
|
-
};
|
|
688
|
-
} catch (error) {
|
|
689
|
-
return {
|
|
690
|
-
success: false,
|
|
691
|
-
message: error.message || "Failed to validate QR code",
|
|
692
|
-
error: { code: "NETWORK_ERROR" }
|
|
693
|
-
};
|
|
694
749
|
}
|
|
695
750
|
}
|
|
751
|
+
};
|
|
752
|
+
|
|
753
|
+
// src/services/DeviceAccessService.ts
|
|
754
|
+
var DeviceAccessService = {
|
|
696
755
|
/**
|
|
697
|
-
*
|
|
756
|
+
* Get all devices
|
|
698
757
|
*/
|
|
699
|
-
async
|
|
700
|
-
return
|
|
701
|
-
|
|
702
|
-
data: request
|
|
758
|
+
getDevices: async (params) => {
|
|
759
|
+
return httpClient.get(EP_DEVICES, {
|
|
760
|
+
params: params || { per_page: 100 }
|
|
703
761
|
});
|
|
704
|
-
}
|
|
762
|
+
},
|
|
705
763
|
/**
|
|
706
|
-
*
|
|
764
|
+
* Get all QR devices
|
|
707
765
|
*/
|
|
708
|
-
async
|
|
709
|
-
return
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
}
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
var NFCAccessService = class {
|
|
717
|
-
constructor(apiClient) {
|
|
718
|
-
this.apiClient = apiClient;
|
|
719
|
-
}
|
|
766
|
+
getQRDevices: async (params) => {
|
|
767
|
+
return httpClient.get(
|
|
768
|
+
EP_QR_DEVICES,
|
|
769
|
+
{
|
|
770
|
+
params: params || { per_page: 100 }
|
|
771
|
+
}
|
|
772
|
+
);
|
|
773
|
+
},
|
|
720
774
|
/**
|
|
721
|
-
*
|
|
775
|
+
* Get all QR devices with pagination support
|
|
722
776
|
*/
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
777
|
+
getAllQRDevices: async () => {
|
|
778
|
+
const perPage = 50;
|
|
779
|
+
let currentPage = 1;
|
|
780
|
+
let allData = [];
|
|
781
|
+
while (true) {
|
|
782
|
+
const response = await DeviceAccessService.getQRDevices({
|
|
783
|
+
page: currentPage,
|
|
784
|
+
per_page: perPage
|
|
785
|
+
});
|
|
786
|
+
if (!response.success || !response.data) {
|
|
787
|
+
break;
|
|
788
|
+
}
|
|
789
|
+
allData = [...allData, ...response.data];
|
|
790
|
+
if (!response.meta || !response.meta.total_pages) {
|
|
791
|
+
break;
|
|
792
|
+
}
|
|
793
|
+
if (currentPage >= response.meta.total_pages) {
|
|
794
|
+
break;
|
|
795
|
+
}
|
|
796
|
+
currentPage++;
|
|
797
|
+
}
|
|
798
|
+
return allData;
|
|
799
|
+
},
|
|
726
800
|
/**
|
|
727
|
-
*
|
|
801
|
+
* Get accessible QR codes for current user
|
|
728
802
|
*/
|
|
729
|
-
|
|
730
|
-
return
|
|
731
|
-
|
|
803
|
+
getAccessibleQRs: async () => {
|
|
804
|
+
return httpClient.get(
|
|
805
|
+
EP_QR_ACCESS
|
|
806
|
+
);
|
|
807
|
+
},
|
|
732
808
|
/**
|
|
733
|
-
*
|
|
809
|
+
* Get user devices
|
|
734
810
|
*/
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
device.nfc_range || 100,
|
|
746
|
-
userLocation
|
|
811
|
+
getUserDevices: async (request2) => {
|
|
812
|
+
const { userId, page, per_page } = request2;
|
|
813
|
+
return httpClient.get(
|
|
814
|
+
`${EP_USERS}/${userId}/devices`,
|
|
815
|
+
{
|
|
816
|
+
params: {
|
|
817
|
+
...page && { page },
|
|
818
|
+
...per_page && { per_page }
|
|
819
|
+
}
|
|
820
|
+
}
|
|
747
821
|
);
|
|
748
|
-
|
|
749
|
-
valid: locationCheck.ok,
|
|
750
|
-
distance: locationCheck.distance
|
|
751
|
-
};
|
|
752
|
-
}
|
|
822
|
+
},
|
|
753
823
|
/**
|
|
754
|
-
*
|
|
824
|
+
* Get device by ID
|
|
755
825
|
*/
|
|
756
|
-
async
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
message: "Invalid NFC code format",
|
|
762
|
-
error: { code: "INVALID_NFC_FORMAT" }
|
|
763
|
-
};
|
|
764
|
-
}
|
|
765
|
-
if (!skipRepetitiveCheck && !checkRepetitiveRead(nfcCode)) {
|
|
766
|
-
return {
|
|
767
|
-
success: false,
|
|
768
|
-
message: "NFC card was recently scanned. Please wait before scanning again.",
|
|
769
|
-
error: { code: "REPETITIVE_READ" }
|
|
770
|
-
};
|
|
771
|
-
}
|
|
772
|
-
if (!skipLocationCheck) {
|
|
773
|
-
const locationValidation = this.validateLocation(device, userLocation);
|
|
774
|
-
if (!locationValidation.valid) {
|
|
775
|
-
return {
|
|
776
|
-
success: false,
|
|
777
|
-
message: "You are not within the allowed range of this device",
|
|
778
|
-
error: {
|
|
779
|
-
code: "LOCATION_OUT_OF_RANGE",
|
|
780
|
-
details: locationValidation.distance ? `Distance: ${locationValidation.distance.toFixed(2)}m` : void 0
|
|
781
|
-
}
|
|
782
|
-
};
|
|
783
|
-
}
|
|
784
|
-
}
|
|
785
|
-
try {
|
|
786
|
-
const response = await this.apiClient.post({
|
|
787
|
-
endpoint: EP_NFC_CHECK,
|
|
788
|
-
data: { nfc_code: nfcCode }
|
|
789
|
-
});
|
|
790
|
-
if (!response.success) {
|
|
791
|
-
return {
|
|
792
|
-
success: false,
|
|
793
|
-
message: response.message || "NFC validation failed",
|
|
794
|
-
error: { code: "API_ERROR" }
|
|
795
|
-
};
|
|
796
|
-
}
|
|
797
|
-
return {
|
|
798
|
-
success: true,
|
|
799
|
-
message: "NFC card validated successfully",
|
|
800
|
-
entrance: response.data
|
|
801
|
-
};
|
|
802
|
-
} catch (error) {
|
|
803
|
-
return {
|
|
804
|
-
success: false,
|
|
805
|
-
message: error.message || "Failed to validate NFC card",
|
|
806
|
-
error: { code: "NETWORK_ERROR" }
|
|
807
|
-
};
|
|
808
|
-
}
|
|
809
|
-
}
|
|
810
|
-
};
|
|
811
|
-
|
|
812
|
-
// src/services/CheckInService.ts
|
|
813
|
-
var CheckInService = class {
|
|
814
|
-
constructor(apiClient) {
|
|
815
|
-
this.apiClient = apiClient;
|
|
816
|
-
}
|
|
817
|
-
/**
|
|
818
|
-
* Get nearby branches based on user location
|
|
819
|
-
*/
|
|
820
|
-
async getNearbyBranches(request) {
|
|
821
|
-
const { latitude, longitude, radius } = request;
|
|
822
|
-
if (!validateCoordinates({ latitude, longitude })) {
|
|
823
|
-
throw new Error("Invalid coordinates provided");
|
|
824
|
-
}
|
|
825
|
-
return this.apiClient.get({
|
|
826
|
-
endpoint: EP_BRANCHES,
|
|
827
|
-
params: {
|
|
828
|
-
latitude,
|
|
829
|
-
longitude,
|
|
830
|
-
...radius && { radius }
|
|
831
|
-
}
|
|
832
|
-
});
|
|
833
|
-
}
|
|
834
|
-
/**
|
|
835
|
-
* Check in to a branch
|
|
836
|
-
*/
|
|
837
|
-
async checkIn(options) {
|
|
838
|
-
const { branchId, entranceType, userId, userLocation: _userLocation } = options;
|
|
839
|
-
try {
|
|
840
|
-
const entranceRequest = {
|
|
841
|
-
user_id: userId,
|
|
842
|
-
branch_id: branchId,
|
|
843
|
-
entrance_type: entranceType,
|
|
844
|
-
is_manual_recording: false
|
|
845
|
-
};
|
|
846
|
-
const response = await this.apiClient.post({
|
|
847
|
-
endpoint: EP_ENTRANCES,
|
|
848
|
-
data: entranceRequest
|
|
849
|
-
});
|
|
850
|
-
if (!response.success) {
|
|
851
|
-
return {
|
|
852
|
-
success: false,
|
|
853
|
-
message: response.message || "Check-in failed",
|
|
854
|
-
error: { code: "API_ERROR" }
|
|
855
|
-
};
|
|
856
|
-
}
|
|
857
|
-
return {
|
|
858
|
-
success: true,
|
|
859
|
-
message: "Check-in successful",
|
|
860
|
-
entrance: response.data
|
|
861
|
-
};
|
|
862
|
-
} catch (error) {
|
|
863
|
-
return {
|
|
864
|
-
success: false,
|
|
865
|
-
message: error.message || "Failed to check-in",
|
|
866
|
-
error: { code: "NETWORK_ERROR" }
|
|
867
|
-
};
|
|
868
|
-
}
|
|
869
|
-
}
|
|
870
|
-
/**
|
|
871
|
-
* Get branch by ID
|
|
872
|
-
*/
|
|
873
|
-
async getBranchById(branchId) {
|
|
874
|
-
return this.apiClient.get({
|
|
875
|
-
endpoint: `${EP_BRANCHES}/${branchId}`
|
|
876
|
-
});
|
|
877
|
-
}
|
|
878
|
-
/**
|
|
879
|
-
* Get all branches (with pagination)
|
|
880
|
-
*/
|
|
881
|
-
async getAllBranches(params) {
|
|
882
|
-
return this.apiClient.get({
|
|
883
|
-
endpoint: EP_BRANCHES,
|
|
884
|
-
params
|
|
885
|
-
});
|
|
886
|
-
}
|
|
887
|
-
};
|
|
888
|
-
|
|
889
|
-
// src/services/RemoteWorkService.ts
|
|
890
|
-
var RemoteWorkService = class {
|
|
891
|
-
constructor(apiClient) {
|
|
892
|
-
this.apiClient = apiClient;
|
|
893
|
-
}
|
|
894
|
-
/**
|
|
895
|
-
* Log remote work entry or exit
|
|
896
|
-
*/
|
|
897
|
-
async logRemoteWork(options) {
|
|
898
|
-
const { userId, entranceType, timestamp, description } = options;
|
|
899
|
-
try {
|
|
900
|
-
const createdAt = timestamp ? typeof timestamp === "string" ? timestamp : formatISO(timestamp) : formatISO();
|
|
901
|
-
const entranceRequest = {
|
|
902
|
-
user_id: userId,
|
|
903
|
-
entrance_type: entranceType,
|
|
904
|
-
is_manual_recording: true,
|
|
905
|
-
is_remote_work: true,
|
|
906
|
-
created_at: createdAt,
|
|
907
|
-
description
|
|
908
|
-
};
|
|
909
|
-
const response = await this.apiClient.post({
|
|
910
|
-
endpoint: EP_ENTRANCES,
|
|
911
|
-
data: { entrance: entranceRequest }
|
|
912
|
-
});
|
|
913
|
-
if (!response.success) {
|
|
914
|
-
return {
|
|
915
|
-
success: false,
|
|
916
|
-
message: response.message || "Failed to log remote work",
|
|
917
|
-
error: { code: "API_ERROR" }
|
|
918
|
-
};
|
|
919
|
-
}
|
|
920
|
-
return {
|
|
921
|
-
success: true,
|
|
922
|
-
message: "Remote work logged successfully",
|
|
923
|
-
entrance: response.data
|
|
924
|
-
};
|
|
925
|
-
} catch (error) {
|
|
926
|
-
return {
|
|
927
|
-
success: false,
|
|
928
|
-
message: error.message || "Failed to log remote work",
|
|
929
|
-
error: { code: "NETWORK_ERROR" }
|
|
930
|
-
};
|
|
931
|
-
}
|
|
932
|
-
}
|
|
933
|
-
/**
|
|
934
|
-
* Log remote work entry
|
|
935
|
-
*/
|
|
936
|
-
async logEntry(options) {
|
|
937
|
-
return this.logRemoteWork({
|
|
938
|
-
...options,
|
|
939
|
-
entranceType: 0 /* ENTRY */
|
|
940
|
-
});
|
|
941
|
-
}
|
|
942
|
-
/**
|
|
943
|
-
* Log remote work exit
|
|
944
|
-
*/
|
|
945
|
-
async logExit(options) {
|
|
946
|
-
return this.logRemoteWork({
|
|
947
|
-
...options,
|
|
948
|
-
entranceType: 1 /* EXIT */
|
|
949
|
-
});
|
|
950
|
-
}
|
|
951
|
-
};
|
|
952
|
-
|
|
953
|
-
// src/services/DeviceAccessService.ts
|
|
954
|
-
var DeviceAccessService = class {
|
|
955
|
-
constructor(apiClient) {
|
|
956
|
-
this.apiClient = apiClient;
|
|
957
|
-
}
|
|
958
|
-
/**
|
|
959
|
-
* Get all devices
|
|
960
|
-
*/
|
|
961
|
-
async getDevices(params) {
|
|
962
|
-
return this.apiClient.get({
|
|
963
|
-
endpoint: EP_DEVICES,
|
|
964
|
-
params: params || { per_page: 100 }
|
|
965
|
-
});
|
|
966
|
-
}
|
|
967
|
-
/**
|
|
968
|
-
* Get all QR devices
|
|
969
|
-
*/
|
|
970
|
-
async getQRDevices(params) {
|
|
971
|
-
return this.apiClient.get({
|
|
972
|
-
endpoint: EP_QR_DEVICES,
|
|
973
|
-
params: params || { per_page: 100 }
|
|
974
|
-
});
|
|
975
|
-
}
|
|
976
|
-
/**
|
|
977
|
-
* Get all QR devices with pagination support
|
|
978
|
-
*/
|
|
979
|
-
async getAllQRDevices() {
|
|
980
|
-
const perPage = 50;
|
|
981
|
-
let currentPage = 1;
|
|
982
|
-
let allData = [];
|
|
983
|
-
while (true) {
|
|
984
|
-
const response = await this.getQRDevices({
|
|
985
|
-
page: currentPage,
|
|
986
|
-
per_page: perPage
|
|
987
|
-
});
|
|
988
|
-
if (!response.success || !response.data) {
|
|
989
|
-
break;
|
|
990
|
-
}
|
|
991
|
-
allData = [...allData, ...response.data];
|
|
992
|
-
if (!response.meta || !response.meta.total_pages) {
|
|
993
|
-
break;
|
|
994
|
-
}
|
|
995
|
-
if (currentPage >= response.meta.total_pages) {
|
|
996
|
-
break;
|
|
997
|
-
}
|
|
998
|
-
currentPage++;
|
|
999
|
-
}
|
|
1000
|
-
return allData;
|
|
1001
|
-
}
|
|
1002
|
-
/**
|
|
1003
|
-
* Get accessible QR codes for current user
|
|
1004
|
-
*/
|
|
1005
|
-
async getAccessibleQRs() {
|
|
1006
|
-
return this.apiClient.get({
|
|
1007
|
-
endpoint: EP_QR_ACCESS
|
|
1008
|
-
});
|
|
1009
|
-
}
|
|
1010
|
-
/**
|
|
1011
|
-
* Get user devices
|
|
1012
|
-
*/
|
|
1013
|
-
async getUserDevices(request) {
|
|
1014
|
-
const { userId, page, per_page } = request;
|
|
1015
|
-
return this.apiClient.get({
|
|
1016
|
-
endpoint: `${EP_USERS}/${userId}/devices`,
|
|
1017
|
-
params: {
|
|
1018
|
-
...page && { page },
|
|
1019
|
-
...per_page && { per_page }
|
|
1020
|
-
}
|
|
1021
|
-
});
|
|
1022
|
-
}
|
|
1023
|
-
/**
|
|
1024
|
-
* Get device by ID
|
|
1025
|
-
*/
|
|
1026
|
-
async getDeviceById(deviceId) {
|
|
1027
|
-
return this.apiClient.get({
|
|
1028
|
-
endpoint: `${EP_DEVICES}/${deviceId}`
|
|
1029
|
-
});
|
|
1030
|
-
}
|
|
826
|
+
getDeviceById: async (deviceId) => {
|
|
827
|
+
return httpClient.get(
|
|
828
|
+
`${EP_DEVICES}/${deviceId}`
|
|
829
|
+
);
|
|
830
|
+
},
|
|
1031
831
|
/**
|
|
1032
832
|
* Check if user has access to a device
|
|
1033
833
|
*/
|
|
1034
|
-
async
|
|
834
|
+
checkDeviceAccess: async (deviceId) => {
|
|
1035
835
|
try {
|
|
1036
|
-
const response = await
|
|
836
|
+
const response = await DeviceAccessService.getAccessibleQRs();
|
|
1037
837
|
if (!response.success || !response.data) {
|
|
1038
838
|
return false;
|
|
1039
839
|
}
|
|
@@ -1041,24 +841,25 @@ var DeviceAccessService = class {
|
|
|
1041
841
|
} catch {
|
|
1042
842
|
return false;
|
|
1043
843
|
}
|
|
1044
|
-
}
|
|
844
|
+
},
|
|
1045
845
|
/**
|
|
1046
846
|
* Find QR device by QR code ID
|
|
1047
847
|
*/
|
|
1048
|
-
async
|
|
848
|
+
findDeviceByQRCode: async (qrCodeId) => {
|
|
1049
849
|
try {
|
|
1050
|
-
const devices = await
|
|
1051
|
-
|
|
850
|
+
const devices = await DeviceAccessService.getAllQRDevices();
|
|
851
|
+
const qr = devices.find((device) => device.qr_code_id === qrCodeId);
|
|
852
|
+
return qr;
|
|
1052
853
|
} catch {
|
|
1053
854
|
return void 0;
|
|
1054
855
|
}
|
|
1055
|
-
}
|
|
856
|
+
},
|
|
1056
857
|
/**
|
|
1057
858
|
* Find QR device by NFC code
|
|
1058
859
|
*/
|
|
1059
|
-
async
|
|
860
|
+
findDeviceByNFCCode: async (nfcCode) => {
|
|
1060
861
|
try {
|
|
1061
|
-
const devices = await
|
|
862
|
+
const devices = await DeviceAccessService.getAllQRDevices();
|
|
1062
863
|
return devices.find((device) => device.nfc_code === nfcCode);
|
|
1063
864
|
} catch {
|
|
1064
865
|
return void 0;
|
|
@@ -1153,21 +954,15 @@ var LocationService = class {
|
|
|
1153
954
|
return this.logLocationVerification(log);
|
|
1154
955
|
}
|
|
1155
956
|
};
|
|
1156
|
-
|
|
1157
|
-
// src/providers/PassgageAccessProvider.tsx
|
|
1158
|
-
import React, { createContext, useContext, useMemo } from "react";
|
|
1159
|
-
|
|
1160
|
-
// src/utils/secureStorage.ts
|
|
1161
|
-
import * as Keychain from "react-native-keychain";
|
|
1162
957
|
var STORAGE_KEYS = {
|
|
1163
958
|
TOKENS: "passgage_auth_tokens",
|
|
1164
959
|
USER: "passgage_user_info"
|
|
1165
960
|
};
|
|
1166
|
-
var SecureStorage =
|
|
961
|
+
var SecureStorage = {
|
|
1167
962
|
/**
|
|
1168
963
|
* Save tokens to secure storage
|
|
1169
964
|
*/
|
|
1170
|
-
async
|
|
965
|
+
saveTokens: async (tokens) => {
|
|
1171
966
|
try {
|
|
1172
967
|
await Keychain.setGenericPassword(
|
|
1173
968
|
STORAGE_KEYS.TOKENS,
|
|
@@ -1181,11 +976,11 @@ var SecureStorage = class {
|
|
|
1181
976
|
console.error("Failed to save tokens to secure storage:", error);
|
|
1182
977
|
throw new Error("Failed to save tokens");
|
|
1183
978
|
}
|
|
1184
|
-
}
|
|
979
|
+
},
|
|
1185
980
|
/**
|
|
1186
981
|
* Get tokens from secure storage
|
|
1187
982
|
*/
|
|
1188
|
-
async
|
|
983
|
+
getTokens: async () => {
|
|
1189
984
|
try {
|
|
1190
985
|
const credentials = await Keychain.getGenericPassword({
|
|
1191
986
|
service: STORAGE_KEYS.TOKENS
|
|
@@ -1199,11 +994,11 @@ var SecureStorage = class {
|
|
|
1199
994
|
console.error("Failed to get tokens from secure storage:", error);
|
|
1200
995
|
return null;
|
|
1201
996
|
}
|
|
1202
|
-
}
|
|
997
|
+
},
|
|
1203
998
|
/**
|
|
1204
999
|
* Clear tokens from secure storage
|
|
1205
1000
|
*/
|
|
1206
|
-
async
|
|
1001
|
+
clearTokens: async () => {
|
|
1207
1002
|
try {
|
|
1208
1003
|
await Keychain.resetGenericPassword({
|
|
1209
1004
|
service: STORAGE_KEYS.TOKENS
|
|
@@ -1211,11 +1006,11 @@ var SecureStorage = class {
|
|
|
1211
1006
|
} catch (error) {
|
|
1212
1007
|
console.error("Failed to clear tokens from secure storage:", error);
|
|
1213
1008
|
}
|
|
1214
|
-
}
|
|
1009
|
+
},
|
|
1215
1010
|
/**
|
|
1216
1011
|
* Save user information
|
|
1217
1012
|
*/
|
|
1218
|
-
async
|
|
1013
|
+
saveUser: async (user) => {
|
|
1219
1014
|
try {
|
|
1220
1015
|
await Keychain.setGenericPassword(
|
|
1221
1016
|
STORAGE_KEYS.USER,
|
|
@@ -1229,11 +1024,11 @@ var SecureStorage = class {
|
|
|
1229
1024
|
console.error("Failed to save user to secure storage:", error);
|
|
1230
1025
|
throw new Error("Failed to save user");
|
|
1231
1026
|
}
|
|
1232
|
-
}
|
|
1027
|
+
},
|
|
1233
1028
|
/**
|
|
1234
1029
|
* Get user information
|
|
1235
1030
|
*/
|
|
1236
|
-
async
|
|
1031
|
+
getUser: async () => {
|
|
1237
1032
|
try {
|
|
1238
1033
|
const credentials = await Keychain.getGenericPassword({
|
|
1239
1034
|
service: STORAGE_KEYS.USER
|
|
@@ -1247,11 +1042,11 @@ var SecureStorage = class {
|
|
|
1247
1042
|
console.error("Failed to get user from secure storage:", error);
|
|
1248
1043
|
return null;
|
|
1249
1044
|
}
|
|
1250
|
-
}
|
|
1045
|
+
},
|
|
1251
1046
|
/**
|
|
1252
1047
|
* Clear user information
|
|
1253
1048
|
*/
|
|
1254
|
-
async
|
|
1049
|
+
clearUser: async () => {
|
|
1255
1050
|
try {
|
|
1256
1051
|
await Keychain.resetGenericPassword({
|
|
1257
1052
|
service: STORAGE_KEYS.USER
|
|
@@ -1259,257 +1054,567 @@ var SecureStorage = class {
|
|
|
1259
1054
|
} catch (error) {
|
|
1260
1055
|
console.error("Failed to clear user from secure storage:", error);
|
|
1261
1056
|
}
|
|
1262
|
-
}
|
|
1057
|
+
},
|
|
1263
1058
|
/**
|
|
1264
1059
|
* Clear all data (tokens + user)
|
|
1265
1060
|
*/
|
|
1266
|
-
async
|
|
1267
|
-
await
|
|
1268
|
-
await
|
|
1061
|
+
clearAll: async () => {
|
|
1062
|
+
await SecureStorage.clearTokens();
|
|
1063
|
+
await SecureStorage.clearUser();
|
|
1269
1064
|
}
|
|
1270
1065
|
};
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1066
|
+
var qrScannerStore = create((set, get) => ({
|
|
1067
|
+
loading: false,
|
|
1068
|
+
error: null,
|
|
1069
|
+
qrDevices: [],
|
|
1070
|
+
qrAccessDevices: [],
|
|
1071
|
+
handleQrAccess: async () => {
|
|
1072
|
+
const res = await DeviceAccessService.getAccessibleQRs();
|
|
1073
|
+
if (res.data) {
|
|
1074
|
+
set({ qrAccessDevices: res.data.qr_ids });
|
|
1075
|
+
}
|
|
1076
|
+
},
|
|
1077
|
+
fetchQrDevicesAndAccess: async () => {
|
|
1078
|
+
try {
|
|
1079
|
+
const [qrDevicesRes, qrAccessRes] = await Promise.all([
|
|
1080
|
+
DeviceAccessService.getAllQRDevices(),
|
|
1081
|
+
DeviceAccessService.getAccessibleQRs()
|
|
1082
|
+
]);
|
|
1083
|
+
if (qrDevicesRes && qrDevicesRes.length > 0 && qrAccessRes) {
|
|
1084
|
+
set({
|
|
1085
|
+
qrDevices: qrDevicesRes,
|
|
1086
|
+
qrAccessDevices: qrAccessRes.data?.qr_ids
|
|
1087
|
+
});
|
|
1088
|
+
}
|
|
1089
|
+
} catch (error) {
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
}));
|
|
1093
|
+
var locationStore = create((set, get) => ({
|
|
1094
|
+
location: null,
|
|
1095
|
+
error: null,
|
|
1096
|
+
loading: false,
|
|
1097
|
+
refreshLocation: (config = {
|
|
1098
|
+
enableHighAccuracy: true,
|
|
1099
|
+
timeout: 15e3,
|
|
1100
|
+
maximumAge: 1e4
|
|
1101
|
+
}) => {
|
|
1102
|
+
Geolocation2.getCurrentPosition(
|
|
1103
|
+
(position) => {
|
|
1104
|
+
set({
|
|
1105
|
+
loading: false,
|
|
1106
|
+
error: null,
|
|
1107
|
+
location: {
|
|
1108
|
+
latitude: position.coords.latitude,
|
|
1109
|
+
longitude: position.coords.longitude,
|
|
1110
|
+
accuracy: position.coords.accuracy,
|
|
1111
|
+
altitude: position.coords.altitude ?? void 0,
|
|
1112
|
+
altitudeAccuracy: position.coords.altitudeAccuracy ?? void 0,
|
|
1113
|
+
heading: position.coords.heading ?? void 0,
|
|
1114
|
+
speed: position.coords.speed ?? void 0
|
|
1115
|
+
}
|
|
1116
|
+
});
|
|
1117
|
+
},
|
|
1118
|
+
(err) => {
|
|
1119
|
+
set({
|
|
1120
|
+
loading: false,
|
|
1121
|
+
error: err
|
|
1122
|
+
});
|
|
1123
|
+
},
|
|
1124
|
+
config
|
|
1125
|
+
);
|
|
1126
|
+
}
|
|
1127
|
+
}));
|
|
1274
1128
|
|
|
1275
|
-
// src/
|
|
1276
|
-
var
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
}
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
const { apiClient, services } = useMemo(() => {
|
|
1295
|
-
const secureStorage = createSecureStorage();
|
|
1296
|
-
let authService;
|
|
1297
|
-
const client = createApiClient({
|
|
1298
|
-
baseURL: config.baseURL,
|
|
1299
|
-
token: config.token,
|
|
1300
|
-
apiVersion: config.apiVersion,
|
|
1301
|
-
timeout: config.timeout,
|
|
1302
|
-
onUnauthorized: config.onUnauthorized,
|
|
1303
|
-
onError: config.onError,
|
|
1304
|
-
onTokenRefreshNeeded: async () => {
|
|
1305
|
-
const storedTokens = await authService.getStoredTokens();
|
|
1306
|
-
if (!storedTokens) {
|
|
1307
|
-
return null;
|
|
1308
|
-
}
|
|
1309
|
-
const result = await authService.refreshToken(storedTokens.refresh.token);
|
|
1310
|
-
if (result.success) {
|
|
1311
|
-
return result.tokens.access.token;
|
|
1312
|
-
}
|
|
1313
|
-
return null;
|
|
1129
|
+
// src/utils/flowHelpers.ts
|
|
1130
|
+
var enteranceFlow = async (data, options) => {
|
|
1131
|
+
const location = locationStore.getState().location;
|
|
1132
|
+
const qrAccessDevices = qrScannerStore.getState().qrAccessDevices;
|
|
1133
|
+
const { qrCode, device, isQrCode, nfcCode = "" } = data;
|
|
1134
|
+
if (isQrCode ? !validateQRCode(qrCode) : validateNFCCode(nfcCode)) {
|
|
1135
|
+
return {
|
|
1136
|
+
success: false,
|
|
1137
|
+
message: "Invalid QR code format",
|
|
1138
|
+
error: { code: "INVALID_QR_FORMAT" }
|
|
1139
|
+
};
|
|
1140
|
+
}
|
|
1141
|
+
const checkAccessible = isQrCode ? qrAccessDevices.find((qrAccessDevice) => qrAccessDevice === qrCode) : qrAccessDevices.find((qrAccessDevice) => qrAccessDevice === nfcCode);
|
|
1142
|
+
if (!checkAccessible) {
|
|
1143
|
+
return {
|
|
1144
|
+
success: false,
|
|
1145
|
+
message: "You do not have permission. Please contact your administrator",
|
|
1146
|
+
error: {
|
|
1147
|
+
code: "QR_ACCESS_DENIED"
|
|
1314
1148
|
}
|
|
1315
|
-
});
|
|
1316
|
-
authService = new AuthService(client);
|
|
1317
|
-
authService.setTokenStorage(secureStorage);
|
|
1318
|
-
const allServices = {
|
|
1319
|
-
authService,
|
|
1320
|
-
qrAccessService: new QRAccessService(client),
|
|
1321
|
-
nfcAccessService: new NFCAccessService(client),
|
|
1322
|
-
checkInService: new CheckInService(client),
|
|
1323
|
-
remoteWorkService: new RemoteWorkService(client),
|
|
1324
|
-
deviceAccessService: new DeviceAccessService(client),
|
|
1325
|
-
locationService: new LocationService(client)
|
|
1326
1149
|
};
|
|
1150
|
+
}
|
|
1151
|
+
if (!options.skipRepetitiveCheck && !checkRepetitiveRead(isQrCode ? qrCode : nfcCode)) {
|
|
1327
1152
|
return {
|
|
1328
|
-
|
|
1329
|
-
|
|
1153
|
+
success: false,
|
|
1154
|
+
message: "QR code was recently scanned. Please wait before scanning again.",
|
|
1155
|
+
error: { code: "REPETITIVE_READ" }
|
|
1330
1156
|
};
|
|
1331
|
-
}, [config.baseURL, config.token, config.apiVersion, config.timeout, config.onUnauthorized, config.onError]);
|
|
1332
|
-
const setToken = (newToken) => {
|
|
1333
|
-
apiClient.setToken(newToken);
|
|
1334
|
-
};
|
|
1335
|
-
const clearToken = () => {
|
|
1336
|
-
apiClient.clearToken();
|
|
1337
|
-
};
|
|
1338
|
-
const contextValue = {
|
|
1339
|
-
apiClient,
|
|
1340
|
-
...services,
|
|
1341
|
-
config,
|
|
1342
|
-
setToken,
|
|
1343
|
-
clearToken
|
|
1344
|
-
};
|
|
1345
|
-
return /* @__PURE__ */ React.createElement(PassgageAccessContext.Provider, { value: contextValue }, children);
|
|
1346
|
-
}
|
|
1347
|
-
function usePassgageAccess() {
|
|
1348
|
-
const context = useContext(PassgageAccessContext);
|
|
1349
|
-
if (!context) {
|
|
1350
|
-
throw new Error(
|
|
1351
|
-
"usePassgageAccess must be used within a PassgageAccessProvider"
|
|
1352
|
-
);
|
|
1353
1157
|
}
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
onLogoutSuccess,
|
|
1364
|
-
autoRestore = true
|
|
1365
|
-
} = options;
|
|
1366
|
-
const { authService } = usePassgageAccess();
|
|
1367
|
-
const [isAuthenticated, setIsAuthenticated] = useState(false);
|
|
1368
|
-
const [user, setUser] = useState(null);
|
|
1369
|
-
const [isLoading, setIsLoading] = useState(false);
|
|
1370
|
-
const [error, setError] = useState(null);
|
|
1371
|
-
useEffect(() => {
|
|
1372
|
-
if (!autoRestore) {
|
|
1373
|
-
return;
|
|
1374
|
-
}
|
|
1375
|
-
const restoreAuth = async () => {
|
|
1376
|
-
try {
|
|
1377
|
-
setIsLoading(true);
|
|
1378
|
-
const authenticated = await authService.isAuthenticated();
|
|
1379
|
-
if (authenticated) {
|
|
1380
|
-
const storedTokens = await authService.getStoredTokens();
|
|
1381
|
-
const storedUser = await authService.getStoredUser();
|
|
1382
|
-
if (storedTokens && storedUser) {
|
|
1383
|
-
authService["apiClient"].setToken(storedTokens.access.token);
|
|
1384
|
-
setIsAuthenticated(true);
|
|
1385
|
-
setUser(storedUser);
|
|
1386
|
-
try {
|
|
1387
|
-
const userResult = await authService.getCurrentUser();
|
|
1388
|
-
if (userResult.success) {
|
|
1389
|
-
setUser(userResult.user);
|
|
1390
|
-
}
|
|
1391
|
-
} catch (error2) {
|
|
1392
|
-
console.warn("Failed to fetch fresh user info:", error2);
|
|
1393
|
-
}
|
|
1394
|
-
}
|
|
1158
|
+
if (!options.skipLocationCheck) {
|
|
1159
|
+
const locationValidation = validateLocation(device, location ?? void 0);
|
|
1160
|
+
if (!locationValidation.valid) {
|
|
1161
|
+
return {
|
|
1162
|
+
success: false,
|
|
1163
|
+
message: `You are not within the allowed range of this device${location?.latitude},${location?.longitude}`,
|
|
1164
|
+
error: {
|
|
1165
|
+
code: "LOCATION_OUT_OF_RANGE",
|
|
1166
|
+
details: locationValidation.distance ? `Distance: ${locationValidation.distance.toFixed(2)}m` : void 0
|
|
1395
1167
|
}
|
|
1396
|
-
}
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1168
|
+
};
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
const onlineRequestBody = {
|
|
1172
|
+
device_id: device?.id ?? ""
|
|
1173
|
+
};
|
|
1174
|
+
if (device?.is_iot) {
|
|
1175
|
+
try {
|
|
1176
|
+
const enteranceResIot = await QRAccessService.triggerIoTDevice(device.id);
|
|
1177
|
+
if (enteranceResIot.success) {
|
|
1178
|
+
try {
|
|
1179
|
+
const enteranceQrRes = await QRAccessService.createEntranceFromQR(
|
|
1180
|
+
onlineRequestBody
|
|
1181
|
+
);
|
|
1182
|
+
if (enteranceQrRes.success) {
|
|
1183
|
+
addQrReadRecord(qrCode);
|
|
1184
|
+
return {
|
|
1185
|
+
success: !!enteranceQrRes?.success,
|
|
1186
|
+
message: enteranceQrRes.success ? "Enterance validated successfully" : "Enterance validation failed",
|
|
1187
|
+
entrance: enteranceQrRes.data
|
|
1188
|
+
};
|
|
1189
|
+
} else {
|
|
1190
|
+
return {
|
|
1191
|
+
success: false,
|
|
1192
|
+
message: enteranceQrRes.message ?? "Trigger IOT validation failed"
|
|
1193
|
+
};
|
|
1421
1194
|
}
|
|
1195
|
+
} catch (error) {
|
|
1196
|
+
return {
|
|
1197
|
+
success: false,
|
|
1198
|
+
message: error?.message ?? "Qr enterance failed"
|
|
1199
|
+
};
|
|
1422
1200
|
}
|
|
1423
|
-
|
|
1424
|
-
} catch (error2) {
|
|
1425
|
-
const errorMessage = error2.message || "An error occurred during login";
|
|
1426
|
-
setError(errorMessage);
|
|
1427
|
-
if (onLoginError) {
|
|
1428
|
-
onLoginError(errorMessage);
|
|
1429
|
-
}
|
|
1201
|
+
} else {
|
|
1430
1202
|
return {
|
|
1431
1203
|
success: false,
|
|
1432
|
-
|
|
1433
|
-
code: "UNKNOWN_ERROR"
|
|
1204
|
+
message: enteranceResIot.message ?? "Trigger IOT validation failed"
|
|
1434
1205
|
};
|
|
1435
|
-
} finally {
|
|
1436
|
-
setIsLoading(false);
|
|
1437
|
-
}
|
|
1438
|
-
},
|
|
1439
|
-
[authService, onLoginSuccess, onLoginError]
|
|
1440
|
-
);
|
|
1441
|
-
const logout = useCallback(async () => {
|
|
1442
|
-
try {
|
|
1443
|
-
setIsLoading(true);
|
|
1444
|
-
setError(null);
|
|
1445
|
-
await authService.logout();
|
|
1446
|
-
setIsAuthenticated(false);
|
|
1447
|
-
setUser(null);
|
|
1448
|
-
if (onLogoutSuccess) {
|
|
1449
|
-
onLogoutSuccess();
|
|
1450
1206
|
}
|
|
1451
|
-
} catch (
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
setIsLoading(false);
|
|
1207
|
+
} catch (error) {
|
|
1208
|
+
return {
|
|
1209
|
+
success: false,
|
|
1210
|
+
message: error ?? "Trigger IOT validation failed"
|
|
1211
|
+
};
|
|
1457
1212
|
}
|
|
1458
|
-
}
|
|
1459
|
-
const refreshToken = useCallback(async () => {
|
|
1213
|
+
} else {
|
|
1460
1214
|
try {
|
|
1461
|
-
const
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1215
|
+
const enteranceQrRes = await QRAccessService.createEntranceFromQR(
|
|
1216
|
+
onlineRequestBody
|
|
1217
|
+
);
|
|
1218
|
+
addQrReadRecord(qrCode);
|
|
1219
|
+
return {
|
|
1220
|
+
success: !!enteranceQrRes?.success,
|
|
1221
|
+
message: enteranceQrRes.success ? "QR code validated successfully" : "QR code validation failed",
|
|
1222
|
+
entrance: enteranceQrRes.data
|
|
1223
|
+
};
|
|
1224
|
+
} catch (error) {
|
|
1225
|
+
return {
|
|
1226
|
+
success: false,
|
|
1227
|
+
message: error.message || "Failed to validate QR code",
|
|
1228
|
+
error: { code: "NETWORK_ERROR" }
|
|
1229
|
+
};
|
|
1230
|
+
}
|
|
1231
|
+
}
|
|
1232
|
+
};
|
|
1233
|
+
var loginSuccessFlow = async (result) => {
|
|
1234
|
+
qrScannerStore.getState().fetchQrDevicesAndAccess();
|
|
1235
|
+
if (result.success) {
|
|
1236
|
+
await SecureStorage.saveTokens(result.tokens);
|
|
1237
|
+
if (result.user) {
|
|
1238
|
+
await SecureStorage.saveUser?.(result.user);
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
};
|
|
1242
|
+
|
|
1243
|
+
// src/stores/authStore.ts
|
|
1244
|
+
var useAuthStore = create((set, get) => ({
|
|
1245
|
+
loading: false,
|
|
1246
|
+
error: null,
|
|
1247
|
+
authStatus: false,
|
|
1248
|
+
user: null,
|
|
1249
|
+
restoreAuth: async () => {
|
|
1250
|
+
const tokens = await SecureStorage.getTokens?.();
|
|
1251
|
+
try {
|
|
1252
|
+
set({ loading: true });
|
|
1253
|
+
const isAuthenticated = tokens !== null && tokens?.access?.token?.length > 0;
|
|
1254
|
+
if (isAuthenticated) {
|
|
1255
|
+
const storedTokens = await SecureStorage.getTokens?.();
|
|
1256
|
+
const storedUser = await SecureStorage.getUser?.();
|
|
1257
|
+
if (storedTokens && storedUser) {
|
|
1258
|
+
setTokenToHttpClient(storedTokens.access.token);
|
|
1259
|
+
set({
|
|
1260
|
+
user: storedUser,
|
|
1261
|
+
authStatus: true
|
|
1262
|
+
});
|
|
1263
|
+
try {
|
|
1264
|
+
const userResult = await AuthService.getCurrentUser();
|
|
1265
|
+
if (userResult.success) {
|
|
1266
|
+
set({
|
|
1267
|
+
user: userResult.user
|
|
1268
|
+
});
|
|
1269
|
+
qrScannerStore.getState().fetchQrDevicesAndAccess();
|
|
1270
|
+
}
|
|
1271
|
+
} catch (error) {
|
|
1272
|
+
console.warn("Failed to fetch fresh user info:", error);
|
|
1273
|
+
}
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
} catch (error) {
|
|
1277
|
+
console.error("Failed to restore authentication:", error);
|
|
1278
|
+
set({ error: error.message || "Failed to restore authentication" });
|
|
1279
|
+
} finally {
|
|
1280
|
+
set({ loading: false });
|
|
1281
|
+
}
|
|
1282
|
+
},
|
|
1283
|
+
login: async (credentials, resolve, reject) => {
|
|
1284
|
+
try {
|
|
1285
|
+
set({ loading: true, error: null });
|
|
1286
|
+
const result = await AuthService.login(credentials);
|
|
1287
|
+
if (result.success) {
|
|
1288
|
+
resolve?.(result.user);
|
|
1289
|
+
loginSuccessFlow(result);
|
|
1290
|
+
set({ authStatus: true, user: result.user || null });
|
|
1291
|
+
} else {
|
|
1292
|
+
get().logout();
|
|
1293
|
+
reject?.(result.error);
|
|
1294
|
+
set({ error: result.error });
|
|
1295
|
+
}
|
|
1296
|
+
} catch (error) {
|
|
1297
|
+
get().logout();
|
|
1298
|
+
const errorMessage = error.message || "An error occurred during login";
|
|
1299
|
+
set({ error: errorMessage });
|
|
1300
|
+
reject?.(errorMessage);
|
|
1301
|
+
} finally {
|
|
1302
|
+
set({ loading: false });
|
|
1303
|
+
}
|
|
1304
|
+
},
|
|
1305
|
+
loginWithAzure: async (credentials, resolve, reject) => {
|
|
1306
|
+
try {
|
|
1307
|
+
set({ loading: true, error: null });
|
|
1308
|
+
const result = await AuthService.loginWithAzure(credentials);
|
|
1309
|
+
if (result.success) {
|
|
1310
|
+
loginSuccessFlow(result);
|
|
1311
|
+
resolve?.(result.user);
|
|
1312
|
+
set({ authStatus: true, user: result.user || null });
|
|
1313
|
+
} else {
|
|
1314
|
+
reject?.(result.error);
|
|
1315
|
+
get().logout();
|
|
1316
|
+
set({ error: result.error });
|
|
1317
|
+
}
|
|
1318
|
+
} catch (error) {
|
|
1319
|
+
const errorMessage = error.message || "An error occurred during Azure login";
|
|
1320
|
+
get().logout();
|
|
1321
|
+
set({ error: errorMessage });
|
|
1322
|
+
reject?.(errorMessage);
|
|
1323
|
+
} finally {
|
|
1324
|
+
set({ loading: false });
|
|
1325
|
+
}
|
|
1326
|
+
},
|
|
1327
|
+
logout: async (resolve) => {
|
|
1328
|
+
try {
|
|
1329
|
+
set({
|
|
1330
|
+
loading: true,
|
|
1331
|
+
error: null
|
|
1332
|
+
});
|
|
1333
|
+
await SecureStorage.clearTokens?.();
|
|
1334
|
+
await SecureStorage.clearUser?.();
|
|
1335
|
+
set({ authStatus: false, user: null });
|
|
1336
|
+
resolve?.();
|
|
1337
|
+
} catch (error) {
|
|
1338
|
+
const errorMessage = error.message || "An error occurred during logout";
|
|
1339
|
+
set({ error: errorMessage });
|
|
1340
|
+
console.error("Logout failed:", error);
|
|
1341
|
+
} finally {
|
|
1342
|
+
set({ loading: false });
|
|
1343
|
+
}
|
|
1344
|
+
},
|
|
1345
|
+
refreshToken: async () => {
|
|
1346
|
+
try {
|
|
1347
|
+
const storedTokens = await SecureStorage.getTokens?.();
|
|
1348
|
+
if (!storedTokens) {
|
|
1349
|
+
return false;
|
|
1350
|
+
}
|
|
1351
|
+
const result = await AuthService.refreshToken(storedTokens.refresh.token);
|
|
1352
|
+
if (result.success) {
|
|
1353
|
+
return true;
|
|
1354
|
+
} else {
|
|
1355
|
+
await get().logout();
|
|
1356
|
+
return false;
|
|
1357
|
+
}
|
|
1358
|
+
} catch (error) {
|
|
1359
|
+
console.error("Token refresh failed:", error);
|
|
1360
|
+
await get().logout();
|
|
1475
1361
|
return false;
|
|
1476
1362
|
}
|
|
1477
|
-
},
|
|
1478
|
-
|
|
1479
|
-
|
|
1363
|
+
},
|
|
1364
|
+
clearError: () => {
|
|
1365
|
+
set({ error: null });
|
|
1366
|
+
}
|
|
1367
|
+
}));
|
|
1368
|
+
var PassgageAccessProvideContext = createContext(void 0);
|
|
1369
|
+
function PassgageAccessProvider(props) {
|
|
1370
|
+
const {
|
|
1371
|
+
children,
|
|
1372
|
+
baseURL,
|
|
1373
|
+
msalToken,
|
|
1374
|
+
apiVersion = "v2",
|
|
1375
|
+
timeout = 3e4,
|
|
1376
|
+
rememberUser = true,
|
|
1377
|
+
onUnauthorized = () => {
|
|
1378
|
+
},
|
|
1379
|
+
locationPermissionErrorCallback = () => {
|
|
1380
|
+
},
|
|
1381
|
+
getLocationErrorCallback = () => {
|
|
1382
|
+
}
|
|
1383
|
+
} = props;
|
|
1384
|
+
const useLocationStore = locationStore();
|
|
1385
|
+
const authStore = useAuthStore();
|
|
1386
|
+
useEffect(() => {
|
|
1387
|
+
const msalTokenInitFlow = async () => {
|
|
1388
|
+
try {
|
|
1389
|
+
await authStore.loginWithAzure(
|
|
1390
|
+
{
|
|
1391
|
+
id_token: msalToken ?? "",
|
|
1392
|
+
device_type: Platform.OS === "ios" ? "ios" : "android"
|
|
1393
|
+
},
|
|
1394
|
+
() => {
|
|
1395
|
+
},
|
|
1396
|
+
(error) => {
|
|
1397
|
+
console.error("Failed to login with Azure:", error);
|
|
1398
|
+
authStore.logout();
|
|
1399
|
+
}
|
|
1400
|
+
);
|
|
1401
|
+
} catch (error) {
|
|
1402
|
+
console.error("Failed to login with Azure:", error);
|
|
1403
|
+
authStore.logout();
|
|
1404
|
+
}
|
|
1405
|
+
};
|
|
1406
|
+
const LocationPermission = Platform.select({
|
|
1407
|
+
android: PERMISSIONS.ANDROID.ACCESS_FINE_LOCATION,
|
|
1408
|
+
ios: PERMISSIONS.IOS.LOCATION_WHEN_IN_USE,
|
|
1409
|
+
default: PERMISSIONS.IOS.LOCATION_WHEN_IN_USE
|
|
1410
|
+
});
|
|
1411
|
+
if (LocationPermission) {
|
|
1412
|
+
RNPermissions.check(LocationPermission).then((status) => {
|
|
1413
|
+
switch (status) {
|
|
1414
|
+
case RESULTS.DENIED:
|
|
1415
|
+
request(LocationPermission).then(() => {
|
|
1416
|
+
useLocationStore.refreshLocation();
|
|
1417
|
+
});
|
|
1418
|
+
break;
|
|
1419
|
+
case RESULTS.BLOCKED:
|
|
1420
|
+
case RESULTS.UNAVAILABLE:
|
|
1421
|
+
case RESULTS.GRANTED:
|
|
1422
|
+
case RESULTS.LIMITED:
|
|
1423
|
+
useLocationStore.refreshLocation();
|
|
1424
|
+
break;
|
|
1425
|
+
}
|
|
1426
|
+
}).catch((error) => {
|
|
1427
|
+
locationPermissionErrorCallback(error);
|
|
1428
|
+
console.error(error);
|
|
1429
|
+
});
|
|
1430
|
+
}
|
|
1431
|
+
createHttpClient({ apiVersion, baseURL, timeout });
|
|
1432
|
+
if (msalToken) {
|
|
1433
|
+
msalTokenInitFlow();
|
|
1434
|
+
} else {
|
|
1435
|
+
authStore.logout();
|
|
1436
|
+
onUnauthorized({
|
|
1437
|
+
name: "Unauthorized",
|
|
1438
|
+
message: "MSAL Token is required"
|
|
1439
|
+
});
|
|
1440
|
+
}
|
|
1441
|
+
if (rememberUser) {
|
|
1442
|
+
authStore.restoreAuth();
|
|
1443
|
+
}
|
|
1480
1444
|
}, []);
|
|
1445
|
+
useEffect(() => {
|
|
1446
|
+
if (useLocationStore.error) {
|
|
1447
|
+
getLocationErrorCallback(useLocationStore.error);
|
|
1448
|
+
}
|
|
1449
|
+
}, [useLocationStore.error]);
|
|
1450
|
+
return /* @__PURE__ */ React.createElement(PassgageAccessProvideContext.Provider, { value: { ...props } }, children);
|
|
1451
|
+
}
|
|
1452
|
+
var usePassgageAccessContext = () => {
|
|
1453
|
+
const ctx = useContext(PassgageAccessProvideContext);
|
|
1454
|
+
if (!ctx) {
|
|
1455
|
+
throw new Error(
|
|
1456
|
+
"usePassgageAccessContext must be used inside AuthProvider"
|
|
1457
|
+
);
|
|
1458
|
+
}
|
|
1459
|
+
return ctx;
|
|
1460
|
+
};
|
|
1461
|
+
var usePassgageQRScanner = (payload) => {
|
|
1462
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
1463
|
+
const [error, setError] = useState(null);
|
|
1464
|
+
const { qrDevices } = qrScannerStore();
|
|
1465
|
+
const scan = async (qrCode, device) => {
|
|
1466
|
+
setIsLoading(true);
|
|
1467
|
+
setError(null);
|
|
1468
|
+
try {
|
|
1469
|
+
let qrDevice = device;
|
|
1470
|
+
if (!qrDevice) {
|
|
1471
|
+
qrDevice = qrDevices.find((item) => item.qr_code_id === qrCode);
|
|
1472
|
+
if (!qrDevice) {
|
|
1473
|
+
payload.onError?.(
|
|
1474
|
+
error ?? { message: "QR Device not found", success: false }
|
|
1475
|
+
);
|
|
1476
|
+
return;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
const result = await enteranceFlow(
|
|
1480
|
+
{
|
|
1481
|
+
device: qrDevice,
|
|
1482
|
+
qrCode,
|
|
1483
|
+
isQrCode: true
|
|
1484
|
+
},
|
|
1485
|
+
payload.options
|
|
1486
|
+
);
|
|
1487
|
+
if (!result.success) {
|
|
1488
|
+
payload.onError?.(result);
|
|
1489
|
+
} else {
|
|
1490
|
+
payload.onSuccess?.(result.entrance);
|
|
1491
|
+
}
|
|
1492
|
+
} catch (err) {
|
|
1493
|
+
const errorObj = {
|
|
1494
|
+
success: false,
|
|
1495
|
+
message: err?.message || "Qr scan failed",
|
|
1496
|
+
error: { code: "QR_SCAN_FAILED" }
|
|
1497
|
+
};
|
|
1498
|
+
setError(errorObj);
|
|
1499
|
+
payload.onError?.(errorObj);
|
|
1500
|
+
} finally {
|
|
1501
|
+
setIsLoading(false);
|
|
1502
|
+
}
|
|
1503
|
+
};
|
|
1481
1504
|
return {
|
|
1482
|
-
|
|
1483
|
-
logout,
|
|
1484
|
-
refreshToken,
|
|
1485
|
-
isAuthenticated,
|
|
1486
|
-
user,
|
|
1505
|
+
scan,
|
|
1487
1506
|
isLoading,
|
|
1507
|
+
error
|
|
1508
|
+
};
|
|
1509
|
+
};
|
|
1510
|
+
var bigInt = __require("big-integer");
|
|
1511
|
+
var reversedHexToDec = (reversedHex) => {
|
|
1512
|
+
try {
|
|
1513
|
+
reversedHex = reversedHex.replace(/\s+/g, "");
|
|
1514
|
+
let originalHex = "";
|
|
1515
|
+
for (let i = reversedHex.length; i > 0; i -= 2) {
|
|
1516
|
+
originalHex += reversedHex.substring(i - 2, i);
|
|
1517
|
+
}
|
|
1518
|
+
let decimalValue = bigInt(originalHex, 16);
|
|
1519
|
+
return decimalValue.toString();
|
|
1520
|
+
} catch (error) {
|
|
1521
|
+
return reversedHex;
|
|
1522
|
+
}
|
|
1523
|
+
};
|
|
1524
|
+
function usePassgageNFCScanner(payload) {
|
|
1525
|
+
const [supportNFC, setSupportNFC] = useState();
|
|
1526
|
+
const [isScanning, setIsScanning] = useState(false);
|
|
1527
|
+
const [nfcData, setNfcData] = useState();
|
|
1528
|
+
const { qrDevices } = qrScannerStore();
|
|
1529
|
+
const [error, setError] = useState(null);
|
|
1530
|
+
useEffect(() => {
|
|
1531
|
+
NfcManager.isSupported().then((supported) => {
|
|
1532
|
+
if (supported) {
|
|
1533
|
+
setSupportNFC(true);
|
|
1534
|
+
} else {
|
|
1535
|
+
setSupportNFC(false);
|
|
1536
|
+
}
|
|
1537
|
+
});
|
|
1538
|
+
return () => {
|
|
1539
|
+
NfcManager.cancelTechnologyRequest();
|
|
1540
|
+
};
|
|
1541
|
+
}, []);
|
|
1542
|
+
const stopScanning = async () => {
|
|
1543
|
+
try {
|
|
1544
|
+
NfcManager.cancelTechnologyRequest().finally(() => {
|
|
1545
|
+
setIsScanning(false);
|
|
1546
|
+
});
|
|
1547
|
+
} catch {
|
|
1548
|
+
}
|
|
1549
|
+
};
|
|
1550
|
+
const handleNFCTag = async (tag) => {
|
|
1551
|
+
if (!tag.id) {
|
|
1552
|
+
return;
|
|
1553
|
+
}
|
|
1554
|
+
try {
|
|
1555
|
+
const nfcCode = reversedHexToDec(tag.id);
|
|
1556
|
+
if (Platform.OS === "ios") {
|
|
1557
|
+
await NfcManager.cancelTechnologyRequest();
|
|
1558
|
+
}
|
|
1559
|
+
const device = qrDevices.find((device2) => device2.nfc_code === nfcCode);
|
|
1560
|
+
if (!device) {
|
|
1561
|
+
throw new Error("NFC device not found");
|
|
1562
|
+
}
|
|
1563
|
+
const result = await enteranceFlow(
|
|
1564
|
+
{
|
|
1565
|
+
device,
|
|
1566
|
+
nfcCode,
|
|
1567
|
+
isQrCode: false
|
|
1568
|
+
},
|
|
1569
|
+
payload.options
|
|
1570
|
+
);
|
|
1571
|
+
if (!result.success) {
|
|
1572
|
+
throw new Error(result.message);
|
|
1573
|
+
}
|
|
1574
|
+
payload.onSuccess?.(result.entrance);
|
|
1575
|
+
stopScanning();
|
|
1576
|
+
} catch (err) {
|
|
1577
|
+
const error2 = err;
|
|
1578
|
+
setError(error2);
|
|
1579
|
+
payload.onError?.(error2);
|
|
1580
|
+
stopScanning();
|
|
1581
|
+
}
|
|
1582
|
+
};
|
|
1583
|
+
const startScanning = async () => {
|
|
1584
|
+
await NfcManager.requestTechnology(NfcTech.Ndef, {
|
|
1585
|
+
invalidateAfterFirstRead: true
|
|
1586
|
+
});
|
|
1587
|
+
await NfcManager.getTag().then((tag) => {
|
|
1588
|
+
setNfcData(tag?.id ?? "");
|
|
1589
|
+
handleNFCTag(tag || {});
|
|
1590
|
+
}).catch(() => false);
|
|
1591
|
+
NfcManager.cancelTechnologyRequest();
|
|
1592
|
+
setIsScanning(true);
|
|
1593
|
+
setError(null);
|
|
1594
|
+
};
|
|
1595
|
+
return {
|
|
1596
|
+
nfcData,
|
|
1597
|
+
supportNFC,
|
|
1598
|
+
startScanning,
|
|
1599
|
+
stopScanning,
|
|
1488
1600
|
error,
|
|
1489
|
-
|
|
1601
|
+
isScanning
|
|
1490
1602
|
};
|
|
1491
1603
|
}
|
|
1492
|
-
|
|
1493
|
-
// src/hooks/usePassgageQRScanner.ts
|
|
1494
|
-
import { useState as useState3, useCallback as useCallback3 } from "react";
|
|
1495
|
-
|
|
1496
|
-
// src/hooks/useLocation.ts
|
|
1497
|
-
import { useState as useState2, useEffect as useEffect2, useCallback as useCallback2 } from "react";
|
|
1498
|
-
import Geolocation from "@react-native-community/geolocation";
|
|
1499
1604
|
function useLocation(options = {}) {
|
|
1500
|
-
const [location, setLocation] =
|
|
1501
|
-
const [error, setError] =
|
|
1502
|
-
const [isLoading, setIsLoading] =
|
|
1605
|
+
const [location, setLocation] = useState(null);
|
|
1606
|
+
const [error, setError] = useState(null);
|
|
1607
|
+
const [isLoading, setIsLoading] = useState(true);
|
|
1503
1608
|
const config = {
|
|
1504
1609
|
enableHighAccuracy: options.enableHighAccuracy ?? true,
|
|
1505
1610
|
timeout: options.timeout ?? 15e3,
|
|
1506
1611
|
maximumAge: options.maximumAge ?? 1e4
|
|
1507
1612
|
};
|
|
1508
|
-
const refreshLocation =
|
|
1613
|
+
const refreshLocation = useCallback(async () => {
|
|
1509
1614
|
setIsLoading(true);
|
|
1510
1615
|
setError(null);
|
|
1511
1616
|
return new Promise((resolve) => {
|
|
1512
|
-
|
|
1617
|
+
Geolocation2.getCurrentPosition(
|
|
1513
1618
|
(position) => {
|
|
1514
1619
|
setLocation({
|
|
1515
1620
|
latitude: position.coords.latitude,
|
|
@@ -1532,10 +1637,10 @@ function useLocation(options = {}) {
|
|
|
1532
1637
|
);
|
|
1533
1638
|
});
|
|
1534
1639
|
}, [config.enableHighAccuracy, config.timeout, config.maximumAge]);
|
|
1535
|
-
|
|
1640
|
+
useEffect(() => {
|
|
1536
1641
|
refreshLocation();
|
|
1537
1642
|
if (options.watchPosition) {
|
|
1538
|
-
const watchId =
|
|
1643
|
+
const watchId = Geolocation2.watchPosition(
|
|
1539
1644
|
(position) => {
|
|
1540
1645
|
setLocation({
|
|
1541
1646
|
latitude: position.coords.latitude,
|
|
@@ -1553,7 +1658,7 @@ function useLocation(options = {}) {
|
|
|
1553
1658
|
config
|
|
1554
1659
|
);
|
|
1555
1660
|
return () => {
|
|
1556
|
-
|
|
1661
|
+
Geolocation2.clearWatch(watchId);
|
|
1557
1662
|
};
|
|
1558
1663
|
}
|
|
1559
1664
|
return void 0;
|
|
@@ -1566,376 +1671,7 @@ function useLocation(options = {}) {
|
|
|
1566
1671
|
};
|
|
1567
1672
|
}
|
|
1568
1673
|
|
|
1569
|
-
// src/hooks/usePassgageQRScanner.ts
|
|
1570
|
-
function usePassgageQRScanner(options = {}) {
|
|
1571
|
-
const { qrAccessService, deviceAccessService } = usePassgageAccess();
|
|
1572
|
-
const { location } = useLocation();
|
|
1573
|
-
const [isLoading, setIsLoading] = useState3(false);
|
|
1574
|
-
const [error, setError] = useState3(null);
|
|
1575
|
-
const scan = useCallback3(
|
|
1576
|
-
async (qrCode, device) => {
|
|
1577
|
-
setIsLoading(true);
|
|
1578
|
-
setError(null);
|
|
1579
|
-
try {
|
|
1580
|
-
let qrDevice = device;
|
|
1581
|
-
if (!qrDevice) {
|
|
1582
|
-
qrDevice = await deviceAccessService.findDeviceByQRCode(qrCode);
|
|
1583
|
-
if (!qrDevice) {
|
|
1584
|
-
throw new Error("QR device not found");
|
|
1585
|
-
}
|
|
1586
|
-
}
|
|
1587
|
-
const result = await qrAccessService.validateQR({
|
|
1588
|
-
qrCode,
|
|
1589
|
-
device: qrDevice,
|
|
1590
|
-
userLocation: location || void 0,
|
|
1591
|
-
skipLocationCheck: options.skipLocationCheck,
|
|
1592
|
-
skipRepetitiveCheck: options.skipRepetitiveCheck
|
|
1593
|
-
});
|
|
1594
|
-
if (!result.success) {
|
|
1595
|
-
throw new Error(result.message);
|
|
1596
|
-
}
|
|
1597
|
-
options.onSuccess?.(result.entrance);
|
|
1598
|
-
} catch (err) {
|
|
1599
|
-
const error2 = err;
|
|
1600
|
-
setError(error2);
|
|
1601
|
-
options.onError?.(error2);
|
|
1602
|
-
} finally {
|
|
1603
|
-
setIsLoading(false);
|
|
1604
|
-
}
|
|
1605
|
-
},
|
|
1606
|
-
[qrAccessService, deviceAccessService, location, options]
|
|
1607
|
-
);
|
|
1608
|
-
return {
|
|
1609
|
-
scan,
|
|
1610
|
-
isLoading,
|
|
1611
|
-
error
|
|
1612
|
-
};
|
|
1613
|
-
}
|
|
1614
|
-
|
|
1615
|
-
// src/hooks/usePassgageNFCScanner.ts
|
|
1616
|
-
import { useState as useState4, useCallback as useCallback4, useEffect as useEffect3 } from "react";
|
|
1617
|
-
import NfcManager, { NfcTech } from "react-native-nfc-manager";
|
|
1618
|
-
function reversedHexToDec(hexString) {
|
|
1619
|
-
const hex = hexString.replace(/:/g, "");
|
|
1620
|
-
return parseInt(hex, 16).toString();
|
|
1621
|
-
}
|
|
1622
|
-
function usePassgageNFCScanner(options = {}) {
|
|
1623
|
-
const { nfcAccessService, deviceAccessService } = usePassgageAccess();
|
|
1624
|
-
const { location } = useLocation();
|
|
1625
|
-
const [isScanning, setIsScanning] = useState4(false);
|
|
1626
|
-
const [error, setError] = useState4(null);
|
|
1627
|
-
const stopScanning = useCallback4(async () => {
|
|
1628
|
-
try {
|
|
1629
|
-
await NfcManager.cancelTechnologyRequest();
|
|
1630
|
-
setIsScanning(false);
|
|
1631
|
-
} catch {
|
|
1632
|
-
}
|
|
1633
|
-
}, []);
|
|
1634
|
-
const handleNFCTag = useCallback4(
|
|
1635
|
-
async (tag) => {
|
|
1636
|
-
if (!tag.id) {
|
|
1637
|
-
return;
|
|
1638
|
-
}
|
|
1639
|
-
try {
|
|
1640
|
-
const nfcCode = reversedHexToDec(tag.id);
|
|
1641
|
-
const device = await deviceAccessService.findDeviceByNFCCode(nfcCode);
|
|
1642
|
-
if (!device) {
|
|
1643
|
-
throw new Error("NFC device not found");
|
|
1644
|
-
}
|
|
1645
|
-
const result = await nfcAccessService.validateNFC({
|
|
1646
|
-
nfcCode,
|
|
1647
|
-
device,
|
|
1648
|
-
userLocation: location || void 0,
|
|
1649
|
-
skipLocationCheck: options.skipLocationCheck,
|
|
1650
|
-
skipRepetitiveCheck: options.skipRepetitiveCheck
|
|
1651
|
-
});
|
|
1652
|
-
if (!result.success) {
|
|
1653
|
-
throw new Error(result.message);
|
|
1654
|
-
}
|
|
1655
|
-
options.onSuccess?.(result.entrance);
|
|
1656
|
-
await stopScanning();
|
|
1657
|
-
} catch (err) {
|
|
1658
|
-
const error2 = err;
|
|
1659
|
-
setError(error2);
|
|
1660
|
-
options.onError?.(error2);
|
|
1661
|
-
await stopScanning();
|
|
1662
|
-
}
|
|
1663
|
-
},
|
|
1664
|
-
[nfcAccessService, deviceAccessService, location, options, stopScanning]
|
|
1665
|
-
);
|
|
1666
|
-
const startScanning = useCallback4(async () => {
|
|
1667
|
-
setIsScanning(true);
|
|
1668
|
-
setError(null);
|
|
1669
|
-
try {
|
|
1670
|
-
await NfcManager.requestTechnology(NfcTech.Ndef, {
|
|
1671
|
-
invalidateAfterFirstRead: true
|
|
1672
|
-
});
|
|
1673
|
-
const tag = await NfcManager.getTag();
|
|
1674
|
-
await handleNFCTag(tag || {});
|
|
1675
|
-
} catch (err) {
|
|
1676
|
-
const error2 = err;
|
|
1677
|
-
setError(error2);
|
|
1678
|
-
options.onError?.(error2);
|
|
1679
|
-
setIsScanning(false);
|
|
1680
|
-
}
|
|
1681
|
-
}, [handleNFCTag, options]);
|
|
1682
|
-
useEffect3(() => {
|
|
1683
|
-
if (options.autoStart) {
|
|
1684
|
-
startScanning();
|
|
1685
|
-
}
|
|
1686
|
-
return () => {
|
|
1687
|
-
stopScanning();
|
|
1688
|
-
};
|
|
1689
|
-
}, [options.autoStart, startScanning, stopScanning]);
|
|
1690
|
-
return {
|
|
1691
|
-
startScanning,
|
|
1692
|
-
stopScanning,
|
|
1693
|
-
isScanning,
|
|
1694
|
-
error
|
|
1695
|
-
};
|
|
1696
|
-
}
|
|
1697
|
-
|
|
1698
|
-
// src/hooks/usePassgageCheckIn.ts
|
|
1699
|
-
import { useState as useState5, useCallback as useCallback5 } from "react";
|
|
1700
|
-
function usePassgageCheckIn(options = {}) {
|
|
1701
|
-
const { checkInService } = usePassgageAccess();
|
|
1702
|
-
const { location } = useLocation();
|
|
1703
|
-
const [isLoading, setIsLoading] = useState5(false);
|
|
1704
|
-
const [error, setError] = useState5(null);
|
|
1705
|
-
const getNearbyBranches = useCallback5(
|
|
1706
|
-
async (params) => {
|
|
1707
|
-
if (!location) {
|
|
1708
|
-
return {
|
|
1709
|
-
success: false,
|
|
1710
|
-
error: "Location not available"
|
|
1711
|
-
};
|
|
1712
|
-
}
|
|
1713
|
-
setIsLoading(true);
|
|
1714
|
-
setError(null);
|
|
1715
|
-
try {
|
|
1716
|
-
const response = await checkInService.getNearbyBranches({
|
|
1717
|
-
latitude: location.latitude,
|
|
1718
|
-
longitude: location.longitude,
|
|
1719
|
-
radius: params?.radius || options.radius
|
|
1720
|
-
});
|
|
1721
|
-
if (response.success && response.data) {
|
|
1722
|
-
return {
|
|
1723
|
-
success: true,
|
|
1724
|
-
data: response.data
|
|
1725
|
-
};
|
|
1726
|
-
}
|
|
1727
|
-
return {
|
|
1728
|
-
success: false,
|
|
1729
|
-
error: response.message || "Failed to fetch nearby branches"
|
|
1730
|
-
};
|
|
1731
|
-
} catch (err) {
|
|
1732
|
-
const error2 = err;
|
|
1733
|
-
setError(error2);
|
|
1734
|
-
return {
|
|
1735
|
-
success: false,
|
|
1736
|
-
error: error2.message || "Failed to fetch nearby branches"
|
|
1737
|
-
};
|
|
1738
|
-
} finally {
|
|
1739
|
-
setIsLoading(false);
|
|
1740
|
-
}
|
|
1741
|
-
},
|
|
1742
|
-
[checkInService, location, options.radius]
|
|
1743
|
-
);
|
|
1744
|
-
const checkInEntry = useCallback5(
|
|
1745
|
-
async (params) => {
|
|
1746
|
-
setIsLoading(true);
|
|
1747
|
-
setError(null);
|
|
1748
|
-
try {
|
|
1749
|
-
const result = await checkInService.checkIn({
|
|
1750
|
-
branchId: params.branchId,
|
|
1751
|
-
userId: params.userId,
|
|
1752
|
-
entranceType: 0 /* ENTRY */,
|
|
1753
|
-
userLocation: location || void 0
|
|
1754
|
-
});
|
|
1755
|
-
if (result.success) {
|
|
1756
|
-
return {
|
|
1757
|
-
success: true,
|
|
1758
|
-
data: result.entrance
|
|
1759
|
-
};
|
|
1760
|
-
}
|
|
1761
|
-
return {
|
|
1762
|
-
success: false,
|
|
1763
|
-
error: result.message || "Check-in failed"
|
|
1764
|
-
};
|
|
1765
|
-
} catch (err) {
|
|
1766
|
-
const error2 = err;
|
|
1767
|
-
setError(error2);
|
|
1768
|
-
return {
|
|
1769
|
-
success: false,
|
|
1770
|
-
error: error2.message || "Check-in failed"
|
|
1771
|
-
};
|
|
1772
|
-
} finally {
|
|
1773
|
-
setIsLoading(false);
|
|
1774
|
-
}
|
|
1775
|
-
},
|
|
1776
|
-
[checkInService, location]
|
|
1777
|
-
);
|
|
1778
|
-
const checkInExit = useCallback5(
|
|
1779
|
-
async (params) => {
|
|
1780
|
-
setIsLoading(true);
|
|
1781
|
-
setError(null);
|
|
1782
|
-
try {
|
|
1783
|
-
const result = await checkInService.checkIn({
|
|
1784
|
-
branchId: params.branchId,
|
|
1785
|
-
userId: params.userId,
|
|
1786
|
-
entranceType: 1 /* EXIT */,
|
|
1787
|
-
userLocation: location || void 0
|
|
1788
|
-
});
|
|
1789
|
-
if (result.success) {
|
|
1790
|
-
return {
|
|
1791
|
-
success: true,
|
|
1792
|
-
data: result.entrance
|
|
1793
|
-
};
|
|
1794
|
-
}
|
|
1795
|
-
return {
|
|
1796
|
-
success: false,
|
|
1797
|
-
error: result.message || "Check-out failed"
|
|
1798
|
-
};
|
|
1799
|
-
} catch (err) {
|
|
1800
|
-
const error2 = err;
|
|
1801
|
-
setError(error2);
|
|
1802
|
-
return {
|
|
1803
|
-
success: false,
|
|
1804
|
-
error: error2.message || "Check-out failed"
|
|
1805
|
-
};
|
|
1806
|
-
} finally {
|
|
1807
|
-
setIsLoading(false);
|
|
1808
|
-
}
|
|
1809
|
-
},
|
|
1810
|
-
[checkInService, location]
|
|
1811
|
-
);
|
|
1812
|
-
return {
|
|
1813
|
-
getNearbyBranches,
|
|
1814
|
-
checkInEntry,
|
|
1815
|
-
checkInExit,
|
|
1816
|
-
isLoading,
|
|
1817
|
-
error
|
|
1818
|
-
};
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
// src/hooks/usePassgageRemoteWork.ts
|
|
1822
|
-
import { useState as useState6, useCallback as useCallback6 } from "react";
|
|
1823
|
-
function usePassgageRemoteWork() {
|
|
1824
|
-
const { remoteWorkService } = usePassgageAccess();
|
|
1825
|
-
const [isLoading, setIsLoading] = useState6(false);
|
|
1826
|
-
const [error, setError] = useState6(null);
|
|
1827
|
-
const logEntry = useCallback6(
|
|
1828
|
-
async (params) => {
|
|
1829
|
-
setIsLoading(true);
|
|
1830
|
-
setError(null);
|
|
1831
|
-
try {
|
|
1832
|
-
const result = await remoteWorkService.logRemoteWork({
|
|
1833
|
-
userId: params.userId,
|
|
1834
|
-
entranceType: 0 /* ENTRY */,
|
|
1835
|
-
timestamp: params.timestamp,
|
|
1836
|
-
description: params.description
|
|
1837
|
-
});
|
|
1838
|
-
if (result.success) {
|
|
1839
|
-
return {
|
|
1840
|
-
success: true,
|
|
1841
|
-
data: result.entrance
|
|
1842
|
-
};
|
|
1843
|
-
}
|
|
1844
|
-
return {
|
|
1845
|
-
success: false,
|
|
1846
|
-
error: result.message || "Failed to log entry"
|
|
1847
|
-
};
|
|
1848
|
-
} catch (err) {
|
|
1849
|
-
const error2 = err;
|
|
1850
|
-
setError(error2);
|
|
1851
|
-
return {
|
|
1852
|
-
success: false,
|
|
1853
|
-
error: error2.message || "Failed to log entry"
|
|
1854
|
-
};
|
|
1855
|
-
} finally {
|
|
1856
|
-
setIsLoading(false);
|
|
1857
|
-
}
|
|
1858
|
-
},
|
|
1859
|
-
[remoteWorkService]
|
|
1860
|
-
);
|
|
1861
|
-
const logExit = useCallback6(
|
|
1862
|
-
async (params) => {
|
|
1863
|
-
setIsLoading(true);
|
|
1864
|
-
setError(null);
|
|
1865
|
-
try {
|
|
1866
|
-
const result = await remoteWorkService.logRemoteWork({
|
|
1867
|
-
userId: params.userId,
|
|
1868
|
-
entranceType: 1 /* EXIT */,
|
|
1869
|
-
timestamp: params.timestamp,
|
|
1870
|
-
description: params.description
|
|
1871
|
-
});
|
|
1872
|
-
if (result.success) {
|
|
1873
|
-
return {
|
|
1874
|
-
success: true,
|
|
1875
|
-
data: result.entrance
|
|
1876
|
-
};
|
|
1877
|
-
}
|
|
1878
|
-
return {
|
|
1879
|
-
success: false,
|
|
1880
|
-
error: result.message || "Failed to log exit"
|
|
1881
|
-
};
|
|
1882
|
-
} catch (err) {
|
|
1883
|
-
const error2 = err;
|
|
1884
|
-
setError(error2);
|
|
1885
|
-
return {
|
|
1886
|
-
success: false,
|
|
1887
|
-
error: error2.message || "Failed to log exit"
|
|
1888
|
-
};
|
|
1889
|
-
} finally {
|
|
1890
|
-
setIsLoading(false);
|
|
1891
|
-
}
|
|
1892
|
-
},
|
|
1893
|
-
[remoteWorkService]
|
|
1894
|
-
);
|
|
1895
|
-
return {
|
|
1896
|
-
logEntry,
|
|
1897
|
-
logExit,
|
|
1898
|
-
isLoading,
|
|
1899
|
-
error
|
|
1900
|
-
};
|
|
1901
|
-
}
|
|
1902
|
-
|
|
1903
1674
|
// src/index.ts
|
|
1904
1675
|
var SDK_VERSION = "1.0.4";
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
AuthService,
|
|
1908
|
-
CheckInService,
|
|
1909
|
-
DeviceAccessService,
|
|
1910
|
-
DeviceDirection,
|
|
1911
|
-
DeviceUsage,
|
|
1912
|
-
endpoints_exports as Endpoints,
|
|
1913
|
-
EntranceType,
|
|
1914
|
-
LocationService,
|
|
1915
|
-
NFCAccessService,
|
|
1916
|
-
PassgageAccessProvider,
|
|
1917
|
-
QRAccessService,
|
|
1918
|
-
RemoteWorkService,
|
|
1919
|
-
SDK_VERSION,
|
|
1920
|
-
calculateDistance,
|
|
1921
|
-
checkOnLocation,
|
|
1922
|
-
checkRepetitiveRead,
|
|
1923
|
-
clearReadRecords,
|
|
1924
|
-
createApiClient,
|
|
1925
|
-
formatDate,
|
|
1926
|
-
formatDateTime,
|
|
1927
|
-
formatISO,
|
|
1928
|
-
formatTime,
|
|
1929
|
-
parseISO,
|
|
1930
|
-
useLocation,
|
|
1931
|
-
usePassgageAccess,
|
|
1932
|
-
usePassgageAuth,
|
|
1933
|
-
usePassgageCheckIn,
|
|
1934
|
-
usePassgageNFCScanner,
|
|
1935
|
-
usePassgageQRScanner,
|
|
1936
|
-
usePassgageRemoteWork,
|
|
1937
|
-
validateCoordinates,
|
|
1938
|
-
validateDeviceId,
|
|
1939
|
-
validateNFCCode,
|
|
1940
|
-
validateQRCode
|
|
1941
|
-
};
|
|
1676
|
+
|
|
1677
|
+
export { ApiClient, AuthService, DeviceAccessService, DeviceDirection, DeviceUsage, endpoints_exports as Endpoints, EntranceType, LocationService, NfcAccessService, PassgageAccessProvider, QRAccessService, SDK_VERSION, addQrReadRecord, calculateDistance, checkOnLocation, checkRepetitiveRead, clearReadRecords, createApiClient, formatDate, formatDateTime, formatISO, formatTime, parseISO, reversedHexToDec, shouldValidateLocation, useLocation, usePassgageAccessContext, usePassgageNFCScanner, usePassgageQRScanner, validateCoordinates, validateDeviceId, validateLocation, validateNFCCode, validateQRCode };
|