Initial commit

This commit is contained in:
2025-08-04 16:33:07 +03:30
commit f798e8e35c
9595 changed files with 1208683 additions and 0 deletions

View File

@@ -0,0 +1,171 @@
<script setup>
import InvoiceEditable from '@/views/apps/invoice/InvoiceEditable.vue'
import InvoiceSendInvoiceDrawer from '@/views/apps/invoice/InvoiceSendInvoiceDrawer.vue'
// 👉 Default Blank Data
const invoiceData = ref({
invoice: {
id: 5037,
issuedDate: '',
service: '',
total: 0,
avatar: '',
invoiceStatus: '',
dueDate: '',
balance: 0,
client: {
address: '112, Lorem Ipsum, Florida',
company: 'Greeva Inc',
companyEmail: 'johndoe@greeva.com',
contact: '+1 123 3452 12',
country: 'USA',
name: 'John Doe',
},
},
paymentDetails: {
totalDue: '$12,110.55',
bankName: 'American Bank',
country: 'United States',
iban: 'ETD95476213',
swiftCode: 'BR91905',
},
purchasedProducts: [{
title: '',
cost: 0,
hours: 0,
description: '',
}],
note: '',
paymentMethod: '',
salesperson: '',
thanksNote: '',
})
const paymentTerms = ref(true)
const clientNotes = ref(false)
const paymentStub = ref(false)
const selectedPaymentMethod = ref('Bank Account')
const paymentMethods = [
'Bank Account',
'PayPal',
'UPI Transfer',
]
const isSendPaymentSidebarVisible = ref(false)
const addProduct = value => {
invoiceData.value?.purchasedProducts.push(value)
}
const removeProduct = id => {
invoiceData.value?.purchasedProducts.splice(id, 1)
}
</script>
<template>
<VRow>
<!-- 👉 InvoiceEditable -->
<VCol
cols="12"
md="9"
>
<InvoiceEditable
:data="invoiceData"
@push="addProduct"
@remove="removeProduct"
/>
</VCol>
<!-- 👉 Right Column: Invoice Action -->
<VCol
cols="12"
md="3"
>
<VCard class="mb-8">
<VCardText>
<!-- 👉 Send Invoice -->
<VBtn
block
prepend-icon="tabler-send"
class="mb-4"
@click="isSendPaymentSidebarVisible = true"
>
Send Invoice
</VBtn>
<!-- 👉 Preview -->
<VBtn
block
color="secondary"
variant="tonal"
class="mb-4"
:to="{ name: 'apps-invoice-preview-id', params: { id: '5036' } }"
>
Preview
</VBtn>
<!-- 👉 Save -->
<VBtn
block
color="secondary"
variant="tonal"
>
Save
</VBtn>
</VCardText>
</VCard>
<!-- 👉 Select payment method -->
<AppSelect
id="payment-method"
v-model="selectedPaymentMethod"
:items="paymentMethods"
label="Accept Payment Via"
class="mb-6"
/>
<!-- 👉 Payment Terms -->
<div class="d-flex align-center justify-space-between">
<VLabel for="payment-terms">
Payment Terms
</VLabel>
<div>
<VSwitch
id="payment-terms"
v-model="paymentTerms"
/>
</div>
</div>
<!-- 👉 Client Notes -->
<div class="d-flex align-center justify-space-between">
<VLabel for="client-notes">
Client Notes
</VLabel>
<div>
<VSwitch
id="client-notes"
v-model="clientNotes"
/>
</div>
</div>
<!-- 👉 Payment Stub -->
<div class="d-flex align-center justify-space-between">
<VLabel for="payment-stub">
Payment Stub
</VLabel>
<div>
<VSwitch
id="payment-stub"
v-model="paymentStub"
/>
</div>
</div>
</VCol>
</VRow>
<!-- 👉 Send Invoice Sidebar -->
<InvoiceSendInvoiceDrawer v-model:is-drawer-open="isSendPaymentSidebarVisible" />
</template>

View File

