All source files · Workbench
sanity/schemaTypes/documents.ts
import { defineField, defineType } from 'sanity';
const sourceReferences = () => defineField({
name: 'sources', title: 'Evidence sources', type: 'array',
of: [{ type: 'reference', to: [{ type: 'evidenceSource' }] }],
validation: (rule) => rule.required().min(1),
});
const competitionReference = () => defineField({
name: 'competition', type: 'reference', to: [{ type: 'competition' }], validation: (rule) => rule.required(),
});
const simulationFlag = () => defineField({
name: 'isSimulated', title: 'Simulated example', type: 'boolean', initialValue: false,
description: 'Enable for invented training scenarios. These must never be presented as organizer changes.',
});
const fieldKey = () => defineField({
name: 'fieldKey', title: 'Field being checked', type: 'string',
options: { list: ['deadlineUtc', 'startUtc', 'eligibility', 'aiPolicy', 'prizeDescription', 'noSpendPolicy', 'payoutTiming'] },
validation: (rule) => rule.required(),
});
export const competition = defineType({
name: 'competition', title: 'Competition / planning window', type: 'document',
fields: [
defineField({ name: 'title', type: 'string', validation: (rule) => rule.required() }),
defineField({ name: 'slug', type: 'slug', options: { source: 'title' }, validation: (rule) => rule.required() }),
defineField({ name: 'organizer', type: 'string', validation: (rule) => rule.required() }),
defineField({ name: 'summary', type: 'text', rows: 3 }),
defineField({ name: 'url', title: 'Official challenge URL', type: 'url', validation: (rule) => rule.required().uri({ scheme: ['https'] }) }),
defineField({ name: 'startUtc', title: 'Official start (UTC)', type: 'datetime', validation: (rule) => rule.required() }),
defineField({ name: 'deadlineUtc', title: 'Accepted deadline (UTC)', type: 'datetime', validation: (rule) => rule.required(), description: 'Review source revisions before changing this accepted value.' }),
defineField({ name: 'deadlineLabel', title: 'Deadline as published', type: 'string', validation: (rule) => rule.required() }),
defineField({ name: 'prizeAmountUsd', title: 'Top individual cash prize (USD)', type: 'number', validation: (rule) => rule.min(0) }),
defineField({ name: 'prizeDescription', type: 'text', rows: 2 }),
defineField({ name: 'winnerAnnouncement', type: 'string' }),
defineField({ name: 'eligibility', type: 'text', rows: 2 }),
defineField({ name: 'aiPolicy', type: 'text', rows: 2 }),
defineField({ name: 'noSpendPolicy', type: 'text', rows: 2 }),
defineField({ name: 'payoutTiming', type: 'text', rows: 2 }),
defineField({ name: 'paymentMechanism', type: 'string' }),
defineField({ name: 'ipSummary', type: 'text', rows: 3 }),
defineField({ name: 'verificationStatus', type: 'string', initialValue: 'needsReview', options: { list: ['verified', 'needsReview', 'unverified'] } }),
defineField({ name: 'verifiedAt', title: 'Sources checked at', type: 'datetime' }),
defineField({ name: 'buildHours', title: 'Estimated build hours', type: 'number', validation: (rule) => rule.min(0), description: 'Planning estimate, not an organizer requirement or completion guarantee.' }),
defineField({ name: 'humanMinutes', title: 'Estimated human review minutes', type: 'number', validation: (rule) => rule.min(0) }),
defineField({ name: 'dependencies', title: 'Planning dependencies (estimates)', type: 'array', of: [{ type: 'string' }] }),
defineField({ name: 'availableSince', title: 'Planning window available since', type: 'datetime' }),
defineField({ name: 'effortEstimateNote', type: 'string', initialValue: 'Illustrative planning estimates; adjust to your project and available time.' }),
sourceReferences(),
],
preview: { select: { title: 'title', subtitle: 'deadlineLabel' } },
});
export const evidenceSource = defineType({
name: 'evidenceSource', title: 'Evidence source', type: 'document',
fields: [
defineField({ name: 'title', type: 'string', validation: (rule) => rule.required() }),
defineField({ name: 'url', type: 'url', validation: (rule) => rule.uri({ scheme: ['https'] }) }),
defineField({ name: 'publisher', type: 'string', validation: (rule) => rule.required() }),
defineField({ name: 'kind', type: 'string', options: { list: ['officialRules', 'officialChallenge', 'officialDocumentation', 'simulation'] }, validation: (rule) => rule.required() }),
defineField({ name: 'checkedAt', type: 'datetime', validation: (rule) => rule.required() }),
defineField({ name: 'summary', title: 'Brief factual extract / observation', type: 'text', rows: 3, validation: (rule) => rule.required() }),
simulationFlag(),
],
validation: (rule) => rule.custom((document: any) => document?.isSimulated || document?.url ? true : 'Official sources need a source URL.'),
preview: { select: { title: 'title', subtitle: 'publisher' } },
});
export const sourceClaim = defineType({
name: 'sourceClaim', title: 'Source claim / conflict', type: 'document',
fields: [
competitionReference(), fieldKey(), sourceReferences(),
defineField({ name: 'reportedValue', title: 'Value as reported', type: 'text', rows: 2, validation: (rule) => rule.required() }),
defineField({ name: 'normalizedValue', title: 'Normalized value for comparison', type: 'string' }),
defineField({ name: 'state', type: 'string', options: { list: ['consistent', 'conflicting', 'superseded', 'unverified'] }, initialValue: 'unverified', validation: (rule) => rule.required() }),
defineField({ name: 'severity', type: 'string', options: { list: ['info', 'warning', 'critical'] }, initialValue: 'info' }),
defineField({ name: 'observedAt', type: 'datetime', validation: (rule) => rule.required() }),
defineField({ name: 'note', type: 'text', rows: 3 }), simulationFlag(),
],
preview: { select: { title: 'fieldKey', subtitle: 'reportedValue' } },
});
export const evidenceRevision = defineType({
name: 'evidenceRevision', title: 'Evidence revision to review', type: 'document',
fields: [
competitionReference(), fieldKey(), sourceReferences(),
defineField({ name: 'previousValue', type: 'string', validation: (rule) => rule.required() }),
defineField({ name: 'proposedValue', type: 'string', validation: (rule) => rule.required() }),
defineField({ name: 'observedAt', type: 'datetime', validation: (rule) => rule.required() }),
defineField({ name: 'status', type: 'string', options: { list: ['pendingReview', 'approved', 'rejected'] }, initialValue: 'pendingReview', validation: (rule) => rule.required() }),
defineField({ name: 'impact', title: 'Effect on the planned window', type: 'text', rows: 2 }),
defineField({ name: 'description', type: 'text', rows: 3 }), simulationFlag(),
],
preview: { select: { title: 'fieldKey', subtitle: 'status' } },
});
export const reviewDecision = defineType({
name: 'reviewDecision', title: 'Approval / rejection record', type: 'document',
fields: [
competitionReference(),
defineField({ name: 'revision', type: 'reference', to: [{ type: 'evidenceRevision' }], validation: (rule) => rule.required() }),
defineField({ name: 'decision', type: 'string', options: { list: ['pending', 'approved', 'rejected'] }, initialValue: 'pending', validation: (rule) => rule.required() }),
defineField({ name: 'decisionNote', title: 'Reason and evidence checked', type: 'text', rows: 3, validation: (rule) => rule.required() }),
defineField({ name: 'reviewerLabel', title: 'Public role label', type: 'string', initialValue: 'Reviewer', description: 'Use a role label. This public dataset must not store a real name, email, ID, or financial details.', validation: (rule) => rule.required() }),
defineField({ name: 'decidedAt', type: 'datetime' }), simulationFlag(),
],
validation: (rule) => rule.custom((document: any) => document?.decision === 'pending' || document?.decidedAt ? true : 'Approved/rejected records need a decision timestamp.'),
preview: { select: { title: 'decision', subtitle: 'decisionNote' } },
});
export const projectNote = defineType({
name: 'projectNote', title: 'Project / planning note', type: 'document',
fields: [
defineField({ name: 'title', type: 'string', validation: (rule) => rule.required() }),
defineField({ name: 'body', type: 'text', rows: 6, validation: (rule) => rule.required() }),
defineField({ name: 'category', type: 'string', options: { list: ['planning', 'verification', 'simulation', 'setup'] }, validation: (rule) => rule.required() }),
defineField({ name: 'competition', type: 'reference', to: [{ type: 'competition' }] }),
defineField({ name: 'updatedAt', type: 'datetime', validation: (rule) => rule.required() }), simulationFlag(),
],
preview: { select: { title: 'title', subtitle: 'category' } },
});