From cbc706d934e381092c597dab2a36eac19e3f9edd672d9ed2d62035e002308df3 Mon Sep 17 00:00:00 2001 From: Claude Dev Date: Mon, 9 Feb 2026 20:24:09 -0500 Subject: [PATCH] Add recent support tickets section to customer dashboard Shows the 5 most recent tickets with status/priority chips and relative timestamps. Includes empty state with Create Ticket button. Co-Authored-By: Claude Sonnet 4.5 --- .../Account/DashboardController.php | 7 + website/resources/ts/Pages/Dashboard.vue | 125 +++++++++++++++++- 2 files changed, 129 insertions(+), 3 deletions(-) diff --git a/website/app/Http/Controllers/Account/DashboardController.php b/website/app/Http/Controllers/Account/DashboardController.php index a4c6090..61bc76f 100644 --- a/website/app/Http/Controllers/Account/DashboardController.php +++ b/website/app/Http/Controllers/Account/DashboardController.php @@ -48,6 +48,12 @@ class DashboardController extends Controller ->whereIn('status', ['open', 'in_progress', 'waiting']) ->count(); + $recentTickets = SupportTicket::query() + ->where('user_id', $user->id) + ->latest() + ->take(5) + ->get(['id', 'subject', 'status', 'priority', 'created_at', 'updated_at']); + return Inertia::render('Dashboard', [ 'activeServicesCount' => $activeServicesCount, 'activeSubscriptionsCount' => $activeSubscriptionsCount, @@ -56,6 +62,7 @@ class DashboardController extends Controller 'pendingInvoicesAmount' => number_format((float) $pendingInvoicesAmount, 2, '.', ''), 'nextRenewalDate' => $nextRenewalDate, 'openTicketsCount' => $openTicketsCount, + 'recentTickets' => $recentTickets, ]); } } diff --git a/website/resources/ts/Pages/Dashboard.vue b/website/resources/ts/Pages/Dashboard.vue index c5b380f..a714d05 100644 --- a/website/resources/ts/Pages/Dashboard.vue +++ b/website/resources/ts/Pages/Dashboard.vue @@ -3,8 +3,8 @@ import { Link } from '@inertiajs/vue3' import { computed } from 'vue' import AccountLayout from '@/Layouts/AccountLayout.vue' import StatCard from '@/Components/StatCard.vue' -import { resolveSubscriptionStatusColor, resolveInvoiceStatusColor, formatPrice } from '@/utils/resolvers' -import type { Subscription, Invoice } from '@/types' +import { resolveSubscriptionStatusColor, resolveInvoiceStatusColor, resolveTicketStatusColor, resolveTicketPriorityColor, formatPrice } from '@/utils/resolvers' +import type { Subscription, Invoice, SupportTicket } from '@/types' interface Props { activeServicesCount: number @@ -14,6 +14,7 @@ interface Props { pendingInvoicesAmount: string nextRenewalDate: string | null openTicketsCount: number + recentTickets: SupportTicket[] } defineOptions({ layout: AccountLayout }) @@ -40,6 +41,33 @@ const formattedNextRenewal = computed(() => { return formatDate(props.nextRenewalDate) }) +function formatRelativeTime(dateString: string): string { + const date = new Date(dateString) + const now = new Date() + const diffMs = now.getTime() - date.getTime() + const diffMinutes = Math.floor(diffMs / 60000) + const diffHours = Math.floor(diffMs / 3600000) + const diffDays = Math.floor(diffMs / 86400000) + + if (diffMinutes < 1) { + return 'Just now' + } + if (diffMinutes < 60) { + return `${diffMinutes}m ago` + } + if (diffHours < 24) { + return `${diffHours}h ago` + } + if (diffDays < 7) { + return `${diffDays}d ago` + } + return formatDate(dateString) +} + +function formatStatus(status: string): string { + return status.replace(/_/g, ' ') +} + const unpaidInvoices = computed(() => { return props.latestInvoices.filter( (invoice: Invoice) => invoice.status === 'pending' || invoice.status === 'overdue', @@ -324,7 +352,98 @@ const unpaidInvoices = computed(() => { - + + + + + + Recent Support Tickets + + View All + + + + +
+ +
+ No support tickets yet +
+ + + Create Ticket + + +
+
+ + + + + Subject + Status + Priority + Last Updated + + + + + + + {{ ticket.subject }} + + + + + {{ formatStatus(ticket.status) }} + + + + + {{ ticket.priority }} + + + + {{ formatRelativeTime(ticket.updated_at) }} + + + + +
+
+
+ +