@@ -0,0 +1,177 @@
<script setup>
import InvoiceAddPaymentDrawer from '@/views/apps/invoice/InvoiceAddPaymentDrawer.vue'
import InvoiceEditable from '@/views/apps/invoice/InvoiceEditable.vue'
import InvoiceSendInvoiceDrawer from '@/views/apps/invoice/InvoiceSendInvoiceDrawer.vue'
const invoiceData = ref()
const route = useRoute('apps-invoice-edit-id')
const { data: invoiceDetails } = await useApi(`/apps/invoice/${ route.params.id }`)
if (invoiceDetails.value) {
invoiceData.value = {
invoice: invoiceDetails.value.invoice,
paymentDetails: invoiceDetails.value.paymentDetails,
purchasedProducts: [{
title: 'App Design',
cost: 24,
hours: 2,
description: 'Designed UI kit & app pages.',
}],
note: 'It was a pleasure working with you and your team. We hope you will keep us in mind for future freelance projects. Thank You!',
paymentMethod: 'Bank Account',
salesperson: 'Tom Cook',
thanksNote: 'Thanks for your business',
}
}
const addProduct = value => {
invoiceData.value?.purchasedProducts.push(value)
}
const removeProduct = id => {
invoiceData.value?.purchasedProducts.splice(id, 1)
}
const isSendSidebarActive = ref(false)
const isAddPaymentSidebarActive = ref(false)
const paymentTerms = ref(true)
const clientNotes = ref(false)
const paymentStub = ref(false)
const selectedPaymentMethod = ref('Bank Account')
const paymentMethods = [
'Bank Account',
'PayPal',
'UPI Transfer',
]
</script>
<template>
<VRow v-if="invoiceData && invoiceData?.invoice">
<!-- 👉 InvoiceEditable -->
<VCol
cols="12"
md="9"
>
<InvoiceEditable
v-if="invoiceData?.invoice"
:data="invoiceData"
@push="addProduct"
@remove="removeProduct"
/>
</VCol>
<!-- 👉 Right Column: Invoice Action -->
<VCol
cols="12"
md="3"
>
<VCard class="mb-8">
<VCardText>
<!-- 👉 Send Invoice Trigger button -->
<VBtn
block
prepend-icon="tabler-send"
class="mb-4"
@click="isSendSidebarActive = true"
>
Send Invoice
</VBtn>
<div class="d-flex flex-wrap gap-4">
<!-- 👉 Preview button -->
<VBtn
color="secondary"
variant="tonal"
class="flex-grow-1"
:to="{ name: 'apps-invoice-preview-id', params: { id: route.params.id } }"
>
Preview
</VBtn>
<!-- 👉 Save button -->
<VBtn
color="secondary"
variant="tonal"
class="mb-4 flex-grow-1"
>
Save
</VBtn>
</div>
<!-- 👉 Add Payment trigger button -->
<VBtn
block
color="success"
prepend-icon="tabler-currency-dollar"
@click="isAddPaymentSidebarActive = true"
>
Add Payment
</VBtn>
</VCardText>
</VCard>
<!-- 👉 Accept payment via -->
<AppSelect
id="payment-method"
v-model="selectedPaymentMethod"
:items="paymentMethods"
label="Accept Payment Via"
class="mb-4"
/>
<!-- 👉 Payment Terms -->
<div class="d-flex align-center justify-space-between">
<VLabel for="payment-terms">
Payment Terms
</VLabel>
<div>
<VSwitch
id="payment-terms"
v-model="paymentTerms"
/>
</div>
</div>
<!-- 👉 Client Notes -->
<div class="d-flex align-center justify-space-between">
<VLabel for="client-notes">
Client Notes
</VLabel>
<div>
<VSwitch
id="client-notes"
v-model="clientNotes"
/>
</div>
</div>
<!-- 👉 Payment Stub -->
<div class="d-flex align-center justify-space-between">
<VLabel for="payment-stub">
Payment Stub
</VLabel>
<div>
<VSwitch
id="payment-stub"
v-model="paymentStub"
/>
</div>
</div>
</VCol>
<!-- 👉 Invoice send drawer -->
<InvoiceSendInvoiceDrawer v-model:is-drawer-open="isSendSidebarActive" />
<!-- 👉 Invoice add payment drawer -->
<InvoiceAddPaymentDrawer v-model:is-drawer-open="isAddPaymentSidebarActive" />
</VRow>
<section v-else>
<VAlert
type="error"
variant="tonal"
>
Invoice with ID {{ route.params.id }} not found!
</VAlert>
</section>
</template>

