v6 update

This commit is contained in:
Mohit Panjwani
2022-01-10 16:06:17 +05:30
parent b770e6277f
commit bdea879273
722 changed files with 19047 additions and 9186 deletions

View File

@ -0,0 +1,259 @@
<template>
<BasePage>
<BasePageHeader :title="$t('payments.title')">
<BaseBreadcrumb slot="breadcrumbs">
<BaseBreadcrumbItem
:title="$t('general.home')"
:to="`/${globalStore.companySlug}/customer/dashboard`"
/>
<BaseBreadcrumbItem :title="$tc('payments.payment', 2)" to="#" active />
</BaseBreadcrumb>
<template #actions>
<BaseButton
v-show="paymentStore.totalPayments"
variant="primary-outline"
@click="toggleFilter"
>
{{ $t('general.filter') }}
<template #right="slotProps">
<BaseIcon
v-if="!showFilters"
:class="slotProps.class"
name="FilterIcon"
/>
<BaseIcon v-else :class="slotProps.class" name="XIcon" />
</template>
</BaseButton>
</template>
</BasePageHeader>
<BaseFilterWrapper v-show="showFilters" @clear="clearFilter">
<BaseInputGroup :label="$t('payments.payment_number')" class="px-3">
<BaseInput
v-model="filters.payment_number"
:placeholder="$t('payments.payment_number')"
/>
</BaseInputGroup>
<BaseInputGroup :label="$t('payments.payment_mode')" class="px-3">
<BaseMultiselect
v-model="filters.payment_mode"
value-prop="id"
track-by="name"
:filter-results="false"
label="name"
resolve-on-load
:delay="100"
searchable
:options="searchPayment"
:placeholder="$t('payments.payment_mode')"
/>
</BaseInputGroup>
</BaseFilterWrapper>
<BaseEmptyPlaceholder
v-if="showEmptyScreen"
:title="$t('payments.no_payments')"
:description="$t('payments.list_of_payments')"
>
<CapsuleIcon class="mt-5 mb-4" />
</BaseEmptyPlaceholder>
<div v-show="!showEmptyScreen" class="relative table-container">
<BaseTable
ref="table"
:data="fetchData"
:columns="paymentColumns"
:placeholder-count="paymentStore.totalPayments >= 20 ? 10 : 5"
class="mt-10"
>
<template #cell-payment_date="{ row }">
{{ row.data.formatted_payment_date }}
</template>
<template #cell-payment_number="{ row }">
<router-link
:to="{
path: `payments/${row.data.id}/view`,
}"
class="font-medium text-primary-500"
>
{{ row.data.payment_number }}
</router-link>
</template>
<template #cell-payment_mode="{ row }">
<span>
{{
row.data.payment_method
? row.data.payment_method.name
: $t('payments.not_selected')
}}
</span>
</template>
<template #cell-invoice_number="{ row }">
<span>
{{
row.data.invoice?.invoice_number
? row.data.invoice?.invoice_number
: $t('payments.no_invoice')
}}
</span>
</template>
<template #cell-amount="{ row }">
<div v-html="utils.formatMoney(row.data.amount, currency)" />
</template>
<template #cell-actions="{ row }">
<BaseDropdown>
<template #activator>
<BaseIcon name="DotsHorizontalIcon" class="w-5 text-gray-500" />
</template>
<router-link :to="`payments/${row.data.id}/view`">
<BaseDropdownItem>
<BaseIcon name="EyeIcon" class="h-5 mr-3 text-gray-600" />
{{ $t('general.view') }}
</BaseDropdownItem>
</router-link>
</BaseDropdown>
</template>
</BaseTable>
</div>
</BasePage>
</template>
<script setup>
import { debouncedWatch } from '@vueuse/core'
import BaseTable from '@/scripts/components/base/base-table/BaseTable.vue'
import CapsuleIcon from '@/scripts/components/icons/empty/CapsuleIcon.vue'
import { ref, reactive, inject, computed } from 'vue'
import BaseDropdownItem from '@/scripts/components/base/BaseDropdownItem.vue'
import BaseDropdown from '@/scripts/components/base/BaseDropdown.vue'
import { useI18n } from 'vue-i18n'
import { usePaymentStore } from '@/scripts/customer/stores/payment'
import { useGlobalStore } from '@/scripts/customer/stores/global'
import { useRoute } from 'vue-router'
const { tm, t } = useI18n()
let showFilters = ref(false)
let sortedBy = ref('created_at')
let isFetchingInitialData = ref(true)
let table = ref(null)
const filters = reactive({
payment_mode: '',
payment_number: '',
})
//Utils
const utils = inject('utils')
//Store
const route = useRoute()
const paymentStore = usePaymentStore()
const globalStore = useGlobalStore()
// Computed Props
const showEmptyScreen = computed(() => {
return !paymentStore.totalPayments && !isFetchingInitialData.value
})
const currency = computed(() => {
return globalStore.currency
})
// Payment Table columns Data
const paymentColumns = computed(() => {
return [
{
key: 'payment_date',
label: t('payments.date'),
thClass: 'extra',
tdClass: 'font-medium text-gray-900',
},
{ key: 'payment_number', label: t('payments.payment_number') },
{ key: 'payment_mode', label: t('payments.payment_mode') },
{ key: 'invoice_number', label: t('invoices.invoice_number') },
{ key: 'amount', label: t('payments.amount') },
{
key: 'actions',
label: '',
tdClass: 'text-right text-sm font-medium',
sortable: false,
},
]
})
// Created
debouncedWatch(
filters,
() => {
setFilters()
},
{ debounce: 500 }
)
// Methods
async function searchPayment(search) {
let res = await paymentStore.fetchPaymentModes(
search,
globalStore.companySlug
)
return res.data.data
}
async function fetchData({ page, filter, sort }) {
let data = {
payment_method_id:
filters.payment_mode !== null ? filters.payment_mode : '',
payment_number: filters.payment_number,
orderByField: sort.fieldName || 'created_at',
orderBy: sort.order || 'desc',
page,
}
isFetchingInitialData.value = true
let response = await paymentStore.fetchPayments(data, globalStore.companySlug)
isFetchingInitialData.value = false
return {
data: response.data.data,
pagination: {
totalPages: response.data.meta.last_page,
currentPage: page,
totalCount: response.data.meta.total,
limit: 10,
},
}
}
function refreshTable() {
table.value.refresh()
}
function setFilters() {
refreshTable()
}
function clearFilter() {
filters.customer = ''
filters.payment_mode = ''
filters.payment_number = ''
}
function toggleFilter() {
if (showFilters.value) {
clearFilter()
}
showFilters.value = !showFilters.value
}
</script>

