瀏覽代碼

feat: dados reais na visão geral do Financeiro (#CB017)

FinancialPage consome /financial-dashboard/overview e popula os cards,
últimas movimentações e desempenho por franquia. Remove o DevBanner.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ebagabee 2 天之前
父節點
當前提交
1fb0dfcd7a

+ 6 - 0
src/api/financial_dashboard.js

@@ -0,0 +1,6 @@
+import api from "src/api";
+
+export const getFinancialOverview = async () => {
+  const { data } = await api.get("/financial-dashboard/overview");
+  return data.payload;
+};

+ 24 - 1
src/components/financial/FranchisePerformanceCard.vue

@@ -10,6 +10,7 @@
       dense
       :rows="rows"
       :columns="columns"
+      :loading="loading"
       row-key="id"
       :pagination="{ rowsPerPage: 0 }"
       hide-pagination
@@ -17,6 +18,22 @@
       class="q-mt-md"
       style="max-height: 280px"
     >
+      <template #body-cell-revenue="{ row }">
+        <q-td align="left">{{ formatCurrency(row.revenue) }}</q-td>
+      </template>
+
+      <template #body-cell-expense="{ row }">
+        <q-td align="left">{{ formatCurrency(row.expense) }}</q-td>
+      </template>
+
+      <template #body-cell-performance="{ row }">
+        <q-td align="left">
+          <span :class="row.performance >= 0 ? 'text-positive' : 'text-negative'">
+            {{ row.performance }}%
+          </span>
+        </q-td>
+      </template>
+
       <template #no-data>
         <div class="full-width text-center text-caption text-foreground q-py-md">
           Nenhum dado encontrado
@@ -27,7 +44,13 @@
 </template>
 
 <script setup>
-const rows = [];
+defineProps({
+  rows: { type: Array, default: () => [] },
+  loading: { type: Boolean, default: false },
+});
+
+const formatCurrency = (value) =>
+  new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(value ?? 0);
 
 const columns = [
   { name: "franchise", label: "Franquia", field: "franchise", align: "left" },

+ 12 - 1
src/components/financial/LastTransactionsCard.vue

@@ -10,6 +10,7 @@
       dense
       :rows="rows"
       :columns="columns"
+      :loading="loading"
       row-key="id"
       :pagination="{ rowsPerPage: 0 }"
       hide-pagination
@@ -17,6 +18,10 @@
       class="transactions-table q-mt-md"
       style="max-height: 280px"
     >
+      <template #body-cell-value="{ row }">
+        <q-td align="left">{{ formatCurrency(row.value) }}</q-td>
+      </template>
+
       <template #body-cell-status="{ row }">
         <q-td align="left">
           <q-badge
@@ -37,7 +42,13 @@
 </template>
 
 <script setup>
-const rows = [];
+defineProps({
+  rows: { type: Array, default: () => [] },
+  loading: { type: Boolean, default: false },
+});
+
+const formatCurrency = (value) =>
+  new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(value ?? 0);
 
 const columns = [
   {

+ 54 - 19
src/pages/financial/FinancialPage.vue

@@ -1,7 +1,6 @@
 <template>
   <div>
     <DefaultHeaderPage title="Financeiro" :show-filter-icon="false" />
-    <div class="q-px-md"><DevBanner /></div>
     <div class="row justify-end q-px-md q-mb-xs">
       <q-btn
         flat
@@ -20,25 +19,25 @@
       <FinancialCard
         title="Fundo de Marketing"
         icon="mdi-cash-multiple"
-        :financial-value="0"
-        :percentage="0"
-        description="Estável"
+        :financial-value="overview.marketing_fund.value"
+        :percentage="overview.marketing_fund.percentage"
+        :description="trend(overview.marketing_fund.percentage)"
         :hide-values="!showValues"
       />
       <FinancialCard
         title="Receita de Mensalidade"
         icon="mdi-cash-multiple"
-        :financial-value="0"
-        :percentage="0"
-        description="Estável"
+        :financial-value="overview.monthly_revenue.value"
+        :percentage="overview.monthly_revenue.percentage"
+        :description="trend(overview.monthly_revenue.percentage)"
         :hide-values="!showValues"
       />
       <FinancialCard
         title="TBR Total"
         icon="mdi-cash-multiple"
-        :financial-value="0"
-        :percentage="0"
-        description="Estável"
+        :financial-value="overview.tbr_total.value"
+        :percentage="overview.tbr_total.percentage"
+        :description="trend(overview.tbr_total.percentage)"
         :hide-values="!showValues"
       />
       <TreasuryCard />
@@ -48,45 +47,81 @@
       <FinancialCard
         title="Contas a Pagar"
         icon="mdi-cash-minus"
-        :financial-value="0"
-        :integer="0"
+        :financial-value="overview.accounts_payable.value"
+        :integer="overview.accounts_payable.count"
         integer-label="pagamentos pendentes"
         :hide-values="!showValues"
       />
       <FinancialCard
         title="Contas a Receber"
         icon="mdi-cash-plus"
-        :financial-value="0"
-        :integer="0"
+        :financial-value="overview.accounts_receivable.value"
+        :integer="overview.accounts_receivable.count"
         integer-label="pagamentos pendentes"
         :hide-values="!showValues"
       />
       <FinancialCard
         title="Saldo Bancário Total"
         icon="mdi-bank-outline"
-        :financial-value="0"
+        :financial-value="overview.bank_balance_total"
         :hide-values="!showValues"
       />
     </div>
 
     <div class="row q-px-md q-gutter-md q-mt-none">
-      <LastTransactionsCard />
-      <FranchisePerformanceCard />
+      <LastTransactionsCard :rows="overview.last_transactions" :loading="loading" />
+      <FranchisePerformanceCard :rows="overview.franchise_performance" :loading="loading" />
     </div>
   </div>
 </template>
 
 <script setup>
-import { ref, watch } from "vue";
+import { ref, watch, onMounted } from "vue";
+import { Notify } from "quasar";
 import DefaultHeaderPage from "src/components/layout/DefaultHeaderPage.vue";
-import DevBanner from "src/components/shared/DevBanner.vue";
 import FinancialCard from "src/components/financial/FinancialCard.vue";
 import TreasuryCard from "src/components/financial/TreasuryCard.vue";
 import LastTransactionsCard from "src/components/financial/LastTransactionsCard.vue";
 import FranchisePerformanceCard from "src/components/financial/FranchisePerformanceCard.vue";
+import { getFinancialOverview } from "src/api/financial_dashboard";
 
 const STORAGE_KEY = "financial_show_values";
 const showValues = ref(localStorage.getItem(STORAGE_KEY) !== "false");
 
 watch(showValues, (val) => localStorage.setItem(STORAGE_KEY, String(val)));
+
+const loading = ref(false);
+const overview = ref(emptyOverview());
+
+function emptyOverview() {
+  return {
+    marketing_fund: { value: 0, percentage: 0 },
+    monthly_revenue: { value: 0, percentage: 0 },
+    tbr_total: { value: 0, percentage: 0 },
+    accounts_payable: { value: 0, count: 0 },
+    accounts_receivable: { value: 0, count: 0 },
+    bank_balance_total: 0,
+    last_transactions: [],
+    franchise_performance: [],
+  };
+}
+
+const trend = (percentage) => {
+  if (percentage > 0) return "Em alta";
+  if (percentage < 0) return "Em queda";
+  return "Estável";
+};
+
+const fetchOverview = async () => {
+  loading.value = true;
+  try {
+    overview.value = await getFinancialOverview();
+  } catch {
+    Notify.create({ message: "Não foi possível carregar os dados financeiros.", type: "negative" });
+  } finally {
+    loading.value = false;
+  }
+};
+
+onMounted(fetchOverview);
 </script>