dce-reactkit 3.4.0-beta.8 → 3.4.0

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.
Files changed (128) hide show
  1. package/.eslintrc.js +0 -2
  2. package/dist/cjs/index.js +190 -36371
  3. package/dist/cjs/index.js.map +1 -1
  4. package/dist/cjs/types/helpers/abbreviate.test.d.ts +5 -0
  5. package/dist/cjs/types/helpers/avg.test.d.ts +5 -0
  6. package/dist/cjs/types/helpers/canReviewLogs.d.ts +5 -0
  7. package/dist/cjs/types/helpers/canReviewLogs.test.d.ts +5 -0
  8. package/dist/cjs/types/helpers/ceilToNumDecimals.test.d.ts +5 -0
  9. package/dist/cjs/types/helpers/compareArraysByProp.test.d.ts +5 -0
  10. package/dist/cjs/types/helpers/extractProp.test.d.ts +5 -0
  11. package/dist/cjs/types/helpers/floorToNumDecimals.test.d.ts +5 -0
  12. package/dist/cjs/types/helpers/forceNumIntoBounds.test.d.ts +5 -0
  13. package/dist/cjs/types/helpers/genCSV.test.d.ts +5 -0
  14. package/dist/cjs/types/helpers/genCommaList.d.ts +12 -0
  15. package/dist/cjs/types/helpers/genCommaList.test.d.ts +1 -0
  16. package/dist/cjs/types/helpers/getHumanReadableDate.test.d.ts +5 -0
  17. package/dist/cjs/types/helpers/getLocalTimeInfo.test.d.ts +6 -0
  18. package/dist/cjs/types/helpers/getMonthName.test.d.ts +5 -0
  19. package/dist/cjs/types/helpers/getOrdinal.test.d.ts +5 -0
  20. package/dist/cjs/types/helpers/getPartOfDay.test.d.ts +5 -0
  21. package/dist/cjs/types/helpers/getTimeInfoInET.test.d.ts +5 -0
  22. package/dist/cjs/types/helpers/validators/shared/constants/ERROR_MESSAGES.d.ts +10 -0
  23. package/dist/cjs/types/helpers/validators/shared/helpers/validateRegex.d.ts +19 -0
  24. package/dist/cjs/types/helpers/validators/shared/types/ValidationResult.d.ts +12 -0
  25. package/dist/cjs/types/helpers/validators/validateEmail.d.ts +11 -0
  26. package/dist/cjs/types/helpers/validators/validateEmail.test.d.ts +1 -0
  27. package/dist/cjs/types/helpers/validators/validatePhoneNumber.d.ts +11 -0
  28. package/dist/cjs/types/helpers/validators/validatePhoneNumber.test.d.ts +1 -0
  29. package/dist/cjs/types/helpers/validators/validateString.d.ts +22 -0
  30. package/dist/cjs/types/helpers/validators/validateString.test.d.ts +1 -0
  31. package/dist/cjs/types/index.d.ts +1 -6
  32. package/dist/esm/index.js +419 -36579
  33. package/dist/esm/index.js.map +1 -1
  34. package/dist/esm/types/helpers/abbreviate.test.d.ts +5 -0
  35. package/dist/esm/types/helpers/avg.test.d.ts +5 -0
  36. package/dist/esm/types/helpers/canReviewLogs.d.ts +5 -0
  37. package/dist/esm/types/helpers/canReviewLogs.test.d.ts +5 -0
  38. package/dist/esm/types/helpers/ceilToNumDecimals.test.d.ts +5 -0
  39. package/dist/esm/types/helpers/compareArraysByProp.test.d.ts +5 -0
  40. package/dist/esm/types/helpers/extractProp.test.d.ts +5 -0
  41. package/dist/esm/types/helpers/floorToNumDecimals.test.d.ts +5 -0
  42. package/dist/esm/types/helpers/forceNumIntoBounds.test.d.ts +5 -0
  43. package/dist/esm/types/helpers/genCSV.test.d.ts +5 -0
  44. package/dist/esm/types/helpers/genCommaList.d.ts +12 -0
  45. package/dist/esm/types/helpers/genCommaList.test.d.ts +1 -0
  46. package/dist/esm/types/helpers/getHumanReadableDate.test.d.ts +5 -0
  47. package/dist/esm/types/helpers/getLocalTimeInfo.test.d.ts +6 -0
  48. package/dist/esm/types/helpers/getMonthName.test.d.ts +5 -0
  49. package/dist/esm/types/helpers/getOrdinal.test.d.ts +5 -0
  50. package/dist/esm/types/helpers/getPartOfDay.test.d.ts +5 -0
  51. package/dist/esm/types/helpers/getTimeInfoInET.test.d.ts +5 -0
  52. package/dist/esm/types/helpers/validators/shared/constants/ERROR_MESSAGES.d.ts +10 -0
  53. package/dist/esm/types/helpers/validators/shared/helpers/validateRegex.d.ts +19 -0
  54. package/dist/esm/types/helpers/validators/shared/types/ValidationResult.d.ts +12 -0
  55. package/dist/esm/types/helpers/validators/validateEmail.d.ts +11 -0
  56. package/dist/esm/types/helpers/validators/validateEmail.test.d.ts +1 -0
  57. package/dist/esm/types/helpers/validators/validatePhoneNumber.d.ts +11 -0
  58. package/dist/esm/types/helpers/validators/validatePhoneNumber.test.d.ts +1 -0
  59. package/dist/esm/types/helpers/validators/validateString.d.ts +22 -0
  60. package/dist/esm/types/helpers/validators/validateString.test.d.ts +1 -0
  61. package/dist/esm/types/index.d.ts +1 -6
  62. package/dist/index.d.ts +35 -166
  63. package/package.json +13 -17
  64. package/src/client/initClient.tsx +20 -12
  65. package/src/components/AppWrapper.tsx +1 -1
  66. package/src/components/ItemPicker/index.test.tsx +10 -10
  67. package/src/helpers/abbreviate.test.ts +26 -0
  68. package/src/helpers/avg.test.ts +24 -0
  69. package/src/helpers/canReviewLogs.test.ts +67 -0
  70. package/src/helpers/canReviewLogs.ts +20 -0
  71. package/src/helpers/ceilToNumDecimals.test.ts +39 -0
  72. package/src/helpers/compareArraysByProp.test.ts +102 -0
  73. package/src/helpers/extractProp.test.ts +34 -0
  74. package/src/helpers/floorToNumDecimals.test.ts +39 -0
  75. package/src/helpers/forceNumIntoBounds.test.ts +34 -0
  76. package/src/helpers/genCSV.test.ts +61 -0
  77. package/src/helpers/genCSV.ts +5 -2
  78. package/src/helpers/genCommaList.test.ts +57 -0
  79. package/src/helpers/genCommaList.ts +21 -0
  80. package/src/helpers/getHumanReadableDate.test.ts +29 -0
  81. package/src/helpers/getLocalTimeInfo.test.ts +48 -0
  82. package/src/helpers/getMonthName.test.ts +79 -0
  83. package/src/helpers/getOrdinal.test.ts +84 -0
  84. package/src/helpers/{getOrdinal.tsx → getOrdinal.ts} +1 -0
  85. package/src/helpers/getPartOfDay.test.ts +56 -0
  86. package/src/helpers/getTimeInfoInET.test.ts +72 -0
  87. package/src/helpers/validators/shared/constants/ERROR_MESSAGES.ts +18 -0
  88. package/src/helpers/validators/shared/helpers/validateRegex.ts +47 -0
  89. package/src/helpers/validators/shared/types/ValidationResult.ts +20 -0
  90. package/src/helpers/validators/validateEmail.test.ts +110 -0
  91. package/src/helpers/validators/validateEmail.ts +47 -0
  92. package/src/helpers/validators/validatePhoneNumber.test.ts +94 -0
  93. package/src/helpers/validators/validatePhoneNumber.ts +49 -0
  94. package/src/helpers/validators/validateString.test.ts +175 -0
  95. package/src/helpers/validators/validateString.ts +171 -0
  96. package/src/index.ts +10 -12
  97. package/tsconfig.json +6 -2
  98. package/dist/cjs/types/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.d.ts +0 -10
  99. package/dist/cjs/types/components/DBEntryManagerPanel/AddOrEditDBEntry/index.d.ts +0 -34
  100. package/dist/cjs/types/components/DBEntryManagerPanel/helpers/generateEndpointPath.d.ts +0 -9
  101. package/dist/cjs/types/components/DBEntryManagerPanel/index.d.ts +0 -25
  102. package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntry.d.ts +0 -8
  103. package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntryField.d.ts +0 -49
  104. package/dist/cjs/types/components/DBEntryManagerPanel/types/DBEntryFieldType.d.ts +0 -12
  105. package/dist/cjs/types/helpers/addDBEditorEndpoints.d.ts +0 -40
  106. package/dist/esm/types/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.d.ts +0 -10
  107. package/dist/esm/types/components/DBEntryManagerPanel/AddOrEditDBEntry/index.d.ts +0 -34
  108. package/dist/esm/types/components/DBEntryManagerPanel/helpers/generateEndpointPath.d.ts +0 -9
  109. package/dist/esm/types/components/DBEntryManagerPanel/index.d.ts +0 -25
  110. package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntry.d.ts +0 -8
  111. package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntryField.d.ts +0 -49
  112. package/dist/esm/types/components/DBEntryManagerPanel/types/DBEntryFieldType.d.ts +0 -12
  113. package/dist/esm/types/helpers/addDBEditorEndpoints.d.ts +0 -40
  114. package/src/components/DBEntryManagerPanel/AddOrEditDBEntry/CreatableMultiselect.tsx +0 -286
  115. package/src/components/DBEntryManagerPanel/AddOrEditDBEntry/index.tsx +0 -722
  116. package/src/components/DBEntryManagerPanel/helpers/generateEndpointPath.ts +0 -15
  117. package/src/components/DBEntryManagerPanel/index.tsx +0 -511
  118. package/src/components/DBEntryManagerPanel/types/DBEntry.ts +0 -7
  119. package/src/components/DBEntryManagerPanel/types/DBEntryField.ts +0 -94
  120. package/src/components/DBEntryManagerPanel/types/DBEntryFieldType.ts +0 -18
  121. package/src/helpers/addDBEditorEndpoints.ts +0 -124
  122. /package/src/helpers/{abbreviate.tsx → abbreviate.ts} +0 -0
  123. /package/src/helpers/{avg.tsx → avg.ts} +0 -0
  124. /package/src/helpers/{ceilToNumDecimals.tsx → ceilToNumDecimals.ts} +0 -0
  125. /package/src/helpers/{floorToNumDecimals.tsx → floorToNumDecimals.ts} +0 -0
  126. /package/src/helpers/{forceNumIntoBounds.tsx → forceNumIntoBounds.ts} +0 -0
  127. /package/src/helpers/{getLocalTimeInfo.tsx → getLocalTimeInfo.ts} +0 -0
  128. /package/src/helpers/{getTimeInfoInET.tsx → getTimeInfoInET.ts} +0 -0
