Platform polish: bulk invite, file requirements, filtering redesign, UX fixes
- F1: Set seed jury/mentors/observers to NONE status (not invited), remove passwords - F2: Add bulk invite UI with checkbox selection and floating toolbar - F3: Add getProjectRequirements backend query + requirement slots on project detail - F4: Redesign filtering section: AI criteria textarea, "What AI sees" card, field-aware eligibility rules with human-readable previews - F5: Auto-redirect to pipeline detail when only one pipeline exists - F6: Make project names clickable in pipeline intake panel - F7: Fix pipeline creation error: edition context fallback + .min(1) validation - Pipeline wizard sections: add isActive locking, info tooltips, UX improvements Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -12,8 +12,69 @@ import {
|
||||
} from '@/components/ui/select'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Card, CardContent } from '@/components/ui/card'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Plus, Trash2, FileText } from 'lucide-react'
|
||||
import { InfoTooltip } from '@/components/ui/info-tooltip'
|
||||
import type { IntakeConfig, FileRequirementConfig } from '@/types/pipeline-wizard'
|
||||
import {
|
||||
FILE_TYPE_CATEGORIES,
|
||||
getActiveCategoriesFromMimeTypes,
|
||||
categoriesToMimeTypes,
|
||||
} from '@/lib/file-type-categories'
|
||||
|
||||
type FileTypePickerProps = {
|
||||
value: string[]
|
||||
onChange: (mimeTypes: string[]) => void
|
||||
}
|
||||
|
||||
function FileTypePicker({ value, onChange }: FileTypePickerProps) {
|
||||
const activeCategories = getActiveCategoriesFromMimeTypes(value)
|
||||
|
||||
const toggleCategory = (categoryId: string) => {
|
||||
const isActive = activeCategories.includes(categoryId)
|
||||
const newCategories = isActive
|
||||
? activeCategories.filter((id) => id !== categoryId)
|
||||
: [...activeCategories, categoryId]
|
||||
onChange(categoriesToMimeTypes(newCategories))
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="space-y-2">
|
||||
<Label className="text-xs">Accepted Types</Label>
|
||||
<div className="flex flex-wrap gap-1.5">
|
||||
{FILE_TYPE_CATEGORIES.map((cat) => {
|
||||
const isActive = activeCategories.includes(cat.id)
|
||||
return (
|
||||
<Button
|
||||
key={cat.id}
|
||||
type="button"
|
||||
variant={isActive ? 'default' : 'outline'}
|
||||
size="sm"
|
||||
className="h-7 text-xs px-2.5"
|
||||
onClick={() => toggleCategory(cat.id)}
|
||||
>
|
||||
{cat.label}
|
||||
</Button>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
<div className="flex flex-wrap gap-1">
|
||||
{activeCategories.length === 0 ? (
|
||||
<Badge variant="secondary" className="text-[10px]">All types</Badge>
|
||||
) : (
|
||||
activeCategories.map((catId) => {
|
||||
const cat = FILE_TYPE_CATEGORIES.find((c) => c.id === catId)
|
||||
return cat ? (
|
||||
<Badge key={catId} variant="secondary" className="text-[10px]">
|
||||
{cat.label}
|
||||
</Badge>
|
||||
) : null
|
||||
})
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
type IntakeSectionProps = {
|
||||
config: IntakeConfig
|
||||
@@ -67,7 +128,10 @@ export function IntakeSection({ config, onChange, isActive }: IntakeSectionProps
|
||||
<div className="space-y-4">
|
||||
<div className="flex items-center justify-between">
|
||||
<div>
|
||||
<Label>Submission Window</Label>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Label>Submission Window</Label>
|
||||
<InfoTooltip content="When enabled, projects can only be submitted within the configured date range." />
|
||||
</div>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
Enable timed submission windows for project intake
|
||||
</p>
|
||||
@@ -85,7 +149,10 @@ export function IntakeSection({ config, onChange, isActive }: IntakeSectionProps
|
||||
{/* Late Policy */}
|
||||
<div className="grid gap-4 sm:grid-cols-2">
|
||||
<div className="space-y-2">
|
||||
<Label>Late Submission Policy</Label>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Label>Late Submission Policy</Label>
|
||||
<InfoTooltip content="Controls how submissions after the deadline are handled. 'Reject' blocks them, 'Flag' accepts but marks as late, 'Accept' treats them normally." />
|
||||
</div>
|
||||
<Select
|
||||
value={config.lateSubmissionPolicy ?? 'flag'}
|
||||
onValueChange={(value) =>
|
||||
@@ -108,7 +175,10 @@ export function IntakeSection({ config, onChange, isActive }: IntakeSectionProps
|
||||
|
||||
{(config.lateSubmissionPolicy ?? 'flag') === 'flag' && (
|
||||
<div className="space-y-2">
|
||||
<Label>Grace Period (hours)</Label>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Label>Grace Period (hours)</Label>
|
||||
<InfoTooltip content="Extra time after the deadline during which late submissions are still accepted but flagged." />
|
||||
</div>
|
||||
<Input
|
||||
type="number"
|
||||
min={0}
|
||||
@@ -125,7 +195,10 @@ export function IntakeSection({ config, onChange, isActive }: IntakeSectionProps
|
||||
{/* File Requirements */}
|
||||
<div className="space-y-3">
|
||||
<div className="flex items-center justify-between">
|
||||
<Label>File Requirements</Label>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<Label>File Requirements</Label>
|
||||
<InfoTooltip content="Define what files applicants must upload. Each requirement can specify accepted formats and size limits." />
|
||||
</div>
|
||||
<Button type="button" variant="outline" size="sm" onClick={addFileReq} disabled={isActive}>
|
||||
<Plus className="h-3.5 w-3.5 mr-1" />
|
||||
Add Requirement
|
||||
@@ -187,6 +260,14 @@ export function IntakeSection({ config, onChange, isActive }: IntakeSectionProps
|
||||
<Label className="text-xs">Required</Label>
|
||||
</div>
|
||||
</div>
|
||||
<div className="sm:col-span-2">
|
||||
<FileTypePicker
|
||||
value={req.acceptedMimeTypes}
|
||||
onChange={(mimeTypes) =>
|
||||
updateFileReq(index, { acceptedMimeTypes: mimeTypes })
|
||||
}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<Button
|
||||
type="button"
|
||||
|
||||
Reference in New Issue
Block a user