feat(ticket): add section icons, theme-aware system timeline, and nested sidebar active routes

This commit is contained in:
2026-07-02 11:33:53 +05:30
parent c73ba0a01f
commit 2eb58856af
5 changed files with 64 additions and 34 deletions

View File

@@ -54,28 +54,32 @@ export function TicketClassContentSkeleton() {
</CardContent>
</Card>
<div className="space-y-4">
<div className="flex items-center gap-2">
<Skeleton className="size-5 rounded" />
<Skeleton className="h-5 w-56" />
</div>
{Array.from({ length: 3 }).map((_, index) => (
<div key={index} className="relative flex gap-4 pb-8 last:pb-0">
{index < 2 ? (
<span className="absolute top-6 left-2.75 h-[calc(100%-10px)] w-px bg-border" />
) : null}
<Skeleton className="relative z-10 mt-0.5 size-6 shrink-0 rounded-full" />
<div className="min-w-0 flex-1 space-y-3">
<div className="flex flex-wrap items-center gap-3">
<Skeleton className="h-4 w-44" />
<Skeleton className="h-3 w-28" />
</div>
<Skeleton className="h-3 w-56" />
<Skeleton className="h-12 w-full" />
</div>
<Card>
<CardHeader>
<div className="flex items-center gap-2">
<Skeleton className="size-5 rounded" />
<Skeleton className="h-5 w-56" />
</div>
))}
</div>
</CardHeader>
<CardContent>
{Array.from({ length: 3 }).map((_, index) => (
<div key={index} className="relative flex gap-4 pb-8 last:pb-0">
{index < 2 ? (
<span className="absolute top-6 left-2.75 h-[calc(100%-10px)] w-px bg-border" />
) : null}
<Skeleton className="relative z-10 mt-0.5 size-6 shrink-0 rounded-full" />
<div className="min-w-0 flex-1 space-y-3">
<div className="flex flex-wrap items-center gap-3">
<Skeleton className="h-4 w-44" />
<Skeleton className="h-3 w-28" />
</div>
<Skeleton className="h-3 w-56" />
<Skeleton className="h-12 w-full" />
</div>
</div>
))}
</CardContent>
</Card>
</div>
);
}

View File

@@ -2,7 +2,7 @@
import { useEffect, useMemo, useState } from 'react';
import { usePathname, useRouter, useSearchParams } from 'next/navigation';
import { Component } from 'lucide-react';
import { AlertTriangle, Component } from 'lucide-react';
import { Badge } from '@/components/ui/badge';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
@@ -89,6 +89,7 @@ export function TicketDefectClassTabs({
<Card className="w-full">
<CardHeader>
<CardTitle className="flex items-center gap-2 text-base">
<AlertTriangle className="size-5 shrink-0 text-primary" />
<span>Issues</span>
<Badge
variant="secondary"

View File

@@ -3,9 +3,10 @@
import { History, MessageSquareText } from 'lucide-react';
import { PersonInfo } from '@/components/person-avatar';
import { Badge } from '@/components/ui/badge';
import { Timeline, TimelineItem } from '@/components/ui/timeline';
import { cn } from '@/lib/utils';
import type { TicketDetail, TicketHistoryItem } from '@/types';
import type { TicketDetail, TicketHistoryItem, TicketHistorySource } from '@/types';
import { formatTimelineDateTime } from '@/utils/date';
import { getPersonLabel } from '@/utils/person';
@@ -39,14 +40,21 @@ function HistoryNote({ item }: { item: TicketHistoryItem }) {
);
}
function getSourceLabel(source: TicketHistorySource) {
return source === 'system' ? 'System Generated' : 'User Action';
}
function SystemHistoryEntry({ item }: { item: TicketHistoryItem }) {
const noteText = item.note?.text?.trim();
return (
<div className="rounded-xl border border-dashed border-violet-300/80 bg-violet-50/90 px-4 py-3.5 dark:border-violet-400/25 dark:bg-violet-500/8">
<span className="inline-flex rounded-full bg-violet-200/90 px-2.5 py-0.5 text-[10px] font-semibold tracking-wider text-violet-700 uppercase dark:bg-violet-500/20 dark:text-violet-300">
System Generated
</span>
<div className="rounded-md border border-dashed border-primary/25 bg-primary/5 px-4 py-3.5">
<Badge
className="rounded-xs px-2.5 py-0.5 text-[10px] font-semibold tracking-wider uppercase"
>
{getSourceLabel(item.source)}
</Badge>
<h3 className="mt-2 text-sm font-semibold text-foreground">{item.title}</h3>
{noteText ? (
<p className="mt-1 text-sm leading-relaxed text-muted-foreground">
@@ -62,7 +70,7 @@ function UserHistoryEntry({ item }: { item: TicketHistoryItem }) {
const isAssigned = item.event_type === 'assigned';
return (
<div className="rounded-xl border border-border bg-card p-4 shadow-sm">
<div className="rounded-md border border-border bg-card p-4">
<h3 className="mb-3 text-sm font-semibold text-foreground">{item.title}</h3>
<PersonInfo person={item.actor} />
{isAssigned && item.target_user ? (
@@ -116,7 +124,7 @@ export function TicketHistoryCard({ ticket }: { ticket: TicketDetail }) {
))}
</Timeline>
) : (
<div className="rounded-lg border border-dashed border-border/80 bg-muted/10 px-4 py-8 text-center">
<div className="rounded-md border border-dashed border-border/80 bg-muted/10 px-4 py-8 text-center">
<p className="text-sm font-medium text-muted-foreground">
No timeline entries yet
</p>

View File

@@ -1,5 +1,7 @@
'use client';
import { LayoutDashboard } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { PersonInfo } from '@/components/person-avatar';
import type { TicketOverviewDetail } from '@/types';
@@ -40,7 +42,10 @@ export function TicketOverviewCard({
return (
<Card>
<CardHeader>
<CardTitle>Overview</CardTitle>
<CardTitle className="flex items-center gap-2">
<LayoutDashboard className="size-5 text-primary" />
Overview
</CardTitle>
</CardHeader>
<CardContent className="grid grid-cols-2 gap-x-4 gap-y-6 md:grid-cols-4">