feat: transaction list

This commit is contained in:
Jannat Patel
2025-09-23 11:26:46 +05:30
parent f239987043
commit c3702ee6d5
6 changed files with 412 additions and 8 deletions

View File

@@ -1,8 +1,8 @@
<template>
<div class="flex flex-col justify-between h-full">
<div class="flex flex-col justify-between h-full text-base">
<div>
<div class="flex itemsc-center justify-between">
<div class="text-xl font-semibold leading-none mb-1 text-ink-gray-9">
<div class="flex items-center justify-between">
<div class="text-xl font-semibold leading-none mb-2 text-ink-gray-9">
{{ __(label) }}
</div>
<Badge
@@ -12,7 +12,7 @@
theme="orange"
/>
</div>
<div class="text-xs text-ink-gray-5">
<div class="text-ink-gray-6 leading-5">
{{ __(description) }}
</div>
</div>

View File

@@ -44,7 +44,8 @@
? { fields: activeTab.fields }
: {}),
...(activeTab.label == 'Evaluators' ||
activeTab.label == 'Members'
activeTab.label == 'Members' ||
activeTab.label == 'Transactions'
? { 'onUpdate:show': (val) => (show = val), show }
: {}),
}"
@@ -80,6 +81,7 @@ import Categories from '@/components/Settings/Categories.vue'
import EmailTemplates from '@/components/Settings/EmailTemplates.vue'
import BrandSettings from '@/components/Settings/BrandSettings.vue'
import PaymentGateways from '@/components/Settings/PaymentGateways.vue'
import Transactions from '@/components/Settings/Transactions.vue'
import ZoomSettings from '@/components/Settings/ZoomSettings.vue'
import Badges from '@/components/Settings/Badges.vue'
@@ -168,8 +170,7 @@ const tabsStructure = computed(() => {
{
label: 'Configuration',
icon: 'CreditCard',
description:
'Configure the payment gateway and other payment related settings',
description: 'Manage all your payment related settings and defaults',
fields: [
{
label: 'Default Currency',
@@ -209,6 +210,12 @@ const tabsStructure = computed(() => {
template: markRaw(PaymentGateways),
description: 'Add and manage all your payment gateways',
},
{
label: 'Transactions',
icon: 'Landmark',
template: markRaw(Transactions),
description: 'View all your payment transactions',
},
],
},
{

View File

@@ -0,0 +1,152 @@
<template>
<Dialog
v-model="show"
:options="{
title: __('Transaction Details'),
size: '3xl',
}"
>
<template #body-content>
<div v-if="transactionData" class="text-base">
<div class="grid grid-cols-3 gap-5 mt-5">
<FormControl
:label="__('Payment Received')"
type="checkbox"
v-model="transactionData.payment_received"
/>
<FormControl
:label="__('Payment For Certificate')"
type="checkbox"
v-model="transactionData.payment_for_certificate"
/>
</div>
<div class="grid grid-cols-3 gap-5 mt-5">
<Link
:label="__('Member')"
doctype="User"
v-model="transactionData.member"
/>
<FormControl
:label="__('Billing Name')"
v-model="transactionData.billing_name"
/>
<Link
:label="__('Source')"
v-model="transactionData.source"
doctype="LMS Source"
/>
</div>
<div class="font-semibold mt-10">
{{ __('Payment Details') }}
</div>
<div class="grid grid-cols-3 gap-5 mt-5">
<Link
:label="__('Payment For Document Type')"
v-model="transactionData.payment_for_document_type"
doctype="DocType"
/>
<Link
:label="__('Payment For Document')"
v-model="transactionData.payment_for_document"
:doctype="transactionData.payment_for_document_type"
/>
<Link
:label="__('Address')"
v-model="transactionData.address"
doctype="Address"
/>
</div>
<div class="grid grid-cols-3 gap-5 mt-5">
<Link
:label="__('Currency')"
v-model="transactionData.currency"
doctype="Currency"
/>
<FormControl :label="__('Amount')" v-model="transactionData.amount" />
<FormControl
:label="__('Order ID')"
v-model="transactionData.order_id"
/>
</div>
<div class="grid grid-cols-3 gap-5 mt-5">
<FormControl :label="__('GSTIN')" v-model="transactionData.gstin" />
<FormControl :label="__('PAN')" v-model="transactionData.pan" />
<FormControl
:label="__('Payment ID')"
v-model="transactionData.payment_id"
/>
</div>
</div>
</template>
<template #actions="{ close }">
<div class="space-x-2 pb-5 float-right">
<Button @click="openDetails(close)">
{{ __('Open the ') }}
{{
transaction.payment_for_document_type == 'LMS Course'
? __('Course')
: __('Batch')
}}
</Button>
<Button variant="solid" @click="saveTransaction(close)">
{{ __('Save') }}
</Button>
</div>
</template>
</Dialog>
</template>
<script setup lang="ts">
import { Dialog, FormControl, Button } from 'frappe-ui'
import { useRouter } from 'vue-router'
import { ref, watch } from 'vue'
import Link from '@/components/Controls/Link.vue'
const show = defineModel<boolean>({ required: true, default: false })
const transactions = defineModel<any>('transactions')
const router = useRouter()
const showModal = defineModel('show')
const transactionData = ref<{ [key: string]: any } | null>(null)
const props = defineProps<{
transaction: { [key: string]: any } | null
}>()
watch(
() => props.transaction,
(newVal) => {
transactionData.value = newVal ? { ...newVal } : null
},
{ immediate: true }
)
const saveTransaction = (close: () => void) => {
transactions.value.setValue
.submit({
...transactionData.value,
})
.then(() => {
close()
})
}
const openDetails = (close: Function) => {
if (props.transaction) {
const docType = props.transaction.payment_for_document_type
const docName = props.transaction.payment_for_document
if (docType && docName) {
router.push({
name: docType == 'LMS Course' ? 'CourseDetail' : 'BatchDetail',
params: {
[docType == 'LMS Course' ? 'courseName' : 'batchName']: docName,
},
})
}
}
close()
showModal.value = false
}
</script>

View File

@@ -0,0 +1,241 @@
<template>
<div class="flex min-h-0 flex-col text-base">
<div class="mb-5">
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
{{ __(label) }}
</div>
<div class="text-ink-gray-6 leading-5">
{{ __(description) }}
</div>
</div>
<div class="flex items-center space-x-5 mb-4">
<FormControl
v-model="billingName"
:placeholder="__('Filter by Billing Name')"
/>
<Link
v-model="member"
doctype="User"
:placeholder="__('Filter by Member')"
/>
<FormControl
v-model="paymentReceived"
type="checkbox"
:label="__('Payment Received')"
/>
<FormControl
v-model="paymentForCertificate"
type="checkbox"
:label="__('Payment for Certificate')"
/>
</div>
<div v-if="transactions.data?.length" class="overflow-y-scroll">
<ListView
:columns="columns"
:rows="transactions.data"
row-key="name"
:options="{
showTooltip: false,
selectable: false,
onRowClick: (row: { [key: string]: any }) => {
openForm(row)
},
}"
>
<ListHeader
class="mb-2 grid items-center space-x-4 rounded bg-surface-gray-2 p-2"
>
<ListHeaderItem :item="item" v-for="item in columns">
<template #prefix="{ item }">
<FeatherIcon
v-if="item.icon"
:name="item.icon"
class="h-4 w-4 stroke-1.5"
/>
</template>
</ListHeaderItem>
</ListHeader>
<ListRows>
<ListRow :row="row" v-for="row in transactions.data">
<template #default="{ column, item }">
<ListRowItem :item="row[column.key]" :align="column.align">
<FormControl
v-if="
['payment_received', 'payment_for_certificate'].includes(
column.key
)
"
type="checkbox"
v-model="row[column.key]"
:disabled="true"
/>
<div v-else-if="column.key == 'amount'">
{{ getCurrencySymbol(row['currency']) }} {{ row[column.key] }}
</div>
<div v-else class="leading-5 text-sm">
{{ row[column.key] }}
</div>
</ListRowItem>
</template>
</ListRow>
</ListRows>
</ListView>
<div
v-if="transactions.data.length && transactions.hasNextPage"
class="flex justify-center mt-4"
>
<Button @click="transactions.next()">
<template #prefix>
<RefreshCw class="h-3 w-3 stroke-1.5" />
</template>
{{ __('Load More') }}
</Button>
</div>
</div>
</div>
<TransactionDetails
v-model="showForm"
:transaction="currentTransaction"
v-model:transactions="transactions"
v-model:show="show"
/>
</template>
<script setup lang="ts">
import {
Button,
createListResource,
ListView,
ListHeader,
ListHeaderItem,
FeatherIcon,
ListRows,
ListRow,
ListRowItem,
FormControl,
} from 'frappe-ui'
import { computed, ref, watch } from 'vue'
import { RefreshCw } from 'lucide-vue-next'
import TransactionDetails from './TransactionDetails.vue'
import Link from '@/components/Controls/Link.vue'
const showForm = ref(false)
const currentTransaction = ref<{ [key: string]: any } | null>(null)
const show = defineModel('show')
const billingName = ref(null)
const paymentReceived = ref(false)
const paymentForCertificate = ref(false)
const member = ref(null)
const props = defineProps({
label: {
type: String,
required: true,
},
description: {
type: String,
default: '',
},
})
const transactions = createListResource({
doctype: 'LMS Payment',
fields: [
'name',
'member',
'billing_name',
'source',
'payment_for_document_type',
'payment_for_document',
'payment_received',
'payment_for_certificate',
'currency',
'amount',
'order_id',
'payment_id',
'gstin',
'pan',
'address',
],
auto: true,
orderBy: 'modified desc',
})
watch(
[billingName, member, paymentReceived, paymentForCertificate],
([
newBillingName,
newMember,
newPaymentReceived,
newPaymentForCertificate,
]) => {
transactions.update({
filters: [
newBillingName ? [['billing_name', 'like', `%${newBillingName}%`]] : [],
newMember ? [['member', '=', newMember]] : [],
newPaymentReceived
? [['payment_received', '=', newPaymentReceived]]
: [],
newPaymentForCertificate
? [['payment_for_certificate', '=', newPaymentForCertificate]]
: [],
].flat(),
})
transactions.reload()
},
{ immediate: true }
)
const openForm = (transaction: { [key: string]: any }) => {
currentTransaction.value = transaction
showForm.value = true
}
const getCurrencySymbol = (currency: string) => {
const currencySymbols: Record<string, string> = {
USD: '$',
EUR: '€',
GBP: '£',
INR: '₹',
AED: 'د.إ',
CHF: 'Fr',
JPY: '¥',
AUD: '$',
}
return currencySymbols[currency] || currency
}
const columns = computed(() => {
return [
{
label: __('Billing Name'),
icon: 'user',
key: 'billing_name',
width: '30%',
},
{
label: __('Amount'),
icon: 'dollar-sign',
key: 'amount',
width: '20%',
align: 'right',
},
{
label: __('Payment Received'),
icon: 'check-circle',
key: 'payment_received',
width: '25%',
align: 'center',
},
{
label: __('Payment for Certificate'),
icon: 'award',
key: 'payment_for_certificate',
width: '25%',
align: 'center',
},
]
})
</script>