From 30339432ebd2ba74dfd7394cd0cf0409383f49ae Mon Sep 17 00:00:00 2001 From: Shivampal157 Date: Mon, 17 Nov 2025 04:02:45 +0530 Subject: [PATCH 1/4] updated SponsorshipModel UI --- Frontend/setOpen(false)} | 0 Frontend/setOpen(true)} | 0 .../SponsorshipModal/SponsorshipModal.tsx | 192 ++++++++++++++++++ Frontend/src/main.tsx | 57 ++++-- 4 files changed, 236 insertions(+), 13 deletions(-) create mode 100644 Frontend/setOpen(false)} create mode 100644 Frontend/setOpen(true)} create mode 100644 Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx diff --git a/Frontend/setOpen(false)} b/Frontend/setOpen(false)} new file mode 100644 index 0000000..e69de29 diff --git a/Frontend/setOpen(true)} b/Frontend/setOpen(true)} new file mode 100644 index 0000000..e69de29 diff --git a/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx b/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx new file mode 100644 index 0000000..a5be6a0 --- /dev/null +++ b/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx @@ -0,0 +1,192 @@ +import React, { useEffect, useRef, useState } from "react"; + +type Props = { + open: boolean; + onClose: () => void; + // optional campaign data; you can extend later + campaign?: { + title?: string; + brand?: string; + overview?: string; + }; +}; + +export default function SponsorshipModal({ open, onClose, campaign }: Props) { + const [tab, setTab] = useState("overview"); + const dialogRef = useRef(null); + + // keep focus inside modal when open + useEffect(() => { + if (!open) return; + const prevActive = document.activeElement as HTMLElement | null; + // focus the modal container + dialogRef.current?.focus(); + + const onKey = (e: KeyboardEvent) => { + if (e.key === "Escape") { + onClose(); + } + }; + window.addEventListener("keydown", onKey); + return () => { + window.removeEventListener("keydown", onKey); + prevActive?.focus(); + }; + }, [open, onClose]); + + if (!open) return null; + + const t = campaign ?? { + title: "Festive Promo", + brand: "Sparkle Co", + overview: "This is a sample campaign overview to test the modal.", + }; + + const tabs = [ + { id: "overview", label: "Overview" }, + { id: "requirements", label: "Requirements" }, + { id: "brand", label: "Brand Info" }, + { id: "analytics", label: "Analytics" }, + ]; + + return ( + // overlay +
+ {/* backdrop */} +
+ + {/* modal */} +
+ {/* header */} +
+
+ {t.brand?.slice(0, 1) || "B"} +
+
+
{t.title}
+
{t.brand}
+
+
+
Match Score
+
+
+
+ + {/* tabs */} +
+
+ {tabs.map((tt) => { + const isActive = tab === tt.id; + return ( + + ); + })} +
+ + {/* content box */} +
+ {tab === "overview" && ( +
+

Overview

+
{t.overview}
+
+ )} + + {tab === "requirements" && ( +
+

Requirements

+
    +
  • Minimum reach: 50k
  • +
  • Deliverable: 1 short + 1 reel
  • +
  • Timeline: 7 days
  • +
+
+ )} + + {tab === "brand" && ( +
+

Brand Info

+
+ {t.brand} is a sample brand used for local testing. +
+
+ )} + + {tab === "analytics" && ( +
+

Analytics

+
+ No analytics available in local test mode. +
+
+ )} +
+ + {/* footer buttons */} +
+ + + +
+
+
+
+ ); +} diff --git a/Frontend/src/main.tsx b/Frontend/src/main.tsx index 18b97e0..42dce18 100644 --- a/Frontend/src/main.tsx +++ b/Frontend/src/main.tsx @@ -1,14 +1,45 @@ -import { StrictMode } from "react"; +// src/main.tsx — temporary test entry +import React from "react"; import { createRoot } from "react-dom/client"; -import "./index.css"; -import { Provider } from "react-redux"; -import App from "./App.tsx"; -import store from "./redux/store.ts"; - -createRoot(document.getElementById("root")!).render( - // - - - - // , -); +import "./index.css"; // agar file nahi hai to hata sakte ho (nahin to leave it) + +import SponsorshipModal from "./components/SponsorshipModal/SponsorshipModal"; + +function TestApp() { + const [open, setOpen] = React.useState(false); + + const sample = { + id: "c1", + title: "Festive Promo", + brandName: "Sparkle Co", + brandAvatar: "", + brandEmail: "brand@example.com", + overview: "This is a sample campaign overview to test the modal.", + requirements: ["1 Reel", "2 Posts"], + analytics: { engagementRate: 3.4, avgViews: 12000, matchScore: 80 }, + }; + + return ( +
+

