Add clone support for all entity types
Clone button on every card opens the editor in create mode pre-filled with copied data and a "(Copy)" name suffix. Cancelling discards the clone — entity is only persisted on Save. Supported: LED targets, color strip sources, KC targets, pattern templates, picture sources, capture templates, PP templates. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -210,7 +210,7 @@ function _renderSegmentRowInner(index, segment) {
|
||||
`;
|
||||
}
|
||||
|
||||
export async function showTargetEditor(targetId = null) {
|
||||
export async function showTargetEditor(targetId = null, cloneData = null) {
|
||||
try {
|
||||
// Load devices and CSS sources for dropdowns
|
||||
const [devicesResp, cssResp] = await Promise.all([
|
||||
@@ -263,6 +263,24 @@ export async function showTargetEditor(targetId = null) {
|
||||
} else {
|
||||
segments.forEach(seg => addTargetSegment(seg));
|
||||
}
|
||||
} else if (cloneData) {
|
||||
// Cloning — create mode but pre-filled from clone data
|
||||
document.getElementById('target-editor-id').value = '';
|
||||
document.getElementById('target-editor-name').value = (cloneData.name || '') + ' (Copy)';
|
||||
deviceSelect.value = cloneData.device_id || '';
|
||||
const fps = cloneData.fps ?? 30;
|
||||
document.getElementById('target-editor-fps').value = fps;
|
||||
document.getElementById('target-editor-fps-value').textContent = fps;
|
||||
document.getElementById('target-editor-keepalive-interval').value = cloneData.keepalive_interval ?? 1.0;
|
||||
document.getElementById('target-editor-keepalive-interval-value').textContent = cloneData.keepalive_interval ?? 1.0;
|
||||
document.getElementById('target-editor-title').textContent = t('targets.add');
|
||||
|
||||
const segments = cloneData.segments || [];
|
||||
if (segments.length === 0) {
|
||||
addTargetSegment();
|
||||
} else {
|
||||
segments.forEach(seg => addTargetSegment(seg));
|
||||
}
|
||||
} else {
|
||||
// Creating new target — start with one empty segment
|
||||
document.getElementById('target-editor-id').value = '';
|
||||
@@ -276,11 +294,11 @@ export async function showTargetEditor(targetId = null) {
|
||||
}
|
||||
|
||||
// Auto-name generation
|
||||
_targetNameManuallyEdited = !!targetId;
|
||||
_targetNameManuallyEdited = !!(targetId || cloneData);
|
||||
document.getElementById('target-editor-name').oninput = () => { _targetNameManuallyEdited = true; };
|
||||
window._targetAutoName = _autoGenerateTargetName;
|
||||
deviceSelect.onchange = () => { _updateDeviceInfo(); _updateKeepaliveVisibility(); _updateFpsRecommendation(); _autoGenerateTargetName(); };
|
||||
if (!targetId) _autoGenerateTargetName();
|
||||
if (!targetId && !cloneData) _autoGenerateTargetName();
|
||||
|
||||
// Show/hide standby interval based on selected device capabilities
|
||||
_updateDeviceInfo();
|
||||
@@ -735,6 +753,9 @@ export function createTargetCard(target, deviceMap, colorStripSourceMap) {
|
||||
▶️
|
||||
</button>
|
||||
`}
|
||||
<button class="btn btn-icon btn-secondary" onclick="cloneTarget('${target.id}')" title="${t('common.clone')}">
|
||||
📋
|
||||
</button>
|
||||
<button class="btn btn-icon btn-secondary" onclick="showTargetEditor('${target.id}')" title="${t('common.edit')}">
|
||||
✏️
|
||||
</button>
|
||||
@@ -819,6 +840,18 @@ export async function stopTargetOverlay(targetId) {
|
||||
});
|
||||
}
|
||||
|
||||
export async function cloneTarget(targetId) {
|
||||
try {
|
||||
const resp = await fetch(`${API_BASE}/picture-targets/${targetId}`, { headers: getHeaders() });
|
||||
if (!resp.ok) throw new Error('Failed to load target');
|
||||
const target = await resp.json();
|
||||
showTargetEditor(null, target);
|
||||
} catch (error) {
|
||||
console.error('Failed to clone target:', error);
|
||||
showToast('Failed to clone target', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
export async function deleteTarget(targetId) {
|
||||
const confirmed = await showConfirm(t('targets.delete.confirm'));
|
||||
if (!confirmed) return;
|
||||
|
||||
Reference in New Issue
Block a user