Methods that don’t work because they don’t recognize it

My intention was to make a page for modifying a map, where I take the positions of each element (they are images of tables) and make a real-time editor.

However, when trying to move a table from one point to another, despite calling the functions correctly, I get errors that I am not making calls.

I have looked at possible errors such as that the methods should not be static, but they are not.

@page "/mapa-edicion/{id:int}"
@inject NavigationManager Navigation
@inject ILogger<SalonModify> Logger
@inject IJSRuntime JSRuntime

@using System.Text.Json
@using System.Net.Http
@using RMSuit_v2.Models

<script src="https://cdnjs.cloudflare.com/ajax/libs/Sortable/1.14.0/Sortable.min.js"></script>


<style>
    .map-container {
        position: relative;
        width: 100%;
        height: 700px;
        border: 1px solid #ccc;
        overflow: hidden;
        background-color: #b8b8b8;
    }

    .zoom-container {
        position: absolute;
        top: 0;
        left: 0;
        width: 100%;
        height: 100%;
        transform-origin: top left;
        transition: transform 0.3s;
    }

    .drawing-item {
        position: absolute;
        width: 100px;
        height: 100px;
        cursor: pointer;
        user-select: none;
    }

        .drawing-item img {
            max-width: 100%;
            max-height: 100%;
            width: auto;
            height: auto;
        }
</style>

<h3>Mapa del Salón</h3>

@if (salonResponse == null)
{
    <p>Cargando detalles del salón...</p>
}
else
{
    <div class="map-container" @onclick="OnMapClick">
        <div class="zoom-container" style="transform: scale(1);">
            @if (salonResponse.dibujos != null && salonResponse.dibujos.Any())
            {
                var orderedDrawings = salonResponse.dibujos
                .OrderBy(d => d.estado == "D")
                .ThenBy(d => d.estado)
                .ToList();

                var occupiedPositions = new HashSet<(int, int)>();

                @foreach (var dibujo in orderedDrawings)
                {
                    var salonDetails = salonResponse.mesas
                    .Where(m => m.dibujos == dibujo.dibujos)
                    .ToList();

                    foreach (var salonDetail in salonDetails)
                    {
                        int adjustedLeft = (int)(salonDetail.posicionX);
                        int adjustedTop = (int)(salonDetail.posicionY);

                        // Aplicar el ratio de pantalla
                        int ratioAdjustedTop = (int)(adjustedTop * ratioPantalla);
                        int ratioAdjustedLeft = (int)(adjustedLeft * ratioPantalla);

                        var adjustedPosition = (ratioAdjustedTop, ratioAdjustedLeft);

                        Logger.LogInformation($"ID Dibujo: {dibujo.dibujos}, Estado: {dibujo.estado}, Posición Ajustada: (top: {adjustedTop}, left: {adjustedLeft}), Ratio: {ratioPantalla}");

                        if (dibujo.estado == "D" || !occupiedPositions.Contains(adjustedPosition))
                        {
                            Logger.LogInformation($"Procesando dibujo con ID: {dibujo.dibujos}, Posición: (top: {adjustedPosition.Item1}, left: {adjustedPosition.Item2})");
                            occupiedPositions.Add(adjustedPosition);

                            <div class="drawing-item"
                                 style="top:@(adjustedPosition.Item1 + "px"); left:@(adjustedPosition.Item2 + "px");"
                                 @onclick="() => OnImageClick(dibujo.dibujos)"
                                 id="[email protected]">
                                @if (!string.IsNullOrEmpty(dibujo.grafico))
                                {
                                    <img src="data:image/jpeg;base64,@dibujo.grafico" alt="@dibujo.estado" />
                                }
                                else
                                {
                                    <p>No hay imagen disponible.</p>
                                }
                            </div>
                        }
                    }
                }
            }
            else
            {
                <p>No se encontraron dibujos para este salón.</p>
            }
        </div>
    </div>
}

<button @onclick="GuardarCambios">Guardar Cambios</button>
<button @onclick="Cancelar">Cancelar</button>