View File

@@ -0,0 +1,442 @@
<script setup>
const searchQuery = ref('')
const selectedStatus = ref(null)
const selectedRows = ref([])
// Data table options
const itemsPerPage = ref(10)
const page = ref(1)
const sortBy = ref()
const orderBy = ref()
const updateOptions = options => {
sortBy.value = options.sortBy[0]?.key
orderBy.value = options.sortBy[0]?.order
}
const widgetData = ref([
{
title: 'Clients',
value: 24,
icon: 'tabler-user',
},
{
title: 'Invoices',
value: 165,
icon: 'tabler-file-invoice',
},
{
title: 'Paid',
value: '$2.46k',
icon: 'tabler-checks',
},
{
title: 'Unpaid',
value: '$876',
icon: 'tabler-circle-off',
},
])
// 👉 headers
const headers = [
{
title: '#',
key: 'id',
},
{
title: 'Status',
key: 'status',
sortable: false,
},
{
title: 'Client',
key: 'client',
},
{
title: 'Total',
key: 'total',
},
{
title: 'Issued Date',
key: 'date',
},
{
title: 'Balance',
key: 'balance',
},
{
title: 'Actions',
key: 'actions',
sortable: false,
},
]
const {
data: invoiceData,
execute: fetchInvoices,
} = await useApi(createUrl('/apps/invoice', {
query: {
q: searchQuery,
status: selectedStatus,
itemsPerPage,
page,
sortBy,
orderBy,
},
}))
const invoices = computed(() => invoiceData.value.invoices)
const totalInvoices = computed(() => invoiceData.value.totalInvoices)
// 👉 Invoice balance variant resolver
const resolveInvoiceBalanceVariant = (balance, total) => {
if (balance === total)
return {
status: 'Unpaid',
chip: { color: 'error' },
}
if (balance === 0)
return {
status: 'Paid',
chip: { color: 'success' },
}
return {
status: balance,
chip: { variant: 'text' },
}
}
const resolveInvoiceStatusVariantAndIcon = status => {
if (status === 'Partial Payment')
return {
variant: 'warning',
icon: 'tabler-chart-pie-2',
}
if (status === 'Paid')
return {
variant: 'success',
icon: 'tabler-check',
}
if (status === 'Downloaded')
return {
variant: 'info',
icon: 'tabler-arrow-down',
}
if (status === 'Draft')
return {
variant: 'primary',
icon: 'tabler-folder',
}
if (status === 'Sent')
return {
variant: 'secondary',
icon: 'tabler-mail',
}
if (status === 'Past Due')
return {
variant: 'error',
icon: 'tabler-help',
}
return {
variant: 'secondary',
icon: 'tabler-x',
}
}
const computedMoreList = computed(() => {
return paramId => [
{
title: 'Download',
value: 'download',
prependIcon: 'tabler-download',
},
{
title: 'Edit',
value: 'edit',
prependIcon: 'tabler-pencil',
to: {
name: 'apps-invoice-edit-id',
params: { id: paramId },
},
},
{
title: 'Duplicate',
value: 'duplicate',
prependIcon: 'tabler-layers-intersect',
},
]
})
const deleteInvoice = async id => {
await $api(`/apps/invoice/${ id }`, { method: 'DELETE' })
// Delete from selectedRows
const index = selectedRows.value.findIndex(row => row === id)
if (index !== -1)
selectedRows.value.splice(index, 1)
// Refetch Invoices
fetchInvoices()
}
</script>
<template>
<section v-if="invoices">
<!-- 👉 Invoice Widgets -->
<VCard class="mb-6">
<VCardText class="px-3">
<VRow>
<template
v-for="(data, id) in widgetData"
:key="id"
>
<VCol
cols="12"
sm="6"
md="3"
class="px-6"
>
<div
class="d-flex justify-space-between align-center"
:class="$vuetify.display.xs
? id !== widgetData.length - 1 ? 'border-b pb-4' : ''
: $vuetify.display.sm
? id < (widgetData.length / 2) ? 'border-b pb-4' : ''
: ''"
>
<div class="d-flex flex-column">
<h4 class="text-h4">
{{ data.value }}
</h4>
<span class="text-body-1 text-capitalize">{{ data.title }}</span>
</div>
<VAvatar
variant="tonal"
rounded
size="42"
>
<VIcon
:icon="data.icon"
size="26"
color="high-emphasis"
/>
</VAvatar>
</div>
</VCol>
<VDivider
v-if="$vuetify.display.mdAndUp ? id !== widgetData.length - 1
: $vuetify.display.smAndUp ? id % 2 === 0
: false"
vertical
inset
length="60"
/>
</template>
</VRow>
</VCardText>
</VCard>
<VCard id="invoice-list">
<VCardText class="d-flex justify-space-between align-center flex-wrap gap-4">
<div class="d-flex gap-4 align-center flex-wrap">
<div class="d-flex align-center gap-2">
<span>Show</span>
<AppSelect
:model-value="itemsPerPage"
:items="[
{ value: 10, title: '10' },
{ value: 25, title: '25' },
{ value: 50, title: '50' },
{ value: 100, title: '100' },
{ value: -1, title: 'All' },
]"
style="inline-size: 5.5rem;"
@update:model-value="itemsPerPage = parseInt($event, 10)"
/>
</div>
<!-- 👉 Create invoice -->
<VBtn
prepend-icon="tabler-plus"
:to="{ name: 'apps-invoice-add' }"
>
Create invoice
</VBtn>
</div>
<div class="d-flex align-center flex-wrap gap-4">
<!-- 👉 Search -->
<div class="invoice-list-filter">
<AppTextField
v-model="searchQuery"
placeholder="Search Invoice"
/>
</div>
<!-- 👉 Select status -->
<div class="invoice-list-filter">
<AppSelect
v-model="selectedStatus"
placeholder="Invoice Status"
clearable
clear-icon="tabler-x"
single-line
:items="['Downloaded', 'Draft', 'Sent', 'Paid', 'Partial Payment', 'Past Due']"
/>
</div>
</div>
</VCardText>
<VDivider />
<!-- SECTION Datatable -->
<VDataTableServer
v-model:model-value="selectedRows"
v-model:items-per-page="itemsPerPage"
v-model:page="page"
show-select
:items-length="totalInvoices"
:headers="headers"
:items="invoices"
item-value="id"
class="text-no-wrap"
@update:options="updateOptions"
>
<!-- id -->
<template #item.id="{ item }">
<RouterLink :to="{ name: 'apps-invoice-preview-id', params: { id: item.id } }">
#{{ item.id }}
</RouterLink>
</template>
<!-- status -->
<template #item.status="{ item }">
<VTooltip>
<template #activator="{ props }">
<VAvatar
:size="28"
v-bind="props"
:color="resolveInvoiceStatusVariantAndIcon(item.invoiceStatus).variant"
variant="tonal"
>
<VIcon
:size="16"
:icon="resolveInvoiceStatusVariantAndIcon(item.invoiceStatus).icon"
/>
</VAvatar>
</template>
<p class="mb-0">
{{ item.invoiceStatus }}
</p>
<p class="mb-0">
Balance: {{ item.balance }}
</p>
<p class="mb-0">
Due date: {{ item.dueDate }}
</p>
</VTooltip>
</template>
<!-- client -->
<template #item.client="{ item }">
<div class="d-flex align-center">
<VAvatar
size="34"
:color="!item.avatar.length ? resolveInvoiceStatusVariantAndIcon(item.invoiceStatus).variant : undefined"
:variant="!item.avatar.length ? 'tonal' : undefined"
class="me-3"
>
<VImg
v-if="item.avatar.length"
:src="item.avatar"
/>
<span v-else>{{ avatarText(item.client.name) }}</span>
</VAvatar>
<div class="d-flex flex-column">
<RouterLink
:to="{ name: 'pages-user-profile-tab', params: { tab: 'profile' } }"
class="text-link font-weight-medium"
>
{{ item.client.name }}
</RouterLink>
<span class="text-sm text-medium-emphasis">{{ item.client.companyEmail }}</span>
</div>
</div>
</template>
<!-- Total -->
<template #item.total="{ item }">
${{ item.total }}
</template>
<!-- Date -->
<template #item.date="{ item }">
{{ item.issuedDate }}
</template>
<!-- Balance -->
<template #item.balance="{ item }">
<VChip
v-if="typeof ((resolveInvoiceBalanceVariant(item.balance, item.total)).status) === 'string'"
:color="resolveInvoiceBalanceVariant(item.balance, item.total).chip.color"
label
size="x-small"
>
{{ (resolveInvoiceBalanceVariant(item.balance, item.total)).status }}
</VChip>
<template v-else>
<span class="text-base text-high-emphasis">
{{ Number((resolveInvoiceBalanceVariant(item.balance, item.total)).status) > 0 ? `$${(resolveInvoiceBalanceVariant(item.balance, item.total)).status}` : `-$${Math.abs(Number((resolveInvoiceBalanceVariant(item.balance, item.total)).status))}` }}
</span>
</template>
</template>
<!-- Actions -->
<template #item.actions="{ item }">
<IconBtn @click="deleteInvoice(item.id)">
<VIcon icon="tabler-trash" />
</IconBtn>
<IconBtn :to="{ name: 'apps-invoice-preview-id', params: { id: item.id } }">
<VIcon icon="tabler-eye" />
</IconBtn>
<MoreBtn
:menu-list="computedMoreList(item.id)"
item-props
color="undefined"
/>
</template>
<!-- pagination -->
<template #bottom>
<TablePagination
v-model:page="page"
:items-per-page="itemsPerPage"
:total-items="totalInvoices"
/>
</template>
</VDataTableServer>
<!-- !SECTION -->
</VCard>
</section>
<section v-else>
<VCard>
<VCardTitle>No Invoice Found</VCardTitle>
</VCard>
</section>
</template>
<style lang="scss">
#invoice-list {
.invoice-list-actions {
inline-size: 8rem;
}
.invoice-list-filter {
inline-size: 12rem;
}
}
</style>

