Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@

import { Suspense } from 'react';
import { useParams, useSearchParams } from 'next/navigation';
import Link from 'next/link';
import {
ArrowLeft,
CalendarClock,
Info,
Loader2,
Expand All @@ -18,8 +16,8 @@ import { Tabs, TabsContent, TabsList, TabsTrigger } from '@/components/ui/tabs';
import { AuthGuard } from '@/components/auth';
import Loading from '@/components/Loading';
import EmptyState from '@/components/EmptyState';
import { DueCountdown } from '@/components/bounties/DueCountdown';
import BountyGeneralSettingsTab from '@/components/organization/bounties/settings/BountyGeneralSettingsTab';
import BountyTimelineSettingsTab from '@/components/organization/bounties/settings/BountyTimelineSettingsTab';
import BountySettingsPanel from '@/components/organization/bounties/manage/BountySettingsPanel';
import {
useBountyOverview,
Expand Down Expand Up @@ -64,15 +62,7 @@ function BountySettingsView() {
return (
<AuthGuard redirectTo='/auth?mode=signin' fallback={<Loading />}>
<div className='bg-background min-h-screen'>
<div className='mx-auto max-w-7xl px-6 py-8'>
<Link
href={`/organizations/${organizationId}/bounties/${bountyId}`}
className='mb-6 inline-flex items-center gap-1.5 text-sm text-zinc-400 transition-colors hover:text-white'
>
<ArrowLeft className='h-4 w-4' />
Back to dashboard
</Link>

<div className='w-full px-6 py-8'>
<div className='mb-6 flex items-center gap-3'>
<Settings className='text-primary h-6 w-6' />
<div>
Expand Down Expand Up @@ -153,7 +143,7 @@ function BountySettingsContent({

{/* Rewards — on-chain, immutable once funded. */}
<TabsContent value='rewards' className='mt-0'>
<div className='max-w-2xl space-y-4'>
<div className='space-y-4'>
<OnChainNotice />
<div className='rounded-2xl border border-zinc-800 bg-zinc-900/40 p-5'>
<h3 className='mb-3 text-sm font-semibold text-white'>
Expand Down Expand Up @@ -187,52 +177,13 @@ function BountySettingsContent({
</div>
</TabsContent>

{/* Timeline — on-chain deadline, immutable once funded. */}
{/* Timeline — on-chain submission deadline (read-only) + editable
off-chain application window. */}
<TabsContent value='timeline' className='mt-0'>
<div className='max-w-2xl space-y-4'>
<OnChainNotice />
<div className='rounded-2xl border border-zinc-800 bg-zinc-900/40 p-5'>
<h3 className='mb-3 text-sm font-semibold text-white'>Timeline</h3>
<dl className='space-y-2.5 text-sm'>
{overview.submissionDeadline ? (
<div className='flex items-center justify-between gap-3'>
<dt className='text-zinc-400'>Submission deadline</dt>
<dd>
<DueCountdown
deadline={overview.submissionDeadline}
className='flex items-center gap-1.5 text-xs font-medium text-zinc-200'
/>
</dd>
</div>
) : (
<Row label='Submission deadline' value='Not set' />
)}
{overview.applicationWindowCloseAt && (
<div className='flex items-center justify-between gap-3'>
<dt className='text-zinc-400'>Applications close</dt>
<dd>
<DueCountdown
deadline={overview.applicationWindowCloseAt}
className='flex items-center gap-1.5 text-xs font-medium text-zinc-200'
/>
</dd>
</div>
)}
{overview.maxApplicants != null && (
<Row
label='Max applicants'
value={String(overview.maxApplicants)}
/>
)}
{overview.shortlistSize != null && (
<Row
label='Shortlist size'
value={String(overview.shortlistSize)}
/>
)}
</dl>
</div>
</div>
<BountyTimelineSettingsTab
organizationId={organizationId}
bountyId={bountyId}
/>
</TabsContent>

{/* Close-out — cancel / refund + archive (moved from the dashboard). */}
Expand All @@ -258,31 +209,3 @@ function OnChainNotice() {
</div>
);
}

function Row({
label,
value,
hint,
capitalize,
}: {
label: string;
value: string;
hint?: string | null;
capitalize?: boolean;
}) {
return (
<div className='flex items-start justify-between gap-4'>
<dt className='text-zinc-400'>{label}</dt>
<dd className='max-w-[60%] text-right'>
<span
className={`font-medium text-white ${capitalize ? 'capitalize' : ''}`}
>
{value}
</span>
{hint && (
<span className='mt-0.5 block text-xs text-zinc-500'>{hint}</span>
)}
</dd>
</div>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ export default function BountySettingsPanel({
const archiveBusy = archiveMutation.isPending || restoreMutation.isPending;

return (
<div className='max-w-2xl space-y-6'>
<div className='space-y-6'>
<div className='rounded-2xl border border-red-500/30 bg-red-500/[0.04] p-5'>
<div className='flex items-start gap-3'>
<AlertTriangle className='mt-0.5 h-5 w-5 shrink-0 text-red-400' />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ const generalSchema = z.object({
demoVideo: z.boolean(),
media: z.boolean(),
reputationMinimum: z.union([z.number().int().min(0), z.nan()]).optional(),
applicationWindowCloseAt: z.string().optional(),
maxApplicants: z.union([z.number().int().min(1), z.nan()]).optional(),
shortlistSize: z.union([z.number().int().min(1), z.nan()]).optional(),
});
Expand Down Expand Up @@ -74,15 +73,6 @@ const REQUIREMENTS: Array<{
{ name: 'media', label: 'Media', hint: 'At least one image is required.' },
];

/** ISO date-time -> value for <input type="datetime-local"> (local time). */
function toLocalInput(iso?: string | null): string {
if (!iso) return '';
const d = new Date(iso);
if (Number.isNaN(d.getTime())) return '';
const pad = (n: number) => String(n).padStart(2, '0');
return `${d.getFullYear()}-${pad(d.getMonth() + 1)}-${pad(d.getDate())}T${pad(d.getHours())}:${pad(d.getMinutes())}`;
}

/** Empty / NaN number field -> null; otherwise the number. */
function numOrNull(v: number | undefined): number | null {
return v == null || Number.isNaN(v) ? null : v;
Expand Down Expand Up @@ -161,7 +151,6 @@ function GeneralForm({
demoVideo: bounty.submissionRequirements.demoVideo,
media: bounty.submissionRequirements.media,
reputationMinimum: bounty.reputationMinimum ?? undefined,
applicationWindowCloseAt: toLocalInput(bounty.applicationWindowCloseAt),
maxApplicants: bounty.maxApplicants ?? undefined,
shortlistSize: bounty.shortlistSize ?? undefined,
},
Expand All @@ -184,10 +173,6 @@ function GeneralForm({
reputationMinimum: isOpenSingle
? numOrNull(values.reputationMinimum)
: undefined,
applicationWindowCloseAt:
isApplication && values.applicationWindowCloseAt
? new Date(values.applicationWindowCloseAt).toISOString()
: undefined,
maxApplicants:
isApplication || (bounty.entryType === 'OPEN' && isCompetition)
? numOrNull(values.maxApplicants)
Expand Down Expand Up @@ -216,7 +201,7 @@ function GeneralForm({
isApplication || (bounty.entryType === 'OPEN' && isCompetition);

return (
<form onSubmit={handleSubmit(onSubmit)} className='max-w-2xl space-y-8'>
<form onSubmit={handleSubmit(onSubmit)} className='space-y-8'>
{/* ── Scope ── */}
<Section
title='Scope'
Expand Down Expand Up @@ -330,19 +315,6 @@ function GeneralForm({
</Field>
)}

{isApplication && (
<Field
label='Applications close'
error={errors.applicationWindowCloseAt?.message}
>
<Input
type='datetime-local'
{...register('applicationWindowCloseAt')}
className={inputClassName}
/>
</Field>
)}

{showMaxApplicants && (
<Field label='Max applicants' error={errors.maxApplicants?.message}>
<Input
Expand Down
Loading