aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorVidhu Kant Sharma <vidhukant@vidhukant.com>2024-04-06 10:26:44 +0530
committerVidhu Kant Sharma <vidhukant@vidhukant.com>2024-04-06 10:26:44 +0530
commit63823d41addec00556a93eabffa455630d169ca6 (patch)
tree2b548e2a6b00ae7ef3859e4a4cb807329a62a4a2 /src
parenta26ab9f60314420aad1c7d2a328d299f503532fa (diff)
added basic (and incomplete) print preview template
Diffstat (limited to 'src')
-rw-r--r--src/assets/placeholderlogo.pngbin0 -> 10419 bytes
-rw-r--r--src/assets/placeholderqr.pngbin0 -> 6999 bytes
-rw-r--r--src/classes/user.ts21
-rw-r--r--src/components/InvoiceHeaderEditor.vue (renamed from src/components/invoice_header_editor.vue)28
-rw-r--r--src/components/PrintPreview.vue31
-rw-r--r--src/components/PrintPreviewHeader.vue99
-rw-r--r--src/components/PrintPreviewItemsList.vue59
-rw-r--r--src/components/PrintPreviewRecipientDetails.vue146
-rw-r--r--src/components/invoice_header.vue1
-rw-r--r--src/views/NewInvoice.vue4
-rw-r--r--src/views/ViewInvoice.vue40
11 files changed, 394 insertions, 35 deletions
diff --git a/src/assets/placeholderlogo.png b/src/assets/placeholderlogo.png
new file mode 100644
index 0000000..6675ddd
--- /dev/null
+++ b/src/assets/placeholderlogo.png
Binary files differ
diff --git a/src/assets/placeholderqr.png b/src/assets/placeholderqr.png
new file mode 100644
index 0000000..634fe46
--- /dev/null
+++ b/src/assets/placeholderqr.png
Binary files differ
diff --git a/src/classes/user.ts b/src/classes/user.ts
new file mode 100644
index 0000000..dc18717
--- /dev/null
+++ b/src/classes/user.ts
@@ -0,0 +1,21 @@
+export default class User {
+ FullName: string
+ FirmName: string
+ Gstin: string
+ Phone: string
+ Email: string
+ Website: string
+ Username: string
+ IsVerified: bool
+
+ constructor() {
+ this.FullName = ''
+ this.FirmName = ''
+ this.Gstin = ''
+ this.Phone = ''
+ this.Email = ''
+ this.Website = ''
+ this.Username = ''
+ this.IsVerified = false
+ }
+}
diff --git a/src/components/invoice_header_editor.vue b/src/components/InvoiceHeaderEditor.vue
index 83c5281..6fe8a25 100644
--- a/src/components/invoice_header_editor.vue
+++ b/src/components/InvoiceHeaderEditor.vue
@@ -4,6 +4,7 @@ import { useRouter } from 'vue-router'
import axios from 'axios'
import { useToast } from 'vue-toast-notification'
import Customer from "./../classes/customer"
+import User from "./../classes/user"
const toast = useToast({
position: 'top-right'
@@ -16,6 +17,7 @@ const submitting = ref(false) // shows spinner on continue button
const allCustomers = ref([])
const customer = ref(new Customer())
+const user = ref(new User())
const customerSelection = ref(null)
const invoiceDate = ref(new Date().toISOString().substr(0, 10))
@@ -36,12 +38,31 @@ const getAllCustomers = async () => {
gettingData.value = false
}
+const getUser = async () => {
+ user.value = new User()
+ gettingData.value = true
+
+ try {
+ const r = await axios.get('/user')
+ if (r.status === 200) {
+ user.value = r.data.data
+ }
+ } catch (err) {
+ toast.error('An unhandled exception occoured. Please check logs')
+ console.error(err)
+ }
+
+ gettingData.value = false
+ console.log(user)
+}
+
const submit = async (e: Event) => {
e.preventDefault()
submitting.value = true
try {
const c = toRaw(customer.value)
+ const u = toRaw(user.value)
const res = await axios.post('/invoice', {
"invoicedate": new Date(toRaw(invoiceDate.value)).toISOString(),
@@ -68,6 +89,12 @@ const submit = async (e: Event) => {
"customerphone": c.Phone,
"customeremail": c.Email,
"customerwebsite": c.Website,
+ "issuerfirmname": u.FirmName,
+ "issuerfirmaddress": u.AddressText,
+ "issuerfirmgstin": u.Gstin,
+ "issuerfirmphone": u.Phone,
+ "issuerfirmemail": u.Email,
+ "issuerfirmwebsite": u.Website,
})
route.push({ name: "edit-invoice", params: { id: res.data.data.ID }})
@@ -110,6 +137,7 @@ const refreshCustomer = async () => {
}
onMounted(() => {
+ getUser()
getAllCustomers()
})
</script>
diff --git a/src/components/PrintPreview.vue b/src/components/PrintPreview.vue
new file mode 100644
index 0000000..ecbda88
--- /dev/null
+++ b/src/components/PrintPreview.vue
@@ -0,0 +1,31 @@
+<script setup lang="ts">
+ import PrintPreviewHeader from './PrintPreviewHeader.vue'
+ import PrintPreviewRecipientDetails from './PrintPreviewRecipientDetails.vue'
+ import PrintPreviewItemsList from './PrintPreviewItemsList.vue'
+
+ const props = defineProps(["invoice"])
+
+ setTimeout(() => {
+ console.log(props.invoice)
+ }, 1000)
+</script>
+
+<template>
+ <div class="print-preview">
+ <PrintPreviewHeader
+ :invoice="props.invoice" />
+ <PrintPreviewRecipientDetails
+ :invoice="props.invoice"/>
+ <PrintPreviewItemsList
+ :items="props.invoice.Items"/>
+ </div>
+</template>
+
+<style>
+.print-preview * {
+ font-size: 9pt;
+}
+.print-preview p {
+ margin: 0;
+}
+</style>
diff --git a/src/components/PrintPreviewHeader.vue b/src/components/PrintPreviewHeader.vue
new file mode 100644
index 0000000..33f82f2
--- /dev/null
+++ b/src/components/PrintPreviewHeader.vue
@@ -0,0 +1,99 @@
+<script setup lang="ts">
+ const props = defineProps(["invoice"])
+</script>
+
+<template>
+ <div class="print-preview-header">
+ <div class="logo-container">
+ <img src="../assets/placeholderlogo.png"/>
+ </div>
+
+ <div>
+ <div class="firm-name">
+ <p>{{ props.invoice.IssuerFirmName }}</p>
+ </div>
+ <div class="firm-address">
+ {{ props.invoice.IssuerFirmAddress }}
+ </div>
+ </div>
+
+ <div class="header-col3">
+ <div class="firm-contact">
+ <div class="label">
+ <span><strong>Phone:</strong></span>
+ <span>{{ props.invoice.IssuerFirmPhone }}</span>
+ </div>
+ <div class="label">
+ <span><strong>Email:</strong></span>
+ <span>{{ props.invoice.IssuerFirmEmail }}</span>
+ </div>
+ <div class="label">
+ <span><strong>Website:</strong></span>
+ <span>{{ props.invoice.IssuerFirmWebsite }}</span>
+ </div>
+ <div class="label">
+ <span><strong>GSTIN:</strong></span>
+ <span>{{ props.invoice.IssuerFirmGstin }}</span>
+ </div>
+ </div>
+ <div class="qr-container">
+ <img src="../assets/placeholderqr.png"/>
+ </div>
+ </div>
+ </div>
+</template>
+
+<style>
+.print-preview-header {
+ display: grid;
+ grid-template-columns: 1.7in 2.4in auto;
+ grid-column-gap: 1em;
+ width: 100%;
+ margin-bottom: 1em;
+}
+.logo-container {
+ display: flex;
+ justify-content: center;
+ align-items: flex-start;
+ max-width: 1.7in;
+}
+.logo-container img {
+ max-width: 100%;
+ max-height: 100%;
+}
+.firm-name {
+ height: 2em;
+}
+.firm-name p {
+ line-height: 0.8em;
+ font-size: 1.6em;
+ font-weight: bold;
+}
+.firm-address {
+ font-size: 0.9em;
+ line-height: 1.2em;
+ white-space: pre-wrap;
+}
+.header-col3 {
+ display: grid;
+ grid-template-columns: auto 0.8in;
+}
+.firm-contact {
+ display: flex;
+ flex-direction: column;
+ font-size: 0.9em;
+}
+.firm-contact .label {
+ display: grid;
+ grid-template-columns: 4.5em auto;
+}
+.qr-container {
+ display: flex;
+ justify-content: flex-end;
+ align-items: flex-start;
+ width: 100%;
+}
+.qr-container img {
+ max-width: 100%;
+}
+</style>
diff --git a/src/components/PrintPreviewItemsList.vue b/src/components/PrintPreviewItemsList.vue
new file mode 100644
index 0000000..72f0fc1
--- /dev/null
+++ b/src/components/PrintPreviewItemsList.vue
@@ -0,0 +1,59 @@
+<script setup lang="ts">
+ const props = defineProps(["items"])
+</script>
+
+<template>
+ <div class="items-list-wrapper">
+ <div class="items-list-header">
+ <div class="cell">#</div>
+ <div class="cell">Item Name</div>
+ <div class="cell">HSN</div>
+ <div class="cell">Qty</div>
+ <div class="cell">Unit Price</div>
+ <div class="cell">Discount</div>
+ <div class="cell">Taxable Value</div>
+ <div class="cell">GST</div>
+ <div class="cell">Total</div>
+ </div>
+ <div class="item-list">
+ <div v-for="(item, index) in props.items" :key="item['id']" class="item-list-row">
+ <div class="cell">{{ index + 1 }}</div>
+ <div class="cell">{{ item.Name }}</div>
+ <div class="cell">{{ item.HSN }}</div>
+ <div class="cell">1</div>
+ <div class="cell">100</div>
+ <div class="cell">0</div>
+ <div class="cell">10</div>
+ <div class="cell">18</div>
+ <div class="cell">1000</div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<style>
+.items-list-wrapper {
+ margin-top: 1em;
+ border-top: 1px solid gray;
+ border-left: 1px solid gray;
+}
+.cell {
+ border-right: 1px solid gray;
+ border-bottom: 1px solid gray;
+}
+.items-list-header, .item-list-row {
+ display: grid;
+ grid-template-columns: 0.3in 2in 0.5in 0.5in 0.5in 0.6in 0.77in 1.5in 0.6in;
+}
+.items-list-header .cell {
+ font-weight: bold;
+ text-align: center;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+}
+.item-list {
+ display: flex;
+ flex-direction: column;
+}
+</style>
diff --git a/src/components/PrintPreviewRecipientDetails.vue b/src/components/PrintPreviewRecipientDetails.vue
new file mode 100644
index 0000000..6f1f5b7
--- /dev/null
+++ b/src/components/PrintPreviewRecipientDetails.vue
@@ -0,0 +1,146 @@
+<script setup lang="ts">
+ const props = defineProps(["invoice"])
+</script>
+
+<template>
+ <div class="print-preview-recipient-info">
+ <div class="recipient-info--row1">
+ <div class="recipient-info--col1">
+ </div>
+ <div class="recipient-info--col2">
+ <span class="invoice-label">
+ <span><strong>INVOICE</strong></span>
+ <span>Original for Recipient</span>
+ </span>
+ </div>
+ </div>
+ <div class="recipient-info--row2">
+ <div class="recipient-info--col1 recipient-contact">
+ <div class="label">
+ <span><strong>Bill To</strong></span>
+ <span>{{ props.invoice.CustomerName }}</span>
+ </div>
+ <div class="label">
+ <span><strong>Address</strong></span>
+ <span>
+ {{ props.invoice.BillingAddress.AddressText }}<br>
+ {{ props.invoice.BillingAddress.City }}
+ ({{ props.invoice.BillingAddress.PostalCode }}),
+ {{ props.invoice.BillingAddress.State }},
+ {{ props.invoice.BillingAddress.Country }}
+ </span>
+ </div>
+ <div class="label">
+ <span><strong>Contact</strong></span>
+ <span>{{ props.invoice.CustomerContactName }}</span>
+ </div>
+ <div class="label">
+ <span><strong>Phone</strong></span>
+ <span>{{ props.invoice.CustomerPhone }}</span>
+ </div>
+ <div class="label">
+ <span><strong>Email</strong></span>
+ <span>{{ props.invoice.CustomerEmail }}</span>
+ </div>
+ <div class="label">
+ <span><strong>Website</strong></span>
+ <span>{{ props.invoice.CustomerWebsite }}</span>
+ </div>
+ <div class="label">
+ <span><strong>GSTIN</strong></span>
+ <span>{{ props.invoice.CustomerGstin }}</span>
+ </div>
+ </div>
+ <div class="recipient-info--col2">
+ <div class="invoice-meta">
+ <div class="label">
+ <span><strong>Invoice Number: </strong></span>
+ <span>{{ props.invoice.InvoiceNumber }}</span>
+ </div>
+ <div class="label">
+ <span><strong>Invoice Date: </strong></span>
+ <span>{{ new Date(props.invoice.InvoiceDate).toLocaleDateString() }}</span>
+ </div>
+ </div>
+ <div class="shipping-address">
+ <div class="label">
+ <span><strong>Shipping Address</strong></span>
+ <span>
+ {{ props.invoice.ShippingAddress.AddressText }}<br>
+ {{ props.invoice.ShippingAddress.City }}
+ ({{ props.invoice.ShippingAddress.PostalCode }}),
+ {{ props.invoice.ShippingAddress.State }},
+ {{ props.invoice.ShippingAddress.Country }}
+ </span>
+
+ <span><strong>City</strong></span>
+ <span>{{ props.invoice.ShippingAddress.City }}</span>
+
+ <span><strong>State</strong></span>
+ <span>{{ props.invoice.ShippingAddress.State }}</span>
+
+ <span><strong>ZIP Code</strong></span>
+ <span>{{ props.invoice.ShippingAddress.PostalCode }}</span>
+
+ <span><strong>Country</strong></span>
+ <span>{{ props.invoice.ShippingAddress.Country }}</span>
+ </div>
+ </div>
+ </div>
+ </div>
+ </div>
+</template>
+
+<style>
+.print-preview-recipient-info {
+ border: 1px solid gray;
+ display: grid;
+ grid-template-rows: 2em auto;
+ width: 100%;
+}
+.recipient-info--row1,
+.recipient-info--row2 {
+ display: grid;
+ grid-template-columns: 3fr 4fr;
+}
+.recipient-info--row1 {
+ border-bottom: 1px solid gray;
+}
+.recipient-info--col1 {
+ border-right: 1px solid gray;
+}
+.recipient-info--col1 {
+ padding: 0.1em 0.3em;
+}
+.invoice-label {
+ width: 100%;
+ height: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 0 0.3em;
+}
+.recipient-contact {
+ display: flex;
+ flex-direction: column;
+}
+.recipient-contact .label {
+ display: grid;
+ grid-template-columns: 5em auto;
+}
+.invoice-meta {
+ width: 100%;
+ border-bottom: 1px solid gray;
+ padding: 0.3em 0.3em;
+ display: grid;
+ grid-template-columns: 1fr 1fr;
+}
+.invoice-meta .label, .shipping-address .label {
+ display: grid;
+ grid-template-columns: 9em auto;
+}
+.shipping-address {
+ width: 100%;
+ padding: 0.1em 0.3em;
+}
+</style>
diff --git a/src/components/invoice_header.vue b/src/components/invoice_header.vue
index 5a57fc8..fe35980 100644
--- a/src/components/invoice_header.vue
+++ b/src/components/invoice_header.vue
@@ -1,5 +1,4 @@
<script setup lang="ts">
-import { defineProps } from 'vue'
const props = defineProps(["invoice"])
</script>
diff --git a/src/views/NewInvoice.vue b/src/views/NewInvoice.vue
index 06d3ec7..2e2230c 100644
--- a/src/views/NewInvoice.vue
+++ b/src/views/NewInvoice.vue
@@ -1,7 +1,7 @@
<script setup lang="ts">
-import invoiceHeaderEditor from './../components/invoice_header_editor.vue'
+import InvoiceHeaderEditor from './../components/InvoiceHeaderEditor.vue'
</script>
<template>
- <invoiceHeaderEditor />
+ <InvoiceHeaderEditor />
</template>
diff --git a/src/views/ViewInvoice.vue b/src/views/ViewInvoice.vue
index 5f2a783..b8b7f2d 100644
--- a/src/views/ViewInvoice.vue
+++ b/src/views/ViewInvoice.vue
@@ -11,6 +11,8 @@ import invoiceHeader from './../components/invoice_header.vue'
import invoiceItemsTable from './../components/invoice_items_table.vue'
import invoiceSummary from './../components/invoice_summary.vue'
+import PrintPreview from './../components/PrintPreview.vue'
+
const toast = useToast({
position: 'top-right'
})
@@ -52,50 +54,24 @@ onMounted(() => {
<template>
<div id="print-preview" class="bg-white text-black">
- <invoiceHeader
- :invoice="invoice" />
- <invoiceItemsTable
- preview=true
- :items="items"
- :isLoading="itemsTableIsLoading" />
- <invoiceSummary
- :items="items"
- :isLoading="itemsTableIsLoading"
- />
+ <PrintPreview :invoice="invoice"/>
</div>
<button id="print-button" class="btn btn-primary" @click="handlePrint">Print</button>
</template>
<style>
#print-preview {
- width: 670px;
-}
-#print-preview * {
- font-size: 12pt !important;
-}
-#print-preview .sup {
-}
-#print-preview table {
- width: 100%;
-}
-#print-preview table * {
- font-size: 10pt !important;
-}
-#print-preview .invoice-items-table {
- margin-bottom: auto !important;
-}
-#print-preview .invoice-summary {
- margin-top: auto !important;
-}
-#print-preview .item-name-cell span {
- max-width: 150pt !important;
+ max-height: 90vh;
+ /*display: none;*/
+ aspect-ratio: 1 / 1.414;
}
@media print {
body {
background-color: white;
}
#print-preview {
- width: auto !important;
+ display: block;
+ width: 100% !important;
height: 100% !important;
}
#sidebar, #navbar, #print-button, .btn {