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