| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159 |
- <template>
- <q-dialog ref="dialogRef" maximized transition-show="slide-up" transition-hide="slide-down" @hide="onDialogHide">
- <q-card class="bg-page column no-wrap" style="height: 100dvh;">
- <div class="movements-header row items-center bg-white q-px-sm">
- <q-btn flat round dense icon="mdi-arrow-left" color="text" @click="onDialogCancel" />
- <q-space />
- <span class="text-subtitle1 text-weight-bold gradient-diarista">{{ $t('provider.payments.movements_title') }}</span>
- <q-space />
- <div style="width: 40px" />
- </div>
- <q-scroll-area class="col" style="flex: 1 1 0;">
- <div v-if="loading" class="flex flex-center q-py-xl">
- <q-spinner color="primary" size="40px" />
- </div>
- <div v-else class="q-pa-md">
- <q-card
- v-for="item in movements"
- :key="item.id"
- class="movement-card bg-surface shadow-card q-mb-sm"
- :flat="false"
- >
- <q-card-section class="q-pa-sm">
- <div class="row items-center no-wrap q-gutter-x-sm">
- <q-avatar size="40px" :color="movBgColor(item.type)" :text-color="movIconColor(item.type)">
- <q-icon :name="movIcon(item.type)" size="20px" />
- </q-avatar>
- <div class="col column">
- <span class="mov-label">{{ movLabel(item) }}</span>
- <span class="mov-date">{{ formatMovDate(item.created_at) }}</span>
- </div>
- <span class="mov-value" :class="item.gross_amount >= 0 ? 'text-success' : 'text-error'">
- {{ (item.gross_amount >= 0 ? '+' : '') + formatCurrency(Math.abs(item.gross_amount)) }}
- </span>
- </div>
- </q-card-section>
- </q-card>
- </div>
- </q-scroll-area>
- </q-card>
- </q-dialog>
- </template>
- <script setup>
- import { ref, onMounted, computed } from 'vue';
- import { useDialogPluginComponent, useQuasar } from 'quasar';
- import { useI18n } from 'vue-i18n';
- import { formatCurrency } from 'src/helpers/utils';
- import { getPaymentSplits } from 'src/api/paymentSplit';
- import { getWithdrawals } from 'src/api/providerWithdrawal';
- defineEmits([...useDialogPluginComponent.emits]);
- const { dialogRef, onDialogHide, onDialogCancel } = useDialogPluginComponent();
- const { t } = useI18n();
- const $q = useQuasar();
- const loading = ref(true);
- const allMovements = ref([]);
- const movements = computed(() => {
- return [...allMovements.value].sort((a, b) =>
- new Date(b.created_at) - new Date(a.created_at)
- );
- });
- const loadMovements = async () => {
- loading.value = true;
- try {
- const [splits, withdrawals] = await Promise.all([
- getPaymentSplits(),
- getWithdrawals(),
- ]);
- const serviceItems = (splits || []).map((s) => ({
- id: `split-${s.id}`,
- type: 'servico',
- client_name: s.client_name,
- gross_amount: parseFloat(s.gross_amount || 0),
- created_at: s.created_at,
- }));
- const withdrawalItems = (withdrawals || []).map((w) => ({
- id: `withdrawal-${w.id}`,
- type: 'saque',
- gross_amount: -(parseFloat(w.gross_amount || 0)),
- created_at: w.created_at,
- }));
- allMovements.value = [...serviceItems, ...withdrawalItems];
- } catch (error) {
- $q.notify({ type: 'negative', message: error?.response?.data?.message || error.message });
- } finally {
- loading.value = false;
- }
- };
- const movIcon = (type) => {
- const map = { saque: 'mdi-bank-transfer-out', servico: 'mdi-broom' };
- return map[type] ?? 'mdi-circle';
- };
- const movBgColor = (type) => {
- const map = { saque: 'secondary-bg', servico: 'success-bg' };
- return map[type] ?? 'neutral-bg';
- };
- const movIconColor = (type) => {
- const map = { saque: 'secondary', servico: 'success' };
- return map[type] ?? 'text';
- };
- const movLabel = (item) => {
- if (item.type === 'servico') return t('provider.payments.mov_servico') + (item.client_name ? ` - ${item.client_name}` : '');
- if (item.type === 'saque') return t('provider.payments.mov_saque');
- return '';
- };
- const formatMovDate = (dateStr) => {
- if (!dateStr) return '';
- try { return new Date(dateStr).toLocaleDateString('pt-BR', { day: '2-digit', month: '2-digit', year: 'numeric' }); }
- catch { return dateStr; }
- };
- onMounted(() => {
- loadMovements();
- });
- </script>
- <style scoped lang="scss">
- .movements-header {
- padding-top: calc(env(safe-area-inset-top) + 12px);
- padding-bottom: 12px;
- min-height: calc(50px + env(safe-area-inset-top));
- box-shadow: 0px 1px 8px rgba(0, 0, 0, 0.08);
- }
- .movement-card {
- border-radius: 12px;
- }
- .mov-label {
- font-size: 13px;
- font-weight: 600;
- color: #3a3a4a;
- }
- .mov-date {
- font-size: 11px;
- color: #888;
- margin-top: 2px;
- }
- .mov-value {
- font-size: 14px;
- font-weight: 700;
- white-space: nowrap;
- }
- </style>
|