139 lines
3.9 KiB
JavaScript
139 lines
3.9 KiB
JavaScript
const REVIEW_STATES = new Set(['none', 'review', 'needsFix', 'approved']);
|
|
const REVIEW_COLUMNS = new Set(['review', 'approved']);
|
|
const REVIEW_LIFECYCLE_EVENTS = new Set([
|
|
'review_requested',
|
|
'review_changes_requested',
|
|
'review_approved',
|
|
'review_started',
|
|
]);
|
|
const REVIEW_RESET_STATUSES = new Set(['in_progress', 'deleted']);
|
|
|
|
function normalizeReviewState(value) {
|
|
const normalized = typeof value === 'string' && value.trim() ? value.trim() : '';
|
|
return REVIEW_STATES.has(normalized) ? normalized : 'none';
|
|
}
|
|
|
|
function eventReviewState(event) {
|
|
if (!event || typeof event !== 'object' || !REVIEW_LIFECYCLE_EVENTS.has(event.type)) {
|
|
return null;
|
|
}
|
|
return normalizeReviewState(event.to);
|
|
}
|
|
|
|
function derivePendingReviewState(events, startIndex) {
|
|
for (let index = startIndex - 1; index >= 0; index -= 1) {
|
|
const event = events[index];
|
|
if (!event || typeof event !== 'object') continue;
|
|
|
|
const reviewState = eventReviewState(event);
|
|
if (reviewState) {
|
|
return reviewState === 'needsFix'
|
|
? { state: 'needsFix', source: 'history_pending_needs_fix' }
|
|
: { state: 'none', source: 'history_pending_reset' };
|
|
}
|
|
|
|
if (
|
|
event.type === 'task_created' ||
|
|
(event.type === 'status_changed' &&
|
|
(REVIEW_RESET_STATUSES.has(event.to) || event.to === 'pending'))
|
|
) {
|
|
return { state: 'none', source: 'history_pending_reset' };
|
|
}
|
|
}
|
|
|
|
return { state: 'none', source: 'history_pending_reset' };
|
|
}
|
|
|
|
function getReviewStateFromHistory(task) {
|
|
const events = Array.isArray(task && task.historyEvents) ? task.historyEvents : [];
|
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
const event = events[index];
|
|
if (!event || typeof event !== 'object') continue;
|
|
|
|
const reviewState = eventReviewState(event);
|
|
if (reviewState) {
|
|
return {
|
|
state: reviewState,
|
|
source: `history_${event.type}`,
|
|
};
|
|
}
|
|
|
|
if (event.type === 'status_changed') {
|
|
if (REVIEW_RESET_STATUSES.has(event.to)) {
|
|
return {
|
|
state: 'none',
|
|
source: 'history_status_reset',
|
|
};
|
|
}
|
|
if (event.to === 'pending') {
|
|
return derivePendingReviewState(events, index);
|
|
}
|
|
}
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
function getEffectiveReviewState(task, kanbanEntry) {
|
|
const historyState = getReviewStateFromHistory(task);
|
|
if (historyState) {
|
|
return historyState;
|
|
}
|
|
|
|
const status = typeof task?.status === 'string' ? task.status.trim() : '';
|
|
const normalizeFallback = (state, source) => {
|
|
const normalized = normalizeReviewState(state);
|
|
if (normalized === 'none') {
|
|
return null;
|
|
}
|
|
|
|
if (status === 'in_progress' || status === 'deleted') {
|
|
return {
|
|
state: 'none',
|
|
source: `${source}_status_reset`,
|
|
};
|
|
}
|
|
|
|
if (status === 'pending') {
|
|
return normalized === 'needsFix'
|
|
? { state: 'needsFix', source: `${source}_pending_needs_fix` }
|
|
: { state: 'none', source: `${source}_pending_reset` };
|
|
}
|
|
|
|
if (status === 'completed') {
|
|
return normalized === 'review' || normalized === 'approved'
|
|
? { state: normalized, source }
|
|
: { state: 'none', source: `${source}_completed_reset` };
|
|
}
|
|
|
|
return { state: normalized, source };
|
|
};
|
|
|
|
const persisted = normalizeReviewState(task && task.reviewState);
|
|
const persistedFallback = normalizeFallback(persisted, 'task_review_state');
|
|
if (persistedFallback) {
|
|
return persistedFallback;
|
|
}
|
|
|
|
if (kanbanEntry && REVIEW_COLUMNS.has(kanbanEntry.column)) {
|
|
const kanbanFallback = normalizeFallback(kanbanEntry.column, 'kanban_column');
|
|
if (kanbanFallback) {
|
|
return kanbanFallback;
|
|
}
|
|
}
|
|
|
|
return {
|
|
state: 'none',
|
|
source: 'none',
|
|
};
|
|
}
|
|
|
|
module.exports = {
|
|
REVIEW_COLUMNS,
|
|
REVIEW_LIFECYCLE_EVENTS,
|
|
REVIEW_RESET_STATUSES,
|
|
REVIEW_STATES,
|
|
getEffectiveReviewState,
|
|
getReviewStateFromHistory,
|
|
normalizeReviewState,
|
|
};
|