@@ -1,722 +0,0 @@
1
- /**
2
- * Panel for adding a DBEntry to the database
3
- * @author Yuen Ler Chow
4
- */
5
-
6
- // Import React
7
- import React, { useReducer } from 'react';
8
-
9
- // Import FontAwesome
10
- import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
11
- import { faSave } from '@fortawesome/free-solid-svg-icons';
12
-
13
- // Import shared components
14
- import CheckboxButton from '../../CheckboxButton';
15
- import LoadingSpinner from '../../LoadingSpinner';
16
- import RadioButton from '../../RadioButton';
17
- import ButtonInputGroup from '../../ButtonInputGroup';
18
-
19
- // Import shared helpers
20
- import { alert, showFatalError } from '../../AppWrapper';
21
- import visitServerEndpoint from '../../../helpers/visitServerEndpoint';
22
-
23
- // Import other types
24
- import DBEntry from '../types/DBEntry';
25
- import DBEntryField from '../types/DBEntryField';
26
- import DBEntryFieldType from '../types/DBEntryFieldType';
27
-
28
- // Import other components
29
- import CreatableMultiselect from './CreatableMultiselect';
30
-
31
-
32
- /*------------------------------------------------------------------------*/
33
- /* -------------------------------- Types ------------------------------- */
34
- /*------------------------------------------------------------------------*/
35
-
36
- // Props definition
37
- type Props = {
38
- /**
39
- * Handler for when the user finishes adding the DBEntry
40
- * (if no DBEntry is returned, process was cancelled)
41
- * @param DBEntry the DBEntry that was just created
42
- */
43
- onFinished: (dbEntry?: DBEntry) => void,
44
- /**
45
- * Function to validate the DBEntry before saving
46
- * @param dbEntry
47
- */
48
- validateEntry?: (dbEntry: DBEntry) => Promise<void>,
49
- /**
50
- * Function to modify the DBEntry before saving
51
- * @param dbEntry
52
- * @returns the modified DBEntry
53
- */
54
- modifyEntry?: (dbEntry: DBEntry) => DBEntry,
55
- // The fields needed to create a new DBEntry
56
- entryFields: DBEntryField[],
57
- // The DBEntry to edit (if any)
58
- dbEntryToEdit?: DBEntry,
59
- // The unique object key of the DBEntry
60
- idPropName: string,
61
- // item name
62
- itemName: string,
63
- // Server endpoint path to save the DBEntry
64
- saveEndpointPath: string,
65
- // All entries in the database
66
- entries: DBEntry[],
67
- };
68
-
69
- /*------------------------------------------------------------------------*/
70
- /* -------------------------------- Style ------------------------------- */
71
- /*------------------------------------------------------------------------*/
72
-
73
- const style = `
74
- .AddOrEditDBEntry-input-label {
75
- min-width: 7rem;
76
- }
77
- `;
78
-
79
- /*------------------------------------------------------------------------*/
80
- /* -------------------------------- State ------------------------------- */
81
- /*------------------------------------------------------------------------*/
82
-
83
- /* -------- State Definition -------- */
84
-
85
- type State = {
86
- // The DBEntry's current state
87
- entry: DBEntry,
88
- // True if currently saving
89
- saving: boolean,
90
- };
91
-
92
- /* ------------- Actions ------------ */
93
-
94
- // Types of actions
95
- enum ActionType {
96
- // Update the DBEntry
97
- UpdateDBEntry = 'UpdateDBEntry',
98
- // Start the save spinner
99
- StartSave = 'StartSave',
100
- }
101
-
102
- // Action definitions
103
- type Action = (
104
- | {
105
- // Action type
106
- type: ActionType.UpdateDBEntry,
107
- // New state of the DBEntry
108
- dbEntry: DBEntry,
109
- }
110
- | {
111
- // Action type
112
- type: (
113
- | ActionType.StartSave
114
- ),
115
- }
116
- );
117
-
118
- /**
119
- * Reducer that executes actions
120
- * @author Yuen Ler Chow
121
- * @param state current state
122
- * @param action action to execute
123
- */
124
- const reducer = (state: State, action: Action): State => {
125
- switch (action.type) {
126
- case ActionType.UpdateDBEntry: {
127
- return {
128
- ...state,
129
- entry: action.dbEntry,
130
- };
131
- }
132
- case ActionType.StartSave: {
133
- return {
134
- ...state,
135
- saving: true,
136
- };
137
- }
138
- default: {
139
- return state;
140
- }
141
- }
142
- };
143
-
144
- /*------------------------------------------------------------------------*/
145
- /* ------------------------------ Component ----------------------------- */
146
- /*------------------------------------------------------------------------*/
147
-
148
- const AddOrEditDBEntry: React.FC<Props> = (props) => {
149
- /*------------------------------------------------------------------------*/
150
- /* -------------------------------- Setup ------------------------------- */
151
- /*------------------------------------------------------------------------*/
152
-
153
- /* -------------- Props ------------- */
154
-
155
- // Destructure all props
156
- const {
157
- onFinished,
158
- entryFields,
159
- dbEntryToEdit,
160
- validateEntry,
161
- modifyEntry,
162
- idPropName,
163
- saveEndpointPath,
164
- entries,
165
- itemName,
166
- } = props;
167
-
168
- /* -------------- State ------------- */
169
-
170
- // Initial state
171
- const initialState: State = {
172
- entry: dbEntryToEdit || {},
173
- saving: false,
174
- };
175
-
176
- // Initialize state
177
- const [state, dispatch] = useReducer(reducer, initialState);
178
-
179
- // Destructure common state
180
- const {
181
- entry,
182
- saving,
183
- } = state;
184
-
185
- /*------------------------------------------------------------------------*/
186
- /* ------------------------- Component Functions ------------------------ */
187
- /*------------------------------------------------------------------------*/
188
-
189
- /**
190
- * Save changes to the DBEntry and then finish
191
- * @author Yuen Ler Chow
192
- */
193
- const save = async () => {
194
- // Start the save loading indicator
195
- dispatch({ type: ActionType.StartSave });
196
-
197
- // add all default values to the entry
198
- entryFields.forEach((field) => {
199
- if (field.defaultValue && !entry[field.objectKey]) {
200
- entry[field.objectKey] = field.defaultValue;
201
- }
202
- });
203
-
204
- const modifiedEntry = modifyEntry ? modifyEntry(entry) : entry;
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
-
209
- // Send to server
210
- try {
211
- await visitServerEndpoint({
212
- path: saveEndpointPath,
213
- method: 'POST',
214
- params: {
215
- item: JSON.stringify(modifiedEntry),
216
- },
217
- });
218
-
219
- // Finish
220
- onFinished(entry);
221
- } catch (err) {
222
- return showFatalError(err);
223
- }
224
- };
225
-
226
- /**
227
- * Cancel and return without saving
228
- * @author Gabe Abrams
229
- */
230
- const cancel = () => {
231
- onFinished(undefined);
232
- };
233
-
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
- */
240
-
241
- const validate = (fields: DBEntryField[]) => {
242
- let validationError = '';
243
- for (let i = 0; i < fields.length; i += 1) {
244
- const field = fields[i];
245
- const value = entry[field.objectKey];
246
-
247
- // Check if required field is empty. Field is automatically required if it is the idPropName
248
- if ((field.required || (field.objectKey === idPropName)) && !value) {
249
- validationError = `Please fill in the ${field.label} field`;
250
- return validationError;
251
- }
252
-
253
- // Check if unique field is unique
254
- if (field.objectKey === idPropName && !dbEntryToEdit) {
255
- if (entries.find((e) => { return e[idPropName] === value; })) {
256
- validationError = `An item with the ${field.label} ${value} already exists. ${field.label} must be unique.`;
257
- return validationError;
258
- }
259
- }
260
-
261
- // If they have entered a value for the field, check if it is valid
262
- if (value || field.type === DBEntryFieldType.Object) {
263
- // String validation
264
- if (field.type === DBEntryFieldType.String) {
265
- if (
266
- // Minimum length requirement is defined
267
- field.minNumChars
268
- // Value is too short
269
- && value.length < field.minNumChars
270
- ) {
271
- validationError = `${field.label} must be at least ${field.minNumChars} character${field.minNumChars === 1 ? '' : 's'} long`;
272
- } else if (
273
- // Maximum length requirement is defined
274
- field.maxNumChars
275
- // Value is too long
276
- && value.length > field.maxNumChars
277
- ) {
278
- validationError = `${field.label} must be at most ${field.maxNumChars} character${field.maxNumChars === 1 ? '' : 's'} long`;
279
- }
280
- // Number validation
281
- } else if (field.type === DBEntryFieldType.Number) {
282
- if (
283
- // Minimum value requirement is defined
284
- field.minNumber
285
- // Value is too small
286
- && value < field.minNumber
287
- ) {
288
- validationError = `${field.label} must be at least ${field.minNumber}`;
289
- } else if (
290
- // Maximum value requirement is defined
291
- field.maxNumber
292
- // Value is too large
293
- && value > field.maxNumber
294
- ) {
295
- validationError = `${field.label} must be at most ${field.maxNumber}`;
296
- }
297
- } else if (
298
- field.type === DBEntryFieldType.StringArray
299
- || field.type === DBEntryFieldType.NumberArray
300
- ) {
301
- // String and Number Array validation
302
- if (
303
- // Minimum number of elements requirement is defined
304
- field.minNumElements
305
- // Value has too few elements
306
- && value.length < field.minNumElements
307
- ) {
308
- validationError = `${field.label} must have at least ${field.minNumElements} value${field.minNumElements === 1 ? '' : 's'}`;
309
- } else if (
310
- // Maximum number of elements requirement is defined
311
- field.maxNumElements
312
- // Value has too many elements
313
- && value.length > field.maxNumElements
314
- ) {
315
- validationError = `${field.label} must have at most ${field.maxNumElements} value${field.maxNumElements === 1 ? '' : 's'}`;
316
- } else if (field.type === DBEntryFieldType.NumberArray) {
317
- // Number Array validation
318
- for (let j = 0; j < value.length; j += 1) {
319
- if (
320
- // Minimum value requirement is defined
321
- field.minNumber
322
- // Value is too small
323
- && value[j] < field.minNumber
324
- ) {
325
- validationError = `${field.label} values must be at least ${field.minNumber}`;
326
- return validationError;
327
- } if (
328
- // Maximum value requirement is defined
329
- field.maxNumber
330
- // Value is too large
331
- && value[j] > field.maxNumber
332
- ) {
333
- validationError = `${field.label} values must be at most ${field.maxNumber}`;
334
- return validationError;
335
- }
336
- }
337
- }
338
- } else if (field.type === DBEntryFieldType.Object) {
339
- validationError = validate(field.subfields);
340
- }
341
- }
342
- if (validationError) {
343
- return validationError;
344
- }
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);
374
-
375
- /**
376
- * Render a single entry field
377
- * @author Yuen Ler Chow
378
- * @param field the entry field to render
379
- * @param disabled true if the field should be disabled
380
- */
381
- const renderEntryField = (field: DBEntryField, disabled: boolean) => {
382
- if (field.type === DBEntryFieldType.String) {
383
- if (field.choices) {
384
- return (
385
- <div
386
- key={field.objectKey}
387
- className="mb-2"
388
- >
389
- <div
390
- className="input-group"
391
- style={{
392
- pointerEvents: (disabled ? 'none' : 'auto'),
393
- }}
394
- >
395
- <ButtonInputGroup
396
- label={field.label}
397
- >
398
- {
399
- field.choices.map((choice) => {
400
- return (
401
- <RadioButton
402
- key={choice.value}
403
- text={choice.title}
404
- selected={entry[field.objectKey] === choice.value}
405
- onSelected={() => {
406
- entry[field.objectKey] = choice.value;
407
- dispatch({
408
- type: ActionType.UpdateDBEntry,
409
- dbEntry: entry,
410
- });
411
- }}
412
- ariaLabel={choice.title}
413
- />
414
- );
415
- })
416
- }
417
- </ButtonInputGroup>
418
- </div>
419
- </div>
420
- );
421
- }
422
-
423
- return (
424
- <div
425
- className="mb-2"
426
- key={field.objectKey}
427
- >
428
- <div className="input-group">
429
- <span
430
- className="AddOrEditDBEntry-input-label input-group-text"
431
- >
432
- {field.label}
433
- </span>
434
- <input
435
- disabled={disabled}
436
- type="text"
437
- className="form-control"
438
- placeholder={field.placeholder}
439
- aria-describedby="AddOrEditDBEntry-form-name-label"
440
- value={entry[field.objectKey] || ''}
441
- onChange={(e) => {
442
- entry[field.objectKey] = (
443
- e.target.value
444
- );
445
- dispatch({
446
- type: ActionType.UpdateDBEntry,
447
- dbEntry: entry,
448
- });
449
- }}
450
- />
451
- </div>
452
- </div>
453
- );
454
- }
455
-
456
- if (field.type === DBEntryFieldType.Number) {
457
- return (
458
- <div
459
- key={field.objectKey}
460
- className="mb-2"
461
- >
462
- <div className="input-group">
463
- <span
464
- className="AddOrEditDBEntry-input-label input-group-text"
465
- >
466
- {field.label}
467
- </span>
468
- <input
469
- type="text"
470
- className="form-control"
471
- placeholder={field.placeholder}
472
- aria-describedby="AddOrEditDBEntry-form-name-label"
473
- value={entry[field.objectKey] || ''}
474
- disabled={disabled}
475
- onChange={(e) => {
476
- entry[field.objectKey] = (
477
- e.target.value
478
- .replace(/[^0-9]/g, '')
479
- );
480
- dispatch({
481
- type: ActionType.UpdateDBEntry,
482
- dbEntry: entry,
483
- });
484
- }}
485
- />
486
- </div>
487
- </div>
488
- );
489
- }
490
-
491
- if (field.type === DBEntryFieldType.StringArray) {
492
- if (field.choices) {
493
- return (
494
- <div
495
- key={field.objectKey}
496
- className="mb-2"
497
- >
498
- <div
499
- className="input-group"
500
- style={{
501
- pointerEvents: (disabled ? 'none' : 'auto'),
502
- }}
503
- >
504
- <ButtonInputGroup
505
- label={field.label}
506
- >
507
- {
508
- field.choices.map((choice) => {
509
- return (
510
- <CheckboxButton
511
- key={choice.value}
512
- text={choice.title}
513
- checked={entry[field.objectKey] && entry[field.objectKey].includes(choice.value)}
514
- onChanged={(checked) => {
515
- if (checked) {
516
- // Initialize array if it doesn't exist
517
- if (!entry[field.objectKey]) {
518
- entry[field.objectKey] = [];
519
- }
520
-
521
- // Add value to array
522
- entry[field.objectKey].push(choice.value);
523
- } else {
524
- // Remove value from array
525
- entry[field.objectKey] = (
526
- entry[field.objectKey]
527
- .filter((val: string) => {
528
- return val !== choice.value;
529
- })
530
- );
531
- }
532
-
533
- // Save
534
- dispatch({
535
- type: ActionType.UpdateDBEntry,
536
- dbEntry: entry,
537
- });
538
- }}
539
- ariaLabel={choice.title}
540
- />
541
- );
542
- })
543
- }
544
- </ButtonInputGroup>
545
- </div>
546
- </div>
547
- );
548
- }
549
-
550
- // Flexible (no specific choices for this field)
551
- return (
552
- <div
553
- key={field.objectKey}
554
- className="mb-2"
555
- >
556
- <div className="input-group">
557
- <span
558
- className="AddOrEditDBEntry-input-label input-group-text"
559
- >
560
- {field.label}
561
- </span>
562
- <div className="flex-grow-1">
563
- <CreatableMultiselect
564
- disabled={disabled}
565
- type={DBEntryFieldType.StringArray}
566
- values={entry[field.objectKey] || []}
567
- onChange={(values) => {
568
- // Update entry and save
569
- entry[field.objectKey] = values;
570
- dispatch({
571
- type: ActionType.UpdateDBEntry,
572
- dbEntry: entry,
573
- });
574
- }}
575
- />
576
- </div>
577
- </div>
578
- </div>
579
- );
580
- }
581
-
582
- if (field.type === DBEntryFieldType.NumberArray) {
583
- return (
584
- <div
585
- key={field.objectKey}
586
- className="mb-2"
587
- >
588
- <div className="input-group">
589
- <span
590
- className="AddOrEditDBEntry-input-label input-group-text"
591
- >
592
- {field.label}
593
- </span>
594
- <div className="flex-grow-1">
595
- <CreatableMultiselect
596
- disabled={disabled}
597
- type={DBEntryFieldType.NumberArray}
598
- values={entry[field.objectKey] || []}
599
- onChange={(values) => {
600
- entry[field.objectKey] = values;
601
- dispatch({
602
- type: ActionType.UpdateDBEntry,
603
- dbEntry: entry,
604
- });
605
- }}
606
- />
607
- </div>
608
- </div>
609
- </div>
610
- );
611
- }
612
-
613
- if (field.type === DBEntryFieldType.Object) {
614
- return (
615
- <div
616
- key={field.objectKey}
617
- className="mb-2"
618
- >
619
- <div className="input-group">
620
- {/* Label */}
621
- <span
622
- className="AddOrEditDBEntry-input-label input-group-text"
623
- >
624
- {field.label}
625
- </span>
626
- {/* Add each subfield */}
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>
634
- </div>
635
- </div>
636
- );
637
- }
638
-
639
- // This should never happen
640
- return null;
641
- };
642
-
643
- // UI
644
- body = (
645
- <div>
646
- <h1>{dbEntryToEdit ? `Edit ${itemName}` : `Create ${itemName}`}</h1>
647
- {/* Entry fields */}
648
- {
649
- entryFields.map((field: DBEntryField) => {
650
- const disabled = (idPropName === field.objectKey && dbEntryToEdit !== undefined)
651
- || (field.lockAfterCreation !== undefined && dbEntryToEdit !== undefined);
652
- return renderEntryField(field, disabled);
653
- })
654
- }
655
-
656
- {/* Buttons */}
657
- <div className="text-center mt-2">
658
- <button
659
- type="button"
660
- id="AddOrEditDBEntry-save-changes-button"
661
- className="btn btn-primary btn-lg me-1"
662
- aria-label="Save changes"
663
- onClick={async () => {
664
- if (validationError && validationError.length > 0) {
665
- return alert(
666
- 'Please fix the following error',
667
- validationError,
668
- );
669
- }
670
- if (validateEntry) {
671
- try {
672
- await validateEntry(entry);
673
- } catch (error) {
674
- return alert(
675
- 'Please fix the following error',
676
- String(error),
677
- );
678
- }
679
- }
680
- save();
681
- }}
682
- >
683
- <FontAwesomeIcon
684
- icon={faSave}
685
- className="me-1"
686
- />
687
- Save
688
- </button>
689
- <button
690
- type="button"
691
- id="AddOrEditDBEntry-cancel-button"
692
- className="btn btn-secondary btn-lg me-1"
693
- aria-label="save changes"
694
- onClick={cancel}
695
- >
696
- Cancel
697
- </button>
698
- </div>
699
- </div>
700
- );
701
- }
702
-
703
- /*----------------------------------------*/
704
- /* --------------- Main UI -------------- */
705
- /*----------------------------------------*/
706
-
707
- return (
708
- <div className="alert alert-light text-black">
709
- <style>
710
- {style}
711
- </style>
712
- {body}
713
- </div>
714
- );
715
- };
716
-
717
- /*------------------------------------------------------------------------*/
718
- /* ------------------------------- Wrap Up ------------------------------ */
719
- /*------------------------------------------------------------------------*/
720
-
721
- // Export component
722
- export default AddOrEditDBEntry;