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