Vue js: set notification count to zero - vue.js

in my Vue Js code below, i created count for notification socket, so whenever a new notification received it count+ and appears beside the icon ... now i wanted to set back the count to zero when a user click on notification icon but i couldn't figure out how to do it.
below in app.vue i set a prop called number and i pass it to the sidebar to appear on all project pages , this number is the count of the notifications .. is there a way to set it back to zero after user click on the icon in the side bar?
<template>
<router-view :number="count" #send="getNewCount">
<sidebar :number="count" />
</router-view>
</template>
<script>
import sidebar from "#/views/Layout/DashboardLayout.vue";
import axios from "axios";
import {
io
} from "socket.io-client";
let socket = io("h***********/");
export default {
components: {
sidebar,
},
data() {
return {
user2: JSON.parse(sessionStorage.getItem("user")),
count: 0,
today: null,
};
},
props: {
number: {
type: Number,
default: 0,
},},
async created() {
console.log(this.count);
const response = await axios.get(
`https://l*********rs/api/${this.user2._id}/`, {
headers: {
Authorization: "Bearer " + sessionStorage.getItem("user"),
},
}
);
// await this.$store.dispatch("user", response.data.response);
socket.emit("user_connected", this.user2.username);
// console.log(this.user2,"userr")
socket.on("verify_connection", (data) => {
this.verify_connection = data;
console.log(data, "s")
});
socket.emit("user_connected", this.user2.username);
socket.on("verify_connection", (data) => {
console.log("heyy", data);
this.verify_connection = data;
});
socket.on("updated_flat", (data) => {
console.log("heyy", data);
this.makeToast(" success ", "'b-toaster-top-center");
});
socket.on("test", (data) => {
console.log("heyy", data);
// this.makeToast("success", "b-toaster-top-right");
});
;
},
methods: {
// playSound() {
// var audio = document.getElementById("audio");
// audio.play();
// },
getNewCount(data) {
this.count = data;
},
makeToast(variant = null, toaster, append = false) {
this.$bvToast.toast(" edited ", {
title: "BootstrapVue Toast",
variant: variant,
autoHideDelay: 10000,
toaster: toaster,
position: "top-right",
appendToast: append,
});
// this.playSound();
this.count = this.count + 1;
console.log(this.count,"count");
},
}
}
</script>
sidebar:
<template>
<div class="wrapper">
<side-bar >
<template slot="links">
<sidebar-item v-if="roles ==='Admin'"
:link="{
name: ' notifications',
path: '/notifications',
icon: 'ni ni-bell-55 text-green'
}">
</sidebar-item>
<p class="notifCount" v-if="roles ==='Admin'"> {{ number }} </p>
</template>
</side-bar>
<div class="main-content">
<dashboard-navbar :type="$route.meta.navbarType"></dashboard-navbar>
<div #click="$sidebar.displaySidebar(false)">
<fade-transition :duration="200" origin="center top" mode="out-in">
<!-- your content here -->
<router-view></router-view>
</fade-transition>
</div>
<content-footer v-if="!$route.meta.hideFooter"></content-footer>
</div>
</div>
</template>
<script>
/* eslint-disable no-new */
import PerfectScrollbar from 'perfect-scrollbar';
import 'perfect-scrollbar/css/perfect-scrollbar.css';
function hasElement(className) {
return document.getElementsByClassName(className).length > 0;
}
function initScrollbar(className) {
if (hasElement(className)) {
new PerfectScrollbar(`.${className}`);
} else {
// try to init it later in case this component is loaded async
setTimeout(() => {
initScrollbar(className);
}, 100);
}
}
import DashboardNavbar from './DashboardNavbar.vue';
import ContentFooter from './ContentFooter.vue';
import DashboardContent from './Content.vue';
import { FadeTransition } from 'vue2-transitions';
export default {
components: {
DashboardNavbar,
ContentFooter,
DashboardContent,
FadeTransition
},
props: {
number: {
type: Number,
default: 0,
},
},
methods: {
initScrollbar() {
let isWindows = navigator.platform.startsWith('Win');
if (isWindows) {
initScrollbar('sidenav');
}
},
},
computed: {
roles() {
let roles = JSON.parse(sessionStorage.getItem('user')).role;
return roles;
},},
mounted() {
this.initScrollbar()
}
};
</script>

Emit an event from sidebar and handle the event in the main app.
main app:
<template>
<!-- ... -->
<sidebar :number="count" #reset="onReset" />
<!-- ... -->
</template>
<script>
export default {
// ...
methods: {
onReset() {
// api calls, etc.
this.count = 0;
}
}
}
</script>
sidebar:
<template>
<!-- ... -->
<button #click="$emit('reset')">Reset notification count</button>
<!-- ... -->
</template>

