SupportPage.vue 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. <template>
  2. <div>
  3. <DefaultHeaderPage title="Suporte" />
  4. <DefaultTable
  5. ref="tableRef"
  6. :columns="columns"
  7. :api-call="getSupportTickets"
  8. :add-item="canAdd"
  9. title="Tickets"
  10. :female="false"
  11. description="tickets"
  12. @on-add-item="openAddEditTicketDialog()"
  13. >
  14. <template #body-cell-severity="{ row }">
  15. <q-td align="left">
  16. <q-badge
  17. :color="severityColor(row.severity)"
  18. :label="severityLabel(row.severity)"
  19. style="padding: 6px 10px"
  20. />
  21. </q-td>
  22. </template>
  23. <template #body-cell-origin="{ row }">
  24. <q-td align="left">{{ originLabel(row) }}</q-td>
  25. </template>
  26. <template #body-cell-status="{ row }">
  27. <q-td align="left">
  28. <q-badge
  29. :color="statusColor(row.status)"
  30. :label="statusLabel(row.status)"
  31. style="padding: 6px 10px"
  32. />
  33. </q-td>
  34. </template>
  35. <template #body-cell-actions="{ row }">
  36. <q-td style="display: flex; gap: 4px; align-items: center; justify-content: center">
  37. <q-btn
  38. outline
  39. :icon="canManage(row) ? 'mdi-pencil-outline' : 'mdi-eye-outline'"
  40. style="width: 36px"
  41. @click.prevent.stop="handleEdit(row)"
  42. />
  43. <q-btn
  44. v-if="canEdit && canManage(row) && row.status === 'in_progress'"
  45. outline
  46. icon="mdi-check-circle-outline"
  47. style="width: 36px"
  48. @click.prevent.stop="handleClose(row)"
  49. />
  50. <q-btn
  51. v-if="canDelete && canManage(row) && row.status === 'in_progress'"
  52. outline
  53. icon="mdi-trash-can-outline"
  54. style="width: 36px"
  55. @click.prevent.stop="handleDelete(row)"
  56. />
  57. </q-td>
  58. </template>
  59. </DefaultTable>
  60. </div>
  61. </template>
  62. <script setup>
  63. import { computed, defineAsyncComponent, useTemplateRef, watch } from "vue";
  64. import { useQuasar } from "quasar";
  65. import DefaultTable from "src/components/defaults/DefaultTable.vue";
  66. import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
  67. import { getSupportTickets, deleteSupportTicket } from "src/api/support_ticket";
  68. import { userStore } from "src/stores/user";
  69. import { permissionStore } from "src/stores/permission";
  70. const AddEditTicketDialog = defineAsyncComponent(
  71. () => import("src/pages/support/components/AddEditTicketDialog.vue"),
  72. );
  73. const CloseTicketDialog = defineAsyncComponent(
  74. () => import("src/pages/support/components/CloseTicketDialog.vue"),
  75. );
  76. const $q = useQuasar();
  77. const store = userStore();
  78. const permissions = permissionStore();
  79. const canAdd = computed(() => permissions.getAccess("franchisee_support", "add"));
  80. const canEdit = computed(() => permissions.getAccess("franchisee_support", "edit"));
  81. const canDelete = computed(() => permissions.getAccess("franchisee_support", "delete"));
  82. const tableRef = useTemplateRef("tableRef");
  83. watch(
  84. () => store.selectedUnit?.id,
  85. (newId, oldId) => {
  86. if (newId && newId !== oldId) tableRef.value?.refresh();
  87. },
  88. );
  89. const openAddEditTicketDialog = (ticket = null) => {
  90. $q.dialog({
  91. component: AddEditTicketDialog,
  92. componentProps: { ticket },
  93. }).onOk(() => {
  94. tableRef.value?.refresh();
  95. });
  96. };
  97. const columns = [
  98. { name: "id", label: "Ticket", field: "id", align: "left", style: "width: 5%", format: (val) => `#${String(val).padStart(6, "0")}` },
  99. { name: "severity", label: "Prioridade", field: "severity", align: "left", style: "width: 10%" },
  100. { name: "created_at", label: "Data", field: "created_at", align: "left", style: "width: 12%" },
  101. { name: "origin", label: "Origem", field: "origin", align: "left", style: "width: 12%" },
  102. { name: "sector", label: "Setor", field: "sector", align: "left", style: "width: 16%" },
  103. { name: "title", label: "Título", field: "title", align: "left", style: "width: 25%" },
  104. { name: "status", label: "Status", field: "status", align: "left", style: "width: 10%" },
  105. { name: "actions", label: "Ações", field: "actions", align: "center", style: "width: 10%" },
  106. ];
  107. const originLabel = (row) => {
  108. if (row.scope === "internal") return "Interno";
  109. return "Matriz";
  110. };
  111. // Franchisee só pode gerenciar tickets internos que ela mesma criou
  112. const canManage = (row) => row.origin === "unit" && row.scope === "internal";
  113. const severityLabel = (severity) => {
  114. const map = { alta: "Alta", normal: "Normal", baixa: "Baixa" };
  115. return map[severity] ?? severity;
  116. };
  117. const severityColor = (severity) => {
  118. const map = { alta: "negative", normal: "warning", baixa: "positive" };
  119. return map[severity] ?? "grey";
  120. };
  121. const statusLabel = (status) => {
  122. const map = {
  123. in_progress: "Em andamento",
  124. resolved: "Resolvido",
  125. unresolved: "Não resolvido",
  126. };
  127. return map[status] ?? status;
  128. };
  129. const statusColor = (status) => {
  130. const map = {
  131. in_progress: "warning",
  132. resolved: "positive",
  133. unresolved: "negative",
  134. };
  135. return map[status] ?? "grey";
  136. };
  137. const handleEdit = (row) => {
  138. openAddEditTicketDialog(row);
  139. };
  140. const handleClose = (row) => {
  141. $q.dialog({
  142. component: CloseTicketDialog,
  143. componentProps: { ticket: row },
  144. }).onOk(() => {
  145. tableRef.value?.refresh();
  146. });
  147. };
  148. const handleDelete = (row) => {
  149. $q.dialog({
  150. title: "Confirmar exclusão",
  151. message: "Tem certeza que deseja excluir este ticket?",
  152. ok: { color: "negative", label: "Excluir" },
  153. cancel: { color: "primary", outline: true, label: "Cancelar" },
  154. }).onOk(async () => {
  155. await deleteSupportTicket(row.id);
  156. tableRef.value?.refresh();
  157. });
  158. };
  159. </script>