Files
pleno-vue/src/components/displays/superuser/tables/SubusersTable.vue
T

310 lines
9.4 KiB
Vue

<script setup>
import Swal from "sweetalert2";
import { usePaginatedListInstance } from "@/components/pagination/paginatedList.vue";
import { SessionUser } from "@/components/session/token/SessionUser.vue";
import ActionSettingsWheelButton from "@/components/displays/buttons/ActionSettingsWheelButton.vue";
import ActionSettingsWheelItem from "@/components/displays/buttons/ActionSettingsWheelItem.vue";
const props = defineProps({
objects: {
type: Array,
default: () => [],
},
showCustomer: {
type: Boolean,
default: false,
},
});
const { loadList } = usePaginatedListInstance();
const canEditPermissions = () =>
props.showCustomer ? SessionUser.canAccessSuperUser() : SessionUser.canAccessUser() || SessionUser.hasPermission("SUBUSERS_EDIT");
const canDisableAccess = () =>
props.showCustomer ? SessionUser.canAccessSuperUser() : SessionUser.canAccessUser() || SessionUser.hasPermission("SUBUSERS_DELETE");
const canResendInvite = (subuser) =>
(props.showCustomer ? SessionUser.canAccessSuperUser() : SessionUser.canAccessUser() || SessionUser.hasPermission("SUBUSERS_EDIT"))
&& Boolean(subuser?.can_resend_invite ?? subuser?.setup_required);
const hasRowActions = (subuser) =>
(canEditPermissions() && subuser?.grant_id) || canResendInvite(subuser) || (canDisableAccess() && subuser?.grant_id);
const formatDateTime = (dateString) => {
if (!dateString) {
return "-";
}
return new Date(dateString).toLocaleString("da-DK");
};
const formatPhone = (subuser) => {
if (!subuser?.phone_country_code || !subuser?.phone) {
return subuser?.setup_required ? "Oplyses ved accept" : "-";
}
return `+${subuser.phone_country_code} ${subuser.phone}`;
};
const formatUsername = (subuser) => {
if (subuser?.username) {
return `@${subuser.username}`;
}
return subuser?.setup_required ? "Brugernavn oplyses ved accept" : "-";
};
const formatEmail = (subuser) => {
if (subuser?.email) {
return subuser.email;
}
return subuser?.setup_required ? "E-mail oplyses ved accept" : "-";
};
const formatCustomer = (subuser) => {
if (!props.showCustomer) {
return "";
}
const number = subuser?.customer_number ?? "-";
const name = subuser?.customer_name || "Ukendt kunde";
return `${number} - ${name}`;
};
const permissionSummary = (subuser) =>
SessionUser.objects.subusers.functions.permissionSummary(subuser?.grant_permissions || []);
const statusLabel = (subuser) => {
switch (subuser?.access_state) {
case "active":
return "Aktiv";
case "pending_setup":
return "Afventer opsætning";
case "disabled":
return "Deaktiveret";
default:
return "Ingen adgang";
}
};
const statusClass = (subuser) => {
switch (subuser?.access_state) {
case "active":
return "is-success";
case "pending_setup":
return "is-warning";
case "disabled":
return "is-light";
default:
return "is-light";
}
};
const refreshList = async () => {
await loadList();
};
const onEditPermissions = async (subuser) => {
if (!subuser?.grant_id) {
return;
}
await SessionUser.objects.subuser_grants.functions.showPermissionEditForm(
{
id: subuser.grant_id,
permissions: subuser.grant_permissions || [],
name: subuser.name || formatPhone(subuser) || `Chauffør ${subuser.id}`,
},
refreshList
);
};
const onEditNote = async (subuser) => {
if (!subuser?.grant_id) {
return;
}
const result = await Swal.fire({
title: "Redigér note",
input: "textarea",
inputLabel: "Intern note",
inputValue: subuser.grant_note || "",
inputPlaceholder: "Skriv en note til chaufførens adgang",
showCancelButton: true,
confirmButtonText: "Gem",
cancelButtonText: "Annuller",
preConfirm: async (value) => {
try {
await SessionUser.objects.subuser_grants.set.note(subuser.grant_id, value || "");
return value || "";
} catch (error) {
Swal.showValidationMessage("Kunne ikke gemme noten.");
return false;
}
},
});
if (result.isConfirmed) {
await refreshList();
}
};
const onToggleEnabled = async (subuser, enabled) => {
if (!subuser?.grant_id) {
return;
}
const actionLabel = enabled ? "genaktivere" : "deaktivere";
const result = await Swal.fire({
title: `${enabled ? "Genaktivér" : "Deaktivér"} adgang`,
text: `Vil du ${actionLabel} chaufførens adgang for den valgte kunde?`,
icon: "warning",
showCancelButton: true,
confirmButtonText: enabled ? "Genaktivér" : "Deaktivér",
cancelButtonText: "Annuller",
});
if (!result.isConfirmed) {
return;
}
try {
await SessionUser.objects.subuser_grants.set.enabled(subuser.grant_id, enabled);
await refreshList();
} catch (error) {
await Swal.fire({
title: "Kunne ikke opdatere adgang",
text: error?.response?.data?.data?.message || error?.response?.data?.message || "Der opstod en fejl.",
icon: "error",
confirmButtonText: "Luk",
});
}
};
const onResendInvite = async (subuser) => {
await SessionUser.objects.subusers.functions.resendInvite(subuser, refreshList, { superuser: props.showCustomer });
};
</script>
<template>
<div class="table-container">
<table class="table is-fullwidth is-striped is-hoverable">
<thead>
<tr>
<th>ID</th>
<th v-if="showCustomer">Kunde</th>
<th>Chauffør</th>
<th>Kontakt</th>
<th>Adgang</th>
<th>Tilladelser</th>
<th>Note</th>
<th>Oprettet</th>
<th>Opdateret</th>
<th class="has-text-right">Handlinger</th>
</tr>
</thead>
<tbody>
<tr v-if="props.objects.length === 0">
<td :colspan="showCustomer ? 10 : 9" class="has-text-centered has-text-grey py-6">Ingen chauffører fundet.</td>
</tr>
<tr v-for="subuser in props.objects" :key="`${subuser.id}-${subuser.grant_id || 'none'}`">
<td>{{ subuser.id }}</td>
<td v-if="showCustomer">
<div class="has-text-weight-semibold">{{ formatCustomer(subuser) }}</div>
<div class="is-size-7 has-text-grey">Grant #{{ subuser.grant_id }}</div>
</td>
<td>
<div class="has-text-weight-semibold">{{ subuser.name || "-" }}</div>
<div class="is-size-7 has-text-grey" :data-testid="`subuser-username-${subuser.id}`">
{{ formatUsername(subuser) }}
</div>
</td>
<td>
<div :data-testid="`subuser-email-${subuser.id}`">{{ formatEmail(subuser) }}</div>
<div class="is-size-7 has-text-grey" :data-testid="`subuser-phone-${subuser.id}`">
{{ formatPhone(subuser) }}
</div>
</td>
<td>
<span class="tag" :class="statusClass(subuser)" :data-testid="`subuser-status-${subuser.id}`">
{{ statusLabel(subuser) }}
</span>
<div class="is-size-7 has-text-grey mt-2">
<span v-if="subuser.grant_enabled">Grant aktiv</span>
<span v-else>Grant inaktiv</span>
</div>
</td>
<td>
<div class="permission-summary">{{ permissionSummary(subuser) }}</div>
</td>
<td>
<div>{{ subuser.grant_note || "-" }}</div>
</td>
<td>{{ formatDateTime(subuser.created_at) }}</td>
<td>{{ formatDateTime(subuser.updated_at) }}</td>
<td>
<div class="buttons is-justify-content-flex-end action-buttons">
<ActionSettingsWheelButton v-if="hasRowActions(subuser)">
<template #actions>
<ActionSettingsWheelItem
v-if="canEditPermissions() && subuser.grant_id"
icon="fas fa-pen"
label="Redigér note"
:click-action="() => onEditNote(subuser)"
:test-id="`subuser-note-${subuser.id}`"
/>
<ActionSettingsWheelItem
v-if="canEditPermissions() && subuser.grant_id"
icon="fas fa-user-shield"
label="Tilladelser"
:click-action="() => onEditPermissions(subuser)"
:test-id="`subuser-permissions-${subuser.id}`"
/>
<ActionSettingsWheelItem
v-if="canResendInvite(subuser)"
icon="fas fa-paper-plane"
label="Gensend"
:click-action="() => onResendInvite(subuser)"
:test-id="`subuser-resend-${subuser.id}`"
/>
<ActionSettingsWheelItem
v-if="canDisableAccess() && subuser.grant_id"
:icon="subuser.grant_enabled ? 'fas fa-ban' : 'fas fa-check'"
:label="subuser.grant_enabled ? 'Deaktivér' : 'Aktivér'"
:template="subuser.grant_enabled ? 'danger' : 'success'"
:click-action="() => onToggleEnabled(subuser, !subuser.grant_enabled)"
:test-id="`subuser-toggle-${subuser.id}`"
/>
</template>
</ActionSettingsWheelButton>
</div>
</td>
</tr>
</tbody>
</table>
</div>
</template>
<style scoped>
.permission-summary {
max-width: 260px;
word-break: break-word;
}
.action-buttons {
gap: 0.5rem;
flex-wrap: wrap;
}
</style>