Merge pull request #368 from shenlvkang-collab/pr/mobile-add-case-submit

fix(mobile): give the Add Case modal a reachable submit button
This commit is contained in:
Ark0N
2026-09-06 23:02:45 +02:00
committed by GitHub
4 changed files with 55 additions and 15 deletions
+7
View File
@@ -2589,8 +2589,15 @@
<div class="modal-content modal-lg set-shell">
<div class="modal-header set-shell-head">
<h3>Add Case</h3>
<!-- mobile.css hides this modal's .set-foot, so on a phone the footer's
Create/Link button is unreachable and the modal cannot be submitted
at all. Mirrors the Settings modal's header Save: close first in the
DOM so the focus trap still lands on it, row-reverse puts this to
its left. Both buttons are driven together by switchCaseModalTab()
and submitCaseModal(). -->
<div class="set-head-actions">
<button class="modal-close" onclick="app.closeCreateCaseModal()" aria-label="Close create case">&times;</button>
<button class="set-head-save" id="caseModalSubmitMobile" onclick="app.submitCaseModal()">Create</button>
</div>
</div>
<div class="set-body">
+4 -2
View File
@@ -3333,8 +3333,10 @@ html:is([data-skin="paper-gray"], [data-skin="solarized-light"], [data-skin="cat
recessed tray and matching pill geometry instead of reading as a fat
accent pill parked beside a stray × glyph. Tray colors come from skin
tokens, never a hardcoded black alpha, or the light skins get a grey slab.
`:has()` keeps the tray off the two sheets that carry a lone × (Session
Options and Add Case save from inside their own forms). */
`:has()` keeps the tray off Session Options, the one sheet left carrying a
lone × because it saves from inside its own per-section forms. Add Case
now has a header save of its own (its footer is hidden below 860px, so
that button is the only way to submit it there), and picks up the tray. */
:is(#appSettingsModal, #sessionOptionsModal, #createCaseModal) .set-head-actions:has(.set-head-save) {
padding: 3px;
border: 1px solid var(--border);
+28 -13
View File
@@ -2550,15 +2550,19 @@ Object.assign(CodemanApp.prototype, {
// A switched-to panel starts at its own top.
const doc = document.getElementById('createCaseDoc');
if (doc) doc.scrollTop = 0;
// Update submit button (hide for manage tab)
const submitBtn = document.getElementById('caseModalSubmit');
// Update submit buttons (hide for manage tab). Two of them: mobile.css hides
// this modal's .set-foot, so phones submit through the header button instead.
const submitBtns = ['caseModalSubmit', 'caseModalSubmitMobile']
.map((id) => document.getElementById(id))
.filter(Boolean);
if (tabName === 'case-manage') {
submitBtn.style.display = 'none';
submitBtns.forEach((btn) => {
btn.style.display = 'none';
});
this.renderCaseManageList();
this.refreshDockerExports();
} else {
submitBtn.style.display = '';
submitBtn.textContent =
const label =
tabName === 'case-create'
? 'Create'
: tabName === 'case-clone'
@@ -2568,6 +2572,10 @@ Object.assign(CodemanApp.prototype, {
: tabName === 'case-docker'
? 'Link Docker'
: 'Link';
submitBtns.forEach((btn) => {
btn.style.display = '';
btn.textContent = label;
});
}
// Focus appropriate input
if (tabName === 'case-create') {
@@ -2588,14 +2596,19 @@ Object.assign(CodemanApp.prototype, {
},
async submitCaseModal() {
const btn = document.getElementById('caseModalSubmit');
const originalText = btn.textContent;
btn.classList.add('loading');
btn.textContent =
// Both submit buttons move together: whichever one the user pressed, the
// other must show the same pending state and be equally unclickable.
const btns = ['caseModalSubmit', 'caseModalSubmitMobile'].map((id) => document.getElementById(id)).filter(Boolean);
const originalText = btns.map((btn) => btn.textContent);
const pendingText =
this.caseModalTab === 'case-create' ? 'Creating...' : this.caseModalTab === 'case-clone' ? 'Cloning...' : 'Linking...';
// A clone holds this request open for minutes; without disabling the button a
// second click fires a second clone (the loser then fails on ALREADY_EXISTS).
btn.disabled = true;
btns.forEach((btn) => {
btn.classList.add('loading');
btn.textContent = pendingText;
btn.disabled = true;
});
try {
if (this.caseModalTab === 'case-create') {
await this.createCase();
@@ -2609,9 +2622,11 @@ Object.assign(CodemanApp.prototype, {
await this.linkCase();
}
} finally {
btn.classList.remove('loading');
btn.disabled = false;
btn.textContent = originalText;
btns.forEach((btn, index) => {
btn.classList.remove('loading');
btn.disabled = false;
btn.textContent = originalText[index];
});
}
},