SupportPage.vue 4.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167
  1. <template>
  2. <div>
  3. <DefaultHeaderPage title="Suporte" />
  4. <DefaultTable
  5. ref="tableRef"
  6. :columns="columns"
  7. :api-call="getSupportTickets"
  8. add-item
  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="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="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 { defineAsyncComponent, useTemplateRef } 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. const AddEditTicketDialog = defineAsyncComponent(
  69. () => import("src/pages/support/components/AddEditTicketDialog.vue"),
  70. );
  71. const CloseTicketDialog = defineAsyncComponent(
  72. () => import("src/pages/support/components/CloseTicketDialog.vue"),
  73. );
  74. const $q = useQuasar();
  75. const tableRef = useTemplateRef("tableRef");
  76. const openAddEditTicketDialog = (ticket = null) => {
  77. $q.dialog({
  78. component: AddEditTicketDialog,
  79. componentProps: { ticket },
  80. }).onOk(() => {
  81. tableRef.value?.refresh();
  82. });
  83. };
  84. const columns = [
  85. { name: "id", label: "Ticket", field: "id", align: "left", style: "width: 5%", format: (val) => `#${String(val).padStart(6, "0")}` },
  86. { name: "severity", label: "Prioridade", field: "severity", align: "left", style: "width: 10%" },
  87. { name: "created_at", label: "Data", field: "created_at", align: "left", style: "width: 12%" },
  88. { name: "origin", label: "Origem", field: "origin", align: "left", style: "width: 12%" },
  89. { name: "sector", label: "Setor", field: "sector", align: "left", style: "width: 16%" },
  90. { name: "title", label: "Título", field: "title", align: "left", style: "width: 25%" },
  91. { name: "status", label: "Status", field: "status", align: "left", style: "width: 10%" },
  92. { name: "actions", label: "Ações", field: "actions", align: "center", style: "width: 10%" },
  93. ];
  94. const originLabel = (row) => {
  95. if (row.scope === "internal") return "Interno";
  96. return "Matriz";
  97. };
  98. // Franchisee só pode gerenciar tickets internos que ela mesma criou
  99. const canManage = (row) => row.origin === "unit" && row.scope === "internal";
  100. const severityLabel = (severity) => {
  101. const map = { alta: "Alta", normal: "Normal", baixa: "Baixa" };
  102. return map[severity] ?? severity;
  103. };
  104. const severityColor = (severity) => {
  105. const map = { alta: "negative", normal: "warning", baixa: "positive" };
  106. return map[severity] ?? "grey";
  107. };
  108. const statusLabel = (status) => {
  109. const map = {
  110. in_progress: "Em andamento",
  111. resolved: "Resolvido",
  112. unresolved: "Não resolvido",
  113. };
  114. return map[status] ?? status;
  115. };
  116. const statusColor = (status) => {
  117. const map = {
  118. in_progress: "warning",
  119. resolved: "positive",
  120. unresolved: "negative",
  121. };
  122. return map[status] ?? "grey";
  123. };
  124. const handleEdit = (row) => {
  125. openAddEditTicketDialog(row);
  126. };
  127. const handleClose = (row) => {
  128. $q.dialog({
  129. component: CloseTicketDialog,
  130. componentProps: { ticket: row },
  131. }).onOk(() => {
  132. tableRef.value?.refresh();
  133. });
  134. };
  135. const handleDelete = (row) => {
  136. $q.dialog({
  137. title: "Confirmar exclusão",
  138. message: "Tem certeza que deseja excluir este ticket?",
  139. ok: { color: "negative", label: "Excluir" },
  140. cancel: { color: "primary", outline: true, label: "Cancelar" },
  141. }).onOk(async () => {
  142. await deleteSupportTicket(row.id);
  143. tableRef.value?.refresh();
  144. });
  145. };
  146. </script>