@code {
    [Parameter] public int id { get; set; }
    private SalonResponse? salonResponse;
    private double ratioPantalla = 1.0;
    private int? hiddenDibujoId = null;
    private int maxX;
    private int maxY;

    private bool isDragging = false;

    private void CalculateMaxDimensions()
    {
        maxX = 0;
        maxY = 0;


        if (salonResponse?.dibujos != null && salonResponse.mesas != null)
        {
            foreach (var dibujo in salonResponse.dibujos)
            {

                var salonDetails = salonResponse.mesas
                    .Where(m => m.dibujos == dibujo.dibujos)
                    .ToList();

                foreach (var salonDetail in salonDetails)
                {

                    var adjustedLeft = (int)(salonDetail.posicionX);
                    var adjustedTop = (int)(salonDetail.posicionY);

                    maxX = Math.Max(maxX, adjustedLeft);
                    maxY = Math.Max(maxY, adjustedTop);
                }
            }
        }
        else
        {
            Logger.LogWarning("No se pudo calcular las dimensiones máximas: 'salonResponse', 'dibujos' o 'mesas' es nulo.");
        }
    }

    protected override async Task OnInitializedAsync()
    {
        await LoadSalonDetail();
        CalculateMaxDimensions();

    }

    protected override async Task OnAfterRenderAsync(bool firstRender)
    {
        if (firstRender)
        {
            await Task.Delay(500); // Esperar milisegundos
            ratioPantalla = await CalculateScreenRatio();

            await JSRuntime.InvokeVoidAsync("initializeSortable");
        }
    }
    private async Task LoadSalonDetail()
    {
        try
        {
            using var client = new HttpClient(new HttpClientHandler
                {
                    ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => true
                });

            var url = $"https://37.59.32.58:1380/Master/Salons/GetSalonDetail/{id}?includeDrawings=true&initialCatalog=ELSIFON";
            var response = await client.GetStringAsync(url);

            var options = new JsonSerializerOptions
                {
                    PropertyNamingPolicy = JsonNamingPolicy.CamelCase,
                    PropertyNameCaseInsensitive = true
                };

            salonResponse = JsonSerializer.Deserialize<SalonResponse>(response, options);

            if (salonResponse == null)
            {
                Logger.LogWarning("No se pudieron cargar los detalles del salón. salonResponse es null.");
            }
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error al cargar los detalles del salón.");
        }
    }

    private async Task<double> CalculateScreenRatio()
    {
        return await JSRuntime.InvokeAsync<double>("calculateScreenRatio");
    }

    private void Cancelar()
    {
        Navigation.NavigateTo($"/salonmapa/{id}");
    }

    private void OnImageClick(int dibujoId)
    {

        Logger.LogWarning($"Image clicked: {dibujoId}");

        hiddenDibujoId = dibujoId;
        StateHasChanged();
    }

    private async Task AddDraggingClass(string elementId)
    {
        await JSRuntime.InvokeVoidAsync("addDraggingClass", elementId);
    }






    private async Task OnMapClick(MouseEventArgs e)
    {

        Logger.LogWarning($"Map clicked: {e.ClientX}, {e.ClientY}");

        if (hiddenDibujoId.HasValue && isDragging)
        {
            var position = await JSRuntime.InvokeAsync<(int x, int y)>("getMousePosition", e.ClientX, e.ClientY);

            // Actualizar la posición en la lista de mesas
            var dibujoId = hiddenDibujoId.Value;
            hiddenDibujoId = null;

            await JSRuntime.InvokeVoidAsync("updateElementPosition", $"dibujo-{dibujoId}", position.x, position.y);

            Logger.LogInformation($"Dibujo {dibujoId} movido a nueva posición: (x: {position.x}, y: {position.y})");

            // Actualizar la posición en el modelo
            await UpdateElementPositionAsync(dibujoId, position.x, position.y);

            isDragging = false;
        }
        else
        {
            Logger.LogWarning("No se encontró un dibujo seleccionado para mover o no se está arrastrando.");
        }
    }

    private async Task GuardarCambios()
    {
        try
        {

            await JSRuntime.InvokeVoidAsync("saveNewPositions");

            using var client = new HttpClient();


            if (salonResponse?.dibujos != null)
            {
                foreach (var dibujo in salonResponse.dibujos)
                {

                    var detalle = salonResponse.mesas?.FirstOrDefault(m => m.dibujos == dibujo.dibujos);
                    if (detalle != null)
                    {
                        var url = $"https://37.59.32.58:1380/Master/SalonTables/UpdateTablePosition/{id}/{detalle.dibujos}/{detalle.dibujos}?initialCatalog=ELSIFON";
                        var response = await client.PostAsJsonAsync(url, detalle);

                        if (response.IsSuccessStatusCode)
                        {
                            Logger.LogInformation($"Posición actualizada para dibujo ID: {detalle.dibujos}");
                        }
                        else
                        {
                            Logger.LogError($"Error al actualizar la posición para dibujo ID: {detalle.dibujos}");
                        }
                    }
                    else
                    {
                        Logger.LogWarning($"No se encontró un detalle para el dibujo con ID: {dibujo.dibujos}");
                    }
                }
            }
            else
            {
                Logger.LogWarning("No se encontraron dibujos en el salón o la respuesta del salón es nula.");
            }


            Navigation.NavigateTo($"/salonmapa/{id}");
        }
        catch (Exception ex)
        {
            Logger.LogError(ex, "Error al guardar los cambios en las posiciones.");
        }
    }

    public event Action<int, int, int> OnPositionUpdated = delegate { };


    // Arrastre

    [JSInvokable]
    public Task SetDraggingState(bool dragging)
    {
        // Lógica para actualizar el estado de arrastre
        isDragging = dragging;
        return Task.CompletedTask;
    }

    [JSInvokable]
    public async Task UpdateElementPositionAsync(int dibujoId, int newX, int newY)
    {
        if (salonResponse?.mesas != null)
        {
            var detalle = salonResponse.mesas.FirstOrDefault(m => m.dibujos == dibujoId);
            if (detalle != null)
            {
                Logger.LogInformation($"Actualizando posición para dibujo ID {dibujoId}: (X: {newX}, Y: {newY})");

                // Validar y actualizar posición
                if (newX >= 0 && newY >= 0 && newX <= maxX && newY <= maxY)
                {
                    detalle.posicionX = (int)(newX / ratioPantalla);
                    detalle.posicionY = (int)(newY / ratioPantalla);

                    // Actualizar en la interfaz de usuario
                    await JSRuntime.InvokeVoidAsync("updateElementPosition", $"dibujo-{dibujoId}", newX, newY);

                    OnPositionUpdated?.Invoke(dibujoId, newX, newY);
                }
                else
                {
                    Logger.LogWarning($"Coordenadas fuera de rango para dibujo ID {dibujoId}: (X: {newX}, Y: {newY})");
                }
            }
            else
            {
                Logger.LogWarning($"No se encontró un detalle para el dibujo con ID: {dibujoId}");
            }
        }
    }


    // Para mostrar mensajes de js en logger

    [JSInvokable]
    public static Task LogMessage(string message)
    {
        var logger = LoggerFactory.Create(builder => builder.AddConsole()).CreateLogger("JSLogger");
        logger.LogInformation($"Mensaje desde JS: {message}");
        return Task.CompletedTask;
    }




}

