chore(front): refactor admin settings users

This commit is contained in:
ArneBo 2025-03-12 12:00:48 +01:00
parent 1779e66d4a
commit 606903f7e7
5 changed files with 284 additions and 268 deletions

View File

@ -5,6 +5,8 @@ import { ref, computed, reactive, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import DangerousButton from '~/components/common/DangerousButton.vue' import DangerousButton from '~/components/common/DangerousButton.vue'
import Layout from '~/components/ui/Layout.vue'
import Button from '~/components/ui/Button.vue'
import axios from 'axios' import axios from 'axios'
@ -171,23 +173,23 @@ const launchAction = async () => {
<span v-if="needsRefresh"> <span v-if="needsRefresh">
{{ t('components.common.ActionTable.message.needsRefresh') }} {{ t('components.common.ActionTable.message.needsRefresh') }}
</span> </span>
<button <Button
class="ui basic icon button" tiny
icon="bi-arrow-clockwise"
:title="labels.refresh" :title="labels.refresh"
:aria-label="labels.refresh" :aria-label="labels.refresh"
@click="$emit('refresh')" @click="$emit('refresh')"
> />
<i class="refresh icon" />
</button>
</div> </div>
<div <Layout
v-if="actionUrl && actions.length > 0" v-if="actionUrl && actions.length > 0"
class="ui small left floated form" stack
no-gap
class="ui form"
> >
<div class="ui inline fields">
<div class="field">
<label for="actions-select">{{ t('components.common.ActionTable.label.actions') }}</label> <label for="actions-select">{{ t('components.common.ActionTable.label.actions') }}</label>
<Layout flex>
<select <select
id="actions-select" id="actions-select"
v-model="currentActionName" v-model="currentActionName"
@ -201,8 +203,6 @@ const launchAction = async () => {
{{ action.label }} {{ action.label }}
</option> </option>
</select> </select>
</div>
<div class="field">
<dangerous-button <dangerous-button
v-if="selectAll || currentAction?.isDangerous" v-if="selectAll || currentAction?.isDangerous"
:disabled="checked.length === 0 || undefined" :disabled="checked.length === 0 || undefined"
@ -227,16 +227,17 @@ const launchAction = async () => {
</span> </span>
</template> </template>
</dangerous-button> </dangerous-button>
<button <Button
v-else v-else
primary
:disabled="checked.length === 0" :disabled="checked.length === 0"
:aria-label="labels.performAction" :aria-label="labels.performAction"
:class="['ui', {disabled: checked.length === 0}, {'loading': isLoading}, 'button']" :class="[{disabled: checked.length === 0}, {'loading': isLoading}]"
style="margin-top: 7px;"
@click="launchAction" @click="launchAction"
> >
{{ t('components.common.ActionTable.button.go') }} {{ t('components.common.ActionTable.button.go') }}
</button> </Button>
</div>
<div class="count field"> <div class="count field">
<span v-if="selectAll"> <span v-if="selectAll">
{{ t('components.common.ActionTable.button.allSelected', objectsData.count) }} {{ t('components.common.ActionTable.button.allSelected', objectsData.count) }}
@ -265,11 +266,10 @@ const launchAction = async () => {
</a> </a>
</template> </template>
</div> </div>
</div> </Layout>
<div <Alert
v-if="errors.length > 0" v-if="errors.length > 0"
role="alert" red
class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
{{ t('components.common.ActionTable.header.error') }} {{ t('components.common.ActionTable.header.error') }}
@ -282,10 +282,10 @@ const launchAction = async () => {
{{ error }} {{ error }}
</li> </li>
</ul> </ul>
</div> </Alert>
<div <Alert
v-if="result" v-if="result"
class="ui positive message" green
> >
<p> <p>
<span> <span>
@ -297,8 +297,8 @@ const launchAction = async () => {
name="action-success-footer" name="action-success-footer"
:result="result" :result="result"
/> />
</div> </Alert>
</div> </Layout>
</th> </th>
</tr> </tr>
<tr> <tr>

View File

@ -8,6 +8,12 @@ import { useStore } from '~/store'
import axios from 'axios' import axios from 'axios'
import Layout from '~/components/ui/Layout.vue'
import Button from '~/components/ui/Button.vue'
import Alert from '~/components/ui/Alert.vue'
import Spacer from '~/components/ui/Spacer.vue'
import Input from '~/components/ui/Input.vue'
interface Invitation { interface Invitation {
code: string code: string
} }
@ -47,14 +53,14 @@ const getUrl = (code: string) => store.getters['instance/absoluteUrl'](router.re
<template> <template>
<div> <div>
<form <Layout
form
class="ui form" class="ui form"
@submit.prevent="submit" @submit.prevent="submit"
> >
<div <Alert
v-if="errors.length > 0" v-if="errors.length > 0"
role="alert" red
class="ui negative message"
> >
<h4 class="header"> <h4 class="header">
{{ t('components.manage.users.InvitationForm.header.failure') }} {{ t('components.manage.users.InvitationForm.header.failure') }}
@ -67,31 +73,31 @@ const getUrl = (code: string) => store.getters['instance/absoluteUrl'](router.re
{{ error }} {{ error }}
</li> </li>
</ul> </ul>
</div> </Alert>
<div class="inline fields"> <div class="inline fields">
<div class="ui field"> <Input
<label for="invitation-code">{{ t('components.manage.users.InvitationForm.label.invite') }}</label>
<input
v-model="code" v-model="code"
for="invitation-code" for="invitation-code"
name="code" name="code"
type="text" type="text"
:label="t('components.manage.users.InvitationForm.label.invite')"
:placeholder="labels.placeholder" :placeholder="labels.placeholder"
> >
</div> <template #input-right>
<div class="ui field"> <Button
<button primary
:class="['ui', {loading: isLoading}, 'button']" :class="[{loading: isLoading}]"
:disabled="isLoading" :disabled="isLoading"
type="submit" type="submit"
> >
{{ t('components.manage.users.InvitationForm.button.new') }} {{ t('components.manage.users.InvitationForm.button.new') }}
</button> </Button>
</template>
</Input>
</div> </div>
</div> </Layout>
</form> <Spacer :size="16" />
<div v-if="invitations.length > 0"> <div v-if="invitations.length > 0">
<div class="ui hidden divider" />
<table class="ui ui basic table"> <table class="ui ui basic table">
<thead> <thead>
<tr> <tr>
@ -118,12 +124,14 @@ const getUrl = (code: string) => store.getters['instance/absoluteUrl'](router.re
</tr> </tr>
</tbody> </tbody>
</table> </table>
<button <Spacer :size="8"/>
class="ui basic button" <Button
destructive
icon="bi-trash"
@click="invitations.length = 0" @click="invitations.length = 0"
> >
{{ t('components.manage.users.InvitationForm.button.clear') }} {{ t('components.manage.users.InvitationForm.button.clear') }}
</button> </Button>
</div> </div>
</div> </div>
</template> </template>

View File

@ -11,7 +11,11 @@ import moment from 'moment'
import axios from 'axios' import axios from 'axios'
import ActionTable from '~/components/common/ActionTable.vue' import ActionTable from '~/components/common/ActionTable.vue'
import Pagination from '~/components/vui/Pagination.vue' import Pagination from '~/components/ui/Pagination.vue'
import Layout from '~/components/ui/Layout.vue'
import Input from '~/components/ui/Input.vue'
import Spacer from '~/components/ui/Spacer.vue'
import Loader from '~/components/ui/Loader.vue'
import useSharedLabels from '~/composables/locale/useSharedLabels' import useSharedLabels from '~/composables/locale/useSharedLabels'
import useOrdering from '~/composables/navigation/useOrdering' import useOrdering from '~/composables/navigation/useOrdering'
@ -100,19 +104,23 @@ const labels = computed(() => ({
</script> </script>
<template> <template>
<div> <Layout
<div class="ui inline form"> form
<div class="fields"> class="ui inline form"
>
<div class="ui field"> <div class="ui field">
<label for="invitations-search">{{ t('components.manage.users.InvitationsTable.label.search') }}</label> <label for="invitations-search">{{ t('components.manage.users.InvitationsTable.label.search') }}</label>
<input <Input
id="invitations-search" id="invitations-search"
v-model="query" v-model="query"
search
name="search" name="search"
type="text" type="text"
:placeholder="labels.searchPlaceholder" :placeholder="labels.searchPlaceholder"
> />
</div> </div>
<Layout flex>
<Spacer grow />
<div class="field"> <div class="field">
<label for="invitations-ordering">{{ t('components.manage.users.InvitationsTable.ordering.label') }}</label> <label for="invitations-ordering">{{ t('components.manage.users.InvitationsTable.ordering.label') }}</label>
<select <select
@ -147,15 +155,12 @@ const labels = computed(() => ({
</option> </option>
</select> </select>
</div> </div>
</div> </Layout>
</div> </Layout>
<div class="dimmable"> <div class="dimmable">
<div <Loader
v-if="isLoading" v-if="isLoading"
class="ui active inverted dimmer" />
>
<div class="ui loader" />
</div>
<action-table <action-table
v-if="result" v-if="result"
:objects-data="result" :objects-data="result"
@ -224,17 +229,14 @@ const labels = computed(() => ({
</action-table> </action-table>
</div> </div>
<div> <div>
<pagination <Pagination
v-if="result && result.count > paginateBy" v-if="result && result.count > paginateBy"
v-model:current="page" v-model:current="page"
:compact="true"
:paginate-by="paginateBy" :paginate-by="paginateBy"
:total="result.count"
/> />
<span v-if="result && result.results.length > 0"> <span v-if="result && result.results.length > 0">
{{ t('components.manage.users.InvitationsTable.pagination.results', { start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count }, result.results.length) }} {{ t('components.manage.users.InvitationsTable.pagination.results', { start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count }, result.results.length) }}
</span> </span>
</div> </div>
</div>
</template> </template>

View File

@ -10,7 +10,11 @@ import { useI18n } from 'vue-i18n'
import axios from 'axios' import axios from 'axios'
import ActionTable from '~/components/common/ActionTable.vue' import ActionTable from '~/components/common/ActionTable.vue'
import Pagination from '~/components/vui/Pagination.vue' import Pagination from '~/components/ui/Pagination.vue'
import Layout from '~/components/ui/Layout.vue'
import Input from '~/components/ui/Input.vue'
import Spacer from '~/components/ui/Spacer.vue'
import useSharedLabels from '~/composables/locale/useSharedLabels' import useSharedLabels from '~/composables/locale/useSharedLabels'
import useErrorHandler from '~/composables/useErrorHandler' import useErrorHandler from '~/composables/useErrorHandler'
@ -102,19 +106,25 @@ const labels = computed(() => ({
</script> </script>
<template> <template>
<div> <Layout
<div class="ui inline form"> form
class="ui form"
>
<div class="fields"> <div class="fields">
<div class="ui field"> <div class="ui field">
<label for="users-search">{{ t('components.manage.users.UsersTable.label.search') }}</label> <Input
<input
id="users-search" id="users-search"
v-model="query" v-model="query"
search
:label="t('components.manage.users.UsersTable.label.search')"
name="search" name="search"
type="text" type="text"
:placeholder="labels.searchPlaceholder" :placeholder="labels.searchPlaceholder"
> />
</div> </div>
<Spacer :size="16" />
<Layout flex>
<Spacer grow />
<div class="field"> <div class="field">
<label for="users-ordering">{{ t('components.manage.users.UsersTable.ordering.label') }}</label> <label for="users-ordering">{{ t('components.manage.users.UsersTable.ordering.label') }}</label>
<select <select
@ -146,7 +156,7 @@ const labels = computed(() => ({
</option> </option>
</select> </select>
</div> </div>
</div> </Layout>
</div> </div>
<div class="dimmable"> <div class="dimmable">
<div <div
@ -257,17 +267,15 @@ const labels = computed(() => ({
</action-table> </action-table>
</div> </div>
<div> <div>
<pagination <Pagination
v-if="result && result.count > paginateBy" v-if="result && result.count > paginateBy"
v-model:current="page" v-model:current="page"
:compact="true"
:paginate-by="paginateBy" :paginate-by="paginateBy"
:total="result.count"
/> />
<span v-if="result && result.results.length > 0"> <span v-if="result && result.results.length > 0">
{{ t('components.manage.users.UsersTable.pagination.results', {start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}, result.results.length) }} {{ t('components.manage.users.UsersTable.pagination.results', {start: ((page-1) * paginateBy) + 1, end: ((page-1) * paginateBy) + result.results.length, total: result.count}, result.results.length) }}
</span> </span>
</div> </div>
</div> </Layout>
</template> </template>

View File

@ -2,6 +2,10 @@
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { computed } from 'vue' import { computed } from 'vue'
import Layout from '~/components/ui/Layout.vue'
import Tabs from '~/components/ui/Tabs.vue'
import Tab from '~/components/ui/Tab.vue'
const { t } = useI18n() const { t } = useI18n()
const labels = computed(() => ({ const labels = computed(() => ({
@ -11,28 +15,22 @@ const labels = computed(() => ({
</script> </script>
<template> <template>
<div <Layout
v-title="labels.manageUsers" v-title="labels.manageUsers"
class="main" main
stack
> >
<nav <Tabs>
class="ui secondary pointing menu" <Tab
role="navigation" :title="t('views.admin.users.Base.link.users')"
:aria-label="labels.secondaryMenu"
>
<router-link
class="ui item"
:to="{name: 'manage.users.users.list'}" :to="{name: 'manage.users.users.list'}"
> />
{{ t('views.admin.users.Base.link.users') }}
</router-link> <Tab
<router-link :title="t('views.admin.users.Base.link.invitations')"
class="ui item"
:to="{name: 'manage.users.invitations.list'}" :to="{name: 'manage.users.invitations.list'}"
> />
{{ t('views.admin.users.Base.link.invitations') }} </Tabs>
</router-link>
</nav>
<router-view :key="$route.fullPath" /> <router-view :key="$route.fullPath" />
</div> </Layout>
</template> </template>