Skip to content

pkp/pkp-lib#857 Credit Roles #661

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jul 16, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/components/Form/FormGroup.vue
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ import FieldBaseAutosuggest from './fields/FieldBaseAutosuggest.vue';
import FieldAuthors from './fields/FieldAuthors.vue';
import FieldColor from './fields/FieldColor.vue';
import FieldControlledVocab from './fields/FieldControlledVocab.vue';
import FieldCreditRoles from './fields/FieldCreditRoles.vue';
import FieldPubId from './fields/FieldPubId.vue';
import FieldHtml from './fields/FieldHtml.vue';
import FieldMetadataSetting from './fields/FieldMetadataSetting.vue';
Expand Down Expand Up @@ -95,6 +96,7 @@ export default {
FieldAuthors,
FieldColor,
FieldControlledVocab,
FieldCreditRoles,
FieldPubId,
FieldHtml,
FieldMetadataSetting,
Expand Down
19 changes: 19 additions & 0 deletions src/components/Form/fields/FieldCreditRoles.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks';

import * as FieldCreditRolesStories from './FieldCreditRoles.stories.js';

<Meta of={FieldCreditRolesStories} />

# FieldCreditRole

## Usage

A special component to maintain Credit roles and degrees of authors (contributors).

The CreditRoles currently saved are shown in a tabular way as roles and degrees.

The `value` is an array of objects `{ role, degree }`.

<Primary />
<Controls />
<Stories />
29 changes: 29 additions & 0 deletions src/components/Form/fields/FieldCreditRoles.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
import FieldCreditRoles from './FieldCreditRoles.vue';
import FieldCreditRolesMock from '@/components/Form/mocks/field-credit-roles';

export default {
title: 'Forms/FieldCreditRoles',
component: FieldCreditRoles,
render: (args) => ({
components: {FieldCreditRoles},
setup() {
function change(name, prop, newValue, localeKey) {
args[prop] = newValue;
}

return {args, change};
},
template: '<FieldCreditRoles v-bind="args" @change="change"/>',
}),
parameters: {
docs: {
story: {
height: '500px',
},
},
},
};

export const Base = {
args: {...FieldCreditRolesMock},
};
139 changes: 139 additions & 0 deletions src/components/Form/fields/FieldCreditRoles.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,139 @@
<template>
<!-- To be able to scroll to this field on error-->
<div
:id="`${props.formId}-${props.name}`"
class="pkpFormField pkpFormField--creditRoles"
>
<div class="pkpFormField__heading">
<label :id="labelId" class="pkpFormFieldLabel">
{{ t('submission.submit.creditRoles.title', {}) }}
</label>
</div>
<div :id="descriptionId" class="pkpFormField__description">
{{ t('submission.submit.creditRoles.description', {}) }}
</div>
<div class="pkpFormField__control pkpFormField--creditRoles__control">
<PkpTable :labelled-by="labelId" :described-by="descriptionId">
<TableHeader>
<TableColumn id="" class="w-[45%]">
{{ t('submission.submit.creditRoles.role', {}) }}
</TableColumn>
<TableColumn id="">
{{ t('submission.submit.creditRoles.degree', {}) }}
</TableColumn>
<TableColumn id="" class="w-[100px]">&nbsp;</TableColumn>
</TableHeader>
<TableBody>
<TableRow
v-for="({role, degree}, creditRoleIndex) in currentValue"
:key="creditRoleIndex"
>
<TableCell>
<FieldSelect
name="role"
:label="t('submission.submit.creditRoles.selectRole')"
:is-required="true"
:value="role"
:options="props.options.roles"
class="creditRole__roleSelect"
@change="
(fieldName, propName, newValue, localeKey) =>
updateCreditRole(creditRoleIndex, fieldName, newValue)
"
/>
</TableCell>
<TableCell>
<FieldSelect
name="degree"
:label="t('submission.submit.creditRoles.selectDegree')"
:value="degree"
:options="props.options.degrees"
class="creditRole__roleSelect"
@change="
(fieldName, propName, newValue, localeKey) =>
updateCreditRole(creditRoleIndex, fieldName, newValue)
"
/>
</TableCell>
<TableCell>
<PkpButton
:is-warnable="true"
@click="removeCreditRole(creditRoleIndex)"
>
{{ t('submission.submit.creditRoles.button.remove') }}
</PkpButton>
</TableCell>
</TableRow>
</TableBody>
<template #bottom-controls>
<PkpButton @click="addCreditRole()">
{{ t('submission.submit.creditRoles.button.add') }}
</PkpButton>
</template>
</PkpTable>
</div>
</div>
</template>

<script setup>
import {computed, useId} from 'vue';
import {t} from '@/utils/i18n';
import PkpButton from '@/components/Button/Button.vue';
import PkpTable from '@/components/Table/Table.vue';
import TableHeader from '@/components/Table/TableHeader.vue';
import TableBody from '@/components/Table/TableBody.vue';
import TableRow from '@/components/Table/TableRow.vue';
import TableColumn from '@/components/Table/TableColumn.vue';
import TableCell from '@/components/Table/TableCell.vue';
import FieldSelect from './FieldSelect.vue';

const props = defineProps({
/** Field key used for form submission */
name: {
type: String,
default: null,
},
/** The ID of the form this field should appear in. This is passed down from the `Form`. */
formId: {
type: String,
default: null,
},
/** Current value of the field */
value: {
type: Array,
default: () => [],
},
options: {
type: Object,
required: true,
},
});

/**
* Accessibility
*/
const labelId = useId();
const descriptionId = useId();

const emit = defineEmits(['change']);

const currentValue = computed({
get: () => props.value,
set: (newVal) => emit('change', props.name, 'value', newVal),
});

function addCreditRole() {
currentValue.value.push({
role: props.options.roles[0].value,
degree: props.options.degrees[0].value,
});
}

function removeCreditRole(index) {
currentValue.value.splice(index, 1);
}

function updateCreditRole(index, fieldName, newValue) {
currentValue.value[index][fieldName] = newValue;
}
</script>
36 changes: 36 additions & 0 deletions src/components/Form/mocks/field-credit-roles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
export default {
name: 'contributor-credit-roles',
component: 'contributor-credit-roles',
value: [
{
role: 'https://credit.niso.org/contributor-roles/data-curation/',
degree: null,
},
],
options: {
roles: [
{
label: 'Conceptualization',
value: 'https://credit.niso.org/contributor-roles/conceptualization/',
},
{
label: 'Data Curation',
value: 'https://credit.niso.org/contributor-roles/data-curation/',
},
{
label: 'Formal Analysis',
value: 'https://credit.niso.org/contributor-roles/formal-analysis/',
},
],
degrees: [
{
label: '',
value: null,
},
{
label: 'LEAD',
value: 'LEAD',
},
],
},
};