Local Test: Sponsorship Modal

+ + + setOpen(false)} campaign={sample} /> +
+ ); +} + +const rootEl = document.getElementById("root") || document.body.appendChild(document.createElement("div")); +createRoot(rootEl as HTMLElement).render(); From b1ae1fc67598fdf02cb16ddaa09fc88473b882e4 Mon Sep 17 00:00:00 2001 From: Shivampal157 Date: Sun, 14 Dec 2025 14:52:53 +0530 Subject: [PATCH 2/4] Restore correct app entry point and fix main.tsx --- Frontend/src/main.tsx | 51 ++++++++++--------------------------------- 1 file changed, 12 insertions(+), 39 deletions(-) diff --git a/Frontend/src/main.tsx b/Frontend/src/main.tsx index 42dce18..bcad9f7 100644 --- a/Frontend/src/main.tsx +++ b/Frontend/src/main.tsx @@ -1,45 +1,18 @@ -// src/main.tsx — temporary test entry import React from "react"; import { createRoot } from "react-dom/client"; -import "./index.css"; // agar file nahi hai to hata sakte ho (nahin to leave it) +import { Provider } from "react-redux"; +import App from "./App"; +import store from "./redux/store"; +import "./index.css"; -import SponsorshipModal from "./components/SponsorshipModal/SponsorshipModal"; +const root = document.getElementById("root"); -function TestApp() { - const [open, setOpen] = React.useState(false); - - const sample = { - id: "c1", - title: "Festive Promo", - brandName: "Sparkle Co", - brandAvatar: "", - brandEmail: "brand@example.com", - overview: "This is a sample campaign overview to test the modal.", - requirements: ["1 Reel", "2 Posts"], - analytics: { engagementRate: 3.4, avgViews: 12000, matchScore: 80 }, - }; - - return ( -
-

Local Test: Sponsorship Modal

