Skip to content
Merged
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
21 changes: 7 additions & 14 deletions packages/nextjs/app/bounties/[id]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { useEffect, useMemo, useState } from "react";
import { useParams } from "next/navigation";
import { getJWT } from "@lighthouse-web3/kavach";
import lighthouse from "@lighthouse-web3/sdk";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { formatEther, parseAbiItem, parseEther } from "viem";
import {
useAccount,
Expand All @@ -14,6 +16,7 @@ import {
useWriteContract,
} from "wagmi";
import { BugAntIcon } from "@heroicons/react/24/outline";
import MDEditor from "~~/components/MDEditor";
import { Address } from "~~/components/scaffold-eth";
import { BountyStatus, bountyABI } from "~~/contracts/BountyABI";
import deployedContracts from "~~/contracts/deployedContracts";
Expand Down Expand Up @@ -264,7 +267,9 @@ export default function BountyDetailsPage() {
<div className="space-y-6">
<div>
<h2 className="text-xl font-akira mb-3 text-white">{metadata.title}</h2>
<p className="whitespace-pre-wrap text-gray-300 font-roboto leading-relaxed">{metadata.description}</p>
<div className="markdown-content">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{metadata.description}</ReactMarkdown>
</div>
</div>

<div className="grid grid-cols-1 md:grid-cols-2 gap-6 pt-6 border-t border-gray-800">
Expand Down Expand Up @@ -388,19 +393,7 @@ export default function BountyDetailsPage() {
value={title}
onChange={e => setTitle(e.target.value)}
/>
<div>
<textarea
className="w-full px-4 py-3 bg-black border border-gray-800 text-white font-roboto focus:outline-none focus:border-[var(--color-secondary)]/50 transition-colors min-h-40"
placeholder="Describe the vulnerability, reproduction steps, expected vs actual behavior, impacted components, and potential impact..."
value={description}
onChange={e => setDescription(e.target.value)}
maxLength={5000}
/>
<div className="mt-2 flex justify-between text-xs text-gray-500 font-roboto">
<span>Tip: Include minimal PoC or steps to reproduce.</span>
<span>{description.length}/5000</span>
</div>
</div>
<MDEditor value={description} setValue={setDescription} />
<input
type="text"
placeholder="Contact (email, Telegram, ENS, etc.)"
Expand Down
10 changes: 2 additions & 8 deletions packages/nextjs/app/bounties/create/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import {
ShieldExclamationIcon,
UserCircleIcon,
} from "@heroicons/react/24/outline";
import MDEditor from "~~/components/MDEditor";
import { AddressInput, EtherInput } from "~~/components/scaffold-eth";
import { useScaffoldWriteContract } from "~~/hooks/scaffold-eth";
import { notification } from "~~/utils/scaffold-eth";
Expand Down Expand Up @@ -133,14 +134,7 @@ export default function CreateBountyPage() {
</FormField>

<FormField label="Description" icon={<ChatBubbleLeftRightIcon className="h-5 w-5" />}>
<textarea
value={form.description}
onChange={e => setForm({ ...form, description: e.target.value })}
className="w-full px-4 py-3 bg-black border border-gray-800 text-white font-roboto focus:outline-none focus:border-[var(--color-secondary)]/50 transition-colors min-h-32"
rows={6}
placeholder="Describe the scope, vulnerability impact, and requirements for this bounty..."
required
/>
<MDEditor value={form.description} setValue={(value: string) => setForm({ ...form, description: value })} />
</FormField>

<div className="pt-4 border-t border-gray-800">
Expand Down
6 changes: 5 additions & 1 deletion packages/nextjs/app/bounties/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import { useEffect, useMemo, useState } from "react";
import Link from "next/link";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { formatEther, parseAbiItem } from "viem";
import { useAccount, usePublicClient } from "wagmi";
import { useReadContracts } from "wagmi";
Expand Down Expand Up @@ -103,7 +105,9 @@ const BountyCard = ({
</div>
</div>
<h2 className="text-xl font-akira mb-3 text-white truncate">{metadata.title}</h2>
<p className="mb-4 line-clamp-2 h-12 text-gray-400 font-roboto text-sm">{metadata.description}</p>
<div className="markdown-content max-h-14 overflow-hidden">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{metadata.description}</ReactMarkdown>
</div>
<div className="flex justify-between items-center text-sm mb-4 pt-4 border-t border-gray-800">
<div>
<p className="text-gray-500 font-roboto text-xs mb-1">Reward</p>
Expand Down
13 changes: 10 additions & 3 deletions packages/nextjs/app/reports/[bounty]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import { useEffect, useMemo, useState } from "react";
import { useParams, useSearchParams } from "next/navigation";
import lighthouse from "@lighthouse-web3/sdk";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";
import { formatEther, parseAbiItem } from "viem";
import {
useAccount,
Expand Down Expand Up @@ -327,9 +329,12 @@ export default function ReportDetailsPage() {
{reportJson.description && (
<div>
<strong className="text-gray-400 font-roboto text-sm block mb-2">Description:</strong>
<p className="whitespace-pre-wrap text-gray-300 font-roboto leading-relaxed">
<div className="markdown-content">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{reportJson.description}</ReactMarkdown>
{/* <p className="whitespace-pre-wrap text-gray-300 font-roboto leading-relaxed">
{reportJson.description}
</p>
</p> */}
</div>
</div>
)}
{reportJson.contact && (
Expand Down Expand Up @@ -418,7 +423,9 @@ function PublicReport({ cid }: { cid: string }) {
{data.description && (
<div>
<strong className="text-gray-400 font-roboto text-sm block mb-2">Description:</strong>
<p className="whitespace-pre-wrap text-gray-300 font-roboto leading-relaxed">{data.description}</p>
<div className="markdown-content">
<ReactMarkdown remarkPlugins={[remarkGfm]}>{data.description}</ReactMarkdown>
</div>
</div>
)}
{data.contact && (
Expand Down
50 changes: 50 additions & 0 deletions packages/nextjs/components/MDEditor.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import { useState } from "react";
import ReactMarkdown from "react-markdown";
import remarkGfm from "remark-gfm";

interface MDEditorProps {
value: string;
setValue: (str: string) => void;
}
export default function MDEditor({ value, setValue }: MDEditorProps) {
const [editorMode, setEditorMode] = useState<"write" | "preview">("write");
return (
<div className="markdown-editor-container">
<div className="editor-tabs">
<button type="button" className={editorMode === "write" ? "active" : ""} onClick={() => setEditorMode("write")}>
Write
</button>
<button
type="button"
className={editorMode === "preview" ? "active" : ""}
onClick={() => setEditorMode("preview")}
>
Preview
</button>
</div>
<div className="editor-body">
{editorMode === "write" ? (
<textarea
className="editor-textarea"
placeholder="Describe the vulnerability, reproduction steps, expected vs actual behavior, impacted components, and potential impact..."
value={value}
onChange={e => setValue(e.target.value)}
maxLength={5000}
/>
) : (
<div className="editor-preview markdown-content">
{value ? (
<ReactMarkdown remarkPlugins={[remarkGfm]}>{value}</ReactMarkdown>
) : (
<span className="preview-empty">Nothing to preview</span>
)}
</div>
)}
</div>
<div className="editor-footer">
<span>Supports Markdown</span>
<span className="char-count">{value.length.toLocaleString()}/5,000</span>
</div>
</div>
);
}
4 changes: 4 additions & 0 deletions packages/nextjs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@
"react": "~19.0.0",
"react-dom": "~19.0.0",
"react-hot-toast": "~2.4.0",
"react-markdown": "^10.1.0",
"react-syntax-highlight": "^15.3.1",
"rehype-react": "^8.0.0",
"remark-gfm": "^4.0.1",
"usehooks-ts": "~3.1.0",
"viem": "2.34.0",
"wagmi": "2.16.4",
Expand Down
Loading