Files
radicall/resources/js/Admin/Pages/WithdrawRequests/index.vue
T

268 lines
10 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup>
import Master from "../../layouts/master.vue";
import { ref, reactive, watch } from 'vue';
import TableGeneric from "../../components/TableGeneric.vue";
import { useConfirm } from 'primevue/useconfirm';
import { useToast } from 'primevue/usetoast';
import { useWithdrawRequestStore } from "../../store/WithdrawRequest.js";
import UIModal from "../../components/ui/UIModal.vue";
const withdrawRequestStore = useWithdrawRequestStore();
const toast = useToast();
const props = defineProps({
withdrawRequests: {
type: Object,
},
stats: {
type: Object,
},
search: {
type: String
},
status: {
type: String
},
})
const withdrawRequests = ref(props.withdrawRequests ?? []);
const searchLoader = ref(false);
const actionLoader = ref(false);
const columns = ref(withdrawRequestStore.columns)
const statusOptions = [
'همه وضعیت ها',
'در انتظار بررسی',
'تایید شده',
'رد شده',
];
const queryParams = reactive({
search: props.search ?? '',
status: props.status ?? 'همه وضعیت ها',
})
const confirm = useConfirm();
watch(() => props.withdrawRequests, (newRequests) => {
if (newRequests) {
withdrawRequests.value = newRequests;
}
}, { deep: true, immediate: true });
const onPageChange = (event) => {
withdrawRequestStore.navigateToPage(event.page + 1);
};
const searchData = () => {
searchLoader.value = true;
withdrawRequestStore.search(queryParams).then(() => {
searchLoader.value = false;
});
}
const onSearchChange = (value) => {
queryParams.search = value;
searchData();
};
const formatCardNumber = (cardNumber) => cardNumber.replace(/(\d{4})(?=\d)/g, '$1-');
const showApproveConfirm = (data) => {
confirm.require({
group: 'deleteTemplate',
header: 'تایید درخواست برداشت',
message: `مبلغ ${Number(data.amount).toLocaleString('fa-IR')} تومان از کیف پول «${data.user?.fullname}» کسر و به ولت سایت منتقل می‌شود.`,
icon: 'fi fi-rr-info',
rejectProps: {
label: 'انصراف',
icon: 'fi fi-rr-ban',
outlined: true,
size: 'small',
class: '!text-xs !flex pt-1 !item-center',
iconClass: '!flex !text-xs',
},
acceptProps: {
label: 'تایید برداشت',
icon: 'fi fi-rr-check',
size: 'small',
class: '!text-xs !flex pt-1 !item-center',
iconClass: '!flex !text-xs',
},
accept: () => {
actionLoader.value = true;
withdrawRequestStore.approve(data.id)
.catch((error) => {
toast.add({ severity: 'error', summary: 'خطا', detail: error.response?.data?.message || 'خطا در تایید درخواست', life: 4000 });
})
.finally(() => {
actionLoader.value = false;
});
},
reject: () => {
}
});
}
const rejectModalVisible = ref(false);
const rejectingRequest = ref(null);
const rejectNote = ref('');
const openRejectModal = (data) => {
rejectingRequest.value = data;
rejectNote.value = '';
rejectModalVisible.value = true;
}
const submitReject = () => {
actionLoader.value = true;
withdrawRequestStore.reject(rejectingRequest.value.id, rejectNote.value)
.then(() => {
rejectModalVisible.value = false;
})
.catch((error) => {
toast.add({ severity: 'error', summary: 'خطا', detail: error.response?.data?.message || 'خطا در رد درخواست', life: 4000 });
})
.finally(() => {
actionLoader.value = false;
});
}
</script>
<template>
<master>
<div class="mb-4 grid grid-cols-2a justify-between items-center bg-white border-[1px] border-client-gray/20 py-4 px-4 rounded-2xl">
<h2 class="font-bold text-client-Fontcolor">{{ withdrawRequestStore.indexPageTitle }}</h2>
</div>
<div class="mb-4 grid grid-cols-3 max-lg:grid-cols-1 gap-4">
<div class="bg-white border-[1px] border-client-gray/20 rounded-2xl p-4 flex flex-col gap-2">
<span class="text-xs text-gray-500">درخواست‌های در انتظار بررسی</span>
<strong class="text-lg text-client-Fontcolor">{{ Number(stats.pending_count).toLocaleString('fa-IR') }} درخواست</strong>
</div>
<div class="bg-white border-[1px] border-client-gray/20 rounded-2xl p-4 flex flex-col gap-2">
<span class="text-xs text-gray-500">مجموع مبلغ در انتظار برداشت</span>
<strong class="text-lg text-client-Fontcolor">{{ Number(stats.pending_amount).toLocaleString('fa-IR') }} تومان</strong>
</div>
<div class="bg-white border-[1px] border-client-gray/20 rounded-2xl p-4 flex flex-col gap-2">
<span class="text-xs text-gray-500">موجودی ولت سایت</span>
<strong class="text-lg text-client-Fontcolor">{{ Number(stats.system_wallet_balance).toLocaleString('fa-IR') }} تومان</strong>
</div>
</div>
<div class="card border-[1px] border-client-gray/20 bg-white rounded-2xl">
<TableGeneric
:data="withdrawRequests.data"
:columns="columns"
:selectable="false"
:pagination="withdrawRequests.meta"
:searchable="true"
:searchValue="queryParams.search"
:model-value="[]"
@page-change="onPageChange"
@search-change="onSearchChange"
>
<template #toolbar-actions>
<div class="grid grid-cols-a gap-4 justify-start">
<Dropdown
v-model="queryParams.status"
:options="statusOptions"
placeholder="انتخاب وضعیت..."
class="!text-xs"
@change="searchData"
/>
</div>
</template>
<template #column-user="{ data }">
<div class="flex flex-col gap-1">
<span class="text-sm">{{ data.user?.fullname }}</span>
<span class="text-xs text-gray-500">{{ data.user?.level }} - {{ data.user?.phone }}</span>
</div>
</template>
<template #column-bank_card="{ data }">
<div v-if="data.bank_card" class="flex flex-col gap-1">
<pre dir="ltr" class="text-right text-xs">{{ formatCardNumber(data.bank_card.card_number) }}</pre>
<span class="text-xs text-gray-500">{{ data.bank_card.holder_name }}</span>
</div>
<span v-else class="text-xs text-gray-500">حذف شده</span>
</template>
<template #column-status="{ data }">
<div class="flex flex-col gap-1 items-start">
<Tag
class="!text-xs"
:value="data.status"
:severity="withdrawRequestStore.getStatusSeverity(data.status)"
/>
<span v-if="data.admin_note" class="text-xs text-gray-500">{{ data.admin_note }}</span>
</div>
</template>
<template #column-actions="{ data }">
<div v-if="data.status === 'در انتظار بررسی'" class="grid grid-cols-2a justify-start gap-2">
<Button
label="تایید"
size="small"
severity="success"
class="!text-xs"
:disabled="actionLoader"
@click="showApproveConfirm(data)"
/>
<Button
label="رد"
size="small"
severity="danger"
variant="outlined"
class="!text-xs"
:disabled="actionLoader"
@click="openRejectModal(data)"
/>
</div>
</template>
</TableGeneric>
</div>
<UIModal
v-model:visible="rejectModalVisible"
title="رد درخواست برداشت"
size="small"
>
<div class="flex flex-col gap-3">
<p class="text-sm text-gray-600">
درخواست برداشت «{{ rejectingRequest ? Number(rejectingRequest.amount).toLocaleString('fa-IR') : '' }} تومان» کاربر «{{ rejectingRequest?.user?.fullname }}» رد می‌شود.
</p>
<textarea
v-model="rejectNote"
rows="4"
placeholder="توضیح برای کاربر (اختیاری)"
class="w-full border border-gray-200 rounded-xl p-3 text-sm outline-none focus:border-gray-400 resize-y"
></textarea>
<div class="flex items-center justify-start gap-2">
<Button
label="رد درخواست"
size="small"
severity="danger"
class="!text-xs"
:loading="actionLoader"
:disabled="actionLoader"
@click="submitReject"
/>
<Button
label="انصراف"
size="small"
severity="secondary"
variant="outlined"
class="!text-xs"
:disabled="actionLoader"
@click="rejectModalVisible = false"
/>
</div>
</div>
</UIModal>
</master>
</template>