Files
libredesk/frontend/src/stores/user.js
Abhinav Raut 67e1230485 feat: agent availability status
New columns in users table to store user availability status.

Websocket pings sets the last active at timestamp, once user stops sending pings (on disconnect) after 5 minutes the user availalbility status changes to offline.

Detects auto away by checking for mouse, keyboard events and sets user status to away.

User can also set their status to away manually from the sidebar.

Migrations for v0.3.0

Minor visual fixes.

Bump version in package.json
2025-02-26 04:34:30 +05:30

121 lines
3.3 KiB
JavaScript

import { ref, computed } from 'vue'
import { defineStore } from 'pinia'
import { handleHTTPError } from '@/utils/http'
import { useEmitter } from '@/composables/useEmitter'
import { EMITTER_EVENTS } from '@/constants/emitterEvents'
import { adminNavItems, reportsNavItems } from '@/constants/navigation'
import { filterNavItems } from '@/utils/nav-permissions'
import api from '@/api'
export const useUserStore = defineStore('user', () => {
const user = ref({
id: null,
first_name: '',
last_name: '',
avatar_url: '',
email: '',
teams: [],
permissions: [],
availability_status: 'offline'
})
const emitter = useEmitter()
const userID = computed(() => user.value.id)
const firstName = computed(() => user.value.first_name || '')
const lastName = computed(() => user.value.last_name || '')
const avatar = computed(() => user.value.avatar_url || '')
const permissions = computed(() => user.value.permissions || [])
const email = computed(() => user.value.email)
const teams = computed(() => user.value.teams || [])
const getFullName = computed(() => {
const first = user.value.first_name ?? ''
const last = user.value.last_name ?? ''
return `${first} ${last}`.trim()
})
const getInitials = computed(() => {
const firstInitial = user.value.first_name?.charAt(0)?.toUpperCase() || ''
const lastInitial = user.value.last_name?.charAt(0)?.toUpperCase() || ''
return `${firstInitial}${lastInitial}`
})
const can = (permission) => {
return user.value.permissions.includes(permission)
}
const hasAdminTabPermissions = computed(() => {
return filterNavItems(adminNavItems, can).length > 0
})
const hasReportTabPermissions = computed(() => {
return filterNavItems(reportsNavItems, can).length > 0
})
const getCurrentUser = async () => {
try {
const response = await api.getCurrentUser()
const userData = response?.data?.data
if (userData) {
user.value = userData
} else {
throw new Error('No user data found')
}
} catch (error) {
if (error.response?.status !== 401) {
emitter.emit(EMITTER_EVENTS.SHOW_TOAST, {
title: 'Could not fetch current user',
variant: 'destructive',
description: handleHTTPError(error).message
})
}
}
}
const setCurrentUser = (userData) => {
user.value = userData
}
const setAvatar = (avatarURL) => {
if (typeof avatarURL !== 'string') {
console.warn('Avatar URL must be a string')
return
}
user.value.avatar_url = avatarURL
}
const clearAvatar = () => {
user.value.avatar_url = ''
}
const updateUserAvailability = async (status, isManual = true) => {
try {
const apiStatus = status === 'away' && isManual ? 'away_manual' : status
await api.updateCurrentUserAvailability({ status: apiStatus })
user.value.availability_status = apiStatus
} catch (error) {
if (error?.response?.status === 401) window.location.href = '/'
}
}
return {
user,
userID,
firstName,
lastName,
avatar,
email,
teams,
permissions,
getFullName,
getInitials,
hasAdminTabPermissions,
hasReportTabPermissions,
setCurrentUser,
getCurrentUser,
clearAvatar,
setAvatar,
updateUserAvailability,
can
}
})