Development: Allow applicants to unsubmit a submitted application - #2515
Development: Allow applicants to unsubmit a submitted application#2515az108 wants to merge 13 commits into
Development: Allow applicants to unsubmit a submitted application#2515Conversation
Replace the irreversible "Withdraw" action on submitted applications with a forgiving "Unsubmit" that reverts the application to draft, provided the job deadline has not passed. Applicants who clicked Withdraw to fix a typo would previously lock themselves out of re-applying; with this change they can edit and resubmit until the deadline. - ApplicationService.withdrawApplication now sets the state back to SAVED instead of WITHDRAWN, refuses any state other than SENT, and refuses after the job's endDate has passed. No withdrawal email is sent. - UI button + dialog + toast text changed across EN/DE; eligibility is restricted to SENT so the action is hidden once a professor starts a review. - Endpoint path and Java method name kept as "withdraw" to avoid a client/OpenAPI rename; the user-facing terminology change is what issue #1523 asked for. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Up to standards ✅🟢 Issues
|
…plication # Conflicts: # src/main/webapp/i18n/en/button.json
|
🤖 No OpenAPI or client changes needed. |
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🤖 No OpenAPI or client changes needed. |
Stray entry from a merge conflict — nothing references `button.update`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🤖 No OpenAPI or client changes needed. |
1 similar comment
|
🤖 No OpenAPI or client changes needed. |
|
📊 Server Test Coverage Too Low 🔍 View coverage locally: ./gradlew test jacocoTestReport
open build/reports/jacoco/test/html/index.html🌐 View coverage from GitHub: |
Resolved conflicts in i18n/de/global.json and i18n/en/global.json for the errorWithdrawingApplication toast; kept the unsubmit-specific deadline message added on this branch. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
🤖 No OpenAPI or client changes needed. |
|
There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions. |
|
There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions. |
|
There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions. |
Up to standards ✅🟢 Issues
|
| Metric | Results |
|---|---|
| Complexity | 13 |
NEW Get contextual insights on your PRs based on Codacy's metrics, along with PR and Jira context, without leaving GitHub. Enable AI reviewer
TIP This summary will be updated as you push new changes.
|
🤖 No OpenAPI or client changes needed. |
|
There hasn't been any activity on this pull request recently. Therefore, this pull request has been automatically marked as stale and will be closed if no further activity occurs within seven days. Thank you for your contributions. |
|
🤖 No OpenAPI or client changes needed. |
|
🤖 No OpenAPI or client changes needed. |
Resolve the conflict in ApplicationService#withdrawApplication, where this branch and main changed the same method in incompatible ways: - this branch repurposes it from "withdraw" to "unsubmit" (SENT -> SAVED, deadline guard, no email) - main added reference-request cancellation plus a withdrawal email to the old withdraw behaviour Kept this branch's unsubmit semantics, since the feature intentionally replaces withdrawal with a reversible move back to draft. Main's cancellation is deliberately not carried into the unsubmit flow: it flips pending requests to CANCELLED, but dispatchInvitations only re-invites entries that are ADDED with no token, so those referees would never be re-invited on resubmit. An applicant unsubmitting to fix a typo would lose their reference requests for good, after referees were told the letter was no longer needed. With withdrawal gone, cancelPendingForWithdrawnApplication and its sendCancellationEmail helper had no remaining caller and were removed, along with the WithdrawCancellation tests covering them. ReferenceRequestStatus CANCELLED and EmailType REFERENCE_LETTER_CANCELLED are kept: both are still part of the API contract and rendered by the client and PDF export. Co-Authored-By: Claude <noreply@anthropic.com>
|
🤖 No OpenAPI or client changes needed. |
…flow Reinstates cancelPendingForWithdrawnApplication and sendCancellationEmail together with their tests, and calls the cancellation again from the unsubmit flow. Both files are byte-identical to main again, so the feature is back exactly as it was written. Removing it here was the wrong call. The reasoning at the time only weighed the applicant who unsubmits and resubmits shortly after; it did not account for the applicant who never resubmits, whose referees would then never hear anything. One consequence is still open and needs a decision rather than a guess. dispatchInvitations only re-invites entries that are ADDED with no token, while cancelling leaves the entry CANCELLED with its token set, so a referee cancelled by an unsubmit is not invited again if the application is resubmitted. Separately, findReminderCandidates filters on request status alone and never looks at the application state, so referees attached to an application sitting in draft keep receiving reminders. Both are recorded here so the follow-up decision is made deliberately. Verified: 786 server tests pass, including the two restored cancellation tests. Co-Authored-By: Claude <noreply@anthropic.com>
|
🤖 No OpenAPI or client changes needed. |
… submitted again Settles the withdraw behaviour discussed in review. There is one button, called Withdraw, available while the application is still SENT and therefore not once it is in review. It puts the application back into draft and cancels the pending reference requests, which is what makes the links already emailed to the recommenders stop working. Submitting again now sends those recommenders a fresh request. The cancellation and the dead links were already in place. The missing piece was the second submission: dispatchInvitations only issued a token for entries that had never been invited, so a recommender cancelled by a withdrawal was skipped and never heard from us again. It now also re-issues for cancelled entries, with a new token so the pre-withdrawal link stays dead. Recommenders who already submitted, declined or let their link expire are still skipped, so a second submission never asks the same person twice. Naming goes back to Withdraw across the button, dialog and toasts. The wording does not go back with it: the pre-branch text claimed the action could not be undone, which is no longer true, so it now says the application returns to draft, that the existing recommendation links stop working, and that submitting again sends a new request. The submit confirmation warns about that re-invitation when it applies. The component already holds the reference requests, so the check needs no extra request and mirrors the server condition exactly. Its base message also claimed that submitting made further edits impossible, which withdrawing now disproves, so that has been corrected too. Verified: 788 server tests, 2010 client tests, typecheck, eslint with no errors, a11y lint, the production build and prettier. Co-Authored-By: Claude <noreply@anthropic.com>
|
🤖 No OpenAPI or client changes needed. |
|
🤖 No OpenAPI or client changes needed. |
Checklist
General
Server
Client
Motivation and Context
Closes #1523, implementing the issue's Option B / "Forgiving" approach.
Today, "Withdraw" is irreversible: once an applicant clicks it the application moves to a terminal
WITHDRAWNstate and the server refuses to let them apply to the same position again. Issue #1523 observed that applicants treat Withdraw as an "Undo" button (typically to fix a typo and resubmit), so the current design silently disqualifies users who were trying to correct their submission.Option B: turn Withdraw into Unsubmit — a revertible action that puts the application back to
SAVED(draft), as long as the job's deadline has not passed. The applicant can then edit and resubmit.Description
Server
ApplicationService.withdrawApplication(src/main/java/de/tum/cit/aet/application/service/ApplicationService.java):SENT(throwsOperationNotAllowedException).endDateis in the past.SAVEDinstead ofWITHDRAWN.APPLICATION_WITHDRAWNemail — the action is now "undo submission", not a permanent goodbye.PUT /api/applications/withdraw/{id}) and Java method name are unchanged, so no OpenAPI regen / generated-client churn is needed. The user-facing terminology rename is what the issue asked for; renaming the internal API is out of scope.Client
[SENT, IN_REVIEW]toSENTonly, so once a professor starts reviewing the action disappears and the applicant can't accidentally yank an in-progress evaluation back.endDateavailable — relying on the server reject + a friendly error message keeps the diff small; a follow-up could addendDatetoJobCardDTOand hide the button entirely past the deadline).Email
APPLICATION_WITHDRAWNemail type is no longer dispatched. The enum value and template files are left in place so nothing else has to move; they simply stop being used.Non-goals (deliberately out of scope)
WITHDRAWNenum value fromApplicationState.APPLICATION_WITHDRAWN.htmlandAPPLICATION_WITHDRAWN_subject.htmltemplate files.WITHDRAWNrows (prod DB is wiped per current state; nothing to migrate).endDateto client-visible DTOs so the Unsubmit button can be hidden past the deadline (server still enforces correctness; this is a follow-up UX polish).Steps for Testing
Prerequisites:
Test (happy path):
Test (state guard):
Test (deadline guard):
Repeat steps 1–6 on the application overview list page (where the same action lives in the row menu).
Review Progress
Code Review
Manual Tests
Test Coverage
Client
Server
Last updated: 2026-07-28 18:18:24 UTC