dce-reactkit 3.4.0-beta.5 → 3.4.0-beta.7
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/cjs/index.js +54 -39
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/types/components/DBEntryManagerPanel/AddOrEditDBEntry/index.d.ts +1 -0
- package/dist/esm/index.js +54 -39
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/components/DBEntryManagerPanel/AddOrEditDBEntry/index.d.ts +1 -0
- package/package.json +1 -1
- package/src/components/AppWrapper.tsx +1 -1
- package/src/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.tsx +2 -6
- package/src/components/DBEntryManagerPanel/AddOrEditDBEntry/index.tsx +75 -51
- package/src/components/DBEntryManagerPanel/index.tsx +2 -2
package/package.json
CHANGED
|
@@ -8,7 +8,6 @@ import { MultiValue } from 'react-select';
|
|
|
8
8
|
// Import other types
|
|
9
9
|
import DBEntryFieldType from '../types/DBEntryFieldType';
|
|
10
10
|
|
|
11
|
-
|
|
12
11
|
/*------------------------------------------------------------------------*/
|
|
13
12
|
/* -------------------------------- Types ------------------------------- */
|
|
14
13
|
/*------------------------------------------------------------------------*/
|
|
@@ -87,7 +86,6 @@ const reducer = (state: State, action: Action): State => {
|
|
|
87
86
|
/*------------------------------------------------------------------------*/
|
|
88
87
|
|
|
89
88
|
const CreatableMultiselect: React.FC<Props> = (props) => {
|
|
90
|
-
|
|
91
89
|
// Destructure all props
|
|
92
90
|
const {
|
|
93
91
|
type,
|
|
@@ -109,7 +107,6 @@ const CreatableMultiselect: React.FC<Props> = (props) => {
|
|
|
109
107
|
// Destructure common state
|
|
110
108
|
const { inputValue } = state;
|
|
111
109
|
|
|
112
|
-
|
|
113
110
|
/*------------------------------------------------------------------------*/
|
|
114
111
|
/* ------------------------- Component Functions ------------------------ */
|
|
115
112
|
/*------------------------------------------------------------------------*/
|
|
@@ -178,7 +175,7 @@ const CreatableMultiselect: React.FC<Props> = (props) => {
|
|
|
178
175
|
.filter((val) => {
|
|
179
176
|
return !values.some((value) => {
|
|
180
177
|
return value === val;
|
|
181
|
-
})
|
|
178
|
+
});
|
|
182
179
|
})
|
|
183
180
|
);
|
|
184
181
|
onChange([...values, ...newValues] as string[]);
|
|
@@ -196,8 +193,6 @@ const CreatableMultiselect: React.FC<Props> = (props) => {
|
|
|
196
193
|
* @param event keyboard event
|
|
197
194
|
*/
|
|
198
195
|
const handleKeyDown: KeyboardEventHandler = (event) => {
|
|
199
|
-
event.preventDefault();
|
|
200
|
-
|
|
201
196
|
// Skip if no input value
|
|
202
197
|
if (!inputValue) {
|
|
203
198
|
return;
|
|
@@ -205,6 +200,7 @@ const CreatableMultiselect: React.FC<Props> = (props) => {
|
|
|
205
200
|
|
|
206
201
|
// Add values if enter or tab is pressed
|
|
207
202
|
if (['Enter', 'Tab'].includes(event.key)) {
|
|
203
|
+
event.preventDefault();
|
|
208
204
|
addValues(inputValue);
|
|
209
205
|
}
|
|
210
206
|
};
|
|
@@ -28,6 +28,7 @@ import DBEntryFieldType from '../types/DBEntryFieldType';
|
|
|
28
28
|
// Import other components
|
|
29
29
|
import CreatableMultiselect from './CreatableMultiselect';
|
|
30
30
|
|
|
31
|
+
|
|
31
32
|
/*------------------------------------------------------------------------*/
|
|
32
33
|
/* -------------------------------- Types ------------------------------- */
|
|
33
34
|
/*------------------------------------------------------------------------*/
|
|
@@ -42,12 +43,12 @@ type Props = {
|
|
|
42
43
|
onFinished: (dbEntry?: DBEntry) => void,
|
|
43
44
|
/**
|
|
44
45
|
* Function to validate the DBEntry before saving
|
|
45
|
-
* @param dbEntry
|
|
46
|
+
* @param dbEntry
|
|
46
47
|
*/
|
|
47
48
|
validateEntry?: (dbEntry: DBEntry) => Promise<void>,
|
|
48
49
|
/**
|
|
49
50
|
* Function to modify the DBEntry before saving
|
|
50
|
-
* @param dbEntry
|
|
51
|
+
* @param dbEntry
|
|
51
52
|
* @returns the modified DBEntry
|
|
52
53
|
*/
|
|
53
54
|
modifyEntry?: (dbEntry: DBEntry) => DBEntry,
|
|
@@ -57,6 +58,8 @@ type Props = {
|
|
|
57
58
|
dbEntryToEdit?: DBEntry,
|
|
58
59
|
// The unique object key of the DBEntry
|
|
59
60
|
idPropName: string,
|
|
61
|
+
// item name
|
|
62
|
+
itemName: string,
|
|
60
63
|
// Server endpoint path to save the DBEntry
|
|
61
64
|
saveEndpointPath: string,
|
|
62
65
|
// All entries in the database
|
|
@@ -159,6 +162,7 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
159
162
|
idPropName,
|
|
160
163
|
saveEndpointPath,
|
|
161
164
|
entries,
|
|
165
|
+
itemName,
|
|
162
166
|
} = props;
|
|
163
167
|
|
|
164
168
|
/* -------------- State ------------- */
|
|
@@ -199,6 +203,9 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
199
203
|
|
|
200
204
|
const modifiedEntry = modifyEntry ? modifyEntry(entry) : entry;
|
|
201
205
|
|
|
206
|
+
// add id key to entry so that when we delete the item, the key will always be "id"
|
|
207
|
+
modifiedEntry.id = modifiedEntry[idPropName];
|
|
208
|
+
|
|
202
209
|
// Send to server
|
|
203
210
|
try {
|
|
204
211
|
await visitServerEndpoint({
|
|
@@ -224,53 +231,35 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
224
231
|
onFinished(undefined);
|
|
225
232
|
};
|
|
226
233
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
/*----------------------------------------*/
|
|
234
|
-
|
|
235
|
-
// Body
|
|
236
|
-
let body: React.ReactNode;
|
|
237
|
-
|
|
238
|
-
/* ------------- Loading ------------ */
|
|
239
|
-
|
|
240
|
-
if (saving) {
|
|
241
|
-
body = (
|
|
242
|
-
<LoadingSpinner />
|
|
243
|
-
);
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
/* -------------- Form -------------- */
|
|
247
|
-
|
|
248
|
-
// If not saving, validate what the user has entered
|
|
249
|
-
if (!saving) {
|
|
250
|
-
// Validation error if there is one
|
|
251
|
-
let validationError: string | undefined = undefined;
|
|
234
|
+
/**
|
|
235
|
+
* Create validation error message for the DBEntry
|
|
236
|
+
* @author Yuen Ler Chow
|
|
237
|
+
* @param fields the fields to validate
|
|
238
|
+
* @returns the validation error message, or an empty string if no error
|
|
239
|
+
*/
|
|
252
240
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
241
|
+
const validate = (fields: DBEntryField[]) => {
|
|
242
|
+
let validationError = '';
|
|
243
|
+
for (let i = 0; i < fields.length; i += 1) {
|
|
244
|
+
const field = fields[i];
|
|
256
245
|
const value = entry[field.objectKey];
|
|
257
246
|
|
|
258
|
-
// Check if required field is empty
|
|
259
|
-
if (field.required && !value) {
|
|
247
|
+
// Check if required field is empty. Field is automatically required if it is the idPropName
|
|
248
|
+
if ((field.required || (field.objectKey === idPropName)) && !value) {
|
|
260
249
|
validationError = `Please fill in the ${field.label} field`;
|
|
261
|
-
|
|
250
|
+
return validationError;
|
|
262
251
|
}
|
|
263
252
|
|
|
264
253
|
// Check if unique field is unique
|
|
265
254
|
if (field.objectKey === idPropName) {
|
|
266
255
|
if (entries.find((e) => { return e[idPropName] === value; })) {
|
|
267
256
|
validationError = `An item with the ${field.label} ${value} already exists. ${field.label} must be unique.`;
|
|
268
|
-
|
|
257
|
+
return validationError;
|
|
269
258
|
}
|
|
270
259
|
}
|
|
271
260
|
|
|
272
261
|
// If they have entered a value for the field, check if it is valid
|
|
273
|
-
if (value) {
|
|
262
|
+
if (value || field.type === DBEntryFieldType.Object) {
|
|
274
263
|
// String validation
|
|
275
264
|
if (field.type === DBEntryFieldType.String) {
|
|
276
265
|
if (
|
|
@@ -334,24 +323,54 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
334
323
|
&& value[j] < field.minNumber
|
|
335
324
|
) {
|
|
336
325
|
validationError = `${field.label} values must be at least ${field.minNumber}`;
|
|
337
|
-
|
|
338
|
-
}
|
|
326
|
+
return validationError;
|
|
327
|
+
} if (
|
|
339
328
|
// Maximum value requirement is defined
|
|
340
329
|
field.maxNumber
|
|
341
330
|
// Value is too large
|
|
342
331
|
&& value[j] > field.maxNumber
|
|
343
332
|
) {
|
|
344
333
|
validationError = `${field.label} values must be at most ${field.maxNumber}`;
|
|
345
|
-
|
|
334
|
+
return validationError;
|
|
346
335
|
}
|
|
347
336
|
}
|
|
348
337
|
}
|
|
338
|
+
} else if (field.type === DBEntryFieldType.Object) {
|
|
339
|
+
validationError = validate(field.subfields);
|
|
349
340
|
}
|
|
350
341
|
}
|
|
351
342
|
if (validationError) {
|
|
352
|
-
|
|
343
|
+
return validationError;
|
|
353
344
|
}
|
|
354
345
|
}
|
|
346
|
+
return validationError;
|
|
347
|
+
};
|
|
348
|
+
|
|
349
|
+
/*------------------------------------------------------------------------*/
|
|
350
|
+
/* ------------------------------- Render ------------------------------- */
|
|
351
|
+
/*------------------------------------------------------------------------*/
|
|
352
|
+
|
|
353
|
+
/*----------------------------------------*/
|
|
354
|
+
/* ---------------- Views --------------- */
|
|
355
|
+
/*----------------------------------------*/
|
|
356
|
+
|
|
357
|
+
// Body
|
|
358
|
+
let body: React.ReactNode;
|
|
359
|
+
|
|
360
|
+
/* ------------- Loading ------------ */
|
|
361
|
+
|
|
362
|
+
if (saving) {
|
|
363
|
+
body = (
|
|
364
|
+
<LoadingSpinner />
|
|
365
|
+
);
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
/* -------------- Form -------------- */
|
|
369
|
+
|
|
370
|
+
// If not saving, validate what the user has entered
|
|
371
|
+
if (!saving) {
|
|
372
|
+
// Validation error if there is one
|
|
373
|
+
const validationError = validate(entryFields);
|
|
355
374
|
|
|
356
375
|
/**
|
|
357
376
|
* Render a single entry field
|
|
@@ -367,7 +386,8 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
367
386
|
key={field.objectKey}
|
|
368
387
|
className="mb-2"
|
|
369
388
|
>
|
|
370
|
-
<div
|
|
389
|
+
<div
|
|
390
|
+
className="input-group"
|
|
371
391
|
style={{
|
|
372
392
|
pointerEvents: (disabled ? 'none' : 'auto'),
|
|
373
393
|
}}
|
|
@@ -475,7 +495,8 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
475
495
|
key={field.objectKey}
|
|
476
496
|
className="mb-2"
|
|
477
497
|
>
|
|
478
|
-
<div
|
|
498
|
+
<div
|
|
499
|
+
className="input-group"
|
|
479
500
|
style={{
|
|
480
501
|
pointerEvents: (disabled ? 'none' : 'auto'),
|
|
481
502
|
}}
|
|
@@ -589,7 +610,7 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
589
610
|
);
|
|
590
611
|
}
|
|
591
612
|
|
|
592
|
-
if (field.type
|
|
613
|
+
if (field.type === DBEntryFieldType.Object) {
|
|
593
614
|
return (
|
|
594
615
|
<div
|
|
595
616
|
key={field.objectKey}
|
|
@@ -603,11 +624,13 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
603
624
|
{field.label}
|
|
604
625
|
</span>
|
|
605
626
|
{/* Add each subfield */}
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
627
|
+
<div className="flex-grow-1 p-2 form-control">
|
|
628
|
+
{
|
|
629
|
+
field.subfields.map((subfield: DBEntryField) => {
|
|
630
|
+
return <div>{renderEntryField(subfield, disabled)}</div>;
|
|
631
|
+
})
|
|
632
|
+
}
|
|
633
|
+
</div>
|
|
611
634
|
</div>
|
|
612
635
|
</div>
|
|
613
636
|
);
|
|
@@ -615,11 +638,12 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
615
638
|
|
|
616
639
|
// This should never happen
|
|
617
640
|
return null;
|
|
618
|
-
}
|
|
641
|
+
};
|
|
619
642
|
|
|
620
643
|
// UI
|
|
621
644
|
body = (
|
|
622
645
|
<div>
|
|
646
|
+
<h1>{dbEntryToEdit ? `Edit ${itemName}` : `Create ${itemName}`}</h1>
|
|
623
647
|
{/* Entry fields */}
|
|
624
648
|
{
|
|
625
649
|
entryFields.map((field: DBEntryField) => {
|
|
@@ -637,7 +661,7 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
637
661
|
className="btn btn-primary btn-lg me-1"
|
|
638
662
|
aria-label="Save changes"
|
|
639
663
|
onClick={async () => {
|
|
640
|
-
if (validationError) {
|
|
664
|
+
if (validationError && validationError.length > 0) {
|
|
641
665
|
return alert(
|
|
642
666
|
'Please fix the following error',
|
|
643
667
|
validationError,
|
|
@@ -645,7 +669,7 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
645
669
|
}
|
|
646
670
|
if (validateEntry) {
|
|
647
671
|
try {
|
|
648
|
-
validateEntry(entry);
|
|
672
|
+
await validateEntry(entry);
|
|
649
673
|
} catch (error) {
|
|
650
674
|
return alert(
|
|
651
675
|
'Please fix the following error',
|
|
@@ -672,7 +696,7 @@ const AddOrEditDBEntry: React.FC<Props> = (props) => {
|
|
|
672
696
|
Cancel
|
|
673
697
|
</button>
|
|
674
698
|
</div>
|
|
675
|
-
</div
|
|
699
|
+
</div>
|
|
676
700
|
);
|
|
677
701
|
}
|
|
678
702
|
|
|
@@ -224,7 +224,6 @@ const reducer = (state: State, action: Action): State => {
|
|
|
224
224
|
/*------------------------------------------------------------------------*/
|
|
225
225
|
|
|
226
226
|
const DBEntryManagerPanel: React.FC<Props> = (props) => {
|
|
227
|
-
|
|
228
227
|
// Destructure all props
|
|
229
228
|
const {
|
|
230
229
|
entryFields,
|
|
@@ -238,7 +237,7 @@ const DBEntryManagerPanel: React.FC<Props> = (props) => {
|
|
|
238
237
|
disableEdit,
|
|
239
238
|
collectionName,
|
|
240
239
|
adminsOnly,
|
|
241
|
-
filterQuery
|
|
240
|
+
filterQuery,
|
|
242
241
|
} = props;
|
|
243
242
|
|
|
244
243
|
/* -------------- State ------------- */
|
|
@@ -481,6 +480,7 @@ const DBEntryManagerPanel: React.FC<Props> = (props) => {
|
|
|
481
480
|
dbEntryToEdit={dbEntryToEdit}
|
|
482
481
|
idPropName={idPropName}
|
|
483
482
|
entries={dbEntries}
|
|
483
|
+
itemName={itemName}
|
|
484
484
|
onFinished={(entry?: DBEntry) => {
|
|
485
485
|
dispatch({
|
|
486
486
|
type: ActionType.FinishAdd,
|