mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-11-15 02:49:33 +01:00
feat(cases): rename in the app's own dialog instead of browser prompts
This commit is contained in:
1 parent
ab0bc0aecd
commit
201603e57b
3 files changed
+237
-73
No files matched your search
@@ -3792,6 +3792,35 @@
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rename a case (and its folder): opened by the Manage tab's pencil button, see renameCase() in session-ui.js.
|
||||
Placed after the case modal so it stacks above it. -->
|
||||
<div class="modal" id="renameCaseModal">
|
||||
<div class="modal-backdrop" onclick="app.closeRenameCase()"></div>
|
||||
<div class="modal-content modal-sm">
|
||||
<div class="modal-header">
|
||||
<h3>Rename case</h3>
|
||||
<button class="modal-close" onclick="app.closeRenameCase()" aria-label="Cancel">×</button>
|
||||
</div>
|
||||
<div class="modal-body">
|
||||
<div class="form-row">
|
||||
<label for="renameCaseInput" id="renameCaseLabel">New name</label>
|
||||
<input type="text" id="renameCaseInput" class="set-input" maxlength="100" autocomplete="off" spellcheck="false"
|
||||
placeholder="letters, numbers, - and _" onkeydown="if (event.key === 'Enter') app.submitRenameCase()">
|
||||
</div>
|
||||
<div class="form-row hidden" id="renameCaseFolderRow">
|
||||
<label class="checkbox-row"><input type="checkbox" id="renameCaseFolder"> Also rename the folder on disk</label>
|
||||
<span class="form-hint" id="renameCaseFolderHint" data-i18n-skip></span>
|
||||
</div>
|
||||
<p class="form-hint" id="renameCaseNote">A case under the cases folder is renamed together with its folder.</p>
|
||||
<p class="form-hint hidden" id="renameCaseError" role="alert" style="color: var(--danger, #e5534b);"></p>
|
||||
</div>
|
||||
<div class="modal-footer">
|
||||
<button class="btn-toolbar" onclick="app.closeRenameCase()">Cancel</button>
|
||||
<button class="btn-toolbar btn-primary" id="renameCaseSubmit" onclick="app.submitRenameCase()">Rename</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Mobile Case Picker (Bottom Sheet) -->
|
||||
<div class="modal mobile-case-picker" id="mobileCasePickerModal">
|
||||
<div class="modal-backdrop" onclick="app.closeMobileCasePicker()"></div>
|
||||
|
||||
@@ -4996,48 +4996,86 @@ Object.assign(CodemanApp.prototype, {
|
||||
},
|
||||
|
||||
/**
|
||||
* Rename a case and its folder (POST /api/cases/:name/rename). A linked case asks whether the real folder
|
||||
* should be renamed too (Cancel renames only the alias). When live sessions have the folder open the server
|
||||
* refuses and lists them; they are only stopped after a second, explicit confirmation.
|
||||
* Rename a case and its folder (POST /api/cases/:name/rename), in the app's own dialog.
|
||||
*
|
||||
* A linked case shows a checkbox for renaming the real folder too (unticked = the name only). When live
|
||||
* sessions have the folder open the server refuses and lists them; the dialog then lists them and its button
|
||||
* becomes "Stop sessions and rename", so they are only stopped after a second, explicit press. Errors from
|
||||
* the server (a taken name, a folder another program has open) stay inside the dialog so the name can be fixed.
|
||||
*/
|
||||
async renameCase(name) {
|
||||
renameCase(name) {
|
||||
const info = (this.cases || []).find((c) => c.name === name);
|
||||
const entered = prompt(`Rename case "${name}" to (letters, numbers, - and _):`, name);
|
||||
const newName = (entered || '').trim();
|
||||
if (!newName || newName === name) return;
|
||||
this._renameCaseState = { name, linked: Boolean(info?.linked), stop: false, busy: false };
|
||||
const input = document.getElementById('renameCaseInput');
|
||||
const label = document.getElementById('renameCaseLabel');
|
||||
if (label) label.textContent = `New name for "${name}"`;
|
||||
if (input) input.value = name;
|
||||
const row = document.getElementById('renameCaseFolderRow');
|
||||
row?.classList.toggle('hidden', !info?.linked);
|
||||
const box = document.getElementById('renameCaseFolder');
|
||||
if (box) box.checked = false;
|
||||
const hint = document.getElementById('renameCaseFolderHint');
|
||||
if (hint) hint.textContent = info?.linked ? `${info.path} (unticked: only the name in this list changes)` : '';
|
||||
document.getElementById('renameCaseNote')?.classList.toggle('hidden', Boolean(info?.linked));
|
||||
this._renameCaseNotice('');
|
||||
const submit = document.getElementById('renameCaseSubmit');
|
||||
if (submit) submit.textContent = 'Rename';
|
||||
document.getElementById('renameCaseModal')?.classList.add('active');
|
||||
input?.focus?.();
|
||||
input?.select?.();
|
||||
},
|
||||
|
||||
closeRenameCase() {
|
||||
document.getElementById('renameCaseModal')?.classList.remove('active');
|
||||
this._renameCaseState = null;
|
||||
},
|
||||
|
||||
/** Show (or clear, with '') a message inside the rename dialog. */
|
||||
_renameCaseNotice(message) {
|
||||
const el = document.getElementById('renameCaseError');
|
||||
if (!el) return;
|
||||
el.textContent = message;
|
||||
el.classList.toggle('hidden', !message);
|
||||
},
|
||||
|
||||
async submitRenameCase() {
|
||||
const state = this._renameCaseState;
|
||||
if (!state || state.busy) return;
|
||||
const name = state.name;
|
||||
const newName = (document.getElementById('renameCaseInput')?.value || '').trim();
|
||||
if (!newName || newName === name) {
|
||||
this._renameCaseNotice('Enter a different name.');
|
||||
return;
|
||||
}
|
||||
if (!/^[a-zA-Z0-9_-]+$/.test(newName)) {
|
||||
this.showToast('Use only letters, numbers, hyphens and underscores', 'error');
|
||||
this._renameCaseNotice('Use only letters, numbers, hyphens and underscores.');
|
||||
return;
|
||||
}
|
||||
const body = { newName };
|
||||
if (info?.linked) {
|
||||
body.renameFolder = confirm(
|
||||
`Also rename the folder on disk?\n\n${info.path}\n\nOK renames the folder to "${newName}" next to it. ` +
|
||||
`Cancel keeps the folder and only changes the case name.`
|
||||
);
|
||||
}
|
||||
const send = async () => {
|
||||
if (state.linked) body.renameFolder = Boolean(document.getElementById('renameCaseFolder')?.checked);
|
||||
if (state.stop) body.stopSessions = true;
|
||||
state.busy = true;
|
||||
const submit = document.getElementById('renameCaseSubmit');
|
||||
if (submit) submit.disabled = true;
|
||||
try {
|
||||
const res = await fetch(`/api/cases/${encodeURIComponent(name)}/rename`, {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(body),
|
||||
});
|
||||
return res.json().catch(() => ({ success: false, error: 'Unexpected response' }));
|
||||
};
|
||||
try {
|
||||
let data = await send();
|
||||
const data = await res.json().catch(() => ({ success: false, error: 'Unexpected response' }));
|
||||
if (data.requiresStop) {
|
||||
const list = (data.sessions || []).map((s) => ` - ${s.name}`).join('\n');
|
||||
if (!confirm(`These sessions have the folder open and will be stopped (nothing is restarted):\n\n${list}\n\nStop them and rename?`)) {
|
||||
return;
|
||||
}
|
||||
body.stopSessions = true;
|
||||
data = await send();
|
||||
}
|
||||
if (!data.success) {
|
||||
this.showToast(data.error || 'Failed to rename case', 'error');
|
||||
const list = (data.sessions || []).map((s) => `${s.name}`).join(', ');
|
||||
state.stop = true;
|
||||
this._renameCaseNotice(`These sessions have the folder open: ${list}. They will be stopped (nothing is restarted) before the folder is renamed.`);
|
||||
if (submit) submit.textContent = 'Stop sessions and rename';
|
||||
return;
|
||||
}
|
||||
if (!data.success) {
|
||||
this._renameCaseNotice(data.error || 'Failed to rename the case.');
|
||||
return;
|
||||
}
|
||||
this.closeRenameCase();
|
||||
this.showToast(`Case renamed to "${newName}"`, 'success');
|
||||
// The per-case run options are stored under the case name in this browser.
|
||||
try {
|
||||
@@ -5052,7 +5090,10 @@ Object.assign(CodemanApp.prototype, {
|
||||
await this.loadQuickStartCases(current === name ? newName : current);
|
||||
this.renderCaseManageList();
|
||||
} catch (err) {
|
||||
this.showToast('Failed to rename case: ' + err.message, 'error');
|
||||
this._renameCaseNotice('Failed to rename the case: ' + err.message);
|
||||
} finally {
|
||||
state.busy = false;
|
||||
if (submit) submit.disabled = false;
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
Reference in new issue
Block a user