<script>
    const alertPlaceholder = document.getElementById("alert-placeholder");

    function createAlert(title, message, clz, action, id) {
        id ??= md5(title + message); // MD5 id from the content
        if (alertPlaceholder.querySelector(`#alert-${id}`)) {
            return; // check if there are duplicates
        }

        const wrapper = document.createElement("div");
        wrapper.id = `alert-${id}`;
        wrapper.classList.add("toast", clz);
        wrapper.role = "alert";
        wrapper.ariaLive = "assertive";
        wrapper.ariaAtomic = "true";
        wrapper.style.width = "500px"; // 500px is default modal size

        const header = document.createElement("div");
        header.classList.add("toast-header");
        header.innerHTML = `<strong class="me-auto">${safe(title)}</strong> <button type="button" class="btn-close" data-bs-dismiss="toast" aria-label="close"></button>`;
        wrapper.appendChild(header);

        const body = document.createElement("div");
        body.classList.add("toast-body", "text-bg-light");
        body.innerText = message;
        wrapper.appendChild(body);

        alertPlaceholder.appendChild(wrapper);
        const toast = new bootstrap.Toast(wrapper);
        wrapper.addEventListener("hidden.bs.toast", _ => {
            wrapper.remove();  // bootstrap doesn't remove elements
            (action || reload)();
        });
        toast.show();
    }

    function showFailure(title, description, error) {
        let details;
        try {
            details = JSON.parse(error.text).error; // execution handler json error response
        } catch (_) {
            details = error.text ?? error.message ?? error;
        }
        createAlert(title, description(details), "text-bg-danger");
    }

    function showSuccess(title, description, action) {
        createAlert(title, description, "text-bg-success", action);
    }

</script>
