Sfoglia il codice sorgente

refactor: altera ordem do layout do dashboard e adiciona novos dados

Gustavo Mantovani 1 mese fa
parent
commit
5a99607562

+ 108 - 10
src/helpers/buildMetricCards.js

@@ -8,21 +8,24 @@ import {
 export function buildMetricCards(summary, options = {}) {
   const { isAllPropertiesSelected = false, referenceLabel = "" } = options;
 
-  const totalExpenses = Number(summary.expenses ?? 0);
-  const netBalance = Number(
-    summary.net_revenue ??
-      summary.net_balance_amount ??
-      Number(summary.reserve_total ?? 0) -
-        Number(summary.total_forward_fee_all ?? 0),
-  );
-  const grossRevenue = Number(summary.reserve_total ?? 0);
-  const netRevenue = netBalance;
-  const netPayout = Number(summary.final_payout_amount ?? 0);
+  // const totalExpenses = Number(summary.expenses ?? 0);
+
+  // const netBalance = Number(
+  //  summary.net_revenue ??
+  //    summary.net_balance_amount ??
+  //    Number(summary.reserve_total ?? 0) -
+  //      Number(summary.total_forward_fee_all ?? 0),
+  // );
+
+  // const grossRevenue = Number(summary.reserve_total ?? 0);
+  // const netRevenue = netBalance;
+  // const netPayout = Number(summary.final_payout_amount ?? 0);
 
   const occupancyCaption = isAllPropertiesSelected
     ? "percentual consolidado do período"
     : `${formatInteger(summary.occupied_nights_in_month)} de ${formatInteger(summary.days_in_month)} dias`;
 
+  /*
   return [
     {
       label: "Faturamento Bruto",
@@ -79,4 +82,99 @@ export function buildMetricCards(summary, options = {}) {
       caption: "manutenção e operação",
     },
   ];
+  */
+
+  return [
+    // INDICADORES
+
+    {
+      label: "Ocupação",
+      value: formatPercent(summary.occupancy_rate, 1),
+      caption: occupancyCaption,
+    },
+
+    {
+      label: "Total de Reservas",
+      value: formatInteger(summary.reservations_count),
+      caption: "no período",
+    },
+
+    {
+      label: "Dias por Reserva",
+      value: formatDecimal(summary.average_nights_per_reservation),
+      caption: "média de permanência",
+    },
+
+    {
+      label: "Diária Média",
+      value: formatCurrency(summary.average_price_per_night),
+      caption: "Bruto ÷ diárias",
+    },
+
+    {
+      label: "Limpezas",
+      value: formatInteger(summary.cleanings_count),
+      caption: "total no período",
+    },
+
+    {
+      label: "Manutenção",
+      value: formatInteger(summary.maintenance_days),
+      caption: "dias bloqueados",
+    },
+
+    {
+      label: "Ticket Médio/Reserva",
+      value: formatCurrency(summary.average_reservation_ticket),
+      caption: isAllPropertiesSelected
+        ? "Todos apartamentos"
+        : "No imóvel selecionado",
+    },
+
+    // FINANCEIRO
+
+    {
+      label: "Faturamento Bruto",
+      value: formatCurrency(summary.gross_revenue),
+      caption: referenceLabel || "Mês selecionado",
+    },
+
+    {
+      label: "OTA",
+      value: formatCurrency(summary.ota_amount),
+      caption: "taxas plataformas",
+    },
+
+    {
+      label: "Faturamento Líquido",
+      value: formatCurrency(summary.net_revenue_amount),
+      caption: "Bruto - OTA",
+    },
+
+    {
+      label: "Total Limpeza",
+      value: formatCurrency(summary.cleaning_total_amount),
+      caption: "taxa de limpeza",
+    },
+
+    {
+      label: "Gestão Kizzo",
+      value: formatCurrency(summary.management_fee_amount),
+      caption: "comissão gestão",
+    },
+
+    {
+      label: "Despesas",
+      value: formatCurrency(summary.expenses_amount),
+      caption: "manutenção e operação",
+    },
+
+    {
+      label: "Repasse",
+      value: formatCurrency(summary.payout_amount),
+      caption: isAllPropertiesSelected
+        ? "Todos apartamentos"
+        : "No imóvel selecionado",
+    },
+  ];
 }

+ 68 - 35
src/pages/dashboard/DashboardPage.vue

@@ -30,9 +30,24 @@
 
         <q-separator class="dashboard-separator" />
 
+        <!--
         <div class="dashboard-section-caption">
           Faturamento mês atual, Ocupação e Reservar
         </div>
+        !-->
+
+        <div class="section-header">
+          <span class="dashboard-section-caption">
+            Indicadores
+
+            <q-tooltip
+              anchor="center left"
+              self="center right"
+            >
+              Os números exibidos são referentes ao desempenho da reserva
+            </q-tooltip>
+          </span>
+        </div>
 
         <div class="metrics-grid">
           <DashboardMetricCard
@@ -45,6 +60,19 @@
           />
         </div>
 
+        <div class="section-header">
+          <span class="dashboard-section-caption">
+            Financeiro
+
+            <q-tooltip
+              anchor="center left"
+              self="center right"
+            >
+              Os valores exibidos, são referentes ao desempenho financeiro
+            </q-tooltip>
+          </span>
+        </div>
+
         <div class="metrics-grid">
           <DashboardMetricCard
             v-for="card in secondRowCards"
@@ -168,6 +196,14 @@ const defaultSummary = Object.freeze({
   available_days: 0,
   days_in_month: 30,
   properties_count: 1,
+
+  gross_revenue: 0,
+  ota_amount: 0,
+  net_revenue_amount: 0,
+  cleaning_total_amount: 0,
+  management_fee_amount: 0,
+  expenses_amount: 0,
+  payout_amount: 0,
 });
 
 const monthLabels = [
@@ -300,8 +336,14 @@ const allMetricCards = computed(() =>
     totalCapacityDays: totalCapacityDays.value,
   }),
 );
