diff --git a/docs/Notes/notes-prototype-1.md b/docs/Notes/notes-prototype-1.md index e69de29..d6bddba 100644 --- a/docs/Notes/notes-prototype-1.md +++ b/docs/Notes/notes-prototype-1.md @@ -0,0 +1,15 @@ +Notes: Filtering Round: Criteria- Older than 3 years (for all in the startup category), those who submit something random (like a spam project) (AI?) + +-Add filters to the page (who sent documents, etc.) + +-Partners section should be a semi-crm system to track possible sponsors and partners + +-No translation into french (no localization) + +-Ameliorate the user experience (make it more simple) + +-Special Awards- Specific Jury Members (jury members will have to choose amongst projects that fit specific criteria (like the country they're based))- Spotlight on Africa, Coup de Coeur - Make a special award section and make a special case for judge invitation for special awards and allow us to make special awards and assign them to the selection of judges for the special awards specifically (And give them their own login space to see everything) +-Invite jury member (with tag for special awards) --> Make special award (Criteria needed, Add a tag for special award (so for example, if a location is italy it will auto have the tag for COup de Coeur (since it's criteria is it only exists in certain countries))) - This is also a separate Jury Round - Use AI to sort through elligible projects based on the plain-language criteria (so the AI interprets the criteria and all projects and smart-assigns them to the round) - Make sure this round allows them to simply choose which project will win (since they have independent criteria) - Make a mix of voting for which project wins the project (or recommended project for the award), but also in the round they can simply assign a project to the award without any criteria requirements and such + + + diff --git a/public/images/MOPC-white-small.png b/public/images/MOPC-white-small.png new file mode 100644 index 0000000..ca27e62 Binary files /dev/null and b/public/images/MOPC-white-small.png differ diff --git a/public/images/ocean.png b/public/images/ocean.png new file mode 100644 index 0000000..e9261e9 Binary files /dev/null and b/public/images/ocean.png differ diff --git a/src/app/(admin)/admin/rounds/[id]/page.tsx b/src/app/(admin)/admin/rounds/[id]/page.tsx index 617a56e..3ff016b 100644 --- a/src/app/(admin)/admin/rounds/[id]/page.tsx +++ b/src/app/(admin)/admin/rounds/[id]/page.tsx @@ -47,6 +47,10 @@ import { Plus, ArrowRightCircle, Minus, + XCircle, + AlertTriangle, + ListChecks, + ClipboardCheck, } from 'lucide-react' import { toast } from 'sonner' import { AssignProjectsDialog } from '@/components/admin/assign-projects-dialog' @@ -64,9 +68,23 @@ function RoundDetailContent({ roundId }: { roundId: string }) { const [advanceOpen, setAdvanceOpen] = useState(false) const [removeOpen, setRemoveOpen] = useState(false) - const { data: round, isLoading } = trpc.round.get.useQuery({ id: roundId }) + const { data: round, isLoading, refetch: refetchRound } = trpc.round.get.useQuery({ id: roundId }) const { data: progress } = trpc.round.getProgress.useQuery({ id: roundId }) + // Filtering queries (only fetch for FILTERING rounds) + const roundType = (round?.settingsJson as { roundType?: string } | null)?.roundType + const isFilteringRound = roundType === 'FILTERING' + + const { data: filteringStats, refetch: refetchFilteringStats } = + trpc.filtering.getResultStats.useQuery( + { roundId }, + { enabled: isFilteringRound } + ) + const { data: filteringRules } = trpc.filtering.getRules.useQuery( + { roundId }, + { enabled: isFilteringRound } + ) + const utils = trpc.useUtils() const updateStatus = trpc.round.updateStatus.useMutation({ onSuccess: () => { @@ -85,6 +103,40 @@ function RoundDetailContent({ roundId }: { roundId: string }) { }, }) + // Filtering mutations + const executeRules = trpc.filtering.executeRules.useMutation() + const finalizeResults = trpc.filtering.finalizeResults.useMutation() + + const handleExecuteFiltering = async () => { + try { + const result = await executeRules.mutateAsync({ roundId }) + toast.success( + `Filtering complete: ${result.passed} passed, ${result.filteredOut} filtered out, ${result.flagged} flagged` + ) + refetchFilteringStats() + } catch (error) { + toast.error( + error instanceof Error ? error.message : 'Failed to execute filtering' + ) + } + } + + const handleFinalizeFiltering = async () => { + try { + const result = await finalizeResults.mutateAsync({ roundId }) + toast.success( + `Finalized: ${result.passed} passed, ${result.filteredOut} filtered out` + ) + refetchFilteringStats() + refetchRound() + utils.project.list.invalidate() + } catch (error) { + toast.error( + error instanceof Error ? error.message : 'Failed to finalize' + ) + } + } + if (isLoading) { return } @@ -403,6 +455,128 @@ function RoundDetailContent({ roundId }: { roundId: string }) { + {/* Filtering Section (for FILTERING rounds) */} + {isFilteringRound && ( + + +
+
+ + + Project Filtering + + + Run automated screening rules on projects in this round + +
+
+ +
+
+
+ + {/* Stats */} + {filteringStats && filteringStats.total > 0 ? ( +
+
+
+ +
+
+

{filteringStats.total}

+

Total

+
+
+
+
+ +
+
+

+ {filteringStats.passed} +

+

Passed

+
+
+
+
+ +
+
+

+ {filteringStats.filteredOut} +

+

Filtered Out

+
+
+
+
+ +
+
+

+ {filteringStats.flagged} +

+

Flagged

+
+
+
+ ) : ( +
+ +

No filtering results yet

+

+ Configure rules and run filtering to screen projects +

+
+ )} + + {/* Quick links */} +
+ + + {filteringStats && filteringStats.total > 0 && ( + + )} +
+
+
+ )} + {/* Quick Actions */} @@ -416,12 +590,14 @@ function RoundDetailContent({ roundId }: { roundId: string }) { Import Projects - + {!isFilteringRound && ( + + )}