|
1 | 1 | import { db, schema } from '$api/db/db'; |
2 | | -import { abilityBuilder, enum_, schemaBuilder, pubsub as rumblePubsub } from '$api/rumble'; |
| 2 | +import { abilityBuilder, enum_, query, schemaBuilder, pubsub as rumblePubsub } from '$api/rumble'; |
3 | 3 | import { basics } from './basics'; |
4 | 4 | import { isGlobalAdmin } from '$api/services/isAdminEmail'; |
5 | 5 | import { assertFindFirstExists, assertFirstEntryExists } from '@m1212e/rumble'; |
@@ -193,19 +193,28 @@ async function applyAmendmentToResolution( |
193 | 193 | case 'ALTER_POSITION': { |
194 | 194 | // Resolve current index from stable clause ID |
195 | 195 | const sourceIdx = findClauseIndex(resolution.operative, amendment.targetClauseId!); |
196 | | - const destIdx = amendment.targetPosition!; |
197 | | - if (destIdx < 0 || destIdx > resolution.operative.length) { |
| 196 | + const targetPosition = amendment.targetPosition!; |
| 197 | + // targetPosition follows the "insert after" convention: -1 means beginning |
| 198 | + if (targetPosition < -1 || targetPosition >= resolution.operative.length) { |
198 | 199 | throw new GraphQLError('Destination index out of range'); |
199 | 200 | } |
200 | 201 | const [clause] = resolution.operative.splice(sourceIdx, 1); |
201 | 202 | // After removing from source, the target index might shift |
202 | | - const adjustedDest = destIdx > sourceIdx ? destIdx - 1 : destIdx; |
203 | | - resolution.operative.splice(adjustedDest, 0, clause); |
| 203 | + const adjustedTargetPosition = |
| 204 | + targetPosition >= sourceIdx ? targetPosition - 1 : targetPosition; |
| 205 | + resolution.operative.splice(adjustedTargetPosition + 1, 0, clause); |
204 | 206 | // Adjust other pending amendments' targetPosition for the structural shift |
205 | 207 | // First: source removal shifts indices down |
206 | 208 | await adjustPendingPositions(tx, paper.id, amendment.id, 'decrement', sourceIdx, 'gt'); |
207 | 209 | // Then: destination insertion shifts indices up |
208 | | - await adjustPendingPositions(tx, paper.id, amendment.id, 'increment', adjustedDest, 'gte'); |
| 210 | + await adjustPendingPositions( |
| 211 | + tx, |
| 212 | + paper.id, |
| 213 | + amendment.id, |
| 214 | + 'increment', |
| 215 | + adjustedTargetPosition + 1, |
| 216 | + 'gte' |
| 217 | + ); |
209 | 218 | break; |
210 | 219 | } |
211 | 220 | } |
@@ -562,6 +571,10 @@ schemaBuilder.mutationFields((t) => ({ |
562 | 571 | .findFirst({ where: { id: amendment.paperId } }) |
563 | 572 | .then(assertFindFirstExists); |
564 | 573 |
|
| 574 | + if (paper.status !== 'AMENDMENT_PHASE') { |
| 575 | + throw new GraphQLError('Paper must be in AMENDMENT_PHASE to adopt amendments'); |
| 576 | + } |
| 577 | + |
565 | 578 | await assertCommitteeChairOrAdmin(ctx, paper.committeeId); |
566 | 579 |
|
567 | 580 | await db.transaction(async (tx) => { |
@@ -606,6 +619,10 @@ schemaBuilder.mutationFields((t) => ({ |
606 | 619 | .findFirst({ where: { id: amendment.paperId } }) |
607 | 620 | .then(assertFindFirstExists); |
608 | 621 |
|
| 622 | + if (paper.status !== 'AMENDMENT_PHASE') { |
| 623 | + throw new GraphQLError('Paper must be in AMENDMENT_PHASE to accept amendments'); |
| 624 | + } |
| 625 | + |
609 | 626 | await assertCommitteeChairOrAdmin(ctx, paper.committeeId); |
610 | 627 |
|
611 | 628 | await db.transaction(async (tx) => { |
|
0 commit comments