@agilemotion/oui-react-js 1.8.27 → 1.8.29
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/dist/ApplicationManager.js +13 -8
- package/dist/DateUtils.js +78 -0
- package/dist/DynamicJS.js +5 -0
- package/dist/RestService.js +2 -3
- package/dist/Utils.js +3 -3
- package/dist/components/DataGrid.js +5 -1
- package/dist/components/DataGridColumn.js +25 -4
- package/dist/components/HtmlPanel.js +4 -2
- package/dist/components/Icon.js +3 -0
- package/dist/components/PDFViewer.js +6 -1
- package/dist/components/TabPanel.js +6 -0
- package/dist/components/TableCellContent.js +46 -1
- package/dist/components/facialRecognition/FaceRecognitionComponent.js +205 -0
- package/dist/components/facialRecognition/FacialRecognition.css +3 -0
- package/dist/components/facialRecognition/FacialRegistration.js +73 -0
- package/dist/components/facialRecognition/FacialVerification.js +84 -0
- package/dist/components/facialRecognition/Modal.js +33 -0
- package/dist/components/facialRecognition/service/faceApi.js +55 -0
- package/dist/components/form/BaseField.js +1 -4
- package/dist/components/form/FieldSet.js +4 -2
- package/dist/components/form/Form.js +1 -0
- package/dist/components/form/GridField.js +5 -5
- package/dist/components/form/ImageEditor.js +200 -84
- package/dist/components/form/LookupField.js +3 -4
- package/dist/components/form/RadioGroup.js +1 -1
- package/dist/components/layout/Layout.js +16 -0
- package/dist/components/layout/View.js +2 -0
- package/dist/components/signatures/DocumentContainer.js +1 -1
- package/dist/components/signatures/ViewUtils.js +1 -1
- package/dist/event/ActionHandlers.js +1 -1
- package/dist/event/ServiceCallActionHandler.js +2 -1
- package/dist/js/Validators.js +2 -1
- package/package.json +13 -4
|
@@ -645,14 +645,19 @@ class ApplicationManager {
|
|
|
645
645
|
objValue = null;
|
|
646
646
|
}
|
|
647
647
|
if (_this2.isExpression(objValue)) {
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
scriptValue
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
648
|
+
try {
|
|
649
|
+
let scriptValue = _DynamicJS.default.executeScript(`resolveParam_${Math.random().toString().replace('.', '_')}`, objValue, null, null, eventData);
|
|
650
|
+
if (typeof scriptValue === 'undefined') {
|
|
651
|
+
scriptValue = null;
|
|
652
|
+
}
|
|
653
|
+
let objKey = key === 'valueExpression' ? 'value' : key;
|
|
654
|
+
updated[objKey] = scriptValue !== null && scriptValue.instanceType === 'TypedValue' ? scriptValue.value : scriptValue;
|
|
655
|
+
if (typeof updated[objKey] === 'undefined') {
|
|
656
|
+
delete updated.type;
|
|
657
|
+
}
|
|
658
|
+
} catch (e) {
|
|
659
|
+
console.error("Error processing parameter expression : " + objValue);
|
|
660
|
+
throw e;
|
|
656
661
|
}
|
|
657
662
|
} else {
|
|
658
663
|
updated[key] = objValue;
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.makeDateReplacer = makeDateReplacer;
|
|
7
|
+
// Build "YYYY-MM-DDTHH:mm:ss.SSS+HH:MM" for a given time zone
|
|
8
|
+
function zonedISOString(date) {
|
|
9
|
+
let timeZone = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'Africa/Johannesburg';
|
|
10
|
+
// 1) Get wall-clock parts in the target zone
|
|
11
|
+
const dtf = new Intl.DateTimeFormat('en-CA', {
|
|
12
|
+
timeZone,
|
|
13
|
+
hour12: false,
|
|
14
|
+
year: 'numeric',
|
|
15
|
+
month: '2-digit',
|
|
16
|
+
day: '2-digit',
|
|
17
|
+
hour: '2-digit',
|
|
18
|
+
minute: '2-digit',
|
|
19
|
+
second: '2-digit'
|
|
20
|
+
});
|
|
21
|
+
const parts = Object.fromEntries(dtf.formatToParts(date).map(p => [p.type, p.value]));
|
|
22
|
+
const ms = String(date.getMilliseconds()).padStart(3, '0');
|
|
23
|
+
|
|
24
|
+
// 2) Compute numeric offset (+/- minutes) for that zone at this instant
|
|
25
|
+
// (works across DST if your zone observes it)
|
|
26
|
+
const asUTC = Date.UTC(Number(parts.year), Number(parts.month) - 1, Number(parts.day), Number(parts.hour), Number(parts.minute), Number(parts.second), date.getMilliseconds());
|
|
27
|
+
const offsetMinutes = Math.round((asUTC - date.getTime()) / 60000); // e.g. +120 for SAST
|
|
28
|
+
const sign = offsetMinutes >= 0 ? '+' : '-';
|
|
29
|
+
const abs = Math.abs(offsetMinutes);
|
|
30
|
+
const offHH = String(Math.trunc(abs / 60)).padStart(2, '0');
|
|
31
|
+
const offMM = String(abs % 60).padStart(2, '0');
|
|
32
|
+
const offset = `${sign}${offHH}:${offMM}`;
|
|
33
|
+
return `${parts.year}-${parts.month}-${parts.day}T${parts.hour}:${parts.minute}:${parts.second}.${ms}${offset}`;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Replacer: converts Date instances anywhere in the object graph
|
|
37
|
+
function makeDateReplacer() {
|
|
38
|
+
let {
|
|
39
|
+
timeZone = 'Africa/Johannesburg',
|
|
40
|
+
mode = 'zoned-iso'
|
|
41
|
+
} = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
42
|
+
return function replacer(_key, value) {
|
|
43
|
+
if (value instanceof Date) {
|
|
44
|
+
if (mode === 'epoch-ms') return value.getTime(); // 1732575600000
|
|
45
|
+
if (mode === 'utc-iso') return value.toISOString(); // 2025-11-25T22:00:00.000Z
|
|
46
|
+
return zonedISOString(value, timeZone); // default: SAST ISO with +02:00
|
|
47
|
+
}
|
|
48
|
+
return value;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Usage
|
|
53
|
+
const obj = {
|
|
54
|
+
data: {
|
|
55
|
+
person: {
|
|
56
|
+
dob: new Date('2025-11-25T22:00:00.000Z')
|
|
57
|
+
},
|
|
58
|
+
when: new Date(),
|
|
59
|
+
// now
|
|
60
|
+
list: [1, new Date('2025-06-01T10:00:00Z')]
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
// Serialize with SAST (UTC+02:00)
|
|
65
|
+
const jsonSAST = JSON.stringify(obj, makeDateReplacer({
|
|
66
|
+
timeZone: 'Africa/Johannesburg'
|
|
67
|
+
}));
|
|
68
|
+
console.log(jsonSAST);
|
|
69
|
+
|
|
70
|
+
// If you prefer epoch millis everywhere (more portable)
|
|
71
|
+
const jsonEpoch = JSON.stringify(obj, makeDateReplacer({
|
|
72
|
+
mode: 'epoch-ms'
|
|
73
|
+
}));
|
|
74
|
+
|
|
75
|
+
// If you want to keep UTC ISO but be explicit (baseline behavior)
|
|
76
|
+
const jsonUTC = JSON.stringify(obj, makeDateReplacer({
|
|
77
|
+
mode: 'utc-iso'
|
|
78
|
+
}));
|
package/dist/DynamicJS.js
CHANGED
|
@@ -6,6 +6,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.default = exports.TEMPLATE_REGEX = void 0;
|
|
7
7
|
var _ApplicationManager = _interopRequireWildcard(require("./ApplicationManager"));
|
|
8
8
|
var _Utils = _interopRequireDefault(require("./Utils"));
|
|
9
|
+
var _DateUtils = require("./DateUtils");
|
|
9
10
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
10
11
|
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
11
12
|
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
@@ -40,6 +41,9 @@ class DynamicJS {
|
|
|
40
41
|
return _Utils.default.isNull(value) || value.toString().trim().length === 0;
|
|
41
42
|
}
|
|
42
43
|
evaluateExpression = expression => {
|
|
44
|
+
if (typeof expression !== 'string') {
|
|
45
|
+
return expression;
|
|
46
|
+
}
|
|
43
47
|
return _ApplicationManager.default.resolveExpressionValue(expression);
|
|
44
48
|
};
|
|
45
49
|
parseScript = (script, componentId, isScriptlet) => {
|
|
@@ -88,6 +92,7 @@ class DynamicJS {
|
|
|
88
92
|
eventParam.data = event.data;
|
|
89
93
|
}
|
|
90
94
|
try {
|
|
95
|
+
// NB : The javascript serializer uses UCT for dates and it is 2 hours behind. TODO : use DateUtils.makeDateReplacer
|
|
91
96
|
let func = new Function(parsedScript.replace(/\$event/g, JSON.stringify(eventParam)));
|
|
92
97
|
if (!_Utils.default.isNull(name)) {
|
|
93
98
|
DynamicJS.prototype[name] = func;
|
package/dist/RestService.js
CHANGED
|
@@ -43,7 +43,7 @@ class RestService {
|
|
|
43
43
|
let requestBodyParamFound = false;
|
|
44
44
|
for (const parameter of service.parameters) {
|
|
45
45
|
let parameterValue = _ApplicationManager.default.resolveParameterValue(parameter, request.event ? request.event.data : null);
|
|
46
|
-
if (!_Utils.default.isNull(parameter.validator) && !_Utils.default.evaluateBooleanExpression(parameter.validator.nullable, parameter.name) && this.isParamValueNull(parameterValue)) {
|
|
46
|
+
if (!_Utils.default.isNull(parameter.validator) && (parameter.validator.nullable && !_Utils.default.evaluateBooleanExpression(parameter.validator.nullable, parameter.name) || _Utils.default.evaluateBooleanExpression(parameter.validator.required && _Utils.default.evaluateBooleanExpression(parameter.validator.required, parameter.name))) && this.isParamValueNull(parameterValue)) {
|
|
47
47
|
reject({
|
|
48
48
|
errorType: 'INVALID_PARAMETER',
|
|
49
49
|
parameter
|
|
@@ -188,8 +188,7 @@ class RestService {
|
|
|
188
188
|
if (_Utils.default.isNull(parameter.type)) {
|
|
189
189
|
parameter.type = 'String';
|
|
190
190
|
}
|
|
191
|
-
if (!_Utils.default.isNull(
|
|
192
|
-
console.error('INVALID PARAMETER : ', parameter);
|
|
191
|
+
if (!_Utils.default.isNull(parameter.validator) && (parameter.validator.nullable && !_Utils.default.evaluateBooleanExpression(parameter.validator.nullable, parameter.name) || _Utils.default.evaluateBooleanExpression(parameter.validator.required && _Utils.default.evaluateBooleanExpression(parameter.validator.required, parameter.name))) && this.isParamValueNull(parameter.value)) {
|
|
193
192
|
reject({
|
|
194
193
|
errorType: 'INVALID_PARAMETER',
|
|
195
194
|
parameter
|
package/dist/Utils.js
CHANGED
|
@@ -13,7 +13,7 @@ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e
|
|
|
13
13
|
const colors = ['#F44336', '#e91e63', '#9c27b0', '#673ab7', '#ff9800', '#ff5722', '#795548', '#607d8b', '#3f51b5', '#2196F3', '#00bcd4', '#009688', '#2196F3', '#32c787', '#00BCD4', '#ff5652', '#ffc107', '#ff85af', '#FF9800', '#39bbb0', '#4CAF50', '#ffeb3b', '#ffc107'];
|
|
14
14
|
class Utils {
|
|
15
15
|
constructor() {}
|
|
16
|
-
static SYSTEM_ERROR_MESSAGE = "A system error has
|
|
16
|
+
static SYSTEM_ERROR_MESSAGE = "A system error has occurred. Please contact your system administrator";
|
|
17
17
|
static isNull(value) {
|
|
18
18
|
return value === null || typeof value === 'undefined';
|
|
19
19
|
}
|
|
@@ -147,7 +147,7 @@ class Utils {
|
|
|
147
147
|
return JSON.stringify(request);
|
|
148
148
|
}
|
|
149
149
|
static getFieldColspan = field => {
|
|
150
|
-
return !Utils.isNull(field.attributes) && !Utils.isNull(field.attributes['colspan']) ?
|
|
150
|
+
return !Utils.isNull(field.attributes) && !Utils.isNull(field.attributes['colspan']) ? parseInt(typeof field.attributes.colspan === 'string' ? _DynamicJS.default.executeScript("__colspan_" + field.id, field.attributes.colspan) : field.attributes.colspan) : 1;
|
|
151
151
|
};
|
|
152
152
|
static createRow = () => {
|
|
153
153
|
let row = {};
|
|
@@ -353,7 +353,7 @@ class Utils {
|
|
|
353
353
|
};
|
|
354
354
|
static publishSystemErrorMessage = function (viewId) {
|
|
355
355
|
let component = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
356
|
-
Utils.publishErrorMessage('A system error has
|
|
356
|
+
Utils.publishErrorMessage('A system error has occurred. Please try again later', viewId, component);
|
|
357
357
|
};
|
|
358
358
|
static base64ToArrayBuffer = base64 => {
|
|
359
359
|
const binaryString = window.atob(base64);
|
|
@@ -276,6 +276,9 @@ const DataGrid = exports.DataGrid = /*#__PURE__*/_react.default.memo(/*#__PURE__
|
|
|
276
276
|
if (!_Utils.default.isNull(props.config.visible)) {
|
|
277
277
|
setVisible(_Utils.default.evaluateBooleanExpression(props.config.visible, props.config.id));
|
|
278
278
|
}
|
|
279
|
+
},
|
|
280
|
+
set visible(visible) {
|
|
281
|
+
setVisible(visible);
|
|
279
282
|
}
|
|
280
283
|
};
|
|
281
284
|
};
|
|
@@ -917,7 +920,8 @@ const DataGrid = exports.DataGrid = /*#__PURE__*/_react.default.memo(/*#__PURE__
|
|
|
917
920
|
dataBinding: !_Utils.default.isNull(column.dataBinding) ? column.dataBinding : column.id,
|
|
918
921
|
row: row,
|
|
919
922
|
contentType: column.fieldType || column.editor?.fieldType,
|
|
920
|
-
cellFormatter: column.cellFormatter
|
|
923
|
+
cellFormatter: column.cellFormatter,
|
|
924
|
+
criteria: criteria
|
|
921
925
|
}))));
|
|
922
926
|
})))), _Utils.default.isNull(props.pagination) || props.pagination === true ? /*#__PURE__*/_react.default.createElement(_TablePagination.default, {
|
|
923
927
|
rowsPerPageOptions: rowsPerPageOptions,
|
|
@@ -11,6 +11,8 @@ var _reactSuperResponsiveTable = require("react-super-responsive-table");
|
|
|
11
11
|
var _TableSortLabel = _interopRequireDefault(require("@mui/material/TableSortLabel"));
|
|
12
12
|
require("./DataGrid.css");
|
|
13
13
|
var _styles = require("@mui/styles");
|
|
14
|
+
var _Tooltip = _interopRequireDefault(require("@mui/material/Tooltip"));
|
|
15
|
+
var _Box = _interopRequireDefault(require("@mui/material/Box"));
|
|
14
16
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
17
|
const useStyles = (0, _styles.makeStyles)(theme => ({
|
|
16
18
|
tableHeaderCell: {
|
|
@@ -148,11 +150,30 @@ const DataGridColumn = /*#__PURE__*/_react.default.memo(/*#__PURE__*/_react.defa
|
|
|
148
150
|
},
|
|
149
151
|
onMouseDown: e => handleResizeStart(e, props.config.id)
|
|
150
152
|
}), /*#__PURE__*/_react.default.createElement(_TableSortLabel.default, {
|
|
151
|
-
active:
|
|
152
|
-
direction:
|
|
153
|
+
active: false,
|
|
154
|
+
direction: "desc",
|
|
153
155
|
onClick: props.createSortHandler(config.id)
|
|
154
|
-
|
|
156
|
+
// keep the sort icon from squashing the text
|
|
157
|
+
,
|
|
158
|
+
sx: {
|
|
159
|
+
'& .MuiTableSortLabel-icon': {
|
|
160
|
+
flexShrink: 0
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
}, /*#__PURE__*/_react.default.createElement(_Tooltip.default, {
|
|
164
|
+
title: label + postfix
|
|
165
|
+
}, /*#__PURE__*/_react.default.createElement(_Box.default, {
|
|
166
|
+
component: "span",
|
|
167
|
+
sx: {
|
|
168
|
+
display: 'inline-block',
|
|
169
|
+
maxWidth: '100%',
|
|
170
|
+
overflow: 'hidden',
|
|
171
|
+
textOverflow: 'ellipsis',
|
|
172
|
+
whiteSpace: 'nowrap',
|
|
173
|
+
verticalAlign: 'bottom'
|
|
174
|
+
}
|
|
175
|
+
}, label + postfix)), 'id' === config.id ? /*#__PURE__*/_react.default.createElement("span", {
|
|
155
176
|
className: classes.visuallyHidden
|
|
156
|
-
},
|
|
177
|
+
}, "sorted descending") : null));
|
|
157
178
|
}));
|
|
158
179
|
var _default = exports.default = DataGridColumn;
|
|
@@ -83,9 +83,11 @@ const HtmlPanel = /*#__PURE__*/_react.default.memo(/*#__PURE__*/_react.default.f
|
|
|
83
83
|
get id() {
|
|
84
84
|
return props.config.id;
|
|
85
85
|
},
|
|
86
|
-
loadData: actionConfig => {
|
|
86
|
+
loadData: (actionConfig, event) => {
|
|
87
87
|
if (actionConfig && actionConfig.value) {
|
|
88
|
-
|
|
88
|
+
console.log("\n\n\n\n\n HTML PANEL : ", actionConfig.value);
|
|
89
|
+
let data = _ApplicationManager.default.resolveExpressionValue(actionConfig.value, event?.data);
|
|
90
|
+
console.log("DATA : ", data);
|
|
89
91
|
let templateScript = Handlebars.compile(template);
|
|
90
92
|
setContent(templateScript(data));
|
|
91
93
|
}
|
package/dist/components/Icon.js
CHANGED
|
@@ -26,6 +26,7 @@ var _Notifications = _interopRequireDefault(require("@mui/icons-material/Notific
|
|
|
26
26
|
var _Person = _interopRequireDefault(require("@mui/icons-material/Person"));
|
|
27
27
|
var _Refresh = _interopRequireDefault(require("@mui/icons-material/Refresh"));
|
|
28
28
|
var _Folder = _interopRequireDefault(require("@mui/icons-material/Folder"));
|
|
29
|
+
var _FolderOpen = _interopRequireDefault(require("@mui/icons-material/FolderOpen"));
|
|
29
30
|
var _AttachFile = _interopRequireDefault(require("@mui/icons-material/AttachFile"));
|
|
30
31
|
var _CalendarToday = _interopRequireDefault(require("@mui/icons-material/CalendarToday"));
|
|
31
32
|
var _react = _interopRequireWildcard(require("react"));
|
|
@@ -166,6 +167,8 @@ class Icon extends _react.Component {
|
|
|
166
167
|
});
|
|
167
168
|
} else if (this.props.id === 'FOLDER') {
|
|
168
169
|
return /*#__PURE__*/_react.default.createElement(_Folder.default, null);
|
|
170
|
+
} else if (this.props.id === 'FOLDER_OPEN') {
|
|
171
|
+
return /*#__PURE__*/_react.default.createElement(_FolderOpen.default, null);
|
|
169
172
|
} else if (this.props.id === 'CALENDAR') {
|
|
170
173
|
return /*#__PURE__*/_react.default.createElement(_CalendarToday.default, null);
|
|
171
174
|
} else if (this.props.id === 'SIGN') {
|
|
@@ -4,10 +4,12 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var _react =
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
8
|
var _reactPdf = require("react-pdf");
|
|
9
9
|
var _Utils = _interopRequireDefault(require("../Utils"));
|
|
10
10
|
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
11
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
12
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
11
13
|
_reactPdf.pdfjs.GlobalWorkerOptions.workerSrc = `//cdnjs.cloudflare.com/ajax/libs/pdf.js/${_reactPdf.pdfjs.version}/pdf.worker.js`;
|
|
12
14
|
const PDFViewer = props => {
|
|
13
15
|
const [pageNumber, setPageNumber] = _react.default.useState(1);
|
|
@@ -21,6 +23,9 @@ const PDFViewer = props => {
|
|
|
21
23
|
props.onDocumentLoadSuccess(numPages);
|
|
22
24
|
}
|
|
23
25
|
};
|
|
26
|
+
(0, _react.useEffect)(() => {
|
|
27
|
+
setPageNumber(1);
|
|
28
|
+
}, [props.file]);
|
|
24
29
|
const changePage = offset => {
|
|
25
30
|
setPageNumber(pageNumber + offset);
|
|
26
31
|
};
|
|
@@ -110,6 +110,12 @@ const TabPanel = /*#__PURE__*/_react.default.memo(/*#__PURE__*/_react.default.fo
|
|
|
110
110
|
}
|
|
111
111
|
return children;
|
|
112
112
|
},
|
|
113
|
+
set value(value) {
|
|
114
|
+
setTabValue(value);
|
|
115
|
+
},
|
|
116
|
+
get value() {
|
|
117
|
+
return tabValue;
|
|
118
|
+
},
|
|
113
119
|
setPageHasError: (pageId, hasError) => {
|
|
114
120
|
setErrors({
|
|
115
121
|
...errors,
|
|
@@ -16,6 +16,7 @@ const TableCellContent = /*#__PURE__*/_react.default.memo(/*#__PURE__*/_react.de
|
|
|
16
16
|
const [refresher, setRefresher] = _react.default.useState(true);
|
|
17
17
|
const [editor, setEditor] = _react.default.useState(null);
|
|
18
18
|
const editorValue = _react.default.useRef(null);
|
|
19
|
+
const escapeRegex = s => s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
19
20
|
const formatter = new Intl.NumberFormat("en-ZA", {
|
|
20
21
|
style: "currency",
|
|
21
22
|
currency: "ZAR",
|
|
@@ -97,6 +98,45 @@ const TableCellContent = /*#__PURE__*/_react.default.memo(/*#__PURE__*/_react.de
|
|
|
97
98
|
const formatted = `${String(date.getDate()).padStart(2, '0')}/` + `${String(date.getMonth() + 1).padStart(2, '0')}/` + `${date.getFullYear()}`;
|
|
98
99
|
return formatted;
|
|
99
100
|
};
|
|
101
|
+
|
|
102
|
+
// Build flat, deduped list of search terms from criteria.parameters
|
|
103
|
+
const buildSearchTerms = parameters => {
|
|
104
|
+
const set = new Set();
|
|
105
|
+
for (const p of parameters || []) {
|
|
106
|
+
if (p?.value && typeof p.value === 'string') {
|
|
107
|
+
for (const term of p.value.split(/\s+/)) {
|
|
108
|
+
const t = term.trim();
|
|
109
|
+
if (t) set.add(t.toLowerCase());
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
return Array.from(set); // lowercase terms
|
|
114
|
+
};
|
|
115
|
+
const highlightTokens = (value, terms) => {
|
|
116
|
+
if (!value || !terms?.length) return value;
|
|
117
|
+
|
|
118
|
+
// Combine terms into a single capturing group for split
|
|
119
|
+
const pattern = `(${terms.map(t => escapeRegex(t)).join('|')})`;
|
|
120
|
+
const splitRegex = new RegExp(pattern, 'gi');
|
|
121
|
+
const exactRegex = new RegExp(`^(?:${pattern})$`, 'i'); // to test parts
|
|
122
|
+
|
|
123
|
+
const parts = value.split(splitRegex);
|
|
124
|
+
return parts.map((part, i) => {
|
|
125
|
+
const isMatch = exactRegex.test(part);
|
|
126
|
+
return isMatch ? /*#__PURE__*/_react.default.createElement("span", {
|
|
127
|
+
key: i,
|
|
128
|
+
style: {
|
|
129
|
+
backgroundColor: '#ffeb3b',
|
|
130
|
+
color: '#000',
|
|
131
|
+
borderRadius: '4px',
|
|
132
|
+
fontWeight: 'bold',
|
|
133
|
+
boxShadow: '0 0 3px rgba(0,0,0,0.3)'
|
|
134
|
+
}
|
|
135
|
+
}, part) : /*#__PURE__*/_react.default.createElement("span", {
|
|
136
|
+
key: i
|
|
137
|
+
}, part);
|
|
138
|
+
});
|
|
139
|
+
};
|
|
100
140
|
const getStaticDisplayValue = () => {
|
|
101
141
|
let displayValue;
|
|
102
142
|
let contentValue;
|
|
@@ -133,7 +173,12 @@ const TableCellContent = /*#__PURE__*/_react.default.memo(/*#__PURE__*/_react.de
|
|
|
133
173
|
// TODO : First check if the editor has a display template. If it does, use it to get the display value
|
|
134
174
|
displayValue = contentValue.dataRecordDescription;
|
|
135
175
|
} else {
|
|
136
|
-
|
|
176
|
+
if (props.criteria && props.criteria.parameters) {
|
|
177
|
+
const terms = buildSearchTerms(props.criteria.parameters);
|
|
178
|
+
return highlightTokens(contentValue, terms);
|
|
179
|
+
} else {
|
|
180
|
+
return contentValue;
|
|
181
|
+
}
|
|
137
182
|
}
|
|
138
183
|
}
|
|
139
184
|
if (props.cellFormatter) {
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var faceapi = _interopRequireWildcard(require("face-api.js"));
|
|
9
|
+
var _faceApi2 = require("./service/faceApi");
|
|
10
|
+
var _reactRouterDom = require("react-router-dom");
|
|
11
|
+
require("./FacialRecognition.css");
|
|
12
|
+
var _Button = _interopRequireDefault(require("@mui/material/Button"));
|
|
13
|
+
var _Utils = _interopRequireDefault(require("../../Utils"));
|
|
14
|
+
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
|
|
15
|
+
function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function (e) { return e ? t : r; })(e); }
|
|
16
|
+
function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != typeof e && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && {}.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
|
|
17
|
+
const FaceRecognitionComponent = _ref => {
|
|
18
|
+
let {
|
|
19
|
+
mode,
|
|
20
|
+
processingPath,
|
|
21
|
+
onComplete
|
|
22
|
+
} = _ref;
|
|
23
|
+
const [imageUrl, setImageUrl] = (0, _react.useState)(null);
|
|
24
|
+
const [boxes, setBoxes] = (0, _react.useState)([]);
|
|
25
|
+
const [step, setStep] = (0, _react.useState)(1);
|
|
26
|
+
const [modelsLoaded, setModelsLoaded] = (0, _react.useState)(false);
|
|
27
|
+
const [loading, setLoading] = (0, _react.useState)(false);
|
|
28
|
+
const [resultMessage, setResultMessage] = (0, _react.useState)(null);
|
|
29
|
+
const [resultSuccess, setResultSuccess] = (0, _react.useState)(null);
|
|
30
|
+
const videoRef = (0, _react.useRef)(null);
|
|
31
|
+
const streamRef = (0, _react.useRef)(null);
|
|
32
|
+
const canvasRef = (0, _react.useRef)(null);
|
|
33
|
+
const navigate = (0, _reactRouterDom.useNavigate)();
|
|
34
|
+
(0, _react.useEffect)(() => {
|
|
35
|
+
const loadModels = async () => {
|
|
36
|
+
await faceapi.nets.ssdMobilenetv1.loadFromUri('/models');
|
|
37
|
+
await faceapi.nets.faceLandmark68Net.loadFromUri('/models');
|
|
38
|
+
await faceapi.nets.faceRecognitionNet.loadFromUri('/models');
|
|
39
|
+
setModelsLoaded(true);
|
|
40
|
+
};
|
|
41
|
+
loadModels();
|
|
42
|
+
}, []);
|
|
43
|
+
(0, _react.useEffect)(() => {
|
|
44
|
+
if (step === 2 && videoRef.current) {
|
|
45
|
+
loadCamera();
|
|
46
|
+
} else if (step === 3 && streamRef.current) {
|
|
47
|
+
const stream = streamRef.current;
|
|
48
|
+
stream?.getTracks().forEach(track => track.stop());
|
|
49
|
+
}
|
|
50
|
+
}, [step]);
|
|
51
|
+
(0, _react.useEffect)(() => {
|
|
52
|
+
const video = videoRef.current;
|
|
53
|
+
if (!video || !modelsLoaded || step !== 2) return;
|
|
54
|
+
const detect = () => detectFace();
|
|
55
|
+
video.addEventListener('canplay', detect);
|
|
56
|
+
const interval = setInterval(detect, 200);
|
|
57
|
+
return () => {
|
|
58
|
+
video.removeEventListener('canplay', detect);
|
|
59
|
+
clearInterval(interval);
|
|
60
|
+
};
|
|
61
|
+
}, [modelsLoaded, step]);
|
|
62
|
+
const loadCamera = async () => {
|
|
63
|
+
try {
|
|
64
|
+
const stream = await navigator.mediaDevices.getUserMedia({
|
|
65
|
+
video: true
|
|
66
|
+
});
|
|
67
|
+
if (videoRef.current) {
|
|
68
|
+
videoRef.current.srcObject = stream;
|
|
69
|
+
}
|
|
70
|
+
streamRef.current = stream;
|
|
71
|
+
} catch (err) {
|
|
72
|
+
console.error('Error accessing webcam:', err);
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
(0, _react.useEffect)(() => {
|
|
76
|
+
// 👇 THIS is the cleanup for stopping the webcam
|
|
77
|
+
return () => {
|
|
78
|
+
const stream = streamRef.current;
|
|
79
|
+
stream?.getTracks().forEach(track => track.stop());
|
|
80
|
+
};
|
|
81
|
+
}, []);
|
|
82
|
+
const detectFace = async () => {
|
|
83
|
+
const video = videoRef.current;
|
|
84
|
+
const canvas = canvasRef.current;
|
|
85
|
+
if (!video || !canvas) return;
|
|
86
|
+
canvas.width = video.videoWidth;
|
|
87
|
+
canvas.height = video.videoHeight;
|
|
88
|
+
const detections = await faceapi.detectAllFaces(video).withFaceLandmarks().withFaceDescriptors();
|
|
89
|
+
const resizedDetections = faceapi.resizeResults(detections, {
|
|
90
|
+
width: video.videoWidth,
|
|
91
|
+
height: video.videoHeight
|
|
92
|
+
});
|
|
93
|
+
setBoxes(resizedDetections.map(d => d.detection.box));
|
|
94
|
+
const ctx = canvas.getContext('2d');
|
|
95
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
96
|
+
faceapi.draw.drawDetections(canvas, resizedDetections);
|
|
97
|
+
faceapi.draw.drawFaceLandmarks(canvas, resizedDetections);
|
|
98
|
+
};
|
|
99
|
+
const captureImage = async () => {
|
|
100
|
+
if (loading) return;
|
|
101
|
+
setLoading(true);
|
|
102
|
+
const video = videoRef.current;
|
|
103
|
+
try {
|
|
104
|
+
const detection = await faceapi.detectSingleFace(video).withFaceLandmarks().withFaceDescriptor();
|
|
105
|
+
if (!detection) {
|
|
106
|
+
alert("No face detected.");
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
const box = detection.detection.box;
|
|
110
|
+
const croppedCanvas = document.createElement('canvas');
|
|
111
|
+
croppedCanvas.width = box.width;
|
|
112
|
+
croppedCanvas.height = box.height;
|
|
113
|
+
const ctx = croppedCanvas.getContext('2d');
|
|
114
|
+
ctx.drawImage(video, box.x, box.y, box.width, box.height, 0, 0, box.width, box.height);
|
|
115
|
+
const faceDataUrl = croppedCanvas.toDataURL();
|
|
116
|
+
const descriptor = detection.descriptor;
|
|
117
|
+
setImageUrl(faceDataUrl);
|
|
118
|
+
const response = mode === 'register' ? await (0, _faceApi2.registerFace)(descriptor, faceDataUrl, processingPath) : await (0, _faceApi2.verifyFace)(descriptor, processingPath);
|
|
119
|
+
if (response.outcome !== 'SUCCESS') {
|
|
120
|
+
setStep(3);
|
|
121
|
+
setResultMessage(response.message || `Failed to ${mode} face.`);
|
|
122
|
+
setResultSuccess(false);
|
|
123
|
+
onComplete(response);
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
setResultMessage(response.message || `${mode === 'register' ? 'Registered' : 'Verified'} successfully!`);
|
|
127
|
+
setResultSuccess(true);
|
|
128
|
+
setStep(3);
|
|
129
|
+
onComplete(response);
|
|
130
|
+
} catch (err) {
|
|
131
|
+
//console.log('API errors:', err);
|
|
132
|
+
setResultMessage('Verification failed.' || `${mode === 'verify' ? 'Verification' : 'Registeration'} failed!`);
|
|
133
|
+
setStep(3);
|
|
134
|
+
//alert('Something went wrong while processing your face. Please try again.');
|
|
135
|
+
} finally {
|
|
136
|
+
setLoading(false);
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
return /*#__PURE__*/_react.default.createElement("div", {
|
|
140
|
+
style: {
|
|
141
|
+
display: 'flex',
|
|
142
|
+
alignContent: 'center',
|
|
143
|
+
justifyContent: 'center'
|
|
144
|
+
}
|
|
145
|
+
}, /*#__PURE__*/_react.default.createElement("div", {
|
|
146
|
+
style: {
|
|
147
|
+
maxWidth: '28rem',
|
|
148
|
+
textAlign: 'center'
|
|
149
|
+
}
|
|
150
|
+
}, step < 3 ? /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("h2", {
|
|
151
|
+
className: "text-xl font-bold mb-2"
|
|
152
|
+
}, mode === 'register' ? "Let's register your face" : "Facial verification"), /*#__PURE__*/_react.default.createElement("p", {
|
|
153
|
+
className: "text-gray-600 mb-6"
|
|
154
|
+
}, mode === 'register' ? 'Look into the camera to register your face.' : 'Look into the camera for verification.'), step === 1 && /*#__PURE__*/_react.default.createElement(_Button.default, {
|
|
155
|
+
variant: 'contained',
|
|
156
|
+
size: "large",
|
|
157
|
+
color: 'primary',
|
|
158
|
+
onClick: () => setStep(2)
|
|
159
|
+
}, "Start ", mode === 'register' ? 'Registration' : 'Verification'), step === 2 && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", {
|
|
160
|
+
style: {
|
|
161
|
+
display: 'flex',
|
|
162
|
+
alignContent: 'center',
|
|
163
|
+
justifyContent: 'center',
|
|
164
|
+
position: 'relative',
|
|
165
|
+
aspectRatio: '16 / 9',
|
|
166
|
+
margin: '1em 0',
|
|
167
|
+
height: 'auto',
|
|
168
|
+
borderRadius: '4px'
|
|
169
|
+
}
|
|
170
|
+
}, /*#__PURE__*/_react.default.createElement("video", {
|
|
171
|
+
ref: videoRef,
|
|
172
|
+
autoPlay: true,
|
|
173
|
+
muted: true,
|
|
174
|
+
playsInline: true,
|
|
175
|
+
style: {
|
|
176
|
+
width: '100%',
|
|
177
|
+
height: 'auto'
|
|
178
|
+
}
|
|
179
|
+
}), /*#__PURE__*/_react.default.createElement("canvas", {
|
|
180
|
+
ref: canvasRef,
|
|
181
|
+
style: {
|
|
182
|
+
position: 'absolute',
|
|
183
|
+
top: 0,
|
|
184
|
+
left: 0,
|
|
185
|
+
width: '100%',
|
|
186
|
+
height: '100%'
|
|
187
|
+
},
|
|
188
|
+
className: "absolute top-0 left-0 w-full h-full"
|
|
189
|
+
})), /*#__PURE__*/_react.default.createElement(_Button.default, {
|
|
190
|
+
variant: 'contained',
|
|
191
|
+
size: "large",
|
|
192
|
+
color: 'primary',
|
|
193
|
+
onClick: captureImage,
|
|
194
|
+
disabled: loading || !videoRef.current
|
|
195
|
+
}, loading ? 'Processing...' : 'Capture'))) : /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("h2", {
|
|
196
|
+
className: `text-xl font-bold mb-2 ${resultSuccess ? 'text-green-600' : 'text-red-600'}`
|
|
197
|
+
}, resultSuccess ? mode === 'register' ? "" : "" : "Something went wrong"), /*#__PURE__*/_react.default.createElement("p", {
|
|
198
|
+
className: `text-xl font-bold mb-2 ${resultSuccess ? 'text-green-600' : 'text-red-600'}`
|
|
199
|
+
}, resultMessage || `Face ${mode} result.`), imageUrl && /*#__PURE__*/_react.default.createElement("img", {
|
|
200
|
+
src: imageUrl,
|
|
201
|
+
alt: "Captured Face",
|
|
202
|
+
className: "rounded-xl border shadow-lg w-full"
|
|
203
|
+
}))));
|
|
204
|
+
};
|
|
205
|
+
var _default = exports.default = FaceRecognitionComponent;
|