diff --git a/app/javascript/dashboard/api/agentCapacityPolicies.js b/app/javascript/dashboard/api/agentCapacityPolicies.js new file mode 100644 index 000000000..ebcce0b98 --- /dev/null +++ b/app/javascript/dashboard/api/agentCapacityPolicies.js @@ -0,0 +1,66 @@ +/* global axios */ +import ApiClient from './ApiClient'; + +export class AgentCapacityPoliciesAPI extends ApiClient { + constructor() { + super('agent_capacity_policies', { accountScoped: true }); + } + + // GET /api/v1/accounts/:accountId/agent_capacity_policies + get(params = {}) { + return axios.get(this.url, { params }); + } + + // GET /api/v1/accounts/:accountId/agent_capacity_policies/:id + show(id) { + return axios.get(`${this.url}/${id}`); + } + + // POST /api/v1/accounts/:accountId/agent_capacity_policies + create(data) { + return axios.post(this.url, { agent_capacity_policy: data }); + } + + // PUT /api/v1/accounts/:accountId/agent_capacity_policies/:id + update(id, data) { + return axios.put(`${this.url}/${id}`, { agent_capacity_policy: data }); + } + + // DELETE /api/v1/accounts/:accountId/agent_capacity_policies/:id + delete(id) { + return axios.delete(`${this.url}/${id}`); + } + + // POST /api/v1/accounts/:accountId/agent_capacity_policies/:id/users + assignUser(id, userId) { + return axios.post(`${this.url}/${id}/users`, { + user_id: userId, + }); + } + + // DELETE /api/v1/accounts/:accountId/agent_capacity_policies/:id/users/:userId + removeUser(id, userId) { + return axios.delete(`${this.url}/${id}/users/${userId}`); + } + + // POST /api/v1/accounts/:accountId/agent_capacity_policies/:id/inbox_limits/:inboxId + setInboxLimit(id, inboxId, conversationLimit) { + return axios.post(`${this.url}/${id}/inbox_limits/${inboxId}`, { + conversation_limit: conversationLimit, + }); + } + + // DELETE /api/v1/accounts/:accountId/agent_capacity_policies/:id/inbox_limits/:inboxId + removeInboxLimit(id, inboxId) { + return axios.delete(`${this.url}/${id}/inbox_limits/${inboxId}`); + } + + // GET /api/v1/accounts/:accountId/agents/:agentId/capacity + getAgentCapacity(accountId, agentId, inboxId = null) { + const url = `/api/v1/accounts/${accountId}/agents/${agentId}/capacity`; + const params = inboxId ? { inbox_id: inboxId } : {}; + return axios.get(url, { params }); + } +} + +export default new AgentCapacityPoliciesAPI(); diff --git a/app/javascript/dashboard/api/assignmentMetrics.js b/app/javascript/dashboard/api/assignmentMetrics.js new file mode 100644 index 000000000..afb6a9286 --- /dev/null +++ b/app/javascript/dashboard/api/assignmentMetrics.js @@ -0,0 +1,52 @@ +/* global axios */ +import ApiClient from './ApiClient'; + +export class AssignmentMetricsAPI extends ApiClient { + constructor() { + super('reports/assignment_metrics', { accountScoped: true }); + } + + // GET /api/v1/accounts/:accountId/reports/assignment_metrics + get(params = {}) { + return axios.get(this.url, { params }); + } + + // GET /api/v1/accounts/:accountId/agents/:userId/assignment_history + getAgentHistory(userId, params = {}) { + return axios.get( + `/api/v1/accounts/${this.accountId}/agents/${userId}/assignment_history`, + { params } + ); + } + + // Get all agents assignment history + getAllAgentsHistory(params = {}) { + return axios.get(this.url, { + params: { ...params, include_agent_history: true }, + }); + } + + // Get policy performance metrics + getPolicyPerformance(params = {}) { + return axios.get(this.url, { + params: { ...params, include_policy_performance: true }, + }); + } + + // Get agent utilization metrics + getAgentUtilization(params = {}) { + return axios.get(this.url, { + params: { ...params, include_utilization: true }, + }); + } + + // Export report data + exportReport(type, params = {}) { + return axios.get(`${this.url}/export`, { + params: { type, ...params }, + responseType: 'blob', + }); + } +} + +export default new AssignmentMetricsAPI(); diff --git a/app/javascript/dashboard/api/assignmentPolicies.js b/app/javascript/dashboard/api/assignmentPolicies.js new file mode 100644 index 000000000..14c0eefbc --- /dev/null +++ b/app/javascript/dashboard/api/assignmentPolicies.js @@ -0,0 +1,35 @@ +/* global axios */ +import ApiClient from './ApiClient'; + +export class AssignmentPoliciesAPI extends ApiClient { + constructor() { + super('assignment_policies', { accountScoped: true }); + } + + // GET /api/v1/accounts/:accountId/assignment_policies + get() { + return axios.get(this.url); + } + + // GET /api/v1/accounts/:accountId/assignment_policies/:id + show(id) { + return axios.get(`${this.url}/${id}`); + } + + // POST /api/v1/accounts/:accountId/assignment_policies + create(data) { + return axios.post(this.url, { assignment_policy: data }); + } + + // PUT /api/v1/accounts/:accountId/assignment_policies/:id + update(id, data) { + return axios.put(`${this.url}/${id}`, { assignment_policy: data }); + } + + // DELETE /api/v1/accounts/:accountId/assignment_policies/:id + delete(id) { + return axios.delete(`${this.url}/${id}`); + } +} + +export default new AssignmentPoliciesAPI(); diff --git a/app/javascript/dashboard/api/inboxes.js b/app/javascript/dashboard/api/inboxes.js index 361b9472f..0e3fbacf2 100644 --- a/app/javascript/dashboard/api/inboxes.js +++ b/app/javascript/dashboard/api/inboxes.js @@ -32,6 +32,16 @@ class Inboxes extends CacheEnabledApiClient { syncTemplates(inboxId) { return axios.post(`${this.url}/${inboxId}/sync_templates`); } + + assignPolicy(inboxId, policyId) { + return axios.post(`${this.url}/${inboxId}/assignment_policy`, { + assignment_policy_id: policyId, + }); + } + + removePolicy(inboxId) { + return axios.delete(`${this.url}/${inboxId}/assignment_policy`); + } } export default new Inboxes(); diff --git a/app/javascript/dashboard/api/leaves.js b/app/javascript/dashboard/api/leaves.js new file mode 100644 index 000000000..cdd7b328c --- /dev/null +++ b/app/javascript/dashboard/api/leaves.js @@ -0,0 +1,55 @@ +/* global axios */ +import ApiClient from './ApiClient'; + +export class LeavesAPI extends ApiClient { + constructor() { + super('leaves', { accountScoped: true }); + } + + // GET /api/v1/accounts/:accountId/leaves + get(params = {}) { + return axios.get(this.url, { params }); + } + + // GET /api/v1/accounts/:accountId/leaves/:id + show(id) { + return axios.get(`${this.url}/${id}`); + } + + // POST /api/v1/accounts/:accountId/leaves + create(data) { + return axios.post(this.url, { leave: data.leave, user_id: data.user_id }); + } + + // PUT /api/v1/accounts/:accountId/leaves/:id + update(id, data) { + return axios.put(`${this.url}/${id}`, { leave: data }); + } + + // DELETE /api/v1/accounts/:accountId/leaves/:id + delete(id) { + return axios.delete(`${this.url}/${id}`); + } + + // POST /api/v1/accounts/:accountId/leaves/:id/approve + approve(id, data = {}) { + return axios.post(`${this.url}/${id}/approve`, data); + } + + // POST /api/v1/accounts/:accountId/leaves/:id/reject + reject(id, data = {}) { + return axios.post(`${this.url}/${id}/reject`, data); + } + + // GET /api/v1/accounts/:accountId/leaves/my_leaves + getMyLeaves(params = {}) { + return axios.get(`${this.url}/my_leaves`, { params }); + } + + // GET /api/v1/accounts/:accountId/leaves/pending_approvals + getPendingApprovals(params = {}) { + return axios.get(`${this.url}/pending_approvals`, { params }); + } +} + +export default new LeavesAPI(); diff --git a/app/javascript/dashboard/components-next/button/Button.vue b/app/javascript/dashboard/components-next/button/Button.vue index dde1d3d9a..347c79a6d 100644 --- a/app/javascript/dashboard/components-next/button/Button.vue +++ b/app/javascript/dashboard/components-next/button/Button.vue @@ -54,6 +54,11 @@ const filteredAttrs = computed(() => { standardAttrs[key] = value; }); + // Default to type="button" to prevent accidental form submissions + if (!standardAttrs.type) { + standardAttrs.type = 'button'; + } + return standardAttrs; }); diff --git a/app/javascript/dashboard/components-next/filter/inputs/MultiSelect.vue b/app/javascript/dashboard/components-next/filter/inputs/MultiSelect.vue index be11847fb..93e455208 100644 --- a/app/javascript/dashboard/components-next/filter/inputs/MultiSelect.vue +++ b/app/javascript/dashboard/components-next/filter/inputs/MultiSelect.vue @@ -91,6 +91,7 @@ const toggleOption = option => {