@pensasystems/pensa-react-native 0.1.0-beta.1 → 0.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/PensaSdkReactNative.podspec +1 -1
- package/README.md +99 -5
- package/android/build.gradle +2 -1
- package/android/src/main/java/com/pensasdkreactnative/PensaListeners.kt +10 -1
- package/android/src/main/java/com/pensasdkreactnative/PensaSdkReactNativeModule.kt +359 -9
- package/ios/PensaEventEmitter.swift +2 -1
- package/ios/PensaListeners.swift +12 -1
- package/ios/PensaSdkReactNative-Bridging-Header.h +4 -0
- package/ios/PensaSdkReactNative.mm +28 -0
- package/ios/PensaSdkReactNative.swift +287 -0
- package/lib/module/index.js +21 -0
- package/lib/module/index.js.map +1 -1
- package/lib/module/types.js +17 -0
- package/lib/module/types.js.map +1 -1
- package/lib/typescript/src/index.d.ts +14 -0
- package/lib/typescript/src/index.d.ts.map +1 -1
- package/lib/typescript/src/types.d.ts +122 -0
- package/lib/typescript/src/types.d.ts.map +1 -1
- package/package.json +1 -2
- package/src/index.tsx +56 -0
- package/src/types.ts +134 -0
package/README.md
CHANGED
|
@@ -7,17 +7,18 @@ The Pensa Mobile App SDK is a developer toolkit designed to simplify adding Pens
|
|
|
7
7
|
Integrating the Pensa SDK into your React Native project is simple. Follow the steps below to add the SDK and enable advanced shelf recognition features in your app:
|
|
8
8
|
|
|
9
9
|
### 1- Install the SDK Package
|
|
10
|
+
Registry: https://www.npmjs.com/package/@pensasystems/pensa-react-native
|
|
10
11
|
|
|
11
12
|
Add the npm package to your project:
|
|
12
13
|
|
|
13
14
|
```bash
|
|
14
|
-
npm install pensa-
|
|
15
|
+
npm install @pensasystems/pensa-react-native
|
|
15
16
|
```
|
|
16
17
|
|
|
17
18
|
or
|
|
18
19
|
|
|
19
20
|
```bash
|
|
20
|
-
yarn add pensa-
|
|
21
|
+
yarn add @pensasystems/pensa-react-native
|
|
21
22
|
```
|
|
22
23
|
|
|
23
24
|
### 2- Install Native Dependencies (iOS)
|
|
@@ -44,7 +45,7 @@ Initializing the Pensa SDK is straightforward. Follow the steps below to set up
|
|
|
44
45
|
Call `initPensa()` at the startup of your app, preferably in a root file like `index.js` before calling `AppRegistry.registerComponent`.
|
|
45
46
|
|
|
46
47
|
```tsx
|
|
47
|
-
import {initPensa} from 'pensa-
|
|
48
|
+
import {initPensa} from '@pensasystems/pensa-react-native';
|
|
48
49
|
|
|
49
50
|
initPensa({
|
|
50
51
|
clientId: 'YOUR_CLIENT_ID',
|
|
@@ -71,7 +72,7 @@ To listen to SDK events such as upload progress, success, or failure, you can cr
|
|
|
71
72
|
```tsx
|
|
72
73
|
// hooks/usePensaListeners.ts
|
|
73
74
|
import {useEffect} from 'react';
|
|
74
|
-
import {PensaEvents} from 'pensa-
|
|
75
|
+
import {PensaEvents} from '@pensasystems/pensa-react-native';
|
|
75
76
|
|
|
76
77
|
export const usePensaListeners = () => {
|
|
77
78
|
useEffect(() => {
|
|
@@ -151,7 +152,7 @@ import {
|
|
|
151
152
|
showScanArea,
|
|
152
153
|
showStockingScreen,
|
|
153
154
|
showStoreChecklist,
|
|
154
|
-
} from 'pensa-
|
|
155
|
+
} from '@pensasystems/pensa-react-native';
|
|
155
156
|
```
|
|
156
157
|
|
|
157
158
|
### Searching stores
|
|
@@ -273,6 +274,99 @@ showScanArea(scanId, storeId, globalStoreId)
|
|
|
273
274
|
|
|
274
275
|
This method allows you to navigate directly to a scan area without going through the store selection flow.
|
|
275
276
|
|
|
277
|
+
# Troubleshooting (Android)
|
|
278
|
+
|
|
279
|
+
## 1. Core Library Desugaring Required
|
|
280
|
+
|
|
281
|
+
If you see an error mentioning that `core library desugaring` must be enabled (typically referencing `AAR metadata` or `desugar_jdk_libs`), update your Android project as follows:
|
|
282
|
+
|
|
283
|
+
### **Step 1 — Add desugaring dependency**
|
|
284
|
+
|
|
285
|
+
In your app-level `build.gradle`:
|
|
286
|
+
|
|
287
|
+
```gradle
|
|
288
|
+
dependencies {
|
|
289
|
+
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
|
|
290
|
+
}
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
### **Step 2 — Enable desugaring & set Java 17**
|
|
294
|
+
|
|
295
|
+
Inside `android { compileOptions { ... } }`:
|
|
296
|
+
|
|
297
|
+
```gradle
|
|
298
|
+
compileOptions {
|
|
299
|
+
sourceCompatibility JavaVersion.VERSION_17
|
|
300
|
+
targetCompatibility JavaVersion.VERSION_17
|
|
301
|
+
coreLibraryDesugaringEnabled true
|
|
302
|
+
}
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
This configuration is required because the native Pensa Android SDK uses Java 17 language features.
|
|
306
|
+
|
|
307
|
+
---
|
|
308
|
+
|
|
309
|
+
## 2. Min SDK Version Requirement
|
|
310
|
+
|
|
311
|
+
If your Android build fails with a message indicating:
|
|
312
|
+
|
|
313
|
+
> *minSdkVersion cannot be smaller than 26 for library com.pensasystems:pensasdk*
|
|
314
|
+
|
|
315
|
+
Then your `minSdkVersion` must be updated.
|
|
316
|
+
|
|
317
|
+
### **Fix**
|
|
318
|
+
|
|
319
|
+
In your app-level `build.gradle`:
|
|
320
|
+
|
|
321
|
+
```gradle
|
|
322
|
+
defaultConfig {
|
|
323
|
+
minSdkVersion 26
|
|
324
|
+
}
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
The native Pensa Android SDK requires **minSdk 26**.
|
|
328
|
+
|
|
329
|
+
---
|
|
330
|
+
|
|
331
|
+
## 3. Metro Bundler Cannot Connect in Debug Mode (Android Only)
|
|
332
|
+
|
|
333
|
+
If the Android app cannot connect to Metro (e.g., blank screen, “Unable to load script from assets”), update your debug network rules.
|
|
334
|
+
|
|
335
|
+
### **Step 1 — Create `network_security_config.xml`**
|
|
336
|
+
|
|
337
|
+
Create a file:
|
|
338
|
+
|
|
339
|
+
```
|
|
340
|
+
android/app/src/main/res/xml/network_security_config.xml
|
|
341
|
+
```
|
|
342
|
+
|
|
343
|
+
With the following content:
|
|
344
|
+
|
|
345
|
+
```xml
|
|
346
|
+
<?xml version="1.0" encoding="utf-8"?>
|
|
347
|
+
<network-security-config>
|
|
348
|
+
<domain-config cleartextTrafficPermitted="true">
|
|
349
|
+
<domain includeSubdomains="true">10.0.2.2</domain>
|
|
350
|
+
<domain includeSubdomains="true">localhost</domain>
|
|
351
|
+
<domain includeSubdomains="true">127.0.0.1</domain>
|
|
352
|
+
</domain-config>
|
|
353
|
+
</network-security-config>
|
|
354
|
+
```
|
|
355
|
+
|
|
356
|
+
### **Step 2 — Reference it in `AndroidManifest.xml`**
|
|
357
|
+
|
|
358
|
+
Inside your `<application>` tag:
|
|
359
|
+
|
|
360
|
+
```xml
|
|
361
|
+
<application
|
|
362
|
+
android:networkSecurityConfig="@xml/network_security_config"
|
|
363
|
+
... >
|
|
364
|
+
```
|
|
365
|
+
|
|
366
|
+
This allows the Android emulator to access the React Native bundler during development.
|
|
367
|
+
|
|
368
|
+
---
|
|
369
|
+
|
|
276
370
|
### Example Project
|
|
277
371
|
|
|
278
372
|
For code examples and integration best practices, refer to our GitHub repository.
|
package/android/build.gradle
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
|
|
1
2
|
buildscript {
|
|
2
3
|
ext.getExtOrDefault = {name ->
|
|
3
4
|
return rootProject.ext.has(name) ? rootProject.ext.get(name) : project.properties['PensaSdkReactNative_' + name]
|
|
@@ -83,7 +84,7 @@ def kotlin_version = getExtOrDefault("kotlinVersion")
|
|
|
83
84
|
|
|
84
85
|
dependencies {
|
|
85
86
|
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.1.5'
|
|
86
|
-
implementation "com.pensasystems:pensasdk:1.0.
|
|
87
|
+
implementation "com.pensasystems:pensasdk:1.0.23"
|
|
87
88
|
implementation "com.facebook.react:react-android"
|
|
88
89
|
implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
|
|
89
90
|
}
|
|
@@ -2,9 +2,10 @@ package com.pensasdkreactnative
|
|
|
2
2
|
|
|
3
3
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
4
|
import com.pensasystems.pensasdk.listener.CantScanEventListener
|
|
5
|
+
import com.pensasystems.pensasdk.listener.CompletedScanWithGuidListener
|
|
5
6
|
import com.pensasystems.pensasdk.listener.ScanUploadListener
|
|
6
7
|
|
|
7
|
-
object PensaListeners : ScanUploadListener, CantScanEventListener {
|
|
8
|
+
object PensaListeners : ScanUploadListener, CantScanEventListener, CompletedScanWithGuidListener {
|
|
8
9
|
|
|
9
10
|
private var emitter: PensaEventEmitterModule? = null
|
|
10
11
|
|
|
@@ -41,4 +42,12 @@ object PensaListeners : ScanUploadListener, CantScanEventListener {
|
|
|
41
42
|
"reason" to cantScanReason
|
|
42
43
|
))
|
|
43
44
|
}
|
|
45
|
+
|
|
46
|
+
override fun onScanCompletedWithGuid(guid: String?, tdlinxId: String, scanId: Int) {
|
|
47
|
+
emitter?.sendEvent("onScanCompletedWithGuid", mapOf(
|
|
48
|
+
"guid" to guid,
|
|
49
|
+
"tdlinxId" to tdlinxId,
|
|
50
|
+
"scanId" to scanId
|
|
51
|
+
))
|
|
52
|
+
}
|
|
44
53
|
}
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
package com.pensasdkreactnative
|
|
2
2
|
|
|
3
|
+
import com.facebook.react.bridge.Arguments
|
|
3
4
|
import com.facebook.react.bridge.ReactApplicationContext
|
|
4
5
|
import com.facebook.react.bridge.ReactContextBaseJavaModule
|
|
5
6
|
import com.facebook.react.bridge.ReactMethod
|
|
6
7
|
import com.facebook.react.bridge.Promise
|
|
8
|
+
import com.facebook.react.bridge.ReadableArray
|
|
7
9
|
import com.facebook.react.bridge.ReadableMap
|
|
10
|
+
import com.facebook.react.bridge.ReadableType
|
|
8
11
|
import com.pensasystems.pensasdk.PensaSdk
|
|
9
12
|
import com.pensasystems.pensasdk.PensaSdkConfiguration
|
|
10
|
-
import com.pensasystems.pensasdk.
|
|
11
|
-
import com.pensasystems.pensasdk.
|
|
13
|
+
import com.pensasystems.pensasdk.model.PensaReportType
|
|
14
|
+
import com.pensasystems.pensasdk.model.PensaShelfItem
|
|
12
15
|
|
|
13
16
|
class PensaSdkReactNativeModule(reactContext: ReactApplicationContext) :
|
|
14
17
|
ReactContextBaseJavaModule(reactContext) {
|
|
@@ -40,6 +43,7 @@ class PensaSdkReactNativeModule(reactContext: ReactApplicationContext) :
|
|
|
40
43
|
.enableLogging(isLoggingEnabled)
|
|
41
44
|
.setScanUploadListener(PensaListeners)
|
|
42
45
|
.setCantScanEventListener(PensaListeners)
|
|
46
|
+
.setCompletedScanWithGuidListener(PensaListeners)
|
|
43
47
|
.build()
|
|
44
48
|
|
|
45
49
|
PensaSdk.initPensa(
|
|
@@ -69,7 +73,7 @@ class PensaSdkReactNativeModule(reactContext: ReactApplicationContext) :
|
|
|
69
73
|
|
|
70
74
|
@ReactMethod
|
|
71
75
|
fun showShelfScans(promise: Promise) {
|
|
72
|
-
val activity = currentActivity
|
|
76
|
+
val activity = reactApplicationContext.currentActivity
|
|
73
77
|
if (activity != null) {
|
|
74
78
|
PensaSdk.showShelfScans(
|
|
75
79
|
context = activity,
|
|
@@ -82,7 +86,7 @@ class PensaSdkReactNativeModule(reactContext: ReactApplicationContext) :
|
|
|
82
86
|
}
|
|
83
87
|
@ReactMethod
|
|
84
88
|
fun showProductScans(promise: Promise) {
|
|
85
|
-
val activity = currentActivity
|
|
89
|
+
val activity = reactApplicationContext.currentActivity
|
|
86
90
|
if (activity != null) {
|
|
87
91
|
PensaSdk.showProductScans(
|
|
88
92
|
context = activity,
|
|
@@ -96,7 +100,7 @@ class PensaSdkReactNativeModule(reactContext: ReactApplicationContext) :
|
|
|
96
100
|
|
|
97
101
|
@ReactMethod
|
|
98
102
|
fun showStoreSearchView(promise: Promise) {
|
|
99
|
-
val activity = currentActivity
|
|
103
|
+
val activity = reactApplicationContext.currentActivity
|
|
100
104
|
if (activity != null) {
|
|
101
105
|
PensaSdk.showStoreSearchView(
|
|
102
106
|
context = activity,
|
|
@@ -110,7 +114,7 @@ class PensaSdkReactNativeModule(reactContext: ReactApplicationContext) :
|
|
|
110
114
|
|
|
111
115
|
@ReactMethod
|
|
112
116
|
fun showStoresScreen(promise: Promise) {
|
|
113
|
-
val activity = currentActivity
|
|
117
|
+
val activity = reactApplicationContext.currentActivity
|
|
114
118
|
if (activity != null) {
|
|
115
119
|
PensaSdk.showStoresScreen(
|
|
116
120
|
context = activity,
|
|
@@ -124,7 +128,7 @@ class PensaSdkReactNativeModule(reactContext: ReactApplicationContext) :
|
|
|
124
128
|
|
|
125
129
|
@ReactMethod
|
|
126
130
|
fun showScanArea(scanId: Int, storeId: Int?, globalStoreId: String?, promise: Promise) {
|
|
127
|
-
val activity = currentActivity
|
|
131
|
+
val activity = reactApplicationContext.currentActivity
|
|
128
132
|
if (activity != null) {
|
|
129
133
|
PensaSdk.showScanArea(
|
|
130
134
|
context = activity,
|
|
@@ -141,7 +145,7 @@ class PensaSdkReactNativeModule(reactContext: ReactApplicationContext) :
|
|
|
141
145
|
|
|
142
146
|
@ReactMethod
|
|
143
147
|
fun showStockingScreen(promise: Promise) {
|
|
144
|
-
val activity = currentActivity
|
|
148
|
+
val activity = reactApplicationContext.currentActivity
|
|
145
149
|
if (activity != null) {
|
|
146
150
|
PensaSdk.showStockingScreen(
|
|
147
151
|
context = activity,
|
|
@@ -155,7 +159,7 @@ class PensaSdkReactNativeModule(reactContext: ReactApplicationContext) :
|
|
|
155
159
|
|
|
156
160
|
@ReactMethod
|
|
157
161
|
fun showStoreChecklist(globalStoreId: String, guid: String?, sectionKey: String?, promise: Promise) {
|
|
158
|
-
val activity = currentActivity
|
|
162
|
+
val activity = reactApplicationContext.currentActivity
|
|
159
163
|
if (activity != null) {
|
|
160
164
|
PensaSdk.showStoreChecklist(
|
|
161
165
|
context = activity,
|
|
@@ -169,4 +173,350 @@ class PensaSdkReactNativeModule(reactContext: ReactApplicationContext) :
|
|
|
169
173
|
promise.reject("NO_ACTIVITY", "Current activity is null")
|
|
170
174
|
}
|
|
171
175
|
}
|
|
176
|
+
|
|
177
|
+
@ReactMethod
|
|
178
|
+
fun showStoreChecklistWithGuid(globalStoreId: String, guid: String, promise: Promise) {
|
|
179
|
+
val activity = reactApplicationContext.currentActivity
|
|
180
|
+
if (activity != null) {
|
|
181
|
+
PensaSdk.showStoreChecklistWithGuid(
|
|
182
|
+
context = activity,
|
|
183
|
+
globalStoreId = globalStoreId,
|
|
184
|
+
guid = guid,
|
|
185
|
+
onError = { error -> promise.reject("SHOW_STORE_CHECKLIST_WITH_GUID_FAILED", error) }
|
|
186
|
+
)
|
|
187
|
+
promise.resolve(null)
|
|
188
|
+
} else {
|
|
189
|
+
promise.reject("NO_ACTIVITY", "Current activity is null")
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
@ReactMethod
|
|
194
|
+
fun fetchScanStatuses(scanIds: ReadableArray, promise: Promise) {
|
|
195
|
+
try {
|
|
196
|
+
val ids = mutableListOf<Int>()
|
|
197
|
+
for (i in 0 until scanIds.size()) {
|
|
198
|
+
ids.add(scanIds.getInt(i))
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
PensaSdk.fetchScanStatuses(
|
|
202
|
+
scanIds = ids,
|
|
203
|
+
onSuccess = { statuses ->
|
|
204
|
+
val rnList = statuses.map { status ->
|
|
205
|
+
val map = Arguments.createMap()
|
|
206
|
+
map.putInt("id", status.id)
|
|
207
|
+
map.putString("created", status.created)
|
|
208
|
+
map.putString("fastStatus", status.fastStatus)
|
|
209
|
+
map.putString("scanTimestamp", status.scanTimestamp)
|
|
210
|
+
map.putString("scannerId", status.scannerId)
|
|
211
|
+
map.putString("status", status.status)
|
|
212
|
+
|
|
213
|
+
val metadata = Arguments.createMap()
|
|
214
|
+
metadata.putString("guid", status.metadata?.guid)
|
|
215
|
+
metadata.putString("serverUploadStarted", status.metadata?.serverUploadStarted)
|
|
216
|
+
map.putMap("metadata", metadata)
|
|
217
|
+
|
|
218
|
+
map
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
val result = Arguments.createArray()
|
|
222
|
+
rnList.forEach { result.pushMap(it) }
|
|
223
|
+
promise.resolve(result)
|
|
224
|
+
|
|
225
|
+
},
|
|
226
|
+
onError = { error ->
|
|
227
|
+
promise.reject("FETCH_SCAN_STATUSES_FAILED", error)
|
|
228
|
+
}
|
|
229
|
+
)
|
|
230
|
+
} catch (e: Exception) {
|
|
231
|
+
promise.reject("FETCH_SCAN_STATUSES_EXCEPTION", e)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@ReactMethod
|
|
236
|
+
fun fetchVisitScansStatus(guid: String, promise: Promise) {
|
|
237
|
+
try {
|
|
238
|
+
PensaSdk.fetchVisitScansStatus(
|
|
239
|
+
guid = guid,
|
|
240
|
+
onSuccess = { result ->
|
|
241
|
+
val scans = result.scans
|
|
242
|
+
val map = Arguments.createMap().apply {
|
|
243
|
+
result.guid?.let { putString("guid", it) }
|
|
244
|
+
result.visitStatus?.let { putString("visitStatus", it) }
|
|
245
|
+
if (scans == null) {
|
|
246
|
+
} else {
|
|
247
|
+
val scansArray = Arguments.createArray()
|
|
248
|
+
scans.forEach { scan ->
|
|
249
|
+
val uploadCompleted = scan.uploadCompleted
|
|
250
|
+
scansArray.pushMap(
|
|
251
|
+
Arguments.createMap().apply {
|
|
252
|
+
scan.created?.let { putString("created", it) }
|
|
253
|
+
scan.fastStatus?.let { putString("fastStatus", it) }
|
|
254
|
+
scan.scanId?.let { putInt("scanId", it) }
|
|
255
|
+
scan.status?.let { putString("status", it) }
|
|
256
|
+
when (uploadCompleted) {
|
|
257
|
+
null -> {}
|
|
258
|
+
else -> putBoolean("uploadCompleted", uploadCompleted)
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
)
|
|
262
|
+
}
|
|
263
|
+
putArray("scans", scansArray)
|
|
264
|
+
}
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
promise.resolve(map)
|
|
268
|
+
},
|
|
269
|
+
onError = { error ->
|
|
270
|
+
promise.reject("FETCH_VISIT_SCANS_STATUS_FAILED", error)
|
|
271
|
+
}
|
|
272
|
+
)
|
|
273
|
+
} catch (e: Exception) {
|
|
274
|
+
promise.reject("FETCH_VISIT_SCANS_STATUS_EXCEPTION", e)
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
@ReactMethod
|
|
279
|
+
fun fetchOnDemandReports(
|
|
280
|
+
scanId: Double?,
|
|
281
|
+
guid: String?,
|
|
282
|
+
projectId: Double?,
|
|
283
|
+
reportTypes: ReadableArray?,
|
|
284
|
+
promise: Promise
|
|
285
|
+
) {
|
|
286
|
+
try {
|
|
287
|
+
val finalReportTypes = parseReportTypes(reportTypes)
|
|
288
|
+
val normalizedScanId = scanId?.toInt()
|
|
289
|
+
val normalizedProjectId = projectId?.toInt()
|
|
290
|
+
|
|
291
|
+
PensaSdk.fetchOnDemandReports(
|
|
292
|
+
scanId = normalizedScanId,
|
|
293
|
+
guid = guid,
|
|
294
|
+
projectId = normalizedProjectId,
|
|
295
|
+
reportTypes = finalReportTypes,
|
|
296
|
+
onSuccess = { report ->
|
|
297
|
+
val result = Arguments.createMap()
|
|
298
|
+
|
|
299
|
+
fun toProductMap(p: com.pensasystems.pensasdk.model.PensaOnDemandProduct) =
|
|
300
|
+
Arguments.createMap().apply {
|
|
301
|
+
p.brand?.let { putString("brand", it) }
|
|
302
|
+
p.brandId?.let { putInt("brandId", it) }
|
|
303
|
+
|
|
304
|
+
p.category?.let { putString("category", it) }
|
|
305
|
+
p.categoryId?.let { putInt("categoryId", it) }
|
|
306
|
+
|
|
307
|
+
p.expectedFacings?.let { putInt("expectedFacings", it) }
|
|
308
|
+
p.facings?.let { putInt("facings", it) }
|
|
309
|
+
|
|
310
|
+
p.manufacturer?.let { putString("manufacturer", it) }
|
|
311
|
+
p.manufacturerId?.let { putInt("manufacturerId", it) }
|
|
312
|
+
|
|
313
|
+
p.product?.let { putString("product", it) }
|
|
314
|
+
p.productId?.let { putInt("productId", it) }
|
|
315
|
+
|
|
316
|
+
p.upc?.let { putString("upc", it) }
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
fun toBucketMap(b: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket) =
|
|
320
|
+
Arguments.createMap().apply {
|
|
321
|
+
val arr = Arguments.createArray()
|
|
322
|
+
b.productList.forEach { arr.pushMap(toProductMap(it)) }
|
|
323
|
+
putArray("productList", arr)
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
fun putBucket(target: com.facebook.react.bridge.WritableMap, key: String, bucket: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?) {
|
|
327
|
+
if (bucket != null) target.putMap(key, toBucketMap(bucket))
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
fun fillReportBuckets(
|
|
331
|
+
target: com.facebook.react.bridge.WritableMap,
|
|
332
|
+
facingsSeen: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
333
|
+
fullFacings: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
334
|
+
fullFacingsAndProductPositions: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
335
|
+
itemsSeen: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
336
|
+
newProducts: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
337
|
+
newProductPackaging: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
338
|
+
missingPriceTag: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
339
|
+
noOos: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
340
|
+
oos: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
341
|
+
productPositions: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
342
|
+
scanFailed: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
343
|
+
scanRejected: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
344
|
+
scanSkipped: com.pensasystems.pensasdk.model.PensaOnDemandReportBucket?,
|
|
345
|
+
) {
|
|
346
|
+
finalReportTypes.distinct().forEach { t ->
|
|
347
|
+
when (t) {
|
|
348
|
+
PensaReportType.FACINGS_SEEN -> putBucket(target, "facingsSeen", facingsSeen)
|
|
349
|
+
PensaReportType.FULL_FACINGS -> putBucket(target, "fullFacings", fullFacings)
|
|
350
|
+
PensaReportType.FULL_FACINGS_AND_PRODUCT_POSITIONS ->
|
|
351
|
+
putBucket(target, "fullFacingsAndProductPositions", fullFacingsAndProductPositions)
|
|
352
|
+
PensaReportType.ITEMS_SEEN -> putBucket(target, "itemsSeen", itemsSeen)
|
|
353
|
+
PensaReportType.NEW_PRODUCTS -> putBucket(target, "newProducts", newProducts)
|
|
354
|
+
PensaReportType.NEW_PRODUCT_PACKAGING -> putBucket(target, "newProductPackaging", newProductPackaging)
|
|
355
|
+
PensaReportType.MISSING_PRICE_TAG -> putBucket(target, "missingPriceTag", missingPriceTag)
|
|
356
|
+
PensaReportType.NO_OOS -> putBucket(target, "noOos", noOos)
|
|
357
|
+
PensaReportType.OOS -> putBucket(target, "oos", oos)
|
|
358
|
+
PensaReportType.PRODUCT_POSITIONS -> putBucket(target, "productPositions", productPositions)
|
|
359
|
+
PensaReportType.SCAN_FAILED -> putBucket(target, "scanFailed", scanFailed)
|
|
360
|
+
PensaReportType.SCAN_REJECTED -> putBucket(target, "scanRejected", scanRejected)
|
|
361
|
+
PensaReportType.SCAN_SKIPPED -> putBucket(target, "scanSkipped", scanSkipped)
|
|
362
|
+
}
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
fillReportBuckets(
|
|
367
|
+
result,
|
|
368
|
+
report.facingsSeen,
|
|
369
|
+
report.fullFacings,
|
|
370
|
+
report.fullFacingsAndProductPositions,
|
|
371
|
+
report.itemsSeen,
|
|
372
|
+
report.newProducts,
|
|
373
|
+
report.newProductPackaging,
|
|
374
|
+
report.missingPriceTag,
|
|
375
|
+
report.noOos,
|
|
376
|
+
report.oos,
|
|
377
|
+
report.productPositions,
|
|
378
|
+
report.scanFailed,
|
|
379
|
+
report.scanRejected,
|
|
380
|
+
report.scanSkipped
|
|
381
|
+
)
|
|
382
|
+
|
|
383
|
+
// Other fields
|
|
384
|
+
report.projectId?.let { result.putInt("projectId", it) }
|
|
385
|
+
report.projectName?.let { result.putString("projectName", it) }
|
|
386
|
+
report.projectReportId?.let { result.putInt("projectReportId", it) }
|
|
387
|
+
report.projectReportName?.let { result.putString("projectReportName", it) }
|
|
388
|
+
report.realogramSignedUrl?.let { result.putString("realogramSignedUrl", it) }
|
|
389
|
+
report.shelfId?.let { result.putInt("shelfId", it) }
|
|
390
|
+
report.shelfName?.let { result.putString("shelfName", it) }
|
|
391
|
+
report.status?.let { result.putString("status", it) }
|
|
392
|
+
val scans = report.scans
|
|
393
|
+
if (scans != null) {
|
|
394
|
+
val scansArray = Arguments.createArray()
|
|
395
|
+
scans.forEach { scan ->
|
|
396
|
+
scansArray.pushMap(
|
|
397
|
+
Arguments.createMap().apply {
|
|
398
|
+
fillReportBuckets(
|
|
399
|
+
this,
|
|
400
|
+
scan.facingsSeen,
|
|
401
|
+
scan.fullFacings,
|
|
402
|
+
scan.fullFacingsAndProductPositions,
|
|
403
|
+
scan.itemsSeen,
|
|
404
|
+
scan.newProducts,
|
|
405
|
+
scan.newProductPackaging,
|
|
406
|
+
scan.missingPriceTag,
|
|
407
|
+
scan.noOos,
|
|
408
|
+
scan.oos,
|
|
409
|
+
scan.productPositions,
|
|
410
|
+
scan.scanFailed,
|
|
411
|
+
scan.scanRejected,
|
|
412
|
+
scan.scanSkipped
|
|
413
|
+
)
|
|
414
|
+
scan.projectId?.let { putInt("projectId", it) }
|
|
415
|
+
scan.projectName?.let { putString("projectName", it) }
|
|
416
|
+
scan.projectReportId?.let { putInt("projectReportId", it) }
|
|
417
|
+
scan.projectReportName?.let { putString("projectReportName", it) }
|
|
418
|
+
scan.realogramSignedUrl?.let { putString("realogramSignedUrl", it) }
|
|
419
|
+
scan.scanId?.let { putInt("scanId", it) }
|
|
420
|
+
scan.shelfId?.let { putInt("shelfId", it) }
|
|
421
|
+
scan.shelfName?.let { putString("shelfName", it) }
|
|
422
|
+
scan.status?.let { putString("status", it) }
|
|
423
|
+
}
|
|
424
|
+
)
|
|
425
|
+
}
|
|
426
|
+
result.putArray("scans", scansArray)
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
val visit = report.visit
|
|
430
|
+
if (visit != null) {
|
|
431
|
+
result.putMap(
|
|
432
|
+
"visit",
|
|
433
|
+
Arguments.createMap().apply {
|
|
434
|
+
visit.endTime?.let { putString("endTime", it) }
|
|
435
|
+
visit.identifier?.let { putString("identifier", it) }
|
|
436
|
+
visit.startTime?.let { putString("startTime", it) }
|
|
437
|
+
visit.storeName?.let { putString("storeName", it) }
|
|
438
|
+
visit.storeTdlinx?.let { putString("storeTdlinx", it) }
|
|
439
|
+
}
|
|
440
|
+
)
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
promise.resolve(result)
|
|
444
|
+
},
|
|
445
|
+
onError = { error ->
|
|
446
|
+
promise.reject("FETCH_ON_DEMAND_REPORTS_FAILED", error)
|
|
447
|
+
}
|
|
448
|
+
)
|
|
449
|
+
} catch (e: Exception) {
|
|
450
|
+
promise.reject("ON_DEMAND_EXCEPTION", e)
|
|
451
|
+
}
|
|
452
|
+
}
|
|
453
|
+
|
|
454
|
+
@ReactMethod
|
|
455
|
+
fun fetchProductImageBase64(productId: Int, promise: Promise) {
|
|
456
|
+
try {
|
|
457
|
+
val activity = reactApplicationContext.currentActivity
|
|
458
|
+
if (activity != null) {
|
|
459
|
+
PensaSdk.fetchProductImageBase64(
|
|
460
|
+
context = activity,
|
|
461
|
+
productId = productId,
|
|
462
|
+
onSuccess = { base64 ->
|
|
463
|
+
promise.resolve(base64)
|
|
464
|
+
},
|
|
465
|
+
onError = { error ->
|
|
466
|
+
promise.reject("FETCH_PRODUCT_IMAGE_FAILED", error)
|
|
467
|
+
}
|
|
468
|
+
)
|
|
469
|
+
} else {
|
|
470
|
+
promise.reject("NO_ACTIVITY", "Current activity is null")
|
|
471
|
+
}
|
|
472
|
+
} catch (e: Exception) {
|
|
473
|
+
promise.reject("FETCH_PRODUCT_IMAGE_EXCEPTION", e)
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
@ReactMethod
|
|
478
|
+
fun fetchShelves(globalStoreId: String, promise: Promise) {
|
|
479
|
+
try {
|
|
480
|
+
PensaSdk.fetchShelves(
|
|
481
|
+
globalStoreId = globalStoreId,
|
|
482
|
+
onSuccess = { shelves ->
|
|
483
|
+
val result = Arguments.createArray()
|
|
484
|
+
|
|
485
|
+
shelves.forEach { shelf ->
|
|
486
|
+
val map = Arguments.createMap()
|
|
487
|
+
map.putInt("id", shelf.id)
|
|
488
|
+
map.putString("name", shelf.name)
|
|
489
|
+
result.pushMap(map)
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
promise.resolve(result)
|
|
493
|
+
},
|
|
494
|
+
onError = { error ->
|
|
495
|
+
promise.reject("FETCH_SHELVES_FAILED", error)
|
|
496
|
+
}
|
|
497
|
+
)
|
|
498
|
+
} catch (e: Exception) {
|
|
499
|
+
promise.reject("FETCH_SHELVES_EXCEPTION", e)
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
// Helpers
|
|
504
|
+
|
|
505
|
+
private fun parseReportTypes(reportTypes: ReadableArray?): List<PensaReportType> {
|
|
506
|
+
if (reportTypes == null || reportTypes.size() == 0) return listOf(PensaReportType.ITEMS_SEEN)
|
|
507
|
+
|
|
508
|
+
val list = mutableListOf<PensaReportType>()
|
|
509
|
+
for (i in 0 until reportTypes.size()) {
|
|
510
|
+
if (reportTypes.getType(i) != ReadableType.String) continue
|
|
511
|
+
val raw = reportTypes.getString(i) ?: continue
|
|
512
|
+
val parsed = try {
|
|
513
|
+
PensaReportType.valueOf(raw)
|
|
514
|
+
} catch (e: Exception) {
|
|
515
|
+
null
|
|
516
|
+
}
|
|
517
|
+
if (parsed != null) list.add(parsed)
|
|
518
|
+
}
|
|
519
|
+
|
|
520
|
+
return if (list.isEmpty()) listOf(PensaReportType.ITEMS_SEEN) else list.distinct()
|
|
521
|
+
}
|
|
172
522
|
}
|
package/ios/PensaListeners.swift
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import Foundation
|
|
2
2
|
import PensaSdk
|
|
3
3
|
|
|
4
|
-
class PensaListeners: NSObject, ScanUploadListener, CantScanEventListener {
|
|
4
|
+
class PensaListeners: NSObject, ScanUploadListener, CantScanEventListener, CompletedScanWithGuidListener {
|
|
5
5
|
|
|
6
6
|
static let shared = PensaListeners()
|
|
7
7
|
|
|
@@ -34,4 +34,15 @@ class PensaListeners: NSObject, ScanUploadListener, CantScanEventListener {
|
|
|
34
34
|
body: ["tdlinxId": tdlinxId, "scanAreaId": scanAreaId, "reason": cantScanReason]
|
|
35
35
|
)
|
|
36
36
|
}
|
|
37
|
+
|
|
38
|
+
func onScanCompletedWithGuid(guid: String?, tdlinxId: String, scanId: Int) {
|
|
39
|
+
PensaEventEmitter.shared?.sendEvent(
|
|
40
|
+
withName: "onScanCompletedWithGuid",
|
|
41
|
+
body: [
|
|
42
|
+
"guid": guid ?? NSNull(),
|
|
43
|
+
"tdlinxId": tdlinxId,
|
|
44
|
+
"scanId": scanId
|
|
45
|
+
]
|
|
46
|
+
)
|
|
47
|
+
}
|
|
37
48
|
}
|
|
@@ -37,6 +37,10 @@ RCT_EXTERN_METHOD(showStoreChecklist:(NSString *)globalStoreId
|
|
|
37
37
|
withResolver:(RCTPromiseResolveBlock)resolve
|
|
38
38
|
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
39
39
|
|
|
40
|
+
RCT_EXTERN_METHOD(fetchShelves:(NSString *)globalStoreId
|
|
41
|
+
withResolver:(RCTPromiseResolveBlock)resolve
|
|
42
|
+
withRejecter:(RCTPromiseRejectBlock)reject)
|
|
43
|
+
|
|
40
44
|
@end
|
|
41
45
|
|
|
42
46
|
@interface RCT_EXTERN_MODULE(PensaEventEmitter, RCTEventEmitter)
|