aboutsummaryrefslogtreecommitdiff
path: root/src/components
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/components
parenta26ab9f60314420aad1c7d2a328d299f503532fa (diff)
added basic (and incomplete) print preview template
Diffstat (limited to 'src/components')
-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
6 files changed, 363 insertions, 1 deletions
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>