Pārlūkot izejas kodu

Merge branch 'feature/SERPRATI-GUS-plataforma-v1' of gogs.softpar.inf.br:Softpar/sfp_front_vue_serprati_digital into feature/SERPRATI-GUS-plataforma-v1

kayo henrique 3 nedēļas atpakaļ
vecāks
revīzija
d35fdc6994

+ 10 - 0
src/api/appointment.js

@@ -29,6 +29,16 @@ export const getPartnerAppointments = async () => {
   return data.payload;
 };
 
+export const approveAppointmentParceiro = async (id) => {
+  const { data } = await api.put(`/parceiro/appointment/${id}/approve`);
+  return data.payload;
+};
+
+export const rejectAppointmentParceiro = async (id) => {
+  const { data } = await api.put(`/parceiro/appointment/${id}/reject`);
+  return data.payload;
+};
+
 export const getAdminCounters = async () => {
   const { data } = await api.get("/appointment/admin/counters");
   return data.payload;

+ 148 - 71
src/pages/parceiros-convenios/AgendamentosParceiroPage.vue

@@ -3,72 +3,102 @@
     <DefaultHeaderPage />
 
     <div class="q-pa-md">
+
       <div v-if="loading" class="flex flex-center q-py-xl">
         <q-spinner color="violet-normal" size="48px" />
       </div>
 
-      <div v-else-if="appointments.length === 0" class="text-center text-grey q-py-xl">
-        {{ $t("http.errors.no_records_found") }}
-      </div>
+      <template v-else>
+        <div class="counters-row q-mb-md">
+          <div class="counter-card">
+            <span class="counter-value">
+              <q-icon name="mdi-clock-outline" color="warning" />
+              {{ counters.pendentes }}
+            </span>
+            <span class="counter-label">{{ $t("agendamento.status.pendente") }}</span>
+          </div>
+          <div class="counter-card">
+            <span class="counter-value">
+              <q-icon name="mdi-check-circle-outline" color="positive" />
+              {{ counters.confirmados }}
+            </span>
+            <span class="counter-label">{{ $t("agendamento.status.confirmado") }}</span>
+          </div>
+          <div class="counter-card">
+            <span class="counter-value">
+              <q-icon name="mdi-close-circle-outline" color="negative" />
+              {{ counters.recusados }}
+            </span>
+            <span class="counter-label">{{ $t("agendamento.status.recusado") }}</span>
+          </div>
+        </div>
+
+        <div v-if="appointments.length === 0" class="text-center text-grey q-py-xl">
+          {{ $t("http.errors.no_records_found") }}
+        </div>
+
+        <q-table
+          v-else
+          class="softpar-table q-pa-sm"
+          :rows="pagedAppointments"
+          :columns="columns"
+          row-key="id"
+          hide-pagination
+          :rows-per-page-options="[0]"
+        >
+          <template #body-cell-status="props">
+            <q-td :props="props">
+              <q-chip
+                outline
+                :color="statusColor(props.row.status)"
+                :label="$t(`agendamento.status.${props.row.status}`)"
+                size="sm"
+              />
+            </q-td>
+          </template>
+
+          <template #body-cell-acoes="props">
+            <q-td :props="props">
+              <div class="row no-wrap items-center" style="gap: 4px">
+                <q-btn
+                  dense
+                  round
+                  icon="mdi-check"
+                  color="positive"
+                  size="sm"
+                  :unelevated="props.row.status === 'confirmado'"
+                  :outline="props.row.status !== 'confirmado'"
+                  :loading="actionId === props.row.id && actionType === 'approve'"
+                  @click.prevent.stop="onApprove(props.row)"
+                />
+                <q-btn
+                  dense
+                  round
+                  icon="mdi-close"
+                  color="negative"
+                  size="sm"
+                  :unelevated="props.row.status === 'recusado' || props.row.status === 'cancelado'"
+                  :outline="props.row.status !== 'recusado' && props.row.status !== 'cancelado'"
+                  :loading="actionId === props.row.id && actionType === 'reject'"
+                  @click.prevent.stop="onReject(props.row)"
+                />
+              </div>
+            </q-td>
+          </template>
+        </q-table>
+
+        <div v-if="totalPages > 1" class="flex flex-center q-mt-lg">
+          <q-pagination
+            v-model="currentPage"
+            :max="totalPages"
+            boundary-numbers
+            color="violet-normal"
+            active-color="violet-normal"
+            direction-links
+          />
+        </div>
+      </template>
 
-      <q-table
-        v-else
-        class="softpar-table q-pa-sm"
-        :rows="pagedAppointments"
-        :columns="columns"
-        row-key="id"
-        hide-pagination
-        :rows-per-page-options="[0]"
-      >
-        <template #body-cell-status="props">
-          <q-td :props="props">
-            <q-chip
-              outline
-              :color="statusColor(props.row.status)"
-              :label="$t(`agendamento.status.${props.row.status}`)"
-              size="sm"
-            />
-          </q-td>
-        </template>
-
-        <template #body-cell-acoes="props">
-          <q-td :props="props">
-            <q-btn
-              dense
-              round
-              flat
-              icon="mdi-check"
-              color="positive"
-              size="sm"
-              :disable="props.row.status !== 'pendente'"
-              :loading="actionId === props.row.id && actionType === 'approve'"
-              @click="onApprove(props.row)"
-            />
-            <q-btn
-              dense
-              round
-              flat
-              icon="mdi-close"
-              color="negative"
-              size="sm"
-              :disable="props.row.status !== 'pendente'"
-              :loading="actionId === props.row.id && actionType === 'reject'"
-              @click="onReject(props.row)"
-            />
-          </q-td>
-        </template>
-      </q-table>
-
-      <div v-if="totalPages > 1" class="flex flex-center q-mt-lg">
-        <q-pagination
-          v-model="currentPage"
-          :max="totalPages"
-          boundary-numbers
-          color="violet-normal"
-          active-color="violet-normal"
-          direction-links
-        />
-      </div>
     </div>
   </div>
 </template>
@@ -77,7 +107,11 @@
 import { ref, computed, onMounted } from "vue";
 import { useQuasar } from "quasar";
 import { useI18n } from "vue-i18n";
-import { getPartnerAppointments, approveAppointment, rejectAppointment } from "src/api/appointment";
+import {
+  getPartnerAppointments,
+  approveAppointmentParceiro,
+  rejectAppointmentParceiro,
+} from "src/api/appointment";
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
 
 const $q = useQuasar();
@@ -91,15 +125,21 @@ const actionId = ref(null);
 const actionType = ref(null);
 
 const columns = computed(() => [
-  { name: "pedido", label: t("agendamento.col.pedido"), field: "order_number", align: "left" },
-  { name: "associado", label: t("agendamento.associado"), field: (row) => row.user?.name || "—", align: "left" },
-  { name: "servico", label: t("agendamento.col.servico"), field: (row) => row.partner_agreement_service?.name || "—", align: "left" },
-  { name: "solicitacao", label: t("agendamento.col.solicitacao"), field: (row) => formatDate(row.created_at), align: "left" },
-  { name: "horario", label: t("common.terms.hour2"), field: (row) => formatDateTime(row.date, row.time), align: "left" },
-  { name: "acoes", label: t("common.terms.actions"), field: "id", align: "center" },
-  { name: "status", label: t("common.terms.status"), field: "status", align: "center" },
+  { name: "pedido",     label: t("agendamento.col.pedido"),     field: "order_number",                                                              align: "left" },
+  { name: "associado",  label: t("agendamento.associado"),       field: (row) => row.user?.name || "—",                                              align: "left" },
+  { name: "servico",    label: t("agendamento.col.servico"),     field: (row) => row.partner_agreement_service?.name || "—",                         align: "left" },
+  { name: "solicitacao",label: t("agendamento.col.solicitacao"), field: (row) => formatDate(row.created_at),                                         align: "left" },
+  { name: "horario",    label: t("common.terms.hour2"),          field: (row) => formatDateTime(row.date, row.time),                                 align: "left" },
+  { name: "acoes",      label: t("common.terms.actions"),        field: "id",                                                                        align: "center" },
+  { name: "status",     label: t("common.terms.status"),         field: "status",                                                                    align: "center" },
 ]);
 
+const counters = computed(() => ({
+  pendentes:  appointments.value.filter((a) => a.status === "pendente").length,
+  confirmados: appointments.value.filter((a) => a.status === "confirmado").length,
+  recusados:  appointments.value.filter((a) => a.status === "recusado" || a.status === "cancelado").length,
+}));
+
 const totalPages = computed(() => Math.max(1, Math.ceil(appointments.value.length / PER_PAGE)));
 
 const pagedAppointments = computed(() => {
@@ -128,6 +168,7 @@ const formatDateTime = (date, time) => {
 };
 
 const onApprove = (row) => {
+  if (row.status !== "pendente") return;
   $q.dialog({
     title: t("common.ui.messages.confirm_action"),
     message: t("agendamento.confirm_approve"),
@@ -137,7 +178,7 @@ const onApprove = (row) => {
     actionId.value = row.id;
     actionType.value = "approve";
     try {
-      await approveAppointment(row.id);
+      await approveAppointmentParceiro(row.id);
       row.status = "confirmado";
       $q.notify({ type: "positive", message: t("http.success") });
     } catch {
@@ -150,6 +191,7 @@ const onApprove = (row) => {
 };
 
 const onReject = (row) => {
+  if (row.status !== "pendente") return;
   $q.dialog({
     title: t("common.ui.messages.confirm_action"),
     message: t("agendamento.confirm_reject"),
@@ -159,7 +201,7 @@ const onReject = (row) => {
     actionId.value = row.id;
     actionType.value = "reject";
     try {
-      await rejectAppointment(row.id);
+      await rejectAppointmentParceiro(row.id);
       row.status = "recusado";
       $q.notify({ type: "positive", message: t("http.success") });
     } catch {
@@ -185,3 +227,38 @@ onMounted(async () => {
 <style lang="scss">
 @import "src/css/table.scss";
 </style>
+
+<style scoped lang="scss">
+@use "src/css/quasar.variables.scss" as vars;
+
+.counters-row {
+  display: flex;
+  gap: 12px;
+}
+
+.counter-card {
+  flex: 1;
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  padding: 16px;
+  border-radius: 8px;
+  border: 1.5px solid vars.$color-border;
+  background: vars.$surface;
+  color: #661d75;
+}
+
+.counter-value {
+  font-size: 1.75rem;
+  font-weight: 700;
+  line-height: 1;
+  margin-bottom: 4px;
+  display: flex;
+  align-items: center;
+  gap: 6px;
+}
+
+.counter-label {
+  font-size: 0.85rem;
+}
+</style>