Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 570ded6

Browse files
committedJun 12, 2024·
send invitation
1 parent 4ca60ed commit 570ded6

25 files changed

+532
-2261
lines changed
 

‎src/components/Container/PageOJS.vue

-2
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@
22
import Page from '@/components/Container/Page.vue';
33
import SubmissionsPage from '@/pages/submissions/SubmissionsPage.vue';
44
import UserInvitationPage from '@/pages/userInvitation/UserInvitationPage.vue';
5-
import AcceptInvitationPage from '@/pages/acceptInvitation/AcceptInvitationPage.vue';
65
76
export default {
87
components: {
98
SubmissionsPage,
109
UserInvitationPage,
11-
AcceptInvitationPage,
1210
},
1311
extends: Page,
1412
};

‎src/composables/useForm.js

+15
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,20 @@ export function useForm(_form) {
4949
);
5050
}
5151

52+
function connectWithErrors(errors) {
53+
watch(
54+
errors,
55+
(newErrors) => {
56+
Object.keys(newErrors).forEach((key) => {
57+
if (doesFieldExist(form.value, key)) {
58+
form.value.errors[key] = newErrors[key];
59+
}
60+
});
61+
},
62+
{immediate: true},
63+
);
64+
}
65+
5266
function set(key, data) {
5367
Object.keys(data).forEach(function (dataKey) {
5468
form.value[dataKey] = data[dataKey];
@@ -109,5 +123,6 @@ export function useForm(_form) {
109123
clearForm,
110124
form,
111125
connectWithPayload,
126+
connectWithErrors,
112127
};
113128
}

‎src/pages/acceptInvitation/AcceptInvitationCreateUserAccount.vue

-24
This file was deleted.

‎src/pages/acceptInvitation/AcceptInvitationCreateUserForms.vue

-20
This file was deleted.

‎src/pages/acceptInvitation/AcceptInvitationHeader.vue

-17
This file was deleted.

‎src/pages/acceptInvitation/AcceptInvitationPage.mdx

-9
This file was deleted.

‎src/pages/acceptInvitation/AcceptInvitationPage.stories.js

-28
This file was deleted.

‎src/pages/acceptInvitation/AcceptInvitationPage.vue

-407
This file was deleted.

‎src/pages/acceptInvitation/AcceptInvitationPageStore.js

-519
This file was deleted.

‎src/pages/acceptInvitation/AcceptInvitationReview.vue

-212
This file was deleted.

‎src/pages/acceptInvitation/AcceptInvitationUserRoles.vue

-34
This file was deleted.

‎src/pages/acceptInvitation/AcceptInvitationVerifyOrcid.vue

-11
This file was deleted.

‎src/pages/acceptInvitation/mocks/pageInitConfig.js

-204
This file was deleted.

‎src/pages/invitations/InvitationsPage.vue

-89
This file was deleted.

‎src/pages/invitations/InvitationsPageStore.js

-84
This file was deleted.

‎src/pages/userInvitation/UserInvitationDetailsFormStep.vue

+40-17
Original file line numberDiff line numberDiff line change
@@ -1,71 +1,94 @@
11
<template>
2-
<div v-if="store.user === null">
2+
<div v-if="store.invitationPayload.userId === null">
33
<pkp-form
44
v-bind="userForm"
55
class="userInvitation__stepForm"
66
@set="updateUserForm"
77
></pkp-form>
88
</div>
9-
<div v-if="store.user !== null">
9+
<div v-if="store.invitationPayload.userId !== null">
1010
<div class="userInvitation__reviewPanel__item">
1111
<h4 class="userInvitation__reviewPanel__item__header">Email Address</h4>
1212
<div class="userInvitation__reviewPanel__item__value">
13-
{{ store.user.email }}
13+
{{ store.invitationPayload.email }}
1414
</div>
1515
<h4 class="userInvitation__reviewPanel__item__header">ORCID ID</h4>
1616
<div class="userInvitation__reviewPanel__item__value">
1717
{{
18-
store.user.orcid ? store.user.orcid : t('invitation.orcid.message')
18+
store.invitationPayload.orcid
19+
? store.invitationPayload.orcid
20+
: t('invitation.orcid.message')
1921
}}
2022
<icon
21-
v-if="store.user.orcidAccessToken !== null"
23+
v-if="store.invitationPayload.orcidValidation"
2224
icon="orcid"
2325
:inline="true"
2426
/>
2527
</div>
2628
<h4 class="userInvitation__reviewPanel__item__header">Given Name</h4>
2729
<div class="userInvitation__reviewPanel__item__value">
28-
{{ store.user.fullName }}
30+
{{ store.invitationPayload.givenName }}
2931
</div>
3032
<h4 class="userInvitation__reviewPanel__item__header">Family Name</h4>
3133
<div class="userInvitation__reviewPanel__item__value">
32-
{{ store.user.fullName }}
34+
{{ store.invitationPayload.familyName }}
3335
</div>
3436
<h4 class="userInvitation__reviewPanel__item__header">Affiliation</h4>
3537
<div class="userInvitation__reviewPanel__item__value">
36-
{{ store.user.affiliation[store.primaryLocale] }}
38+
{{ store.invitationPayload.affiliation }}
3739
</div>
3840
</div>
3941
</div>
4042
<br />
41-
<UserInvitationUserGroupsTable :section="section" />
43+
<UserInvitationUserGroupsTable
44+
:user-groups="userGroups"
45+
:errors="sectionErrors"
46+
/>
4247
</template>
4348

4449
<script setup>
45-
import {defineProps} from 'vue';
50+
import {defineProps, computed} from 'vue';
4651
import PkpForm from '@/components/Form/Form.vue';
4752
import {useTranslation} from '@/composables/useTranslation';
4853
import UserInvitationUserGroupsTable from './UserInvitationUserGroupsTable.vue';
4954
import {useUserInvitationPageStore} from './UserInvitationPageStore';
5055
import {useForm} from '@/composables/useForm';
5156
5257
function updateUserForm(a, {fields}, c, d) {
53-
fields.forEach((field) => {
54-
if (store.invitationPayload[field.name] !== field.value) {
55-
store.updatePayload(field.name, field.value);
56-
}
57-
});
58+
if (fields) {
59+
fields.forEach((field) => {
60+
if (store.invitationPayload[field.name] !== field.value) {
61+
store.updatePayload(field.name, field.value);
62+
}
63+
});
64+
}
5865
}
5966
6067
const {t} = useTranslation();
6168
const store = useUserInvitationPageStore();
6269
6370
const props = defineProps({
64-
section: {type: Object, required: true},
71+
form: {type: Object, required: true},
72+
userGroups: {type: Object, required: true},
73+
validateFields: {type: Array, required: true},
6574
});
75+
const {
76+
form: userForm,
77+
connectWithPayload,
78+
connectWithErrors,
79+
} = useForm(props.form);
6680
67-
const {form: userForm, connectWithPayload} = useForm(props.section.form);
6881
connectWithPayload(store.invitationPayload);
82+
83+
const sectionErrors = computed(() => {
84+
return props.validateFields.reduce((obj, key) => {
85+
if (store.errors[key]) {
86+
obj[key] = store.errors[key];
87+
}
88+
return obj;
89+
}, {});
90+
});
91+
connectWithErrors(sectionErrors);
6992
</script>
7093
<style lang="less">
7194
select {

‎src/pages/userInvitation/UserInvitationEmailComposerStep.vue

+31-16
Original file line numberDiff line numberDiff line change
@@ -4,39 +4,39 @@
44
:add-c-c-label="t('common.addCCBCC')"
55
:attach-files-label="t('common.attachFiles')"
66
:attached-files-label="t('common.attachedFiles')"
7-
:attachers="section.email.attachers"
8-
:attachments="section.email.attachments"
9-
:bcc="section.email.bcc"
7+
:attachers="props.email.attachers"
8+
:attachments="props.email.attachments"
9+
:bcc="props.email.bcc"
1010
:bcc-label="t('email.bcc')"
1111
:body-label="t('stageParticipants.notify.message')"
12-
:can-change-recipients="section.email.canChangeRecipients"
13-
:cc="section.email.cc"
12+
:can-change-recipients="props.email.canChangeRecipients"
13+
:cc="props.email.cc"
1414
:cc-label="t('email.cc')"
1515
:confirm-switch-locale-label="t('email.confirmSwitchLocale')"
1616
:deselect-label="t('common.deselect')"
17-
:email-templates="section.email.emailTemplates"
17+
:email-templates="props.email.emailTemplates"
1818
:email-templates-api-url="store.emailTemplatesApiUrl"
19-
:errors="section.email.errors"
19+
:errors="sectionErrors.emailComposer"
2020
:find-template-label="t('common.findTemplate')"
21-
:initial-template-key="section.email.initialTemplateKey"
21+
:initial-template-key="props.email.initialTemplateKey"
2222
:insert-label="t('common.insert')"
2323
:insert-modal-label="t('common.insertContent')"
2424
:insert-content-label="t('common.content')"
2525
:insert-search-label="t('common.insertContentSearch')"
2626
:load-template-label="t('common.emailTemplates')"
27-
:locale="section.email.locale"
28-
:locales="section.email.locales"
27+
:locale="props.email.locale"
28+
:locales="props.email.locales"
2929
:more-search-results-label="t('common.numberedMore')"
30-
:recipient-options="section.email.recipientOptions"
31-
:recipients="section.email.recipients"
30+
:recipient-options="props.email.recipientOptions"
31+
:recipients="props.email.recipients"
3232
:recipients-label="t('email.to')"
3333
:remove-item-label="t('common.removeItem')"
3434
:searching-label="t('common.searching')"
3535
:search-results-label="t('search.searchResults')"
3636
:subject-label="t('email.subject')"
3737
:switch-to-label="t('common.switchTo')"
3838
:switch-to-named-language-label="t('common.switchToNamedItem')"
39-
:variables="section.email.variables"
39+
:variables="props.email.variables"
4040
v-bind="emailComposer"
4141
@set="(componentName, update) => updateEmail(update)"
4242
></composer>
@@ -50,7 +50,13 @@ import {defineProps} from 'vue';
5050
import {useUserInvitationPageStore} from './UserInvitationPageStore';
5151
5252
const props = defineProps({
53-
section: {type: Object, required: true},
53+
email: {type: Object, required: true},
54+
validateFields: {
55+
type: Array,
56+
default() {
57+
return null;
58+
},
59+
},
5460
});
5561
const {t} = useTranslation();
5662
@@ -67,8 +73,17 @@ function updateEmail(update) {
6773
6874
if (!store.invitationPayload.body) {
6975
updateEmail({
70-
subject: props.section.email.subject,
71-
body: props.section.email.body,
76+
subject: props.email.subject,
77+
body: props.email.body,
7278
});
7379
}
80+
81+
const sectionErrors = computed(() => {
82+
return props.validateFields.reduce((obj, key) => {
83+
if (store.errors[key]) {
84+
obj[key] = store.errors[key];
85+
}
86+
return obj;
87+
}, {});
88+
});
7489
</script>

‎src/pages/userInvitation/UserInvitationPage.stories.js

+46-2
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,52 @@ export const Init = {
4848
),
4949
http.post(
5050
'https://mock/index.php/publicknowledge/api/v1/invitations/15',
51-
() => {
52-
return HttpResponse.json({});
51+
async ({request}) => {
52+
const data = await request.json();
53+
let errors = {};
54+
55+
data.userGroupsToAdd.forEach((element, index) => {
56+
let objectErrors = {};
57+
Object.keys(element).forEach((key) => {
58+
if (element[key] === null) {
59+
objectErrors[key] = ['This field is required'];
60+
}
61+
});
62+
if (Object.keys(objectErrors).length > 0) {
63+
errors['userGroupsToAdd'] = {
64+
...errors['userGroupsToAdd'],
65+
[index]: objectErrors,
66+
};
67+
}
68+
});
69+
70+
if (data.email === '') {
71+
errors['email'] = ['This field is required'];
72+
}
73+
if (data.orcid === '') {
74+
errors['orcid'] = ['This field is required'];
75+
}
76+
if (data.familyName === '') {
77+
errors['familyName'] = ['This field is required'];
78+
}
79+
if (data.givenName === '') {
80+
errors['givenName'] = ['This field is required'];
81+
}
82+
83+
Object.keys(data.emailComposer).forEach((element) => {
84+
if (data.emailComposer[element] === '') {
85+
errors['emailComposer'] = {
86+
...errors['emailComposer'],
87+
[element]: ['This field is required'],
88+
};
89+
}
90+
});
91+
92+
if (Object.keys(errors).length > 0) {
93+
return HttpResponse.json(errors, {status: 422});
94+
}
95+
96+
return HttpResponse.json({status: 201});
5397
},
5498
),
5599
http.post(

‎src/pages/userInvitation/UserInvitationPage.vue

+3-68
Original file line numberDiff line numberDiff line change
@@ -31,53 +31,12 @@
3131
<p>{{ step.description }}</p>
3232
</panel-section>
3333
<panel-section v-for="section in step.sections" :key="section.id">
34-
<template v-if="step.type === 'email'">
35-
<notification
36-
v-if="Object.keys(store.errors).length > 0"
37-
type="warning"
38-
>
39-
{{ t('invitation.wizard.errors') }}
40-
</notification>
41-
</template>
4234
<component
4335
:is="userInvitationComponents[section.sectionComponent]"
4436
:key="section.sectionComponent"
45-
:section="section"
37+
v-bind="section.props"
4638
/>
4739
</panel-section>
48-
<!-- <template v-if="step.type === 'form' && step.sections.length > 0">
49-
<panel-section v-for="section in step.sections" :key="section.id">
50-
<UserInvitationDetailsFormStep :section="section" />
51-
</panel-section>
52-
</template>
53-
<template v-if="step.type === 'form' && step.sections.length === 0">
54-
<panel-section :key="step.id">
55-
<UserInvitationSearchFormStep
56-
:email-field="store.emailField"
57-
:username-field="store.usernameField"
58-
:orcid-field="store.orcidField"
59-
:email-change="store.emailChange"
60-
:orcid-change="store.usernameChange"
61-
:username-change="store.orcidChange"
62-
/>
63-
</panel-section>
64-
</template>
65-
<template v-else-if="step.type === 'email'">
66-
<panel-section v-for="section in step.sections" :key="section.id">
67-
<notification
68-
v-if="Object.keys(store.errors).length"
69-
type="warning"
70-
>
71-
{{ t('invitation.wizard.errors') }}
72-
</notification>
73-
<UserInvitationEmailComposerStep
74-
:id="step.id"
75-
:section="section"
76-
:update-step="store.updateStep"
77-
:email-templates-api-url="emailTemplatesApiUrl"
78-
/>
79-
</panel-section>
80-
</template> -->
8140
</panel>
8241
</step>
8342
</steps>
@@ -127,11 +86,6 @@ const props = defineProps({
12786
type: Array,
12887
required: true,
12988
},
130-
/** API url search user */
131-
searchUserApiUrl: {
132-
type: String,
133-
required: true,
134-
},
13589
/** primary language */
13690
primaryLocale: {
13791
type: String,
@@ -142,11 +96,6 @@ const props = defineProps({
14296
type: String,
14397
required: true,
14498
},
145-
/** API url for send email invitations for users */
146-
inviteUserApiUrl: {
147-
type: String,
148-
required: true,
149-
},
15099
pageTitle: {
151100
type: String,
152101
required: true,
@@ -155,24 +104,10 @@ const props = defineProps({
155104
type: String,
156105
required: true,
157106
},
158-
/** Url for redirect to invitations */
159-
userInvitationSavedUrl: {
160-
type: String,
161-
required: true,
162-
},
163-
/** all user groups */
164-
userGroups: {
165-
type: Array,
166-
required: true,
167-
},
168-
/** User object for edit invitations */
169-
user: {
107+
invitationPayload: {
170108
type: Object,
171-
default() {
172-
return null;
173-
},
109+
required: true,
174110
},
175-
invitationPayload: {type: Object, required: true},
176111
});
177112
const {t} = useTranslation();
178113
const wrapper = ref(null);

‎src/pages/userInvitation/UserInvitationPageStore.js

+148-122
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ export const useUserInvitationPageStore = defineComponentStore(
88
(pageInitConfig) => {
99
const {openDialog} = useModal();
1010

11-
/** Invitation payload, initial valu */
11+
/** Invitation payload, initial value */
1212
const invitationPayload = ref({...pageInitConfig.invitationPayload});
1313

1414
function updatePayload(fieldName, value) {
@@ -18,27 +18,21 @@ export const useUserInvitationPageStore = defineComponentStore(
1818
/** Steps */
1919
const currentStepId = ref(pageInitConfig.steps[0].id);
2020
const steps = ref(pageInitConfig.steps);
21-
const pageTitleDescription = ref(pageInitConfig.pageTitleDescription);
22-
const primaryLocale = ref(pageInitConfig.primaryLocale);
23-
const errors = ref({});
2421
const startedSteps = ref([]);
25-
const user = ref(pageInitConfig.user);
2622

2723
/**
2824
* The currently active step
2925
*/
3026
const currentStep = computed(() => {
3127
return steps.value.find((step) => step.id === currentStepId.value);
3228
});
33-
3429
/**
3530
* The index of the currently active step
3631
* in the steps array
3732
*/
3833
const currentStepIndex = computed(() => {
3934
return steps.value.findIndex((step) => step.id === currentStepId.value);
4035
});
41-
4236
/**
4337
* Is the current step the first step?
4438
*/
@@ -54,11 +48,99 @@ export const useUserInvitationPageStore = defineComponentStore(
5448
});
5549

5650
/**
57-
* Are there any validation errors?
51+
* Add a step change to the browser history so the
52+
* user can use the browser's back button
53+
*
54+
* @param {Object} step The step to add
5855
*/
59-
const isValid = computed(() => {
60-
return Object.keys(errors.value).length === 0;
56+
function addHistory(step) {
57+
window.history.pushState({}, step.name, '#' + step.id);
58+
}
59+
60+
/**
61+
* Go to the next step or submit if this is the last step
62+
*/
63+
async function nextStep() {
64+
if (registeredActionsForSteps[currentStep.value.id]) {
65+
let shouldContinue = true;
66+
shouldContinue =
67+
await registeredActionsForSteps[currentStep.value.id]();
68+
if (!shouldContinue) {
69+
return;
70+
}
71+
}
72+
if (isOnLastStep.value) {
73+
submitInvitation();
74+
} else {
75+
if (!currentStep.value?.skipInvitationUpdate) {
76+
await updateInvitation();
77+
// this needs to check only relevant errors for given step using the step.validateFields
78+
if (currentStepErrorsPerSection.value.length === 0) {
79+
openStep(steps.value[1 + currentStepIndex.value].id);
80+
}
81+
} else {
82+
openStep(steps.value[1 + currentStepIndex.value].id);
83+
}
84+
}
85+
}
86+
87+
/**
88+
* Go to a step in the wizard
89+
*
90+
* @param {String} stepId
91+
*/
92+
function openStep(stepId) {
93+
const newStep = steps.value.find((step) => step.id === stepId);
94+
if (!newStep) {
95+
return;
96+
}
97+
errors.value = [];
98+
currentStepId.value = stepId;
99+
}
100+
101+
/**
102+
* Go to the previous step in the wizard
103+
*/
104+
function previousStep() {
105+
const previousIndex = currentStepIndex.value - 1;
106+
if (previousIndex >= 0) {
107+
openStep(steps.value[previousIndex].id);
108+
}
109+
}
110+
/**
111+
* Update when the step changes
112+
*/
113+
watch(currentStepIndex, async (newVal, oldVal) => {
114+
if (newVal === oldVal) {
115+
return;
116+
}
117+
118+
// Update the list of steps that have been started
119+
steps.value.forEach((step, i) => {
120+
if (
121+
!startedSteps.value.includes(step.id) &&
122+
i <= currentStepIndex.value
123+
) {
124+
startedSteps.value.push(step.id);
125+
}
126+
});
127+
128+
// Track step changes in the title and browser history
129+
const step = steps.value[newVal];
130+
// document.title = this.getPageTitle(step);
131+
if (step.id !== window.location.hash.replace('#', '')) {
132+
addHistory(step);
133+
}
134+
135+
// Trigger validation on the review step
136+
if (newVal === steps.value.length - 1) {
137+
// validate();
138+
}
61139
});
140+
141+
/** Page titles */
142+
const pageTitleDescription = ref(pageInitConfig.pageTitleDescription);
143+
const primaryLocale = ref(pageInitConfig.primaryLocale);
62144
/**
63145
* The title to show at the top of the page
64146
*/
@@ -82,6 +164,40 @@ export const useUserInvitationPageStore = defineComponentStore(
82164
);
83165
});
84166

167+
/** All Erros */
168+
const errors = ref({});
169+
170+
/**
171+
* Are there any validation errors?
172+
*/
173+
const isValid = computed(() => {
174+
return !Object.keys(errors.value).length;
175+
});
176+
177+
/**
178+
* set current step section errors
179+
*/
180+
const currentStepErrorsPerSection = computed(() => {
181+
let error = [];
182+
if (Object.keys(errors.value).length != 0) {
183+
currentStep.value.sections.forEach((element, index) => {
184+
if (element.props.validateFields.length > 0) {
185+
let sectionErr = {};
186+
element.props.validateFields.forEach((field) => {
187+
if (Object.keys(errors.value).includes(field)) {
188+
sectionErr[field] = errors.value[field];
189+
}
190+
});
191+
if (Object.keys(sectionErr).length != 0) {
192+
error[index] = sectionErr;
193+
}
194+
}
195+
});
196+
}
197+
198+
return error;
199+
});
200+
85201
/** Handling invitation */
86202
const invitationId = ref(null);
87203
/**
@@ -111,38 +227,39 @@ export const useUserInvitationPageStore = defineComponentStore(
111227
body: invitationPayload.value,
112228
expectValidationError: true,
113229
});
114-
230+
await fetch();
115231
if (validationError.value) {
116232
errors.value = validationError.value;
117233
} else {
118234
errors.value = [];
119235
}
120-
121-
await fetch();
122236
}
123237

124238
/** Submit invitation */
125239
async function submitInvitation() {
126-
const {apiUrl} = useUrl(`invitations/${invitationId.value}/submit`);
240+
await updateInvitation();
241+
if (isValid.value) {
242+
const {apiUrl} = useUrl(`invitations/${invitationId.value}/submit`);
127243

128-
const {data, fetch} = useFetch(apiUrl, {
129-
method: 'POST',
130-
body: {},
131-
});
244+
const {data, fetch} = useFetch(apiUrl, {
245+
method: 'POST',
246+
body: {},
247+
});
132248

133-
await fetch();
134-
if (data.value) {
135-
openDialog({
136-
title: 'Invitation sent',
137-
actions: [
138-
{
139-
label: 'Ok',
140-
callback: (close) => {
141-
close();
249+
await fetch();
250+
if (data.value) {
251+
openDialog({
252+
title: 'Invitation sent',
253+
actions: [
254+
{
255+
label: 'Ok',
256+
callback: (close) => {
257+
close();
258+
},
142259
},
143-
},
144-
],
145-
});
260+
],
261+
});
262+
}
146263
}
147264
}
148265

@@ -151,37 +268,6 @@ export const useUserInvitationPageStore = defineComponentStore(
151268
registeredActionsForSteps[stepId] = callback;
152269
}
153270

154-
/**
155-
* Update when the step changes
156-
*/
157-
watch(currentStepIndex, async (newVal, oldVal) => {
158-
if (newVal === oldVal) {
159-
return;
160-
}
161-
162-
// Update the list of steps that have been started
163-
steps.value.forEach((step, i) => {
164-
if (
165-
!startedSteps.value.includes(step.id) &&
166-
i <= currentStepIndex.value
167-
) {
168-
startedSteps.value.push(step.id);
169-
}
170-
});
171-
172-
// Track step changes in the title and browser history
173-
const step = steps.value[newVal];
174-
// document.title = this.getPageTitle(step);
175-
if (step.id !== window.location.hash.replace('#', '')) {
176-
addHistory(step);
177-
}
178-
179-
// Trigger validation on the review step
180-
if (newVal === steps.value.length - 1) {
181-
// validate();
182-
}
183-
});
184-
185271
onMounted(() => {
186272
/**
187273
* Open the correct step when the page is loaded
@@ -191,66 +277,6 @@ export const useUserInvitationPageStore = defineComponentStore(
191277
}
192278
});
193279

194-
/**
195-
* Add a step change to the browser history so the
196-
* user can use the browser's back button
197-
*
198-
* @param {Object} step The step to add
199-
*/
200-
function addHistory(step) {
201-
window.history.pushState({}, step.name, '#' + step.id);
202-
}
203-
204-
/**
205-
* Go to the next step or submit if this is the last step
206-
*/
207-
async function nextStep() {
208-
if (registeredActionsForSteps[currentStep.value.id]) {
209-
let shouldContinue = true;
210-
shouldContinue =
211-
await registeredActionsForSteps[currentStep.value.id]();
212-
if (!shouldContinue) {
213-
return;
214-
}
215-
}
216-
if (isOnLastStep.value) {
217-
submitInvitation();
218-
} else {
219-
if (!currentStep.value?.skipInvitationUpdate) {
220-
await updateInvitation();
221-
// this needs to check only relevant errors for given step using the step.validateFields
222-
if (!errors.value?.length) {
223-
openStep(steps.value[1 + currentStepIndex.value].id);
224-
}
225-
} else {
226-
openStep(steps.value[1 + currentStepIndex.value].id);
227-
}
228-
}
229-
}
230-
231-
/**
232-
* Go to a step in the wizard
233-
*
234-
* @param {String} stepId
235-
*/
236-
function openStep(stepId) {
237-
const newStep = steps.value.find((step) => step.id === stepId);
238-
if (!newStep) {
239-
return;
240-
}
241-
currentStepId.value = stepId;
242-
}
243-
244-
/**
245-
* Go to the previous step in the wizard
246-
*/
247-
function previousStep() {
248-
const previousIndex = currentStepIndex.value - 1;
249-
if (previousIndex >= 0) {
250-
openStep(steps.value[previousIndex].id);
251-
}
252-
}
253-
254280
return {
255281
invitationPayload,
256282
updatePayload,
@@ -265,12 +291,12 @@ export const useUserInvitationPageStore = defineComponentStore(
265291
startedSteps,
266292
stepTitle,
267293
openStep,
294+
currentStepErrorsPerSection,
268295

269296
//page values
270297
steps,
271298
pageTitleDescription,
272299
errors,
273-
user,
274300

275301
//form feilds
276302
primaryLocale,

‎src/pages/userInvitation/UserInvitationSearchFormStep.vue

+4-2
Original file line numberDiff line numberDiff line change
@@ -34,9 +34,10 @@ import FieldText from '@/components/Form/fields/FieldText.vue';
3434
import {useUserInvitationPageStore} from './UserInvitationPageStore';
3535
import {useFetch} from '@/composables/useFetch';
3636
import {useUrl} from '@/composables/useUrl';
37+
3738
defineProps({
38-
section: {
39-
type: Object,
39+
validateFields: {
40+
type: Array,
4041
default() {
4142
return null;
4243
},
@@ -84,6 +85,7 @@ async function searchUser() {
8485
store.updatePayload('familyName', user.fullName.split(' ')[1]);
8586
store.updatePayload('orcid', user.orcid);
8687
store.updatePayload('currentUserGroups', user.groups);
88+
store.updatePayload('affiliation', user.affiliation);
8789
} else {
8890
// TODO: add back error handling
8991
/*errors.value = {

‎src/pages/userInvitation/UserInvitationUserGroupsTable.vue

+30-15
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,10 @@
88
<TableColumn></TableColumn>
99
</TableHeader>
1010
<TableBody>
11-
<TableRow v-for="(row, index) in store.currentUserGroups" :key="index">
11+
<TableRow
12+
v-for="(row, index) in store.invitationPayload.currentUserGroups"
13+
:key="index"
14+
>
1215
<TableCell>
1316
{{ row.name[store.primaryLocale] }}
1417
</TableCell>
@@ -22,22 +25,20 @@
2225
{{ row.masthead ? row.masthead : '---' }}
2326
</TableCell>
2427
<TableCell>
25-
<pkp-button
26-
:is-warnable="true"
27-
@click="store.removeUserGroup(row, index)"
28-
>
28+
<pkp-button :is-warnable="true" @click="removeUserGroup(row, index)">
2929
{{ t('invitation.role.removeRole.button') }}
3030
</pkp-button>
3131
</TableCell>
3232
</TableRow>
33-
<TableRow v-for="(row, index) in userGroups" :key="index">
33+
<TableRow v-for="(row, index) in allUserGroups" :key="index">
3434
<TableCell>
3535
<field-select
3636
name="userGroup"
3737
:label="t('invitation.role.selectRole')"
3838
:is-required="true"
3939
:value="row.userGroup"
4040
:options="availableUserGroups"
41+
:all-errors="userGroupErrors[index]"
4142
class="userInvitation__roleSelect"
4243
@change="
4344
(fieldName, propName, newValue, localeKey) =>
@@ -52,6 +53,7 @@
5253
input-type="date"
5354
:is-required="true"
5455
:value="row.dateStart"
56+
:all-errors="userGroupErrors[index]"
5557
@change="
5658
(fieldName, propName, newValue, localeKey) =>
5759
updateUserGroup(index, fieldName, newValue)
@@ -65,6 +67,7 @@
6567
input-type="date"
6668
:value="row.dateEnd"
6769
:is-required="true"
70+
:all-errors="userGroupErrors[index]"
6871
@change="
6972
(fieldName, propName, newValue, localeKey) =>
7073
updateUserGroup(index, fieldName, newValue)
@@ -81,6 +84,7 @@
8184
{label: 'Appear on the masthead', value: true},
8285
{label: 'Dose not appear on the masthead', value: false},
8386
]"
87+
:all-errors="userGroupErrors[index]"
8488
@change="
8589
(fieldName, propName, newValue, localeKey) =>
8690
updateUserGroup(index, fieldName, newValue)
@@ -106,6 +110,7 @@
106110

107111
<script setup>
108112
import {computed} from 'vue';
113+
import {useTranslation} from '@/composables/useTranslation';
109114
import PkpTable from '@/components/TableNext/Table.vue';
110115
import TableCell from '@/components/TableNext/TableCell.vue';
111116
import TableHeader from '@/components/TableNext/TableHeader.vue';
@@ -118,41 +123,51 @@ import FieldText from '@/components/Form/fields/FieldText.vue';
118123
import {useUserInvitationPageStore} from './UserInvitationPageStore';
119124
120125
const store = useUserInvitationPageStore();
126+
const {t} = useTranslation();
121127
122-
const userGroups = computed(() => store.invitationPayload.userGroups);
128+
const allUserGroups = computed(() => store.invitationPayload.userGroupsToAdd);
123129
124130
const props = defineProps({
125-
section: {type: Object, required: true},
131+
userGroups: {type: Object, required: true},
126132
});
127133
128134
function updateUserGroup(index, fieldName, newValue) {
129-
const userGroupsUpdate = [...store.invitationPayload.userGroups];
135+
const userGroupsUpdate = [...store.invitationPayload.userGroupsToAdd];
130136
131137
userGroupsUpdate[index][fieldName] = newValue;
132138
133-
store.updatePayload('userGroups', userGroupsUpdate);
139+
store.updatePayload('userGroupsToAdd', userGroupsUpdate);
134140
}
135141
136142
const availableUserGroups = computed(() => {
137-
return props.section.userGroups.filter((element) => {
143+
return props.userGroups.filter((element) => {
138144
return !store.invitationPayload.currentUserGroups.find(
139145
(data) => data.id === element.value,
140146
);
141147
});
142148
});
143149
144150
function addUserGroup() {
145-
const userGroupsUpdate = [...store.invitationPayload.userGroups];
151+
const userGroupsUpdate = [...store.invitationPayload.userGroupsToAdd];
146152
userGroupsUpdate.push({
147-
role_name: null,
153+
userGroup: null,
148154
dateStart: null,
149155
dateEnd: null,
150156
masthead: null,
151-
value: null,
152157
});
158+
store.updatePayload('userGroupsToAdd', userGroupsUpdate);
159+
}
153160
154-
store.updatePayload('userGroups', userGroupsUpdate);
161+
function removeUserGroup(userGroup, index) {
162+
store.invitationPayload.currentUserGroups.splice(index, 1);
163+
const userGroupsToRemove = [...store.invitationPayload.userGroupsToRemove];
164+
userGroupsToRemove.push(userGroup.id);
165+
store.updatePayload('userGroupsToRemove', userGroupsToRemove);
155166
}
167+
168+
const userGroupErrors = computed(() => {
169+
return store.errors.userGroupsToAdd || [];
170+
});
156171
</script>
157172
<style>
158173
select {

‎src/pages/userInvitation/mocks/pageInitConfig.js

+215-203
Large diffs are not rendered by default.

‎src/pages/viewInvitation/ViewInvitationDetails.vue

-38
This file was deleted.

‎src/pages/viewInvitation/ViewInvitationPage.vue

-118
This file was deleted.

0 commit comments

Comments
 (0)
Please sign in to comment.