From c03b4b397278b6c2f3f8bbd7c8c895aa34ee41dd Mon Sep 17 00:00:00 2001 From: Henrichy <107367296+Henrichy@users.noreply.github.com> Date: Tue, 2 Sep 2025 22:57:00 +0100 Subject: [PATCH 1/2] Done --- .../components/ActionCard/ActionCard.tsx | 25 + src/app/dashboard/components/header.tsx | 2 +- .../components/resuables/ChartCard.tsx | 70 +++ .../components/resuables/ProjectCard.tsx | 211 +++---- .../components/resuables/ReportDiscussion.tsx | 33 ++ .../components/resuables/WriteAReport.tsx | 541 ++++++++---------- src/app/dashboard/layout.tsx | 15 +- .../researcher/projects/[projectId]/page.tsx | 241 ++++---- .../dashboard/researcher/projects/mockData.ts | 490 +++++----------- .../dashboard/researcher/projects/page.tsx | 136 +++-- .../researcher/reports/[id]/page.tsx | 2 +- src/components/foot.tsx | 9 + 12 files changed, 815 insertions(+), 960 deletions(-) create mode 100644 src/app/dashboard/components/ActionCard/ActionCard.tsx create mode 100644 src/app/dashboard/components/resuables/ChartCard.tsx create mode 100644 src/app/dashboard/components/resuables/ReportDiscussion.tsx create mode 100644 src/components/foot.tsx diff --git a/src/app/dashboard/components/ActionCard/ActionCard.tsx b/src/app/dashboard/components/ActionCard/ActionCard.tsx new file mode 100644 index 0000000..a4f5da3 --- /dev/null +++ b/src/app/dashboard/components/ActionCard/ActionCard.tsx @@ -0,0 +1,25 @@ +"use client"; + +import React from "react"; + +interface ActionCardProps { + label: string; + buttonText: string; + onClick?: () => void; +} + +const ActionCard: React.FC = ({ label, buttonText, onClick }) => { + return ( +
+ {label} + +
+ ); +}; + +export default ActionCard; diff --git a/src/app/dashboard/components/header.tsx b/src/app/dashboard/components/header.tsx index 086f6d2..45c91b0 100644 --- a/src/app/dashboard/components/header.tsx +++ b/src/app/dashboard/components/header.tsx @@ -26,7 +26,7 @@ const Header = () => { const titleMap: Record = { admin: "Admin Dashboard", "project-owner": "Project Owner Dashboard", - researcher: "Researcher Dashboard", + researcher: "Security Researcher Dashboard", validator: "Validator Dashboard", }; diff --git a/src/app/dashboard/components/resuables/ChartCard.tsx b/src/app/dashboard/components/resuables/ChartCard.tsx new file mode 100644 index 0000000..99460bd --- /dev/null +++ b/src/app/dashboard/components/resuables/ChartCard.tsx @@ -0,0 +1,70 @@ +import React from "react"; + +interface ChatCardProps { + role: "Validator" | "Researcher"; + name: string; + address: string; + message: string; + stats: { label: string; value: string | number }[]; + reputation: number; +} + +const ChatCard: React.FC = ({ + role, + name, + address, + message, + stats, + reputation, +}) => { + return ( +
+ {/* Role */} +

{role}

+ + {/* Header */} +
+
+
+

{name}

+

{address}

+
+
+ + {/* Message */} +
+ {message} +
+ + {/* Stats */} +
+ {stats.map((s, i) => ( + + {s.label} | {s.value} + + ))} + + Reputation | {reputation}% + +
+ + {/* Input Box */} +
+ + +
+ +

+ All conversations are monitored and recorded for security purposes. +

+
+ ); +}; + +export default ChatCard; diff --git a/src/app/dashboard/components/resuables/ProjectCard.tsx b/src/app/dashboard/components/resuables/ProjectCard.tsx index 08d2757..7428d23 100644 --- a/src/app/dashboard/components/resuables/ProjectCard.tsx +++ b/src/app/dashboard/components/resuables/ProjectCard.tsx @@ -9,33 +9,9 @@ type Props = { project: Project; }; -// pick top two langs and normalize them to 100% -function getTopTwoNormalized( - langs: Record< - string, - { percentage: number; bgColor: string; logo: string; icon: string } - > -) { - const entries = Object.values(langs) - .sort((a, b) => b.percentage - a.percentage) - .slice(0, 2); - - const total = entries.reduce((sum, l) => sum + l.percentage, 0) || 1; - return entries.map((l) => ({ - ...l, - normalized: (l.percentage / total) * 100, - })); -} - export const ProjectCard: React.FC = ({ project }) => { - const top = getTopTwoNormalized(project.language ?? {}); - - // 📝Fallback for project‐level logo: if none, use first lang’s icon/bg - const fallback = top[0] ?? { icon: "?", bgColor: "bg-gray-700" }; - const logoText = project.logo.text || fallback.icon; - const logoBg = project.logo.bgColor || fallback.bgColor; - const shortenText = (text: string, max: number) => { + if (!text) return ""; if (text.length <= max) return text; const cut = text.slice(0, max); const last = cut.lastIndexOf(" "); @@ -45,117 +21,104 @@ export const ProjectCard: React.FC = ({ project }) => { return ( + className="block col-span-12 sm:col-span-6 lg:col-span-4" + > - {/* Logo + Title */} - -
- {project.logo.logo ? ( + className="bg-[#110D0F] text-white p-6 rounded-2xl border border-gray-700 w-full max-w-md" + > +
+
+ + + {project.status} + +
+ +
+ {/* Logo + Title + Priority */} +
+
+
+ {project.logo.logo ? ( + {project.title} + ) : ( +
+ {project.logo.text} +
+ )} +
+

{project.title}

+
+ + {/* Priority badge */} + + {`Priority: ${project.priority}`} + + + +
+
+ + {/* Amount, Deadline, Status */} +
+ {project.amount && ( +
{project.title} - ) : ( -
- {logoText} + {project.amount} +
+ )} +
+ {/* Left side: Deadline */} +
+ Deadline: +
+ {project.deadline}
- )} +
+ + {/* Right side: Button */} + +
+ View Details +
+
-

{project.title}

- - {/* Description */} - - {shortenText(project.description, 150)} - - {/* Amount & Deadline */} - -
- Money bag - {project.amount} -
-
- Deadline - Deadline: {project.deadline} -
-
+
- {/* Tags */} - - {project.tags.map((tag) => ( - - {tag} - - ))} - - {/* Progress Bar (top 2 langs) */} - {top.length === 2 && ( - -
- {top.map((lang) => ( -
- {lang.logo ? ( - {`${lang.icon} - ) : ( - {lang.icon} - )} - {Math.round(lang.normalized)}% -
- ))} -
-
- )} ); diff --git a/src/app/dashboard/components/resuables/ReportDiscussion.tsx b/src/app/dashboard/components/resuables/ReportDiscussion.tsx new file mode 100644 index 0000000..dc18677 --- /dev/null +++ b/src/app/dashboard/components/resuables/ReportDiscussion.tsx @@ -0,0 +1,33 @@ +import ChatCard from "./ChartCard"; + +const ReportDiscussion = () => { + return ( +
+ + + alert('XSS') in the name field executes on public profiles. High impact—could steal sessions. PoC attached.\n\nSuggestions: Sanitize inputs, add CSP. Need more details?`} + stats={[ + { label: "Ranks", value: 12 }, + { label: "Audits Made", value: 2 }, + ]} + reputation={91} + /> +
+ ); +}; + +export default ReportDiscussion; diff --git a/src/app/dashboard/components/resuables/WriteAReport.tsx b/src/app/dashboard/components/resuables/WriteAReport.tsx index 920c308..a429d54 100644 --- a/src/app/dashboard/components/resuables/WriteAReport.tsx +++ b/src/app/dashboard/components/resuables/WriteAReport.tsx @@ -1,39 +1,27 @@ "use client"; -import { useState, useRef, useEffect, useMemo } from "react"; +import { useState, useRef, useEffect } from "react"; import dynamic from "next/dynamic"; -import { ChevronDownIcon, X } from "lucide-react"; +import { ChevronDownIcon, Upload } from "lucide-react"; import { motion, AnimatePresence } from "framer-motion"; import "react-quill/dist/quill.snow.css"; -import Image from "next/image"; -import { ReportData, uploadToPinata } from "@/lib/utils"; import toast from "react-hot-toast"; -import { writeContractWithStarknetJs } from "@/hooks/useBlockchain"; import { Account, byteArray, cairo } from "starknet"; +import { ReportData, uploadToPinata } from "@/lib/utils"; +import { writeContractWithStarknetJs } from "@/hooks/useBlockchain"; import { useAccount } from "@starknet-react/core"; -import { has } from "lodash"; const ReactQuill = dynamic(() => import("react-quill"), { ssr: false }); const modules = { toolbar: [ - ["header", "font", "size"], ["bold", "italic", "underline", "strike", "blockquote"], - [ - { list: "ordered" }, - { list: "bullet" }, - { indent: "-1" }, - { indent: "+1" }, - ], - ["link", "image", "video"], - ["clean"], + [{ list: "ordered" }, { list: "bullet" }], + ["link", "image"], ], }; const formats = [ - "header", - "font", - "size", "bold", "italic", "underline", @@ -41,27 +29,39 @@ const formats = [ "blockquote", "list", "bullet", - "indent", "link", "image", - "video", - "align", - "code-block", ]; type WriteAReportProps = { isOpen: boolean; onClose: () => void; projectId?: number; + }; -export default function WriteAReport({ +export default function WriteAReportWriteAReport({ isOpen, onClose, projectId, }: WriteAReportProps) { - const [value, setValue] = useState(""); - const [previewMode, setPreviewMode] = useState(false); + const [severityLevel] = useState([ + { id: 1, name: "Low" }, + { id: 2, name: "Medium" }, + { id: 3, name: "High" }, + { id: 4, name: "Critical" }, + ]); + const [categories] = useState([ + { id: 1, name: "Smart Contract" }, + { id: 2, name: "Frontend" }, + { id: 3, name: "" }, + { id: 4, name: "Infrastructure" }, + ]); + + const [selectedLevel, setSelectedLevel] = useState(severityLevel[0]); + const [selectedCategory, setSelectedCategory] = useState(categories[0]); + const [showLevelOptions, setShowLevelOptions] = useState(false); + const [showCategoryOptions, setShowCategoryOptions] = useState(false); const [isSubmitted, setIsSubmitted] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [ipfsHash, setIpfsHash] = useState(""); @@ -69,27 +69,30 @@ export default function WriteAReport({ const [generatedReportId, setGeneratedReportId] = useState(""); const [title, setTitle] = useState(""); const [project, setProject] = useState(""); + const [description, setDescription] = useState(""); + const [risk, setRisk] = useState(""); + const [recommendation, setRecommendation] = useState(""); + const [file, setFile] = useState(null); + const { account } = useAccount(); - const [severityLevel] = useState([ - { id: 1, name: "Low" }, - { id: 2, name: "Medium" }, - { id: 3, name: "High" }, - { id: 4, name: "Critical" }, - ]); - const [selectedLevel, setSelectedLevel] = useState(severityLevel[0]); - const [showOptions, setShowOptions] = useState(false); const dropdownRef = useRef(null); - const { account } = useAccount(); - const toggleOptions = () => { - setShowOptions((prev) => !prev); - }; + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + setShowLevelOptions(false); + setShowCategoryOptions(false); + } + } + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); - const handleSelect = (level: { id: number; name: string }) => { - setSelectedLevel(level); - setShowOptions(false); + const handleFileChange = (e: React.ChangeEvent) => { + if (e.target.files && e.target.files[0]) { + setFile(e.target.files[0]); + } }; - const generateUniqueId = (): string => { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 8); @@ -101,59 +104,47 @@ export default function WriteAReport({ toast.error("Please fill out all fields before submitting."); return; } - - - + setIsSubmitting(true); - + try { - const severityMap: { [key: string]: ReportData["severity"] } = { + const severityMap: { [key: string]: ReportData["severity"] | "" } = { + "Select Severity level": "", Low: "low", Medium: "high", High: "high", Critical: "critical", }; - + + const reportId = generateUniqueId(); setGeneratedReportId(reportId); - + const reportData: ReportData = { id: reportId, name: title.trim(), severity: severityMap[selectedLevel.name] || "low", status: "pending", }; - + const hash = await uploadToPinata(reportData); setIpfsHash(hash); toast.success(`Report uploaded to IPFS! Hash: ${hash}`); - - console.log({ - reportData, - projectId, - account, - hash, - }, 'hash'); - + if (account && projectId) { toast.loading("Submitting to contract..."); - + const contractArgs = { project_id: cairo.uint256(projectId), report_uri: byteArray.byteArrayFromString(hash), }; - + const contractResult = await writeContractWithStarknetJs( account as Account, "submit_report", contractArgs ); - - - console.log({ - contractResult - }, 'result'); - + if (contractResult && contractResult.result && contractResult.status) { setContractReportId(reportId); setIsSubmitted(true); @@ -162,10 +153,7 @@ export default function WriteAReport({ `Report submitted to blockchain successfully! Report ID: ${reportId}` ); } else { - console.error( - "❌ WriteAReport: Contract transaction failed:", - contractResult - ); + console.error("❌ Contract transaction failed:", contractResult); toast.dismiss(); toast.error("Contract transaction failed. Please try again."); } @@ -175,258 +163,201 @@ export default function WriteAReport({ } else { toast.error("Please connect your wallet to submit to blockchain."); } - } catch (error) { + } catch (error: any) { console.error("Error submitting report:", error); - toast.error("Failed to submit report. Please try again."); + toast.error(`Failed to submit report: ${error.message || "Unknown error"}`); } finally { - // setIsSubmitting(false); + setIsSubmitting(false); // re-enabled } }; + - const handleGoBack = () => { - setIsSubmitted(false); - setIsSubmitting(false); - setIpfsHash(""); - setContractReportId(""); - setGeneratedReportId(""); - setTitle(""); - setProject(""); - setValue(""); - setPreviewMode(false); - setSelectedLevel(severityLevel[0]); - }; - useEffect(() => { - function handleClickOutside(event: MouseEvent) { - if ( - dropdownRef.current && - !dropdownRef.current.contains(event.target as Node) - ) { - setShowOptions(false); - } - } - document.addEventListener("mousedown", handleClickOutside); - return () => { - document.removeEventListener("mousedown", handleClickOutside); - }; - }, []); + if (!isOpen) return null; - if (!isOpen) { - return null; - } return ( -
- {!isSubmitted ? ( -
- - - Write a Report - - - - - - {/* Report Title */} - - - setTitle(e.target.value)} - /> - - - {/* Project and Severity Level */} - - {/* Project Input */} -
- - setProject(e.target.value)} - /> -
- - {/* Severity Level Dropdown */} -
- -
- {selectedLevel.name} - - - - - {showOptions && ( - - {severityLevel.map((level) => ( - - ))} - - )} - -
-
-
- - {/* Quill Editor */} - + +

+ Fortichain Security Vulnerability Report +

+ +

+ Submit your security findings and vulnerability reports +

+ + {/* Section 1 */} +
+

+ Section 1 | Description +

+
+ setTitle(e.target.value)} + className="flex-1 bg-[#161113] border border-[#464043] rounded-lg px-4 py-3 text-sm text-gray-300 outline-none" + /> + {/* Severity Dropdown */} +
+ -
-
- + {selectedLevel.name} + + + + {showLevelOptions && ( + + {severityLevel.map((level) => ( + + ))} + + )} + +
- {/* Submit Button */} - - - -
-
-
- ) : ( - // ✅ Success Modal -
-
-
-

- Report Submitted Successfully -

-

- Your report has been submitted. -

- check + {/* Category Dropdown */} +
+ + {showCategoryOptions && ( + + {categories.map((cat) => ( + + ))} + + )} +
+ +
+ + {/* Section 2 */} +
+

+ Section 2 | Potential Risk +

+ +
+ + {/* Section 3 */} +
+

+ Section 3 | Recommendation +

+ +
+ + {/* Section 4 */} +
+

+ Section 4 | Attachments +

+
+

+ Upload Vulnerability Assessments, Pen Test Results, And Compliance Reports +

+ + +

Supports PDF, DOC, DOCX up to 10MB

+
+
+ + {/* Submit */} +
+
- )} +
); } diff --git a/src/app/dashboard/layout.tsx b/src/app/dashboard/layout.tsx index f4498ec..f147d65 100644 --- a/src/app/dashboard/layout.tsx +++ b/src/app/dashboard/layout.tsx @@ -2,14 +2,23 @@ import React from "react"; import { Sidebar } from "./components/sidenav"; import Header from "./components/header"; import { ProjectsProvider } from "@/context/project-context"; +import Foot from "@/components/foot"; const Layout: React.FC<{ children: React.ReactNode }> = ({ children }) => { return ( -
+
+ {/* Header at the top */}
-
- {children} + + {/* Main content area grows, pushes footer down */} +
+
{children}
+
+ + {/* Footer always below content, not fixed */} +
+
diff --git a/src/app/dashboard/researcher/projects/[projectId]/page.tsx b/src/app/dashboard/researcher/projects/[projectId]/page.tsx index cf77f04..a59d5ba 100644 --- a/src/app/dashboard/researcher/projects/[projectId]/page.tsx +++ b/src/app/dashboard/researcher/projects/[projectId]/page.tsx @@ -3,11 +3,14 @@ import { projects } from "../mockData"; import { notFound } from "next/navigation"; import Image from "next/image"; -import { Bookmark, SquarePen } from "lucide-react"; +import { ArrowUpRight, Bookmark, FileCode, SquarePen } from "lucide-react"; import { useState } from "react"; import CardGrid from "@/app/dashboard/components/resuables/ReportCard"; import { motion } from "framer-motion"; import WriteAReport from "@/app/dashboard/components/resuables/WriteAReport"; +import { IoLogoGithub } from "react-icons/io"; +import ActionCard from "@/app/dashboard/components/ActionCard/ActionCard"; +import ReportDiscussion from "@/app/dashboard/components/resuables/ReportDiscussion"; type Props = { params: { @@ -26,6 +29,10 @@ export default function ProjectDetailsPage({ params }: Props) { const [bookmark, setBookmark] = useState(false); const [isWriteModalOpen, setIsWriteModalOpen] = useState(false); const [isViewModalOpen, setIsViewModalOpen] = useState(false); + const [isChatModalOpen, setIsChatModalOpen] = useState(false); + + const handleOpenChatModal = () => setIsChatModalOpen(true); + const handleCloseChatModal = () => setIsChatModalOpen(false); const handleBookmark = () => setBookmark(!bookmark); const handleOpenWriteModal = () => setIsWriteModalOpen(true); @@ -68,156 +75,152 @@ export default function ProjectDetailsPage({ params }: Props) { initial={{ opacity: 0, y: 50 }} animate={{ opacity: 1, y: 0 }} transition={{ duration: 0.9 }} - className="flex justify-between items-center" + className="flex items-center" > -
-
- {project.logo.text} +
+
+ + + {project.status} + +
+
+
+ +

{project.title}

+
+ +
+ Bounty Amount + + {project.amount} +
+ +
+
+
+ Bounty Amount + + {project.amount} +
+
-

{project.title}

- - +
+ Deadline: +
+ {project.deadline} +
+
+
{/* Project description */} + Details + + {project.description} - {/* Tags, Prize Pool, Deadline, Repository */} + {/* Priority, Prize Pool, Deadline, Status */} -
- {project.tags.map((tag) => ( - - {tag} - - ))} -
- -
-
- Money bag logo - Prize Pool: {project.amount} -
- -
- deadline - Deadline: {project.deadline} -
-
- - {/* Repository */} -
- {project.repository?.map((repo) => ( -
- repository - {repo.name} -
- ))} + + Links + +
+
Github Repo
+
Contract Address
- - {/* Languages */} -

Languages

-
- {project.language && ( -
- {Object.entries(project.language).map( - ([languageName, languageData]) => ( -
- {`${languageName} - - {languageName} - - - {languageData.percentage}% - -
- ) - )} -
- )} + + Rewards + +
+ Rewards would be paid on successful completion by validator
- {/* Action Buttons */} - - {/* Write a Report */} - - - {/* View Report */} - {report.length > 0 && ( - - )} - - +
+
+ +
+
+ + +
+
+ +
+
{/* Modals */} + {isChatModalOpen && ( +
+
+ + +
+
+)} + + ); } diff --git a/src/app/dashboard/researcher/projects/mockData.ts b/src/app/dashboard/researcher/projects/mockData.ts index 798c6ef..f2c63ee 100644 --- a/src/app/dashboard/researcher/projects/mockData.ts +++ b/src/app/dashboard/researcher/projects/mockData.ts @@ -1,396 +1,180 @@ -type LanguageDetails = { +// types.ts + +export type LanguageDetails = { percentage: number; logo: string; icon: string; bgColor: string; -}; - -export type Project = { + }; + + export type Project = { id: number; logo: { - logo: string; - text: string; - bgColor: string; + logo: string; + text: string; + bgColor: string; }; title: string; description: string; - amount: string; + amount?: string; // optional, since you don’t use it yet + priority: "Low" | "Medium" | "High" | "Critical"; + viewDetails: string; deadline: string; - tags: string[]; + status: "Available" | "Completed"; + tags?: string[]; + }; - repository?: { - name: string; - link: string; - }[]; - language?: { - typescript?: LanguageDetails; - javascript?: LanguageDetails; - solidity?: LanguageDetails; - cairo?: LanguageDetails; - rust?: LanguageDetails; - go?: LanguageDetails; - python?: LanguageDetails; - java?: LanguageDetails; - c?: LanguageDetails; - }; -}; - - -export const projects: Project[] = [ + // ✅ Mock data + export const projects: Project[] = [ { - id: 1, - logo: { - logo: "/researcherIcon/DG.svg", - text: "DG", - bgColor: "bg-yellow-600", - }, - title: "Defi Guard", - description: - "A decentralized finance (DeFi) security tool designed to protect users and protocols by proactively scanning for vulnerabilities and potential exploits. It leverages advanced threat detection to identify security risks in smart contracts and DeFi platforms, providing real-time alerts and actionable insights. By helping prevent hacks and unauthorized access, it enhances trust and safety within the DeFi ecosystem, ensuring a more secure experience for all participaA decentralized finance (DeFi) protection tool that scans for vulnerabilities in DeFi protocols and helps prevent hacks.nts", - amount: "$50,000", - deadline: "24/09/2025", - tags: ["DeFi", "Storage", "NFTs"], - repository: [ - { - name: "DeFi-Guard-Smartcontract", - link: "www.github.com/defi-guard/smartcontract", - }, - { - name: "DeFi-Guard-Frontend", - link: "www.github.com/defi-guard/frontend", - }, - ], - language: { - typescript: { - percentage: 30, - logo: "/researcherIcon/typescript.svg", - icon: "TS", - bgColor: "bg-[#000055]", - }, - javascript: { - percentage: 10, - logo: "/researcherIcon/javascript.svg", - icon: "JS", - bgColor: "bg-[#4F4F07]", - }, - python: { - percentage: 10, - logo: "/researcherIcon/python.svg", - icon: "PY", - bgColor: "bg-[#0000AA]", - }, - cairo: { - percentage: 50, - logo: "/researcherIcon/cairo.svg", - icon: "CA", - bgColor: "bg-[#5B1712]", - }, - }, + id: 1, + title: "Smart Contract Audit", + logo: { + logo: "/researcherIcon/P1.svg", + text: "SC", + bgColor: "bg-blue-800", + }, + description: "Smart Contract Audit is a planned, thorough evaluation of a smart contract’s codebase, designed to equip researchers with a clear framework for assessing its security, functionality, and efficiency within the Mindblitz ecosystem or related blockchain applications. The audit, to be conducted by specialized security experts, will utilize automated analysis tools and in-depth manual code review to uncover potential vulnerabilities, logical errors, or inefficiencies that could lead to exploits or operational issues. Researchers will focus on verifying the contract’s alignment with its specified requirements, compliance with blockchain industry best practices, and robustness against common attack vectors, such as reentrancy, integer overflows, or unauthorized access. The process will result in a detailed report that identifies any issues, evaluates their severity, and provides actionable recommendations for remediation, enabling researchers to ensure the smart contract’s reliability, security, and trustworthiness prior to its deployment in decentralized systems.", + priority: "High", + viewDetails: "/projects/1", + deadline: "17th - Aug - 2025", + status: "Available", + amount:"$1500" + }, { - id: 2, - logo: { - logo: "/researcherIcon/PC.svg", - text: "PC", - bgColor: "bg-blue-700", - }, - title: "Plankton Chain", - description: - "A Layer 2 blockchain that enhances security while abstracting complex processes, providing a seamless and user-friendly experience for developers and users alike.", - amount: "$50,000", - deadline: "24/09/2025", - tags: ["DeFi", "Storage", "NFTs"], - repository: [ - { - name: "Plankton-Chain-Smartcontract", - link: "www.github.com/plankton-chain/smartcontract", - }, - { - name: "Plankton-Chain-Frontend", - link: "www.github.com/plankton-chain/frontend", - }, - ], - language: { - typescript: { - percentage: 30, - logo: "/researcherIcon/typescript.svg", - icon: "TS", - bgColor: "bg-[#000055]", - }, - javascript: { - percentage: 70, - logo: "/researcherIcon/javascript.svg", - icon: "JS", - bgColor: "bg-[#4F4F07]", - }, - python: { - percentage: 50, - logo: "/researcherIcon/python.svg", - icon: "PY", - bgColor: "bg-[#0000AA]", - }, - cairo: { - percentage: 50, - logo: "/researcherIcon/cairo.svg", - icon: "CA", - bgColor: "bg-[#5B1712]", - }, - }, + id: 2, + title: "Funding Function Audit", + logo: { + logo: "/researcherIcon/P2.svg", + text: "FF", + bgColor: "bg-green-700", + }, + description: "Security assessment of funding mechanisms in decentralized apps.", + priority: "Critical", + viewDetails: "/projects/2", + deadline: "17th - Aug - 2025", + status: "Available", + amount:"$1200" + }, { - id: 3, - logo: { - logo: "/researcherIcon/P1.svg", - text: "FD", - bgColor: "bg-blue-800", - }, - title: "FortiChain", - description: - "A decentralized bug bounty and smart contract security platform that rewards researchers and secures smart contracts by identifying and fixing vulnerabilities.", - amount: "$50,000", - deadline: "24/09/2025", - tags: ["Security", "AI"], - repository: [ - { - name: "FortiChain-Smartcontract", - link: "www.github.com/fortichain/smartcontract", - }, - { - name: "FortiChain-Frontend", - link: "www.github.com/fortichain/frontend", - }, - ], - language: { - typescript: { - percentage: 10, - logo: "/researcherIcon/typescript.svg", - icon: "TS", - bgColor: "bg-[#000055]", - }, - javascript: { - percentage: 30, - logo: "/researcherIcon/javascript.svg", - icon: "JS", - bgColor: "bg-[#4F4F07]", - }, - python: { - percentage: 10, - logo: "/researcherIcon/python.svg", - icon: "PY", - bgColor: "bg-[#0000AA]", - }, - cairo: { - percentage: 50, - logo: "/researcherIcon/cairo.svg", - icon: "CA", - bgColor: "bg-[#5B1712]", - }, - }, + id: 3, + title: "Strategy Review Audit", + logo: { + logo: "/researcherIcon/P2.svg", + text: "SR", + bgColor: "bg-purple-600", + }, + description: "Comprehensive audit of platform’s strategic security approach.", + priority: "Medium", + viewDetails: "/projects/3", + deadline: "17th - Aug - 2025", + status: "Available", + amount:"$800" + }, { - id: 4, - logo: { - logo: "/researcherIcon/DG.svg", - text: "DG", - bgColor: "bg-blue-800", - }, - title: "FortiChain", - description: - "A decentralized bug bounty and smart contract security platform that rewards researchers and secures smart contracts by identifying and fixing vulnerabilities.", - amount: "$50,000", - deadline: "24/09/2025", - tags: ["Security", "AI"], - repository: [ - { - name: "FortiChain-Smartcontract", - link: "www.github.com/fortichain/smartcontract", - }, - { - name: "FortiChain-Frontend", - link: "www.github.com/fortichain/frontend", - }, - ], - language: { - typescript: { - percentage: 10, - logo: "/researcherIcon/typescript.svg", - icon: "TS", - bgColor: "bg-[#000055]", - }, - javascript: { - percentage: 35, - logo: "/researcherIcon/javascript.svg", - icon: "JS", - bgColor: "bg-[#4F4F07]", - }, - python: { - percentage: 50, - logo: "/researcherIcon/python.svg", - icon: "PY", - bgColor: "bg-[#0000AA]", - }, - cairo: { - percentage: 5, - logo: "/researcherIcon/cairo.svg", - icon: "CA", - bgColor: "bg-[#5B1712]", - }, - }, + id: 4, + title: "User Experience Testing", + logo: { + logo: "/researcherIcon/PC.svg", + text: "UX", + bgColor: "bg-red-600", + }, + description: "Testing app usability and detecting issues with user flows.", + priority: "High", + viewDetails: "/projects/4", + deadline: "17th - Aug - 2025", + status: "Completed", + amount:"$1200" }, { - id: 5, - logo: { - logo: "/researcherIcon/P2.svg", - text: "FD", - bgColor: "bg-blue-800", - }, - title: "FortiChain", - description: - "A decentralized bug bounty and smart contract security platform that rewards researchers and secures smart contracts by identifying and fixing vulnerabilities.", - amount: "$50,000", - deadline: "24/09/2025", - tags: ["Security", "AI"], - repository: [ - { - name: "FortiChain-Smartcontract", - link: "www.github.com/fortichain/smartcontract", - }, - { - name: "FortiChain-Frontend", - link: "www.github.com/fortichain/frontend", - }, - ], - language: { - typescript: { - percentage: 40, - logo: "/researcherIcon/typescript.svg", - icon: "TS", - bgColor: "bg-[#000055]", - }, - javascript: { - percentage: 30, - logo: "/researcherIcon/javascript.svg", - icon: "JS", - bgColor: "bg-[#4F4F07]", - }, - python: { - percentage: 10, - logo: "/researcherIcon/python.svg", - icon: "PY", - bgColor: "bg-[#0000AA]", - }, - cairo: { - percentage: 20, - logo: "/researcherIcon/cairo.svg", - icon: "CA", - bgColor: "bg-[#5B1712]", - }, - }, + id: 5, + title: "Quarterly Financial Report", + logo: { + logo: "/researcherIcon/PC.svg", + text: "FR", + bgColor: "bg-yellow-600", + }, + description: "Analysis and audit of the quarterly financial reporting system.", + priority: "Low", + viewDetails: "/projects/5", + deadline: "17th - Aug - 2025", + status: "Completed", + amount:"$1800" + }, { - id: 6, - logo: { - logo: "/researcherIcon/P1.svg", - text: "FD", - bgColor: "bg-blue-800", - }, - title: "FortiChain", - description: - "A decentralized bug bounty and smart contract security platform that rewards researchers and secures smart contracts by identifying and fixing vulnerabilities.", - amount: "$50,000", - deadline: "24/09/2025", - tags: ["Security", "AI"], - repository: [ - { - name: "FortiChain-Smartcontract", - link: "www.github.com/fortichain/smartcontract", - }, - { - name: "FortiChain-Frontend", - link: "www.github.com/fortichain/frontend", - }, - ], - language: { - typescript: { - percentage: 0, - logo: "/researcherIcon/typescript.svg", - icon: "TS", - bgColor: "bg-[#000055]", - }, - javascript: { - percentage: 50, - logo: "/researcherIcon/javascript.svg", - icon: "JS", - bgColor: "bg-[#4F4F07]", - }, - python: { - percentage: 20, - logo: "/researcherIcon/python.svg", - icon: "PY", - bgColor: "bg-[#0000AA]", - }, - cairo: { - percentage: 30, - logo: "/researcherIcon/cairo.svg", - icon: "CA", - bgColor: "bg-[#5B1712]", - }, - }, - }, -]; - - -// cards.ts + id: 6, + title: "Product Launch Plan", + logo: { + logo: "/researcherIcon/P2.svg", + text: "PL", + bgColor: "bg-teal-700", + }, + description: "Audit and review of product launch security and workflows.", + priority: "Medium", + viewDetails: "/projects/6", + deadline: "17th - Aug - 2025", + status: "Completed", + amount:"$1900" -export type Card = { + }, + ]; + + // ------------------ Cards ------------------ + + export type Card = { id: string; date: string; title: string; - severity: string; + severity: "Low" | "Medium" | "High" | "Critical"; score: number; reward: string; }; + // ✅ Mock data export const cards: Card[] = [ { - id: '#8793', - date: '3 Jan, 4:35 PM', - title: 'Filename parameter on Home Page -', - severity: 'Critical', + id: "#8793", + date: "3 Jan, 4:35 PM", + title: "Filename parameter vulnerability on Home Page", + severity: "Critical", score: 9.0, - reward: '$200' + reward: "$200", }, { - id: '#8793', - date: '3 Jan, 4:35 PM', - title: 'Filename parameter on Home Page -', - severity: 'Critical', - score: 9.0, - reward: '$200' + id: "#8794", + date: "4 Jan, 2:15 PM", + title: "SQL injection issue on login form", + severity: "High", + score: 8.5, + reward: "$180", }, { - id: '#8793', - date: '3 Jan, 4:35 PM', - title: 'Filename parameter on Home Page -', - severity: 'Critical', - score: 9.0, - reward: '$200' + id: "#8795", + date: "5 Jan, 11:00 AM", + title: "Unprotected API endpoint exposing user data", + severity: "Critical", + score: 9.3, + reward: "$300", }, { - id: '#8793', - date: '3 Jan, 4:35 PM', - title: 'Filename parameter on Home Page -', - severity: 'Critical', - score: 9.0, - reward: '$200' + id: "#8796", + date: "6 Jan, 9:45 AM", + title: "Cross-site scripting vulnerability in comments section", + severity: "Medium", + score: 6.7, + reward: "$120", }, { - id: '#8793', - date: '3 Jan, 4:35 PM', - title: 'Filename parameter on Home Page -', - severity: 'Critical', - score: 9.0, - reward: '$200' - } + id: "#8797", + date: "7 Jan, 5:30 PM", + title: "Weak password hashing algorithm detected", + severity: "High", + score: 7.8, + reward: "$150", + }, ]; \ No newline at end of file diff --git a/src/app/dashboard/researcher/projects/page.tsx b/src/app/dashboard/researcher/projects/page.tsx index 1599743..a339ce0 100644 --- a/src/app/dashboard/researcher/projects/page.tsx +++ b/src/app/dashboard/researcher/projects/page.tsx @@ -1,68 +1,96 @@ "use client"; -import { motion } from "framer-motion"; // import framer motion -import { SearchIcon } from "lucide-react"; +import { motion } from "framer-motion"; +import { ChevronDown, Check, Filter } from "lucide-react"; import { ProjectCard } from "../../components/resuables/ProjectCard"; import { projects } from "./mockData"; +import { useState } from "react"; +import Foot from "@/components/foot"; const Projects = () => { + const [filter, setFilter] = useState("Available"); + const [open, setOpen] = useState(false); + + // Filter projects by status + const filteredProjects = + filter === "All" ? projects : projects.filter((p) => p.status === filter); + + const options = ["All", "Available", "Assigned"]; + return ( - - {/* Search and Filters */} - + {/* Content */} + -
- - -
-
- - -
-
+ {/* Dropdown + Filter Badge Row */} + + {/* Dropdown */} +
+ - {/* Project Cards */} - - {projects.map((project) => ( - + {/* Dropdown Menu */} + {open && ( +
+ {options.map((option) => ( +
{ + setFilter(option); + setOpen(false); + }} + className={`flex items-center justify-between px-4 py-2 cursor-pointer text-sm rounded-md ${ + filter === option + ? "bg-[#1F1A1C] text-white" + : "text-[#B5B3B4] hover:bg-[#1F1A1C]" + }`} + > + {option} + {filter === option && } +
))} - - +
+ )} +
+ + {/* Active Filter Badge */} +
+ + 1 +
+
+ + + {/* Project Cards */} + + {filteredProjects.map((project) => ( + + ))} + +
+
); }; diff --git a/src/app/dashboard/researcher/reports/[id]/page.tsx b/src/app/dashboard/researcher/reports/[id]/page.tsx index 85cd606..dea50d4 100644 --- a/src/app/dashboard/researcher/reports/[id]/page.tsx +++ b/src/app/dashboard/researcher/reports/[id]/page.tsx @@ -155,7 +155,7 @@ export default function ReportDetailPage({ params }: PageProps) { onClose={() => setShowProvideMoreDetailsModal(false)} onSubmit={(details) => { console.log("Additional details provided:", details); - // Here you would typically send the details to your backend + // Here you would typically send the details to your // For now, we'll just log it and close the modal setShowProvideMoreDetailsModal(false); }} diff --git a/src/components/foot.tsx b/src/components/foot.tsx new file mode 100644 index 0000000..d5d3217 --- /dev/null +++ b/src/components/foot.tsx @@ -0,0 +1,9 @@ +// components/Footer.tsx +export default function Foot() { + return ( +
+ © 2025 FortiChain. All rights reserved. Built on Starknet. +
+ ); + } + \ No newline at end of file From 6158c0b60ad7a4af5aca59cfe748ac5274b588b7 Mon Sep 17 00:00:00 2001 From: Henrichy <107367296+Henrichy@users.noreply.github.com> Date: Fri, 5 Sep 2025 15:33:57 +0100 Subject: [PATCH 2/2] made updates --- .../components/resuables/ReportCard.tsx | 81 +-- .../components/resuables/WriteAReport.css | 7 + .../components/resuables/WriteAReport.tsx | 463 +++++++++--------- .../researcher/projects/[projectId]/page.tsx | 167 +++---- 4 files changed, 340 insertions(+), 378 deletions(-) create mode 100644 src/app/dashboard/components/resuables/WriteAReport.css diff --git a/src/app/dashboard/components/resuables/ReportCard.tsx b/src/app/dashboard/components/resuables/ReportCard.tsx index 181bf8a..111b41e 100644 --- a/src/app/dashboard/components/resuables/ReportCard.tsx +++ b/src/app/dashboard/components/resuables/ReportCard.tsx @@ -1,64 +1,37 @@ "use client"; import { cards } from "../../researcher/projects/mockData"; -import { X } from "lucide-react"; -import { motion, AnimatePresence } from "framer-motion"; -type CardGridProps = { - isOpen: boolean; - onClose: () => void; -}; - -export default function CardGrid({ isOpen, onClose }: CardGridProps) { +export default function CardGrid() { return ( - - {isOpen && ( - -
- {/* Close Button */} - - -

- DeFi Guard Reports -

+
+

+ DeFi Guard Reports +

-
- {cards.map((card) => ( -
-
- {card.id} - {card.date} -
-

{card.title}

-
- - - {card.severity} - - {card.score} - - {card.reward} -
-
- ))} +
+ {cards.map((card) => ( +
+
+ {card.id} + {card.date} +
+

{card.title}

+
+ + + {card.severity} + + {card.score} + + {card.reward}
- - )} - + ))} +
+
); } diff --git a/src/app/dashboard/components/resuables/WriteAReport.css b/src/app/dashboard/components/resuables/WriteAReport.css new file mode 100644 index 0000000..0ea713b --- /dev/null +++ b/src/app/dashboard/components/resuables/WriteAReport.css @@ -0,0 +1,7 @@ +.ql-toolbar button svg { + width: 18px; + height: 18px; + stroke: currentColor; /* ensure outline icons */ + fill: none !important; /* prevent solid fill */ + } + \ No newline at end of file diff --git a/src/app/dashboard/components/resuables/WriteAReport.tsx b/src/app/dashboard/components/resuables/WriteAReport.tsx index a429d54..bb35cee 100644 --- a/src/app/dashboard/components/resuables/WriteAReport.tsx +++ b/src/app/dashboard/components/resuables/WriteAReport.tsx @@ -2,7 +2,7 @@ import { useState, useRef, useEffect } from "react"; import dynamic from "next/dynamic"; -import { ChevronDownIcon, Upload } from "lucide-react"; +import { ChevronDownIcon, Redo, Undo, Upload } from "lucide-react"; import { motion, AnimatePresence } from "framer-motion"; import "react-quill/dist/quill.snow.css"; import toast from "react-hot-toast"; @@ -10,41 +10,71 @@ import { Account, byteArray, cairo } from "starknet"; import { ReportData, uploadToPinata } from "@/lib/utils"; import { writeContractWithStarknetJs } from "@/hooks/useBlockchain"; import { useAccount } from "@starknet-react/core"; +import { Quill } from "react-quill"; +import { renderToStaticMarkup } from "react-dom/server"; + const ReactQuill = dynamic(() => import("react-quill"), { ssr: false }); +if (typeof window !== "undefined") { + const icons = Quill.import("ui/icons"); + icons["undo"] = renderToStaticMarkup(); + icons["redo"] = renderToStaticMarkup(); +} const modules = { - toolbar: [ - ["bold", "italic", "underline", "strike", "blockquote"], - [{ list: "ordered" }, { list: "bullet" }], - ["link", "image"], - ], + toolbar: { + container: [ + ['undo', 'redo'], + [{ header: [1, 2, 3, 4, 5, 6, false] }], + [{ font: [] }], + [{ size: [] }], + ['bold', 'italic', 'underline', 'strike'], + [{ color: [] }, { background: [] }], + [{ script: 'sub' }, { script: 'super' }], + ['blockquote', 'code-block'], + [{ list: 'ordered' }, { list: 'bullet' }], + [{ indent: '-1' }, { indent: '+1' }], + [{ align: [] }], + ['link', 'image', 'video'], + ['clean'] + ], + handlers: { + undo: function () { + // @ts-ignore + this.quill.history.undo(); + }, + redo: function () { + // @ts-ignore + this.quill.history.redo(); + } + } + }, + history: { + delay: 1000, + maxStack: 100, + userOnly: true + } }; + + const formats = [ - "bold", - "italic", - "underline", - "strike", - "blockquote", - "list", - "bullet", - "link", - "image", + 'header', 'font', 'size', + 'bold', 'italic', 'underline', 'strike', + 'color', 'background', + 'script', 'blockquote', 'code-block', + 'list', 'bullet', 'indent', + 'align', + 'link', 'image', 'video', + 'clean' ]; + type WriteAReportProps = { - isOpen: boolean; - onClose: () => void; projectId?: number; - }; -export default function WriteAReportWriteAReport({ - isOpen, - onClose, - projectId, -}: WriteAReportProps) { +export default function WriteAReport({ projectId }: WriteAReportProps) { const [severityLevel] = useState([ { id: 1, name: "Low" }, { id: 2, name: "Medium" }, @@ -54,7 +84,7 @@ export default function WriteAReportWriteAReport({ const [categories] = useState([ { id: 1, name: "Smart Contract" }, { id: 2, name: "Frontend" }, - { id: 3, name: "" }, + { id: 3, name: "Backend" }, { id: 4, name: "Infrastructure" }, ]); @@ -62,13 +92,11 @@ export default function WriteAReportWriteAReport({ const [selectedCategory, setSelectedCategory] = useState(categories[0]); const [showLevelOptions, setShowLevelOptions] = useState(false); const [showCategoryOptions, setShowCategoryOptions] = useState(false); - const [isSubmitted, setIsSubmitted] = useState(false); const [isSubmitting, setIsSubmitting] = useState(false); const [ipfsHash, setIpfsHash] = useState(""); const [contractReportId, setContractReportId] = useState(""); const [generatedReportId, setGeneratedReportId] = useState(""); const [title, setTitle] = useState(""); - const [project, setProject] = useState(""); const [description, setDescription] = useState(""); const [risk, setRisk] = useState(""); const [recommendation, setRecommendation] = useState(""); @@ -79,7 +107,10 @@ export default function WriteAReportWriteAReport({ useEffect(() => { function handleClickOutside(event: MouseEvent) { - if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { + if ( + dropdownRef.current && + !dropdownRef.current.contains(event.target as Node) + ) { setShowLevelOptions(false); setShowCategoryOptions(false); } @@ -93,6 +124,7 @@ export default function WriteAReportWriteAReport({ setFile(e.target.files[0]); } }; + const generateUniqueId = (): string => { const timestamp = Date.now(); const random = Math.random().toString(36).substring(2, 8); @@ -104,50 +136,47 @@ export default function WriteAReportWriteAReport({ toast.error("Please fill out all fields before submitting."); return; } - + setIsSubmitting(true); - + try { const severityMap: { [key: string]: ReportData["severity"] | "" } = { - "Select Severity level": "", Low: "low", Medium: "high", High: "high", Critical: "critical", }; - - + const reportId = generateUniqueId(); setGeneratedReportId(reportId); - + const reportData: ReportData = { id: reportId, name: title.trim(), severity: severityMap[selectedLevel.name] || "low", status: "pending", }; - + const hash = await uploadToPinata(reportData); setIpfsHash(hash); toast.success(`Report uploaded to IPFS! Hash: ${hash}`); - + if (account && projectId) { toast.loading("Submitting to contract..."); - + const contractArgs = { project_id: cairo.uint256(projectId), report_uri: byteArray.byteArrayFromString(hash), }; - + const contractResult = await writeContractWithStarknetJs( account as Account, "submit_report", contractArgs ); - + if (contractResult && contractResult.result && contractResult.status) { setContractReportId(reportId); - setIsSubmitted(true); toast.dismiss(); toast.success( `Report submitted to blockchain successfully! Report ID: ${reportId}` @@ -158,7 +187,6 @@ export default function WriteAReportWriteAReport({ toast.error("Contract transaction failed. Please try again."); } } else if (account && !projectId) { - setIsSubmitted(true); toast.success(`Report uploaded to IPFS successfully! Hash: ${hash}`); } else { toast.error("Please connect your wallet to submit to blockchain."); @@ -167,197 +195,196 @@ export default function WriteAReportWriteAReport({ console.error("Error submitting report:", error); toast.error(`Failed to submit report: ${error.message || "Unknown error"}`); } finally { - setIsSubmitting(false); // re-enabled + setIsSubmitting(false); } }; - - - if (!isOpen) return null; return ( -
- -

- Fortichain Security Vulnerability Report -

- -

- Submit your security findings and vulnerability reports -

- - {/* Section 1 */} -
-

- Section 1 | Description -

-
- setTitle(e.target.value)} - className="flex-1 bg-[#161113] border border-[#464043] rounded-lg px-4 py-3 text-sm text-gray-300 outline-none" - /> - {/* Severity Dropdown */} -
- - - {showLevelOptions && ( - - {severityLevel.map((level) => ( - - ))} - - )} - -
- - {/* Category Dropdown */} -
- - - {showCategoryOptions && ( - - {categories.map((cat) => ( - - ))} - - )} - -
-
- -
+ +

+ Fortichain Security Vulnerability Report +

- {/* Section 2 */} -
-

- Section 2 | Potential Risk -

- -
+

+ Submit your security findings and vulnerability reports +

- {/* Section 3 */} -
-

- Section 3 | Recommendation -

- +

+ Section 1 | Description +

+
+ setTitle(e.target.value)} + className="flex-1 bg-[#161113] border border-[#464043] rounded-lg px-4 py-3 text-sm text-gray-300 outline-none" /> -
+ {/* Severity Dropdown */} +
+ + + {showLevelOptions && ( + + {severityLevel.map((level) => ( + + ))} + + )} + +
- {/* Section 4 */} -
-

- Section 4 | Attachments -

-
-

- Upload Vulnerability Assessments, Pen Test Results, And Compliance Reports -

- -
+ +
+ + {/* Section 2 */} +
+

+ Section 2 | Potential Risk +

+ +
- {/* Submit */} -
-
+ + {/* Section 4 */} +
+

+ Section 4 | Attachments +

+
+

+ Upload Vulnerability Assessments, Pen Test Results, And Compliance + Reports +

+ + +

+ Supports PDF, DOC, DOCX up to 10MB +

- -
+
+ + {/* Submit */} +
+ +
+ ); } diff --git a/src/app/dashboard/researcher/projects/[projectId]/page.tsx b/src/app/dashboard/researcher/projects/[projectId]/page.tsx index a59d5ba..cb478b0 100644 --- a/src/app/dashboard/researcher/projects/[projectId]/page.tsx +++ b/src/app/dashboard/researcher/projects/[projectId]/page.tsx @@ -3,7 +3,7 @@ import { projects } from "../mockData"; import { notFound } from "next/navigation"; import Image from "next/image"; -import { ArrowUpRight, Bookmark, FileCode, SquarePen } from "lucide-react"; +import { ArrowUpRight, Bookmark, FileCode } from "lucide-react"; import { useState } from "react"; import CardGrid from "@/app/dashboard/components/resuables/ReportCard"; import { motion } from "framer-motion"; @@ -11,6 +11,7 @@ import WriteAReport from "@/app/dashboard/components/resuables/WriteAReport"; import { IoLogoGithub } from "react-icons/io"; import ActionCard from "@/app/dashboard/components/ActionCard/ActionCard"; import ReportDiscussion from "@/app/dashboard/components/resuables/ReportDiscussion"; +import WriteAReportWriteAReport from "@/app/dashboard/components/resuables/WriteAReport"; type Props = { params: { @@ -27,19 +28,11 @@ export default function ProjectDetailsPage({ params }: Props) { } const [bookmark, setBookmark] = useState(false); - const [isWriteModalOpen, setIsWriteModalOpen] = useState(false); - const [isViewModalOpen, setIsViewModalOpen] = useState(false); - const [isChatModalOpen, setIsChatModalOpen] = useState(false); - - const handleOpenChatModal = () => setIsChatModalOpen(true); - const handleCloseChatModal = () => setIsChatModalOpen(false); + const [showWriteReport, setShowWriteReport] = useState(false); + const [showViewReport, setShowViewReport] = useState(false); + const [showDiscussion, setShowDiscussion] = useState(false); const handleBookmark = () => setBookmark(!bookmark); - const handleOpenWriteModal = () => setIsWriteModalOpen(true); - const handleCloseWriteModal = () => setIsWriteModalOpen(false); - - const handleOpenViewModal = () => setIsViewModalOpen(true); - const handleCloseViewModal = () => setIsViewModalOpen(false); const bookmarkIcon = bookmark ? ( @@ -47,8 +40,6 @@ export default function ProjectDetailsPage({ params }: Props) { ); - const report = ["fhf"]; - return ( - {/* Main project details */} + {/* Project details */} - {/* Project header */} - + {/* Header */} +
{project.status} @@ -94,133 +81,101 @@ export default function ProjectDetailsPage({ params }: Props) {
-

{project.title}

+

+ {project.title} +

-
Bounty Amount {project.amount}
-
Bounty Amount - {project.amount} + + {project.amount} +
-
- +
+ + {/* Deadline */}
Deadline:
{project.deadline}
+
- {/* Project description */} - - Details - - + + {/* Description */} + Details + {project.description} - {/* Priority, Prize Pool, Deadline, Status */} - - - Links - -
-
Github Repo
-
Contract Address
-
-
- - - Rewards - -
- Rewards would be paid on successful completion by validator + {/* Links */} +
+

Links

+
+
+ + Github Repo +
+
+ + Contract Address +
- - +
+ + {/* Action Cards */}
setShowWriteReport(!showWriteReport)} />
setShowDiscussion(!showDiscussion)} /> -
setShowViewReport(!showViewReport)} />
- {/* Modals */} - - - {isChatModalOpen && ( -
-
- - -
-
-)} - + {/* Inline Sections */} + {showWriteReport && ( +
+ +
+ )} + {showViewReport && ( +
+ +
+ )} + {showDiscussion && ( +
+ +
+ )} ); }