fix(ticket): gate AI detection summary behind assign permission
This commit is contained in:
@@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user