diff --git a/app/(landing)/organizations/[id]/bounties/[bountyId]/page.tsx b/app/(landing)/organizations/[id]/bounties/[bountyId]/page.tsx
index 12387ac4..b2b98946 100644
--- a/app/(landing)/organizations/[id]/bounties/[bountyId]/page.tsx
+++ b/app/(landing)/organizations/[id]/bounties/[bountyId]/page.tsx
@@ -1,3 +1,4 @@
+import { Suspense } from 'react';
import { Metadata } from 'next';
import { AuthGuard } from '@/components/auth';
@@ -11,7 +12,10 @@ export default function BountyManagePageRoute() {
return (
}>
-
+ {/* Dashboard reads ?tab= via useSearchParams; needs a suspense boundary. */}
+ }>
+
+
);
diff --git a/app/(landing)/organizations/[id]/bounties/page.tsx b/app/(landing)/organizations/[id]/bounties/page.tsx
index 170b4fa3..02332495 100644
--- a/app/(landing)/organizations/[id]/bounties/page.tsx
+++ b/app/(landing)/organizations/[id]/bounties/page.tsx
@@ -3,7 +3,15 @@
import { useMemo, useState } from 'react';
import Link from 'next/link';
import { useParams, useRouter } from 'next/navigation';
-import { Calendar, FileText, Plus, Search, Target, Trash2 } from 'lucide-react';
+import {
+ ArrowRight,
+ Calendar,
+ FileText,
+ Plus,
+ Search,
+ Target,
+ Trash2,
+} from 'lucide-react';
import { BoundlessButton } from '@/components/buttons';
import { Badge } from '@/components/ui/badge';
@@ -68,6 +76,26 @@ function statusDisplay(status: string) {
);
}
+/**
+ * Status-aware manage CTA: deep-links into the dashboard tab that matches where
+ * the organizer's attention is needed next.
+ */
+function manageCta(status: string): { label: string; tab: string } {
+ switch (status) {
+ case 'submitted':
+ case 'under_review':
+ return { label: 'Review submissions', tab: 'submissions' };
+ case 'in_progress':
+ return { label: 'Review & pay', tab: 'payout' };
+ case 'completed':
+ return { label: 'View results', tab: 'results' };
+ case 'cancelled':
+ return { label: 'View bounty', tab: 'overview' };
+ default:
+ return { label: 'Manage', tab: 'overview' };
+ }
+}
+
const draftPrizePool = (draft: BountyDraft): number =>
(draft.data?.reward?.prizeTiers ?? []).reduce(
(sum, tier) => sum + (Number(tier.amount) || 0),
@@ -279,6 +307,25 @@ export default function OrganizationBountiesPage() {
{bounty._count?.submissions ?? 0}
+ {(() => {
+ const cta = manageCta(bounty.status);
+ return (
+ {
+ e.stopPropagation();
+ router.push(
+ `/organizations/${organizationId}/bounties/${bounty.id}?tab=${cta.tab}`
+ );
+ }}
+ >
+ {cta.label}
+
+
+ );
+ })()}
);
})}
diff --git a/components/organization/bounties/manage/BountyManagementDashboard.tsx b/components/organization/bounties/manage/BountyManagementDashboard.tsx
index 38ca2944..bf563bc7 100644
--- a/components/organization/bounties/manage/BountyManagementDashboard.tsx
+++ b/components/organization/bounties/manage/BountyManagementDashboard.tsx
@@ -1,7 +1,7 @@
'use client';
import { useState } from 'react';
-import { useParams } from 'next/navigation';
+import { useParams, useSearchParams } from 'next/navigation';
import Link from 'next/link';
import { ArrowLeft, Info, Loader2 } from 'lucide-react';
@@ -30,10 +30,22 @@ import BountyApplicationsPanel from './BountyApplicationsPanel';
import BountySettingsPanel from './BountySettingsPanel';
import BountyResultsPanel from './BountyResultsPanel';
+/** Tabs the org bounty list can deep-link into via `?tab=`. */
+const LINKABLE_TABS = new Set([
+ 'overview',
+ 'applications',
+ 'submissions',
+ 'payout',
+ 'results',
+ 'settings',
+]);
+
export default function BountyManagementDashboard() {
const params = useParams<{ id: string; bountyId: string }>();
+ const searchParams = useSearchParams();
const organizationId = params?.id ?? '';
const bountyId = params?.bountyId ?? '';
+ const requestedTab = searchParams?.get('tab') ?? '';
// Winner staging lives here (above the tab boundary) so it survives tab
// switches and is reachable by the Payout tab (#633).
@@ -87,6 +99,14 @@ export default function BountyManagementDashboard() {
overview.entryType === 'APPLICATION_LIGHT' ||
overview.entryType === 'APPLICATION_FULL';
const isCompleted = overview.status === 'completed';
+
+ // Honor a deep-linked ?tab= only when that tab is actually available for this
+ // bounty (applications need an application mode; results need completion).
+ const tabAvailable =
+ LINKABLE_TABS.has(requestedTab) &&
+ (requestedTab !== 'applications' || isApplication) &&
+ (requestedTab !== 'results' || isCompleted);
+ const initialTab = tabAvailable ? requestedTab : 'overview';
const modeLabel =
overview.entryType && overview.claimType
? computeBountyModeLabel(overview.entryType, overview.claimType)
@@ -145,7 +165,7 @@ export default function BountyManagementDashboard() {
-
+
Overview
{isApplication && (