Skip to content

Commit 7489779

Browse files
committed
Contributor Roles and Type
1 parent 88f4845 commit 7489779

15 files changed

+632
-40
lines changed

src/components/Form/Form.vue

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@
7878
<script>
7979
import FormLocales from './FormLocales.vue';
8080
import FormPage from './FormPage.vue';
81-
import {shouldShowField} from './formHelpers';
81+
import {shouldShowField, requireWhen} from './formHelpers';
8282
import Icon from '@/components/Icon/Icon.vue';
8383
8484
export default {
@@ -88,6 +88,11 @@ export default {
8888
FormPage,
8989
Icon,
9090
},
91+
provide() {
92+
return {
93+
requireWhen: (isRequired) => requireWhen(isRequired, this.fields),
94+
};
95+
},
9196
props: {
9297
/** Used by a parent component, such as `Container`, to identify events emitted from the form and update the form props when necessary. */
9398
id: String,
@@ -379,8 +384,8 @@ export default {
379384
let errors = {};
380385
this.fields.forEach((field) => {
381386
if (
382-
!field.isRequired ||
383-
!shouldShowField(field, this.fields, this.groups)
387+
!shouldShowField(field, this.fields, this.groups) ||
388+
!requireWhen(field.isRequired, this.fields)
384389
) {
385390
return;
386391
}

src/components/Form/FormFieldLabel.vue

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,10 @@
77
<template v-else>
88
{{ label }}
99
</template>
10-
<span v-if="isRequired" class="pkpFormFieldLabel__required">
10+
<span
11+
v-if="requireWhen ? requireWhen(isRequired) : isRequired"
12+
class="pkpFormFieldLabel__required"
13+
>
1114
<span class="aria-hidden">*</span>
1215
<span class="-screenReader">{{ requiredLabel }}</span>
1316
</span>
@@ -17,14 +20,15 @@
1720
<script>
1821
export default {
1922
name: 'FormFieldLabel',
23+
inject: ['requireWhen', null],
2024
props: {
2125
labelId: String,
2226
controlId: String,
2327
label: String,
2428
localeLabel: String,
2529
multilingualLabel: String,
2630
isRequired: {
27-
type: Boolean,
31+
type: [Boolean, Array],
2832
default: false,
2933
},
3034
requiredLabel: String,

src/components/Form/fields/FieldBase.vue

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,8 +42,9 @@ export default {
4242
},
4343
/** Whether or not this field should be presented for each supported language. */
4444
isMultilingual: Boolean,
45-
/** Whether or not a value for this field should be required. */
46-
isRequired: Boolean,
45+
/** Whether or not a value for this field should be required.
46+
* You can also pass an array to require a specific value or multiple values. Similar as below 'showWhen'. */
47+
isRequired: [Boolean, Array],
4748
/** The `name` of another field which should have a truthy value before this field is shown.
4849
* You can also pass an array to require a specific value or multiple values. For example, `['primaryLocale', 'en_US']` would hide this field unless the `primaryLocale` field had a value of `en_US`. Or `['primaryLocale', ['en_US', 'fr_CA']]` to check for multiple values. */
4950
showWhen: [String, Array],

src/components/Form/formHelpers.js

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,3 +66,20 @@ export function shouldShowField(field, fields, groups) {
6666
shouldShowGroup(group, fields) && shouldShowFieldWithinGroup(field, fields)
6767
);
6868
}
69+
70+
/**
71+
* Require when a field value
72+
*
73+
* @param {Boolean|Array} requireValue Require value
74+
* @param {Object} fields All form fields
75+
* @return {Boolean}
76+
*/
77+
78+
export function requireWhen(requireValue, fields) {
79+
if (!Array.isArray(requireValue)) return requireValue;
80+
81+
const [whenFieldName, whenValues] = requireValue;
82+
const val = fields.find((field) => field.name === whenFieldName)?.value;
83+
84+
return whenValues.includes(val);
85+
}
Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
export default {
2-
name: 'userGroupId',
2+
name: 'contributorRoles',
33
component: 'field-options',
4-
label: "Contributor's role",
5-
type: 'radio',
6-
isRequired: false,
7-
value: 14,
4+
label: "Contributor's roles",
5+
type: 'checkbox',
6+
isRequired: true,
7+
value: [1],
88
groupId: 'default',
99
options: [
10-
{value: 14, label: 'Author'},
11-
{value: 15, label: 'Translator'},
10+
{value: 1, label: 'Author'},
11+
{value: 2, label: 'Translator'},
1212
],
1313
};

src/components/Form/mocks/form-contributors.js

Lines changed: 24 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,24 @@ export default {
77
method: 'POST',
88
action: '/submissions/9/publications/__publicationId__/contributors',
99
fields: [
10+
{
11+
name: 'contributorType',
12+
component: 'field-options',
13+
label: 'Contributor Type',
14+
groupId: 'default',
15+
isRequired: true,
16+
isMultilingual: false,
17+
isInert: false,
18+
value: 'PERSON',
19+
type: 'radio',
20+
isOrderable: false,
21+
allowOnlySorting: false,
22+
options: [
23+
{value: 'PERSON', label: 'Person'},
24+
{value: 'ORGANIZATION', label: 'Organization or group'},
25+
{value: 'ANONYMOUS', label: 'Anonymous'},
26+
],
27+
},
1028
{
1129
name: 'givenName',
1230
component: 'field-text',
@@ -113,20 +131,20 @@ export default {
113131
prefix: '',
114132
},
115133
{
116-
name: 'userGroupId',
134+
name: 'contributorRoles',
117135
component: 'field-options',
118136
label: "Contributor's role",
119137
groupId: 'default',
120-
isRequired: false,
138+
isRequired: true,
121139
isMultilingual: false,
122140
isInert: false,
123-
value: 14,
124-
type: 'radio',
141+
value: [1],
142+
type: 'checkbox',
125143
isOrderable: false,
126144
allowOnlySorting: false,
127145
options: [
128-
{value: 14, label: 'Author'},
129-
{value: 15, label: 'Translator'},
146+
{value: 1, label: 'Author'},
147+
{value: 2, label: 'Translator'},
130148
],
131149
},
132150
{

src/components/ListPanel/contributors/ContributorsListPanel.vue

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,11 @@
5555
<template #item-title="{item}">
5656
<div class="whitespace-normal text-justify">
5757
{{ item.fullName }}
58-
<Badge v-if="item.userGroupName">
59-
{{ localize(item.userGroupName) }}
58+
<Badge
59+
v-for="contributorRole in item.contributorRoles"
60+
:key="contributorRole.id"
61+
>
62+
{{ localize(contributorRole.name) }}
6063
</Badge>
6164
</div>
6265
</template>
@@ -399,6 +402,10 @@ export default {
399402
} else if (field.name === 'affiliations') {
400403
field.authorId = author['id'];
401404
field.value = author[field.name];
405+
} else if (field.name === 'contributorRoles') {
406+
field.value = author[field.name].map(
407+
(contributorRole) => contributorRole.id,
408+
);
402409
} else if (Object.keys(author).includes(field.name)) {
403410
field.value = author[field.name];
404411
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
<template>
2+
<DialogBody :is-loading="isLoading">
3+
<div
4+
v-strip-unsafe-html="
5+
t('manager.contributorRoles.alert.delete.message.body', {
6+
identifier: contributorRoleIdentifier,
7+
})
8+
"
9+
></div>
10+
11+
<FieldText
12+
class="mt-8"
13+
size="large"
14+
:value="inputValue"
15+
@input="inputValue = $event.target.value"
16+
/>
17+
<template #actions>
18+
<PkpButton
19+
:disabled="inputValue !== contributorRoleIdentifier"
20+
is-primary
21+
@click="confirmInput"
22+
>
23+
{{ t('common.confirmDelete') }}
24+
</PkpButton>
25+
<PkpButton :is-warnable="true" @click="closeModal">
26+
{{ t('common.cancel') }}
27+
</PkpButton>
28+
</template>
29+
</DialogBody>
30+
</template>
31+
32+
<script setup>
33+
import PkpButton from '@/components/Button/Button.vue';
34+
import DialogBody from '@/components/Modal/DialogBody.vue';
35+
import FieldText from '@/components/Form/fields/FieldText.vue';
36+
import {useLocalize} from '@/composables/useLocalize';
37+
import {ref} from 'vue';
38+
39+
const {t} = useLocalize();
40+
41+
const inputValue = ref('');
42+
const isLoading = ref(false);
43+
const props = defineProps({
44+
/**
45+
* Function to be executed when the user confirms the deletion.
46+
*/
47+
onConfirm: {
48+
type: Function,
49+
required: true,
50+
},
51+
/**
52+
* Function to be executed when the dialog is closed.
53+
*/
54+
onClose: {
55+
type: Function,
56+
required: false,
57+
},
58+
/**
59+
* Identifier of the contributor role to be deleted. Displayed in the dialog message
60+
* and used as the required input for confirming the deletion.
61+
*/
62+
contributorRoleIdentifier: {
63+
type: String,
64+
required: true,
65+
},
66+
});
67+
68+
/**
69+
* Function to confirm the deletion of the role. It checks if the input value
70+
* matches the role identifier and then calls the onConfirm function.
71+
*/
72+
async function confirmInput() {
73+
if (inputValue.value !== props.contributorRoleIdentifier) {
74+
return;
75+
}
76+
77+
isLoading.value = true;
78+
await props.onConfirm();
79+
closeModal();
80+
}
81+
82+
function closeModal() {
83+
props.onClose?.();
84+
}
85+
</script>
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
<template>
2+
<PkpTable>
3+
<template #label>{{ t('manager.contributorRoles.title') }}</template>
4+
<template #top-controls>
5+
<PkpButton @click="contributorRoleManagerStore.roleAdd">
6+
{{ t('manager.contributorRoles.add') }}
7+
</PkpButton>
8+
</template>
9+
<TableHeader>
10+
<TableColumn>
11+
<span>
12+
{{ t('manager.contributorRoles.name') }}
13+
</span>
14+
</TableColumn>
15+
<TableColumn>
16+
<span>
17+
{{ t('manager.contributorRoles.identifier') }}
18+
</span>
19+
</TableColumn>
20+
<TableColumn>
21+
<span class="sr-only">{{ t('common.moreActions') }}</span>
22+
</TableColumn>
23+
</TableHeader>
24+
25+
<TableBody>
26+
<TableRow
27+
v-for="role in contributorRoleManagerStore.roles"
28+
:key="role.id"
29+
>
30+
<TableCell :is-row-header="true">
31+
<span>
32+
{{ localize(role.name) }}
33+
</span>
34+
</TableCell>
35+
<TableCell>
36+
<span>
37+
{{ role.contributorRoleIdentifier }}
38+
</span>
39+
</TableCell>
40+
<TableCell>
41+
<DropdownActions
42+
:label="t('common.moreActions')"
43+
button-variant="ellipsis"
44+
:actions="contributorRoleManagerStore.getItemActions()"
45+
@action="
46+
(actionName) => contributorRoleManagerStore[actionName](role)
47+
"
48+
/>
49+
</TableCell>
50+
</TableRow>
51+
</TableBody>
52+
</PkpTable>
53+
</template>
54+
55+
<script setup>
56+
import PkpTable from '@/components/Table/Table.vue';
57+
import TableHeader from '@/components/Table/TableHeader.vue';
58+
import TableColumn from '@/components/Table/TableColumn.vue';
59+
import TableBody from '@/components/Table/TableBody.vue';
60+
import TableRow from '@/components/Table/TableRow.vue';
61+
import TableCell from '@/components/Table/TableCell.vue';
62+
import DropdownActions from '@/components/DropdownActions/DropdownActions.vue';
63+
import PkpButton from '@/components/Button/Button.vue';
64+
import {localize} from '@/utils/i18n.js';
65+
import {useContributorRoleManagerStore} from '@/managers/ContributorRoleManager/contributorRoleManagerStore.js';
66+
67+
const props = defineProps({});
68+
69+
const contributorRoleManagerStore = useContributorRoleManagerStore(props);
70+
</script>
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<template>
2+
<div>
3+
<SideModalBody>
4+
<template #title>
5+
{{ title }}
6+
</template>
7+
<SideModalLayoutBasic>
8+
<PkpForm
9+
ref="editRole"
10+
class="contributorRoles__contributorRoleForm"
11+
v-bind="form"
12+
@set="set"
13+
@success="
14+
(...args) => {
15+
$emit('contributorRoleSaved', ...args);
16+
onSuccess();
17+
}
18+
"
19+
></PkpForm>
20+
</SideModalLayoutBasic>
21+
</SideModalBody>
22+
</div>
23+
</template>
24+
25+
<script setup>
26+
import SideModalBody from '@/components/Modal/SideModalBody.vue';
27+
import SideModalLayoutBasic from '@/components/Modal/SideModalLayoutBasic.vue';
28+
import PkpForm from '@/components/Form/Form.vue';
29+
import {useForm} from '@/composables/useForm';
30+
31+
const props = defineProps({
32+
title: {type: String, required: true},
33+
formProps: {ype: Object, required: true},
34+
onSuccess: {type: Function, required: true},
35+
});
36+
37+
const {set, form} = useForm(props.formProps);
38+
39+
defineEmits(['contributorRoleSaved']);
40+
</script>

0 commit comments

Comments
 (0)