mirror of
https://github.com/crater-invoice/crater.git
synced 2025-10-28 04:01:10 -04:00
v6 update
This commit is contained in:
28
resources/scripts/admin/views/dashboard/Dashboard.vue
Normal file
28
resources/scripts/admin/views/dashboard/Dashboard.vue
Normal file
@ -0,0 +1,28 @@
|
||||
<script setup>
|
||||
import DashboardStats from '../dashboard/DashboardStats.vue'
|
||||
import DashboardChart from '../dashboard/DashboardChart.vue'
|
||||
import DashboardTable from '../dashboard/DashboardTable.vue'
|
||||
import { useUserStore } from '@/scripts/admin/stores/user'
|
||||
import { onMounted } from 'vue'
|
||||
import { useRoute, useRouter } from 'vue-router'
|
||||
|
||||
const route = useRoute()
|
||||
const userStore = useUserStore()
|
||||
const router = useRouter()
|
||||
|
||||
onMounted(() => {
|
||||
if (route.meta.ability && !userStore.hasAbilities(route.meta.ability)) {
|
||||
router.push({ name: 'account.settings' })
|
||||
} else if (route.meta.isOwner && !userStore.currentUser.is_owner) {
|
||||
router.push({ name: 'account.settings' })
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<BasePage>
|
||||
<DashboardStats />
|
||||
<DashboardChart />
|
||||
<DashboardTable />
|
||||
</BasePage>
|
||||
</template>
|
||||
186
resources/scripts/admin/views/dashboard/DashboardChart.vue
Normal file
186
resources/scripts/admin/views/dashboard/DashboardChart.vue
Normal file
@ -0,0 +1,186 @@
|
||||
<template>
|
||||
<div>
|
||||
<div
|
||||
v-if="dashboardStore.isDashboardDataLoaded"
|
||||
class="grid grid-cols-10 mt-8 bg-white rounded shadow"
|
||||
>
|
||||
<!-- Chart -->
|
||||
<div
|
||||
class="
|
||||
grid grid-cols-1
|
||||
col-span-10
|
||||
px-4
|
||||
py-5
|
||||
lg:col-span-7
|
||||
xl:col-span-8
|
||||
sm:p-6
|
||||
"
|
||||
>
|
||||
<div class="flex justify-between mt-1 mb-4 flex-col md:flex-row">
|
||||
<h6 class="flex items-center sw-section-title h-10">
|
||||
<BaseIcon name="ChartSquareBarIcon" class="text-primary-400 mr-1" />
|
||||
{{ $t('dashboard.monthly_chart.title') }}
|
||||
</h6>
|
||||
|
||||
<div class="w-full my-2 md:m-0 md:w-40 h-10">
|
||||
<BaseMultiselect
|
||||
v-model="selectedYear"
|
||||
:options="years"
|
||||
:allow-empty="false"
|
||||
:show-labels="false"
|
||||
:placeholder="$t('dashboard.select_year')"
|
||||
:can-deselect="false"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<LineChart
|
||||
:invoices="dashboardStore.chartData.invoiceTotals"
|
||||
:expenses="dashboardStore.chartData.expenseTotals"
|
||||
:receipts="dashboardStore.chartData.receiptTotals"
|
||||
:income="dashboardStore.chartData.netIncomeTotals"
|
||||
:labels="dashboardStore.chartData.months"
|
||||
class="sm:w-full"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- Chart Labels -->
|
||||
<div
|
||||
class="
|
||||
grid grid-cols-3
|
||||
col-span-10
|
||||
text-center
|
||||
border-t border-l border-gray-200 border-solid
|
||||
lg:border-t-0 lg:text-right lg:col-span-3
|
||||
xl:col-span-2
|
||||
lg:grid-cols-1
|
||||
"
|
||||
>
|
||||
<div class="p-6">
|
||||
<span class="text-xs leading-5 lg:text-sm">
|
||||
{{ $t('dashboard.chart_info.total_sales') }}
|
||||
</span>
|
||||
<br />
|
||||
<span class="block mt-1 text-xl font-semibold leading-8 lg:text-2xl">
|
||||
<BaseFormatMoney
|
||||
:amount="dashboardStore.totalSales"
|
||||
:currency="companyStore.selectedCompanyCurrency"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<span class="text-xs leading-5 lg:text-sm">
|
||||
{{ $t('dashboard.chart_info.total_receipts') }}
|
||||
</span>
|
||||
<br />
|
||||
<span
|
||||
class="
|
||||
block
|
||||
mt-1
|
||||
text-xl
|
||||
font-semibold
|
||||
leading-8
|
||||
lg:text-2xl
|
||||
text-green-400
|
||||
"
|
||||
>
|
||||
<BaseFormatMoney
|
||||
:amount="dashboardStore.totalReceipts"
|
||||
:currency="companyStore.selectedCompanyCurrency"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div class="p-6">
|
||||
<span class="text-xs leading-5 lg:text-sm">
|
||||
{{ $t('dashboard.chart_info.total_expense') }}
|
||||
</span>
|
||||
<br />
|
||||
<span
|
||||
class="
|
||||
block
|
||||
mt-1
|
||||
text-xl
|
||||
font-semibold
|
||||
leading-8
|
||||
lg:text-2xl
|
||||
text-red-400
|
||||
"
|
||||
>
|
||||
<BaseFormatMoney
|
||||
:amount="dashboardStore.totalExpenses"
|
||||
:currency="companyStore.selectedCompanyCurrency"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
col-span-3
|
||||
p-6
|
||||
border-t border-gray-200 border-solid
|
||||
lg:col-span-1
|
||||
"
|
||||
>
|
||||
<span class="text-xs leading-5 lg:text-sm">
|
||||
{{ $t('dashboard.chart_info.net_income') }}
|
||||
</span>
|
||||
<br />
|
||||
<span
|
||||
class="
|
||||
block
|
||||
mt-1
|
||||
text-xl
|
||||
font-semibold
|
||||
leading-8
|
||||
lg:text-2xl
|
||||
text-primary-500
|
||||
"
|
||||
>
|
||||
<BaseFormatMoney
|
||||
:amount="dashboardStore.totalNetIncome"
|
||||
:currency="companyStore.selectedCompanyCurrency"
|
||||
/>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ChartPlaceholder v-else />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, watch, inject } from 'vue'
|
||||
import { useDashboardStore } from '@/scripts/admin/stores/dashboard'
|
||||
import { useCompanyStore } from '@/scripts/admin/stores/company'
|
||||
import LineChart from '@/scripts/admin/components/charts/LineChart.vue'
|
||||
import ChartPlaceholder from './DashboardChartPlaceholder.vue'
|
||||
import abilities from '@/scripts/admin/stub/abilities'
|
||||
import { useUserStore } from '@/scripts/admin/stores/user'
|
||||
|
||||
const dashboardStore = useDashboardStore()
|
||||
const companyStore = useCompanyStore()
|
||||
|
||||
const utils = inject('utils')
|
||||
const userStore = useUserStore()
|
||||
const years = ref(['This year', 'Previous year'])
|
||||
const selectedYear = ref('This year')
|
||||
|
||||
watch(
|
||||
selectedYear,
|
||||
(val) => {
|
||||
if (val === 'Previous year') {
|
||||
let params = { previous_year: true }
|
||||
loadData(params)
|
||||
} else {
|
||||
loadData()
|
||||
}
|
||||
},
|
||||
{ immediate: true }
|
||||
)
|
||||
|
||||
async function loadData(params) {
|
||||
if (userStore.hasAbilities(abilities.DASHBOARD)) {
|
||||
await dashboardStore.loadData(params)
|
||||
}
|
||||
}
|
||||
</script>
|
||||
@ -0,0 +1,88 @@
|
||||
<template>
|
||||
<BaseContentPlaceholders
|
||||
class="grid grid-cols-10 mt-8 bg-white rounded shadow"
|
||||
>
|
||||
<!-- Chart -->
|
||||
<div
|
||||
class="
|
||||
grid grid-cols-1
|
||||
col-span-10
|
||||
px-4
|
||||
py-5
|
||||
lg:col-span-7
|
||||
xl:col-span-8
|
||||
sm:p-8
|
||||
"
|
||||
>
|
||||
<div class="flex items-center justify-between mb-2 xl:mb-4">
|
||||
<BaseContentPlaceholdersText class="h-10 w-36" :lines="1" />
|
||||
<BaseContentPlaceholdersText class="h-10 w-36 !mt-0" :lines="1" />
|
||||
</div>
|
||||
<BaseContentPlaceholdersBox class="h-80 xl:h-72 sm:w-full" />
|
||||
</div>
|
||||
|
||||
<!-- Chart Labels -->
|
||||
<div
|
||||
class="
|
||||
grid grid-cols-3
|
||||
col-span-10
|
||||
text-center
|
||||
border-t border-l border-gray-200 border-solid
|
||||
lg:border-t-0 lg:text-right lg:col-span-3
|
||||
xl:col-span-2
|
||||
lg:grid-cols-1
|
||||
"
|
||||
>
|
||||
<div
|
||||
class="
|
||||
flex flex-col
|
||||
items-center
|
||||
justify-center
|
||||
p-6
|
||||
lg:justify-end lg:items-end
|
||||
"
|
||||
>
|
||||
<BaseContentPlaceholdersText class="h-3 w-14 xl:h-4" :lines="1" />
|
||||
<BaseContentPlaceholdersText class="w-20 h-5 xl:h-6" :lines="1" />
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
flex flex-col
|
||||
items-center
|
||||
justify-center
|
||||
p-6
|
||||
lg:justify-end lg:items-end
|
||||
"
|
||||
>
|
||||
<BaseContentPlaceholdersText class="h-3 w-14 xl:h-4" :lines="1" />
|
||||
<BaseContentPlaceholdersText class="w-20 h-5 xl:h-6" :lines="1" />
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
flex flex-col
|
||||
items-center
|
||||
justify-center
|
||||
p-6
|
||||
lg:justify-end lg:items-end
|
||||
"
|
||||
>
|
||||
<BaseContentPlaceholdersText class="h-3 w-14 xl:h-4" :lines="1" />
|
||||
<BaseContentPlaceholdersText class="w-20 h-5 xl:h-6" :lines="1" />
|
||||
</div>
|
||||
<div
|
||||
class="
|
||||
flex flex-col
|
||||
items-center
|
||||
justify-center
|
||||
col-span-3
|
||||
p-6
|
||||
border-t border-gray-200 border-solid
|
||||
lg:justify-end lg:items-end lg:col-span-1
|
||||
"
|
||||
>
|
||||
<BaseContentPlaceholdersText class="h-3 w-14 xl:h-4" :lines="1" />
|
||||
<BaseContentPlaceholdersText class="w-20 h-5 xl:h-6" :lines="1" />
|
||||
</div>
|
||||
</div>
|
||||
</BaseContentPlaceholders>
|
||||
</template>
|
||||
79
resources/scripts/admin/views/dashboard/DashboardStats.vue
Normal file
79
resources/scripts/admin/views/dashboard/DashboardStats.vue
Normal file
@ -0,0 +1,79 @@
|
||||
<template>
|
||||
<div class="grid gap-6 sm:grid-cols-2 lg:grid-cols-9 xl:gap-8">
|
||||
<!-- Amount Due -->
|
||||
<DashboardStatsItem
|
||||
:icon-component="DollarIcon"
|
||||
:loading="!dashboardStore.isDashboardDataLoaded"
|
||||
:route="
|
||||
userStore.hasAbilities(abilities.VIEW_INVOICE) ? `/admin/invoices` : ''
|
||||
"
|
||||
:large="true"
|
||||
:label="$t('dashboard.cards.due_amount')"
|
||||
>
|
||||
<BaseFormatMoney
|
||||
:amount="dashboardStore.stats.totalAmountDue"
|
||||
:currency="companyStore.selectedCompanyCurrency"
|
||||
/>
|
||||
</DashboardStatsItem>
|
||||
|
||||
<!-- Customers -->
|
||||
<DashboardStatsItem
|
||||
:icon-component="CustomerIcon"
|
||||
:loading="!dashboardStore.isDashboardDataLoaded"
|
||||
:route="
|
||||
userStore.hasAbilities(abilities.VIEW_CUSTOMER)
|
||||
? `/admin/customers`
|
||||
: ''
|
||||
"
|
||||
:label="$t('dashboard.cards.customers')"
|
||||
>
|
||||
{{ dashboardStore.stats.totalCustomerCount }}
|
||||
</DashboardStatsItem>
|
||||
|
||||
<!-- Invoices -->
|
||||
<DashboardStatsItem
|
||||
:icon-component="InvoiceIcon"
|
||||
:loading="!dashboardStore.isDashboardDataLoaded"
|
||||
:route="
|
||||
userStore.hasAbilities(abilities.VIEW_INVOICE) ? `/admin/invoices` : ''
|
||||
"
|
||||
:label="$t('dashboard.cards.invoices')"
|
||||
>
|
||||
{{ dashboardStore.stats.totalInvoiceCount }}
|
||||
</DashboardStatsItem>
|
||||
|
||||
<!-- Estimates -->
|
||||
<DashboardStatsItem
|
||||
:icon-component="EstimateIcon"
|
||||
:loading="!dashboardStore.isDashboardDataLoaded"
|
||||
:route="
|
||||
userStore.hasAbilities(abilities.VIEW_ESTIMATE)
|
||||
? `/admin/estimates`
|
||||
: ''
|
||||
"
|
||||
:label="$t('dashboard.cards.estimates')"
|
||||
>
|
||||
{{ dashboardStore.stats.totalEstimateCount }}
|
||||
</DashboardStatsItem>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import DollarIcon from '@/scripts/components/icons/dashboard/DollarIcon.vue'
|
||||
import CustomerIcon from '@/scripts/components/icons/dashboard/CustomerIcon.vue'
|
||||
import InvoiceIcon from '@/scripts/components/icons/dashboard/InvoiceIcon.vue'
|
||||
import EstimateIcon from '@/scripts/components/icons/dashboard/EstimateIcon.vue'
|
||||
import abilities from '@/scripts/admin/stub/abilities'
|
||||
import DashboardStatsItem from './DashboardStatsItem.vue'
|
||||
|
||||
import { inject } from 'vue'
|
||||
import { useDashboardStore } from '@/scripts/admin/stores/dashboard'
|
||||
import { useCompanyStore } from '@/scripts/admin/stores/company'
|
||||
import { useUserStore } from '@/scripts/admin/stores/user'
|
||||
|
||||
const utils = inject('utils')
|
||||
|
||||
const dashboardStore = useDashboardStore()
|
||||
const companyStore = useCompanyStore()
|
||||
const userStore = useUserStore()
|
||||
</script>
|
||||
@ -0,0 +1,64 @@
|
||||
<template>
|
||||
<router-link
|
||||
v-if="!loading"
|
||||
class="
|
||||
relative
|
||||
flex
|
||||
justify-between
|
||||
p-3
|
||||
bg-white
|
||||
rounded
|
||||
shadow
|
||||
hover:bg-gray-50
|
||||
xl:p-4
|
||||
lg:col-span-2
|
||||
"
|
||||
:class="{ 'lg:!col-span-3': large }"
|
||||
:to="route"
|
||||
>
|
||||
<div>
|
||||
<span class="text-xl font-semibold leading-tight text-black xl:text-3xl">
|
||||
<slot />
|
||||
</span>
|
||||
<span class="block mt-1 text-sm leading-tight text-gray-500 xl:text-lg">
|
||||
{{ label }}
|
||||
</span>
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<component :is="iconComponent" class="w-10 h-10 xl:w-12 xl:h-12" />
|
||||
</div>
|
||||
</router-link>
|
||||
|
||||
<StatsCardPlaceholder v-else-if="large" />
|
||||
|
||||
<StatsCardSmPlaceholder v-else />
|
||||
</template>
|
||||
|
||||
|
||||
<script setup>
|
||||
import StatsCardPlaceholder from './DashboardStatsPlaceholder.vue'
|
||||
import StatsCardSmPlaceholder from './DashboardStatsSmPlaceholder.vue'
|
||||
|
||||
defineProps({
|
||||
iconComponent: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
loading: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
route: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
large: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
})
|
||||
</script>
|
||||
@ -0,0 +1,20 @@
|
||||
<template>
|
||||
<BaseContentPlaceholders
|
||||
:rounded="true"
|
||||
class="relative flex justify-between w-full p-3 bg-white rounded shadow lg:col-span-3 xl:p-4"
|
||||
>
|
||||
<div>
|
||||
<BaseContentPlaceholdersText
|
||||
class="h-5 -mb-1 w-14 xl:mb-6 xl:h-7"
|
||||
:lines="1"
|
||||
/>
|
||||
<BaseContentPlaceholdersText class="h-3 w-28 xl:h-4" :lines="1" />
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<BaseContentPlaceholdersBox
|
||||
:circle="true"
|
||||
class="w-10 h-10 xl:w-12 xl:h-12"
|
||||
/>
|
||||
</div>
|
||||
</BaseContentPlaceholders>
|
||||
</template>
|
||||
@ -0,0 +1,31 @@
|
||||
<template>
|
||||
<BaseContentPlaceholders
|
||||
:rounded="true"
|
||||
class="
|
||||
relative
|
||||
flex
|
||||
justify-between
|
||||
w-full
|
||||
p-3
|
||||
bg-white
|
||||
rounded
|
||||
shadow
|
||||
lg:col-span-2
|
||||
xl:p-4
|
||||
"
|
||||
>
|
||||
<div>
|
||||
<BaseContentPlaceholdersText
|
||||
class="w-12 h-5 -mb-1 xl:mb-6 xl:h-7"
|
||||
:lines="1"
|
||||
/>
|
||||
<BaseContentPlaceholdersText class="w-20 h-3 xl:h-4" :lines="1" />
|
||||
</div>
|
||||
<div class="flex items-center">
|
||||
<BaseContentPlaceholdersBox
|
||||
:circle="true"
|
||||
class="w-10 h-10 xl:w-12 xl:h-12"
|
||||
/>
|
||||
</div>
|
||||
</BaseContentPlaceholders>
|
||||
</template>
|
||||
180
resources/scripts/admin/views/dashboard/DashboardTable.vue
Normal file
180
resources/scripts/admin/views/dashboard/DashboardTable.vue
Normal file
@ -0,0 +1,180 @@
|
||||
<template>
|
||||
<div>
|
||||
<div class="grid grid-cols-1 gap-6 mt-10 xl:grid-cols-2">
|
||||
<!-- Due Invoices -->
|
||||
<div class="due-invoices">
|
||||
<div class="relative z-10 flex items-center justify-between mb-3">
|
||||
<h6 class="mb-0 text-xl font-semibold leading-normal">
|
||||
{{ $t('dashboard.recent_invoices_card.title') }}
|
||||
</h6>
|
||||
|
||||
<BaseButton
|
||||
size="sm"
|
||||
variant="primary-outline"
|
||||
@click="$router.push('/admin/invoices')"
|
||||
>
|
||||
{{ $t('dashboard.recent_invoices_card.view_all') }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
|
||||
<BaseTable
|
||||
:data="dashboardStore.recentDueInvoices"
|
||||
:columns="dueInvoiceColumns"
|
||||
:loading="!dashboardStore.isDashboardDataLoaded"
|
||||
>
|
||||
<template #cell-user="{ row }">
|
||||
<router-link
|
||||
:to="{ path: `invoices/${row.data.id}/view` }"
|
||||
class="font-medium text-primary-500"
|
||||
>
|
||||
{{ row.data.customer.name }}
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<template #cell-due_amount="{ row }">
|
||||
<BaseFormatMoney
|
||||
:amount="row.data.due_amount"
|
||||
:currency="row.data.customer.currency"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<!-- Actions -->
|
||||
<template
|
||||
v-if="hasAtleastOneInvoiceAbility()"
|
||||
#cell-actions="{ row }"
|
||||
>
|
||||
<InvoiceDropdown :row="row.data" :table="invoiceTableComponent" />
|
||||
</template>
|
||||
</BaseTable>
|
||||
</div>
|
||||
|
||||
<!-- Recent Estimates -->
|
||||
<div class="recent-estimates">
|
||||
<div class="relative z-10 flex items-center justify-between mb-3">
|
||||
<h6 class="mb-0 text-xl font-semibold leading-normal">
|
||||
{{ $t('dashboard.recent_estimate_card.title') }}
|
||||
</h6>
|
||||
|
||||
<BaseButton
|
||||
variant="primary-outline"
|
||||
size="sm"
|
||||
@click="$router.push('/admin/estimates')"
|
||||
>
|
||||
{{ $t('dashboard.recent_estimate_card.view_all') }}
|
||||
</BaseButton>
|
||||
</div>
|
||||
|
||||
<BaseTable
|
||||
:data="dashboardStore.recentEstimates"
|
||||
:columns="recentEstimateColumns"
|
||||
:loading="!dashboardStore.isDashboardDataLoaded"
|
||||
>
|
||||
<template #cell-user="{ row }">
|
||||
<router-link
|
||||
:to="{ path: `estimates/${row.data.id}/view` }"
|
||||
class="font-medium text-primary-500"
|
||||
>
|
||||
{{ row.data.customer.name }}
|
||||
</router-link>
|
||||
</template>
|
||||
|
||||
<template #cell-total="{ row }">
|
||||
<BaseFormatMoney
|
||||
:amount="row.data.total"
|
||||
:currency="row.data.customer.currency"
|
||||
/>
|
||||
</template>
|
||||
|
||||
<template
|
||||
v-if="hasAtleastOneEstimateAbility()"
|
||||
#cell-actions="{ row }"
|
||||
>
|
||||
<EstimateDropdown :row="row" :table="estimateTableComponent" />
|
||||
</template>
|
||||
</BaseTable>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import { useDashboardStore } from '@/scripts/admin/stores/dashboard'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { useUserStore } from '@/scripts/admin/stores/user'
|
||||
import abilities from '@/scripts/admin/stub/abilities'
|
||||
import InvoiceDropdown from '@/scripts/admin/components/dropdowns/InvoiceIndexDropdown.vue'
|
||||
import EstimateDropdown from '@/scripts/admin/components/dropdowns/EstimateIndexDropdown.vue'
|
||||
|
||||
const dashboardStore = useDashboardStore()
|
||||
|
||||
const { t } = useI18n()
|
||||
const userStore = useUserStore()
|
||||
|
||||
const invoiceTableComponent = ref(null)
|
||||
const estimateTableComponent = ref(null)
|
||||
|
||||
const dueInvoiceColumns = computed(() => {
|
||||
return [
|
||||
{
|
||||
key: 'formattedDueDate',
|
||||
label: t('dashboard.recent_invoices_card.due_on'),
|
||||
},
|
||||
{
|
||||
key: 'user',
|
||||
label: t('dashboard.recent_invoices_card.customer'),
|
||||
},
|
||||
{
|
||||
key: 'due_amount',
|
||||
label: t('dashboard.recent_invoices_card.amount_due'),
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
tdClass: 'text-right text-sm font-medium pl-0',
|
||||
thClass: 'text-right pl-0',
|
||||
sortable: false,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
const recentEstimateColumns = computed(() => {
|
||||
return [
|
||||
{
|
||||
key: 'formattedEstimateDate',
|
||||
label: t('dashboard.recent_estimate_card.date'),
|
||||
},
|
||||
{
|
||||
key: 'user',
|
||||
label: t('dashboard.recent_estimate_card.customer'),
|
||||
},
|
||||
{
|
||||
key: 'total',
|
||||
label: t('dashboard.recent_estimate_card.amount_due'),
|
||||
},
|
||||
{
|
||||
key: 'actions',
|
||||
tdClass: 'text-right text-sm font-medium pl-0',
|
||||
thClass: 'text-right pl-0',
|
||||
sortable: false,
|
||||
},
|
||||
]
|
||||
})
|
||||
|
||||
function hasAtleastOneInvoiceAbility() {
|
||||
return userStore.hasAbilities([
|
||||
abilities.DELETE_INVOICE,
|
||||
abilities.EDIT_INVOICE,
|
||||
abilities.VIEW_INVOICE,
|
||||
abilities.SEND_INVOICE,
|
||||
])
|
||||
}
|
||||
|
||||
function hasAtleastOneEstimateAbility() {
|
||||
return userStore.hasAbilities([
|
||||
abilities.CREATE_ESTIMATE,
|
||||
abilities.EDIT_ESTIMATE,
|
||||
abilities.VIEW_ESTIMATE,
|
||||
abilities.SEND_ESTIMATE,
|
||||
])
|
||||
}
|
||||
</script>
|
||||
Reference in New Issue
Block a user