|
|
@@ -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>
|