Untitled
Anonymous
plain_text
02/25/2026 8:26 AM
9.9 KB
14
Indexable
<script setup>
import { onBeforeMount, ref } from 'vue';
import { useInvoiceTemplateStore } from '@/modules/administration/stores';
import { PaginationOptions, SortFilterOptions } from '@/config';
import { useHelpers } from '@/composables';
const { makeSlugToTitleCase } = useHelpers();
const invoiceTemplateStore = useInvoiceTemplateStore();
const pagination = new PaginationOptions();
const sortFilters = new SortFilterOptions();
const searchText = ref('');
const selectedItem = ref(null);
const loading = ref(false);
const items = ref([]);
const totalRecords = ref();
const defaultDialog = ref(false);
const selectedPdf = ref(null);
const openPdf = (row) => {
selectedPdf.value = row.pdf_path; // or full URL if needed
};
onBeforeMount(async () => {
await getItems();
});
const previewMenuItems = (data) => [
{
label: 'Single Project',
icon: 'pi pi-file',
command: () => openSingleProjectPreview(data)
},
{
label: 'Multiple Projects',
icon: 'pi pi-copy',
command: () => openMultipleProjectsPreview(data)
}
];
const openSingleProjectPreview = (data) => {
const previewPath = data?.preview_path;
if (previewPath) window.open(previewPath, '_blank');
};
const openMultipleProjectsPreview = (data) => {
const previewPath = data?.preview_multiple_path;
if (previewPath) window.open(previewPath, '_blank');
};
const openDefaultPreview = (data) => {
const previewPath = data?.preview_path;
if (previewPath) window.open(previewPath, '_blank');
};
const showDefaultDialog = () => {
defaultDialog.value = true;
};
const onSortChange = (event) => {
pagination.resetPageParams();
sortFilters.updateSortFilters(event);
getItems();
};
const onPageChange = (event) => {
pagination.updatePageParams(event);
getItems();
};
const search = async () => {
pagination.resetPageParams();
sortFilters.updateSearch(searchText.value);
getItems();
};
const getItems = async () => {
try {
loading.value = true;
const params = { ...pagination.getPageParams() };
const payload = sortFilters.getSortFilters(searchText.value);
const res = await invoiceTemplateStore.search(payload, params);
items.value = res.data;
totalRecords.value = res.meta.total;
} finally {
loading.value = false;
}
};
const makeDefault = async () => {
try {
loading.value = true;
if (selectedItem.value) {
await invoiceTemplateStore.makeDefault(selectedItem.value.id);
}
await getItems();
selectedItem.value = {};
} finally {
loading.value = false;
}
};
</script>
<template>
<TitleHeader>
<template #title>
<div>
<h1 class="text-2xl sm:text-3xl font-bold">
Invoice Templates
</h1>
<p class="mt-2 text-sm sm:text-base mx-auto sm:mx-0 mb-0">
These invoice templates are used for invoicing.
</p>
</div>
</template>
</TitleHeader>
<Card class="py-3 px-2">
<template #content>
<BaseTable
:reorderableColumns="true"
:value="items"
:page="pagination.page"
:rows="pagination.limit"
:total-records="totalRecords"
:loading="loading"
@sort="onSortChange"
@page="onPageChange"
>
<template #header>
<div class="flex justify-end mb-5">
<Search
v-model="searchText"
@search="search"
class="w-full sm:w-1/2 md:w-1/3 lg:w-1/3"
/>
</div>
</template>
<template #empty> No invoice templates found. </template>
<Column
columnKey="name"
:sortable="true"
field="name"
header="Name"
>
<template #body="{ data }">
<div class="flex items-center justify-between">
<div class="flex items-center gap-4 flex-wrap">
{{ data.name }}
<Tag
severity="info"
v-if="data.is_default"
value="Default"
/>
</div>
</div>
</template>
</Column>
<Column
columnKey="type"
:sortable="true"
field="type"
header="Type"
>
<template #body="{ data }">
{{ makeSlugToTitleCase(data.type) }}
</template>
</Column>
<Column
columnKey="preview_path"
field="preview_path"
header="Preview"
>
<template #body="{ data }">
<SplitButton
v-if="data.preview_multiple_path"
icon="pi pi-eye"
variant="outlined"
label="View Invoice PDF"
:model="previewMenuItems(data)"
/>
<Button
v-else
label="View Invoice PDF"
icon="pi pi-eye"
@click="openPdf(data)"
/>
</template>
</Column>
<Column
columnKey="actions"
v-if="$ability.can('administration.edit')"
header="Actions"
class="flex justify-end"
>
<template #body="{ data }">
<Button
v-if="!data.is_default && data.type != 'prepayment'"
label="Set as Default"
icon="pi pi-star"
@click="showDefaultDialog((selectedItem = data))"
/>
</template>
</Column>
</BaseTable>
<div v-if="selectedPdf" class="pdf-viewer-container">
<div class="pdf-header">
<button @click="selectedPdf = null">Close ❌</button>
</div>
<iframe
:src="selectedPdf"
width="100%"
height="600px"
style="border: none;"
></iframe>
</div>
</template>
</Card>
<Confirmation
v-if="$ability.can('administration.edit')"
v-model="defaultDialog"
variant="success"
:header="`Set ${selectedItem?.name} as Default`"
:content="`Are you sure you want to make this invoice template default?`"
@confirm="makeDefault"
/>
</template>
Editor is loading...
Leave a Comment