Files
Kotyata/apps/client/pages/projects/[projectId]/invoices.vue
2026-03-17 13:24:22 +03:00

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>