<script>


    window.addEventListener('load', () => {
        initializeSortable();
    });

    function logMessage(message) {
        DotNet.invokeMethodAsync('RMSuit_v2', 'LogMessage', message)
            .then(result => {
                console.log('Mensaje registrado:', message);
            })
            .catch(error => {
                console.error('Error al registrar el mensaje:', error);
            });
    }

    window.calculateScreenRatio = () => {
        let width = window.innerWidth;
        let height = window.innerHeight;
        let ratio = 1.0;
        let baseWidth = 1920;
        let baseHeight = 1080;

        ratio = Math.min(width / baseWidth, height / baseHeight);

        logMessage(`Screen Ratio Calculated: ${ratio}`);

        return ratio;
    };

    window.saveNewPositions = () => {
        Object.keys(newPositionData).forEach(dibujoId => {
            let position = newPositionData[dibujoId];
            DotNet.invokeMethodAsync('RMSuit_v2', 'UpdateElementPositionAsync', parseInt(dibujoId), position.x, position.y)
                .then(() => {
                    logMessage(`Posición guardada para Dibujo ID: ${dibujoId}, X: ${position.x}, Y: ${position.y}`);
                })
                .catch(error => {
                    logMessage('Error al guardar la posición:', error);
                });
        });
        newPositionData = {};
    };

    window.addDraggingClass = (elementId) => {

        DotNet.invokeMethodAsync('RMSuit_v2', 'SetDraggingState', true)
            .catch(error => console.error('Error al actualizar estado de arrastre:', error));

        const element = document.getElementById(elementId);
        if (element) {
            element.classList.add("dragging");
        }
    };

    window.initializeSortable = () => {
        const tryInitialize = () => {
            const container = document.querySelector('.zoom-container');
            if (container) {
                logMessage('Contenedor encontrado:', container);

                Sortable.create(container, {
                    animation: 150,
                    onStart: () => {
                        DotNet.invokeMethodAsync('RMSuit_v2', 'SetDraggingState', true)
                            .catch(error => logMessage(`Error al actualizar estado de arrastre: ${error}`));
                    },
                    onEnd: (evt) => {
                        const elementId = evt.item.id.replace('dibujo-', '');
                        const newX = evt.item.offsetLeft;
                        const newY = evt.item.offsetTop;

                        logMessage(`Elemento ${elementId} movido a X: ${newX}, Y: ${newY}`);

                        DotNet.invokeMethodAsync('RMSuit_v2', 'UpdateElementPositionAsync', parseInt(elementId), newX, newY)
                            .catch(error => logMessage(`Error al actualizar la posición: ${error}`));

                        DotNet.invokeMethodAsync('RMSuit_v2', 'SetDraggingState', false)
                            .catch(error => logMessage(`Error al actualizar estado de arrastre: ${error}`));
                    }
                });
            } else {
                logMessage('Sortable: el contenedor no se encontró, reintentando...');
                setTimeout(tryInitialize, 100); // Reintento después de 100ms
            }
        };

        tryInitialize();
    };

    window.updateElementPosition = (elementId, newX, newY) => {
        DotNet.invokeMethodAsync('RMSuit_v2', 'UpdateElementPositionAsync', parseInt(elementId), newX, newY)
            .catch(error => logMessage(`Error al actualizar la posición: ${error}`));
    };




