Skip to content

Commit 5a6c8ec

Browse files
committed
Merge branch 'feature/security-hardening' into main
2 parents 7d436f4 + e155f61 commit 5a6c8ec

22 files changed

Lines changed: 1672 additions & 547 deletions

client/src/components/app-sidebar.tsx

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,8 @@ import {
1515
Search,
1616
ShoppingCart,
1717
Download,
18+
CreditCard,
19+
Megaphone,
1820
} from "lucide-react";
1921
import {
2022
Sidebar,
@@ -61,6 +63,11 @@ const navItems = [
6163
url: "/audit",
6264
icon: ShieldCheck,
6365
},
66+
{
67+
title: "Pricing Plans",
68+
url: "/pricing",
69+
icon: CreditCard,
70+
},
6471
{
6572
title: "Settings",
6673
url: "/settings",
@@ -77,28 +84,33 @@ const adminNavItems = [
7784
{
7885
title: "Orders",
7986
url: "/admin/orders",
80-
icon: ShoppingCart, // Assuming ShoppingCart icon is imported, let's check
87+
icon: ShoppingCart,
8188
},
8289
{
8390
title: "Users",
8491
url: "/admin/users",
85-
icon: ShieldCheck, // or another appropriate icon
92+
icon: ShieldCheck,
8693
},
8794
{
8895
title: "Live Requests",
8996
url: "/admin/requests",
90-
icon: Activity, // need to import
97+
icon: Activity,
9198
},
9299
{
93100
title: "System Health",
94101
url: "/admin/system",
95-
icon: Server, // need to import
102+
icon: Server,
96103
},
97104
{
98105
title: "Free Audit Queue",
99106
url: "/admin/free-audit-queue",
100107
icon: ShieldCheck,
101108
},
109+
{
110+
title: "Free Audit Campaign",
111+
url: "/free-audit-request",
112+
icon: Megaphone,
113+
},
102114
{
103115
title: "Audit Log",
104116
url: "/admin/audit-log",

client/src/pages/admin/overview.tsx

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,37 @@ export default function AdminOverview() {
8484
</Card>
8585
</div>
8686

87+
<h2 className="text-xl font-bold mt-8 mb-4">Post-Audit Feedback</h2>
88+
<div className="grid gap-4 md:grid-cols-3">
89+
<Card>
90+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
91+
<CardTitle className="text-sm font-medium">Feedback Responses</CardTitle>
92+
<Activity className="h-4 w-4 text-muted-foreground" />
93+
</CardHeader>
94+
<CardContent>
95+
<div className="text-2xl font-bold">{metrics?.feedback?.count || 0}</div>
96+
</CardContent>
97+
</Card>
98+
<Card>
99+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
100+
<CardTitle className="text-sm font-medium">Avg Accuracy Rating</CardTitle>
101+
<ShieldCheck className="h-4 w-4 text-muted-foreground" />
102+
</CardHeader>
103+
<CardContent>
104+
<div className="text-2xl font-bold">{metrics?.feedback?.avgAccuracy || "0.0"} <span className="text-sm font-normal text-muted-foreground">/ 5</span></div>
105+
</CardContent>
106+
</Card>
107+
<Card>
108+
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
109+
<CardTitle className="text-sm font-medium">Avg Value Rating</CardTitle>
110+
<DollarSign className="h-4 w-4 text-muted-foreground" />
111+
</CardHeader>
112+
<CardContent>
113+
<div className="text-2xl font-bold">{metrics?.feedback?.avgValue || "0.0"} <span className="text-sm font-normal text-muted-foreground">/ 5</span></div>
114+
</CardContent>
115+
</Card>
116+
</div>
117+
87118
<div className="grid gap-4 md:grid-cols-2">
88119
<Card className="col-span-1">
89120
<CardHeader>

client/src/pages/audit.tsx

Lines changed: 123 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,22 @@ import { apiRequest } from "@/lib/queryClient";
1010
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
1111
import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table";
1212
import { LineChart, Line, ResponsiveContainer } from "recharts";
13+
import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogDescription, DialogFooter } from "@/components/ui/dialog";
14+
import { Textarea } from "@/components/ui/textarea";
1315

1416
export default function AuditPage() {
1517
const [selectedRepoId, setSelectedRepoId] = useState("");
1618
const [branch, setBranch] = useState("");
1719
const [currentAuditId, setCurrentAuditId] = useState<string | null>(null);
1820
const [logs, setLogs] = useState<{log: string, progress: number, timestamp: string}[]>([]);
1921
const [progress, setProgress] = useState(0);
22+
23+
// Feedback Modal State
24+
const [showFeedbackModal, setShowFeedbackModal] = useState(false);
25+
const [feedbackRating, setFeedbackRating] = useState<number | null>(null);
26+
const [valueRating, setValueRating] = useState<number | null>(null);
27+
const [feedbackText, setFeedbackText] = useState("");
28+
2029
const { toast } = useToast();
2130
const queryClient = useQueryClient();
2231

@@ -160,6 +169,45 @@ export default function AuditPage() {
160169
enabled: !!currentAuditId && audit?.status === "complete",
161170
});
162171

172+
const { data: feedbackData } = useQuery<any>({
173+
queryKey: ["/api/audits", currentAuditId, "feedback"],
174+
enabled: !!currentAuditId && audit?.status === "complete",
175+
});
176+
177+
const showFeedbackMutation = useMutation({
178+
mutationFn: async () => {
179+
const res = await apiRequest("POST", `/api/audits/${currentAuditId}/feedback/show`);
180+
return res.json();
181+
}
182+
});
183+
184+
const submitFeedbackMutation = useMutation({
185+
mutationFn: async (data: { responses: any, freeText: string }) => {
186+
const res = await apiRequest("POST", `/api/audits/${currentAuditId}/feedback/submit`, data);
187+
return res.json();
188+
},
189+
onSuccess: () => {
190+
toast({
191+
title: "Feedback Submitted",
192+
description: "Thank you for helping us improve CodeGuard!",
193+
});
194+
setShowFeedbackModal(false);
195+
queryClient.invalidateQueries({ queryKey: ["/api/audits", currentAuditId, "feedback"] });
196+
}
197+
});
198+
199+
const handleDownloadPdf = () => {
200+
window.open(`/api/audits/${currentAuditId}/pdf`, '_blank');
201+
202+
// Wait a brief moment then show feedback prompt if they haven't responded yet
203+
setTimeout(() => {
204+
if (!feedbackData?.respondedAt) {
205+
showFeedbackMutation.mutate();
206+
setShowFeedbackModal(true);
207+
}
208+
}, 1500);
209+
};
210+
163211
const createOrderMutation = useMutation({
164212
mutationFn: async () => {
165213
const res = await apiRequest("POST", "/api/orders", {
@@ -332,7 +380,7 @@ export default function AuditPage() {
332380
<Download className="mr-2 h-4 w-4" />
333381
JSON
334382
</Button>
335-
<Button variant="outline" size="sm" onClick={() => window.open(`/api/audits/${currentAuditId}/pdf`, '_blank')}>
383+
<Button variant="outline" size="sm" onClick={handleDownloadPdf}>
336384
<FileText className="mr-2 h-4 w-4" />
337385
PDF
338386
</Button>
@@ -457,6 +505,80 @@ export default function AuditPage() {
457505
</CardContent>
458506
</Card>
459507
)}
508+
509+
{/* Post-Download Feedback Modal */}
510+
<Dialog open={showFeedbackModal} onOpenChange={setShowFeedbackModal}>
511+
<DialogContent className="sm:max-w-[425px]">
512+
<DialogHeader>
513+
<DialogTitle>Quick Feedback</DialogTitle>
514+
<DialogDescription>
515+
We're constantly improving our ASVS 5.0 audit engine. How did we do on this report?
516+
</DialogDescription>
517+
</DialogHeader>
518+
<div className="grid gap-6 py-4">
519+
<div className="space-y-3">
520+
<Label>How accurate were the security findings?</Label>
521+
<div className="flex justify-between items-center">
522+
<span className="text-xs text-muted-foreground">Poor</span>
523+
{[1, 2, 3, 4, 5].map((val) => (
524+
<Button
525+
key={val}
526+
variant={feedbackRating === val ? "default" : "outline"}
527+
className="w-10 h-10 rounded-full p-0"
528+
onClick={() => setFeedbackRating(val)}
529+
>
530+
{val}
531+
</Button>
532+
))}
533+
<span className="text-xs text-muted-foreground">Excellent</span>
534+
</div>
535+
</div>
536+
537+
<div className="space-y-3">
538+
<Label>Would you pay for this level of automated audit?</Label>
539+
<div className="flex justify-between items-center">
540+
<span className="text-xs text-muted-foreground">No</span>
541+
{[1, 2, 3, 4, 5].map((val) => (
542+
<Button
543+
key={`val-${val}`}
544+
variant={valueRating === val ? "default" : "outline"}
545+
className="w-10 h-10 rounded-full p-0"
546+
onClick={() => setValueRating(val)}
547+
>
548+
{val}
549+
</Button>
550+
))}
551+
<span className="text-xs text-muted-foreground">Definitely</span>
552+
</div>
553+
</div>
554+
555+
<div className="space-y-2">
556+
<Label htmlFor="feedback">Any additional comments? (Optional)</Label>
557+
<Textarea
558+
id="feedback"
559+
placeholder="What was missing? What was helpful?"
560+
value={feedbackText}
561+
onChange={(e) => setFeedbackText(e.target.value)}
562+
/>
563+
</div>
564+
</div>
565+
<DialogFooter>
566+
<Button variant="ghost" onClick={() => setShowFeedbackModal(false)}>Skip</Button>
567+
<Button
568+
onClick={() => {
569+
submitFeedbackMutation.mutate({
570+
responses: { accuracy: feedbackRating, willingnessToPay: valueRating },
571+
freeText: feedbackText
572+
});
573+
}}
574+
disabled={!feedbackRating || submitFeedbackMutation.isPending}
575+
>
576+
{submitFeedbackMutation.isPending && <Loader2 className="mr-2 h-4 w-4 animate-spin" />}
577+
Submit Feedback
578+
</Button>
579+
</DialogFooter>
580+
</DialogContent>
581+
</Dialog>
460582
</div>
461583
);
462584
}

0 commit comments

Comments
 (0)