mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-27 19:51:09 -04:00
Merge branch 'master' of https://gitlab.com/mohit.panjvani/crater-web into recurring-index-refresh
This commit is contained in:
@ -23,9 +23,11 @@
|
||||
@click.stop
|
||||
>
|
||||
<div class="flex relative justify-between mb-2">
|
||||
<label class="flex-1 text-base font-medium text-left text-gray-900">
|
||||
{{ selectedCustomer.name }}
|
||||
</label>
|
||||
<BaseText
|
||||
:text="selectedCustomer.name"
|
||||
:length="30"
|
||||
class="flex-1 text-base font-medium text-left text-gray-900"
|
||||
/>
|
||||
<div class="flex">
|
||||
<a
|
||||
class="
|
||||
@ -302,8 +304,10 @@
|
||||
</span>
|
||||
|
||||
<div class="flex flex-col justify-center text-left">
|
||||
<label
|
||||
<BaseText
|
||||
v-if="customer.name"
|
||||
:text="customer.name"
|
||||
:length="30"
|
||||
class="
|
||||
m-0
|
||||
text-base
|
||||
@ -311,12 +315,11 @@
|
||||
leading-tight
|
||||
cursor-pointer
|
||||
"
|
||||
>
|
||||
{{ customer.name }}
|
||||
</label>
|
||||
|
||||
<label
|
||||
/>
|
||||
<BaseText
|
||||
v-if="customer.contact_name"
|
||||
:text="customer.contact_name"
|
||||
:length="30"
|
||||
class="
|
||||
m-0
|
||||
text-sm
|
||||
@ -324,9 +327,7 @@
|
||||
text-gray-400
|
||||
cursor-pointer
|
||||
"
|
||||
>
|
||||
{{ customer.contact_name }}
|
||||
</label>
|
||||
/>
|
||||
</div>
|
||||
</li>
|
||||
<div
|
||||
|
||||
31
resources/scripts/components/base/BaseText.vue
Normal file
31
resources/scripts/components/base/BaseText.vue
Normal file
@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<BaseCustomTag :tag="tag" :title="text">
|
||||
{{ displayText }}
|
||||
</BaseCustomTag>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from "vue"
|
||||
|
||||
const props = defineProps({
|
||||
tag: {
|
||||
type: String,
|
||||
default: 'div',
|
||||
},
|
||||
|
||||
text: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
|
||||
length: {
|
||||
type: Number,
|
||||
default: 0,
|
||||
}
|
||||
})
|
||||
|
||||
const displayText = computed(() => {
|
||||
|
||||
return props.text.length < props.length ? props.text : `${props.text.substring(0 , props.length)}...`
|
||||
})
|
||||
</script>
|
||||
@ -45,7 +45,7 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { watch, computed, ref, onMounted } from 'vue'
|
||||
import { watch, computed, ref, onBeforeUnmount } from 'vue'
|
||||
import { useGlobalStore } from '@/scripts/stores/global'
|
||||
import { useCompanyStore } from '@/scripts/stores/company'
|
||||
import { useExchangeRateStore } from '@/scripts/stores/exchange-rate'
|
||||
@ -110,7 +110,8 @@ watch(
|
||||
() => props.store[props.storeProp].currency_id,
|
||||
(v) => {
|
||||
onChangeCurrency(v)
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
watch(
|
||||
() => props.customerCurrency,
|
||||
@ -144,7 +145,7 @@ function setCustomerCurrency(v) {
|
||||
|
||||
async function onChangeCurrency(v) {
|
||||
if (v !== companyCurrency.value.id) {
|
||||
if (!props.isEdit) {
|
||||
if (!props.isEdit && v) {
|
||||
await getCurrenctExchangeRate(v)
|
||||
}
|
||||
|
||||
@ -170,4 +171,8 @@ function getCurrenctExchangeRate(v) {
|
||||
isFetching.value = false
|
||||
})
|
||||
}
|
||||
|
||||
onBeforeUnmount(() => {
|
||||
props.store.showExchangeRate = false
|
||||
})
|
||||
</script>
|
||||
|
||||
@ -140,6 +140,10 @@ export const showError = (error) => {
|
||||
showToaster('errors.not_allowed')
|
||||
break
|
||||
|
||||
case 'Email could not be sent to this email address.':
|
||||
showToaster('errors.email_could_not_be_sent')
|
||||
break
|
||||
|
||||
default:
|
||||
showToaster(error, false)
|
||||
break
|
||||
|
||||
@ -1383,7 +1383,8 @@
|
||||
"invalid_credentials": "Invalid Credentials.",
|
||||
"not_allowed": "Not Allowed",
|
||||
"login_invalid_credentials": "These credentials do not match our records.",
|
||||
"enter_valid_cron_format": "Please enter a valid cron format"
|
||||
"enter_valid_cron_format": "Please enter a valid cron format",
|
||||
"email_could_not_be_sent": "Email could not be sent to this email address."
|
||||
},
|
||||
"pdf_estimate_label": "Estimate",
|
||||
"pdf_estimate_number": "Estimate Number",
|
||||
|
||||
@ -278,6 +278,14 @@ export const useInvoiceStore = (useWindow = false) => {
|
||||
axios
|
||||
.post(`/api/v1/invoices/${data.id}/status`, data)
|
||||
.then((response) => {
|
||||
let pos = this.invoices.findIndex(
|
||||
(invoices) => invoices.id === data.id
|
||||
)
|
||||
|
||||
if (this.invoices[pos]) {
|
||||
this.invoices[pos].status = 'SENT'
|
||||
}
|
||||
|
||||
notificationStore.showNotification({
|
||||
type: 'success',
|
||||
message: global.t('invoices.mark_as_sent_successfully'),
|
||||
|
||||
@ -149,14 +149,19 @@
|
||||
</template>
|
||||
|
||||
<template #cell-name="{ row }">
|
||||
<router-link
|
||||
:to="{ path: `customers/${row.data.id}/view` }"
|
||||
class="font-medium text-primary-500 flex flex-col"
|
||||
>
|
||||
{{ row.data.name }}
|
||||
<span class="text-xs text-gray-400">
|
||||
{{ row.data.contact_name ? row.data.contact_name : '' }}</span
|
||||
>
|
||||
<router-link :to="{ path: `customers/${row.data.id}/view` }">
|
||||
<BaseText
|
||||
:text="row.data.name"
|
||||
:length="30"
|
||||
tag="span"
|
||||
class="font-medium text-primary-500 flex flex-col"
|
||||
/>
|
||||
<BaseText
|
||||
:text="row.data.contact_name ? row.data.contact_name : ''"
|
||||
:length="30"
|
||||
tag="span"
|
||||
class="text-xs text-gray-400"
|
||||
/>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
|
||||
@ -131,7 +131,9 @@
|
||||
style="border-top: 1px solid rgba(185, 193, 209, 0.41)"
|
||||
>
|
||||
<div>
|
||||
<div
|
||||
<BaseText
|
||||
:text="customer.name"
|
||||
:length="30"
|
||||
class="
|
||||
pr-2
|
||||
text-sm
|
||||
@ -142,11 +144,12 @@
|
||||
capitalize
|
||||
truncate
|
||||
"
|
||||
>
|
||||
{{ customer.name }}
|
||||
</div>
|
||||
<div
|
||||
/>
|
||||
|
||||
<BaseText
|
||||
v-if="customer.contact_name"
|
||||
:text="customer.contact_name"
|
||||
:length="30"
|
||||
class="
|
||||
mt-1
|
||||
text-xs
|
||||
@ -155,9 +158,7 @@
|
||||
leading-5
|
||||
text-gray-600
|
||||
"
|
||||
>
|
||||
{{ customer.contact_name }}
|
||||
</div>
|
||||
/>
|
||||
</div>
|
||||
<div class="flex-1 font-bold text-right whitespace-nowrap">
|
||||
<BaseFormatMoney
|
||||
|
||||
@ -184,7 +184,7 @@
|
||||
|
||||
<!-- Estimate date -->
|
||||
<template #cell-estimate_date="{ row }">
|
||||
{{ row.data.formatted_estimate_date }}
|
||||
{{ row.data.formatted_estimate_date }}
|
||||
</template>
|
||||
|
||||
<template #cell-estimate_number="{ row }">
|
||||
@ -197,9 +197,7 @@
|
||||
</template>
|
||||
|
||||
<template #cell-name="{ row }">
|
||||
<div>
|
||||
{{ row.data.customer.name }}
|
||||
</div>
|
||||
<BaseText :text="row.data.customer.name" :length="30" />
|
||||
</template>
|
||||
|
||||
<template #cell-status="{ row }">
|
||||
@ -209,7 +207,10 @@
|
||||
</template>
|
||||
|
||||
<template #cell-total="{ row }">
|
||||
<BaseFormatMoney :amount="row.data.total" />
|
||||
<BaseFormatMoney
|
||||
:amount="row.data.total"
|
||||
:currency="row.data.customer.currency"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Actions -->
|
||||
|
||||
@ -180,7 +180,9 @@
|
||||
style="border-bottom: 1px solid rgba(185, 193, 209, 0.41)"
|
||||
>
|
||||
<div class="flex-2">
|
||||
<div
|
||||
<BaseText
|
||||
:text="estimate.customer.name"
|
||||
:length="30"
|
||||
class="
|
||||
pr-2
|
||||
mb-2
|
||||
@ -192,10 +194,8 @@
|
||||
capitalize
|
||||
truncate
|
||||
"
|
||||
>
|
||||
{{ estimate.customer.name }}
|
||||
</div>
|
||||
|
||||
/>
|
||||
|
||||
<div
|
||||
class="
|
||||
mt-1
|
||||
|
||||
@ -422,14 +422,21 @@ async function searchCustomer(search) {
|
||||
}
|
||||
|
||||
async function loadData() {
|
||||
expenseStore.currentExpense.currency_id = companyStore.selectedCompanyCurrency.id
|
||||
expenseStore.currentExpense.selectedCurrency = companyStore.selectedCompanyCurrency
|
||||
if (!isEdit.value) {
|
||||
expenseStore.currentExpense.currency_id =
|
||||
companyStore.selectedCompanyCurrency.id
|
||||
expenseStore.currentExpense.selectedCurrency =
|
||||
companyStore.selectedCompanyCurrency
|
||||
}
|
||||
|
||||
isFetchingInitialData.value = true
|
||||
await expenseStore.fetchPaymentModes({ limit: 'all' })
|
||||
|
||||
if (isEdit.value) {
|
||||
await expenseStore.fetchExpense(route.params.id)
|
||||
|
||||
expenseStore.currentExpense.currency_id =
|
||||
expenseStore.currentExpense.selectedCurrency.id
|
||||
} else if (route.query.customer) {
|
||||
expenseStore.currentExpense.customer_id = route.query.customer
|
||||
}
|
||||
|
||||
@ -181,12 +181,15 @@
|
||||
<template #cell-amount="{ row }">
|
||||
<BaseFormatMoney
|
||||
:amount="row.data.amount"
|
||||
:currency="companyStore.selectedCompanyCurrency"
|
||||
:currency="row.data.currency"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #cell-user_name="{ row }">
|
||||
{{ row.data.customer ? row.data.customer.name : '-' }}
|
||||
<BaseText
|
||||
:text="row.data.customer ? row.data.customer.name : '-'"
|
||||
:length="30"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #cell-notes="{ row }">
|
||||
|
||||
@ -179,7 +179,10 @@
|
||||
</template>
|
||||
|
||||
<template #cell-name="{ row }">
|
||||
{{ row.data.customer.name }}
|
||||
<BaseText
|
||||
:text="row.data.customer.name"
|
||||
:length="30"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Invoice Number -->
|
||||
@ -194,7 +197,7 @@
|
||||
|
||||
<!-- Invoice date -->
|
||||
<template #cell-invoice_date="{ row }">
|
||||
{{ row.data.formatted_invoice_date }}
|
||||
{{ row.data.formatted_invoice_date }}
|
||||
</template>
|
||||
|
||||
<!-- Invoice Total -->
|
||||
|
||||
@ -381,7 +381,9 @@ onSearched = debounce(onSearched, 500)
|
||||
style="border-bottom: 1px solid rgba(185, 193, 209, 0.41)"
|
||||
>
|
||||
<div class="flex-2">
|
||||
<div
|
||||
<BaseText
|
||||
:text="invoice.customer.name"
|
||||
:length="30"
|
||||
class="
|
||||
pr-2
|
||||
mb-2
|
||||
@ -393,9 +395,7 @@ onSearched = debounce(onSearched, 500)
|
||||
capitalize
|
||||
truncate
|
||||
"
|
||||
>
|
||||
{{ invoice.customer.name }}
|
||||
</div>
|
||||
/>
|
||||
|
||||
<div
|
||||
class="
|
||||
|
||||
@ -161,9 +161,11 @@
|
||||
</template>
|
||||
|
||||
<template #cell-name="{ row }">
|
||||
<span>
|
||||
{{ row.data.customer.name }}
|
||||
</span>
|
||||
<BaseText
|
||||
:text="row.data.customer.name"
|
||||
:length="30"
|
||||
tag="span"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template #cell-payment_mode="{ row }">
|
||||
@ -185,7 +187,7 @@
|
||||
<template #cell-amount="{ row }">
|
||||
<BaseFormatMoney
|
||||
:amount="row.data.amount"
|
||||
:currency="companyStore.selectedCompanyCurrency"
|
||||
:currency="row.data.customer.currency"
|
||||
/>
|
||||
</template>
|
||||
|
||||
|
||||
@ -162,7 +162,9 @@
|
||||
style="border-bottom: 1px solid rgba(185, 193, 209, 0.41)"
|
||||
>
|
||||
<div class="flex-2">
|
||||
<div
|
||||
<BaseText
|
||||
:text="payment?.customer?.name "
|
||||
:length="30"
|
||||
class="
|
||||
pr-2
|
||||
mb-2
|
||||
@ -174,9 +176,7 @@
|
||||
capitalize
|
||||
truncate
|
||||
"
|
||||
>
|
||||
{{ payment?.customer?.name }}
|
||||
</div>
|
||||
/>
|
||||
|
||||
<div
|
||||
class="
|
||||
|
||||
@ -124,7 +124,11 @@
|
||||
"
|
||||
>
|
||||
<!-- Tabs -->
|
||||
<BaseTabGroup class="-mb-5" @change="setStatusFilter">
|
||||
<BaseTabGroup
|
||||
class="-mb-5"
|
||||
:default-index="currentStatusIndex"
|
||||
@change="setStatusFilter"
|
||||
>
|
||||
<BaseTab :title="$t('recurring_invoices.active')" filter="ACTIVE" />
|
||||
<BaseTab :title="$t('recurring_invoices.on_hold')" filter="ON_HOLD" />
|
||||
<BaseTab :title="$t('recurring_invoices.all')" filter="ALL" />
|
||||
@ -189,24 +193,29 @@
|
||||
|
||||
<!-- Starts at -->
|
||||
<template #cell-starts_at="{ row }">
|
||||
{{ row.data.formatted_starts_at }}
|
||||
{{ row.data.formatted_starts_at }}
|
||||
</template>
|
||||
|
||||
<!-- Customer -->
|
||||
<template #cell-customer="{ row }">
|
||||
<router-link
|
||||
:to="{ path: `recurring-invoices/${row.data.id}/view` }"
|
||||
class="font-medium text-primary-500 flex flex-col"
|
||||
>
|
||||
{{ row.data.customer.name }}
|
||||
<router-link :to="{ path: `recurring-invoices/${row.data.id}/view` }">
|
||||
<BaseText
|
||||
:text="row.data.customer.name"
|
||||
:length="30"
|
||||
tag="span"
|
||||
class="font-medium text-primary-500 flex flex-col"
|
||||
/>
|
||||
|
||||
<span class="text-xs text-gray-400">
|
||||
{{
|
||||
<BaseText
|
||||
:text="
|
||||
row.data.customer.contact_name
|
||||
? row.data.customer.contact_name
|
||||
: ''
|
||||
}}
|
||||
</span>
|
||||
"
|
||||
:length="30"
|
||||
tag="span"
|
||||
class="text-xs text-gray-400"
|
||||
/>
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
@ -331,6 +340,10 @@ onUnmounted(() => {
|
||||
}
|
||||
})
|
||||
|
||||
const currentStatusIndex = computed(() => {
|
||||
return statusList.value.findIndex((status) => status === filters.status)
|
||||
})
|
||||
|
||||
function canViewActions() {
|
||||
return userStore.hasAbilities([
|
||||
abilities.DELETE_RECURRING_INVOICE,
|
||||
|
||||
@ -238,7 +238,9 @@ onSearched = debounce(onSearched, 500)
|
||||
style="border-bottom: 1px solid rgba(185, 193, 209, 0.41)"
|
||||
>
|
||||
<div class="flex-2">
|
||||
<div
|
||||
<BaseText
|
||||
:text="invoice.customer.name"
|
||||
:length="30"
|
||||
class="
|
||||
pr-2
|
||||
mb-2
|
||||
@ -250,10 +252,8 @@ onSearched = debounce(onSearched, 500)
|
||||
capitalize
|
||||
truncate
|
||||
"
|
||||
>
|
||||
{{ invoice.customer.name }}
|
||||
</div>
|
||||
|
||||
/>
|
||||
|
||||
<div
|
||||
class="
|
||||
mt-1
|
||||
|
||||
Reference in New Issue
Block a user