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..62c5307 --- /dev/null +++ b/Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx @@ -0,0 +1,182 @@ +import React, { useEffect, useRef, useState } from "react"; + +type Props = { + open: boolean; + onClose: () => void; + campaign?: { + title?: string; + brand?: string; + overview?: string; + }; +}; + +type Tab = { + id: string; + label: string; +}; + +export default function SponsorshipModal({ + open, + onClose, + campaign, +}: Props) { + const [tab, setTab] = useState("overview"); + + const dialogRef = useRef(null); + const triggerRef = useRef(null); + + const tabs: Tab[] = [ + { id: "overview", label: "Overview" }, + { id: "requirements", label: "Requirements" }, + { id: "brand", label: "Brand Info" }, + { 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; + + 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 + ) => { + 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: "Sample overview text", + }; + + return ( +
+ {/* Overlay */} +
+ + {/* Modal */} +
+ {/* Header */} +
+

{t.title}

+

{t.brand}

+
+ + {/* Tabs */} +
+
+ {tabs.map((tt, index) => ( + + ))} +
+ + {/* Tab Panel */} +
+ {tab === "overview" &&

{t.overview}

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

Requirements content

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

Brand info content

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

Analytics content

} +
+ + {/* Footer */} +
+ +
+
+
+
+ ); +} \ No newline at end of file diff --git a/Frontend/src/main.tsx b/Frontend/src/main.tsx index 18b97e0..bcad9f7 100644 --- a/Frontend/src/main.tsx +++ b/Frontend/src/main.tsx @@ -1,14 +1,18 @@ -import { StrictMode } from "react"; +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"; +import App from "./App"; +import store from "./redux/store"; +import "./index.css"; + +const root = document.getElementById("root"); + +if (!root) { + throw new Error("Root container missing in index.html"); +} -createRoot(document.getElementById("root")!).render( - // +createRoot(root).render( - // , -); +); \ No newline at end of file