@dcrackel/hematournamentui 1.0.708 → 1.0.709
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/README.md +132 -106
- package/dist/HemaTournamentUI-lib.es.js +6243 -6003
- package/dist/HemaTournamentUI-lib.umd.js +25 -25
- package/dist/hematournamentui-tailwind.css +13 -6
- package/package.json +1 -1
- package/src/stories/Atoms/Input/BaseInput.test.js +97 -85
- package/src/stories/Atoms/Input/BaseInput.vue +149 -150
- package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.stories.js +31 -20
- package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.test.js +109 -0
- package/src/stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue +192 -133
- package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.stories.js +14 -0
- package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.test.js +39 -0
- package/src/stories/Templates/PersonManagement/Admin/Administration/Administration.vue +13 -3
- package/src/stories/Templates/PersonManagement/Basic/BasicEdit.vue +170 -172
- package/src/stories/Templates/PersonManagement/PersonManagement.stories.js +156 -103
- package/src/stories/Templates/PersonManagement/PersonManagement.test.js +329 -0
- package/src/stories/Templates/PersonManagement/PersonManagement.vue +329 -213
- package/src/stories/Templates/PersonManagement/PersonManagementDraft.js +42 -0
- package/src/stories/Util/Validations/PersonManagementValidations.js +45 -29
|
@@ -1,213 +1,329 @@
|
|
|
1
|
-
<template>
|
|
2
|
-
<section v-if="loading">
|
|
3
|
-
<PersonManagementSkeleton />
|
|
4
|
-
</section>
|
|
5
|
-
<section
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
<
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
<div
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
},
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
},
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
1
|
+
<template>
|
|
2
|
+
<section v-if="loading">
|
|
3
|
+
<PersonManagementSkeleton />
|
|
4
|
+
</section>
|
|
5
|
+
<section
|
|
6
|
+
v-if="!loading"
|
|
7
|
+
class="flex flex-col px-3 bg-neutral"
|
|
8
|
+
id="person-management"
|
|
9
|
+
:aria-busy="saving || imageUploading"
|
|
10
|
+
>
|
|
11
|
+
<p v-if="saving" role="status" class="text-quinary text-sm">Saving profile…</p>
|
|
12
|
+
<fieldset
|
|
13
|
+
:key="draftVersion"
|
|
14
|
+
:disabled="isFormDisabled"
|
|
15
|
+
:inert="isFormDisabled"
|
|
16
|
+
class="min-w-0 rounded-lg md:p-9 w-full border-0 p-0"
|
|
17
|
+
>
|
|
18
|
+
<div class="w-full flex flex-row justify-center mb-4">
|
|
19
|
+
<BaseText :color="'primaryHighlight'" :size="'2xl'" :text="getHeaderText" :weight="'normal'" />
|
|
20
|
+
</div>
|
|
21
|
+
<div class="w-full flex-row justify-center mb-4 flex drop-shadow">
|
|
22
|
+
<ImageCropper
|
|
23
|
+
:urlToImage="getImageURL"
|
|
24
|
+
:uploadImageName="uploadImageName"
|
|
25
|
+
:uploadServer="uploadServer"
|
|
26
|
+
:imageServer="imageServer"
|
|
27
|
+
:customClass="getClass"
|
|
28
|
+
:isProfile="true"
|
|
29
|
+
:disabled="isFormDisabled"
|
|
30
|
+
customSkeletonClass="w-32 h-32 border-2 border-dashed rounded-full animate-pulse bg-quaternaryHighlight"
|
|
31
|
+
:cropperOptions="{
|
|
32
|
+
aspectRatio: 4 / 4,
|
|
33
|
+
autoCropArea: 1,
|
|
34
|
+
viewMode: 1,
|
|
35
|
+
movable: true,
|
|
36
|
+
zoomable: true
|
|
37
|
+
}"
|
|
38
|
+
@update:url="handleProfilePicUpdate"
|
|
39
|
+
@update:uploading="handleImageUploading"
|
|
40
|
+
@error:load="handleImageLoadError"
|
|
41
|
+
@error:upload="handleImageUploadError"
|
|
42
|
+
/>
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
<p v-if="imageUploading" role="status" class="mb-4 text-sm text-quinary">Uploading profile image…</p>
|
|
46
|
+
<p v-if="imageErrorMessage" role="alert" class="mb-4 text-sm text-alarm">{{ imageErrorMessage }}</p>
|
|
47
|
+
|
|
48
|
+
<EditEventsTopMenu :tabs="personProfileTabs(userLevel)" :currentTab="currentTab" @tabMenuClick="handleTab" />
|
|
49
|
+
<div
|
|
50
|
+
v-if="showValidationSummary"
|
|
51
|
+
role="alert"
|
|
52
|
+
data-testid="profile-validation-summary"
|
|
53
|
+
class="my-4 rounded-lg border border-alarm p-3 text-sm text-alarm"
|
|
54
|
+
>
|
|
55
|
+
<p class="font-semibold">Please correct these fields before saving:</p>
|
|
56
|
+
<ul class="mt-2 list-disc pl-5">
|
|
57
|
+
<li v-for="error in validationErrors" :key="error.field">
|
|
58
|
+
<button
|
|
59
|
+
type="button"
|
|
60
|
+
class="text-left underline"
|
|
61
|
+
:disabled="!canOpenTab(error.tab)"
|
|
62
|
+
@click="handleTab(error.tab)"
|
|
63
|
+
>
|
|
64
|
+
{{ error.label }}: {{ error.message }} ({{ error.tab }} tab)
|
|
65
|
+
</button>
|
|
66
|
+
</li>
|
|
67
|
+
</ul>
|
|
68
|
+
</div>
|
|
69
|
+
<BasicEdit
|
|
70
|
+
v-if="currentTab === 'Basic'"
|
|
71
|
+
:person="person"
|
|
72
|
+
:fencingClubs="fencingClubs"
|
|
73
|
+
:validation="validation"
|
|
74
|
+
@update:person="updatePerson"
|
|
75
|
+
/>
|
|
76
|
+
<FencerAdmin
|
|
77
|
+
v-if="currentTab === 'Admin'"
|
|
78
|
+
:person="person"
|
|
79
|
+
:people="people"
|
|
80
|
+
:userLevel="userLevel"
|
|
81
|
+
@update:person="updatePerson"
|
|
82
|
+
@merge:merge="doMerge"
|
|
83
|
+
/>
|
|
84
|
+
<Attendance v-if="currentTab === 'Stats'" :attendance="attendance" />
|
|
85
|
+
<Attendance v-if="currentTab === 'Attendance'" :attendance="attendance" />
|
|
86
|
+
|
|
87
|
+
<div v-if="showButtons" class="flex flex-row justify-end mt-8">
|
|
88
|
+
<BaseButton
|
|
89
|
+
v-if="userLevel === 'Admin'"
|
|
90
|
+
class="mr-8"
|
|
91
|
+
color="warn"
|
|
92
|
+
label="Delete"
|
|
93
|
+
size="sm"
|
|
94
|
+
type="secondary"
|
|
95
|
+
:disabled="isFormDisabled"
|
|
96
|
+
@buttonClick="handleDelete"
|
|
97
|
+
/>
|
|
98
|
+
<BaseButton
|
|
99
|
+
class="w-full"
|
|
100
|
+
color="neutral"
|
|
101
|
+
:label="saveButtonLabel"
|
|
102
|
+
size="sm"
|
|
103
|
+
type="secondary"
|
|
104
|
+
:disabled="isSaveDisabled"
|
|
105
|
+
@buttonClick="handleSubmit"
|
|
106
|
+
/>
|
|
107
|
+
</div>
|
|
108
|
+
</fieldset>
|
|
109
|
+
</section>
|
|
110
|
+
</template>
|
|
111
|
+
|
|
112
|
+
<script>
|
|
113
|
+
import ImageCropper from '../../../stories/Organisms/ComplexInputs/ImageCropper/ImageCropper.vue';
|
|
114
|
+
import EditEventsTopMenu from '../../Templates/Menus/EditEventsTopMenu/EditEventsTopMenu.vue';
|
|
115
|
+
import { personProfileTabs } from '../../Util/tabs.js';
|
|
116
|
+
import PersonManagementSkeleton from '../../Organisms/Skeletons/PersonManagement/PersonManagementSkeleton.vue';
|
|
117
|
+
import BaseText from '../../Atoms/Text/BaseText.vue';
|
|
118
|
+
import BaseButton from '../../Molecules/Buttons/BaseButton/BaseButton.vue';
|
|
119
|
+
import BasicEdit from './Basic/BasicEdit.vue';
|
|
120
|
+
import Attendance from '../../Templates/PersonManagement/Attendance/Attendance.vue';
|
|
121
|
+
import FencerAdmin from '../../Templates/PersonManagement/Admin/FencerAdmin/FencerAdmin.vue';
|
|
122
|
+
import { validateField } from '../../Util/Validations/PersonManagementValidations.js';
|
|
123
|
+
import {
|
|
124
|
+
createPersonDraft,
|
|
125
|
+
createPersonValidation,
|
|
126
|
+
getDraftHemaRatingsId,
|
|
127
|
+
profileFields
|
|
128
|
+
} from './PersonManagementDraft.js';
|
|
129
|
+
|
|
130
|
+
export default {
|
|
131
|
+
name: 'PersonManagement',
|
|
132
|
+
emits: ['submit:person', 'delete:person', 'update:image', 'merge:merge', 'error:image'],
|
|
133
|
+
components: {
|
|
134
|
+
FencerAdmin,
|
|
135
|
+
Attendance,
|
|
136
|
+
BasicEdit,
|
|
137
|
+
BaseButton,
|
|
138
|
+
BaseText,
|
|
139
|
+
PersonManagementSkeleton,
|
|
140
|
+
EditEventsTopMenu,
|
|
141
|
+
ImageCropper
|
|
142
|
+
},
|
|
143
|
+
props: {
|
|
144
|
+
personProfile: {
|
|
145
|
+
type: Object,
|
|
146
|
+
default: () => ({})
|
|
147
|
+
},
|
|
148
|
+
fencingClubs: {
|
|
149
|
+
type: Array,
|
|
150
|
+
default: () => []
|
|
151
|
+
},
|
|
152
|
+
people: {
|
|
153
|
+
type: Array,
|
|
154
|
+
default: () => []
|
|
155
|
+
},
|
|
156
|
+
attendance: {
|
|
157
|
+
type: Array,
|
|
158
|
+
default: () => []
|
|
159
|
+
},
|
|
160
|
+
uploadImageName: {
|
|
161
|
+
type: String,
|
|
162
|
+
default: 'profile-pic'
|
|
163
|
+
},
|
|
164
|
+
uploadServer: {
|
|
165
|
+
type: String
|
|
166
|
+
},
|
|
167
|
+
imageServer: {
|
|
168
|
+
type: String
|
|
169
|
+
},
|
|
170
|
+
userLevel: {
|
|
171
|
+
type: String,
|
|
172
|
+
default: 'user'
|
|
173
|
+
},
|
|
174
|
+
loading: {
|
|
175
|
+
type: Boolean,
|
|
176
|
+
default: false
|
|
177
|
+
},
|
|
178
|
+
saveButtonLabel: {
|
|
179
|
+
type: String,
|
|
180
|
+
default: 'Save'
|
|
181
|
+
},
|
|
182
|
+
saving: {
|
|
183
|
+
type: Boolean,
|
|
184
|
+
default: false
|
|
185
|
+
},
|
|
186
|
+
disabled: {
|
|
187
|
+
type: Boolean,
|
|
188
|
+
default: false
|
|
189
|
+
},
|
|
190
|
+
draftResetKey: {
|
|
191
|
+
type: [String, Number],
|
|
192
|
+
default: 0
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
data() {
|
|
196
|
+
return {
|
|
197
|
+
person: createPersonDraft(this.personProfile),
|
|
198
|
+
currentTab: 'Basic',
|
|
199
|
+
validation: createPersonValidation(),
|
|
200
|
+
draftVersion: 0,
|
|
201
|
+
submitAttempted: false,
|
|
202
|
+
imageUploading: false,
|
|
203
|
+
imageErrorMessage: ''
|
|
204
|
+
};
|
|
205
|
+
},
|
|
206
|
+
computed: {
|
|
207
|
+
profileIdentity() {
|
|
208
|
+
const id = this.personProfile?.PersonId;
|
|
209
|
+
return id === undefined || id === null ? null : String(id);
|
|
210
|
+
},
|
|
211
|
+
isFormDisabled() {
|
|
212
|
+
return this.saving || this.disabled;
|
|
213
|
+
},
|
|
214
|
+
isSaveDisabled() {
|
|
215
|
+
return this.isFormDisabled || this.imageUploading;
|
|
216
|
+
},
|
|
217
|
+
validationErrors() {
|
|
218
|
+
return profileFields
|
|
219
|
+
.filter(({ field }) => this.validation[field]?.invalid)
|
|
220
|
+
.map(field => ({ ...field, message: this.validation[field.field].message }));
|
|
221
|
+
},
|
|
222
|
+
showValidationSummary() {
|
|
223
|
+
return this.submitAttempted && this.validationErrors.length > 0;
|
|
224
|
+
},
|
|
225
|
+
getHeaderText() {
|
|
226
|
+
return this.person.DisplayName ? this.person.DisplayName : 'Fencer Profile';
|
|
227
|
+
},
|
|
228
|
+
getImageURL() {
|
|
229
|
+
if (this.person.ProfileImage) return this.person.ProfileImage;
|
|
230
|
+
const images = Array.isArray(this.person.Images) ? this.person.Images : [];
|
|
231
|
+
const portrait = images.find(image => image?.Type === 'Portrait') ?? images[0];
|
|
232
|
+
return portrait?.URL ?? '';
|
|
233
|
+
},
|
|
234
|
+
getClass() {
|
|
235
|
+
return 'border-2 border-dashed rounded-full w-32 h-32';
|
|
236
|
+
},
|
|
237
|
+
showButtons() {
|
|
238
|
+
const role = this.userLevel.toLowerCase();
|
|
239
|
+
return (
|
|
240
|
+
((role === 'admin' || role === 'club') && (this.currentTab === 'Basic' || this.currentTab === 'Admin')) ||
|
|
241
|
+
(role === 'user' && this.currentTab === 'Basic')
|
|
242
|
+
);
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
watch: {
|
|
246
|
+
profileIdentity() {
|
|
247
|
+
this.currentTab = 'Basic';
|
|
248
|
+
this.resetDraft();
|
|
249
|
+
},
|
|
250
|
+
draftResetKey() {
|
|
251
|
+
this.resetDraft();
|
|
252
|
+
}
|
|
253
|
+
},
|
|
254
|
+
methods: {
|
|
255
|
+
personProfileTabs,
|
|
256
|
+
resetDraft() {
|
|
257
|
+
this.person = createPersonDraft(this.personProfile);
|
|
258
|
+
this.validation = createPersonValidation();
|
|
259
|
+
this.submitAttempted = false;
|
|
260
|
+
this.imageUploading = false;
|
|
261
|
+
this.imageErrorMessage = '';
|
|
262
|
+
this.draftVersion += 1;
|
|
263
|
+
},
|
|
264
|
+
handleProfilePicUpdate(url) {
|
|
265
|
+
if (typeof url !== 'string' || !url.trim()) return;
|
|
266
|
+
this.person = { ...this.person, ProfileImage: url };
|
|
267
|
+
this.imageErrorMessage = '';
|
|
268
|
+
this.$emit('update:image', url);
|
|
269
|
+
},
|
|
270
|
+
handleImageUploading(uploading) {
|
|
271
|
+
this.imageUploading = uploading;
|
|
272
|
+
if (uploading) this.imageErrorMessage = '';
|
|
273
|
+
},
|
|
274
|
+
handleImageLoadError(error) {
|
|
275
|
+
this.reportImageError('load', error);
|
|
276
|
+
},
|
|
277
|
+
handleImageUploadError(error) {
|
|
278
|
+
this.imageUploading = false;
|
|
279
|
+
this.reportImageError('upload', error);
|
|
280
|
+
},
|
|
281
|
+
reportImageError(type, error) {
|
|
282
|
+
this.imageErrorMessage = error?.message || 'Could not update the profile image.';
|
|
283
|
+
this.$emit('error:image', { ...error, type, message: this.imageErrorMessage });
|
|
284
|
+
},
|
|
285
|
+
handleSubmit() {
|
|
286
|
+
if (this.isSaveDisabled) return;
|
|
287
|
+
this.submitAttempted = true;
|
|
288
|
+
if (!this.validateAllFields()) return;
|
|
289
|
+
const person = createPersonDraft(this.person);
|
|
290
|
+
const nameParts = person.LegalName.trim().split(/\s+/);
|
|
291
|
+
person.ClubId = person.Club?.ClubId ?? 17;
|
|
292
|
+
person.FirstName = nameParts[0] || '';
|
|
293
|
+
person.LastName = nameParts.slice(1).join(' ');
|
|
294
|
+
this.$emit('submit:person', person);
|
|
295
|
+
},
|
|
296
|
+
handleDelete() {
|
|
297
|
+
if (this.isFormDisabled) return;
|
|
298
|
+
this.$emit('delete:person', createPersonDraft(this.person));
|
|
299
|
+
},
|
|
300
|
+
updatePerson({ field, value }) {
|
|
301
|
+
if (this.isFormDisabled) return;
|
|
302
|
+
this.person = { ...this.person, [field]: value };
|
|
303
|
+
|
|
304
|
+
if (this.validation[field]) {
|
|
305
|
+
this.validation[field] = validateField(field, value);
|
|
306
|
+
}
|
|
307
|
+
},
|
|
308
|
+
validateAllFields() {
|
|
309
|
+
profileFields.forEach(({ field }) => {
|
|
310
|
+
const value = field === 'HemaRatingsId' ? getDraftHemaRatingsId(this.person) : this.person[field];
|
|
311
|
+
this.validation[field] = validateField(field, value);
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
return !Object.values(this.validation).some(field => field.invalid);
|
|
315
|
+
},
|
|
316
|
+
handleTab(tabId) {
|
|
317
|
+
if (this.isFormDisabled || !this.canOpenTab(tabId)) return;
|
|
318
|
+
this.currentTab = tabId;
|
|
319
|
+
},
|
|
320
|
+
canOpenTab(tabId) {
|
|
321
|
+
return personProfileTabs(this.userLevel).some(tab => tab.id === tabId);
|
|
322
|
+
},
|
|
323
|
+
doMerge(data) {
|
|
324
|
+
if (this.isFormDisabled) return;
|
|
325
|
+
this.$emit('merge:merge', data);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
};
|
|
329
|
+
</script>
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
export const profileFields = [
|
|
2
|
+
{ field: 'DisplayName', label: 'Display name', tab: 'Basic' },
|
|
3
|
+
{ field: 'LegalName', label: 'Legal name', tab: 'Basic' },
|
|
4
|
+
{ field: 'EmailAddress', label: 'Email address', tab: 'Basic' },
|
|
5
|
+
{ field: 'HemaRatingsId', label: 'HEMA Ratings ID', tab: 'Admin' }
|
|
6
|
+
];
|
|
7
|
+
|
|
8
|
+
export function getDraftLegalName(person) {
|
|
9
|
+
if (person?.LegalName !== undefined) return String(person.LegalName ?? '');
|
|
10
|
+
return [person?.FirstName, person?.LastName].filter(Boolean).join(' ');
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
function isPrimaryEmail(email) {
|
|
14
|
+
return email?.IsPrimary === true;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getPrimaryEmailAddress(person) {
|
|
18
|
+
const emails = Array.isArray(person?.PersonEmails) ? person.PersonEmails : [];
|
|
19
|
+
return emails.find(isPrimaryEmail)?.EmailAddress ?? '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function getDraftEmailAddress(person) {
|
|
23
|
+
if (person?.EmailAddress !== undefined) return String(person.EmailAddress ?? '');
|
|
24
|
+
if (person?.Email !== undefined) return String(person.Email ?? '');
|
|
25
|
+
return getPrimaryEmailAddress(person);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function getDraftHemaRatingsId(person) {
|
|
29
|
+
if (person?.HemaRatingsId !== undefined) return person.HemaRatingsId ?? '';
|
|
30
|
+
return person?.HEMARatings?.[0]?.HemaRatingId ?? '';
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function createPersonDraft(profile) {
|
|
34
|
+
const person = JSON.parse(JSON.stringify(profile ?? {}));
|
|
35
|
+
return { ...person, LegalName: getDraftLegalName(person), EmailAddress: getDraftEmailAddress(person) };
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function createPersonValidation() {
|
|
39
|
+
return Object.fromEntries(
|
|
40
|
+
[...profileFields.map(({ field }) => field), 'Club'].map(field => [field, { invalid: false, message: '' }])
|
|
41
|
+
);
|
|
42
|
+
}
|