Related

Vuejs & Auth0 : I need to reload page to be Authenticated

I'm a beginner in Vue, and I implemented Auth0 to my Web App using Vue3.
My issue: after logging in, my API call to retrieve data get an unauthorized error 403. If I reload the page, everything is working fine.
What should I do to avoid reloading the page to get authenticated directly?
Here are my scripts:
Main.js
import { createApp } from 'vue'
import App from './App.vue'
import router from './router'
import './index.css'
import dayjs from 'dayjs'
import Datepicker from 'vue3-date-time-picker'
import 'vue3-date-time-picker/dist/main.css'
import { setupAuth } from './auth/index.js'
import authConfig from './auth/config.js'
function callbackRedirect(appState) {
router.push(appState && appState.targetUrl ? appState.targetUrl : '/' );
}
setupAuth(authConfig, callbackRedirect).then((auth) => {
let app = createApp(App).use(router);
app.config.globalProperties.$dayjs = dayjs;
app.component('Datepicker', Datepicker);
app.use(auth).mount('#app');
})
My App.vue script:
<template>
<div v-if="isAuthenticated">
<NavBar />
<router-view/>
</div>
</template>
<script>
import NavBar from './components/NavBar.vue'
export default {
components: { NavBar },
data(){
return {
isAuthenticated: false,
}
},
async mounted(){
await this.getAccessToken()
},
methods: {
async getAccessToken(){
try {
const accessToken = await this.$auth.getTokenSilently()
localStorage.setItem('accessToken', accessToken)
this.isAuthenticated = true
} catch (error) {
console.log('Error occured while trying to retrieve Access Token...', error)
}
},
},
}
</script>
and my Home.vue loading the data:
<template>
<div class="home">
<div class="py-10">
<header>
<div class="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
<h1 class="text-3xl font-bold leading-tight text-gray-900">Monitoring Dashboard</h1>
</div>
</header>
<main>
<div class="max-w-7xl mx-auto sm:px-6 lg:px-8">
<h3 class="m-5 text-lg leading-6 font-medium text-gray-900">Main KPIs</h3>
<div class="md:grid md:grid-cols-3 md:gap-6">
<div v-for="(item, index) in stats" :key="index" class="md:col-span-1">
<div class="bg-white p-5 border-gray-50 rounded-lg shadow-lg mb-5">
<span class="text-sm font-medium text-gray-500 truncate">{{ item.name }}</span>
<p class="mt-1 text-3xl font-bold text-gray-900">{{ parseFloat(item.stat.toFixed(2)) }}</p>
</div>
</div>
</div>
</div>
</main>
</div>
</div>
</template>
<script>
import _ from 'lodash'
import ProductsService from '../services/products.service'
export default {
name: 'Home',
data(){
return{
user: '',
products: '',
stats: '',
}
},
async mounted(){
await this.readProducts()
await this.buildStats()
},
methods: {
async readProducts(){
let temp = null
try {
temp = await ProductsService.readProducts()
this.products = temp.data
} catch (error) {
console.log('Error: cannot retrieve all products...')
}
},
async buildStats(){
//Nb products
const nbProducts = this.products.length
//Nb offers & Uniq NbRetailers
let nbOffers = 0
let retailers = []
for(let product of this.products){
for(let offer of product.offers){
retailers.push(offer.retailer)
nbOffers += 1
}
}
const nbRetailers = _.uniq(retailers).length
this.stats = [
{ name: 'Number of Retailers', stat: nbRetailers },
{ name: 'Number of Products', stat: nbProducts },
{ name: 'Number of Offers', stat: nbOffers },
]
},
},
watch: {
products: function(){
this.buildStats()
}
}
}
</script>
My ./auth/index.js file:
import createAuth0Client from '#auth0/auth0-spa-js'
import { computed, reactive, watchEffect } from 'vue'
let client
const state = reactive({
loading: true,
isAuthenticated: false,
user: {},
popupOpen: false,
error: null,
})
async function loginWithPopup() {
state.popupOpen = true
try {
await client.loginWithPopup(0)
} catch (e) {
console.error(e)
} finally {
state.popupOpen = false
}
state.user = await client.getUser()
state.isAuthenticated = true
}
async function handleRedirectCallback() {
state.loading = true
try {
await client.handleRedirectCallback()
state.user = await client.getUser()
state.isAuthenticated = true
} catch (e) {
state.error = e
} finally {
state.loading = false
}
}
function loginWithRedirect(o) {
return client.loginWithRedirect(o)
}
function getIdTokenClaims(o) {
return client.getIdTokenClaims(o)
}
function getTokenSilently(o) {
return client.getTokenSilently(o)
}
function getTokenWithPopup(o) {
return client.getTokenWithPopup(o)
}
function logout(o) {
return client.logout(o)
}
export const authPlugin = {
isAuthenticated: computed(() => state.isAuthenticated),
loading: computed(() => state.loading),
user: computed(() => state.user),
getIdTokenClaims,
getTokenSilently,
getTokenWithPopup,
handleRedirectCallback,
loginWithRedirect,
loginWithPopup,
logout,
}
export const routeGuard = (to, from, next) => {
const { isAuthenticated, loading, loginWithRedirect } = authPlugin
const verify = () => {
// If the user is authenticated, continue with the route
if (isAuthenticated.value) {
return next()
}
// Otherwise, log in
loginWithRedirect({ appState: { targetUrl: to.fullPath } })
}
// If loading has already finished, check our auth state using `fn()`
if (!loading.value) {
return verify()
}
// Watch for the loading property to change before we check isAuthenticated
watchEffect(() => {
if (loading.value === false) {
return verify()
}
})
}
export const setupAuth = async (options, callbackRedirect) => {
client = await createAuth0Client({
...options,
})
try {
// If the user is returning to the app after authentication
if (
window.location.search.includes('code=') &&
window.location.search.includes('state=')
) {
// handle the redirect and retrieve tokens
const { appState } = await client.handleRedirectCallback()
// Notify subscribers that the redirect callback has happened, passing the appState
// (useful for retrieving any pre-authentication state)
callbackRedirect(appState)
}
} catch (e) {
state.error = e
} finally {
// Initialize our internal authentication state
state.isAuthenticated = await client.isAuthenticated()
state.user = await client.getUser()
state.loading = false
}
return {
install: (app) => {
app.config.globalProperties.$auth = authPlugin
},
}
}

