refactor: payment settings
This commit is contained in:
153
frontend/src/components/Settings/PaymentGatewayDetails.vue
Normal file
153
frontend/src/components/Settings/PaymentGatewayDetails.vue
Normal file
@@ -0,0 +1,153 @@
|
||||
<template>
|
||||
<Dialog
|
||||
v-model="show"
|
||||
:options="{
|
||||
title:
|
||||
gatewayID === 'new'
|
||||
? __('New Payment Gateway')
|
||||
: __('Edit Payment Gateway'),
|
||||
size: '3xl',
|
||||
}"
|
||||
>
|
||||
<template #body-content>
|
||||
<SettingFields
|
||||
v-if="gatewayID != 'new'"
|
||||
:fields="paymentGateway.data.fields"
|
||||
:data="paymentGateway.data.data"
|
||||
class="pt-5 my-0"
|
||||
/>
|
||||
<div v-else>
|
||||
<FormControl
|
||||
v-model="newGateway"
|
||||
:label="__('Select Payment Gateway')"
|
||||
type="select"
|
||||
:options="allGatewayOptions"
|
||||
:required="true"
|
||||
/>
|
||||
<SettingFields
|
||||
v-if="newGateway"
|
||||
:fields="newGatewayFields"
|
||||
:data="null"
|
||||
class="pt-5 my-0"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
<template #actions="{ close }">
|
||||
<div class="pb-5 float-right">
|
||||
<Button variant="solid" @click="saveSettings(close)">
|
||||
{{ __('Save') }}
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</Dialog>
|
||||
</template>
|
||||
<script setup lang="ts">
|
||||
import {
|
||||
Button,
|
||||
call,
|
||||
createListResource,
|
||||
createResource,
|
||||
Dialog,
|
||||
FormControl,
|
||||
} from 'frappe-ui'
|
||||
import { computed, ref, watch } from 'vue'
|
||||
import SettingFields from '@/components/Settings/SettingFields.vue'
|
||||
|
||||
const show = defineModel()
|
||||
const paymentGateways = defineModel<any>('paymentGateways')
|
||||
const newGateway = ref(null)
|
||||
|
||||
const props = defineProps<{
|
||||
gatewayID: string | null
|
||||
}>()
|
||||
|
||||
const paymentGateway = createResource({
|
||||
url: 'lms.lms.api.get_payment_gateway_details',
|
||||
makeParams(values: any) {
|
||||
return {
|
||||
payment_gateway: props.gatewayID,
|
||||
}
|
||||
},
|
||||
transform(data: any) {
|
||||
arrangeFields(data.fields)
|
||||
return data
|
||||
},
|
||||
})
|
||||
|
||||
const allGateways = createListResource({
|
||||
doctype: 'DocType',
|
||||
filters: {
|
||||
module: 'Payment Gateways',
|
||||
},
|
||||
fields: ['name', 'issingle', 'fields'],
|
||||
})
|
||||
|
||||
const arrangeFields = (fields: any[]) => {
|
||||
fields = fields.sort((a, b) => {
|
||||
if (a.type === 'Upload' && b.type !== 'Upload') {
|
||||
return 1
|
||||
} else if (a.type !== 'Upload' && b.type === 'Upload') {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
|
||||
fields.splice(3, 0, {
|
||||
type: 'Column Break',
|
||||
})
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.gatewayID,
|
||||
() => {
|
||||
if (props.gatewayID && props.gatewayID !== 'new') {
|
||||
paymentGateway.reload()
|
||||
} else if (props.gatewayID == 'new') {
|
||||
allGateways.reload()
|
||||
console.log(allGateways.data)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
const saveSettings = (close: () => void) => {
|
||||
call('frappe.client.set_value', {
|
||||
doctype: paymentGateway.data.doctype,
|
||||
name: paymentGateway.data.docname,
|
||||
fieldname: Object.keys(paymentGateway.data.data).reduce(
|
||||
(fields: any, key: string) => {
|
||||
if (
|
||||
paymentGateway.data.data[key] &&
|
||||
typeof paymentGateway.data.data[key] === 'object'
|
||||
) {
|
||||
fields[key] = paymentGateway.data.data[key].file_url
|
||||
} else {
|
||||
fields[key] = paymentGateway.data.data[key]
|
||||
}
|
||||
return fields
|
||||
},
|
||||
{}
|
||||
),
|
||||
}).then(() => {
|
||||
paymentGateway.reload()
|
||||
close()
|
||||
})
|
||||
}
|
||||
|
||||
const allGatewayOptions = computed(() => {
|
||||
let options: string[] = []
|
||||
let gatewayList = allGateways.data?.map((gateway: any) => gateway.name) || []
|
||||
gatewayList.forEach((gateway: any) => {
|
||||
console.log(gateway)
|
||||
let gatewayName = gateway.split(' ')[0]
|
||||
let existingGateways =
|
||||
paymentGateways.value?.data?.map((pg: any) => pg.name) || []
|
||||
if (
|
||||
!options.includes(gatewayName) &&
|
||||
!existingGateways.includes(gatewayName)
|
||||
) {
|
||||
options.push(gatewayName)
|
||||
}
|
||||
})
|
||||
return options.map((gateway: string) => ({ label: gateway, value: gateway }))
|
||||
})
|
||||
</script>
|
||||
140
frontend/src/components/Settings/PaymentGateways.vue
Normal file
140
frontend/src/components/Settings/PaymentGateways.vue
Normal file
@@ -0,0 +1,140 @@
|
||||
<template>
|
||||
<div class="flex min-h-0 flex-col text-base">
|
||||
<div class="flex items-center justify-between mb-5">
|
||||
<div>
|
||||
<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>
|
||||
<Button @click="openForm('new')">
|
||||
<template #prefix>
|
||||
<Plus class="h-3 w-3 stroke-1.5" />
|
||||
</template>
|
||||
{{ __('New') }}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
<div v-if="paymentGateways.data?.length" class="overflow-y-scroll">
|
||||
<ListView
|
||||
:columns="columns"
|
||||
:rows="paymentGateways.data"
|
||||
row-key="name"
|
||||
:options="{
|
||||
showTooltip: false,
|
||||
onRowClick: (row) => {
|
||||
openForm(row.name)
|
||||
},
|
||||
}"
|
||||
>
|
||||
<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 paymentGateways.data">
|
||||
<template #default="{ column, item }">
|
||||
<ListRowItem :item="row[column.key]" :align="column.align">
|
||||
<div v-if="column.key == 'enabled'">
|
||||
<Badge v-if="row[column.key]" theme="green">
|
||||
{{ __('Enabled') }}
|
||||
</Badge>
|
||||
<Badge v-else theme="gray">
|
||||
{{ __('Disabled') }}
|
||||
</Badge>
|
||||
</div>
|
||||
<div v-else class="leading-5 text-sm">
|
||||
{{ row[column.key] }}
|
||||
</div>
|
||||
</ListRowItem>
|
||||
</template>
|
||||
</ListRow>
|
||||
</ListRows>
|
||||
|
||||
<ListSelectBanner>
|
||||
<template #actions="{ unselectAll, selections }">
|
||||
<div class="flex gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
@click="removeAccount(selections, unselectAll)"
|
||||
>
|
||||
<Trash2 class="h-4 w-4 stroke-1.5" />
|
||||
</Button>
|
||||
</div>
|
||||
</template>
|
||||
</ListSelectBanner>
|
||||
</ListView>
|
||||
</div>
|
||||
</div>
|
||||
<PaymentGatewayDetails
|
||||
v-model="showForm"
|
||||
:gatewayID="currentGateway"
|
||||
v-model:paymentGateways="paymentGateways"
|
||||
/>
|
||||
</template>
|
||||
<script setup>
|
||||
import {
|
||||
Badge,
|
||||
Button,
|
||||
createListResource,
|
||||
FeatherIcon,
|
||||
ListView,
|
||||
ListHeader,
|
||||
ListHeaderItem,
|
||||
ListRows,
|
||||
ListRow,
|
||||
ListRowItem,
|
||||
ListSelectBanner,
|
||||
} from 'frappe-ui'
|
||||
import { computed, ref } from 'vue'
|
||||
import { Plus, Trash2 } from 'lucide-vue-next'
|
||||
import PaymentGatewayDetails from '@/components/Settings/PaymentGatewayDetails.vue'
|
||||
|
||||
const showForm = ref(false)
|
||||
const currentGateway = ref(null)
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const paymentGateways = createListResource({
|
||||
doctype: 'Payment Gateway',
|
||||
fields: ['name', 'gateway_settings', 'gateway_controller'],
|
||||
auto: true,
|
||||
orderBy: 'modified desc',
|
||||
})
|
||||
|
||||
const openForm = (gatewayID) => {
|
||||
currentGateway.value = gatewayID
|
||||
showForm.value = true
|
||||
}
|
||||
|
||||
const columns = computed(() => {
|
||||
return [
|
||||
{
|
||||
label: __('Gateway'),
|
||||
key: 'name',
|
||||
icon: 'credit-card',
|
||||
},
|
||||
]
|
||||
})
|
||||
</script>
|
||||
@@ -1,128 +0,0 @@
|
||||
<template>
|
||||
<div class="flex flex-col h-full">
|
||||
<div class="flex items-center justify-between">
|
||||
<div class="text-xl font-semibold mb-1 text-ink-gray-9">
|
||||
{{ label }}
|
||||
</div>
|
||||
<!-- <Badge
|
||||
v-if="isDirty"
|
||||
:label="__('Not Saved')"
|
||||
variant="subtle"
|
||||
theme="orange"
|
||||
/> -->
|
||||
</div>
|
||||
<div class="overflow-y-scroll">
|
||||
<div class="flex flex-col divide-y">
|
||||
<SettingFields :fields="fields" :data="data.doc" />
|
||||
<SettingFields
|
||||
v-if="paymentGateway.data"
|
||||
:fields="paymentGateway.data.fields"
|
||||
:data="paymentGateway.data.data"
|
||||
class="pt-5 my-0"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex flex-row-reverse mt-auto">
|
||||
<Button variant="solid" @click="update">
|
||||
{{ __('Update') }}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
<script setup>
|
||||
import SettingFields from '@/components/Settings/SettingFields.vue'
|
||||
import { createResource, Badge, Button } from 'frappe-ui'
|
||||
import { watch } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
description: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
data: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
fields: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
})
|
||||
|
||||
const paymentGateway = createResource({
|
||||
url: 'lms.lms.api.get_payment_gateway_details',
|
||||
makeParams(values) {
|
||||
return {
|
||||
payment_gateway: props.data.doc.payment_gateway,
|
||||
}
|
||||
},
|
||||
transform(data) {
|
||||
arrangeFields(data.fields)
|
||||
return data
|
||||
},
|
||||
auto: true,
|
||||
})
|
||||
|
||||
const arrangeFields = (fields) => {
|
||||
fields = fields.sort((a, b) => {
|
||||
if (a.type === 'Upload' && b.type !== 'Upload') {
|
||||
return 1
|
||||
} else if (a.type !== 'Upload' && b.type === 'Upload') {
|
||||
return -1
|
||||
}
|
||||
return 0
|
||||
})
|
||||
|
||||
fields.splice(3, 0, {
|
||||
type: 'Column Break',
|
||||
})
|
||||
}
|
||||
|
||||
const saveSettings = createResource({
|
||||
url: 'frappe.client.set_value',
|
||||
makeParams(values) {
|
||||
let fields = {}
|
||||
Object.keys(paymentGateway.data.data).forEach((key) => {
|
||||
if (
|
||||
paymentGateway.data.data[key] &&
|
||||
typeof paymentGateway.data.data[key] === 'object'
|
||||
) {
|
||||
fields[key] = paymentGateway.data.data[key].file_url
|
||||
} else {
|
||||
fields[key] = paymentGateway.data.data[key]
|
||||
}
|
||||
})
|
||||
|
||||
return {
|
||||
doctype: paymentGateway.data.doctype,
|
||||
name: paymentGateway.data.docname,
|
||||
fieldname: fields,
|
||||
}
|
||||
},
|
||||
auto: false,
|
||||
onSuccess(data) {
|
||||
paymentGateway.reload()
|
||||
},
|
||||
})
|
||||
|
||||
const update = () => {
|
||||
props.fields.forEach((f) => {
|
||||
if (f.type != 'Column Break') {
|
||||
props.data.doc[f.name] = f.value
|
||||
}
|
||||
})
|
||||
props.data.save.submit()
|
||||
saveSettings.submit()
|
||||
}
|
||||
|
||||
watch(
|
||||
() => props.data.doc.payment_gateway,
|
||||
() => {
|
||||
paymentGateway.reload()
|
||||
}
|
||||
)
|
||||
</script>
|
||||
@@ -30,12 +30,9 @@
|
||||
</CodeEditor>
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-else-if="field.type == 'Upload'"
|
||||
class="grid grid-cols-2 gap-10"
|
||||
>
|
||||
<div class="space-y-2">
|
||||
<div class="text-sm text-ink-gray-8 font-medium mb-1">
|
||||
<div v-else-if="field.type == 'Upload'">
|
||||
<div class="space-y-1 mb-2">
|
||||
<div class="text-sm text-ink-gray-5 font-medium">
|
||||
{{ __(field.label) }}
|
||||
</div>
|
||||
<div class="text-sm text-ink-gray-5 leading-5">
|
||||
|
||||
@@ -41,18 +41,18 @@
|
||||
v-bind="{
|
||||
label: activeTab.label,
|
||||
description: activeTab.description,
|
||||
...(activeTab.label === 'Branding'
|
||||
...(activeTab.label == 'Branding'
|
||||
? { fields: activeTab.fields }
|
||||
: {}),
|
||||
}"
|
||||
/>
|
||||
<PaymentSettings
|
||||
v-else-if="activeTab.label === 'Payment Gateway'"
|
||||
<!-- <PaymentSettings
|
||||
v-else-if="activeTab.label === 'Gateways'"
|
||||
:label="activeTab.label"
|
||||
:description="activeTab.description"
|
||||
:data="data"
|
||||
:fields="activeTab.fields"
|
||||
/>
|
||||
/> -->
|
||||
<SettingDetails
|
||||
v-else
|
||||
:fields="activeTab.fields"
|
||||
@@ -76,7 +76,7 @@ import Evaluators from '@/components/Settings/Evaluators.vue'
|
||||
import Categories from '@/components/Settings/Categories.vue'
|
||||
import EmailTemplates from '@/components/Settings/EmailTemplates.vue'
|
||||
import BrandSettings from '@/components/Settings/BrandSettings.vue'
|
||||
import PaymentSettings from '@/components/Settings/PaymentSettings.vue'
|
||||
import PaymentGateways from '@/components/Settings/PaymentGateways.vue'
|
||||
import ZoomSettings from '@/components/Settings/ZoomSettings.vue'
|
||||
import Badges from '@/components/Settings/Badges.vue'
|
||||
|
||||
@@ -159,12 +159,12 @@ const tabsStructure = computed(() => {
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Settings',
|
||||
hideLabel: true,
|
||||
label: 'Payment',
|
||||
hideLabel: false,
|
||||
items: [
|
||||
{
|
||||
label: 'Payment Gateway',
|
||||
icon: 'DollarSign',
|
||||
label: 'Configuration',
|
||||
icon: 'CreditCard',
|
||||
description:
|
||||
'Configure the payment gateway and other payment related settings',
|
||||
fields: [
|
||||
@@ -200,6 +200,12 @@ const tabsStructure = computed(() => {
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
label: 'Gateways',
|
||||
icon: 'DollarSign',
|
||||
template: markRaw(PaymentGateways),
|
||||
description: 'Add and manage all your payment gateways',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
@@ -275,7 +281,7 @@ const tabsStructure = computed(() => {
|
||||
name: 'favicon',
|
||||
type: 'Upload',
|
||||
description:
|
||||
'Appears in the browser tab next to the page title, bookmarks, and shortcuts to help users quickly identify the application.',
|
||||
'Appears in the browser tab next to the page title to help users quickly identify the application.',
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
Reference in New Issue
Block a user