Skip to content

Commit e7a6e7c

Browse files
committed
feat: Add upload status events and dedicated error handlers
1 parent ac632a0 commit e7a6e7c

File tree

6 files changed

+84
-8
lines changed

6 files changed

+84
-8
lines changed

apps/widget/src/hooks/Phase1/usePhase1.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,18 @@ export function usePhase1({ goNext, texts, onManuallyEnterData }: IUsePhase1Prop
111111
{
112112
onSuccess(uploadData, uploadValues) {
113113
ParentWindow.UploadStarted({ templateId: uploadData._templateId, uploadId: uploadData._id });
114+
ParentWindow.UploadSuccess({ uploadId: uploadData._id, rowCount: uploadData.totalRecords });
114115
ParentWindow.UploadStatusSuccess({ uploadId: uploadData._id, rowCount: uploadData.totalRecords });
115116
setUploadInfo(uploadData);
116117
if (uploadValues.file) goNext();
117118
else onManuallyEnterData();
118119
},
119120
onError(error: IErrorObject) {
120-
ParentWindow.UploadStatusError({ error, status: error.statusCode });
121+
ParentWindow.UploadError({
122+
errorCode: error.statusCode || 500,
123+
errorMessage: error.message || 'Unknown Error',
124+
});
125+
ParentWindow.UploadStatusError(error);
121126
resetField('file');
122127
setError('file', {
123128
type: 'file',
@@ -131,7 +136,6 @@ export function usePhase1({ goNext, texts, onManuallyEnterData }: IUsePhase1Prop
131136
string[],
132137
IErrorObject,
133138
{ file: File }
134-
// eslint-disable-next-line prettier/prettier
135139
>(['getExcelSheetNames'], (excelSheetFile) => api.getExcelSheetNames(excelSheetFile), {
136140
onSuccess(sheetNames) {
137141
if (sheetNames.length <= 1) {

apps/widget/src/util/parent-window.ts

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@ export function DataImported(value: Record<string, any>[]) {
2828
export function ImportJobCreated(value: IUserJob) {
2929
window.parent.postMessage({ type: EventTypesEnum.IMPORT_JOB_CREATED, value }, '*');
3030
}
31+
export function UploadSuccess(value: { uploadId: string; rowCount: number }) {
32+
window.parent.postMessage({ type: EventTypesEnum.UPLOAD_SUCCESS, value }, '*');
33+
}
34+
export function UploadError(value: { errorCode: number; errorMessage: string }) {
35+
window.parent.postMessage({ type: EventTypesEnum.UPLOAD_ERROR, value }, '*');
36+
}
3137

3238
export function UploadStatusSuccess(value: { uploadId: string; rowCount: number }) {
3339
window.parent.postMessage(
@@ -47,16 +53,16 @@ export function UploadStatusSuccess(value: { uploadId: string; rowCount: number
4753
);
4854
}
4955

50-
export function UploadStatusError(value: { error?: { message?: string }; status: number }) {
56+
export function UploadStatusError(value: { message?: string; error?: string; statusCode?: number }) {
5157
window.parent.postMessage(
5258
{
5359
source: 'impler-embed',
5460
type: 'UPLOAD_STATUS',
5561
payload: {
5662
status: 'ERROR',
5763
meta: {
58-
errorCode: value.status || 500,
59-
errorMessage: value.error?.message || 'Unknown Network Error',
64+
errorCode: value.statusCode || 500,
65+
errorMessage: value.message || value.error || 'Unknown Network Error',
6066
timestamp: new Date().toISOString(),
6167
},
6268
},

libs/embed/src/shared/eventTypes.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ export enum EventTypesEnum {
77
UPLOAD_STARTED = 'UPLOAD_STARTED',
88
UPLOAD_TERMINATED = 'UPLOAD_TERMINATED',
99
UPLOAD_COMPLETED = 'UPLOAD_COMPLETED',
10+
UPLOAD_SUCCESS = 'UPLOAD_SUCCESS',
11+
UPLOAD_ERROR = 'UPLOAD_ERROR',
12+
DATA_IMPORTED = 'DATA_IMPORTED',
13+
IMPORT_JOB_CREATED = 'IMPORT_JOB_CREATED',
1014
}
1115

1216
export enum WidgetEventTypesEnum {

libs/shared/src/types/widget/widget.types.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,8 @@ export enum EventTypesEnum {
7575
UPLOAD_STARTED = 'UPLOAD_STARTED',
7676
UPLOAD_TERMINATED = 'UPLOAD_TERMINATED',
7777
UPLOAD_COMPLETED = 'UPLOAD_COMPLETED',
78+
UPLOAD_SUCCESS = 'UPLOAD_SUCCESS',
79+
UPLOAD_ERROR = 'UPLOAD_ERROR',
7880
DATA_IMPORTED = 'DATA_IMPORTED',
7981
IMPORT_JOB_CREATED = 'IMPORT_JOB_CREATED',
8082
}

packages/client/src/types.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@ export const EventTypes = {
3131
UPLOAD_STARTED: 'UPLOAD_STARTED',
3232
UPLOAD_TERMINATED: 'UPLOAD_TERMINATED',
3333
UPLOAD_COMPLETED: 'UPLOAD_COMPLETED',
34+
UPLOAD_SUCCESS: 'UPLOAD_SUCCESS',
35+
UPLOAD_ERROR: 'UPLOAD_ERROR',
3436
DATA_IMPORTED: 'DATA_IMPORTED',
3537
IMPORT_JOB_CREATED: 'IMPORT_JOB_CREATED',
3638
} as const;
@@ -135,6 +137,8 @@ export type UploadTemplateData = {
135137
templateId: string;
136138
};
137139
export type UploadData = { uploadId: string };
140+
export type UploadSuccessData = { uploadId: string; rowCount: number };
141+
export type UploadErrorData = { errorCode: number; errorMessage: string };
138142

139143
export type EventCalls =
140144
| {
@@ -157,6 +161,14 @@ export type EventCalls =
157161
type: typeof EventTypes.IMPORT_JOB_CREATED;
158162
value: IUserJob;
159163
}
164+
| {
165+
type: typeof EventTypes.UPLOAD_SUCCESS;
166+
value: UploadSuccessData;
167+
}
168+
| {
169+
type: typeof EventTypes.UPLOAD_ERROR;
170+
value: UploadErrorData;
171+
}
160172
| {
161173
type: typeof EventTypes.CLOSE_WIDGET;
162174
}
@@ -236,7 +248,10 @@ export interface IUseImplerProps {
236248
onUploadStart?: (value: UploadTemplateData) => void;
237249
onUploadTerminate?: (value: UploadData) => void;
238250
onUploadComplete?: (value: IUpload) => void;
251+
onUploadSuccess?: (value: UploadSuccessData) => void;
239252
onDataImported?: (importedData: Record<string, any>[]) => void;
240253
onWidgetClose?: () => void;
241254
onImportJobCreated?: (jobInfo: IUserJob) => void;
255+
onWidgetReady?: () => void;
256+
onUploadError?: (error: UploadErrorData) => void;
242257
}

packages/react/src/hooks/useImpler.ts

Lines changed: 48 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { useCallback, useEffect, useState } from 'react';
1+
import { useCallback, useEffect, useRef, useState } from 'react';
22
import { isObject, EventTypes, logError, EventCalls, IShowWidgetProps, IUseImplerProps } from '@impler/client';
33

44
export function useImpler({
@@ -19,13 +19,29 @@ export function useImpler({
1919
onDataImported,
2020
onUploadTerminate,
2121
onImportJobCreated,
22+
onWidgetReady,
23+
onUploadSuccess,
24+
onUploadError,
2225
}: IUseImplerProps) {
2326
const [uuid] = useState(generateUuid());
2427
const [isImplerInitiated, setIsImplerInitiated] = useState(false);
28+
const onWidgetReadyCalledRef = useRef(false);
29+
30+
/*
31+
* Use a ref so the listener always calls the latest version of callbacks
32+
* without needing to re-register with the embed library
33+
*/
34+
const onEventHappenRef = useRef<(eventData: EventCalls) => void>(() => {});
2535

2636
const onEventHappen = useCallback(
2737
(eventData: EventCalls) => {
2838
switch (eventData.type) {
39+
case EventTypes.WIDGET_READY:
40+
if (onWidgetReady && !onWidgetReadyCalledRef.current) {
41+
onWidgetReadyCalledRef.current = true;
42+
onWidgetReady();
43+
}
44+
break;
2945
case EventTypes.UPLOAD_STARTED:
3046
if (onUploadStart) onUploadStart(eventData.value);
3147
break;
@@ -35,6 +51,12 @@ export function useImpler({
3551
case EventTypes.UPLOAD_COMPLETED:
3652
if (onUploadComplete) onUploadComplete(eventData.value);
3753
break;
54+
case EventTypes.UPLOAD_SUCCESS:
55+
if (onUploadSuccess) onUploadSuccess(eventData.value);
56+
break;
57+
case EventTypes.UPLOAD_ERROR:
58+
if (onUploadError) onUploadError(eventData.value);
59+
break;
3860
case EventTypes.DATA_IMPORTED:
3961
if (onDataImported) onDataImported(eventData.value);
4062
break;
@@ -46,13 +68,32 @@ export function useImpler({
4668
break;
4769
}
4870
},
49-
[onUploadComplete, onUploadStart, onUploadTerminate, onWidgetClose]
71+
[
72+
onUploadComplete,
73+
onUploadStart,
74+
onUploadTerminate,
75+
onWidgetClose,
76+
onDataImported,
77+
onImportJobCreated,
78+
onWidgetReady,
79+
onUploadSuccess,
80+
onUploadError,
81+
]
5082
);
5183

84+
// Keep ref in sync with latest callback
85+
useEffect(() => {
86+
onEventHappenRef.current = onEventHappen;
87+
}, [onEventHappen]);
88+
5289
useEffect(() => {
5390
const readyCheckInterval = setInterval(() => {
5491
if (window.impler && window.impler.isReady()) {
5592
setIsImplerInitiated(true);
93+
if (onWidgetReady && !onWidgetReadyCalledRef.current) {
94+
onWidgetReadyCalledRef.current = true;
95+
onWidgetReady();
96+
}
5697
clearInterval(readyCheckInterval);
5798
}
5899
}, 1000);
@@ -66,7 +107,11 @@ export function useImpler({
66107
if (!window.impler) logError('IMPLER_UNDEFINED_ERROR');
67108
else {
68109
window.impler.init(uuid);
69-
window.impler.on('message', onEventHappen, uuid);
110+
/*
111+
* Use a stable wrapper that delegates to the ref,
112+
* so the embed always calls the latest version of onEventHappen
113+
*/
114+
window.impler.on('message', (data: EventCalls) => onEventHappenRef.current(data), uuid);
70115
}
71116
}, []);
72117

0 commit comments

Comments
 (0)