Skip to content
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

pkp/pkp-lib#9658 user access table and table actions #437

Merged
merged 19 commits into from
Feb 4, 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
7 changes: 7 additions & 0 deletions public/globals.js
Original file line number Diff line number Diff line change
Expand Up @@ -406,6 +406,7 @@ window.pkp = {
'email.cc': 'CC',
'email.confirmSwitchLocale':
'Are you sure you want to change to {$localeName} to compose this email? Any changes you have made to the subject and body of the email will be lost.',
'email.email': 'Email',
'email.subject': 'Subject',
'email.to': 'To',
'fileManager.copyeditedFiles': 'Copyedited Files',
Expand Down Expand Up @@ -439,18 +440,22 @@ window.pkp = {
'grid.action.deleteContributor': 'Delete Contributor',
'grid.action.deleteContributor.confirmationMessage':
'Are you sure you want to remove {$name} as a contributor? This action can not be undone.',
'grid.action.disable': 'Disable User',
'grid.action.edit': 'Edit',
'grid.action.editFile': 'Edit a file',
'grid.action.logInAs': 'Login As',
'grid.action.moreInformation': 'More Information',
'grid.action.order': 'Order',
'grid.action.remove': 'Remove',
'grid.action.saveOrdering': 'Save Order',
'grid.action.sort': 'Sort',
'grid.columns.actions': 'Actions',
'grid.libraryFiles.submission.title': 'Submission Library',
'grid.noItems': 'No Items',
'grid.user.confirmLogInAs':
'Log in as this user? All actions you perform will be attributed to this user.',
'grid.user.currentUsers':'Current Users',
'grid.action.mergeUser':'Merge User',
'help.help': 'Help',
'informationCenter.informationCenter': 'Information Center',
'invitation.cancelInvite.actionName': 'Cancel Invite',
Expand Down Expand Up @@ -770,6 +775,8 @@ window.pkp = {
'Are you sure want remove this role permanently?',
'user.role.reviewer': 'Reviewer',
'user.role.reviewers': 'Reviewers',
'user.roles': 'Roles',
'user.startDate': 'Start Date',
'user.username': 'Username',
'userInvitation.cancel.goBack': 'Go Back',
'userInvitation.cancel.message':
Expand Down
11 changes: 11 additions & 0 deletions src/managers/UserAccessManager/UserAccessManager.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import {Primary, Controls, Stories, Meta, ArgTypes} from '@storybook/blocks';

import * as UserAccessManager from './UserAccessManager.stories.js';

<Meta of={UserAccessManager} />

# User Access Manager

This table displays the current list of users ns and allows the user access manager to search users.

<ArgTypes />
39 changes: 39 additions & 0 deletions src/managers/UserAccessManager/UserAccessManager.stories.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import UserAccessManager from './UserAccessManager.vue';
import {http, HttpResponse} from 'msw';
import userAccessMock from './mocks/UserAccessMock.js';

export default {
title: 'Managers/UserAccessManager',
component: UserAccessManager,
};

export const Init = {
render: (args) => ({
components: {UserAccessManager},
setup() {
return {args};
},
template: '<UserAccessManager v-bind="args"/>',
}),
parameters: {
msw: {
handlers: [
http.get(
'https://mock/index.php/publicknowledge/api/v1/users',
async ({request}) => {
const url = new URL(request.url);
const offset = parseInt(url.searchParams.get('offset') || 0);
const count = parseInt(url.searchParams.get('count'));
const users = userAccessMock.items.slice(offset, offset + count);

return HttpResponse.json({
itemsMax: userAccessMock.itemsMax,
items: users,
});
},
),
],
},
},
args: [],
};
71 changes: 71 additions & 0 deletions src/managers/UserAccessManager/UserAccessManager.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<template>
<PkpTable class="mt-2">
<template #label>
<h3 class="text-3xl-bold">
{{ t('grid.user.currentUsers') }}({{
store.userAccessPagination.itemCount
}})
</h3>
</template>
<template #top-controls>
<Search
:search-phrase="searchPhrase"
:search-label="t('userAccess.search')"
@search-phrase-changed="store.setSearchPhrase"
/>
</template>
<TableHeader>
<TableColumn v-for="(column, i) in store.getColumns()" :key="i">
<span :class="column.headerSrOnly ? 'sr-only' : ''">
{{ column.header }}
</span>
</TableColumn>
</TableHeader>
<TableBody>
<TableRow v-for="user in store.userList" :key="user.id">
<component
:is="Components[column.component] || column.component"
v-for="(column, i) in store.getColumns()"
:key="i"
:user="user"
></component>
</TableRow>
</TableBody>
</PkpTable>
<TablePagination
:pagination="store.userAccessPagination"
@set-page="store.setCurrentPage"
/>
</template>

<script setup>
import PkpTable from '@/components/Table/Table.vue';
import TableHeader from '@/components/Table/TableHeader.vue';
import TableColumn from '@/components/Table/TableColumn.vue';
import TableBody from '@/components/Table/TableBody.vue';
import TableRow from '@/components/Table/TableRow.vue';
import {useUserAccessManagerStore} from './UserAccessManagerStore.js';
import TablePagination from '@/components/Table/TablePagination.vue';
import {useTranslation} from '@/composables/useTranslation';
import Search from '@/components/Search/Search.vue';
import {ref} from 'vue';
import UserAccessManagerCellStartDate from './UserAccessManagerCellStartDate.vue';
import UserAccessManagerCellUserGroups from './UserAccessManagerCellUserGroups.vue';
import UserAccessManagerCellActions from './UserAccessManagerCellActions.vue';
import UserAccessManagerCellName from './UserAccessManagerCellName.vue';
import UserAccessManagerCellEmail from './UserAccessManagerCellEmail.vue';
import UserAccessManagerCellAffiliation from './UserAccessManagerCellAffiliation.vue';

const Components = {
UserAccessManagerCellStartDate,
UserAccessManagerCellUserGroups,
UserAccessManagerCellActions,
UserAccessManagerCellName,
UserAccessManagerCellEmail,
UserAccessManagerCellAffiliation,
};

const store = useUserAccessManagerStore();
const {t} = useTranslation();
const searchPhrase = ref('');
</script>
25 changes: 25 additions & 0 deletions src/managers/UserAccessManager/UserAccessManagerCellActions.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
<template>
<TableCell>
<DropdownActions
:actions="store.getItemActions({user})"
:label="t('userAccess.management.options')"
button-variant="ellipsis"
direction="left"
@action="(actionName) => store[actionName]({user})"
/>
</TableCell>
</template>

<script setup>
import TableCell from '@/components/Table/TableCell.vue';
import DropdownActions from '@/components/DropdownActions/DropdownActions.vue';
import {useUserAccessManagerStore} from './UserAccessManagerStore.js';
import {useTranslation} from '@/composables/useTranslation';

defineProps({
user: {type: Object, required: true},
});

const store = useUserAccessManagerStore();
const {t} = useTranslation();
</script>
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<TableCell>
<span class="text-base-normal">
{{ localize(user.affiliation) }}
</span>
</TableCell>
</template>

<script setup>
import TableCell from '@/components/Table/TableCell.vue';

defineProps({
user: {type: Object, required: true},
});
</script>
15 changes: 15 additions & 0 deletions src/managers/UserAccessManager/UserAccessManagerCellEmail.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
<template>
<TableCell>
<span class="text-base-normal">
{{ user.email }}
</span>
</TableCell>
</template>

<script setup>
import TableCell from '@/components/Table/TableCell.vue';

defineProps({
user: {type: Object, required: true},
});
</script>
18 changes: 18 additions & 0 deletions src/managers/UserAccessManager/UserAccessManagerCellName.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<template>
<TableCell>
<span class="text-base-normal">
{{ user.fullName }}
</span>

<Icon v-if="user.orcid" icon="Orcid" class="h-4 w-4" :inline="true" />
</TableCell>
</template>

<script setup>
import TableCell from '@/components/Table/TableCell.vue';
import Icon from '@/components/Icon/Icon.vue';

defineProps({
user: {type: Object, required: true},
});
</script>
19 changes: 19 additions & 0 deletions src/managers/UserAccessManager/UserAccessManagerCellStartDate.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<template>
<TableCell>
<template v-for="(userGroups, i) in user.groups" :key="i">
<div class="flex flex-col">
{{ formatShortDate(userGroups?.startDate) }}
</div>
</template>
</TableCell>
</template>

<script setup>
import TableCell from '@/components/Table/TableCell.vue';
import {useDate} from '@/composables/useDate';

defineProps({
user: {type: Object, required: true},
});
const {formatShortDate} = useDate();
</script>
17 changes: 17 additions & 0 deletions src/managers/UserAccessManager/UserAccessManagerCellUserGroups.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<template>
<TableCell>
<template v-for="(userGroups, i) in user.groups" :key="i">
<div class="flex flex-col">
{{ userGroups.name }}
</div>
</template>
</TableCell>
</template>

<script setup>
import TableCell from '@/components/Table/TableCell.vue';

defineProps({
user: {type: Object, required: true},
});
</script>
Loading
Loading