Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file added Frontend/setOpen(false)}
Empty file.
Empty file added Frontend/setOpen(true)}
Empty file.
192 changes: 192 additions & 0 deletions Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx
Original file line number Diff line number Diff line change
@@ -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<string>("overview");
const dialogRef = useRef<HTMLDivElement | null>(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
<div
aria-modal="true"
role="dialog"
aria-label="Sponsorship details"
className="fixed inset-0 z-50 flex items-center justify-center"
>
{/* backdrop */}
<div
onClick={onClose}
className="absolute inset-0 bg-black/50"
data-testid="backdrop"
/>

{/* modal */}
<div
ref={dialogRef}
tabIndex={-1}
className="relative z-10 w-full max-w-md mx-4 bg-white rounded-2xl shadow-2xl overflow-hidden"
role="document"
>
{/* header */}
<div className="bg-gradient-to-r from-indigo-500 to-pink-400 p-4 text-white flex items-center gap-4">
<div className="w-12 h-12 rounded-full bg-white/20 flex items-center justify-center text-sm font-semibold">
{t.brand?.slice(0, 1) || "B"}
</div>
<div className="flex-1">
<div className="font-bold text-lg">{t.title}</div>
<div className="text-sm opacity-90">{t.brand}</div>
</div>
<div className="text-right text-sm">
<div className="text-xs">Match Score</div>
<div className="h-2 w-20 bg-white/30 rounded-full mt-1" />
</div>
</div>

{/* tabs */}
<div className="p-4">
<div className="flex gap-2 mb-4 flex-wrap">
{tabs.map((tt) => {
const isActive = tab === tt.id;
return (
<button
key={tt.id}
role="tab"
aria-selected={isActive}
aria-controls={`tabpanel-${tt.id}`}
id={`tab-${tt.id}`}
tabIndex={isActive ? 0 : -1}
onClick={() => setTab(tt.id)}
onKeyDown={(e) => {
if (e.key === "Enter" || e.key === " ") {
e.preventDefault();
setTab(tt.id);
}
}}
className={`px-4 py-2 rounded-full text-sm font-medium transition-shadow focus:outline-none focus:ring-2 focus:ring-indigo-400 ${
isActive
? "bg-indigo-600 text-white shadow-lg"
: "bg-gray-200 text-gray-800"
}`}
>
{tt.label}
</button>
);
})}
</div>
Comment on lines +90 to +119
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Implement complete ARIA tab pattern for accessibility.

The tab implementation is missing key accessibility requirements per the ARIA Authoring Practices Guide:

  1. Line 91: The container lacks role="tablist" to identify it as a tab list.
  2. Lines 103-108: Arrow key navigation (Left/Right/Home/End) is not implemented; tabs should be navigable via arrow keys, not just clickable.
  3. Line 101: The tabIndex={isActive ? 0 : -1} pattern prevents keyboard users from tabbing to inactive tabs. The correct pattern is to manage focus with arrow keys while keeping all tabs in the tab sequence.

Apply this diff to add the tablist role:

-        <div className="flex gap-2 mb-4 flex-wrap">
+        <div role="tablist" className="flex gap-2 mb-4 flex-wrap">

For arrow key navigation, extend the onKeyDown handler to manage focus between tabs:

onKeyDown={(e) => {
  if (e.key === "Enter" || e.key === " ") {
    e.preventDefault();
    setTab(tt.id);
  } else if (e.key === "ArrowRight" || e.key === "ArrowLeft") {
    e.preventDefault();
    const currentIndex = tabs.findIndex((t) => t.id === tab);
    const nextIndex =
      e.key === "ArrowRight"
        ? (currentIndex + 1) % tabs.length
        : (currentIndex - 1 + tabs.length) % tabs.length;
    setTab(tabs[nextIndex].id);
    // Focus the next tab button
    document.getElementById(`tab-${tabs[nextIndex].id}`)?.focus();
  }
}}
πŸ€– Prompt for AI Agents
In Frontend/src/components/SponsorshipModal/SponsorshipModal.tsx around lines
90-119, the tab implementation is missing role="tablist", full keyboard
navigation, and is using a tabIndex pattern that removes inactive tabs from the
tab sequence; add role="tablist" to the wrapping div, change tabIndex so tabs
remain focusable (all tabs should be in the tab sequence), and extend the button
onKeyDown handler to handle ArrowRight, ArrowLeft, Home and End keys: compute
the next/previous/first/last tab index from the tabs array, call setTab(...)
with the new id and programmatically focus the corresponding tab button by its
id, and keep existing Enter/Space behavior; ensure aria-selected/aria-controls
ids remain correct.


{/* content box */}
<div
id={`tabpanel-${tab}`}
role="tabpanel"
aria-labelledby={`tab-${tab}`}
className="bg-gray-50 rounded-xl p-4 shadow-sm"
>
{tab === "overview" && (
<div>
<h3 className="font-semibold text-lg mb-2">Overview</h3>
<div className="text-sm text-gray-700">{t.overview}</div>
</div>
)}

{tab === "requirements" && (
<div>
<h3 className="font-semibold text-lg mb-2">Requirements</h3>
<ul className="list-disc pl-5 text-sm text-gray-700">
<li>Minimum reach: 50k</li>
<li>Deliverable: 1 short + 1 reel</li>
<li>Timeline: 7 days</li>
</ul>
</div>
)}

{tab === "brand" && (
<div>
<h3 className="font-semibold text-lg mb-2">Brand Info</h3>
<div className="text-sm text-gray-700">
{t.brand} is a sample brand used for local testing.
</div>
</div>
)}

{tab === "analytics" && (
<div>
<h3 className="font-semibold text-lg mb-2">Analytics</h3>
<div className="text-sm text-gray-700">
No analytics available in local test mode.
</div>
</div>
)}
</div>

{/* footer buttons */}
<div className="flex items-center justify-end gap-3 mt-4">
<button
onClick={() => {
// example contact behaviour: open mailto (safe)
const mailto = `mailto:brand@example.com?subject=Interested in ${encodeURIComponent(
t.title || "campaign"
)}`;
const win = window.open(mailto, "_blank");
if (!win) window.location.href = mailto;
}}
className="px-4 py-2 bg-indigo-600 text-white rounded-lg shadow"
>
Contact
</button>

<button
onClick={onClose}
className="px-4 py-2 bg-white border rounded-lg"
>
Close
</button>
</div>
</div>
</div>
</div>
);
}
57 changes: 44 additions & 13 deletions Frontend/src/main.tsx
Original file line number Diff line number Diff line change
@@ -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(
// <StrictMode>
<Provider store={store}>
<App />
</Provider>
// </StrictMode>,
);
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 },
};
Comment on lines +11 to +20
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Fix type mismatch between campaign object and Props.