- - - setOpen(false)} campaign={sample} /> -
- ); +if (!root) { + throw new Error("Root container missing in index.html"); } -const rootEl = document.getElementById("root") || document.body.appendChild(document.createElement("div")); -createRoot(rootEl as HTMLElement).render(); +createRoot(root).render( + + + +); \ No newline at end of file From 907e34764ae89d3220b93011cbfbee18a5258e7b Mon Sep 17 00:00:00 2001 From: Shivampal157 Date: Sun, 14 Dec 2025 16:20:53 +0530 Subject: [PATCH 3/4] Fix JSX className template literal in SponsorshipModal tabs --- .../SponsorshipModal/SponsorshipModal.tsx | 220 ++++++++---------- 1 file changed, 91 insertions(+), 129 deletions(-) diff --git a/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx b/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx index a5be6a0..f5e134c 100644 --- a/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx +++ b/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx @@ -3,7 +3,6 @@ import React, { useEffect, useRef, useState } from "react"; type Props = { open: boolean; onClose: () => void; - // optional campaign data; you can extend later campaign?: { title?: string; brand?: string; @@ -11,176 +10,139 @@ type Props = { }; }; -export default function SponsorshipModal({ open, onClose, campaign }: Props) { +type Tab = { + id: string; + label: string; +}; + +export default function SponsorshipModal({ + open, + onClose, + campaign, +}: Props) { const [tab, setTab] = useState("overview"); - const dialogRef = useRef(null); + const dialogRef = useRef(null); + + const tabs: Tab[] = [ + { id: "overview", label: "Overview" }, + { id: "requirements", label: "Requirements" }, + { id: "brand", label: "Brand Info" }, + { id: "analytics", label: "Analytics" }, + ]; - // keep focus inside modal when open useEffect(() => { if (!open) return; - const prevActive = document.activeElement as HTMLElement | null; - // focus the modal container dialogRef.current?.focus(); - - const onKey = (e: KeyboardEvent) => { - if (e.key === "Escape") { - onClose(); - } - }; - window.addEventListener("keydown", onKey); - return () => { - window.removeEventListener("keydown", onKey); - prevActive?.focus(); - }; - }, [open, onClose]); + }, [open]); + + const handleTabKeyDown = ( + e: React.KeyboardEvent, + index: number + ) => { + let nextIndex = index; + + switch (e.key) { + case "ArrowRight": + nextIndex = (index + 1) % tabs.length; + break; + case "ArrowLeft": + nextIndex = (index - 1 + tabs.length) % tabs.length; + break; + case "Home": + nextIndex = 0; + break; + case "End": + nextIndex = tabs.length - 1; + break; + default: + return; + } + + e.preventDefault(); + setTab(tabs[nextIndex].id); + document.getElementById(`tab-${tabs[nextIndex].id}`)?.focus(); + }; if (!open) return null; const t = campaign ?? { title: "Festive Promo", brand: "Sparkle Co", - overview: "This is a sample campaign overview to test the modal.", + overview: "Sample overview text", }; - const tabs = [ - { id: "overview", label: "Overview" }, - { id: "requirements", label: "Requirements" }, - { id: "brand", label: "Brand Info" }, - { id: "analytics", label: "Analytics" }, - ]; - return ( - // overlay
- {/* backdrop */}
- {/* modal */}
- {/* header */} -
-
- {t.brand?.slice(0, 1) || "B"} -
-
-
{t.title}
-
{t.brand}
-
-
-
Match Score
-
-
+ {/* Header */} +
+

{t.title}

+

{t.brand}

- {/* tabs */} + {/* Tabs */}
-
- {tabs.map((tt) => { - const isActive = tab === tt.id; - return ( - - ); - })} + > + {tt.label} + + + ))}
- {/* content box */} + {/* Panel */}
- {tab === "overview" && ( -
-

Overview

-
{t.overview}
-
- )} - - {tab === "requirements" && ( -
-

Requirements

-
    -
  • Minimum reach: 50k
  • -
  • Deliverable: 1 short + 1 reel
  • -
  • Timeline: 7 days
  • -
-
- )} - - {tab === "brand" && ( -
-

Brand Info

-
- {t.brand} is a sample brand used for local testing. -
-
- )} - - {tab === "analytics" && ( -
-

Analytics

-
- No analytics available in local test mode. -
-
- )} + {tab === "overview" &&

{t.overview}

} + {tab === "requirements" &&

Requirements content

} + {tab === "brand" &&

Brand info content

} + {tab === "analytics" &&

Analytics content

}
- {/* footer buttons */} -
- - + {/* Footer */} +
@@ -189,4 +151,4 @@ export default function SponsorshipModal({ open, onClose, campaign }: Props) {
); -} +} \ No newline at end of file From 64e2450f7210fc66c73b6bc671298896eae6b345 Mon Sep 17 00:00:00 2001 From: Shivampal157 Date: Sun, 14 Dec 2025 16:48:08 +0530 Subject: [PATCH 4/4] Improve modal accessibility: focus management and Escape key handling --- .../SponsorshipModal/SponsorshipModal.tsx | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx b/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx index f5e134c..62c5307 100644 --- a/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx +++ b/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx @@ -21,7 +21,9 @@ export default function SponsorshipModal({ campaign, }: Props) { const [tab, setTab] = useState("overview"); + const dialogRef = useRef(null); + const triggerRef = useRef(null); const tabs: Tab[] = [ { id: "overview", label: "Overview" }, @@ -30,11 +32,36 @@ export default function SponsorshipModal({ { id: "analytics", label: "Analytics" }, ]; + // 🔹 Focus management (open / close) + useEffect(() => { + if (open) { + triggerRef.current = document.activeElement as HTMLElement; + + // Focus first tab button + document.getElementById(`tab-${tabs[0].id}`)?.focus(); + } else if (triggerRef.current) { + triggerRef.current.focus(); + triggerRef.current = null; + } + }, [open, tabs]); + + // 🔹 Escape key handling useEffect(() => { if (!open) return; - dialogRef.current?.focus(); - }, [open]); + const handleKeyDown = (e: KeyboardEvent) => { + if (e.key === "Escape") { + onClose(); + } + }; + + document.addEventListener("keydown", handleKeyDown); + return () => { + document.removeEventListener("keydown", handleKeyDown); + }; + }, [open, onClose]); + + // 🔹 Keyboard navigation for tabs const handleTabKeyDown = ( e: React.KeyboardEvent, index: number @@ -78,11 +105,13 @@ export default function SponsorshipModal({ aria-label="Sponsorship details" className="fixed inset-0 z-50 flex items-center justify-center" > + {/* Overlay */}
+ {/* Modal */}
{tt.label} - ))}
- {/* Panel */} + {/* Tab Panel */}