View File

@ -0,0 +1,374 @@
<template>
<BasePage class="xl:pl-96">
<BasePageHeader :title="pageTitle.payment_number">
<template #actions>
<BaseButton
:disabled="isSendingEmail"
variant="primary-outline"
tag="a"
download
:href="`/payments/pdf/${payment.unique_hash}`"
>
<template #left="slotProps">
<BaseIcon name="DownloadIcon" :class="slotProps.class" />
{{ $t('general.download') }}
</template>
</BaseButton>
</template>
</BasePageHeader>
<!-- Sidebar -->
<div
class="fixed top-0 left-0 hidden h-full pt-16 pb-4 bg-white w-88 xl:block"
>
<div
class="
flex
items-center
justify-between
px-4
pt-8
pb-6
border border-gray-200 border-solid
"
>
<BaseInput
v-model="searchData.payment_number"
:placeholder="$t('general.search')"
type="text"
variant="gray"
@input="onSearch"
>
<template #right>
<BaseIcon name="SearchIcon" class="h-5 text-gray-400" />
</template>
</BaseInput>
<div class="flex ml-3" role="group" aria-label="First group">
<BaseDropdown
position="bottom-start"
width-class="w-50"
position-class="left-0"
>
<template #activator>
<BaseButton variant="gray">
<BaseIcon name="FilterIcon" class="h-5" />
</BaseButton>
</template>
<div
class="
px-4
py-1
pb-2
mb-2
text-sm
border-b border-gray-200 border-solid
"
>
{{ $t('general.sort_by') }}
</div>
<div class="px-2">
<BaseDropdownItem class="rounded-md pt-3 hover:rounded-md">
<BaseInputGroup class="-mt-3 font-normal">
<BaseRadio
id="filter_invoice_number"
v-model="searchData.orderByField"
:label="$t('invoices.title')"
size="sm"
name="filter"
value="invoice_number"
@update:modelValue="onSearch"
/>
</BaseInputGroup>
</BaseDropdownItem>
</div>
<div class="px-2">
<BaseDropdownItem class="rounded-md pt-3 hover:rounded-md">
<BaseInputGroup class="-mt-3 font-normal">
<BaseRadio
id="filter_payment_date"
v-model="searchData.orderByField"
:label="$t('payments.date')"
size="sm"
name="filter"
value="payment_date"
@update:modelValue="onSearch"
/>
</BaseInputGroup>
</BaseDropdownItem>
</div>
<div class="px-2">
<BaseDropdownItem class="rounded-md pt-3 hover:rounded-md">
<BaseInputGroup class="-mt-3 font-normal">
<BaseRadio
id="filter_payment_number"
v-model="searchData.orderByField"
:label="$t('payments.payment_number')"
size="sm"
name="filter"
value="payment_number"
@update:modelValue="onSearch"
/>
</BaseInputGroup>
</BaseDropdownItem>
</div>
</BaseDropdown>
<BaseButton class="ml-1" variant="white" @click="sortData">
<BaseIcon v-if="getOrderBy" name="SortAscendingIcon" class="h-5" />
<BaseIcon v-else name="SortDescendingIcon" class="h-5" />
</BaseButton>
</div>
</div>
<div
class="
h-full
pb-32
overflow-y-scroll
border-l border-gray-200 border-solid
sw-scroll
"
>
<router-link
v-for="(payment, index) in paymentStore.payments"
:id="'payment-' + payment.id"
:key="index"
:to="`/${globalStore.companySlug}/customer/payments/${payment.id}/view`"
:class="[
'flex justify-between p-4 items-center cursor-pointer hover:bg-gray-100 border-l-4 border-transparent',
{
'bg-gray-100 border-l-4 border-primary-500 border-solid':
hasActiveUrl(payment.id),
},
]"
style="border-bottom: 1px solid rgba(185, 193, 209, 0.41)"
>
<div class="flex-2">
<div
class="
mb-1
text-md
not-italic
font-medium
leading-5
text-gray-500
capitalize
"
>
{{ payment.payment_number }}
</div>
</div>
<div class="flex-1 whitespace-nowrap right">
<BaseFormatMoney
class="
mb-2
text-xl
not-italic
font-semibold
leading-8
text-right text-gray-900
block
"
:amount="payment.amount"
:currency="payment.currency"
/>
<div class="text-sm text-right text-gray-500 non-italic">
{{ payment.formatted_payment_date }}
</div>
</div>
</router-link>
<p
v-if="!paymentStore.payments.length"
class="flex justify-center px-4 mt-5 text-sm text-gray-600"
>
{{ $t('payments.no_matching_payments') }}
</p>
</div>
</div>
<!-- pdf -->
<div
class="flex flex-col min-h-0 mt-8 overflow-hidden"
style="height: 75vh"
>
<iframe
v-if="shareableLink"
:src="shareableLink"
class="flex-1 border border-gray-400 border-solid rounded-md"
/>
</div>
</BasePage>
</template>
<script setup>
import { useI18n } from 'vue-i18n'
import BaseDropdown from '@/scripts/components/base/BaseDropdown.vue'
import BaseDropdownItem from '@/scripts/components/base/BaseDropdownItem.vue'
import { debounce } from 'lodash'
import { ref, reactive, computed, inject, watch } from 'vue'
import { useRoute } from 'vue-router'
import { useNotificationStore } from '@/scripts/stores/notification'
import moment from 'moment'
import { usePaymentStore } from '@/scripts/customer/stores/payment'
import { useGlobalStore } from '@/scripts/customer/stores/global'
// Router
const route = useRoute()
const paymentStore = usePaymentStore()
const globalStore = useGlobalStore()
const { tm, t } = useI18n()
// let id = ref(null)
let payment = reactive({})
let searchData = reactive({
orderBy: '',
orderByField: '',
payment_number: '',
})
let isSearching = ref(false)
let isSendingEmail = ref(false)
let isMarkingAsSent = ref(false)
//Utils
const $utils = inject('utils')
//Store
const notificationStore = useNotificationStore()
// Computed Props
const pageTitle = computed(() => {
return paymentStore.selectedViewPayment
})
const getOrderBy = computed(() => {
if (searchData.orderBy === 'asc' || searchData.orderBy == null) {
return true
}
return false
})
const getOrderName = computed(() =>
getOrderBy.value ? tm('general.ascending') : tm('general.descending')
)
const shareableLink = computed(() => {
return payment.unique_hash ? `/payments/pdf/${payment.unique_hash}` : false
})
// Watcher
watch(route, () => {
loadPayment()
})
// Created
loadPayments()
loadPayment()
onSearch = debounce(onSearch, 500)
// Methods
function hasActiveUrl(id) {
return route.params.id == id
}
async function loadPayments() {
await paymentStore.fetchPayments(
{
limit: 'all',
},
globalStore.companySlug
)
setTimeout(() => {
scrollToPayment()
}, 500)
}
async function loadPayment() {
if (route && route.params.id) {
let response = await paymentStore.fetchViewPayment(
{
id: route.params.id,
},
globalStore.companySlug
)
if (response.data) {
Object.assign(payment, response.data.data)
}
}
}
function scrollToPayment() {
const el = document.getElementById(`payment-${route.params.id}`)
if (el) {
el.scrollIntoView({ behavior: 'smooth' })
el.classList.add('shake')
}
}
async function onSearch() {
let data = {}
if (
searchData.payment_number !== '' &&
searchData.payment_number !== null &&
searchData.payment_number !== undefined
) {
data.payment_number = searchData.payment_number
}
if (searchData.orderBy !== null && searchData.orderBy !== undefined) {
data.orderBy = searchData.orderBy
}
if (
searchData.orderByField !== null &&
searchData.orderByField !== undefined
) {
data.orderByField = searchData.orderByField
}
isSearching.value = true
try {
let response = await paymentStore.searchPayment(
data,
globalStore.companySlug
)
isSearching.value = false
if (response.data.data) {
paymentStore.payments = response.data.data
}
} catch (error) {
isSearching.value = false
}
}
function sortData() {
if (searchData.orderBy === 'asc') {
searchData.orderBy = 'desc'
onSearch()
return true
}
searchData.orderBy = 'asc'
onSearch()
return true
}
</script>