Quellcode durchsuchen

feat: add ordenacao na tabela de repasses por unidade

Gustavo Mantovani vor 1 Monat
Ursprung
Commit
d589ba7b6d
1 geänderte Dateien mit 263 neuen und 33 gelöschten Zeilen
  1. 263 33
      src/pages/dashboard/components/DashboardPayoutTable.vue

+ 263 - 33
src/pages/dashboard/components/DashboardPayoutTable.vue

@@ -3,23 +3,115 @@
     <table class="payout-table">
       <thead>
         <tr>
-          <th>Unidade</th>
-          <th>Reservas</th>
-          <th>Valor</th>
+          <th
+            v-for="(column, index) in columns"
+            :key="column.key"
+          >
+            <div class="header-wrapper">
+              <q-icon
+                class="cursor-pointer sort-icon q-my-md"
+                :name="
+                  column.direction === 'desc'
+                    ? 'south'
+                    : 'north'
+                "
+                size="15px"
+                :color="
+                  column.direction
+                    ? 'primary'
+                    : '#b0b0b0'
+                "
+                @click="toggleSort(column)"
+              />
+
+              <div class="header-content">
+                <q-icon
+                  v-if="index > 0"
+                  class="cursor-pointer move-icon"
+                  name="chevron_left"
+                  size="18px"
+                  color="#888"
+                  @click="moveLeft(index)"
+                />
+
+                <span class="header-label">
+                  {{ column.label }}
+                </span>
+
+                <q-icon
+                  v-if="
+                    index <
+                    columns.length - 1
+                  "
+                  class="cursor-pointer move-icon"
+                  name="chevron_right"
+                  size="18px"
+                  color="#888"
+                  @click="moveRight(index)"
+                />
+              </div>
+            </div>
+          </th>
         </tr>
       </thead>
 
       <tbody>
-        <tr v-for="item in rows" :key="item.property_id">
-          <td>{{ item.property_label }}</td>
-          <td>{{ formatInteger(item.reservations_count) }}</td>
-          <td>{{ formatCurrency(item.final_payout_amount) }}</td>
+        <tr
+          v-for="item in sortedRows"
+          :key="item.property_id"
+        >
+          <td
+            v-for="column in columns"
+            :key="column.key"
+          >
+            <template
+              v-if="
+                column.key ===
+                'final_payout_amount'
+              "
+            >
+              {{
+                formatCurrency(
+                  item[column.key]
+                )
+              }}
+            </template>
+
+            <template
+              v-else-if="
+                column.key ===
+                'reservations_count'
+              "
+            >
+              {{
+                formatInteger(
+                  item[column.key]
+                )
+              }}
+            </template>
+
+            <template v-else>
+              {{ item[column.key] }}
+            </template>
+          </td>
         </tr>
 
         <tr class="payout-total-row">
           <td>Total</td>
-          <td>{{ formatInteger(totalReservations) }}</td>
-          <td>{{ formatCurrency(totalValue) }}</td>
+
+          <td>
+            {{
+              formatInteger(
+                totalReservations
+              )
+            }}
+          </td>
+
+          <td>
+            {{
+              formatCurrency(totalValue)
+            }}
+          </td>
         </tr>
       </tbody>
     </table>
@@ -27,31 +119,143 @@
 </template>
 
 <script setup>
