Enhance SubuserGrantSelector with compact dropdown mode and integrate into user profile

- Added dropdown-based compact layout for `SubuserGrantSelector` to improve subuser grant selection UX.
- Integrated `SubuserGrantSelector` into `UserProfile.vue` for subuser-specific grant management.
- Updated styles to support compact mode, including dynamic icon and text transformation based on environment.
This commit is contained in:
Jeppe Bundgaard
2026-02-12 18:01:32 +01:00
parent 22f9a486c6
commit caff98690c
2 changed files with 77 additions and 10 deletions
@@ -1,6 +1,7 @@
<script setup>
import { computed } from 'vue'
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import { IS_DEV } from '@/config.js';
const props = defineProps({
showLabel: {
@@ -25,21 +26,56 @@ const selectedGrant = computed(() => {
if (!selectedCustomerNumber.value) return null;
return grants.value.find(g => g.billing_customer_number === selectedCustomerNumber.value);
});
const selectedGrantName = computed(() => {
if (!selectedGrant.value) return 'Vælg kunde';
return selectedGrant.value.name || 'Kunde #' + selectedGrant.value.billing_customer_number;
});
</script>
<template>
<div class="grant-selector" :class="{ 'is-compact': compact }" v-if="isSubuser && grants.length > 0">
<label v-if="showLabel" class="label">Vælg kunde</label>
<div class="field has-addons" :class="{ 'is-fullwidth': !compact }">
<p class="control" v-if="!compact">
<!-- Compact mode: Button-style like user element in header -->
<div v-if="compact" class="dropdown is-hoverable">
<div class="dropdown-trigger">
<button class="button is-white" aria-haspopup="true" aria-controls="grant-dropdown-menu">
<span class="icon">
<i class="fas fa-building" :class="{'transform-color-black': !IS_DEV, 'transform-color-red': IS_DEV}"></i>
</span>
<span :class="{'has-text-black': !IS_DEV, 'has-text-red': IS_DEV}">{{ selectedGrantName }}</span>
<span class="icon is-small">
<i class="fas fa-angle-down" :class="{'transform-color-black': !IS_DEV, 'transform-color-red': IS_DEV}" aria-hidden="true"></i>
</span>
</button>
</div>
<div class="dropdown-menu" id="grant-dropdown-menu" role="menu">
<div class="dropdown-content">
<a
v-for="grant in grants"
:key="grant.billing_customer_number"
class="dropdown-item"
:class="{ 'is-active': grant.billing_customer_number === selectedCustomerNumber }"
@click="selectGrant(grant.billing_customer_number)"
>
{{ grant.name || 'Kunde #' + grant.billing_customer_number }}
</a>
</div>
</div>
</div>
<!-- Full mode: Select with icon prefix -->
<div v-else class="field has-addons is-fullwidth">
<p class="control">
<span class="button is-static">
<span class="icon">
<i class="fas fa-building"></i>
</span>
</span>
</p>
<p class="control" :class="{ 'is-expanded': !compact }">
<span class="select" :class="{ 'is-fullwidth': !compact, 'is-small': compact }">
<p class="control is-expanded">
<span class="select is-fullwidth">
<select
:value="selectedCustomerNumber"
@change="selectGrant($event.target.value ? parseInt($event.target.value) : null)"
@@ -68,14 +104,22 @@ const selectedGrant = computed(() => {
margin-bottom: 0;
}
.grant-selector.is-compact .field.has-addons {
margin-bottom: 0;
.grant-selector.is-compact .dropdown-trigger .button {
border: none;
box-shadow: none;
}
.grant-selector.is-compact .select select {
border-radius: 4px;
font-size: 0.875rem;
padding-right: 2rem;
.grant-selector.is-compact .dropdown-menu {
min-width: 200px;
}
.grant-selector.is-compact .dropdown-item {
cursor: pointer;
}
.grant-selector.is-compact .dropdown-item.is-active {
background-color: #3273dc;
color: white;
}
.label {
@@ -93,6 +137,14 @@ const selectedGrant = computed(() => {
border-color: #dbdbdb;
}
.transform-color-black {
filter: invert(100%) grayscale(100%) brightness(0) contrast(100%);
}
.transform-color-red {
filter: invert(27%) sepia(96%) saturate(7493%) hue-rotate(357deg) brightness(103%) contrast(101%);
}
.help {
font-size: 0.85rem;
color: #666;
@@ -10,6 +10,7 @@ import Swal from "sweetalert2";
import MicrosoftAuthenticationBox
from "@/views/dashboards/userDashboard/profile/displays/MicrosoftAuthentication/MicrosoftAuthenticationBox.vue";
import ShowErrorField from "@/components/global/ShowErrorField.vue";
import SubuserGrantSelector from "@/components/session/subuser/SubuserGrantSelector.vue";
import { parseError, removeError, addError } from "@/components/request/HandleGlobalError.vue";
import ConfigurationSwitch from "@/components/displays/superuser/configuration/ConfigurationSwitch.vue";
@@ -504,6 +505,20 @@ const onClickSaveWashCertificateEmail = async () => {
</button>
</div>
</ConfigurationCategory>
<!-- Subuser - Grant selection -->
<ConfigurationCategory
v-if="SessionUser.isSubuser.value"
class="mt-2"
:module="SessionUser.functions.device.isMobile() ? `` : `Brugerprofil`"
title="Underbruger"
description="Du er logget ind som underbruger. Vælg hvilken kunde du vil handle på vegne af."
icon="fas fa-users"
>
<SubuserGrantSelector :show-label="false" />
<p class="help mt-2">
Som underbruger kan du skifte mellem forskellige kunder, som du har adgang til.
</p>
</ConfigurationCategory>
<!-- Notifications - email notifications -->
<ConfigurationCategory
class="mt-2"