Vue Laravel Pusher. How to leave a channel

OK this has been posted a few times and while each of the solutions seem similar, none of them work for me. So lets start with the code.
Main.vue
<template>
...
<room-tabs :selectedRoomId="currentRoom.id" :rooms="joinedRooms"
#onSelectRoom="currentRoom = $event" #onLeaveRoom="leaveRoom($event)"/>
...
</template>
methods: {
connect() {
if (this.currentRoom?.id) {
let vm = this;
this.getMessages();
Echo.join("chat." + this.currentRoom.id)
.here(users => {
this.sendUserActivityNotification(this.$page.props.user, true)
this.users = users
this.usersCount = users.length
})
.joining(user => {
this.users.push(user)
this.usersCount = this.usersCount+1
})
.leaving(user => {
this.sendUserActivityNotification(user, false)
this.users = this.users.filter(({id}) => (id !== user.id))
this.usersCount = this.usersCount-1
})
.listen('NewChatMessage', (e) => {
vm.getMessages();
});
}
},
leaveRoom({id}) {
Echo.leave("chat." + this.currentRoom.id)
this.$store.splice(RoomTypes.LEAVE_ROOM, id)
}
},
RoomTabs.vue
<template>
...
<XIcon class="float-right h-3.5 w-3.5 -mt-2 -mr-2 text-black" aria-hidden="true" #click="leaveRoom(selectedRoomId)"/>
...
</template>
<script>
import {XIcon} from '#heroicons/vue/solid'
import {types as RoomTypes} from "../../../Store/modules/rooms/rooms.types";
export default {
components: {
XIcon,
},
props: ['rooms', 'selectedRoomId'],
emits: ['onSelectRoom', 'onLeaveRoom', 'onClose'],
methods: {
leaveRoom(id) {
this.$store.splice(RoomTypes.LEAVE_ROOM, id)
this.$emit('onClose');
}
},
}
</script>
I cannot seem to disconnect from the room when I click the X on the room tab. What am I missing here?

Vue-i18n not translating inside component script tags

Building a language switcher, all works fine but when I use the $t() inside the data object it will not be dynamic when I switch between a language.
Component.vue
<template>
// loop menu here
<div v-for="item in menu">
{{ item.label }}
</div>
</template>
<script>
const mainMenu = [
{
label: $t('dashboard'),
},
{
label: $t('users'),
},
{
label: $t('settings'),
},
}
export default {
data () {
return {
menu = MainMenu
}
}
}
</script>
i18n.js
// https://vue-i18n.intlify.dev/
import { createI18n } from 'vue-i18n'
export function loadLocalMessages () {
const locales = require.context('../locales', true, /[A-Za-z0-9-_,\s]+\.json$/i)
const messages = {}
locales.keys().forEach(key => {
const matched = key.match(/([A-Za-z0-9-_]+)\./i)
if (matched && matched.length > 1) {
const locale = matched[1]
messages[locale] = locales(key)
}
})
return messages;
}
const i18n = createI18n({
locale: 'en',// .env not working
fallbackLocale: 'en',// .env not working
messages: loadLocalMessages(),
});
export default i18n
<template>
<div v-for="item in menu">
{{ item.label }}
</div>
</template>
<script>
export default {
computed: {
menu() {
return [{
label: this.$t('dashboard'),
}, {
label: this.$t('users'),
}, {
label: this.$t('settings'),
}]
}
}
}
</script>
data is only ever called once when creating the component, and it's not intended to be reactive.
To make a property reactive on $t(), it should be computed:
export default {
computed: {
hello() {
return this.$t('hello')
}
}
}
demo

