This commit is contained in:
177
app/pages/workload/index.vue
Normal file
177
app/pages/workload/index.vue
Normal file
@@ -0,0 +1,177 @@
|
||||
<script setup lang="ts">
|
||||
import type { SelectItem } from '@nuxt/ui'
|
||||
|
||||
const page = ref(1)
|
||||
const statusFilter = ref<string | null>(null)
|
||||
const policyTypeFilter = ref<string | null>(null)
|
||||
|
||||
const statusItems = ref<SelectItem[]>([
|
||||
{ label: 'All Statuses', value: null },
|
||||
{ label: 'Created', value: 'created' },
|
||||
{ label: 'Draft', value: 'draft' },
|
||||
{ label: 'Approved', value: 'approved' },
|
||||
{ label: 'Completed', value: 'completed' }
|
||||
])
|
||||
|
||||
const policyTypeItems = ref<SelectItem[]>([
|
||||
{ label: 'All Types', value: null },
|
||||
{ label: 'Car', value: 'car' },
|
||||
{ label: 'Life', value: 'life' },
|
||||
{ label: 'Fire', value: 'fire' }
|
||||
])
|
||||
|
||||
watch([statusFilter, policyTypeFilter], () => { page.value = 1 })
|
||||
|
||||
const { data, pending, error, refresh } = useWorkload('/tasks', {
|
||||
query: computed(() => ({
|
||||
page: page.value,
|
||||
page_size: 20,
|
||||
...(statusFilter.value && { status: statusFilter.value }),
|
||||
...(policyTypeFilter.value && { policy_type: policyTypeFilter.value })
|
||||
}))
|
||||
})
|
||||
|
||||
const tasks = computed(() => data.value?.data ?? [])
|
||||
const meta = computed(() => data.value?.meta)
|
||||
const total = computed(() => meta.value?.total_count ?? 0)
|
||||
const totalPages = computed(() => meta.value?.total_pages ?? 0)
|
||||
|
||||
const statusColor = (status: string) => {
|
||||
switch (status) {
|
||||
case 'created': return 'yellow'
|
||||
case 'draft': return 'blue'
|
||||
case 'approved': return 'green'
|
||||
case 'completed': return 'gray'
|
||||
default: return 'gray'
|
||||
}
|
||||
}
|
||||
|
||||
const policyTypeColor = (type: string) => {
|
||||
switch (type) {
|
||||
case 'car': return 'blue'
|
||||
case 'life': return 'purple'
|
||||
case 'fire': return 'orange'
|
||||
default: return 'gray'
|
||||
}
|
||||
}
|
||||
|
||||
const getTaskType = (id: string) => {
|
||||
const parts = id?.split(':') ?? []
|
||||
return parts[1] || 'unknown'
|
||||
}
|
||||
|
||||
const formatDate = (date: string) => {
|
||||
if (!date) return '—'
|
||||
return new Date(date).toLocaleDateString('es-PA', {
|
||||
day: '2-digit', month: 'short', year: 'numeric',
|
||||
hour: '2-digit', minute: '2-digit'
|
||||
})
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="p-8 space-y-8 bg-gray-50 min-h-screen">
|
||||
<!-- Header -->
|
||||
<div class="flex justify-between items-center">
|
||||
<div>
|
||||
<h1 class="text-3xl text-slate-900 font-bold">Workload</h1>
|
||||
<p class="text-gray-500 text-sm">Quote & Solicitation Tasks</p>
|
||||
</div>
|
||||
<div class="flex items-center gap-3">
|
||||
<UBadge color="gray" variant="soft" size="lg">{{ total }} tasks</UBadge>
|
||||
<UButton
|
||||
icon="i-heroicons-arrow-path"
|
||||
color="gray"
|
||||
variant="soft"
|
||||
:loading="pending"
|
||||
@click="refresh()"
|
||||
>
|
||||
Refresh
|
||||
</UButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Filters -->
|
||||
<div class="flex gap-4 items-center flex-wrap">
|
||||
<USelect v-model="statusFilter" :items="statusItems" class="w-40" />
|
||||
<USelect v-model="policyTypeFilter" :items="policyTypeItems" class="w-40" />
|
||||
</div>
|
||||
|
||||
<UAlert
|
||||
v-if="error"
|
||||
color="red"
|
||||
variant="soft"
|
||||
title="Failed to load tasks"
|
||||
:description="error.message"
|
||||
/>
|
||||
|
||||
<div v-else-if="pending && tasks.length === 0" class="grid gap-4">
|
||||
<UCard v-for="n in 5" :key="n">
|
||||
<div class="h-20 animate-pulse bg-gray-200 rounded" />
|
||||
</UCard>
|
||||
</div>
|
||||
|
||||
<template v-else>
|
||||
<div class="space-y-3" :class="pending ? 'opacity-60 pointer-events-none' : ''">
|
||||
<NuxtLink
|
||||
v-for="task in tasks"
|
||||
:key="task.id"
|
||||
:to="`/workload/${encodeURIComponent(task.id)}`"
|
||||
>
|
||||
<UCard class="hover:shadow-md transition-shadow cursor-pointer">
|
||||
<div class="flex items-center justify-between gap-4">
|
||||
<!-- Left -->
|
||||
<div class="flex items-center gap-4 min-w-0">
|
||||
<div class="flex flex-col gap-1">
|
||||
<UBadge :color="statusColor(task.status)" variant="soft" size="xs">
|
||||
{{ task.status }}
|
||||
</UBadge>
|
||||
<UBadge :color="policyTypeColor(task.task_info?.policy_type)" variant="outline" size="xs">
|
||||
{{ task.task_info?.policy_type?.toUpperCase() || '—' }}
|
||||
</UBadge>
|
||||
</div>
|
||||
|
||||
<div class="min-w-0">
|
||||
<p class="font-mono text-sm font-medium text-slate-800 truncate">
|
||||
{{ task.application_id }}
|
||||
</p>
|
||||
<p class="text-xs text-gray-400">
|
||||
Provider: <span class="font-mono">{{ task.provider_id }}</span>
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Right -->
|
||||
<div class="flex items-center gap-6 flex-shrink-0 text-sm text-gray-500">
|
||||
<div class="text-right">
|
||||
<p class="text-xs text-gray-400">Type</p>
|
||||
<UBadge color="gray" variant="soft" size="xs">{{ getTaskType(task.id) }}</UBadge>
|
||||
</div>
|
||||
<div class="text-right">
|
||||
<p class="text-xs text-gray-400">Received</p>
|
||||
<p>{{ formatDate(task.created_at) }}</p>
|
||||
</div>
|
||||
<UIcon name="i-heroicons-chevron-right" class="w-4 h-4 text-gray-400" />
|
||||
</div>
|
||||
</div>
|
||||
</UCard>
|
||||
</NuxtLink>
|
||||
|
||||
<div v-if="tasks.length === 0 && !pending" class="text-center py-16 text-gray-400">
|
||||
<UIcon name="i-heroicons-inbox" class="w-12 h-12 mx-auto mb-4" />
|
||||
<p class="text-lg font-medium">No tasks found</p>
|
||||
<p class="text-sm">Adjust your filters or wait for new requests</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Pagination -->
|
||||
<div v-if="totalPages > 1" class="flex justify-center">
|
||||
<UPagination
|
||||
v-model="page"
|
||||
:total="total"
|
||||
:page-count="20"
|
||||
/>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user