The sample campaign object uses field names (brandName, id, brandAvatar, etc.) that don't match the campaign prop type defined in SponsorshipModal.tsx (which expects title?, brand?, overview?). This means the modal will ignore the passed data and fall back to its internal mock values.

Apply this diff to match the expected type:

   const sample = {
-    id: "c1",
     title: "Festive Promo",
-    brandName: "Sparkle Co",
-    brandAvatar: "",
-    brandEmail: "brand@example.com",
+    brand: "Sparkle Co",
     overview: "This is a sample campaign overview to test the modal.",
-    requirements: ["1 Reel", "2 Posts"],
-    analytics: { engagementRate: 3.4, avgViews: 12000, matchScore: 80 },
   };

Note: If you need the full campaign structure with all fields, update the Props type in SponsorshipModal.tsx to include these additional fields and use them in the rendering logic.

πŸ“ Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
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 },
};
const sample = {
title: "Festive Promo",
brand: "Sparkle Co",
overview: "This is a sample campaign overview to test the modal.",
};
πŸ€– Prompt for AI Agents
In Frontend/src/main.tsx around lines 11 to 20, the sample campaign object uses
field names (brandName, id, brandAvatar, brandEmail, requirements, analytics)
that don't match the campaign Props expected by SponsorshipModal.tsx (which
expects title?, brand?, overview?, etc.); update the sample object to use the
exact prop names expected by SponsorshipModal (e.g., title, brand: { name,
avatar, email } or whatever shape the component's Props defines) so the modal
receives real data, or alternatively open SponsorshipModal.tsx and extend/adjust
its Props type to include the existing fields (id, brandName, brandAvatar,
brandEmail, requirements, analytics) and update the component rendering to use
those fields consistently.


return (
<div style={{ padding: 24, fontFamily: "Inter, Arial, sans-serif" }}>
<h1>Local Test: Sponsorship Modal</h1>
<button
onClick={() => setOpen(true)}
style={{
padding: "10px 14px",
background: "#4f46e5",
color: "white",
borderRadius: 8,
border: "none",
cursor: "pointer",
}}
>
Open Modal
</button>

<SponsorshipModal open={open} onClose={() => setOpen(false)} campaign={sample} />
</div>
);
}

const rootEl = document.getElementById("root") || document.body.appendChild(document.createElement("div"));
createRoot(rootEl as HTMLElement).render(<TestApp />);
Comment on lines +1 to +45
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | πŸ”΄ Critical

Critical: Test harness must not replace production entry point.

This file completely replaces the production application entry point with a test harness. Merging this to the main branch will break the entire application, as it removes the Redux store, App component, and all production functionality.

Recommended approach:

  1. Keep the original main.tsx unchanged.
  2. Create a separate test file (e.g., main.test.tsx or test-harness.tsx) for local development testing.
  3. Update your local development script to use the test harness temporarily if needed, but do not commit changes to main.tsx.

Alternatively, if you want to test the modal within the existing app:

  • Import and use SponsorshipModal within the actual application flow (e.g., triggered by a button in an existing page).
  • Add proper state management integration with Redux if applicable.