feat: enhance project search to include all criteria, add AI tag generation button
- ProjectStatesTable local search now covers country, institution, competitionCategory, geographicZone - project-pool.ts DB search extended to institution, country, geographicZone, team member names - AwardShortlist eligibility table gains a search input filtering by title, team, country, institution, category - IndividualAssignmentsTable project filter extended to include country and institution - Add "Generate AI Tags" dropdown item per row in ProjectStatesTable using tag.tagProject mutation Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { useState, useMemo } from 'react'
|
||||
import { trpc } from '@/lib/trpc/client'
|
||||
import { toast } from 'sonner'
|
||||
import { Button } from '@/components/ui/button'
|
||||
import { Input } from '@/components/ui/input'
|
||||
import { Badge } from '@/components/ui/badge'
|
||||
import { Checkbox } from '@/components/ui/checkbox'
|
||||
import { Progress } from '@/components/ui/progress'
|
||||
@@ -32,6 +33,7 @@ import {
|
||||
Play,
|
||||
Trophy,
|
||||
AlertTriangle,
|
||||
Search,
|
||||
} from 'lucide-react'
|
||||
|
||||
type AwardShortlistProps = {
|
||||
@@ -58,6 +60,7 @@ export function AwardShortlist({
|
||||
jobDone,
|
||||
}: AwardShortlistProps) {
|
||||
const [expanded, setExpanded] = useState(false)
|
||||
const [eligibilitySearch, setEligibilitySearch] = useState('')
|
||||
const utils = trpc.useUtils()
|
||||
|
||||
const isRunning = jobStatus === 'PENDING' || jobStatus === 'PROCESSING'
|
||||
@@ -124,6 +127,19 @@ export function AwardShortlist({
|
||||
? Math.round(((currentJobDone ?? 0) / currentJobTotal) * 100)
|
||||
: 0
|
||||
|
||||
const filteredEligibilities = useMemo(() => {
|
||||
if (!shortlist) return []
|
||||
if (!eligibilitySearch.trim()) return shortlist.eligibilities
|
||||
const q = eligibilitySearch.toLowerCase()
|
||||
return shortlist.eligibilities.filter((e: any) =>
|
||||
(e.project?.title || '').toLowerCase().includes(q) ||
|
||||
(e.project?.teamName || '').toLowerCase().includes(q) ||
|
||||
(e.project?.country || '').toLowerCase().includes(q) ||
|
||||
(e.project?.institution || '').toLowerCase().includes(q) ||
|
||||
(e.project?.competitionCategory || '').toLowerCase().includes(q)
|
||||
)
|
||||
}, [shortlist, eligibilitySearch])
|
||||
|
||||
const shortlistedCount = shortlist?.eligibilities?.filter((e) => e.shortlisted).length ?? 0
|
||||
const allShortlisted = shortlist && shortlist.eligibilities.length > 0 && shortlist.eligibilities.every((e) => e.shortlisted)
|
||||
const someShortlisted = shortlistedCount > 0 && !allShortlisted
|
||||
@@ -266,6 +282,18 @@ export function AwardShortlist({
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="mb-3">
|
||||
<div className="relative">
|
||||
<Search className="absolute left-2.5 top-2.5 h-4 w-4 text-muted-foreground" />
|
||||
<Input
|
||||
placeholder="Search projects..."
|
||||
value={eligibilitySearch}
|
||||
onChange={(e) => setEligibilitySearch(e.target.value)}
|
||||
className="pl-9 h-9 max-w-sm"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="border rounded-md overflow-hidden">
|
||||
<table className="w-full text-sm">
|
||||
<thead className="bg-muted/50">
|
||||
@@ -288,7 +316,7 @@ export function AwardShortlist({
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{shortlist.eligibilities.map((e, i) => {
|
||||
{filteredEligibilities.map((e, i) => {
|
||||
const reasoning = (e.aiReasoningJson as Record<string, unknown>)?.reasoning as string | undefined
|
||||
const isTop5 = i < shortlistSize
|
||||
return (
|
||||
|
||||
Reference in New Issue
Block a user