Add subuser and grant management views with pagination components and routing integration

This commit is contained in:
Jeppe Bundgaard
2026-02-11 17:38:36 +01:00
parent 40a6a4f664
commit c9fee21d24
9 changed files with 333 additions and 0 deletions
+16
View File
@@ -0,0 +1,16 @@
<script setup>
import SubuserGrantsPagination from "@/components/displays/pagination/models/SuperUserDashboard/SubuserGrantsPagination.vue";
import PageTitle from "@/components/global/PageTitle.vue";
</script>
<template>
<div>
<PageTitle title="Underbruger Tilladelser" subtitle="Her kan du se en liste over alle underbruger tilladelser i systemet">
</PageTitle>
<SubuserGrantsPagination auto-load="true" />
</div>
</template>
<style scoped>
</style>
+16
View File
@@ -0,0 +1,16 @@
<script setup>
import SubusersPagination from "@/components/displays/pagination/models/SuperUserDashboard/SubusersPagination.vue";
import PageTitle from "@/components/global/PageTitle.vue";
</script>
<template>
<div>
<PageTitle title="Underbrugere" subtitle="Her kan du se en liste over alle underbrugere i systemet">
</PageTitle>
<SubusersPagination auto-load="true" />
</div>
</template>
<style scoped>
</style>
@@ -0,0 +1,75 @@
<script setup>
import SubuserGrantsTable from "@/components/displays/superuser/tables/SubuserGrantsTable.vue";
let props = defineProps(["hideSearch", "autoLoad"]);
import { defineProps } from "vue";
import { useRouter } from "vue-router";
import {
usePaginatedList,
PaginatedListKey
} from "@/components/pagination/paginatedList.vue";
import { provide } from "vue";
const paginatedList = usePaginatedList();
provide(PaginatedListKey, paginatedList);
const {
isLoaded,
isLoading,
list,
loadList,
metaCurrentPage,
metaItemsPerPage,
metaTotalItems,
setEndpoint,
setMetaItemsPerPage,
setPage,
metaSearch,
search,
setFilter,
setOrder,
hideSearchField,
setHideSearchField,
} = paginatedList;
import PaginationNavigation from "@/components/displays/pagination/PaginationNavigation.vue";
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
import PaginationDisplay from "@/components/displays/pagination/PaginationDisplay.vue";
const router = useRouter();
setEndpoint("/subusers/grants", false);
// Hide the search field, if the hideSearch prop is set
if (props.hideSearch) {
setHideSearchField(true);
}
if (props.autoLoad) {
loadList();
}
</script>
<template>
<input @input="search($event.target.value)" class="input" type="text" placeholder="Søg efter tilladelse" v-if="!hideSearchField"/>
<PaginationDisplay :metaItemsPerPage="metaItemsPerPage" :loadFunction="loadList" :isLoading="isLoading" :setMetaItemsPerPage="setMetaItemsPerPage">
<template #paginationColumns>
<!-- Sort by created_at -->
<div class="column is-narrow my-3">
<label class="label is-small">Rækkefølge</label>
<div class="control">
<div class="select">
<select @change="setOrder('created_at', $event.target.value); loadList();">
<option value="asc">Ascending (Oldest first)</option>
<option value="desc" selected>Descending (Newest first)</option>
</select>
</div>
</div>
</div>
</template>
</PaginationDisplay>
<SubuserGrantsTable :objects="list" />
<PaginationNavigation :currentPage="metaCurrentPage" :totalPages="Math.ceil(metaTotalItems / metaItemsPerPage)" :loadFunction="loadList" :setPage="setPage" :isLoading="isLoading" />
<LoadButtonWhileAwait class="is-dark" :isLoading="isLoading" :loadFunction="loadList" icon="fas fa-sync-alt">Reload</LoadButtonWhileAwait>
</template>
<style scoped>
</style>
@@ -0,0 +1,75 @@
<script setup>
import SubusersTable from "@/components/displays/superuser/tables/SubusersTable.vue";
let props = defineProps(["hideSearch", "autoLoad"]);
import { defineProps } from "vue";
import { useRouter } from "vue-router";
import {
usePaginatedList,
PaginatedListKey
} from "@/components/pagination/paginatedList.vue";
import { provide } from "vue";
const paginatedList = usePaginatedList();
provide(PaginatedListKey, paginatedList);
const {
isLoaded,
isLoading,
list,
loadList,
metaCurrentPage,
metaItemsPerPage,
metaTotalItems,
setEndpoint,
setMetaItemsPerPage,
setPage,
metaSearch,
search,
setFilter,
setOrder,
hideSearchField,
setHideSearchField,
} = paginatedList;
import PaginationNavigation from "@/components/displays/pagination/PaginationNavigation.vue";
import LoadButtonWhileAwait from "@/components/request/LoadButtonWhileAwait.vue";
import PaginationDisplay from "@/components/displays/pagination/PaginationDisplay.vue";
const router = useRouter();
setEndpoint("/subusers", false);
// Hide the search field, if the hideSearch prop is set
if (props.hideSearch) {
setHideSearchField(true);
}
if (props.autoLoad) {
loadList();
}
</script>
<template>
<input @input="search($event.target.value)" class="input" type="text" placeholder="Søg efter underbruger" v-if="!hideSearchField"/>
<PaginationDisplay :metaItemsPerPage="metaItemsPerPage" :loadFunction="loadList" :isLoading="isLoading" :setMetaItemsPerPage="setMetaItemsPerPage">
<template #paginationColumns>
<!-- Sort by created_at -->
<div class="column is-narrow my-3">
<label class="label is-small">Rækkefølge</label>
<div class="control">
<div class="select">
<select @change="setOrder('created_at', $event.target.value); loadList();">
<option value="asc">Ascending (Oldest first)</option>
<option value="desc" selected>Descending (Newest first)</option>
</select>
</div>
</div>
</div>
</template>
</PaginationDisplay>
<SubusersTable :objects="list" />
<PaginationNavigation :currentPage="metaCurrentPage" :totalPages="Math.ceil(metaTotalItems / metaItemsPerPage)" :loadFunction="loadList" :setPage="setPage" :isLoading="isLoading" />
<LoadButtonWhileAwait class="is-dark" :isLoading="isLoading" :loadFunction="loadList" icon="fas fa-sync-alt">Reload</LoadButtonWhileAwait>
</template>
<style scoped>
</style>
@@ -0,0 +1,61 @@
<script setup>
import ActionSettingsWheelButton from "@/components/displays/buttons/ActionSettingsWheelButton.vue";
import ActionSettingsWheelItem from "@/components/displays/buttons/ActionSettingsWheelItem.vue";
defineProps(['objects']);
</script>
<template>
<table class="table is-fullwidth">
<thead>
<tr>
<th>ID</th>
<th>Underbruger ID</th>
<th>Kundenummer</th>
<th>Aktiveret</th>
<th>Note</th>
<th>Tilladelser</th>
<th class="has-text-right">Handlinger</th>
</tr>
</thead>
<tbody>
<tr v-for="grant in objects" :key="grant.id">
<td>{{ grant.id }}</td>
<td>{{ grant.subuser }}</td>
<td>{{ grant.billing_customer_number }}</td>
<td>
<span class="tag" :class="grant.enabled ? 'is-success' : 'is-danger'">
{{ grant.enabled ? 'Ja' : 'Nej' }}
</span>
</td>
<td>{{ grant.note || '-' }}</td>
<td>
<div class="tags">
<span class="tag is-info is-light" v-for="permission in (grant.permissions || []).slice(0, 3)" :key="permission">
{{ permission }}
</span>
<span class="tag is-light" v-if="grant.permissions && grant.permissions.length > 3">
+{{ grant.permissions.length - 3 }} mere
</span>
</div>
</td>
<td>
<div class="buttons is-float-right">
<ActionSettingsWheelButton>
<template #actions>
<ActionSettingsWheelItem
icon="fas fa-eye"
label="Se detaljer"
/>
</template>
</ActionSettingsWheelButton>
</div>
</td>
</tr>
</tbody>
</table>
</template>
<style scoped>
</style>
@@ -0,0 +1,42 @@
<script setup>
import ActionSettingsWheelButton from "@/components/displays/buttons/ActionSettingsWheelButton.vue";
import ActionSettingsWheelItem from "@/components/displays/buttons/ActionSettingsWheelItem.vue";
defineProps(['objects']);
</script>
<template>
<table class="table is-fullwidth">
<thead>
<tr>
<th>ID</th>
<th>Kundenummer</th>
<th>Visningsnavn</th>
<th class="has-text-right">Handlinger</th>
</tr>
</thead>
<tbody>
<tr v-for="subuser in objects" :key="subuser.id">
<td>{{ subuser.id }}</td>
<td>{{ subuser.customer_number }}</td>
<td>{{ subuser.display_name }}</td>
<td>
<div class="buttons is-float-right">
<ActionSettingsWheelButton>
<template #actions>
<ActionSettingsWheelItem
icon="fas fa-eye"
label="Se detaljer"
/>
</template>
</ActionSettingsWheelButton>
</div>
</td>
</tr>
</tbody>
</table>
</template>
<style scoped>
</style>
+14
View File
@@ -158,6 +158,8 @@ import GuestQrNewDriver from "@/views/guest/qr/GuestQRNewDriver.vue";
import GuestQRNewDriver from "@/views/guest/qr/GuestQRNewDriver.vue";
import GuestQRNewBooking from "@/views/guest/qr/GuestQRNewBooking.vue";
import CustomerCreationPage from "@/views/pages/auth/CustomerCreationPage.vue";
import Subusers from "@/views/dashboards/superUserDashboard/Subusers.vue";
import SubuserGrants from "@/views/dashboards/superUserDashboard/SubuserGrants.vue";
/**
* Meta data for routes
@@ -597,6 +599,18 @@ export const router = createRouter({
component: Users,
meta: { middleware: superUserMiddleware }
},
{
name: 'subusers',
path: '/user/subusers',
component: Subusers,
meta: { middleware: authMiddleware }
},
{
name: 'subusergrants',
path: '/user/subusers/grants',
component: SubuserGrants,
meta: { middleware: authMiddleware }
},
{
name: 'user',
path: '/superuser/users/:userId',
@@ -0,0 +1,17 @@
<script setup>
import SuperUserDashboardNavigation from "@/views/dashboards/superUserDashboard/SuperUserDashboardNavigation.vue";
import RestrictedPageWrapper from "@/components/page/wrappers/RestrictedPageWrapper.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import SubuserGrantsDisplay from "@/components/displays/SubuserGrants.vue";
</script>
<template>
<RestrictedPageWrapper :hasPermission="SessionUser.canAccessSuperUser()">
<SuperUserDashboardNavigation />
<SubuserGrantsDisplay />
</RestrictedPageWrapper>
</template>
<style scoped>
</style>
@@ -0,0 +1,17 @@
<script setup>
import SuperUserDashboardNavigation from "@/views/dashboards/superUserDashboard/SuperUserDashboardNavigation.vue";
import RestrictedPageWrapper from "@/components/page/wrappers/RestrictedPageWrapper.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import SubusersDisplay from "@/components/displays/Subusers.vue";
</script>
<template>
<RestrictedPageWrapper :hasPermission="SessionUser.canAccessSuperUser()">
<SuperUserDashboardNavigation />
<SubusersDisplay />
</RestrictedPageWrapper>
</template>
<style scoped>
</style>