fixes #4797 Seccion Worker/Notification #51

Merged
alexandre merged 23 commits from 4797-worker-notification-selector into dev 2023-05-15 09:42:17 +00:00
6 changed files with 294 additions and 1 deletions

View File

@ -361,6 +361,7 @@ export default {
list: 'List',
basicData: 'Basic data',
summary: 'Summary',
notifications: 'Notifications',
},
list: {
name: 'Name',
@ -394,6 +395,12 @@ export default {
role: 'Role',
sipExtension: 'Extension',
},
notificationsManager: {
activeNotifications: 'Active notifications',
availableNotifications: 'Available notifications',
subscribed: 'Subscribed to the notification',
unsubscribed: 'Unsubscribed from the notification',
},
imageNotFound: 'Image not found',
},
wagon: {

View File

@ -361,6 +361,7 @@ export default {
list: 'Listado',
basicData: 'Datos básicos',
summary: 'Resumen',
notifications: 'Notificaciones',
},
list: {
name: 'Nombre',
@ -394,6 +395,12 @@ export default {
role: 'Rol',
sipExtension: 'Extensión',
},
notificationsManager: {
activeNotifications: 'Notificaciones activas',
availableNotifications: 'Notificaciones disponibles',
subscribed: 'Se ha suscrito a la notificación',
unsubscribed: 'Se ha dado de baja de la notificación',
},
imageNotFound: 'No se ha encontrado la imagen',
},
wagon: {

View File

@ -0,0 +1,142 @@
<script setup>
import axios from 'axios';
import { useQuasar } from 'quasar';
import { computed, onMounted, onUpdated, ref } from 'vue';
import { useI18n } from 'vue-i18n';
import { useRoute } from 'vue-router';
const $props = defineProps({
id: {
type: Number,
required: false,
default: null,
},
});
const entityId = computed(() => $props.id || route.params.id);
onMounted(() => fetch());
onUpdated(() => fetch());
const route = useRoute();
const { t } = useI18n();
const quasar = useQuasar();
const notifications = ref([]);
async function fetch() {
try {
await axios
.get(`NotificationSubscriptions/${entityId.value}/getList`)
.then(async (res) => {
if (res.data) {
notifications.value = res.data;
}
});
} catch (e) {
alexandre marked this conversation as resolved
Review

Aço es necesari? en la funcio de baix no ha fet falta?

Aço es necesari? en la funcio de baix no ha fet falta?
Review

Per algun motiu en vitest si les cridades que fa axios no estan manejades te llança un error Unhandled Rejection. En esta funció ho he posat en un try/catch perque no havia de fer res, en les altres funcions sí que està manejat gastant el .catch() de axios.

Per algun motiu en vitest si les cridades que fa axios no estan manejades te llança un error *Unhandled Rejection*. En esta funció ho he posat en un try/catch perque no havia de fer res, en les altres funcions sí que està manejat gastant el .catch() de axios.
//
}
}
async function disableNotification(notification) {
await axios
.delete(`NotificationSubscriptions/${notification.id}`)
.catch(() => (notification.active = true))
.then((res) => {
if (res.data) {
notification.id = null;
notification.active = false;
quasar.notify({
type: 'positive',
message: t('worker.notificationsManager.unsubscribed'),
});
}
});
}
async function toggleNotification(notification) {
if (!notification.active) {
await disableNotification(notification);
} else {
await axios
.post(`NotificationSubscriptions`, {
notificationFk: notification.notificationFk,
userFk: entityId.value,
})
.catch(() => (notification.active = false))
.then((res) => {
if (res.data) {
notification.id = res.data.id;
quasar.notify({
type: 'positive',
message: t('worker.notificationsManager.subscribed'),
});
}
});
}
}
</script>
<template>
<QPage>
<QCard class="q-pa-md">
<QList>
<div
v-show="
notifications.filter(
(notification) => notification.active == true
).length
"
>
<QItemLabel header class="text-h6">
{{ t('worker.notificationsManager.activeNotifications') }}
</QItemLabel>
<QItem>
<div
v-for="notification in notifications.filter(
(notification) => notification.active == true
)"
:key="notification.id"
>
<QChip
:key="notification.id"
:label="notification.name"
text-color="white"
color="primary"
class="q-mr-sm"
removable
@remove="disableNotification(notification)"
/>
</div>
</QItem>
</div>
<div v-show="notifications.length">
<QItemLabel header class="text-h6">
{{ t('worker.notificationsManager.availableNotifications') }}
</QItemLabel>
<div class="row">
<QItem
class="col-3"
:key="notification.notificationFk"
v-for="notification in notifications"
>
<QItemSection>
<QItemLabel>{{ notification.name }}</QItemLabel>
<QItemLabel caption>{{
notification.description
}}</QItemLabel>
</QItemSection>
<QItemSection side top>
<QToggle
checked-icon="check"
unchecked-icon="close"
indeterminate-icon="block"
v-model="notification.active"
@update:model-value="toggleNotification(notification)"
/>
</QItemSection>
</QItem>
</div>
</div>
</QList>
</QCard>
</QPage>
</template>

View File

@ -11,7 +11,7 @@ export default {
redirect: { name: 'WorkerMain' },
menus: {
main: ['WorkerList'],
card: [],
card: ['WorkerNotificationsManager'],
},
children: [
{
@ -42,9 +42,19 @@ export default {
path: 'summary',
meta: {
title: 'summary',
icon: 'launch',
},
component: () => import('src/pages/Worker/Card/WorkerSummary.vue'),
},
{
name: 'WorkerNotificationsManager',
path: 'notifications',
meta: {
title: 'notifications',
icon: 'notifications',
},
component: () => import('src/pages/Worker/Card/WorkerNotificationsManager.vue'),
},
],
},
],

View File

@ -0,0 +1,27 @@
describe('WorkerNotificationsManager', () => {
alexandre marked this conversation as resolved
Review

Feslos que no depenguen un de altres, per exemple el ultim depen dels anteriors

Feslos que no depenguen un de altres, per exemple el ultim depen dels anteriors
beforeEach(() => {
const workerId = 1110;
cy.viewport(1280, 720);
cy.login('salesBoss');
cy.visit(`/#/worker/${workerId}/notifications`);
});
it('should unsubscribe 2 notifications, check the unsubscription has been saved, subscribe to other one and should check the data has been saved', () => {
cy.get('.q-chip').should('have.length', 3);
cy.get('.q-toggle__thumb').eq(0).click();
cy.get('.q-notification__message').should('have.text', 'Unsubscribed from the notification');
cy.get('.q-chip > .q-icon').eq(0).click();
cy.reload();
cy.get('.q-chip').should('have.length', 1);
cy.get('.q-toggle__thumb').should('have.length', 3).eq(0).click();
cy.get('.q-notification__message').should('have.text', 'Subscribed to the notification');
cy.get('.q-toggle__thumb').should('have.length', 3).eq(1).click();
cy.get('.q-notification__message').should('have.text', 'Subscribed to the notification');
cy.reload();
cy.get('.q-chip').should('have.length', 3);
});
});

View File

@ -0,0 +1,100 @@
import { vi, describe, expect, it, beforeAll, afterEach } from 'vitest';
import { createWrapper, axios } from 'app/test/vitest/helper';
import WorkerNotificationsManager from 'src/pages/Worker/Card/WorkerNotificationsManager.vue';
describe('WorkerNotificationsManager', () => {
let vm;
const entityId = 1110;
beforeAll(() => {
vm = createWrapper(WorkerNotificationsManager, {
propsData: {
id: entityId,
},
}).vm;
});
afterEach(() => {
vi.clearAllMocks();
});
describe('fetch()', () => {
it('should fetch notification subscriptions and role mappings', async () => {
vi.spyOn(axios, 'get')
.mockResolvedValueOnce({
data: [
{
id: 1,
name: 'Name 1',
description: 'Description 1',
notificationFk: 1,
active: true
},
],
});
await vm.fetch();
expect(axios.get).toHaveBeenCalledWith(`NotificationSubscriptions/${entityId}/getList`);
expect(vm.notifications).toEqual([
{
id: 1,
notificationFk: 1,
name: 'Name 1',
description: 'Description 1',
active: true,
},
]);
});
});
describe('disableNotification()', () => {
it('should disable the notification', async () => {
vi.spyOn(axios, 'delete').mockResolvedValue({ data: { count: 1 } });
vi.spyOn(vm.quasar, 'notify');
const subscriptionId = 1;
vm.notifications = [{ id: 1, active: true }];
await vm.disableNotification(vm.notifications[0]);
expect(axios.delete).toHaveBeenCalledWith(
`NotificationSubscriptions/${subscriptionId}`
);
expect(vm.notifications[0].id).toBeNull();
expect(vm.notifications[0].id).toBeFalsy();
expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'positive' })
);
});
});
describe('toggleNotification()', () => {
it('should activate the notification', async () => {
vi.spyOn(axios, 'post').mockResolvedValue({
data: { id: 1, notificationFk: 1 },
});
vm.notifications = [{ id: null, active: true, notificationFk: 1 }];
await vm.toggleNotification(vm.notifications[0]);
expect(axios.post).toHaveBeenCalledWith('NotificationSubscriptions', {
notificationFk: 1,
userFk: entityId,
});
expect(vm.notifications[0].id).toBe(1);
expect(vm.notifications[0].active).toBeTruthy();
expect(vm.quasar.notify).toHaveBeenCalledWith(
expect.objectContaining({ type: 'positive' })
);
});
it('should disable the notification', async () => {
vi.spyOn(vm, 'disableNotification');
vm.notifications = [{ id: 1, active: false, notificationFk: 1 }];
await vm.toggleNotification(vm.notifications[0]);
expect(vm.notifications[0].id).toBe(null);
expect(vm.notifications[0].active).toBeFalsy();
});
});
});