Files
InvoiceShelf/resources/scripts/admin/stores/members.js
Darko Gjorgjijoski 6343b4a17f Add invitation frontend: invite modal, pending invitations, no-company view
Members Index:
- "Invite Member" button opens InviteMemberModal (email + role dropdown)
- Pending invitations section shows below members table with cancel buttons
- Members store gains inviteMember, fetchPendingInvitations, cancelInvitation

CompanySwitcher:
- Shows pending invitations greyed out below active companies
- Each with Accept/Decline mini-buttons
- Accepting refreshes bootstrap and switches to new company

NoCompanyView:
- Standalone page for users with zero accepted companies
- Shows pending invitations with Accept/Decline or "no companies" message
- Route: /admin/no-company

Invitation Pinia store:
- Manages user's own pending invitations (fetchPending, accept, decline)
- Bootstrap populates invitations from API response

Global store:
- Bootstrap action stores pending_invitations from response
2026-04-03 23:20:41 +02:00

290 lines
8.2 KiB
JavaScript
Vendored

import http from '@/scripts/http'
import { defineStore } from 'pinia'
import { useNotificationStore } from '@/scripts/stores/notification'
import { handleError } from '@/scripts/helpers/error-handling'
export const useMembersStore = (useWindow = false) => {
const defineStoreFunc = useWindow ? window.pinia.defineStore : defineStore
const { global } = window.i18n
return defineStoreFunc('members', {
state: () => ({
roles: [],
users: [],
totalUsers: 0,
pendingInvitations: [],
currentUser: null,
selectAllField: false,
selectedUsers: [],
customerList: [],
userList: [],
userData: {
name: '',
email: '',
password: null,
phone: null,
companies: [],
},
}),
actions: {
resetUserData() {
this.userData = {
name: '',
email: '',
password: null,
phone: null,
role: null,
companies: [],
}
},
fetchUsers(params) {
return new Promise((resolve, reject) => {
http
.get(`/api/v1/members`, { params })
.then((response) => {
this.users = response.data.data
this.totalUsers = response.data.meta.total
resolve(response)
})
.catch((err) => {
handleError(err)
reject(err)
})
})
},
fetchUser(id) {
return new Promise((resolve, reject) => {
http
.get(`/api/v1/members/${id}`)
.then((response) => {
this.userData = response.data.data
if (this.userData?.companies?.length) {
this.userData.companies.forEach((c, i) => {
this.userData.roles.forEach((r) => {
if (r.scope === c.id)
this.userData.companies[i].role = r.name
})
})
}
resolve(response)
})
.catch((err) => {
console.log(err)
handleError(err)
reject(err)
})
})
},
fetchRoles(state) {
return new Promise((resolve, reject) => {
http
.get(`/api/v1/roles`)
.then((response) => {
this.roles = response.data.data
resolve(response)
})
.catch((err) => {
handleError(err)
reject(err)
})
})
},
addUser(data) {
return new Promise((resolve, reject) => {
http
.post('/api/v1/members', data)
.then((response) => {
this.users.push(response.data)
const notificationStore = useNotificationStore()
notificationStore.showNotification({
type: 'success',
message: global.t('members.created_message'),
})
resolve(response)
})
.catch((err) => {
handleError(err)
reject(err)
})
})
},
updateUser(data) {
return new Promise((resolve, reject) => {
http
.put(`/api/v1/members/${data.id}`, data)
.then((response) => {
if (response) {
let pos = this.users.findIndex(
(user) => user.id === response.data.data.id
)
this.users[pos] = response.data.data
}
const notificationStore = useNotificationStore()
notificationStore.showNotification({
type: 'success',
message: global.t('members.updated_message'),
})
resolve(response)
})
.catch((err) => {
handleError(err)
reject(err)
})
})
},
deleteUser(id) {
const notificationStore = useNotificationStore()
return new Promise((resolve, reject) => {
http
.post(`/api/v1/members/delete`, { users: id.ids })
.then((response) => {
let index = this.users.findIndex((user) => user.id === id)
this.users.splice(index, 1)
notificationStore.showNotification({
type: 'success',
message: global.t('members.deleted_message', 1),
})
resolve(response)
})
.catch((err) => {
handleError(err)
reject(err)
})
})
},
deleteMultipleUsers() {
return new Promise((resolve, reject) => {
http
.post(`/api/v1/members/delete`, { users: this.selectedUsers })
.then((response) => {
this.selectedUsers.forEach((user) => {
let index = this.users.findIndex(
(_user) => _user.id === user.id
)
this.users.splice(index, 1)
})
const notificationStore = useNotificationStore()
notificationStore.showNotification({
type: 'success',
message: global.t('members.deleted_message', 2),
})
resolve(response)
})
.catch((err) => {
handleError(err)
reject(err)
})
})
},
searchUsers(params) {
return new Promise((resolve, reject) => {
http
.get(`/api/v1/search`, { params })
.then((response) => {
this.userList = response.data.users.data
this.customerList = response.data.customers.data
resolve(response)
})
.catch((err) => {
handleError(err)
reject(err)
})
})
},
setSelectAllState(data) {
this.selectAllField = data
},
selectUser(data) {
this.selectedUsers = data
if (this.selectedUsers.length === this.users.length) {
this.selectAllField = true
} else {
this.selectAllField = false
}
},
selectAllUsers() {
if (this.selectedUsers.length === this.users.length) {
this.selectedUsers = []
this.selectAllField = false
} else {
let allUserIds = this.users.map((user) => user.id)
this.selectedUsers = allUserIds
this.selectAllField = true
}
},
fetchPendingInvitations() {
return new Promise((resolve, reject) => {
http
.get('/api/v1/company-invitations')
.then((response) => {
this.pendingInvitations = response.data.invitations
resolve(response)
})
.catch((err) => {
handleError(err)
reject(err)
})
})
},
inviteMember(data) {
return new Promise((resolve, reject) => {
http
.post('/api/v1/company-invitations', data)
.then((response) => {
const notificationStore = useNotificationStore()
notificationStore.showNotification({
type: 'success',
message: global.t('members.invited_message'),
})
this.fetchPendingInvitations()
resolve(response)
})
.catch((err) => {
handleError(err)
reject(err)
})
})
},
cancelInvitation(id) {
return new Promise((resolve, reject) => {
http
.delete(`/api/v1/company-invitations/${id}`)
.then((response) => {
const notificationStore = useNotificationStore()
notificationStore.showNotification({
type: 'success',
message: global.t('members.invitation_cancelled'),
})
this.pendingInvitations = this.pendingInvitations.filter(
(inv) => inv.id !== id
)
resolve(response)
})
.catch((err) => {
handleError(err)
reject(err)
})
})
},
},
})()
}