chore(team): checkpoint follow-up frontend edits
This commit is contained in:
parent
651cfa1846
commit
88b3ea2358
2 changed files with 47 additions and 9 deletions
|
|
@ -66,19 +66,36 @@ function normalizeActorKey(value) {
|
|||
return typeof value === 'string' && value.trim() ? value.trim().toLowerCase() : '';
|
||||
}
|
||||
|
||||
function closeTimestampForInterval(interval, timestamp) {
|
||||
const startedAtMs = Date.parse(interval.startedAt);
|
||||
const timestampMs = Date.parse(timestamp);
|
||||
if (Number.isFinite(startedAtMs) && Number.isFinite(timestampMs) && timestampMs < startedAtMs) {
|
||||
return interval.startedAt;
|
||||
}
|
||||
return timestamp;
|
||||
}
|
||||
|
||||
function openReviewInterval(task, reviewer, timestamp = new Date().toISOString()) {
|
||||
const reviewerName = typeof reviewer === 'string' && reviewer.trim() ? reviewer.trim() : '';
|
||||
if (!reviewerName) return false;
|
||||
const reviewerKey = normalizeActorKey(reviewerName);
|
||||
const intervals = Array.isArray(task.reviewIntervals) ? [...task.reviewIntervals] : [];
|
||||
const hasOpenForReviewer = intervals.some(
|
||||
(interval) => !interval.completedAt && normalizeActorKey(interval.reviewer) === reviewerKey
|
||||
);
|
||||
let changed = false;
|
||||
let hasOpenForReviewer = false;
|
||||
const nextIntervals = intervals.map((interval) => {
|
||||
if (interval.completedAt) return interval;
|
||||
if (normalizeActorKey(interval.reviewer) === reviewerKey) {
|
||||
hasOpenForReviewer = true;
|
||||
return interval;
|
||||
}
|
||||
changed = true;
|
||||
return { ...interval, completedAt: closeTimestampForInterval(interval, timestamp) };
|
||||
});
|
||||
if (hasOpenForReviewer) {
|
||||
task.reviewIntervals = intervals;
|
||||
return false;
|
||||
task.reviewIntervals = nextIntervals;
|
||||
return changed;
|
||||
}
|
||||
task.reviewIntervals = [...intervals, { reviewer: reviewerName, startedAt: timestamp }];
|
||||
task.reviewIntervals = [...nextIntervals, { reviewer: reviewerName, startedAt: timestamp }];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -88,7 +105,7 @@ function closeReviewIntervals(task, timestamp = new Date().toISOString()) {
|
|||
task.reviewIntervals = task.reviewIntervals.map((interval) => {
|
||||
if (interval.completedAt) return interval;
|
||||
changed = true;
|
||||
return { ...interval, completedAt: timestamp };
|
||||
return { ...interval, completedAt: closeTimestampForInterval(interval, timestamp) };
|
||||
});
|
||||
return changed;
|
||||
}
|
||||
|
|
@ -315,7 +332,7 @@ function startReview(context, taskId, flags = {}) {
|
|||
});
|
||||
} else {
|
||||
tasks.updateTask(context, task.id, (t) => {
|
||||
openReviewInterval(t, existingActor);
|
||||
openReviewInterval(t, existingActor, latestReviewEvent.timestamp);
|
||||
return t;
|
||||
});
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,7 +1,9 @@
|
|||
import { getTasksBasePath } from '@main/utils/pathDecoder';
|
||||
import { getTasksBasePath, getTeamsBasePath } from '@main/utils/pathDecoder';
|
||||
import { createLogger } from '@shared/utils/logger';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
|
||||
import { withFileLockSync } from './fileLock';
|
||||
import { TeamTaskReader } from './TeamTaskReader';
|
||||
|
||||
import type {
|
||||
|
|
@ -20,6 +22,7 @@ type MutableTeamTask = TeamTask & {
|
|||
};
|
||||
|
||||
const CRASH_REPAIR_GRACE_MS = 5_000;
|
||||
const logger = createLogger('Service:TeamTaskActivityIntervalService');
|
||||
|
||||
function normalizeMemberName(value: string | null | undefined): string {
|
||||
return typeof value === 'string' && value.trim() ? value.trim().toLowerCase() : '';
|
||||
|
|
@ -142,6 +145,23 @@ export class TeamTaskActivityIntervalService {
|
|||
private mutateTeamTasks(
|
||||
teamName: string,
|
||||
mutate: (task: MutableTeamTask) => boolean
|
||||
): ActivityIntervalResult {
|
||||
const lockScope = path.join(getTeamsBasePath(), teamName, 'board-state');
|
||||
try {
|
||||
return withFileLockSync(lockScope, () => this.mutateTeamTasksUnlocked(teamName, mutate));
|
||||
} catch (error) {
|
||||
logger.warn(
|
||||
`[${teamName}] Failed to update task activity intervals: ${
|
||||
error instanceof Error ? error.message : String(error)
|
||||
}`
|
||||
);
|
||||
return { changedTasks: 0 };
|
||||
}
|
||||
}
|
||||
|
||||
private mutateTeamTasksUnlocked(
|
||||
teamName: string,
|
||||
mutate: (task: MutableTeamTask) => boolean
|
||||
): ActivityIntervalResult {
|
||||
const tasksDir = path.join(getTasksBasePath(), teamName);
|
||||
let entries: string[];
|
||||
|
|
@ -216,6 +236,7 @@ export class TeamTaskActivityIntervalService {
|
|||
|
||||
const activeReviewer = getActiveReviewActor(task);
|
||||
if (
|
||||
task.status === 'completed' &&
|
||||
activeReviewer &&
|
||||
normalizeMemberName(activeReviewer) === memberKey &&
|
||||
!hasOpenReviewInterval(task, activeReviewer)
|
||||
|
|
|
|||
Loading…
Reference in a new issue