mirror of
https://github.com/pacnpal/thrilltrack-explorer.git
synced 2025-12-24 16:31:12 -05:00
Fix state machine issues
This commit is contained in:
@@ -3,11 +3,7 @@
|
||||
* Manages moderation workflow with type-safe state transitions and lock coordination
|
||||
*/
|
||||
|
||||
// Generic review data interface for moderation
|
||||
export interface ModerationReviewData {
|
||||
id: string;
|
||||
[key: string]: unknown;
|
||||
}
|
||||
import type { SubmissionItemWithDeps } from './submissionItemsService';
|
||||
|
||||
// State definitions using discriminated unions
|
||||
export type ModerationState =
|
||||
@@ -15,7 +11,7 @@ export type ModerationState =
|
||||
| { status: 'claiming'; itemId: string }
|
||||
| { status: 'locked'; itemId: string; lockExpires: string }
|
||||
| { status: 'loading_data'; itemId: string; lockExpires: string }
|
||||
| { status: 'reviewing'; itemId: string; lockExpires: string; reviewData: ModerationReviewData[] }
|
||||
| { status: 'reviewing'; itemId: string; lockExpires: string; reviewData: SubmissionItemWithDeps[] }
|
||||
| { status: 'approving'; itemId: string }
|
||||
| { status: 'rejecting'; itemId: string }
|
||||
| { status: 'complete'; itemId: string; result: 'approved' | 'rejected' }
|
||||
@@ -28,7 +24,7 @@ export type ModerationAction =
|
||||
| { type: 'LOCK_ACQUIRED'; payload: { lockExpires: string } }
|
||||
| { type: 'LOCK_EXPIRED' }
|
||||
| { type: 'LOAD_DATA' }
|
||||
| { type: 'DATA_LOADED'; payload: { reviewData: ModerationReviewData[] } }
|
||||
| { type: 'DATA_LOADED'; payload: { reviewData: SubmissionItemWithDeps[] } }
|
||||
| { type: 'START_APPROVAL' }
|
||||
| { type: 'START_REJECTION' }
|
||||
| { type: 'COMPLETE'; payload: { result: 'approved' | 'rejected' } }
|
||||
@@ -54,9 +50,14 @@ export function moderationReducer(
|
||||
if (state.status !== 'claiming') {
|
||||
throw new Error(`Illegal transition: ${state.status} → locked`);
|
||||
}
|
||||
// Validate lock expiry date
|
||||
const lockDate = new Date(action.payload.lockExpires);
|
||||
if (isNaN(lockDate.getTime())) {
|
||||
throw new Error('Invalid lock expiry date');
|
||||
}
|
||||
return {
|
||||
status: 'locked',
|
||||
itemId: (state as Extract<ModerationState, { status: 'claiming' }>).itemId,
|
||||
itemId: state.itemId,
|
||||
lockExpires: action.payload.lockExpires
|
||||
};
|
||||
|
||||
@@ -67,7 +68,7 @@ export function moderationReducer(
|
||||
}
|
||||
return {
|
||||
status: 'lock_expired',
|
||||
itemId: (state as Extract<ModerationState, { status: 'locked' | 'reviewing' | 'loading_data' }>).itemId
|
||||
itemId: state.itemId
|
||||
};
|
||||
|
||||
case 'LOAD_DATA':
|
||||
@@ -76,8 +77,8 @@ export function moderationReducer(
|
||||
}
|
||||
return {
|
||||
status: 'loading_data',
|
||||
itemId: (state as Extract<ModerationState, { status: 'locked' }>).itemId,
|
||||
lockExpires: (state as Extract<ModerationState, { status: 'locked' }>).lockExpires
|
||||
itemId: state.itemId,
|
||||
lockExpires: state.lockExpires
|
||||
};
|
||||
|
||||
case 'DATA_LOADED':
|
||||
@@ -86,8 +87,8 @@ export function moderationReducer(
|
||||
}
|
||||
return {
|
||||
status: 'reviewing',
|
||||
itemId: (state as Extract<ModerationState, { status: 'loading_data' }>).itemId,
|
||||
lockExpires: (state as Extract<ModerationState, { status: 'loading_data' }>).lockExpires,
|
||||
itemId: state.itemId,
|
||||
lockExpires: state.lockExpires,
|
||||
reviewData: action.payload.reviewData
|
||||
};
|
||||
|
||||
@@ -97,7 +98,7 @@ export function moderationReducer(
|
||||
}
|
||||
return {
|
||||
status: 'approving',
|
||||
itemId: (state as Extract<ModerationState, { status: 'reviewing' }>).itemId
|
||||
itemId: state.itemId
|
||||
};
|
||||
|
||||
case 'START_REJECTION':
|
||||
@@ -106,7 +107,7 @@ export function moderationReducer(
|
||||
}
|
||||
return {
|
||||
status: 'rejecting',
|
||||
itemId: (state as Extract<ModerationState, { status: 'reviewing' }>).itemId
|
||||
itemId: state.itemId
|
||||
};
|
||||
|
||||
case 'COMPLETE':
|
||||
@@ -115,7 +116,7 @@ export function moderationReducer(
|
||||
}
|
||||
return {
|
||||
status: 'complete',
|
||||
itemId: (state as Extract<ModerationState, { status: 'approving' | 'rejecting' }>).itemId,
|
||||
itemId: state.itemId,
|
||||
result: action.payload.result
|
||||
};
|
||||
|
||||
@@ -127,7 +128,7 @@ export function moderationReducer(
|
||||
}
|
||||
return {
|
||||
status: 'error',
|
||||
itemId: (state as Extract<ModerationState, { itemId: string }>).itemId,
|
||||
itemId: state.itemId,
|
||||
error: action.payload.error
|
||||
};
|
||||
|
||||
@@ -183,7 +184,7 @@ export function hasActiveLock(state: ModerationState): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
const lockExpires = new Date((state as Extract<ModerationState, { lockExpires: string }>).lockExpires);
|
||||
const lockExpires = new Date(state.lockExpires);
|
||||
return lockExpires > new Date();
|
||||
}
|
||||
|
||||
@@ -196,7 +197,7 @@ export function needsLockRenewal(state: ModerationState): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
const lockExpires = new Date((state as Extract<ModerationState, { lockExpires: string }>).lockExpires);
|
||||
const lockExpires = new Date(state.lockExpires);
|
||||
const now = new Date();
|
||||
const timeUntilExpiry = lockExpires.getTime() - now.getTime();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user