From 72517aa19aa68bd124919a62b58b332f802a9c4d Mon Sep 17 00:00:00 2001 From: Femi John Date: Fri, 28 Aug 2026 10:10:44 +0100 Subject: [PATCH 1/2] Add dispute evidence preview component and normalization logic - Implement DisputeEvidencePreview component for displaying evidence links with safety checks. - Introduce normalization functions for dispute evidence to handle various input formats and ensure valid URLs. - Update DisputesPage to utilize the new DisputeEvidencePreview component. - Add tests for evidence normalization to validate functionality and edge cases. --- .../activity-timeline-demo/page.tsx | 2 +- app/(dashboard)/disputes/page.tsx | 30 ++- app/(dashboard)/events/new/page.tsx | 8 +- app/(dashboard)/finances/page.tsx | 5 +- app/(dashboard)/layout.tsx | 1 + app/(dashboard)/settings/page.tsx | 3 +- app/api/og/route.tsx | 2 +- .../disputes/DisputeEvidencePreview.tsx | 41 ++++ components/disputes/states/ExecutedState.tsx | 5 +- components/events/events-table.tsx | 177 +++++++++--------- components/typography-example.tsx | 2 +- lib/__tests__/dispute-evidence.test.ts | 45 +++++ lib/dispute-evidence.ts | 103 ++++++++++ 13 files changed, 315 insertions(+), 109 deletions(-) create mode 100644 components/disputes/DisputeEvidencePreview.tsx create mode 100644 lib/__tests__/dispute-evidence.test.ts create mode 100644 lib/dispute-evidence.ts diff --git a/app/(dashboard)/activity-timeline-demo/page.tsx b/app/(dashboard)/activity-timeline-demo/page.tsx index 466294b2..94a7d822 100644 --- a/app/(dashboard)/activity-timeline-demo/page.tsx +++ b/app/(dashboard)/activity-timeline-demo/page.tsx @@ -363,7 +363,7 @@ export default function MyPage() { onLoadMore - () => void + {'() => void'} undefined Callback when user clicks load more diff --git a/app/(dashboard)/disputes/page.tsx b/app/(dashboard)/disputes/page.tsx index 87c9c942..76d31091 100644 --- a/app/(dashboard)/disputes/page.tsx +++ b/app/(dashboard)/disputes/page.tsx @@ -3,6 +3,7 @@ import { useState } from "react" import { AlertTriangle, CheckCircle, Filter, Search } from "lucide-react" import { DisputePanel } from "@/components/disputes/DisputePanel" +import { DisputeEvidencePreview } from "@/components/disputes/DisputeEvidencePreview" import { mockDisputesByState } from "@/components/disputes/mock-data" import type { DisputeData, DisputeState } from "@/types/disputes" import { Button } from "@/components/ui/button" @@ -26,6 +27,21 @@ import { Label } from "@/components/ui/label" import { Textarea } from "@/components/ui/textarea" import { RadioGroup, RadioGroupItem } from "@/components/ui/radio-group" +type DisputePriority = "high" | "medium" | "low"; + +type PendingDispute = { + id: string; + eventId: string; + eventTitle: string; + category: string; + submittedBy: string; + submittedDate: string; + reason: string; + status: string; + priority: DisputePriority; + evidence: string; +}; + // Mock data for disputes const disputes = [ { @@ -108,7 +124,7 @@ export default function DisputesPage() { const [searchQuery, setSearchQuery] = useState("") const [statusFilter, setStatusFilter] = useState("all") const [priorityFilter, setPriorityFilter] = useState("all") - const [selectedDispute, setSelectedDispute] = useState(null) + const [selectedDispute, setSelectedDispute] = useState(null) const [resolution, setResolution] = useState("") const [resolutionNotes, setResolutionNotes] = useState("") const [selectedDisputeData, setSelectedDisputeData] = useState(mockDisputesByState.none) @@ -126,7 +142,7 @@ export default function DisputesPage() { return matchesSearch && matchesStatus && matchesPriority }) - const getPriorityBadge = (priority) => { + const getPriorityBadge = (priority: DisputePriority | string) => { switch (priority) { case "high": return High @@ -140,6 +156,8 @@ export default function DisputesPage() { } const handleResolve = () => { + if (!selectedDispute) return; + // In a real app, you would send this data to your API console.log({ disputeId: selectedDispute.id, @@ -260,13 +278,7 @@ export default function DisputesPage() {
-
- -
+
diff --git a/app/(dashboard)/events/new/page.tsx b/app/(dashboard)/events/new/page.tsx index 3667fa80..c20027b2 100644 --- a/app/(dashboard)/events/new/page.tsx +++ b/app/(dashboard)/events/new/page.tsx @@ -20,7 +20,7 @@ export default function NewEventPage() { const [title, setTitle] = useState("") const [description, setDescription] = useState("") const [category, setCategory] = useState("") - const [deadline, setDeadline] = useState(null) + const [deadline, setDeadline] = useState(null) const [options, setOptions] = useState([{ text: "", probability: "" }]) const [newOption, setNewOption] = useState("") const [isPublic, setIsPublic] = useState(true) @@ -33,13 +33,13 @@ export default function NewEventPage() { } } - const removeOption = (index) => { + const removeOption = (index: number) => { const updatedOptions = [...options] updatedOptions.splice(index, 1) setOptions(updatedOptions) } - const handleSubmit = (e) => { + const handleSubmit = (e: React.FormEvent) => { e.preventDefault() // In a real app, you would send this data to your API console.log({ @@ -116,7 +116,7 @@ export default function NewEventPage() { - + setDeadline(date ?? null)} initialFocus />
diff --git a/app/(dashboard)/finances/page.tsx b/app/(dashboard)/finances/page.tsx index 0160ea06..a6cca63b 100644 --- a/app/(dashboard)/finances/page.tsx +++ b/app/(dashboard)/finances/page.tsx @@ -1,6 +1,7 @@ "use client" import { useState } from "react" +import type { DateRange } from "react-day-picker" import { ArrowDownRight, ArrowUpRight, Calendar, CreditCard, DollarSign, Download, TrendingUp } from "lucide-react" import { Button } from "@/components/ui/button" import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card" @@ -74,7 +75,7 @@ const financialData = { export default function FinancesPage() { const { hideBalances } = usePrivacy(); - const [date, setDate] = useState({ + const [date, setDate] = useState({ from: new Date(2023, 3, 1), to: new Date(2023, 3, 30), }) @@ -107,7 +108,7 @@ export default function FinancesPage() { mode="range" defaultMonth={date?.from} selected={date} - onSelect={setDate} + onSelect={(range) => setDate(range ?? { from: new Date(2023, 3, 1), to: new Date(2023, 3, 30) })} numberOfMonths={2} /> diff --git a/app/(dashboard)/layout.tsx b/app/(dashboard)/layout.tsx index 7cef05e0..d9f144f8 100644 --- a/app/(dashboard)/layout.tsx +++ b/app/(dashboard)/layout.tsx @@ -5,6 +5,7 @@ import { useState, useEffect } from "react"; import { usePathname } from "next/navigation"; import { Navbar } from "@/components/navbar/Navbar"; import { Breadcrumbs } from "@/components/navbar/Breadcrumbs"; +import { MobileBottomTabs } from "@/components/navbar/MobileBottomTabs"; import { ConnectWalletModal } from "@/components/connect-wallet-modal"; import { getBreadcrumbsForPath } from "@/lib/breadcrumbs"; diff --git a/app/(dashboard)/settings/page.tsx b/app/(dashboard)/settings/page.tsx index ba67c244..7539e76a 100644 --- a/app/(dashboard)/settings/page.tsx +++ b/app/(dashboard)/settings/page.tsx @@ -75,7 +75,7 @@ const densityOptions: Array<{ label: string description: string icon: React.ElementType - tokens: (typeof densityTokens)["cozy"] + tokens: (typeof densityTokens)[Density] }> = [ { value: "cozy", @@ -112,6 +112,7 @@ export default function SettingsPage() { const [reduceMotion, setReduceMotion] = useState(false) const [showNetPayouts, setShowNetPayouts] = useState(true) const [showWalletBadge, setShowWalletBadge] = useState(true) + const [publicActivity, setPublicActivity] = useState(false) const [disputeAlerts, setDisputeAlerts] = useState(true) const [oracleDelayAlerts, setOracleDelayAlerts] = useState(true) const [priceMovementAlerts, setPriceMovementAlerts] = useState(false) diff --git a/app/api/og/route.tsx b/app/api/og/route.tsx index 586e338a..dfd2377d 100644 --- a/app/api/og/route.tsx +++ b/app/api/og/route.tsx @@ -95,7 +95,7 @@ export async function GET(req: NextRequest) { > P - Predictify + Predictify
{fallbackMessage}

; + } + + return ( +
+ {items.map((item) => ( +
+
+ + {item.isPrivate ? 'Private evidence preview' : item.label} + + {item.isPrivate && } +
+ + +
+ ))} +
+ ); +} diff --git a/components/disputes/states/ExecutedState.tsx b/components/disputes/states/ExecutedState.tsx index 789d6817..74b6c111 100644 --- a/components/disputes/states/ExecutedState.tsx +++ b/components/disputes/states/ExecutedState.tsx @@ -3,6 +3,7 @@ import { Badge } from '@/components/ui/badge'; import { TallyBar } from '@/components/disputes/shared/TallyBar'; import { DetailsAccordion } from '@/components/disputes/shared/DetailsAccordion'; import type { DisputeData, DisputeState } from '@/types/disputes'; +import { normalizeDisputeEvidence } from '@/lib/dispute-evidence'; interface ExecutedStateProps { data: DisputeData; @@ -40,8 +41,8 @@ export function ExecutedState({ data }: ExecutedStateProps) { Audit references