fix(ticket): gate AI detection summary behind assign permission

This commit is contained in:
2026-07-29 19:07:22 +05:30
parent 31839d1c70
commit e894676d3b

View File

@@ -8,7 +8,7 @@ import {
MapPin, MapPin,
Monitor, Monitor,
UserRound, UserRound,
Play Play,
} from 'lucide-react'; } from 'lucide-react';
import { useState } from 'react'; import { useState } from 'react';
@@ -172,79 +172,84 @@ export function TicketOverviewCard({
</Card> </Card>
) : null} ) : null}
<Card> <PermissionGuard permissions={PERMISSIONS.TICKET.ASSIGN}>
<CardHeader> <>
<CardTitle className="flex items-center gap-2"> <Card>
<Activity className="size-5 text-primary" /> <CardHeader>
AI Detection Summary <CardTitle className="flex items-center gap-2">
</CardTitle> <Activity className="size-5 text-primary" />
{analysisVideoId ? ( AI Detection Summary
<CardAction> </CardTitle>
<PermissionGuard permissions={PERMISSIONS.TICKET.ASSIGN}> {analysisVideoId ? (
<Button <CardAction>
variant="secondary" <Button
onClick={() => setIsAnalysisOpen(true)} variant="secondary"
> onClick={() => setIsAnalysisOpen(true)}
<Play className="mr-2 size-4" /> >
View Video <Play className="mr-2 size-4" />
</Button> View Video
</PermissionGuard> </Button>
</CardAction> </CardAction>
) : null} ) : null}
</CardHeader> </CardHeader>
<CardContent className="space-y-4"> <CardContent className="space-y-4">
<div className="grid grid-cols-2 gap-3 lg:grid-cols-3 xl:grid-cols-6"> <div className="grid grid-cols-2 gap-3 lg:grid-cols-3 xl:grid-cols-6">
<SummaryCard
label="Total Detections"
value={ticket.ai_result.detection_count}
colorClassName="text-violet-600"
cardClassName="bg-violet-500/8"
/>
{ticket.ai_result.detections_by_class.map((item) => {
const visual = getDefectVisual(item.class_name);
return (
<SummaryCard <SummaryCard
key={item.class_name} label="Total Detections"
label={item.display_name} value={ticket.ai_result.detection_count}
value={item.count} colorClassName="text-violet-600"
colorClassName={visual.colorClassName} cardClassName="bg-violet-500/8"
cardClassName={visual.cardClassName}
/> />
); {ticket.ai_result.detections_by_class.map((item) => {
})} const visual = getDefectVisual(item.class_name);
</div> return (
{hasVideoMetrics ? ( <SummaryCard
<div className="grid gap-3 rounded-lg bg-muted/35 px-4 py-3 text-sm sm:grid-cols-3"> key={item.class_name}
{videoMetadata?.fps ? ( label={item.display_name}
<VideoMetric icon={Gauge} label={`${videoMetadata.fps} FPS`} /> value={item.count}
colorClassName={visual.colorClassName}
cardClassName={visual.cardClassName}
/>
);
})}
</div>
{hasVideoMetrics ? (
<div className="grid gap-3 rounded-lg bg-muted/35 px-4 py-3 text-sm sm:grid-cols-3">
{videoMetadata?.fps ? (
<VideoMetric
icon={Gauge}
label={`${videoMetadata.fps} FPS`}
/>
) : null}
{duration ? (
<VideoMetric icon={Clock3} label={`${duration} Duration`} />
) : null}
{videoMetadata?.resolution?.label ? (
<VideoMetric
icon={Monitor}
label={`${videoMetadata.resolution.label} Resolution`}
/>
) : null}
</div>
) : null} ) : null}
{duration ? ( </CardContent>
<VideoMetric icon={Clock3} label={`${duration} Duration`} /> </Card>
) : null}
{videoMetadata?.resolution?.label ? (
<VideoMetric
icon={Monitor}
label={`${videoMetadata.resolution.label} Resolution`}
/>
) : null}
</div>
) : null}
</CardContent>
</Card>
<Dialog open={isAnalysisOpen} onOpenChange={setIsAnalysisOpen}> <Dialog open={isAnalysisOpen} onOpenChange={setIsAnalysisOpen}>
<DialogContent className="sm:max-w-5xl"> <DialogContent className="sm:max-w-5xl">
<DialogHeader> <DialogHeader>
<DialogTitle>{analysisTitle}</DialogTitle> <DialogTitle>{analysisTitle}</DialogTitle>
<DialogDescription> <DialogDescription>
Video analysis with annotation overlay Video analysis with annotation overlay
</DialogDescription> </DialogDescription>
</DialogHeader> </DialogHeader>
<DialogBody> <DialogBody>
<TicketInlineAnalysisVideo ticket={ticket} /> <TicketInlineAnalysisVideo ticket={ticket} />
</DialogBody> </DialogBody>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
</>
</PermissionGuard>
</div> </div>
); );
} }