289 lines
6.1 KiB
Vue
289 lines
6.1 KiB
Vue
<template>
|
|
<div
|
|
v-if="!total && isFiltersEmpty && !searchTerm && !isInvoicesFetching"
|
|
class="invoice__empty"
|
|
>
|
|
<InvoicesEmpty :id="$route.params.projectId" />
|
|
</div>
|
|
|
|
<template v-else>
|
|
<PageToolbar
|
|
v-model:search="searchTerm"
|
|
search-label="Search an invoices"
|
|
>
|
|
<template #prefix>
|
|
<div class="invoices-awaiting-sum">
|
|
<div class="invoices-awaiting-sum__value">
|
|
<span>{{ awaitingSum }}</span> <UiIconSUpRight class="text-clr-cyan-500" />
|
|
</div>
|
|
|
|
<p class="invoices-awaiting-sum__text">
|
|
Sum of invoices awaiting payment
|
|
</p>
|
|
</div>
|
|
</template>
|
|
|
|
<UiButton
|
|
type="outlined"
|
|
color="secondary"
|
|
size="large"
|
|
:href="`/report/${$route.params.projectId}`"
|
|
icon="csv"
|
|
>
|
|
Export to CSV
|
|
</UiButton>
|
|
|
|
<UiButton
|
|
size="large"
|
|
:href="`/create/invoice/${$route.params.projectId}`"
|
|
>
|
|
Create an invoice
|
|
</UiButton>
|
|
</PageToolbar>
|
|
|
|
<div
|
|
style="
|
|
margin-top: 24px;
|
|
padding: 16px;
|
|
border-radius: 12px;
|
|
background-color: #fff;
|
|
"
|
|
>
|
|
<ResourceFilters
|
|
class="mb-16"
|
|
:filters="filters"
|
|
/>
|
|
|
|
<StrippedTable
|
|
class="invoices__table"
|
|
:columns="columns"
|
|
:data="invoices"
|
|
:loading="isInvoicesFetching"
|
|
>
|
|
<template #cell(currency)="{ row: { original } }">
|
|
<CurrencyName :code="original.currencyCode" />
|
|
</template>
|
|
|
|
<template #cell(amount)="{ row: { original } }">
|
|
<MoneyAmount
|
|
:value="original.amount"
|
|
:currency="original.currencyCode"
|
|
/>
|
|
</template>
|
|
|
|
<template #cell(accrued)="{ row: { original } }">
|
|
<MoneyAmount
|
|
:value="original.accrued"
|
|
:currency="original.currencyCode"
|
|
/>
|
|
</template>
|
|
|
|
<template #cell(address)="{ row: { original } }">
|
|
<span v-if="!original.walletAddress">—</span>
|
|
<TextShortener
|
|
v-else
|
|
:text="original.walletAddress"
|
|
/>
|
|
</template>
|
|
|
|
<template #cell(orderId)="{ row: { original } }">
|
|
<TextShortener :text="original.orderId" />
|
|
</template>
|
|
|
|
<template #cell(date)="{ row: { original } }">
|
|
<FormattedDate :value="original.createdAt" />
|
|
</template>
|
|
|
|
<template #cell(status)="{ row: { original } }">
|
|
<UiBadge
|
|
:text="$t(`invoice_status.${original.status}`)"
|
|
:type="getStatusType(original.status)"
|
|
:icon="getStatusIcon(original.status)"
|
|
/>
|
|
</template>
|
|
|
|
<template #cell(paymentLink)="{ row: { original } }">
|
|
<UiCopyButton
|
|
title="Payment link"
|
|
pressed-title="Link copied"
|
|
:value="`${runtimeConfig.public.payHost}/${original.id}`"
|
|
/>
|
|
</template>
|
|
</StrippedTable>
|
|
</div>
|
|
</template>
|
|
</template>
|
|
|
|
<script setup>
|
|
import { createColumnHelper } from '@tanstack/vue-table'
|
|
import { computed } from 'vue'
|
|
import dayjs from 'dayjs'
|
|
import { getStatusIcon, getStatusType } from '~/helpers/invoices.ts'
|
|
|
|
definePageMeta({
|
|
middleware: ['auth'],
|
|
})
|
|
|
|
const runtimeConfig = useRuntimeConfig()
|
|
|
|
const filters = [
|
|
{
|
|
key: 'statuses[]',
|
|
label: 'Status',
|
|
placeholder: 'All statuses',
|
|
multiple: true,
|
|
options: [
|
|
{
|
|
label: 'Pending',
|
|
value: 'pending',
|
|
},
|
|
{
|
|
label: 'Completed',
|
|
value: 'completed',
|
|
},
|
|
{
|
|
label: 'Expired',
|
|
value: 'expired',
|
|
},
|
|
{
|
|
label: 'Await Payment',
|
|
value: 'awaiting_payment',
|
|
},
|
|
{
|
|
label: 'Overpaid',
|
|
value: 'overpaid',
|
|
},
|
|
{
|
|
label: 'Underpaid',
|
|
value: 'underpaid',
|
|
},
|
|
],
|
|
},
|
|
{
|
|
type: 'calendar',
|
|
key: 'date',
|
|
label: 'Date',
|
|
placeholder: 'All period',
|
|
transform: (value) => {
|
|
const [dateFrom, dateTo] = value
|
|
|
|
if (!dateFrom)
|
|
return
|
|
|
|
return {
|
|
dateFrom: dayjs(dateFrom).format('YYYY-MM-DD'),
|
|
dateTo: dateTo ? dayjs(dateTo).format('YYYY-MM-DD') : undefined,
|
|
}
|
|
},
|
|
},
|
|
]
|
|
|
|
const route = useRoute()
|
|
const { appliedFilters, empty: isFiltersEmpty } = useFilters(filters)
|
|
|
|
const searchTerm = ref()
|
|
|
|
const { data: invoicesData, pending: isInvoicesFetching, refresh } = await useAsyncData(
|
|
'invoices',
|
|
() =>
|
|
$api(`/invoices`, {
|
|
method: 'get',
|
|
params: {
|
|
...appliedFilters.value,
|
|
onlineStoreId: route.params.projectId,
|
|
query: searchTerm.value,
|
|
},
|
|
}),
|
|
{
|
|
watch: [appliedFilters, searchTerm],
|
|
},
|
|
)
|
|
|
|
const total = computed(() => invoicesData.value?.meta.total ?? 0)
|
|
const invoices = computed(() => invoicesData.value?.invoices)
|
|
|
|
const awaitingSum = $money.fullFormat(0, 'USDT')
|
|
|
|
const columnHelper = createColumnHelper()
|
|
|
|
const columns = [
|
|
columnHelper.accessor('currencyCode', {
|
|
id: 'currency',
|
|
header: 'Currency',
|
|
}),
|
|
columnHelper.accessor('amount', {
|
|
header: 'Amount',
|
|
}),
|
|
columnHelper.display({
|
|
id: 'accrued',
|
|
header: 'Accrued',
|
|
}),
|
|
columnHelper.display({
|
|
id: 'address',
|
|
header: 'Address',
|
|
}),
|
|
columnHelper.display({
|
|
id: 'orderId',
|
|
header: 'Order ID',
|
|
}),
|
|
columnHelper.accessor('createdAt', {
|
|
id: 'date',
|
|
header: 'Date',
|
|
}),
|
|
columnHelper.display({
|
|
id: 'status',
|
|
header: 'Status',
|
|
}),
|
|
columnHelper.display({
|
|
id: 'paymentLink',
|
|
}),
|
|
]
|
|
</script>
|
|
|
|
<style lang="scss">
|
|
.invoice {
|
|
&__empty {
|
|
display: inline-flex;
|
|
background-color: $clr-white;
|
|
border-radius: 12px;
|
|
flex: 1;
|
|
width: 100%;
|
|
padding: 16px;
|
|
|
|
> :first-child {
|
|
margin: 0 auto;
|
|
}
|
|
}
|
|
}
|
|
|
|
.invoices {
|
|
&__table {
|
|
td.payment_link {
|
|
width: 1%;
|
|
text-align: right;
|
|
}
|
|
}
|
|
}
|
|
|
|
.invoices-awaiting-sum {
|
|
padding: 8px 16px;
|
|
height: 48px;
|
|
border-radius: 12px;
|
|
background-color: $clr-grey-200;
|
|
|
|
&__value {
|
|
@include txt-i-sb;
|
|
|
|
> span {
|
|
vertical-align: middle;
|
|
}
|
|
}
|
|
|
|
&__text {
|
|
@include txt-s-m;
|
|
|
|
color: $clr-grey-500;
|
|
}
|
|
}
|
|
</style>
|