665 lines
22 KiB
Vue

<template>
<div>
<LayoutsBreadcrumb />
<!-- Header Section -->
<rs-card class="mb-6">
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-priority-high"></Icon>
<h1 class="text-xl font-bold text-primary">Priority Queue Management</h1>
</div>
<rs-button @click="showCreatePriorityModal = true">
<Icon class="mr-1" name="ic:outline-add"></Icon>
Create Priority Level
</rs-button>
</div>
</template>
<template #body>
<p class="text-gray-600">
Manage different priority levels for notifications to ensure critical messages are processed first.
Higher priority notifications will be processed before lower priority ones in the queue.
</p>
</template>
</rs-card>
<!-- Priority Level Statistics -->
<div class="grid grid-cols-1 lg:grid-cols-2 xl:grid-cols-4 gap-6 mb-6">
<rs-card
v-for="(stat, index) in priorityStats"
:key="index"
class="transition-all duration-300 hover:shadow-lg"
>
<div class="pt-5 pb-3 px-5 flex items-center gap-4">
<div
class="p-4 flex justify-center items-center rounded-2xl"
:class="stat.bgColor"
>
<Icon class="text-2xl" :class="stat.iconColor" :name="stat.icon"></Icon>
</div>
<div class="flex-1 truncate">
<span class="block font-bold text-2xl leading-tight" :class="stat.valueColor">
{{ stat.value }}
</span>
<span class="text-sm font-medium text-gray-600">
{{ stat.title }}
</span>
</div>
</div>
</rs-card>
</div>
<!-- Priority Levels Configuration -->
<rs-card class="mb-6">
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-settings"></Icon>
<h3 class="text-lg font-semibold text-primary">Priority Levels</h3>
</div>
<div class="flex items-center gap-3">
<rs-button variant="outline" size="sm" @click="refreshPriorityLevels">
<Icon class="mr-1" name="ic:outline-refresh"></Icon>
Refresh
</rs-button>
<rs-button variant="outline" size="sm" @click="showBulkEditModal = true">
<Icon class="mr-1" name="ic:outline-edit"></Icon>
Bulk Edit
</rs-button>
</div>
</div>
</template>
<template #body>
<div class="space-y-4">
<div
v-for="(priority, index) in priorityLevels"
:key="index"
class="flex items-center justify-between p-4 border rounded-lg"
:class="{
'border-red-200 bg-red-50': priority.level === 'critical',
'border-orange-200 bg-orange-50': priority.level === 'high',
'border-yellow-200 bg-yellow-50': priority.level === 'medium',
'border-blue-200 bg-blue-50': priority.level === 'low',
'border-gray-200 bg-gray-50': priority.level === 'bulk'
}"
>
<div class="flex items-center gap-4">
<div class="flex items-center gap-2">
<div
class="w-4 h-4 rounded-full"
:class="{
'bg-red-500': priority.level === 'critical',
'bg-orange-500': priority.level === 'high',
'bg-yellow-500': priority.level === 'medium',
'bg-blue-500': priority.level === 'low',
'bg-gray-500': priority.level === 'bulk'
}"
></div>
<span class="font-medium text-lg">{{ priority.name }}</span>
<span class="text-sm text-gray-500">({{ priority.level }})</span>
</div>
<div class="flex items-center gap-6">
<div class="text-center">
<p class="text-sm text-gray-600">Weight</p>
<p class="font-bold">{{ priority.weight }}</p>
</div>
<div class="text-center">
<p class="text-sm text-gray-600">Queue Count</p>
<p class="font-bold">{{ priority.queueCount.toLocaleString() }}</p>
</div>
<div class="text-center">
<p class="text-sm text-gray-600">Avg Processing</p>
<p class="font-bold">{{ priority.avgProcessingTime }}</p>
</div>
<div class="text-center">
<p class="text-sm text-gray-600">Status</p>
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full"
:class="{
'bg-green-100 text-green-800': priority.status === 'active',
'bg-red-100 text-red-800': priority.status === 'paused',
'bg-yellow-100 text-yellow-800': priority.status === 'throttled'
}"
>
{{ priority.status }}
</span>
</div>
</div>
</div>
<div class="flex items-center gap-2">
<rs-button
variant="outline"
size="sm"
@click="editPriority(priority)"
>
<Icon class="mr-1" name="ic:outline-edit"></Icon>
Edit
</rs-button>
<rs-button
variant="outline"
size="sm"
:class="priority.status === 'active' ? 'text-red-600' : 'text-green-600'"
@click="togglePriorityStatus(priority)"
>
<Icon
class="mr-1"
:name="priority.status === 'active' ? 'ic:outline-pause' : 'ic:outline-play-arrow'"
></Icon>
{{ priority.status === 'active' ? 'Pause' : 'Resume' }}
</rs-button>
</div>
</div>
</div>
</template>
</rs-card>
<!-- Queue Processing Order -->
<rs-card class="mb-6">
<template #header>
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-sort"></Icon>
<h3 class="text-lg font-semibold text-primary">Processing Order Visualization</h3>
</div>
</template>
<template #body>
<div class="space-y-4">
<p class="text-gray-600 mb-4">
Notifications are processed in the following order based on priority weights:
</p>
<!-- Processing Flow -->
<div class="flex items-center justify-between bg-gray-50 p-4 rounded-lg">
<div
v-for="(level, index) in sortedPriorityLevels"
:key="index"
class="flex flex-col items-center"
>
<div
class="w-16 h-16 rounded-full flex items-center justify-center text-white font-bold text-lg mb-2"
:class="{
'bg-red-500': level.level === 'critical',
'bg-orange-500': level.level === 'high',
'bg-yellow-500': level.level === 'medium',
'bg-blue-500': level.level === 'low',
'bg-gray-500': level.level === 'bulk'
}"
>
{{ level.weight }}
</div>
<span class="text-sm font-medium">{{ level.name }}</span>
<span class="text-xs text-gray-500">{{ level.queueCount }} jobs</span>
<!-- Arrow -->
<Icon
v-if="index < sortedPriorityLevels.length - 1"
class="text-gray-400 mt-2"
name="ic:outline-arrow-forward"
></Icon>
</div>
</div>
<!-- Processing Rules -->
<div class="grid grid-cols-1 lg:grid-cols-2 gap-6 mt-6">
<div class="bg-blue-50 p-4 rounded-lg">
<h4 class="font-medium text-blue-800 mb-2">Processing Rules</h4>
<ul class="text-sm text-blue-700 space-y-1">
<li>• Higher weight = Higher priority</li>
<li>• Critical jobs always processed first</li>
<li>• Same priority jobs use FIFO order</li>
<li>• Bulk jobs processed during low traffic</li>
</ul>
</div>
<div class="bg-green-50 p-4 rounded-lg">
<h4 class="font-medium text-green-800 mb-2">Performance Impact</h4>
<ul class="text-sm text-green-700 space-y-1">
<li>• Critical: &lt; 1 second processing</li>
<li>• High: &lt; 5 seconds processing</li>
<li>• Medium: &lt; 30 seconds processing</li>
<li>• Low/Bulk: Best effort processing</li>
</ul>
</div>
</div>
</div>
</template>
</rs-card>
<!-- Recent Priority Queue Activity -->
<rs-card>
<template #header>
<div class="flex items-center justify-between">
<div class="flex items-center">
<Icon class="mr-2 text-primary" name="ic:outline-history"></Icon>
<h3 class="text-lg font-semibold text-primary">Recent Priority Queue Activity</h3>
</div>
<rs-button variant="outline" size="sm" @click="navigateTo('/notification/queue-scheduler/monitor')">
View Full Monitor
</rs-button>
</div>
</template>
<template #body>
<div class="overflow-x-auto">
<table class="min-w-full divide-y divide-gray-200">
<thead class="bg-gray-50">
<tr>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Job ID
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Priority
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Type
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Status
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Queue Time
</th>
<th class="px-6 py-3 text-left text-xs font-medium text-gray-500 uppercase tracking-wider">
Processing Time
</th>
</tr>
</thead>
<tbody class="bg-white divide-y divide-gray-200">
<tr v-for="(job, index) in recentJobs" :key="index">
<td class="px-6 py-4 whitespace-nowrap text-sm font-medium text-gray-900">
{{ job.id }}
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full"
:class="{
'bg-red-100 text-red-800': job.priority === 'critical',
'bg-orange-100 text-orange-800': job.priority === 'high',
'bg-yellow-100 text-yellow-800': job.priority === 'medium',
'bg-blue-100 text-blue-800': job.priority === 'low',
'bg-gray-100 text-gray-800': job.priority === 'bulk'
}"
>
{{ job.priority }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-900">
{{ job.type }}
</td>
<td class="px-6 py-4 whitespace-nowrap">
<span
class="inline-flex px-2 py-1 text-xs font-semibold rounded-full"
:class="{
'bg-green-100 text-green-800': job.status === 'completed',
'bg-yellow-100 text-yellow-800': job.status === 'processing',
'bg-red-100 text-red-800': job.status === 'failed',
'bg-blue-100 text-blue-800': job.status === 'queued'
}"
>
{{ job.status }}
</span>
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ job.queueTime }}
</td>
<td class="px-6 py-4 whitespace-nowrap text-sm text-gray-500">
{{ job.processingTime }}
</td>
</tr>
</tbody>
</table>
</div>
</template>
</rs-card>
<!-- Create Priority Level Modal -->
<rs-modal v-model="showCreatePriorityModal" title="Create Priority Level">
<div class="space-y-6">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Priority Name</label>
<input
type="text"
v-model="newPriority.name"
class="w-full p-2 border border-gray-300 rounded-md"
placeholder="e.g., Emergency Alerts"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Priority Level</label>
<select v-model="newPriority.level" class="w-full p-2 border border-gray-300 rounded-md">
<option value="critical">Critical</option>
<option value="high">High</option>
<option value="medium">Medium</option>
<option value="low">Low</option>
<option value="bulk">Bulk</option>
</select>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Weight (1-100)</label>
<input
type="number"
v-model="newPriority.weight"
class="w-full p-2 border border-gray-300 rounded-md"
min="1"
max="100"
>
<p class="text-xs text-gray-500 mt-1">Higher weight = Higher priority</p>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Description</label>
<textarea
v-model="newPriority.description"
class="w-full p-2 border border-gray-300 rounded-md"
rows="3"
placeholder="Describe when this priority level should be used..."
></textarea>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Max Processing Time (seconds)</label>
<input
type="number"
v-model="newPriority.maxProcessingTime"
class="w-full p-2 border border-gray-300 rounded-md"
min="1"
>
<p class="text-xs text-gray-500 mt-1">Maximum time allowed for processing jobs of this priority</p>
</div>
</div>
<template #footer>
<div class="flex justify-end gap-3">
<rs-button variant="outline" @click="showCreatePriorityModal = false">
Cancel
</rs-button>
<rs-button @click="createPriorityLevel">
Create Priority Level
</rs-button>
</div>
</template>
</rs-modal>
<!-- Edit Priority Modal -->
<rs-modal v-model="showEditPriorityModal" title="Edit Priority Level">
<div class="space-y-6" v-if="editingPriority">
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Priority Name</label>
<input
type="text"
v-model="editingPriority.name"
class="w-full p-2 border border-gray-300 rounded-md"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Weight (1-100)</label>
<input
type="number"
v-model="editingPriority.weight"
class="w-full p-2 border border-gray-300 rounded-md"
min="1"
max="100"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Max Processing Time (seconds)</label>
<input
type="number"
v-model="editingPriority.maxProcessingTime"
class="w-full p-2 border border-gray-300 rounded-md"
min="1"
>
</div>
<div>
<label class="block text-sm font-medium text-gray-700 mb-2">Status</label>
<select v-model="editingPriority.status" class="w-full p-2 border border-gray-300 rounded-md">
<option value="active">Active</option>
<option value="paused">Paused</option>
<option value="throttled">Throttled</option>
</select>
</div>
</div>
<template #footer>
<div class="flex justify-end gap-3">
<rs-button variant="outline" @click="showEditPriorityModal = false">
Cancel
</rs-button>
<rs-button @click="savePriorityChanges">
Save Changes
</rs-button>
</div>
</template>
</rs-modal>
</div>
</template>
<script setup>
definePageMeta({
title: "Priority Queue Management",
middleware: ["auth"],
requiresAuth: true,
breadcrumb: [
{
name: "Notification",
path: "/notification",
},
{
name: "Queue & Scheduler",
path: "/notification/queue-scheduler",
},
{
name: "Priority Management",
path: "/notification/queue-scheduler/priority",
},
],
});
// Reactive data
const showCreatePriorityModal = ref(false);
const showEditPriorityModal = ref(false);
const showBulkEditModal = ref(false);
const editingPriority = ref(null);
// Priority statistics
const priorityStats = ref([
{
title: "Critical Jobs",
value: "47",
icon: "ic:outline-priority-high",
bgColor: "bg-red-100",
iconColor: "text-red-600",
valueColor: "text-red-600"
},
{
title: "High Priority",
value: "234",
icon: "ic:outline-trending-up",
bgColor: "bg-orange-100",
iconColor: "text-orange-600",
valueColor: "text-orange-600"
},
{
title: "Medium Priority",
value: "1,847",
icon: "ic:outline-remove",
bgColor: "bg-yellow-100",
iconColor: "text-yellow-600",
valueColor: "text-yellow-600"
},
{
title: "Low/Bulk Priority",
value: "5,923",
icon: "ic:outline-trending-down",
bgColor: "bg-blue-100",
iconColor: "text-blue-600",
valueColor: "text-blue-600"
}
]);
// Priority levels configuration
const priorityLevels = ref([
{
name: "Emergency Alerts",
level: "critical",
weight: 100,
queueCount: 47,
avgProcessingTime: "0.8s",
status: "active",
maxProcessingTime: 5,
description: "System emergencies and critical security alerts"
},
{
name: "Real-time Notifications",
level: "high",
weight: 80,
queueCount: 234,
avgProcessingTime: "2.1s",
status: "active",
maxProcessingTime: 10,
description: "Time-sensitive notifications like OTP, payment confirmations"
},
{
name: "Standard Notifications",
level: "medium",
weight: 50,
queueCount: 1847,
avgProcessingTime: "5.3s",
status: "active",
maxProcessingTime: 30,
description: "Regular app notifications and updates"
},
{
name: "Marketing Messages",
level: "low",
weight: 30,
queueCount: 3421,
avgProcessingTime: "12.7s",
status: "active",
maxProcessingTime: 60,
description: "Promotional content and marketing campaigns"
},
{
name: "Bulk Operations",
level: "bulk",
weight: 10,
queueCount: 2502,
avgProcessingTime: "45.2s",
status: "throttled",
maxProcessingTime: 300,
description: "Large batch operations and system maintenance"
}
]);
// New priority form
const newPriority = ref({
name: "",
level: "medium",
weight: 50,
description: "",
maxProcessingTime: 30
});
// Computed sorted priority levels
const sortedPriorityLevels = computed(() => {
return [...priorityLevels.value].sort((a, b) => b.weight - a.weight);
});
// Recent jobs data
const recentJobs = ref([
{
id: "job-001",
priority: "critical",
type: "Security Alert",
status: "completed",
queueTime: "0.1s",
processingTime: "0.8s"
},
{
id: "job-002",
priority: "high",
type: "OTP SMS",
status: "completed",
queueTime: "0.3s",
processingTime: "1.2s"
},
{
id: "job-003",
priority: "medium",
type: "App Notification",
status: "processing",
queueTime: "2.1s",
processingTime: "3.4s"
},
{
id: "job-004",
priority: "low",
type: "Newsletter",
status: "queued",
queueTime: "15.2s",
processingTime: "-"
},
{
id: "job-005",
priority: "bulk",
type: "Data Export",
status: "queued",
queueTime: "45.7s",
processingTime: "-"
}
]);
// Methods
const refreshPriorityLevels = () => {
console.log('Refreshing priority levels...');
// Simulate data refresh
};
const createPriorityLevel = () => {
console.log('Creating priority level:', newPriority.value);
// Add to priority levels
priorityLevels.value.push({
...newPriority.value,
queueCount: 0,
avgProcessingTime: "0s",
status: "active"
});
// Reset form
newPriority.value = {
name: "",
level: "medium",
weight: 50,
description: "",
maxProcessingTime: 30
};
showCreatePriorityModal.value = false;
};
const editPriority = (priority) => {
editingPriority.value = { ...priority };
showEditPriorityModal.value = true;
};
const savePriorityChanges = () => {
const index = priorityLevels.value.findIndex(p => p.name === editingPriority.value.name);
if (index !== -1) {
priorityLevels.value[index] = { ...editingPriority.value };
}
showEditPriorityModal.value = false;
editingPriority.value = null;
};
const togglePriorityStatus = (priority) => {
const newStatus = priority.status === 'active' ? 'paused' : 'active';
priority.status = newStatus;
console.log(`Priority ${priority.name} status changed to ${newStatus}`);
};
</script>
<style lang="scss" scoped></style>