add assignment v2 UI

This commit is contained in:
Tanmay Deep Sharma
2025-07-31 14:58:10 +05:30
parent 2c204326e5
commit 8f9c45ea8c
46 changed files with 4844 additions and 42 deletions
@@ -0,0 +1,139 @@
import agentCapacityPoliciesAPI from '../../../api/agentCapacityPolicies';
export const actions = {
get: async ({ commit }, params = {}) => {
commit('setUIFlag', { isFetching: true });
try {
const response = await agentCapacityPoliciesAPI.get(params);
const policies = response.data.agent_capacity_policies || response.data;
commit('setCapacityPolicies', policies);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetching: false });
}
},
show: async ({ commit }, id) => {
try {
const response = await agentCapacityPoliciesAPI.show(id);
const policy = response.data.agent_capacity_policy || response.data;
commit('setCapacityPolicy', policy);
return policy;
} catch (error) {
throw new Error(error);
}
},
create: async ({ commit }, data) => {
commit('setUIFlag', { isCreating: true });
try {
const response = await agentCapacityPoliciesAPI.create(data);
const policy = response.data.agent_capacity_policy || response.data;
commit('setCapacityPolicy', policy);
return policy;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isCreating: false });
}
},
update: async ({ commit }, { id, ...data }) => {
commit('setUIFlag', { isUpdating: true });
try {
const response = await agentCapacityPoliciesAPI.update(id, data);
commit('setCapacityPolicy', response.data);
return response.data;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isUpdating: false });
}
},
delete: async ({ commit }, id) => {
commit('setUIFlag', { isDeleting: true });
try {
await agentCapacityPoliciesAPI.delete(id);
commit('deleteCapacityPolicy', id);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isDeleting: false });
}
},
assignUser: async ({ commit }, { id, userId }) => {
commit('setUIFlag', { isAssigningAgents: true });
try {
const response = await agentCapacityPoliciesAPI.assignUser(id, userId);
return response.data;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isAssigningAgents: false });
}
},
removeUser: async ({ commit }, { id, userId }) => {
commit('setUIFlag', { isAssigningAgents: true });
try {
const response = await agentCapacityPoliciesAPI.removeUser(id, userId);
return response.data;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isAssigningAgents: false });
}
},
setInboxLimit: async ({ commit }, { id, inboxId, conversationLimit }) => {
commit('setUIFlag', { isUpdating: true });
try {
const response = await agentCapacityPoliciesAPI.setInboxLimit(
id,
inboxId,
conversationLimit
);
return response.data;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isUpdating: false });
}
},
removeInboxLimit: async ({ commit }, { id, inboxId }) => {
commit('setUIFlag', { isUpdating: true });
try {
const response = await agentCapacityPoliciesAPI.removeInboxLimit(
id,
inboxId
);
return response.data;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isUpdating: false });
}
},
getAgentCapacity: async ({ commit, rootGetters }, { agentId, inboxId }) => {
commit('setUIFlag', { isFetching: true });
try {
const accountId = rootGetters['auth/getCurrentAccountId'];
const response = await agentCapacityPoliciesAPI.getAgentCapacity(
accountId,
agentId,
inboxId
);
commit('setAgentCapacity', { agentId, data: response.data });
return response.data;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetching: false });
}
},
};
@@ -0,0 +1,30 @@
export const getters = {
getCapacityPolicies: state => Object.values(state.policies),
getCapacityPolicy: state => id => state.policies[id],
getAgentCapacities: state => Object.values(state.agentCapacities),
getAgentCapacity: state => agentId => state.agentCapacities[agentId],
getUIFlags: state => state.uiFlags,
getActivePolicies: state => {
return Object.values(state.policies).filter(policy => policy.active);
},
getAgentsByPolicy: state => policyId => {
return Object.values(state.agentCapacities).filter(
capacity => capacity.agent_capacity_policy_id === policyId
);
},
getAvailableCapacity: state => agentId => {
const capacity = state.agentCapacities[agentId];
if (!capacity) return 0;
return capacity.max_capacity - capacity.current_load;
},
getAgentsAtCapacity: state => {
return Object.values(state.agentCapacities).filter(
capacity => capacity.current_load >= capacity.max_capacity
);
},
};
@@ -0,0 +1,23 @@
import { getters } from './getters';
import { actions } from './actions';
import { mutations } from './mutations';
const state = {
policies: {},
agentCapacities: {},
uiFlags: {
isFetching: false,
isCreating: false,
isUpdating: false,
isDeleting: false,
isAssigningAgents: false,
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};
@@ -0,0 +1,60 @@
export const mutations = {
setUIFlag(state, data) {
state.uiFlags = {
...state.uiFlags,
...data,
};
},
setCapacityPolicies(state, response) {
// Handle both array and object with agent_capacity_policies key
const policies = response.agent_capacity_policies || response;
state.policies = {};
if (Array.isArray(policies)) {
policies.forEach(policy => {
state.policies[policy.id] = policy;
});
}
},
setCapacityPolicy(state, response) {
// Handle both direct policy and object with agent_capacity_policy key
const policy = response.agent_capacity_policy || response;
state.policies = {
...state.policies,
[policy.id]: policy,
};
},
deleteCapacityPolicy(state, id) {
const { [id]: deleted, ...rest } = state.policies;
state.policies = rest;
},
setAgentCapacities(state, capacities) {
if (Array.isArray(capacities)) {
const newCapacities = {};
capacities.forEach(capacity => {
newCapacities[capacity.agent_id] = capacity;
});
state.agentCapacities = {
...state.agentCapacities,
...newCapacities,
};
} else {
state.agentCapacities = capacities;
}
},
setAgentCapacity(state, { agentId, data }) {
state.agentCapacities = {
...state.agentCapacities,
[agentId]: data,
};
},
deleteAgentCapacity(state, agentId) {
const { [agentId]: deleted, ...rest } = state.agentCapacities;
state.agentCapacities = rest;
},
};
@@ -0,0 +1,137 @@
import assignmentMetricsAPI from '../../../api/assignmentMetrics';
export const actions = {
getMetrics: async ({ commit, state }) => {
commit('setUIFlag', { isFetching: true });
try {
const response = await assignmentMetricsAPI.get(state.filters);
commit('setOverviewMetrics', response.data.overview || {});
commit('setAssignmentTrends', response.data.trends || []);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetching: false });
}
},
getAgentHistory: async ({ commit, state }) => {
commit('setUIFlag', { isFetchingHistory: true });
try {
const response = await assignmentMetricsAPI.getAllAgentsHistory(
state.filters
);
commit('setAgentHistory', response.data.agent_history || []);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetchingHistory: false });
}
},
getAgentHistoryById: async ({ commit, state }, userId) => {
commit('setUIFlag', { isFetchingHistory: true });
try {
const response = await assignmentMetricsAPI.getAgentHistory(
userId,
state.filters
);
return response.data;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetchingHistory: false });
}
},
getPolicyPerformance: async ({ commit, state }) => {
commit('setUIFlag', { isFetchingPerformance: true });
try {
const response = await assignmentMetricsAPI.getPolicyPerformance(
state.filters
);
commit('setPolicyPerformance', response.data.policy_performance || []);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetchingPerformance: false });
}
},
getAgentUtilization: async ({ commit, state }) => {
commit('setUIFlag', { isFetchingUtilization: true });
try {
const response = await assignmentMetricsAPI.getAgentUtilization(
state.filters
);
commit('setAgentUtilization', response.data.agent_utilization || []);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetchingUtilization: false });
}
},
getAssignmentDistribution: async ({ commit, state }) => {
commit('setUIFlag', { isFetchingDistribution: true });
try {
const response = await assignmentMetricsAPI.get(state.filters);
commit(
'setAssignmentDistribution',
response.data.assignment_distribution || {}
);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetchingDistribution: false });
}
},
exportReport: async ({ commit, state }, type) => {
commit('setUIFlag', { isExporting: true });
try {
const response = await assignmentMetricsAPI.exportReport(
type,
state.filters
);
// Create download link
const url = window.URL.createObjectURL(new Blob([response.data]));
const link = document.createElement('a');
link.href = url;
link.setAttribute(
'download',
`assignment-${type}-report-${Date.now()}.csv`
);
document.body.appendChild(link);
link.click();
link.remove();
window.URL.revokeObjectURL(url);
return true;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isExporting: false });
}
},
setFilters: ({ commit, dispatch }, filters) => {
commit('setFilters', filters);
// Refresh all data with new filters
dispatch('getMetrics');
dispatch('getAgentHistory');
dispatch('getPolicyPerformance');
dispatch('getAgentUtilization');
dispatch('getAssignmentDistribution');
},
refreshAllMetrics: ({ dispatch }) => {
return Promise.all([
dispatch('getMetrics'),
dispatch('getAgentHistory'),
dispatch('getPolicyPerformance'),
dispatch('getAgentUtilization'),
dispatch('getAssignmentDistribution'),
]);
},
};
@@ -0,0 +1,62 @@
export const getters = {
getMetrics: state => state.metrics,
getOverviewMetrics: state => state.metrics.overview,
getAgentHistory: state => state.metrics.agentHistory,
getPolicyPerformance: state => state.metrics.policyPerformance,
getAgentUtilization: state => state.metrics.agentUtilization,
getAssignmentDistribution: state => state.metrics.assignmentDistribution,
getAssignmentTrends: state => state.metrics.assignmentTrends,
getFilters: state => state.filters,
getUIFlags: state => state.uiFlags,
getTopPerformingAgents: state => {
return [...state.metrics.agentHistory]
.sort((a, b) => b.assignments_handled - a.assignments_handled)
.slice(0, 5);
},
getTopPerformingPolicies: state => {
return [...state.metrics.policyPerformance]
.sort((a, b) => b.success_rate - a.success_rate)
.slice(0, 5);
},
getAverageUtilization: state => {
const utilization = state.metrics.agentUtilization;
if (!utilization.length) return 0;
const totalUtilization = utilization.reduce(
(sum, agent) => sum + agent.utilization_percentage,
0
);
return (totalUtilization / utilization.length).toFixed(2);
},
getCalculatedTrends: state => {
const history = state.metrics.agentHistory;
if (!history.length) return [];
// Group by date and calculate trends
const trendMap = {};
history.forEach(entry => {
const date = entry.date;
if (!trendMap[date]) {
trendMap[date] = {
date,
total_assignments: 0,
avg_response_time: 0,
count: 0,
};
}
trendMap[date].total_assignments += entry.assignments_handled;
trendMap[date].avg_response_time += entry.avg_response_time;
trendMap[date].count += 1;
});
return Object.values(trendMap).map(trend => ({
date: trend.date,
total_assignments: trend.total_assignments,
avg_response_time: trend.avg_response_time / trend.count,
}));
},
};
@@ -0,0 +1,37 @@
import { getters } from './getters';
import { actions } from './actions';
import { mutations } from './mutations';
const state = {
metrics: {
overview: {},
agentHistory: [],
policyPerformance: [],
agentUtilization: [],
assignmentDistribution: {},
assignmentTrends: [],
},
filters: {
startDate: null,
endDate: null,
agentIds: [],
policyIds: [],
groupBy: 'day',
},
uiFlags: {
isFetching: false,
isFetchingHistory: false,
isFetchingPerformance: false,
isFetchingUtilization: false,
isFetchingDistribution: false,
isExporting: false,
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};
@@ -0,0 +1,39 @@
export const mutations = {
setUIFlag(state, data) {
state.uiFlags = {
...state.uiFlags,
...data,
};
},
setFilters(state, filters) {
state.filters = {
...state.filters,
...filters,
};
},
setOverviewMetrics(state, metrics) {
state.metrics.overview = metrics;
},
setAgentHistory(state, history) {
state.metrics.agentHistory = history;
},
setPolicyPerformance(state, performance) {
state.metrics.policyPerformance = performance;
},
setAgentUtilization(state, utilization) {
state.metrics.agentUtilization = utilization;
},
setAssignmentDistribution(state, distribution) {
state.metrics.assignmentDistribution = distribution;
},
setAssignmentTrends(state, trends) {
state.metrics.assignmentTrends = trends;
},
};
@@ -0,0 +1,67 @@
import assignmentPoliciesAPI from '../../../api/assignmentPolicies';
export const actions = {
get: async ({ commit }) => {
commit('setUIFlag', { isFetching: true });
try {
const response = await assignmentPoliciesAPI.get();
const policies = response.data.assignment_policies || response.data;
commit('setAssignmentPolicies', policies);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetching: false });
}
},
show: async ({ commit }, id) => {
try {
const response = await assignmentPoliciesAPI.show(id);
const policy = response.data.assignment_policy || response.data;
commit('setAssignmentPolicy', policy);
return policy;
} catch (error) {
throw new Error(error);
}
},
create: async ({ commit }, data) => {
commit('setUIFlag', { isCreating: true });
try {
const response = await assignmentPoliciesAPI.create(data);
const policy = response.data.assignment_policy || response.data;
commit('setAssignmentPolicy', policy);
return policy;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isCreating: false });
}
},
update: async ({ commit }, { id, ...data }) => {
commit('setUIFlag', { isUpdating: true });
try {
const response = await assignmentPoliciesAPI.update(id, data);
const policy = response.data.assignment_policy || response.data;
commit('setAssignmentPolicy', policy);
return policy;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isUpdating: false });
}
},
delete: async ({ commit }, id) => {
commit('setUIFlag', { isDeleting: true });
try {
await assignmentPoliciesAPI.delete(id);
commit('deleteAssignmentPolicy', id);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isDeleting: false });
}
},
};
@@ -0,0 +1,11 @@
export const getters = {
getAssignmentPolicies: state => Object.values(state.records),
getAssignmentPolicy: state => id => state.records[id],
getUIFlags: state => state.uiFlags,
getActiveAssignmentPolicies: state => {
return Object.values(state.records).filter(policy => policy.enabled);
},
getPoliciesByPriority: state => {
return Object.values(state.records).sort((a, b) => a.id - b.id);
},
};
@@ -0,0 +1,21 @@
import { getters } from './getters';
import { actions } from './actions';
import { mutations } from './mutations';
const state = {
records: {},
uiFlags: {
isFetching: false,
isCreating: false,
isUpdating: false,
isDeleting: false,
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};
@@ -0,0 +1,27 @@
export const mutations = {
setUIFlag(state, data) {
state.uiFlags = {
...state.uiFlags,
...data,
};
},
setAssignmentPolicies(state, policies) {
state.records = {};
policies.forEach(policy => {
state.records[policy.id] = policy;
});
},
setAssignmentPolicy(state, policy) {
state.records = {
...state.records,
[policy.id]: policy,
};
},
deleteAssignmentPolicy(state, id) {
const { [id]: deleted, ...rest } = state.records;
state.records = rest;
},
};
@@ -0,0 +1,125 @@
import leavesAPI from '../../../api/leaves';
export const actions = {
get: async ({ commit }, params = {}) => {
commit('setUIFlag', { isFetching: true });
try {
const response = await leavesAPI.get(params);
// Handle both possible response structures
const leaves = response.data.leaves || response.data || [];
const meta = response.data.meta || {};
commit('setLeaves', leaves);
commit('setMeta', meta);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetching: false });
}
},
show: async ({ commit }, id) => {
try {
const response = await leavesAPI.show(id);
const leave = response.data.leave || response.data;
commit('setLeave', leave);
return leave;
} catch (error) {
throw new Error(error);
}
},
create: async ({ commit }, data) => {
commit('setUIFlag', { isCreating: true });
try {
const response = await leavesAPI.create(data);
const leave = response.data.leave || response.data;
commit('setLeave', leave);
return leave;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isCreating: false });
}
},
update: async ({ commit }, { id, ...data }) => {
commit('setUIFlag', { isUpdating: true });
try {
const response = await leavesAPI.update(id, data);
commit('setLeave', response.data);
return response.data;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isUpdating: false });
}
},
delete: async ({ commit }, id) => {
commit('setUIFlag', { isDeleting: true });
try {
await leavesAPI.delete(id);
commit('deleteLeave', id);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isDeleting: false });
}
},
approve: async ({ commit }, { id, approverNotes }) => {
commit('setUIFlag', { isApproving: true });
try {
const response = await leavesAPI.approve(id, {
comments: approverNotes,
});
commit('setLeave', response.data.leave);
return response.data.leave;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isApproving: false });
}
},
reject: async ({ commit }, { id, approverNotes }) => {
commit('setUIFlag', { isRejecting: true });
try {
const response = await leavesAPI.reject(id, {
reason: approverNotes,
});
commit('setLeave', response.data.leave);
return response.data.leave;
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isRejecting: false });
}
},
getMyLeaves: async ({ commit }, params = {}) => {
commit('setUIFlag', { isFetching: true });
try {
const response = await leavesAPI.getMyLeaves(params);
commit('setLeaves', response.data.leaves);
commit('setMeta', response.data.meta);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetching: false });
}
},
getPendingApprovals: async ({ commit }, params = {}) => {
commit('setUIFlag', { isFetching: true });
try {
const response = await leavesAPI.getPendingApprovals(params);
commit('setLeaves', response.data.leaves);
commit('setMeta', response.data.meta);
} catch (error) {
throw new Error(error);
} finally {
commit('setUIFlag', { isFetching: false });
}
},
};
@@ -0,0 +1,41 @@
export const getters = {
getLeaves: state => Object.values(state.records),
getLeave: state => id => state.records[id],
getUIFlags: state => state.uiFlags,
getMeta: state => state.meta,
getPendingLeaves: state => {
return Object.values(state.records).filter(
leave => leave.status === 'pending'
);
},
getApprovedLeaves: state => {
return Object.values(state.records).filter(
leave => leave.status === 'approved'
);
},
getRejectedLeaves: state => {
return Object.values(state.records).filter(
leave => leave.status === 'rejected'
);
},
getLeavesByAgent: state => agentId => {
return Object.values(state.records).filter(
leave => leave.agent_id === agentId
);
},
getLeavesByDateRange: state => (startDate, endDate) => {
return Object.values(state.records).filter(leave => {
const leaveStart = new Date(leave.start_date);
const leaveEnd = new Date(leave.end_date);
const rangeStart = new Date(startDate);
const rangeEnd = new Date(endDate);
return leaveStart <= rangeEnd && leaveEnd >= rangeStart;
});
},
};
@@ -0,0 +1,27 @@
import { getters } from './getters';
import { actions } from './actions';
import { mutations } from './mutations';
const state = {
records: {},
meta: {
totalCount: 0,
currentPage: 1,
},
uiFlags: {
isFetching: false,
isCreating: false,
isUpdating: false,
isDeleting: false,
isApproving: false,
isRejecting: false,
},
};
export default {
namespaced: true,
state,
getters,
actions,
mutations,
};
@@ -0,0 +1,31 @@
export const mutations = {
setUIFlag(state, data) {
state.uiFlags = {
...state.uiFlags,
...data,
};
},
setMeta(state, meta) {
state.meta = meta;
},
setLeaves(state, leaves) {
state.records = {};
leaves.forEach(leave => {
state.records[leave.id] = leave;
});
},
setLeave(state, leave) {
state.records = {
...state.records,
[leave.id]: leave,
};
},
deleteLeave(state, id) {
const { [id]: deleted, ...rest } = state.records;
state.records = rest;
},
};