added a move system for the pins
This commit is contained in:
parent
b98207b118
commit
7989ea07c4
@ -111,6 +111,61 @@ body {
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
/* Move Controls */
|
||||
.move-controls {
|
||||
position: fixed;
|
||||
top: 20px;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
background: white;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||||
z-index: 10000;
|
||||
min-width: 300px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
/* Mobile-specific move controls */
|
||||
@media (max-width: 768px) {
|
||||
.move-controls {
|
||||
top: auto;
|
||||
bottom: 20px;
|
||||
left: 10px;
|
||||
right: 10px;
|
||||
transform: none;
|
||||
width: auto;
|
||||
min-width: unset;
|
||||
max-width: 100%;
|
||||
padding: 15px;
|
||||
z-index: 10001; /* Ensure it's on top */
|
||||
background: white; /* Add background */
|
||||
box-shadow: 0 -2px 10px rgba(0,0,0,0.1); /* Add shadow for visibility */
|
||||
}
|
||||
|
||||
.move-controls-content h3 {
|
||||
font-size: 18px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.move-controls-content p {
|
||||
font-size: 14px;
|
||||
margin: 3px 0;
|
||||
}
|
||||
|
||||
.move-controls-actions {
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.move-controls-actions .btn {
|
||||
width: 100%;
|
||||
padding: 12px 16px;
|
||||
font-size: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Buttons */
|
||||
.btn {
|
||||
padding: 10px 16px;
|
||||
@ -986,3 +1041,96 @@ path.leaflet-interactive {
|
||||
stroke-width: 2px !important;
|
||||
fill-opacity: 0.8 !important;
|
||||
}
|
||||
|
||||
/* Marker being moved */
|
||||
.location-marker.leaflet-drag-target {
|
||||
cursor: move !important;
|
||||
}
|
||||
|
||||
/* Popup actions buttons spacing */
|
||||
.popup-actions {
|
||||
display: flex;
|
||||
gap: 5px;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* Pulsing animation for marker being moved */
|
||||
@keyframes pulse-marker {
|
||||
0% {
|
||||
transform: scale(1);
|
||||
opacity: 0.9;
|
||||
}
|
||||
50% {
|
||||
transform: scale(1.2);
|
||||
opacity: 1;
|
||||
}
|
||||
100% {
|
||||
transform: scale(1);
|
||||
opacity: 0.9;
|
||||
}
|
||||
}
|
||||
|
||||
/* Ensure marker animations work */
|
||||
.leaflet-overlay-pane svg path {
|
||||
transform-origin: center;
|
||||
}
|
||||
|
||||
/* Move confirmation popup styles */
|
||||
.move-confirm-popup-wrapper .leaflet-popup-content-wrapper {
|
||||
background: white;
|
||||
border: 2px solid var(--primary-color);
|
||||
box-shadow: 0 4px 12px rgba(0,0,0,0.2);
|
||||
}
|
||||
|
||||
.move-confirm-popup {
|
||||
text-align: center;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.move-confirm-popup h3 {
|
||||
margin: 0 0 10px 0;
|
||||
color: var(--dark-color);
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.move-confirm-popup p {
|
||||
margin: 0 0 15px 0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.move-confirm-popup .popup-actions {
|
||||
display: flex;
|
||||
gap: 10px;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
/* Cancel move button styles */
|
||||
#cancel-move-btn,
|
||||
#mobile-cancel-move-btn {
|
||||
background-color: var(--danger-color);
|
||||
color: white;
|
||||
}
|
||||
|
||||
#cancel-move-btn:hover,
|
||||
#mobile-cancel-move-btn:hover {
|
||||
background-color: #c0392b;
|
||||
}
|
||||
|
||||
/* Ensure crosshairs are visible during move */
|
||||
.crosshair {
|
||||
z-index: 1100;
|
||||
}
|
||||
|
||||
/* Mobile-friendly popup buttons */
|
||||
@media (max-width: 768px) {
|
||||
.popup-content .popup-actions .btn {
|
||||
padding: 10px 12px;
|
||||
font-size: 14px;
|
||||
min-height: 44px; /* Ensure touch-friendly size */
|
||||
}
|
||||
|
||||
.move-confirm-popup .popup-actions .btn {
|
||||
min-width: 100px;
|
||||
min-height: 44px;
|
||||
}
|
||||
}
|
||||
|
||||
@ -7,6 +7,12 @@ import { resetAddressConfirmation } from './ui-controls.js';
|
||||
export let markers = [];
|
||||
export let currentEditingLocation = null;
|
||||
|
||||
// Add these variables at the top with other exports
|
||||
export let isMovingMarker = false;
|
||||
export let movingMarker = null;
|
||||
export let originalPosition = null;
|
||||
export let movingLocationData = null;
|
||||
|
||||
export async function loadLocations() {
|
||||
try {
|
||||
const response = await fetch('/api/locations');
|
||||
@ -96,12 +102,69 @@ function createLocationMarker(location) {
|
||||
weight: 2,
|
||||
opacity: 1,
|
||||
fillOpacity: 0.8,
|
||||
className: 'location-marker' // Add a class for CSS targeting
|
||||
className: 'location-marker'
|
||||
});
|
||||
|
||||
// Add to map
|
||||
marker.addTo(map);
|
||||
|
||||
// Add custom dragging functionality for circle markers
|
||||
let _draggingEnabled = false;
|
||||
let isDragging = false;
|
||||
let dragStartLatLng = null;
|
||||
|
||||
marker.dragging = {
|
||||
enabled: function() {
|
||||
return _draggingEnabled;
|
||||
},
|
||||
enable: function() {
|
||||
_draggingEnabled = true;
|
||||
marker.on('mousedown', startDrag);
|
||||
marker._path.style.cursor = 'move';
|
||||
},
|
||||
disable: function() {
|
||||
_draggingEnabled = false;
|
||||
marker.off('mousedown', startDrag);
|
||||
marker.off('mousemove', drag);
|
||||
marker.off('mouseup', endDrag);
|
||||
map.off('mousemove', drag);
|
||||
map.off('mouseup', endDrag);
|
||||
marker._path.style.cursor = 'pointer';
|
||||
}
|
||||
};
|
||||
|
||||
function startDrag(e) {
|
||||
if (!_draggingEnabled) return;
|
||||
isDragging = true;
|
||||
dragStartLatLng = e.latlng;
|
||||
map.dragging.disable();
|
||||
marker.on('mousemove', drag);
|
||||
map.on('mousemove', drag);
|
||||
marker.on('mouseup', endDrag);
|
||||
map.on('mouseup', endDrag);
|
||||
L.DomEvent.stopPropagation(e);
|
||||
L.DomEvent.preventDefault(e);
|
||||
}
|
||||
|
||||
function drag(e) {
|
||||
if (!isDragging) return;
|
||||
marker.setLatLng(e.latlng);
|
||||
L.DomEvent.stopPropagation(e);
|
||||
L.DomEvent.preventDefault(e);
|
||||
}
|
||||
|
||||
function endDrag(e) {
|
||||
if (!isDragging) return;
|
||||
isDragging = false;
|
||||
map.dragging.enable();
|
||||
marker.off('mousemove', drag);
|
||||
map.off('mousemove', drag);
|
||||
marker.off('mouseup', endDrag);
|
||||
map.off('mouseup', endDrag);
|
||||
L.DomEvent.stopPropagation(e);
|
||||
L.DomEvent.preventDefault(e);
|
||||
}
|
||||
|
||||
const popupContent = createPopupContent(location);
|
||||
marker.bindPopup(popupContent);
|
||||
marker._locationData = location;
|
||||
@ -120,6 +183,9 @@ function createPopupContent(location) {
|
||||
const supportLevel = location['Support Level'] ?
|
||||
`Level ${location['Support Level']}` : 'Not specified';
|
||||
|
||||
// Add debugging
|
||||
console.log('Creating popup for location:', locationId, location);
|
||||
|
||||
return `
|
||||
<div class="popup-content">
|
||||
<h3>${escapeHtml(name)}</h3>
|
||||
@ -136,6 +202,10 @@ function createPopupContent(location) {
|
||||
data-location='${escapeHtml(JSON.stringify(location))}'>
|
||||
✏️ Edit
|
||||
</button>
|
||||
<button class="btn btn-primary btn-sm move-location-popup-btn"
|
||||
data-location='${escapeHtml(JSON.stringify(location))}'>
|
||||
📍 Move
|
||||
</button>
|
||||
</div>
|
||||
` : ''}
|
||||
</div>
|
||||
@ -388,3 +458,198 @@ export function openAddModal(lat, lng) {
|
||||
});
|
||||
document.dispatchEvent(autoLookupEvent);
|
||||
}
|
||||
|
||||
// Replace the startMovingMarker function
|
||||
export function startMovingMarker(location, marker) {
|
||||
console.log('startMovingMarker called with:', location, marker);
|
||||
|
||||
if (!location) {
|
||||
console.error('Missing location data');
|
||||
return;
|
||||
}
|
||||
|
||||
const locationId = location.Id || location.id || location.ID || location._id;
|
||||
if (!locationId) {
|
||||
showStatus('Error: Location ID not found', 'error');
|
||||
return;
|
||||
}
|
||||
|
||||
// Store the location data and original position
|
||||
movingLocationData = location;
|
||||
originalPosition = marker ? marker.getLatLng() : null;
|
||||
isMovingMarker = true;
|
||||
|
||||
// Close any popups
|
||||
map.closePopup();
|
||||
|
||||
// Show crosshairs
|
||||
const crosshair = document.getElementById('crosshair');
|
||||
const crosshairInfo = crosshair.querySelector('.crosshair-info');
|
||||
crosshairInfo.textContent = 'Click to move location here';
|
||||
crosshair.classList.remove('hidden');
|
||||
|
||||
// Update buttons to show cancel state - use event system
|
||||
document.dispatchEvent(new CustomEvent('updateMoveButtons', { detail: { isMoving: true } }));
|
||||
|
||||
// Show instructions
|
||||
const name = [location['First Name'], location['Last Name']]
|
||||
.filter(Boolean).join(' ') || 'Location';
|
||||
showStatus(`Moving "${name}". Click anywhere on the map to set new position.`, 'info');
|
||||
|
||||
// Add click handler to map
|
||||
map.on('click', handleMoveMapClick);
|
||||
|
||||
// Highlight the marker being moved
|
||||
if (marker) {
|
||||
movingMarker = marker;
|
||||
marker.setStyle({
|
||||
fillColor: '#ff7800',
|
||||
fillOpacity: 0.9,
|
||||
radius: 10
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Add new function to handle map clicks during move
|
||||
function handleMoveMapClick(e) {
|
||||
if (!isMovingMarker || !movingLocationData) return;
|
||||
|
||||
const { lat, lng } = e.latlng;
|
||||
|
||||
// Show confirmation dialog
|
||||
showMoveConfirmation(lat, lng);
|
||||
}
|
||||
|
||||
// Add function to show move confirmation
|
||||
function showMoveConfirmation(lat, lng) {
|
||||
const name = [movingLocationData['First Name'], movingLocationData['Last Name']]
|
||||
.filter(Boolean).join(' ') || 'Location';
|
||||
|
||||
// Create a temporary marker at the new position
|
||||
const tempMarker = L.circleMarker([lat, lng], {
|
||||
radius: 10,
|
||||
fillColor: '#ff7800',
|
||||
color: '#fff',
|
||||
weight: 3,
|
||||
opacity: 1,
|
||||
fillOpacity: 0.5
|
||||
}).addTo(map);
|
||||
|
||||
// Create confirmation popup
|
||||
const confirmContent = `
|
||||
<div class="move-confirm-popup">
|
||||
<h3>Confirm Move</h3>
|
||||
<p>Move "${escapeHtml(name)}" to this location?</p>
|
||||
<div class="popup-actions">
|
||||
<button id="confirm-move-btn" class="btn btn-primary btn-sm">✓ Confirm</button>
|
||||
<button id="cancel-move-btn" class="btn btn-secondary btn-sm">✕ Cancel</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
tempMarker.bindPopup(confirmContent, {
|
||||
closeButton: false,
|
||||
className: 'move-confirm-popup-wrapper'
|
||||
}).openPopup();
|
||||
|
||||
// Add event listeners
|
||||
setTimeout(() => {
|
||||
document.getElementById('confirm-move-btn')?.addEventListener('click', async () => {
|
||||
map.removeLayer(tempMarker);
|
||||
await saveMovePosition(lat, lng);
|
||||
});
|
||||
|
||||
document.getElementById('cancel-move-btn')?.addEventListener('click', () => {
|
||||
map.removeLayer(tempMarker);
|
||||
// Continue move mode
|
||||
});
|
||||
}, 100);
|
||||
}
|
||||
|
||||
// Update saveMovePosition to accept coordinates
|
||||
async function saveMovePosition(lat, lng) {
|
||||
if (!movingLocationData) return;
|
||||
|
||||
const locationId = movingLocationData.Id || movingLocationData.id ||
|
||||
movingLocationData.ID || movingLocationData._id;
|
||||
|
||||
if (!locationId) {
|
||||
showStatus('Error: Location ID not found', 'error');
|
||||
cancelMove();
|
||||
return;
|
||||
}
|
||||
|
||||
// Prepare update data
|
||||
const updateData = {
|
||||
latitude: lat.toFixed(8),
|
||||
longitude: lng.toFixed(8),
|
||||
'Geo-Location': `${lat.toFixed(8)};${lng.toFixed(8)}`
|
||||
};
|
||||
|
||||
try {
|
||||
const response = await fetch(`/api/locations/${locationId}`, {
|
||||
method: 'PUT',
|
||||
headers: {
|
||||
'Content-Type': 'application/json'
|
||||
},
|
||||
body: JSON.stringify(updateData)
|
||||
});
|
||||
|
||||
const result = await response.json();
|
||||
|
||||
if (result.success) {
|
||||
showStatus('Location moved successfully!', 'success');
|
||||
cleanupMoveState(); // Use cleanup instead of cancelMove
|
||||
|
||||
// Reload locations to update the map
|
||||
await loadLocations();
|
||||
} else {
|
||||
throw new Error(result.error || 'Failed to update location');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('Error moving location:', error);
|
||||
showStatus(`Failed to move location: ${error.message}`, 'error');
|
||||
cancelMove();
|
||||
}
|
||||
}
|
||||
|
||||
// Add new function to clean up move state without showing cancellation message
|
||||
function cleanupMoveState() {
|
||||
// Hide crosshairs
|
||||
const crosshair = document.getElementById('crosshair');
|
||||
crosshair.classList.add('hidden');
|
||||
|
||||
// Remove map click handler
|
||||
map.off('click', handleMoveMapClick);
|
||||
|
||||
// Reset buttons - use event system
|
||||
document.dispatchEvent(new CustomEvent('updateMoveButtons', { detail: { isMoving: false } }));
|
||||
|
||||
// Reset marker style if exists
|
||||
if (movingMarker && originalPosition) {
|
||||
// Restore original marker style based on support level
|
||||
const location = movingMarker._locationData;
|
||||
let markerColor = '#3388ff';
|
||||
if (location && location['Support Level']) {
|
||||
const level = parseInt(location['Support Level']);
|
||||
switch(level) {
|
||||
case 1: markerColor = '#27ae60'; break;
|
||||
case 2: markerColor = '#f1c40f'; break;
|
||||
case 3: markerColor = '#e67e22'; break;
|
||||
case 4: markerColor = '#e74c3c'; break;
|
||||
}
|
||||
}
|
||||
|
||||
movingMarker.setStyle({
|
||||
fillColor: markerColor,
|
||||
fillOpacity: 0.8,
|
||||
radius: 8
|
||||
});
|
||||
}
|
||||
|
||||
// Reset state
|
||||
isMovingMarker = false;
|
||||
movingMarker = null;
|
||||
originalPosition = null;
|
||||
movingLocationData = null;
|
||||
}
|
||||
@ -8,6 +8,9 @@ export let isAddingLocation = false;
|
||||
export let isAddressConfirmed = false;
|
||||
export let isEditAddressConfirmed = false;
|
||||
|
||||
// Add this after the existing imports
|
||||
let isMoveMode = false;
|
||||
|
||||
// Export function to get current confirmation states
|
||||
export function getAddressConfirmationState() {
|
||||
return {
|
||||
@ -509,5 +512,178 @@ export function setupEventListeners() {
|
||||
showStatus('Error opening edit form', 'error');
|
||||
}
|
||||
}
|
||||
|
||||
// Add handler for move button
|
||||
if (e.target.classList.contains('move-location-popup-btn')) {
|
||||
e.preventDefault();
|
||||
try {
|
||||
const locationData = JSON.parse(e.target.getAttribute('data-location'));
|
||||
// Find the marker that corresponds to this location
|
||||
import('./location-manager.js').then(({ markers, startMovingMarker }) => {
|
||||
const marker = markers.find(m => {
|
||||
const markerData = m._locationData;
|
||||
const locationId = locationData.Id || locationData.id || locationData.ID || locationData._id;
|
||||
const markerId = markerData?.Id || markerData?.id || markerData?.ID || markerData?._id;
|
||||
return markerId === locationId;
|
||||
});
|
||||
|
||||
if (marker) {
|
||||
startMovingMarker(locationData, marker);
|
||||
} else {
|
||||
console.error('Could not find marker for location:', locationData);
|
||||
showStatus('Could not find marker for this location', 'error');
|
||||
}
|
||||
});
|
||||
} catch (error) {
|
||||
console.error('Error starting move:', error);
|
||||
showStatus('Failed to start move mode', 'error');
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// Improve popup button handling for mobile
|
||||
map.on('popupopen', function(e) {
|
||||
const popup = e.popup;
|
||||
const content = popup.getContent();
|
||||
|
||||
// Create a temporary container to parse the HTML
|
||||
const tempDiv = document.createElement('div');
|
||||
tempDiv.innerHTML = content;
|
||||
|
||||
// Find buttons in the popup
|
||||
const editBtn = tempDiv.querySelector('.edit-location-popup-btn');
|
||||
const moveBtn = tempDiv.querySelector('.move-location-popup-btn');
|
||||
|
||||
if (editBtn || moveBtn) {
|
||||
// Re-render popup with proper event handlers
|
||||
setTimeout(() => {
|
||||
const popupNode = popup._contentNode || popup._container;
|
||||
if (!popupNode) return;
|
||||
|
||||
// Edit button
|
||||
const editButton = popupNode.querySelector('.edit-location-popup-btn');
|
||||
if (editButton) {
|
||||
editButton.addEventListener('click', function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
try {
|
||||
const locationData = JSON.parse(this.getAttribute('data-location'));
|
||||
openEditForm(locationData);
|
||||
map.closePopup();
|
||||
} catch (error) {
|
||||
console.error('Error parsing location data:', error);
|
||||
showStatus('Error opening edit form', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Move button
|
||||
const moveButton = popupNode.querySelector('.move-location-popup-btn');
|
||||
if (moveButton) {
|
||||
moveButton.addEventListener('click', async function(event) {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
try {
|
||||
const locationData = JSON.parse(this.getAttribute('data-location'));
|
||||
const { startMovingMarker } = await import('./location-manager.js');
|
||||
startMovingMarker(locationData, e.popup._source);
|
||||
} catch (error) {
|
||||
console.error('Error starting move:', error);
|
||||
showStatus('Failed to start move mode', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
}, 0);
|
||||
}
|
||||
});
|
||||
|
||||
// Add this after the existing event listeners
|
||||
document.addEventListener('updateMoveButtons', (e) => {
|
||||
isMoveMode = e.detail.isMoving;
|
||||
|
||||
if (isMoveMode) {
|
||||
// Hide add location buttons during move mode
|
||||
const addBtn = document.getElementById('add-location-btn');
|
||||
const mobileAddBtn = document.getElementById('mobile-add-location-btn');
|
||||
|
||||
if (addBtn) addBtn.style.display = 'none';
|
||||
if (mobileAddBtn) mobileAddBtn.style.display = 'none';
|
||||
|
||||
// Add cancel move button
|
||||
addCancelMoveButton();
|
||||
} else {
|
||||
// Restore add location buttons
|
||||
const addBtn = document.getElementById('add-location-btn');
|
||||
const mobileAddBtn = document.getElementById('mobile-add-location-btn');
|
||||
|
||||
if (addBtn) addBtn.style.display = '';
|
||||
if (mobileAddBtn) mobileAddBtn.style.display = '';
|
||||
|
||||
// Remove cancel move button
|
||||
removeCancelMoveButton();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Add this to the setupMapEventListeners function
|
||||
export function setupMapEventListeners(mapInstance) {
|
||||
// ...existing code...
|
||||
|
||||
// Handle move location button clicks in popups
|
||||
mapInstance.on('popupopen', function(e) {
|
||||
// ...existing code for edit button...
|
||||
|
||||
// Handle move button
|
||||
const moveBtn = e.popup._contentNode?.querySelector('.move-location-popup-btn');
|
||||
if (moveBtn) {
|
||||
moveBtn.addEventListener('click', async function() {
|
||||
try {
|
||||
const locationData = JSON.parse(this.getAttribute('data-location'));
|
||||
const marker = e.popup._source;
|
||||
|
||||
const { startMovingMarker } = await import('./location-manager.js');
|
||||
startMovingMarker(locationData, marker);
|
||||
} catch (error) {
|
||||
console.error('Error starting move:', error);
|
||||
showStatus('Failed to start move mode', 'error');
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function addCancelMoveButton() {
|
||||
// Desktop button
|
||||
const mapControls = document.querySelector('.map-controls');
|
||||
if (mapControls && !document.getElementById('cancel-move-btn')) {
|
||||
const cancelBtn = document.createElement('button');
|
||||
cancelBtn.id = 'cancel-move-btn';
|
||||
cancelBtn.className = 'btn btn-danger';
|
||||
cancelBtn.innerHTML = '<span class="btn-icon">✕</span><span class="btn-text">Cancel Move</span>';
|
||||
cancelBtn.addEventListener('click', async () => {
|
||||
const { cancelMove } = await import('./location-manager.js');
|
||||
cancelMove();
|
||||
});
|
||||
mapControls.insertBefore(cancelBtn, mapControls.firstChild);
|
||||
}
|
||||
|
||||
// Mobile button
|
||||
const mobileSidebar = document.getElementById('mobile-sidebar');
|
||||
if (mobileSidebar && !document.getElementById('mobile-cancel-move-btn')) {
|
||||
const mobileCancelBtn = document.createElement('button');
|
||||
mobileCancelBtn.id = 'mobile-cancel-move-btn';
|
||||
mobileCancelBtn.className = 'btn btn-danger';
|
||||
mobileCancelBtn.title = 'Cancel Move';
|
||||
mobileCancelBtn.innerHTML = '✕';
|
||||
mobileCancelBtn.addEventListener('click', async () => {
|
||||
const { cancelMove } = await import('./location-manager.js');
|
||||
cancelMove();
|
||||
});
|
||||
mobileSidebar.insertBefore(mobileCancelBtn, mobileSidebar.firstChild);
|
||||
}
|
||||
}
|
||||
|
||||
function removeCancelMoveButton() {
|
||||
document.getElementById('cancel-move-btn')?.remove();
|
||||
document.getElementById('mobile-cancel-move-btn')?.remove();
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user