traceability
This commit is contained in:
307
src/pages/Dashboard.tsx
Normal file
307
src/pages/Dashboard.tsx
Normal file
@@ -0,0 +1,307 @@
|
||||
import { AppLayout } from "@/components/layout/AppLayout";
|
||||
import { useTraceabilityData } from "@/hooks/useTraceabilityData";
|
||||
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
|
||||
import { Badge } from "@/components/ui/badge";
|
||||
import { Button } from "@/components/ui/button";
|
||||
import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
|
||||
import { ScrollArea } from "@/components/ui/scroll-area";
|
||||
import { CSVUpload } from "@/components/CSVUpload";
|
||||
import { WorkPackage } from "@/types/traceability";
|
||||
import {
|
||||
Target,
|
||||
CheckSquare,
|
||||
FileText,
|
||||
TestTube,
|
||||
Layers,
|
||||
Bug,
|
||||
AlertTriangle,
|
||||
GitBranch,
|
||||
TrendingUp,
|
||||
Activity,
|
||||
Download,
|
||||
ChevronDown,
|
||||
Terminal,
|
||||
Upload,
|
||||
} from "lucide-react";
|
||||
import { Link } from "react-router-dom";
|
||||
import { useState } from "react";
|
||||
|
||||
const typeIcons: Record<string, React.ReactNode> = {
|
||||
feature: <Target className="h-5 w-5" />,
|
||||
requirements: <CheckSquare className="h-5 w-5" />,
|
||||
swreq: <FileText className="h-5 w-5" />,
|
||||
"test case": <TestTube className="h-5 w-5" />,
|
||||
epic: <Layers className="h-5 w-5" />,
|
||||
bug: <Bug className="h-5 w-5" />,
|
||||
risk: <AlertTriangle className="h-5 w-5" />,
|
||||
task: <CheckSquare className="h-5 w-5" />,
|
||||
};
|
||||
|
||||
const typeColors: Record<string, string> = {
|
||||
feature: "bg-purple-500/10 text-purple-700 border-purple-500/20",
|
||||
requirements: "bg-blue-500/10 text-blue-700 border-blue-500/20",
|
||||
swreq: "bg-indigo-500/10 text-indigo-700 border-indigo-500/20",
|
||||
"test case": "bg-green-500/10 text-green-700 border-green-500/20",
|
||||
epic: "bg-amber-500/10 text-amber-700 border-amber-500/20",
|
||||
bug: "bg-red-500/10 text-red-700 border-red-500/20",
|
||||
risk: "bg-orange-500/10 text-orange-700 border-orange-500/20",
|
||||
task: "bg-slate-500/10 text-slate-700 border-slate-500/20",
|
||||
};
|
||||
|
||||
export default function Dashboard() {
|
||||
const { data, loading, lastUpdated, refresh, typeCounts, groupedByType, parseLog, setData } =
|
||||
useTraceabilityData();
|
||||
const [showDebug, setShowDebug] = useState(false);
|
||||
const [showUpload, setShowUpload] = useState(false);
|
||||
|
||||
const totalItems = data?.workPackages.length || 0;
|
||||
|
||||
// Calculate traceability coverage
|
||||
const features = groupedByType["feature"] || [];
|
||||
const requirements = groupedByType["requirements"] || [];
|
||||
const swreqs = groupedByType["swreq"] || [];
|
||||
const testCases = groupedByType["test case"] || [];
|
||||
|
||||
const handleDownloadCSV = () => {
|
||||
window.open('/data/traceability_export.csv', '_blank');
|
||||
};
|
||||
|
||||
const handleDataLoaded = (workPackages: WorkPackage[]) => {
|
||||
setData({
|
||||
lastUpdated: new Date(),
|
||||
workPackages
|
||||
});
|
||||
setShowUpload(false);
|
||||
};
|
||||
|
||||
return (
|
||||
<AppLayout
|
||||
lastUpdated={lastUpdated}
|
||||
onRefresh={refresh}
|
||||
isRefreshing={loading}
|
||||
>
|
||||
<div className="space-y-6">
|
||||
{/* Header Stats */}
|
||||
<div className="grid grid-cols-1 md:grid-cols-4 gap-4">
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">Total Items</CardTitle>
|
||||
<GitBranch className="h-4 w-4 text-muted-foreground" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{totalItems}</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Work packages tracked
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">Features</CardTitle>
|
||||
<Target className="h-4 w-4 text-purple-500" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{features.length}</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
System features defined
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">Requirements</CardTitle>
|
||||
<CheckSquare className="h-4 w-4 text-blue-500" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">
|
||||
{requirements.length + swreqs.length}
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
SR + SWR total
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
<Card>
|
||||
<CardHeader className="flex flex-row items-center justify-between pb-2">
|
||||
<CardTitle className="text-sm font-medium">Test Cases</CardTitle>
|
||||
<TestTube className="h-4 w-4 text-green-500" />
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="text-2xl font-bold">{testCases.length}</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Verification tests
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Data Management */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Download className="h-5 w-5" />
|
||||
Data Management
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent className="space-y-4">
|
||||
<div className="flex flex-wrap gap-3">
|
||||
<Button variant="outline" onClick={handleDownloadCSV}>
|
||||
<Download className="h-4 w-4 mr-2" />
|
||||
Download CSV
|
||||
</Button>
|
||||
<Button variant="outline" onClick={() => setShowUpload(!showUpload)}>
|
||||
<Upload className="h-4 w-4 mr-2" />
|
||||
Upload CSV
|
||||
</Button>
|
||||
<Button variant="outline" onClick={refresh} disabled={loading}>
|
||||
Reload Data
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
{showUpload && (
|
||||
<CSVUpload
|
||||
onDataLoaded={handleDataLoaded}
|
||||
onClose={() => setShowUpload(false)}
|
||||
/>
|
||||
)}
|
||||
|
||||
<Collapsible open={showDebug} onOpenChange={setShowDebug}>
|
||||
<CollapsibleTrigger asChild>
|
||||
<Button variant="ghost" size="sm" className="flex items-center gap-2">
|
||||
<Terminal className="h-4 w-4" />
|
||||
Debug Logs
|
||||
<ChevronDown className={`h-4 w-4 transition-transform ${showDebug ? 'rotate-180' : ''}`} />
|
||||
</Button>
|
||||
</CollapsibleTrigger>
|
||||
<CollapsibleContent>
|
||||
<Card className="mt-2 bg-slate-950">
|
||||
<CardContent className="p-4">
|
||||
<ScrollArea className="h-48">
|
||||
<div className="font-mono text-xs text-green-400 space-y-1">
|
||||
{parseLog.length > 0 ? (
|
||||
parseLog.map((log, i) => (
|
||||
<div key={i} className="opacity-90">{log}</div>
|
||||
))
|
||||
) : (
|
||||
<div className="text-slate-500">No logs available. Click "Reload Data" to see parsing logs.</div>
|
||||
)}
|
||||
</div>
|
||||
</ScrollArea>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</CollapsibleContent>
|
||||
</Collapsible>
|
||||
|
||||
<div className="text-xs text-muted-foreground">
|
||||
<p>To update data from OpenProject, run the Python script locally:</p>
|
||||
<code className="bg-muted px-2 py-1 rounded text-xs mt-1 block">
|
||||
python public/data/get_traceability.py
|
||||
</code>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Traceability Summary */}
|
||||
<div className="grid grid-cols-1 lg:grid-cols-2 gap-6">
|
||||
{/* Type Distribution */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<Activity className="h-5 w-5" />
|
||||
Work Package Distribution
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-3">
|
||||
{Object.entries(typeCounts)
|
||||
.sort(([, a], [, b]) => b - a)
|
||||
.map(([type, count]) => (
|
||||
<Link
|
||||
key={type}
|
||||
to={`/alm/${type.replace(" ", "-")}`}
|
||||
className="flex items-center justify-between p-2 rounded-lg hover:bg-accent transition-colors"
|
||||
>
|
||||
<div className="flex items-center gap-3">
|
||||
<div
|
||||
className={`p-2 rounded-md border ${
|
||||
typeColors[type] || "bg-muted"
|
||||
}`}
|
||||
>
|
||||
{typeIcons[type] || <FileText className="h-5 w-5" />}
|
||||
</div>
|
||||
<span className="font-medium capitalize">{type}</span>
|
||||
</div>
|
||||
<Badge variant="secondary">{count}</Badge>
|
||||
</Link>
|
||||
))}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
{/* Quick Links */}
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2">
|
||||
<TrendingUp className="h-5 w-5" />
|
||||
Traceability Chain
|
||||
</CardTitle>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between p-3 bg-muted/30 rounded-lg">
|
||||
<span>Features → Requirements</span>
|
||||
<Badge variant="outline">
|
||||
{features.length} → {requirements.length}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 bg-muted/30 rounded-lg">
|
||||
<span>Requirements → SW Requirements</span>
|
||||
<Badge variant="outline">
|
||||
{requirements.length} → {swreqs.length}
|
||||
</Badge>
|
||||
</div>
|
||||
<div className="flex items-center justify-between p-3 bg-muted/30 rounded-lg">
|
||||
<span>SW Requirements → Test Cases</span>
|
||||
<Badge variant="outline">
|
||||
{swreqs.length} → {testCases.length}
|
||||
</Badge>
|
||||
</div>
|
||||
|
||||
<div className="pt-4 border-t">
|
||||
<h4 className="text-sm font-medium mb-3">Quick Actions</h4>
|
||||
<div className="flex flex-wrap gap-2">
|
||||
<Link to="/documentation">
|
||||
<Badge variant="outline" className="cursor-pointer hover:bg-accent">
|
||||
📄 View Documentation
|
||||
</Badge>
|
||||
</Link>
|
||||
<Link to="/analysis">
|
||||
<Badge variant="outline" className="cursor-pointer hover:bg-accent">
|
||||
🔍 Gap Analysis
|
||||
</Badge>
|
||||
</Link>
|
||||
<Link to="/matrix">
|
||||
<Badge variant="outline" className="cursor-pointer hover:bg-accent">
|
||||
🔗 Traceability Matrix
|
||||
</Badge>
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
|
||||
{/* Loading State */}
|
||||
{loading && (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
Loading traceability data...
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</AppLayout>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user