Special awards: Rounds tab UI, auto-filter threshold, remove auto-tag rules
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m23s
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m23s
- Add Rounds tab to award detail page with create/list/delete functionality - Add "Entry point" badge on first award round (confirmShortlist routes here) - Fix round detail back-link to navigate to parent award when specialAwardId set - Filter award rounds out of competition round list - Add specialAwardId to competition getById round select - Warn on confirmShortlist when no award rounds exist (SEPARATE_POOL mode) - Remove auto-tag rules from award config, edit page, router, and AI service - Fix competitionId not passed when creating awards from competition context - Add AUTO_FILTER quality threshold to AI filtering dashboard Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,8 @@
|
||||
/**
|
||||
* AI-Powered Award Eligibility Service
|
||||
*
|
||||
* Determines project eligibility for special awards using:
|
||||
* - Deterministic field matching (tags, country, category)
|
||||
* - AI interpretation of plain-language criteria
|
||||
* Determines project eligibility for special awards using
|
||||
* AI interpretation of plain-language criteria.
|
||||
*
|
||||
* GDPR Compliance:
|
||||
* - All project data is anonymized before AI processing
|
||||
@@ -70,12 +69,6 @@ quality_score is a 0-100 integer measuring how well the project fits the award c
|
||||
|
||||
// ─── Types ──────────────────────────────────────────────────────────────────
|
||||
|
||||
export type AutoTagRule = {
|
||||
field: 'competitionCategory' | 'country' | 'geographicZone' | 'tags' | 'oceanIssue'
|
||||
operator: 'equals' | 'contains' | 'in'
|
||||
value: string | string[]
|
||||
}
|
||||
|
||||
export interface EligibilityResult {
|
||||
projectId: string
|
||||
eligible: boolean
|
||||
@@ -106,66 +99,6 @@ interface ProjectForEligibility {
|
||||
files?: Array<{ fileType: string | null }>
|
||||
}
|
||||
|
||||
// ─── Auto Tag Rules ─────────────────────────────────────────────────────────
|
||||
|
||||
export function applyAutoTagRules(
|
||||
rules: AutoTagRule[],
|
||||
projects: ProjectForEligibility[]
|
||||
): Map<string, boolean> {
|
||||
const results = new Map<string, boolean>()
|
||||
|
||||
for (const project of projects) {
|
||||
const matches = rules.every((rule) => {
|
||||
const fieldValue = getFieldValue(project, rule.field)
|
||||
|
||||
switch (rule.operator) {
|
||||
case 'equals':
|
||||
return String(fieldValue).toLowerCase() === String(rule.value).toLowerCase()
|
||||
case 'contains':
|
||||
if (Array.isArray(fieldValue)) {
|
||||
return fieldValue.some((v) =>
|
||||
String(v).toLowerCase().includes(String(rule.value).toLowerCase())
|
||||
)
|
||||
}
|
||||
return String(fieldValue || '').toLowerCase().includes(String(rule.value).toLowerCase())
|
||||
case 'in':
|
||||
if (Array.isArray(rule.value)) {
|
||||
return rule.value.some((v) =>
|
||||
String(v).toLowerCase() === String(fieldValue).toLowerCase()
|
||||
)
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
})
|
||||
|
||||
results.set(project.id, matches)
|
||||
}
|
||||
|
||||
return results
|
||||
}
|
||||
|
||||
function getFieldValue(
|
||||
project: ProjectForEligibility,
|
||||
field: AutoTagRule['field']
|
||||
): unknown {
|
||||
switch (field) {
|
||||
case 'competitionCategory':
|
||||
return project.competitionCategory
|
||||
case 'country':
|
||||
return project.country
|
||||
case 'geographicZone':
|
||||
return project.geographicZone
|
||||
case 'tags':
|
||||
return project.tags
|
||||
case 'oceanIssue':
|
||||
return project.oceanIssue
|
||||
default:
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
// ─── AI Criteria Interpretation ─────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user