Skip to content

Commit 2a7166a

Browse files
Merge pull request #94 from Itzzavdheshh/ui
feat: Webhook Management System — Complete Frontend Dashboard for CommDesk
2 parents 00ef567 + b2a86ce commit 2a7166a

34 files changed

Lines changed: 3922 additions & 143 deletions

package-lock.json

Lines changed: 1173 additions & 66 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,9 @@
4545
"@tailwindcss/typography": "^0.5.19",
4646
"@tailwindcss/vite": "^4.3.0",
4747
"@tauri-apps/cli": "2.10.1",
48+
"@testing-library/jest-dom": "^6.9.1",
49+
"@testing-library/react": "^16.3.2",
50+
"@testing-library/user-event": "^14.6.1",
4851
"@types/markdown-to-jsx": "^7.0.1",
4952
"@types/react": "^19.2.14",
5053
"@types/react-dom": "^19.2.3",
@@ -54,13 +57,15 @@
5457
"eslint-plugin-react-hooks": "^7.1.1",
5558
"eslint-plugin-react-refresh": "^0.5.2",
5659
"globals": "^17.6.0",
60+
"jsdom": "^29.1.1",
5761
"prettier": "^3.8.3",
5862
"shadcn": "^3.8.5",
5963
"tailwindcss": "^4.3.0",
6064
"tw-animate-css": "^1.4.0",
6165
"typescript": "~5.8.3",
6266
"typescript-eslint": "^8.59.2",
63-
"vite": "^7.3.3"
67+
"vite": "^7.3.3",
68+
"vitest": "^4.1.6"
6469
},
6570
"overrides": {
6671
"ip-address": "^10.1.1",

src-tauri/Cargo.lock

Lines changed: 10 additions & 10 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,9 @@ crate-type = ["staticlib", "cdylib", "rlib"]
1919
tauri-build = { version = "2.6.1", features = [] }
2020

2121
[dependencies]
22-
tauri = { version = "2.10", features = [] }
23-
tauri-plugin-opener = "2.5"
24-
tauri-plugin-process = "2.3"
25-
tauri-plugin-updater = "2.10"
22+
tauri = { version = "2.0", features = [] }
23+
tauri-plugin-opener = "2.0"
24+
tauri-plugin-process = "2.0"
25+
tauri-plugin-updater = "2.0"
2626
serde = { version = "1", features = ["derive"] }
2727
serde_json = "1"

src/features/Member/v1/mock/dashboardData.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { DashboardData } from "../../../Dashboard/Member/v1/Type/dashboard";
33
export const dashboardData: DashboardData = {
44
user: {
55
name: "Arjun Mehta",
6-
role: "Member",
6+
role: "Admin",
77
},
88

99
summary: {

src/features/SideBar/v1/Section/SideBar.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { RiContactsBookFill } from "react-icons/ri";
2-
import { MdAssignment, MdDashboard, MdEvent, MdGroup, MdSettings, MdWork } from "react-icons/md";
2+
import { MdAssignment, MdDashboard, MdEvent, MdGroup, MdSettings, MdWork, MdWebhook } from "react-icons/md";
33
import { useTheme } from "@/theme";
44
import { ThemeToggle } from "@/Component/ui/ThemeToggle";
55

@@ -49,6 +49,7 @@ const SideBar = () => {
4949
<SideBarLink icon={<MdGroup />} text="Teams" link="/org/member" />
5050
<SideBarLink icon={<MdEvent />} text="Events" link="/org/events" />
5151
<SideBarLink icon={<MdAssignment />} text="Tasks" link="/org/tasks" />
52+
<SideBarLink icon={<MdWebhook />} text="Webhooks" link="/org/dashboard/webhooks" />
5253
<SideBarLink icon={<RiContactsBookFill />} text="Contact Submissions" link="/org/contact" />
5354

5455
{/* Footer */}
Lines changed: 52 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
import { useEffect, useRef } from "react";
2-
import { AlertTriangle, X, Loader2 } from "lucide-react";
2+
import { AlertTriangle, X, Loader2, CheckCircle2, Info, AlertCircle, LucideIcon } from "lucide-react";
3+
4+
type ModalVariant = "danger" | "success" | "warning" | "info";
35

46
interface ConfirmModalProps {
57
isOpen: boolean;
@@ -10,9 +12,37 @@ interface ConfirmModalProps {
1012
onConfirm: () => void;
1113
onCancel: () => void;
1214
isLoading?: boolean;
13-
danger?: boolean;
15+
variant?: ModalVariant;
16+
icon?: LucideIcon;
1417
}
1518

19+
const VARIANT_CONFIG = {
20+
danger: {
21+
bg: "bg-[var(--cd-danger-subtle)]",
22+
text: "text-[var(--cd-danger)]",
23+
btn: "bg-[var(--cd-danger)] hover:bg-[var(--cd-danger-text)]",
24+
icon: AlertTriangle,
25+
},
26+
success: {
27+
bg: "bg-[var(--cd-success-subtle)]",
28+
text: "text-[var(--cd-success)]",
29+
btn: "bg-[var(--cd-success)] hover:bg-[var(--cd-success-text)]",
30+
icon: CheckCircle2,
31+
},
32+
warning: {
33+
bg: "bg-[var(--cd-warning-subtle)]",
34+
text: "text-[var(--cd-warning)]",
35+
btn: "bg-[var(--cd-warning)] hover:bg-[var(--cd-warning-text)]",
36+
icon: AlertCircle,
37+
},
38+
info: {
39+
bg: "bg-[var(--cd-primary-subtle)]",
40+
text: "text-[var(--cd-primary)]",
41+
btn: "bg-[var(--cd-primary)] hover:bg-[var(--cd-primary-text)]",
42+
icon: Info,
43+
},
44+
};
45+
1646
export default function ConfirmModal({
1747
isOpen,
1848
title,
@@ -22,11 +52,13 @@ export default function ConfirmModal({
2252
onConfirm,
2353
onCancel,
2454
isLoading = false,
25-
danger = false,
55+
variant = "info",
56+
icon: CustomIcon,
2657
}: ConfirmModalProps) {
2758
const cancelRef = useRef<HTMLButtonElement>(null);
59+
const config = VARIANT_CONFIG[variant];
60+
const Icon = CustomIcon || config.icon;
2861

29-
// Focus cancel on open & close on Escape
3062
useEffect(() => {
3163
if (!isOpen) return;
3264
cancelRef.current?.focus();
@@ -40,77 +72,58 @@ export default function ConfirmModal({
4072
if (!isOpen) return null;
4173

4274
return (
43-
<div
44-
className="fixed inset-0 z-50 flex items-center justify-center"
45-
aria-modal="true"
46-
role="dialog"
47-
>
75+
<div className="fixed inset-0 z-50 flex items-center justify-center p-4">
4876
{/* Backdrop */}
4977
<div
50-
className="absolute inset-0 bg-black/30 backdrop-blur-[2px]"
78+
className="absolute inset-0 bg-black/40 backdrop-blur-[2px]"
5179
onClick={() => !isLoading && onCancel()}
5280
/>
5381

5482
{/* Dialog */}
5583
<div
56-
className="relative z-10 w-full max-w-md mx-4 rounded-2xl shadow-2xl border overflow-hidden animate-in fade-in zoom-in-95 duration-150"
84+
className="relative z-10 w-full max-w-md rounded-2xl shadow-2xl border overflow-hidden animate-in fade-in zoom-in-95 duration-150"
5785
style={{ backgroundColor: "var(--cd-surface)", borderColor: "var(--cd-border)" }}
5886
>
59-
{/* Close */}
6087
<button
6188
onClick={onCancel}
6289
disabled={isLoading}
6390
className="absolute top-4 right-4 p-1.5 rounded-lg text-[var(--cd-text-muted)] hover:bg-[var(--cd-hover)] hover:text-[var(--cd-text)] transition disabled:opacity-40"
64-
aria-label="Close"
6591
>
6692
<X size={16} />
6793
</button>
6894

69-
<div className="p-6">
70-
{/* Icon + Title */}
71-
<div className="flex items-start gap-4">
72-
<div
73-
className={`shrink-0 w-11 h-11 rounded-xl flex items-center justify-center ${
74-
danger ? "bg-[var(--cd-danger-subtle)]" : "bg-[var(--cd-primary-subtle)]"
75-
}`}
76-
>
77-
<AlertTriangle
78-
size={22}
79-
className={danger ? "text-[var(--cd-danger)]" : "text-[var(--cd-primary)]"}
80-
/>
95+
<div className="p-6 pt-8">
96+
<div className="flex flex-col items-center text-center gap-4">
97+
<div className={`shrink-0 w-14 h-14 rounded-2xl flex items-center justify-center ${config.bg}`}>
98+
<Icon size={28} className={config.text} />
8199
</div>
82-
<div className="flex flex-col gap-2 min-w-0">
83-
<h3 className="text-lg font-bold text-[var(--cd-text)] leading-tight">{title}</h3>
84-
<p className="text-sm text-[var(--cd-text-2)] leading-relaxed">{message}</p>
100+
101+
<div className="flex flex-col gap-2">
102+
<h3 className="text-xl font-bold text-[var(--cd-text)] leading-tight">{title}</h3>
103+
<p className="text-[var(--cd-text-2)] leading-relaxed">{message}</p>
85104
</div>
86105
</div>
87106

88-
{/* Actions */}
89-
<div className="flex items-center justify-end gap-3 mt-8">
107+
<div className="flex items-center gap-3 mt-8">
90108
<button
91109
ref={cancelRef}
92110
onClick={onCancel}
93111
disabled={isLoading}
94-
className="px-5 py-2.5 rounded-xl border border-[var(--cd-border)] text-sm font-bold text-[var(--cd-text-2)] hover:bg-[var(--cd-hover)] hover:text-[var(--cd-text)] transition-all active:scale-95 disabled:opacity-40"
112+
className="flex-1 px-5 py-3 rounded-xl border border-[var(--cd-border)] text-sm font-bold text-[var(--cd-text-2)] hover:bg-[var(--cd-hover)] hover:text-[var(--cd-text)] transition-all active:scale-95 disabled:opacity-40"
95113
>
96114
{cancelLabel}
97115
</button>
98116
<button
99117
onClick={onConfirm}
100118
disabled={isLoading}
101-
className={`flex items-center gap-2 px-5 py-2.5 rounded-xl text-sm font-bold text-white transition-all active:scale-95 shadow-sm disabled:opacity-60 ${
102-
danger
103-
? "bg-[var(--cd-danger)] hover:opacity-90 shadow-[var(--cd-danger-subtle)]"
104-
: "bg-[var(--cd-primary)] hover:opacity-90 shadow-[var(--cd-primary-subtle)]"
105-
}`}
119+
className={`flex-1 flex items-center justify-center gap-2 px-5 py-3 rounded-xl text-sm font-bold text-white transition-all active:scale-95 shadow-sm disabled:opacity-60 ${config.btn}`}
106120
>
107-
{isLoading && <Loader2 size={15} className="animate-spin" />}
121+
{isLoading && <Loader2 size={16} className="animate-spin" />}
108122
{confirmLabel}
109123
</button>
110124
</div>
111125
</div>
112126
</div>
113-
114127
</div>
115128
);
116-
}
129+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
export type WebhookEvent =
2+
| "member.created"
3+
| "member.activated"
4+
| "event.created"
5+
| "hackathon.created"
6+
| "github.push"
7+
| "github.pr.opened";
8+
9+
export type WebhookStatus = "active" | "inactive";
10+
11+
export interface Webhook {
12+
id: string;
13+
name: string;
14+
url: string;
15+
events: WebhookEvent[];
16+
status: WebhookStatus;
17+
secret: string; // Typically masked like ********abcd
18+
permissions?: string[];
19+
lastDeliveryStatus?: "success" | "failed" | "pending";
20+
lastTestedAt?: string;
21+
lastTestStatus?: "success" | "failed";
22+
createdAt: string;
23+
updatedAt: string;
24+
}
25+
26+
export interface WebhookLog {
27+
id: string;
28+
webhookId: string;
29+
event: WebhookEvent;
30+
status: "success" | "failed";
31+
timestamp: string;
32+
responseCode: number;
33+
requestPayload: unknown;
34+
responsePayload: unknown;
35+
}
36+
37+
export interface CreateWebhookPayload {
38+
name: string;
39+
url: string;
40+
events: WebhookEvent[];
41+
secret?: string;
42+
permissions?: string[];
43+
}
44+
45+
export interface UpdateWebhookPayload {
46+
name?: string;
47+
url?: string;
48+
events?: WebhookEvent[];
49+
status?: WebhookStatus;
50+
secret?: string;
51+
}
52+
53+
export interface WebhookFilters {
54+
status: WebhookStatus | "all";
55+
search: string;
56+
page: number;
57+
}
58+
59+
export interface PaginatedWebhooks {
60+
data: Webhook[];
61+
total: number;
62+
totalPages: number;
63+
}
64+
65+
export interface WebhookLogFilters {
66+
status: "all" | "success" | "failed";
67+
event: WebhookEvent | "all";
68+
page: number;
69+
}
70+
71+
export interface PaginatedWebhookLogs {
72+
data: WebhookLog[];
73+
total: number;
74+
totalPages: number;
75+
}

0 commit comments

Comments
 (0)