From c3b4ce9d1eb101f5cdeba343608a643bc8f76f92 Mon Sep 17 00:00:00 2001 From: Claude Date: Sun, 26 Apr 2026 00:51:42 +0000 Subject: [PATCH] Add Edit button to the standalone prop page MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The standalone prop page (/props/[propId]) — used for props whose forecasting window is closed — already had a Resolve affordance but no way to edit. Mirror the Resolve pattern: add an Edit button that opens the existing PropEditDialog, gated on a canEdit check that covers system admins, competition admins (when the prop belongs to a competition), and personal-prop owners. --- app/props/[propId]/page.tsx | 13 +++++++ app/props/[propId]/prop-page-header.tsx | 48 ++++++++++++++++++------- 2 files changed, 49 insertions(+), 12 deletions(-) diff --git a/app/props/[propId]/page.tsx b/app/props/[propId]/page.tsx index b349836..495d53f 100644 --- a/app/props/[propId]/page.tsx +++ b/app/props/[propId]/page.tsx @@ -1,4 +1,5 @@ import { getForecasts, getPropById } from "@/lib/db_actions"; +import { getCurrentUserRole } from "@/lib/db_actions/competition-members"; import { getUserFromCookies } from "@/lib/get-user"; import { Suspense } from "react"; import ErrorPage from "@/components/pages/error-page"; @@ -60,6 +61,17 @@ async function PropPageContent({ // Find user's forecast const userForecast = forecasts.find((f) => f.user_id === user.id); + // Determine if the user is an admin of this prop's competition (if any) + let isCompetitionAdmin = false; + if (prop.competition_id !== null) { + const roleResult = await getCurrentUserRole(prop.competition_id); + if (roleResult.success && roleResult.data === "admin") { + isCompetitionAdmin = true; + } + } + const canEdit = + user.is_admin || isCompetitionAdmin || prop.prop_user_id === user.id; + // Calculate stats const forecastValues = forecasts.map((f) => f.forecast); const average = @@ -76,6 +88,7 @@ async function PropPageContent({ {/* Stats Row */} diff --git a/app/props/[propId]/prop-page-header.tsx b/app/props/[propId]/prop-page-header.tsx index 78ca1c0..4a434c4 100644 --- a/app/props/[propId]/prop-page-header.tsx +++ b/app/props/[propId]/prop-page-header.tsx @@ -5,8 +5,9 @@ import { useRouter } from "next/navigation"; import { VProp } from "@/types/db_types"; import { Badge } from "@/components/ui/badge"; import { Button } from "@/components/ui/button"; -import { ChevronLeft, CheckCircle2 } from "lucide-react"; +import { ChevronLeft, CheckCircle2, Pencil } from "lucide-react"; import { ResolutionDialog } from "@/components/dialogs/resolution-dialog"; +import { PropEditDialog } from "@/components/dialogs/prop-edit-dialog"; import { MarkdownRenderer } from "@/components/markdown"; import { PropStatusBadge } from "@/components/ui/prop-status-badge"; import { getPropStatusFromProp } from "@/lib/prop-status"; @@ -14,14 +15,17 @@ import { getPropStatusFromProp } from "@/lib/prop-status"; interface PropPageHeaderProps { prop: VProp; canResolve: boolean; + canEdit: boolean; } export default function PropPageHeader({ prop, canResolve, + canEdit, }: PropPageHeaderProps) { const router = useRouter(); const [isResolutionDialogOpen, setIsResolutionDialogOpen] = useState(false); + const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); return ( <> @@ -45,17 +49,28 @@ export default function PropPageHeader({ )} - {canResolve && ( - - )} +
+ {canEdit && ( + + )} + {canResolve && ( + + )} +

{prop.prop_text} @@ -82,6 +97,15 @@ export default function PropPageHeader({ isOpen={isResolutionDialogOpen} onClose={() => setIsResolutionDialogOpen(false)} /> + + { + setIsEditDialogOpen(false); + router.refresh(); + }} + /> ); }