+ {/* Autosave error bar — only shows when save fails */}
+ {autosaveStatus === 'error' && (
+
-
-
-
You have unsaved changes
+
-
- {autosaveStatus === 'error' && (
- Save failed — try again
+
+
)}
diff --git a/src/types/competition-configs.ts b/src/types/competition-configs.ts
index 59bb98a..9d7336f 100644
--- a/src/types/competition-configs.ts
+++ b/src/types/competition-configs.ts
@@ -8,9 +8,18 @@ import type { RoundType, AwardEligibilityMode, AwardScoringMode, AwardStatus } f
// These replace the loosely-typed pipeline-wizard.ts configs with
// Zod-validated, compile-time-safe contracts.
+// Shared fields that appear in "General Settings" for all round types
+const generalSettingsFields = {
+ startupAdvanceCount: z.number().int().nonnegative().optional(),
+ conceptAdvanceCount: z.number().int().nonnegative().optional(),
+ notifyOnEntry: z.boolean().default(false),
+ notifyOnAdvance: z.boolean().default(false),
+}
+
// ─── 1. IntakeConfig ─────────────────────────────────────────────────────────
export const IntakeConfigSchema = z.object({
+ ...generalSettingsFields,
allowDrafts: z.boolean().default(true),
draftExpiryDays: z.number().int().positive().default(30),
@@ -43,6 +52,7 @@ export type IntakeConfig = z.infer
// ─── 2. FilteringConfig ──────────────────────────────────────────────────────
export const FilteringConfigSchema = z.object({
+ ...generalSettingsFields,
rules: z
.array(
z.object({
@@ -71,10 +81,6 @@ export const FilteringConfigSchema = z.object({
batchSize: z.number().int().positive().default(20),
aiParseFiles: z.boolean().default(false),
- startupAdvanceCount: z.number().int().nonnegative().optional(),
- conceptAdvanceCount: z.number().int().nonnegative().optional(),
- notifyOnEntry: z.boolean().default(false),
- notifyOnAdvance: z.boolean().default(false),
})
export type FilteringConfig = z.infer
@@ -82,6 +88,7 @@ export type FilteringConfig = z.infer
// ─── 3. EvaluationConfig ─────────────────────────────────────────────────────
export const EvaluationConfigSchema = z.object({
+ ...generalSettingsFields,
requiredReviewsPerProject: z.number().int().positive().default(3),
scoringMode: z.enum(['criteria', 'global', 'binary']).default('criteria'),
@@ -121,6 +128,7 @@ export type EvaluationConfig = z.infer
// ─── 4. SubmissionConfig ─────────────────────────────────────────────────────
export const SubmissionConfigSchema = z.object({
+ ...generalSettingsFields,
eligibleStatuses: z
.array(
z.enum([
@@ -143,6 +151,7 @@ export type SubmissionConfig = z.infer
// ─── 5. MentoringConfig ──────────────────────────────────────────────────────
export const MentoringConfigSchema = z.object({
+ ...generalSettingsFields,
eligibility: z
.enum(['all_advancing', 'requested_only', 'admin_selected'])
.default('requested_only'),
@@ -161,6 +170,7 @@ export type MentoringConfig = z.infer
// ─── 6. LiveFinalConfig ──────────────────────────────────────────────────────
export const LiveFinalConfigSchema = z.object({
+ ...generalSettingsFields,
juryVotingEnabled: z.boolean().default(true),
votingMode: z.enum(['simple', 'criteria']).default('simple'),
@@ -193,6 +203,7 @@ export type LiveFinalConfig = z.infer
// ─── 7. DeliberationConfig ───────────────────────────────────────────────────
export const DeliberationConfigSchema = z.object({
+ ...generalSettingsFields,
juryGroupId: z.string(),
mode: z