Files
manja_dev_ui/src/views/wallet/WalletDashboard.vue
T
Wian Drs 484f89e2ee
NPM Installation / build (16.x, ubuntu-latest) (push) Waiting to run
NPM Installation / build (16.x, windows-latest) (push) Waiting to run
NPM Installation / build (17.x, ubuntu-latest) (push) Waiting to run
NPM Installation / build (17.x, windows-latest) (push) Waiting to run
NPM Installation / build (18.x, ubuntu-latest) (push) Waiting to run
NPM Installation / build (18.x, windows-latest) (push) Waiting to run
Giant Update regsiter
2026-08-01 11:41:31 +07:00

79 lines
8.1 KiB
Vue

<script setup>
import { computed, onMounted, reactive, ref, watch } from 'vue'
import BaseResponsiveDataView from '@/components/base/BaseResponsiveDataView.vue'
import { getMyWallets, getWalletLedger, withdrawWallet } from '@/services/walletService'
import { showError, showSuccess } from '@/utils/swal'
const loading = ref(true), wallets = ref([]), activeId = ref(null), ledger = ref([]), modal = ref(false), saving = ref(false)
const pagination = ref({ current_page: 1, last_page: 1, per_page: 10, total: 0 }), query = ref({ page: 1, per_page: 10 })
const form = reactive({ amount: '', bank_code: '', account_number: '', account_name: '' })
const active = computed(() => wallets.value.find((item) => item.id === activeId.value) || wallets.value[0])
const columns = [{ key: 'date', label: 'Tanggal' }, { key: 'number', label: 'Referensi' }, { key: 'type', label: 'Jenis' }, { key: 'debit', label: 'Debit' }, { key: 'credit', label: 'Kredit' }, { key: 'balance', label: 'Saldo' }]
const money = (value) => `Rp ${Number(value || 0).toLocaleString('id-ID')}`
async function loadBalances() {
loading.value = true
try {
wallets.value = (await getMyWallets())?.data || []
if (!wallets.value.some((item) => item.id === activeId.value)) activeId.value = wallets.value[0]?.id
await loadLedger()
} catch (error) { showError(error?.response?.data?.message || 'Gagal memuat saldo.') }
finally { loading.value = false }
}
async function loadLedger(params = query.value) {
if (!active.value?.ledger_available) {
ledger.value = []; pagination.value = { current_page: 1, last_page: 1, per_page: 10, total: 0 }; return
}
query.value = { ...query.value, ...params }
const response = await getWalletLedger(active.value.id, query.value), rows = response?.data?.data || []
ledger.value = rows.map((item) => ({ ...item, date: new Date(item.created_at).toLocaleString('id-ID'), number: item.transaction?.transaction_number, type: item.transaction?.type, debit: item.entry_type === 'debit' ? money(item.amount) : '-', credit: item.entry_type === 'credit' ? money(item.amount) : '-', balance: money(item.balance_after) }))
pagination.value = { current_page: response?.data?.current_page || 1, last_page: response?.data?.last_page || 1, per_page: response?.data?.per_page || 10, total: response?.data?.total || 0 }
}
function openWithdrawal() { Object.assign(form, { amount: '', bank_code: '', account_number: '', account_name: '' }); modal.value = true }
async function submitWithdrawal() {
saving.value = true
try { await withdrawWallet(active.value.id, form); modal.value = false; showSuccess('Permintaan penarikan settlement berhasil dibuat.'); await loadBalances() }
catch (error) { showError(error?.response?.data?.message || 'Penarikan saldo settlement gagal.') }
finally { saving.value = false }
}
watch(activeId, loadLedger)
onMounted(loadBalances)
</script>
<template>
<div class="brdvx-page-container">
<div class="brdvx-page-header"><h5 class="mb-0 fw-semibold">Saldo Deposit</h5><small class="text-body-secondary">Saldo layanan user, settlement tenant, dan fasilitas kredit voucher pada tenant aktif</small></div>
<div v-if="loading" class="brdvx-loading-state"><CSpinner color="primary" /><p class="brdvx-loading-text">Memuat data...</p></div>
<template v-else>
<div class="wallet-grid">
<button v-for="balance in wallets" :key="balance.id" type="button" class="wallet-card" :class="{ active: active?.id === balance.id, credit: balance.balance_kind === 'credit' }" @click="activeId = balance.id">
<span>{{ (balance.label || 'Saldo Deposit').toUpperCase() }}</span><strong>{{ money(balance.available_balance) }}</strong>
<template v-if="balance.balance_kind === 'credit'"><small>Limit {{ money(balance.credit_limit) }} · Terpakai {{ money(balance.used_credit) }}</small><small class="usage-note">Khusus pembelian voucher hotspot</small></template>
<template v-else><small>Pending {{ money(balance.pending_balance) }} · IDR</small><small class="usage-note">{{ balance.usage_note }}</small></template>
</button>
</div>
<div class="wallet-actions">
<button v-if="active?.balance_kind === 'deposit'" type="button" class="wallet-action" disabled><span class="wallet-action-icon topup"><CIcon icon="cil-data-transfer-down" size="xl" /></span><span><strong>Isi Saldo Deposit</strong><small>Tambahkan saldo untuk transaksi layanan tenant</small></span></button>
<button v-if="active?.can_withdraw" type="button" class="wallet-action" @click="openWithdrawal"><span class="wallet-action-icon withdraw"><CIcon icon="cil-data-transfer-up" size="xl" /></span><span><strong>Tarik Settlement</strong><small>Kirim saldo settlement tenant ke rekening bank</small></span></button>
<div v-if="active?.balance_kind === 'credit'" class="credit-information"><CIcon icon="cil-info" size="xl" /><span><strong>Fasilitas Kredit Voucher</strong><small>Sisa kredit berkurang saat membeli voucher dan bertambah kembali setelah tagihan agen dibayar.</small></span></div>
</div>
<template v-if="active?.ledger_available"><h6 class="mt-4">Mutasi {{ active?.label }}</h6><BaseResponsiveDataView :columns="columns" :items="ledger" server-side :pagination="pagination" @query-change="loadLedger" /></template>
<CAlert v-else-if="active?.balance_kind === 'credit'" color="info" class="mt-4 mb-0">Riwayat pemakaian saldo kredit tersedia pada transaksi penjualan Voucher Hotspot.</CAlert>
</template>
</div>
<CModal :visible="modal" @close="modal = false"><CModalHeader><CModalTitle>Tarik Saldo Settlement</CModalTitle></CModalHeader><CModalBody><CFormLabel>Nominal</CFormLabel><CFormInput v-model.number="form.amount" type="number" min="1" class="mb-3" /><CFormLabel>Bank</CFormLabel><CFormInput v-model="form.bank_code" class="mb-2" /><CFormLabel>Nomor Rekening</CFormLabel><CFormInput v-model="form.account_number" class="mb-2" /><CFormLabel>Nama Pemilik</CFormLabel><CFormInput v-model="form.account_name" /></CModalBody><CModalFooter><CButton color="secondary" @click="modal = false">Batal</CButton><CButton color="primary" :disabled="saving" @click="submitWithdrawal">Proses</CButton></CModalFooter></CModal>
</template>
<style scoped>
.wallet-grid { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,280px),400px)); justify-content:center; gap:1rem; }
.wallet-card { display:flex; width:100%; max-width:400px; min-height:150px; flex-direction:column; justify-content:center; padding:1.25rem; color:white; text-align:left; background:linear-gradient(135deg,#f97316,#ff8c00 58%,#f9b115); border:3px solid transparent; border-radius:1rem; box-shadow:0 .5rem 1rem rgba(249,115,22,.22); }
.wallet-card.active { border-color:rgba(255,255,255,.88); }.wallet-card.credit { background:linear-gradient(135deg,#5856d6,#3399ff); box-shadow:0 .5rem 1rem rgba(51,153,255,.2); }
.wallet-card span,.wallet-card small { opacity:.84; }.wallet-card strong { margin:.4rem 0; font-size:1.5rem; }.usage-note { margin-top:.4rem; font-size:.68rem; }
.wallet-actions { display:grid; grid-template-columns:repeat(auto-fit,minmax(min(100%,230px),300px)); justify-content:center; gap:.75rem; max-width:800px; margin:1.25rem auto 0; }
.wallet-action,.credit-information { display:flex; min-height:78px; align-items:center; gap:.75rem; padding:.75rem; text-align:left; border-radius:.85rem; }
.wallet-action { color:var(--cui-body-color); background:var(--cui-body-bg); border:1px solid var(--cui-border-color); box-shadow:0 .25rem .75rem rgba(0,0,0,.06); }.wallet-action:disabled { cursor:not-allowed; opacity:.55; }
.wallet-action>span:last-child,.credit-information span { display:flex; min-width:0; flex-direction:column; }.wallet-action small,.credit-information small { margin-top:.15rem; font-size:.7rem; line-height:1.25; }
.wallet-action-icon { display:inline-flex; width:44px; height:44px; flex:0 0 44px; align-items:center; justify-content:center; color:white; border-radius:.75rem; }.wallet-action-icon.topup { background:linear-gradient(135deg,#f97316,#f9b115); }.wallet-action-icon.withdraw { background:linear-gradient(135deg,#2eb85c,#1f9d55); }
.credit-information { color:var(--cui-info-text-emphasis); background:var(--cui-info-bg-subtle); border:1px solid var(--cui-info-border-subtle); }
</style>