-defineProps({
+import {
+  computed,
+  ref,
+} from "vue";
+
+const props = defineProps({
   rows: {
     type: Array,
     default: () => [],
   },
+
   totalReservations: {
     type: Number,
     default: 0,
   },
+
   totalValue: {
     type: Number,
     default: 0,
   },
 });
 
-const formatCurrency = (value) => {
-  return new Intl.NumberFormat("pt-BR", {
-    style: "currency",
-    currency: "BRL",
-    minimumFractionDigits: 2,
-  }).format(Number(value ?? 0));
+const columns = ref([
+  {
+    key: "property_label",
+    label: "Unidade",
+    direction: null,
+  },
+
+  {
+    key: "reservations_count",
+    label: "Reservas",
+    direction: null,
+  },
+
+  {
+    key: "final_payout_amount",
+    label: "Valor",
+    direction: null,
+  },
+]);
+
+const toggleSort = (
+  column
+) => {
+  column.direction =
+    column.direction === "asc"
+      ? "desc"
+      : "asc";
+};
+
+const moveLeft = (index) => {
+  if (index === 0) return;
+
+  [
+    columns.value[index - 1],
+    columns.value[index],
+  ] = [
+    columns.value[index],
+    columns.value[index - 1],
+  ];
+};
+
+const moveRight = (index) => {
+  if (
+    index ===
+    columns.value.length - 1
+  ) {
+    return;
+  }
+
+  [
+    columns.value[index + 1],
+    columns.value[index],
+  ] = [
+    columns.value[index],
+    columns.value[index + 1],
+  ];
+};
+
+const sortedRows = computed(() => {
+  const activeSorts =
+    columns.value.filter(
+      (column) => column.direction
+    );
+
+  if (!activeSorts.length) {
+    return [...props.rows];
+  }
+
+  return [...props.rows].sort(
+    (a, b) => {
+      for (const sort of activeSorts) {
+        const direction =
+          sort.direction === "asc"
+            ? 1
+            : -1;
+
+        const valueA =
+          a[sort.key];
+
+        const valueB =
+          b[sort.key];
+
+        if (valueA > valueB) {
+          return direction;
+        }
+
+        if (valueA < valueB) {
+          return -direction;
+        }
+      }
+
+      return 0;
+    }
+  );
+});
+
+const formatCurrency = (
+  value
+) => {
+  return new Intl.NumberFormat(
+    "pt-BR",
+    {
+      style: "currency",
+      currency: "BRL",
+      minimumFractionDigits: 2,
+    }
+  ).format(Number(value ?? 0));
 };
 
-const formatInteger = (value) => {
-  return Number(value ?? 0).toLocaleString("pt-BR");
+const formatInteger = (
+  value
+) => {
+  return Number(
+    value ?? 0
+  ).toLocaleString("pt-BR");
 };
 </script>
 
@@ -70,20 +274,10 @@ const formatInteger = (value) => {
   padding: 10px 16px;
   border-bottom: 1px solid #aaaaaa;
   color: #273136;
-  text-align: left;
   vertical-align: middle;
-}
-
-.payout-table th:nth-child(2),
-.payout-table td:nth-child(2) {
   text-align: center;
 }
 
-.payout-table th:nth-child(3),
-.payout-table td:nth-child(3) {
-  text-align: right;
-}
-
 .payout-table th {
   font-size: 17px !important;
   font-weight: 700;
@@ -95,6 +289,43 @@ const formatInteger = (value) => {
   font-weight: 700;
   border-bottom: 0;
   color: #4d4e4e;
+  text-align: center;
+}
+
+.header-wrapper {
+  display: flex;
+  flex-direction: column;
+  align-items: center;
+  gap: 2px;
+}
+
+.header-content {
+  display: flex;
+  align-items: center;
+  gap: 4px;
+}
+
+.header-label {
+  line-height: 1;
+}
+
+.sort-icon {
+  transition: 0.2s ease;
+}
+
+.move-icon {
+  opacity: 0.7;
+  transition: 0.2s ease;
+}
+
+.move-icon:hover,
+.sort-icon:hover {
+  opacity: 1;
+  transform: scale(1.08);
+}
+
+.cursor-pointer {
+  cursor: pointer;
 }
 
 @media (max-width: 640px) {
@@ -108,14 +339,13 @@ const formatInteger = (value) => {
     font-size: 13px;
   }
 
-  .payout-table th:first-child,
-  .payout-table td:first-child {
-    min-width: 132px;
-  }
-
   .payout-table th,
   .payout-total-row td {
     font-size: 14px !important;
   }
+
+  .header-content {
+    gap: 2px;
+  }
 }
 </style>