-const firstRowCards = computed(() => allMetricCards.value.slice(0, 5));
-const secondRowCards = computed(() => allMetricCards.value.slice(5, 10));
+
+const firstRowCards = computed(() =>
+  allMetricCards.value.slice(0, 7),
+);
+
+const secondRowCards = computed(() =>
+  allMetricCards.value.slice(7, 14),
+);
 
 const availabilityItems = computed(() => {
   const total = totalCapacityDays.value;
@@ -733,11 +775,20 @@ onMounted(async () => {
   vertical-align: middle;
 }
 
+.section-header {
+  display: inline-flex;
+  align-items: center;
+  gap: 8px;
+
+  cursor: pointer;
+}
+
 .metrics-grid {
   display: grid;
-  grid-template-columns: repeat(5, minmax(0, 1fr));
-  gap: 12px;
+  grid-template-columns: repeat(7, minmax(0, 1fr));
+  gap: 10px;
   min-width: 0;
+  align-items: stretch;
 }
 
 .dashboard-panels {
@@ -778,53 +829,35 @@ onMounted(async () => {
   display: none;
 }
 
-@media (max-width: 1380px) {
+@media (max-width: 1650px) {
   .metrics-grid {
-    grid-template-columns: repeat(3, minmax(0, 1fr));
+    grid-template-columns: repeat(7, minmax(0, 1fr));
+    gap: 8px;
   }
 }
 
-@media (max-width: 1024px) {
-  .dashboard-panels {
-    grid-template-columns: 1fr;
+@media (max-width: 1450px) {
+  .metrics-grid {
+    grid-template-columns: repeat(5, minmax(0, 1fr));
   }
 }
 
-@media (max-width: 900px) {
+@media (max-width: 1100px) {
   .metrics-grid {
-    grid-template-columns: repeat(2, minmax(0, 1fr));
+    grid-template-columns: repeat(3, minmax(0, 1fr));
   }
 }
 
-@media (max-width: 640px) {
-  .dashboard-shell {
-    gap: 12px;
-  }
-
-  .dashboard-section-caption {
-    font-size: 16px;
-    line-height: 1.3;
-    margin-bottom: 0;
+@media (max-width: 768px) {
+  .metrics-grid {
+    grid-template-columns: repeat(2, minmax(0, 1fr));
   }
+}
 
+@media (max-width: 640px) {
   .metrics-grid {
     grid-template-columns: 1fr;
     gap: 10px;
   }
-
-  .dashboard-panels {
-    gap: 12px;
-  }
-
-  .panel-card {
-    padding: 14px;
-    min-height: 0;
-    border-radius: 12px;
-  }
-
-  .panel-title {
-    margin-bottom: 12px;
-    font-size: 17px;
-  }
 }
 </style>

+ 4 - 13
src/pages/dashboard/components/DashboardFiltersBar.vue

@@ -46,25 +46,16 @@
         @update:model-value="$emit('update:year', $event)"
       />
 
-      <q-btn-dropdown
+      <q-btn
         class="dashboard-export-btn"
         color="primary"
-        label="Exportar"
+        label="Exportar PDF"
         no-caps
         unelevated
         :disable="loading || exporting || !canExportReport"
         :loading="exporting"
-      >
-        <q-list dense>
-          <q-item v-close-popup clickable @click="$emit('export', 'xlsx')">
-            <q-item-section>XLSX</q-item-section>
-          </q-item>
-
-          <q-item v-close-popup clickable @click="$emit('export', 'pdf')">
-            <q-item-section>PDF</q-item-section>
-          </q-item>
-        </q-list>
-      </q-btn-dropdown>
+        @click="$emit('export', 'pdf')"
+      />
     </div>
   </div>
 </template>