View File

@@ -0,0 +1,452 @@
<script setup>
import { VNodeRenderer } from '@layouts/components/VNodeRenderer'
import { themeConfig } from '@themeConfig'
import InvoiceAddPaymentDrawer from '@/views/apps/invoice/InvoiceAddPaymentDrawer.vue'
import InvoiceSendInvoiceDrawer from '@/views/apps/invoice/InvoiceSendInvoiceDrawer.vue'
const route = useRoute('apps-invoice-preview-id')
const isAddPaymentSidebarVisible = ref(false)
const isSendPaymentSidebarVisible = ref(false)
const { data: invoiceData } = await useApi(`/apps/invoice/${ Number(route.params.id) }`)
const invoice = ref()
const paymentDetails = ref()
if (invoiceData.value) {
invoice.value = invoiceData.value.invoice
paymentDetails.value = invoiceData.value.paymentDetails
}
const purchasedProducts = [
{
name: 'Premium Branding Package',
description: 'Branding & Promotion',
qty: 1,
hours: 15,
price: 32,
},
{
name: 'SMM',
description: 'Social media templates',
qty: 1,
hours: 14,
price: 28,
},
{
name: 'Web Design',
description: 'Web designing package',
qty: 1,
hours: 12,
price: 24,
},
{
name: 'SEO',
description: 'Search engine optimization',
qty: 1,
hours: 5,
price: 22,
},
]
const printInvoice = () => {
window.print()
}
</script>
<template>
<section v-if="invoice && paymentDetails">
<VRow>
<VCol
cols="12"
md="9"
>
<VCard class="invoice-preview-wrapper pa-6 pa-sm-12">
<!-- SECTION Header -->
<div class="invoice-header-preview d-flex flex-wrap justify-space-between flex-column flex-sm-row print-row bg-var-theme-background gap-6 rounded pa-6 mb-6">
<!-- 👉 Left Content -->
<div>
<div class="d-flex align-center app-logo mb-6">
<!-- 👉 Logo -->
<VNodeRenderer :nodes="themeConfig.app.logo" />
<!-- 👉 Title -->
<h6 class="app-logo-title">
{{ themeConfig.app.title }}
</h6>
</div>
<!-- 👉 Address -->
<h6 class="text-h6 font-weight-regular">
Office 149, 450 South Brand Brooklyn
</h6>
<h6 class="text-h6 font-weight-regular">
San Diego County, CA 91905, USA
</h6>
<h6 class="text-h6 font-weight-regular">
+1 (123) 456 7891, +44 (876) 543 2198
</h6>
</div>
<!-- 👉 Right Content -->
<div>
<!-- 👉 Invoice ID -->
<h6 class="font-weight-medium text-lg mb-6">
Invoice #{{ invoice.id }}
</h6>
<!-- 👉 Issue Date -->
<h6 class="text-h6 font-weight-regular">
<span>Date Issued: </span>
<span>{{ new Date(invoice.issuedDate).toLocaleDateString('en-GB') }}</span>
</h6>
<!-- 👉 Due Date -->
<h6 class="text-h6 font-weight-regular">
<span>Due Date: </span>
<span>{{ new Date(invoice.dueDate).toLocaleDateString('en-GB') }}</span>
</h6>
</div>
</div>
<!-- !SECTION -->
<!-- 👉 Payment Details -->
<VRow class="print-row mb-6">
<VCol class="text-no-wrap">
<h6 class="text-h6 mb-4">
Invoice To:
</h6>
<p class="mb-0">
{{ invoice.client.name }}
</p>
<p class="mb-0">
{{ invoice.client.company }}
</p>
<p class="mb-0">
{{ invoice.client.address }}, {{ invoice.client.country }}
</p>
<p class="mb-0">
{{ invoice.client.contact }}
</p>
<p class="mb-0">
{{ invoice.client.companyEmail }}
</p>
</VCol>
<VCol class="text-no-wrap">
<h6 class="text-h6 mb-4">
Bill To:
</h6>
<table>
<tbody>
<tr>
<td class="pe-4">
Total Due:
</td>
<td>
{{ paymentDetails.totalDue }}
</td>
</tr>
<tr>
<td class="pe-4">
Bank Name:
</td>
<td>
{{ paymentDetails.bankName }}
</td>
</tr>
<tr>
<td class="pe-4">
Country:
</td>
<td>
{{ paymentDetails.country }}
</td>
</tr>
<tr>
<td class="pe-4">
IBAN:
</td>
<td>
{{ paymentDetails.iban }}
</td>
</tr>
<tr>
<td class="pe-4">
SWIFT Code:
</td>
<td>
{{ paymentDetails.swiftCode }}
</td>
</tr>
</tbody>
</table>
</VCol>
</VRow>
<!-- 👉 invoice Table -->
<VTable class="invoice-preview-table border text-high-emphasis overflow-hidden mb-6">
<thead>
<tr>
<th scope="col">
ITEM
</th>
<th scope="col">
DESCRIPTION
</th>
<th
scope="col"
class="text-center"
>
HOURS
</th>
<th
scope="col"
class="text-center"
>
QTY
</th>
<th
scope="col"
class="text-center"
>
TOTAL
</th>
</tr>
</thead>
<tbody class="text-base">
<tr
v-for="item in purchasedProducts"
:key="item.name"
>
<td class="text-no-wrap">
{{ item.name }}
</td>
<td class="text-no-wrap">
{{ item.description }}
</td>
<td class="text-center">
{{ item.hours }}
</td>
<td class="text-center">
{{ item.qty }}
</td>
<td class="text-center">
${{ item.price }}
</td>
</tr>
</tbody>
</VTable>
<!-- 👉 Total -->
<div class="d-flex justify-space-between flex-column flex-sm-row print-row">
<div class="mb-2">
<div class="d-flex align-center mb-1">
<h6 class="text-h6 me-2">
Salesperson:
</h6>
<span>Jenny Parker</span>
</div>
<p>Thanks for your business</p>
</div>
<div>
<table class="w-100">
<tbody>
<tr>
<td class="pe-16">
Subtotal:
</td>
<td :class="$vuetify.locale.isRtl ? 'text-start' : 'text-end'">
<h6 class="text-base font-weight-medium">
$1800
</h6>
</td>
</tr>
<tr>
<td class="pe-16">
Discount:
</td>
<td :class="$vuetify.locale.isRtl ? 'text-start' : 'text-end'">
<h6 class="text-base font-weight-medium">
$28
</h6>
</td>
</tr>
<tr>
<td class="pe-16">
Tax:
</td>
<td :class="$vuetify.locale.isRtl ? 'text-start' : 'text-end'">
<h6 class="text-base font-weight-medium">
21%
</h6>
</td>
</tr>
</tbody>
</table>
<VDivider class="my-2" />
<table class="w-100">
<tbody>
<tr>
<td class="pe-16">
Total:
</td>
<td :class="$vuetify.locale.isRtl ? 'text-start' : 'text-end'">
<h6 class="text-base font-weight-medium">
$1690
</h6>
</td>
</tr>
</tbody>
</table>
</div>
</div>
<VDivider class="my-6 border-dashed" />
<p class="mb-0">
<span class="text-high-emphasis font-weight-medium me-1">
Note:
</span>
<span>It was a pleasure working with you and your team. We hope you will keep us in mind for future freelance projects. Thank You!</span>
</p>
</VCard>
</VCol>
<VCol
cols="12"
md="3"
class="d-print-none"
>
<VCard>
<VCardText>
<!-- 👉 Send Invoice Trigger button -->
<VBtn
block
prepend-icon="tabler-send"
class="mb-4"
@click="isSendPaymentSidebarVisible = true"
>
Send Invoice
</VBtn>
<VBtn
block
color="secondary"
variant="tonal"
class="mb-4"
>
Download
</VBtn>
<div class="d-flex flex-wrap gap-4">
<VBtn
variant="tonal"
color="secondary"
class="flex-grow-1"
@click="printInvoice"
>
Print
</VBtn>
<VBtn
color="secondary"
variant="tonal"
class="mb-4 flex-grow-1"
:to="{ name: 'apps-invoice-edit-id', params: { id: route.params.id } }"
>
Edit
</VBtn>
</div>
<!-- 👉 Add Payment trigger button -->
<VBtn
block
prepend-icon="tabler-currency-dollar"
color="success"
@click="isAddPaymentSidebarVisible = true"
>
Add Payment
</VBtn>
</VCardText>
</VCard>
</VCol>
</VRow>
<!-- 👉 Add Payment Sidebar -->
<InvoiceAddPaymentDrawer v-model:is-drawer-open="isAddPaymentSidebarVisible" />
<!-- 👉 Send Invoice Sidebar -->
<InvoiceSendInvoiceDrawer v-model:is-drawer-open="isSendPaymentSidebarVisible" />
</section>
<section v-else>
<VAlert
type="error"
variant="tonal"
>
Invoice with ID {{ route.params.id }} not found!
</VAlert>
</section>
</template>
<style lang="scss">
.invoice-preview-table {
--v-table-header-color: var(--v-theme-surface);
&.v-table .v-table__wrapper table thead tr th {
border-block-end: 1px solid rgba(var(--v-border-color), var(--v-border-opacity)) !important;
}
}
@media print {
.v-theme--dark {
--v-theme-surface: 255, 255, 255;
--v-theme-on-surface: 47, 43, 61;
--v-theme-on-background: 47, 43, 61;
}
body {
background: none !important;
}
.invoice-header-preview,
.invoice-preview-wrapper {
padding: 0 !important;
}
.product-buy-now {
display: none;
}
.v-navigation-drawer,
.layout-vertical-nav,
.app-customizer-toggler,
.layout-footer,
.layout-navbar,
.layout-navbar-and-nav-container {
display: none;
}
.v-card {
box-shadow: none !important;
.print-row {
flex-direction: row !important;
}
}
.layout-content-wrapper {
padding-inline-start: 0 !important;
}
.v-table__wrapper {
overflow: hidden !important;
}
.vue-devtools__anchor {
display: none;
}
}
</style>