Files
Nas-Notification/pages/notification/log-audit/reports.vue

440 lines
13 KiB
Vue

<template>
<div>
<LayoutsBreadcrumb />
<!-- Page Info Card -->
<rs-card class="mb-6">
<template #header>
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-file-download"></Icon>
<h1 class="text-xl font-bold text-primary">Reports & Export</h1>
</div>
</template>
<template #body>
<p class="text-gray-600">
Generate reports and export log data for compliance, auditing, and analysis.
</p>
</template>
</rs-card>
<!-- Quick Export Actions -->
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-6 mb-6">
<rs-card
v-for="(action, index) in quickExportActions"
:key="index"
class="cursor-pointer"
@click="quickExport(action.type)"
>
<div class="pt-5 pb-3 px-5 text-center">
<div
class="p-5 flex justify-center items-center bg-primary/20 rounded-2xl mx-auto mb-4 w-fit"
>
<Icon class="text-primary text-3xl" :name="action.icon"></Icon>
</div>
<span class="block font-bold text-lg leading-tight text-primary mb-2">
{{ action.title }}
</span>
<span class="text-sm text-gray-600">
{{ action.description }}
</span>
</div>
</rs-card>
</div>
<!-- Custom Report Builder -->
<rs-card class="mb-6">
<template #header>
<div class="flex items-center">
<Icon name="ic:outline-build" class="mr-2 text-primary" />
<h3 class="text-lg font-semibold text-primary">Custom Report Builder</h3>
</div>
</template>
<template #body>
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
<!-- Report Configuration -->
<div class="space-y-4">
<FormKit
type="text"
label="Report Name"
v-model="customReport.name"
placeholder="Enter report name"
/>
<FormKit
type="select"
label="Report Type"
v-model="customReport.type"
:options="reportTypeOptions"
placeholder="Select report type"
/>
<FormKit
type="select"
label="Date Range"
v-model="customReport.dateRange"
:options="dateRangeOptions"
placeholder="Select date range"
/>
<FormKit
type="select"
label="Export Format"
v-model="customReport.format"
:options="exportFormatOptions"
placeholder="Select format"
/>
<FormKit
type="checkbox"
label="Include Channels"
v-model="customReport.channels"
:options="channelOptions"
/>
<FormKit
type="checkbox"
label="Include Status"
v-model="customReport.statuses"
:options="statusOptions"
/>
</div>
<!-- Report Preview -->
<div class="space-y-4">
<h4 class="text-lg font-semibold">Report Preview</h4>
<div class="p-4 bg-gray-50 dark:bg-gray-800 rounded-lg min-h-64">
<div class="space-y-3">
<div class="flex justify-between items-center">
<span class="font-medium">Report Name:</span>
<span class="text-primary">{{
customReport.name || "Untitled Report"
}}</span>
</div>
<div class="flex justify-between items-center">
<span class="font-medium">Type:</span>
<span>{{ getReportTypeLabel(customReport.type) }}</span>
</div>
<div class="flex justify-between items-center">
<span class="font-medium">Date Range:</span>
<span>{{ getDateRangeLabel(customReport.dateRange) }}</span>
</div>
<div class="flex justify-between items-center">
<span class="font-medium">Format:</span>
<span>{{ getFormatLabel(customReport.format) }}</span>
</div>
<div class="flex justify-between items-start">
<span class="font-medium">Channels:</span>
<div class="text-right">
<div v-if="customReport.channels.length === 0" class="text-gray-500">
All channels
</div>
<div v-else class="space-y-1">
<div
v-for="channel in customReport.channels"
:key="channel"
class="text-sm"
>
{{ channel }}
</div>
</div>
</div>
</div>
<div class="flex justify-between items-start">
<span class="font-medium">Status:</span>
<div class="text-right">
<div v-if="customReport.statuses.length === 0" class="text-gray-500">
All statuses
</div>
<div v-else class="space-y-1">
<div
v-for="status in customReport.statuses"
:key="status"
class="text-sm"
>
{{ status }}
</div>
</div>
</div>
</div>
</div>
</div>
<div class="flex gap-2">
<rs-button
@click="generateCustomReport"
variant="primary"
:disabled="!customReport.name || !customReport.type"
class="flex-1"
>
<Icon name="ic:outline-play-arrow" class="mr-1" /> Generate Report
</rs-button>
<rs-button @click="saveReportTemplate" variant="secondary-outline">
<Icon name="ic:outline-save" class="mr-1" /> Save Template
</rs-button>
</div>
</div>
</div>
</template>
</rs-card>
<!-- Export History -->
<rs-card>
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon name="ic:outline-history" class="mr-2 text-primary" />
<h3 class="text-lg font-semibold text-primary">Export History</h3>
</div>
</div>
</template>
<template #body>
<div class="space-y-3">
<div
v-for="(export_, index) in exportHistory"
:key="index"
class="flex items-center justify-between p-3 bg-gray-50 dark:bg-gray-800 rounded-lg"
>
<div class="flex items-center">
<Icon
:name="
export_.format === 'pdf'
? 'ic:outline-picture-as-pdf'
: export_.format === 'csv'
? 'ic:outline-table-chart'
: 'ic:outline-grid-on'
"
class="mr-3 text-primary text-xl"
/>
<div>
<p class="font-medium">{{ export_.name }}</p>
<p class="text-sm text-gray-600">{{ export_.description }}</p>
</div>
</div>
<div class="flex items-center gap-4">
<div class="text-right">
<p class="text-sm font-medium">{{ export_.size }}</p>
<p class="text-xs text-gray-500">{{ export_.timestamp }}</p>
</div>
<rs-badge
:variant="
export_.status === 'completed'
? 'success'
: export_.status === 'processing'
? 'warning'
: 'danger'
"
size="sm"
>
{{ export_.status }}
</rs-badge>
<div class="flex gap-1">
<rs-button
v-if="export_.status === 'completed'"
variant="primary-text"
size="sm"
@click="downloadExport(export_)"
>
<Icon name="ic:outline-download" />
</rs-button>
<rs-button variant="danger-text" size="sm" @click="deleteExport(index)">
<Icon name="ic:outline-delete" />
</rs-button>
</div>
</div>
</div>
<div v-if="exportHistory.length === 0" class="text-center py-8 text-gray-500">
<Icon name="ic:outline-folder-open" class="text-4xl mb-2 mx-auto" />
<p>No export history available</p>
</div>
</div>
</template>
</rs-card>
</div>
</template>
<script setup>
definePageMeta({
title: "Reports & Export",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{
name: "Dashboard",
path: "/dashboard",
},
{
name: "Notification",
path: "/notification",
},
{
name: "Logs & Audit Trail",
path: "/notification/log-audit",
},
{
name: "Reports",
path: "/notification/log-audit/reports",
type: "current",
},
],
});
import { ref, computed } from "vue";
// Quick export actions
const quickExportActions = ref([
{
title: "CSV Export",
description: "Export current data to CSV format",
icon: "ic:outline-table-chart",
type: "csv",
},
{
title: "PDF Report",
description: "Generate comprehensive PDF report",
icon: "ic:outline-picture-as-pdf",
type: "pdf",
},
{
title: "Excel Export",
description: "Export data to Excel spreadsheet",
icon: "ic:outline-grid-on",
type: "excel",
},
{
title: "JSON Export",
description: "Export raw data in JSON format",
icon: "ic:outline-code",
type: "json",
},
]);
// Custom report builder
const customReport = ref({
name: "",
type: "",
dateRange: "",
format: "",
channels: [],
statuses: [],
});
const reportTypeOptions = [
{ label: "Delivery Report", value: "delivery" },
{ label: "Performance Analytics", value: "performance" },
{ label: "Error Analysis", value: "errors" },
{ label: "User Engagement", value: "engagement" },
{ label: "Channel Comparison", value: "channels" },
{ label: "Audit Trail", value: "audit" },
];
const dateRangeOptions = [
{ label: "Last 24 Hours", value: "1d" },
{ label: "Last 7 Days", value: "7d" },
{ label: "Last 30 Days", value: "30d" },
{ label: "Last 90 Days", value: "90d" },
{ label: "Last 12 Months", value: "12m" },
{ label: "Custom Range", value: "custom" },
];
const exportFormatOptions = [
{ label: "CSV", value: "csv" },
{ label: "PDF", value: "pdf" },
{ label: "Excel", value: "excel" },
{ label: "JSON", value: "json" },
];
const channelOptions = [
{ label: "Email", value: "Email" },
{ label: "SMS", value: "SMS" },
{ label: "Push Notification", value: "Push Notification" },
{ label: "Webhook", value: "Webhook" },
];
const statusOptions = [
{ label: "Sent", value: "Sent" },
{ label: "Failed", value: "Failed" },
{ label: "Bounced", value: "Bounced" },
{ label: "Opened", value: "Opened" },
{ label: "Queued", value: "Queued" },
];
// Export history
const exportHistory = ref([
{
name: "Notification Analytics Report",
description: "Monthly performance analysis",
format: "pdf",
size: "2.4 MB",
timestamp: "2 hours ago",
status: "completed",
},
{
name: "Delivery Logs Export",
description: "Last 30 days delivery data",
format: "csv",
size: "856 KB",
timestamp: "1 day ago",
status: "completed",
},
]);
// Helper functions
const getReportTypeLabel = (value) => {
const option = reportTypeOptions.find((opt) => opt.value === value);
return option ? option.label : "Not selected";
};
const getDateRangeLabel = (value) => {
const option = dateRangeOptions.find((opt) => opt.value === value);
return option ? option.label : "Not selected";
};
const getFormatLabel = (value) => {
const option = exportFormatOptions.find((opt) => opt.value === value);
return option ? option.label : "Not selected";
};
// Methods
const quickExport = (type) => {
console.log(`Quick export: ${type}`);
alert(`Exporting data in ${type} format. (Implementation pending)`);
};
const generateCustomReport = () => {
console.log("Generating custom report:", customReport.value);
alert(`Generating custom report: ${customReport.value.name}. (Implementation pending)`);
};
const saveReportTemplate = () => {
console.log("Saving report template:", customReport.value);
alert(`Saving report template: ${customReport.value.name}. (Implementation pending)`);
};
const downloadExport = (export_) => {
console.log("Downloading export:", export_);
alert(`Downloading ${export_.name}. (Implementation pending)`);
};
const deleteExport = (index) => {
exportHistory.value.splice(index, 1);
};
</script>
<style lang="scss" scoped>
:deep(.formkit-outer) {
margin-bottom: 1rem;
}
:deep(.formkit-label) {
font-weight: 500;
color: rgb(107 114 128);
font-size: 0.875rem;
margin-bottom: 0.5rem;
}
:deep(.formkit-input) {
border-radius: 0.5rem;
}
</style>