Skip to content

Commit ec91db0

Browse files
committed
schedule: lock all previous cells
1 parent af30185 commit ec91db0

File tree

1 file changed

+93
-15
lines changed

1 file changed

+93
-15
lines changed

src/components/schedule/SchedulePage.tsx

Lines changed: 93 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -123,21 +123,6 @@ const SchedulePage: React.FC = () => {
123123
setPhaseDurations(prev => ({ ...prev, [key]: Math.max(1, value) }));
124124
};
125125

126-
// Helper functions for locking/unlocking dates
127-
const lockDate = (fork: string, phaseId: string, itemName: string, date: string) => {
128-
const key = `${fork}:${phaseId}:${itemName}`;
129-
setLockedDates(prev => ({ ...prev, [key]: date }));
130-
};
131-
132-
const unlockDate = (fork: string, phaseId: string, itemName: string) => {
133-
const key = `${fork}:${phaseId}:${itemName}`;
134-
setLockedDates(prev => {
135-
const { [key]: _removed, ...rest } = prev;
136-
void _removed; // Intentionally discarding this value
137-
return rest;
138-
});
139-
};
140-
141126
// Get the effective date (locked value or calculated value)
142127
const getEffectiveDate = (fork: string, phaseId: string, itemName: string, calculatedDate: string): string => {
143128
const key = `${fork}:${phaseId}:${itemName}`;
@@ -198,6 +183,99 @@ const SchedulePage: React.FC = () => {
198183
[hekotaMainnetDate, hekotaDevnetCount, phaseDurations]
199184
);
200185

186+
// Get all milestones in chronological order for a fork
187+
const getMilestoneOrder = (fork: string): Array<{ phaseId: string; itemName: string }> => {
188+
const projection = fork === 'glamsterdam' ? dynamicGlamsterdamProjection : dynamicHekotaProjection;
189+
const devnetCount = fork === 'glamsterdam' ? glamsterdamDevnetCount : hekotaDevnetCount;
190+
191+
const milestones: Array<{ phaseId: string; itemName: string }> = [
192+
{ phaseId: 'headliner-selection', itemName: 'Proposal Deadline' },
193+
{ phaseId: 'headliner-selection', itemName: 'Selection Date' },
194+
{ phaseId: 'eip-selection', itemName: 'PFI Deadline' },
195+
{ phaseId: 'eip-selection', itemName: 'CFI Deadline' },
196+
];
197+
198+
// Add devnets
199+
for (let i = 0; i < devnetCount; i++) {
200+
milestones.push({ phaseId: 'development', itemName: `Devnet-${i}` });
201+
}
202+
203+
// Add testnets (skip Holesky as it's deprecated)
204+
const testnetPhase = projection.phases.find(p => p.phaseId === 'public-testnets');
205+
testnetPhase?.testnets?.forEach(testnet => {
206+
if (testnet.status !== 'deprecated') {
207+
milestones.push({ phaseId: 'public-testnets', itemName: testnet.name });
208+
}
209+
});
210+
211+
return milestones;
212+
};
213+
214+
// Get calculated date for a milestone from projections
215+
const getCalculatedDateForMilestone = (fork: string, phaseId: string, itemName: string): string => {
216+
const projection = fork === 'glamsterdam' ? dynamicGlamsterdamProjection : dynamicHekotaProjection;
217+
const phase = projection.phases.find(p => p.phaseId === phaseId);
218+
219+
if (phaseId === 'headliner-selection' || phaseId === 'eip-selection') {
220+
const substep = phase?.substeps?.find(s => s.name === itemName);
221+
return substep?.date || substep?.projectedDate || '';
222+
} else if (phaseId === 'development') {
223+
const devnet = phase?.devnets?.find(d => d.name === itemName);
224+
return devnet?.date || devnet?.projectedDate || '';
225+
} else if (phaseId === 'public-testnets') {
226+
const testnet = phase?.testnets?.find(t => t.name === itemName);
227+
return testnet?.date || testnet?.projectedDate || '';
228+
}
229+
return '';
230+
};
231+
232+
// Lock a date and cascade to all previous milestones
233+
const lockDate = (fork: string, phaseId: string, itemName: string, date: string) => {
234+
// Fusaka is read-only, no cascading needed
235+
if (fork === 'fusaka') {
236+
const key = `${fork}:${phaseId}:${itemName}`;
237+
setLockedDates(prev => ({ ...prev, [key]: date }));
238+
return;
239+
}
240+
241+
const milestones = getMilestoneOrder(fork);
242+
const targetIndex = milestones.findIndex(m => m.phaseId === phaseId && m.itemName === itemName);
243+
244+
if (targetIndex === -1) {
245+
// Fallback: just lock the single date
246+
const key = `${fork}:${phaseId}:${itemName}`;
247+
setLockedDates(prev => ({ ...prev, [key]: date }));
248+
return;
249+
}
250+
251+
// Lock all milestones from start up to and including the target
252+
setLockedDates(prev => {
253+
const newLocked = { ...prev };
254+
for (let i = 0; i <= targetIndex; i++) {
255+
const milestone = milestones[i];
256+
const key = `${fork}:${milestone.phaseId}:${milestone.itemName}`;
257+
// Only lock if not already locked
258+
if (!(key in newLocked)) {
259+
const calcDate = getCalculatedDateForMilestone(fork, milestone.phaseId, milestone.itemName);
260+
const effectiveDate = prev[key] ?? calcDate;
261+
newLocked[key] = effectiveDate;
262+
}
263+
}
264+
// Always set the target date to the specified value
265+
newLocked[`${fork}:${phaseId}:${itemName}`] = date;
266+
return newLocked;
267+
});
268+
};
269+
270+
const unlockDate = (fork: string, phaseId: string, itemName: string) => {
271+
const key = `${fork}:${phaseId}:${itemName}`;
272+
setLockedDates(prev => {
273+
const { [key]: _removed, ...rest } = prev;
274+
void _removed;
275+
return rest;
276+
});
277+
};
278+
201279
useMetaTags({
202280
title: 'ACD Planning Sandbox - Forkcast',
203281
description: 'Internal planning tool for Ethereum core developers. Explore hypothetical upgrade timelines - these are not committed dates.',

0 commit comments

Comments
 (0)