</script>

The error:

Mensaje registrado: Error al actualizar estado de arrastre: Error: System.ArgumentException: The assembly ‘RMSuit_v2’ does not contain a public invokable method with [JSInvokableAttribute(“SetDraggingState”)].
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.GetCachedMethodInfo(AssemblyKey assemblyKey, String methodIdentifier)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfo& callInfo, IDotNetObjectReference objectReference, String argsJson)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, String argsJson)
VM15:11 Mensaje registrado: Elemento 101 movido a X: 303, Y: 164
VM15:11 Mensaje registrado: Error al actualizar la posición: Error: System.ArgumentException: The assembly ‘RMSuit_v2’ does not contain a public invokable method with [JSInvokableAttribute(“UpdateElementPositionAsync”)].
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.GetCachedMethodInfo(AssemblyKey assemblyKey, String methodIdentifier)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfo& callInfo, IDotNetObjectReference objectReference, String argsJson)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, String argsJson)
VM15:11 Mensaje registrado: Error al actualizar estado de arrastre: Error: System.ArgumentException: The assembly ‘RMSuit_v2’ does not contain a public invokable method with [JSInvokableAttribute(“SetDraggingState”)].
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.GetCachedMethodInfo(AssemblyKey assemblyKey, String methodIdentifier)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.InvokeSynchronously(JSRuntime jsRuntime, DotNetInvocationInfo& callInfo, IDotNetObjectReference objectReference, String argsJson)
at Microsoft.JSInterop.Infrastructure.DotNetDispatcher.BeginInvokeDotNet(JSRuntime jsRuntime, DotNetInvocationInfo invocationInfo, String argsJson)

I am trying to get these methods to work so that it works with the objective of being able to drag the elements of the images that I am recreating, to create a real-time editor of the map.