Update various configuration files, components, and assets; enhance notification system and API endpoints; improve documentation and styles across the application.

This commit is contained in:
Haqeem Solehan
2025-10-16 16:05:39 +08:00
commit b124ff8092
336 changed files with 94392 additions and 0 deletions

View File

@@ -0,0 +1,301 @@
import { ref } from 'vue'
export const useNotificationLogs = () => {
// Use Nuxt's useFetch instead of $fetch
const config = useRuntimeConfig()
// Logs data state
const logs = ref([])
const loading = ref(false)
const error = ref(null)
const pagination = ref({
page: 1,
limit: 10,
total: 0,
pages: 1
})
const summaryStats = ref({
totalLogs: 0,
failedDeliveries: 0,
successfulDeliveries: 0,
successRate: 0
})
// Filters state
const filters = ref({
startDate: null,
endDate: null,
action: null,
channel: null,
status: null,
actor: '',
keyword: ''
})
// Analytics state
const analyticsData = ref(null)
const analyticsLoading = ref(false)
const analyticsError = ref(null)
// Monitoring state
const monitoringData = ref(null)
const monitoringLoading = ref(false)
const monitoringError = ref(null)
// Fetch logs with filters and pagination
const fetchLogs = async () => {
try {
loading.value = true
error.value = null
// Build query parameters
const queryParams = new URLSearchParams()
queryParams.append('page', pagination.value.page)
queryParams.append('limit', pagination.value.limit)
// Add filters if they exist
if (filters.value.startDate) queryParams.append('startDate', filters.value.startDate)
if (filters.value.endDate) queryParams.append('endDate', filters.value.endDate)
if (filters.value.action) queryParams.append('action', filters.value.action)
if (filters.value.channel) queryParams.append('channel', filters.value.channel)
if (filters.value.status) queryParams.append('status', filters.value.status)
if (filters.value.actor) queryParams.append('actor', filters.value.actor)
if (filters.value.keyword) queryParams.append('keyword', filters.value.keyword)
// Make API call with useFetch
const { data, error: fetchError } = await useFetch(`/api/notifications/logs`, {
method: 'GET',
params: {
page: pagination.value.page,
limit: pagination.value.limit,
...filters.value
}
})
if (fetchError.value) {
throw new Error(fetchError.value.message || 'Failed to fetch logs');
}
if (data.value && data.value.body && data.value.body.success) {
logs.value = data.value.body.data.logs || [];
pagination.value = data.value.body.data.pagination || pagination.value;
// Ensure summary stats are properly assigned
if (data.value.body.data.summary) {
summaryStats.value = {
totalLogs: data.value.body.data.summary.totalLogs || 0,
failedDeliveries: data.value.body.data.summary.failedDeliveries || 0,
successfulDeliveries: data.value.body.data.summary.successfulDeliveries || 0,
successRate: data.value.body.data.summary.successRate || 0
};
}
console.log('Logs fetched successfully:', {
logsCount: logs.value.length,
pagination: pagination.value,
summaryStats: summaryStats.value
});
} else {
throw new Error('Failed to fetch logs: ' + (data.value?.body?.message || 'Unknown error'))
}
} catch (err) {
error.value = err.message || 'An error occurred'
console.error('Error fetching logs:', err)
} finally {
loading.value = false
}
}
// Get a specific log by ID
const getLogById = async (id) => {
try {
loading.value = true
error.value = null
// Make API call
const { data } = await useFetch(`/api/notifications/logs/${id}`, {
method: 'GET'
})
if (data.value && data.value.body && data.value.body.success) {
return data.value.body.data
} else {
throw new Error('Failed to fetch log details')
}
} catch (err) {
error.value = err.message || 'An error occurred'
console.error('Error fetching log details:', err)
return null
} finally {
loading.value = false
}
}
// Update filters and fetch logs
const applyFilters = async (newFilters) => {
filters.value = { ...filters.value, ...newFilters }
pagination.value.page = 1 // Reset to first page when filters change
await fetchLogs()
}
// Clear all filters
const clearFilters = async () => {
filters.value = {
startDate: null,
endDate: null,
action: null,
channel: null,
status: null,
actor: '',
keyword: ''
}
pagination.value.page = 1
await fetchLogs()
}
// Change page
const changePage = async (page) => {
pagination.value.page = page
await fetchLogs()
}
// Fetch analytics data
const fetchAnalytics = async (period = '7d', channel = 'all') => {
try {
analyticsLoading.value = true
analyticsError.value = null
// Make API call
const { data } = await useFetch(`/api/notifications/logs/analytics`, {
method: 'GET',
params: {
period,
channel
}
})
if (data.value && data.value.body && data.value.body.success) {
analyticsData.value = data.value.body.data
} else {
throw new Error('Failed to fetch analytics data')
}
} catch (err) {
analyticsError.value = err.message || 'An error occurred'
console.error('Error fetching analytics data:', err)
} finally {
analyticsLoading.value = false
}
}
// Fetch monitoring data
const fetchMonitoringData = async () => {
try {
monitoringLoading.value = true
monitoringError.value = null
// Make API call
const { data } = await useFetch('/api/notifications/logs/monitoring', {
method: 'GET'
})
if (data.value && data.value.body && data.value.body.success) {
monitoringData.value = data.value.body.data
} else {
throw new Error('Failed to fetch monitoring data')
}
} catch (err) {
monitoringError.value = err.message || 'An error occurred'
console.error('Error fetching monitoring data:', err)
} finally {
monitoringLoading.value = false
}
}
// Format date for display
const formatDate = (date, includeTime = false) => {
if (!date) return ''
const options = { year: 'numeric', month: 'short', day: 'numeric' }
if (includeTime) {
options.hour = '2-digit'
options.minute = '2-digit'
}
return new Date(date).toLocaleDateString(undefined, options)
}
// Format time ago (e.g., "2 minutes ago")
const formatTimeAgo = (timestamp) => {
const now = new Date()
const time = new Date(timestamp)
const diffInMinutes = Math.floor((now - time) / (1000 * 60))
if (diffInMinutes < 1) return "Just now"
if (diffInMinutes < 60) return `${diffInMinutes} minutes ago`
if (diffInMinutes < 1440) return `${Math.floor(diffInMinutes / 60)} hours ago`
return `${Math.floor(diffInMinutes / 1440)} days ago`
}
// Get available actions for filters
const availableActions = [
{ label: 'All Actions', value: null },
{ label: 'Notification Created', value: 'Notification Created' },
{ label: 'Notification Sent', value: 'Notification Sent' },
{ label: 'Delivery Attempted', value: 'Delivery Attempted' },
{ label: 'Delivery Failed', value: 'Delivery Failed' },
{ label: 'Notification Opened', value: 'Notification Opened' },
{ label: 'Template Updated', value: 'Template Updated' },
{ label: 'Notification Queued', value: 'Notification Queued' },
]
// Get available channels for filters
const availableChannels = [
{ label: 'All Channels', value: null },
{ label: 'Email', value: 'Email' },
{ label: 'SMS', value: 'SMS' },
{ label: 'Push Notification', value: 'Push Notification' },
{ label: 'Webhook', value: 'Webhook' }
]
// Get available statuses for filters
const availableStatuses = [
{ label: 'All Statuses', value: null },
{ label: 'Sent', value: 'Sent' },
{ label: 'Failed', value: 'Failed' },
{ label: 'Bounced', value: 'Bounced' },
{ label: 'Opened', value: 'Opened' },
{ label: 'Queued', value: 'Queued' },
{ label: 'Created', value: 'Created' },
{ label: 'Updated', value: 'Updated' }
]
return {
// State
logs,
loading,
error,
pagination,
summaryStats,
filters,
analyticsData,
analyticsLoading,
analyticsError,
monitoringData,
monitoringLoading,
monitoringError,
// Methods
fetchLogs,
getLogById,
applyFilters,
clearFilters,
changePage,
fetchAnalytics,
fetchMonitoringData,
formatDate,
formatTimeAgo,
// Filter options
availableActions,
availableChannels,
availableStatuses
}
}