Testing a Chart.js component with Vue test utils throws a window.matchMedia error

I'm trying to write Mocha/Chai tests for a Vue component which utilizes Chart.js. All tests pass, but I then get this error:
C:\repos\security-center-frontend-2\security-center-frontend\node_modules\mocha\lib\runner.js:726
err.uncaught = true;
^
TypeError: Cannot create property 'uncaught' on string
'window.matchMedia not found! Make sure you're using a polyfill.'
I think that this has to do with the way Vue test utils represent the window object, and that I should somehow stub it, but am not sure of the proper syntax.
My component:
<template>
<card>
<template slot="header">
<h4 v-if="$slots.title || title" class="card-title">
<slot name="title">
{{title}}
</slot>
</h4>
<p class="card-category">
<slot name="subTitle">
{{subTitle}}
</slot>
</p>
</template>
<div>
<div :id="chartId" class="ct-chart"></div>
<div class="footer">
<div class="chart-legend">
<slot name="legend"></slot>
</div>
<hr>
<div class="stats">
<slot name="footer"></slot>
</div>
<div class="pull-right">
</div>
</div>
</div>
</card>
</template>
<script>
import Card from './Card.vue';
export default {
name: 'chart-card',
components: {
Card
},
props: {
footerText: {
type: String,
default: ''
},
title: {
type: String,
default: ''
},
subTitle: {
type: String,
default: ''
},
chartType: {
type: String,
default: 'Line' // Line | Pie | Bar
},
chartOptions: {
type: Object,
default: () => {
return {};
}
},
chartData: {
type: Object,
default: () => {
return {
labels: [],
series: []
};
}
}
},
data () {
return {
chartId: 'no-id'
};
},
methods: {
/***
* Initializes the chart by merging the chart options sent via props and the default chart options
*/
initChart (Chartist) {
const chartIdQuery = `#${this.chartId}`;
Chartist[this.chartType](
chartIdQuery,
this.chartData,
this.chartOptions
);
},
/***
* Assigns a random id to the chart
*/
updateChartId () {
const currentTime = new Date().getTime().toString();
const randomInt = this.getRandomInt(0, currentTime);
this.chartId = `div_${randomInt}`;
},
getRandomInt (min, max) {
return Math.floor(Math.random() * (max - min + 1)) + min;
}
},
mounted () {
this.updateChartId();
import('chartist').then((Chartist) => {
let ChartistLib = Chartist.default || Chartist;
this.$nextTick(() => {
this.initChart(ChartistLib);
});
});
}
};
</script>
<style>
</style>
My tests:
import { expect } from 'chai';
import { mount } from '#vue/test-utils';
import ChartCard from '#/components/Cards/ChartCard.vue';
describe('ChartCard.vue', () => {
it('Has a title', () => {
const wrapper = mount(ChartCard);
wrapper.setProps({title: 'test title'});
expect(wrapper.contains('.card-title')).to.be.true;
});
it('Displays passed title', () => {
const wrapper = mount(ChartCard);
wrapper.setProps({title: 'test title'});
expect(wrapper.text()).to.include('test title');
});
});

Can't display data in a component

I have request that returns json data...and i'm trying to display that data in vue component,but it doesn't work. In console.log is everything ok..json is something like:
[{"id":1,"body":"Hello, this is my first notification","..bla bla
here is my code
<template>
<div class="notification-container">
<p>{{ notification }}</p>
</div>
</template>
<script>
export default {
data() {
return {
notification: '',
}
},
mounted() {
axios.get('/notifications').then((response) => {
this.notification = response.data[0].body;
console.log(this.notification);
});
}
}
</script>
Try something like this :
<template>
<div class="notification-container">
<p>{{ notification /* OR this.notification[0].body */ }}</p>
</div>
</template>
<script>
export default {
data() {
return {
notification: '',
}
},
methods:{
showMsg(){
axios.get('/notifications').then( response => {
console.log(response);
this.notification = response.data[0].body;
/* OR this.notification = response; */
console.log(this.notification);
}).catch(e => {
console.log(e)
});
}
},
created() { // or mounted()
this.showMsg();
}
}
</script>