The fixedColumn feature not being applied in the datatable

I am trying to freeze the first four columns of this datatable when a user is scrolling it. But the style “dtfc-has-left” style=”position: relative;”” is not applied when I inspect the datatable in the browser, and the feature is not working. In case I miss something, I am adding almost all the code here. Please let me know if anyone can see the reason why the frozen columns is not applied.

<head>
    <link rel="stylesheet" href="https://cdn.datatables.net/1.13.4/css/jquery.dataTables.min.css" />
    <link rel="stylesheet" href="https://cdn.datatables.net/fixedcolumns/4.2.2/css/fixedColumns.dataTables.min.css" />
    <link rel="stylesheet" href="https://cdn.datatables.net/buttons/2.4.1/css/buttons.dataTables.min.css" />

</head>
<!-- Tender Selection Dropdown -->
<div class="mb-3">
    <p class="important-note">
        <i class="fas fa-file-contract me-2"></i>Select a tender to add a new estimation. Only eligible tenders are shown in the dropdown below.
    </p>
    <select id="tenderSelect" name="TenderID" class="form-select" size="6">
        <option value="0" selected="@(ViewBag.SelectedTenderID == 0 ? "selected" : null)">
            All Tenders' Estimations
        </option>

        @foreach (var item in ViewBag.Tenders as List<SelectListItem>)
        {
            <option value="@item.Value" selected="@(ViewBag.SelectedTenderID?.ToString() == item.Value ? "selected" : null)">
                @item.Text
            </option>
        }
    </select>
</div>


@if (Model == null || !Model.Any())
{
    <p>No Estimations Available At this Time.</p>
}
else
{
    <div class="dropdown mb-3">
        <button class="btn btn-secondary dropdown-toggle" type="button" id="columnToggleDropdown" data-bs-toggle="dropdown" aria-expanded="false" title="Add/ Remove columns from the view and exports.">
            <i class="fas fa-table-columns me-1"></i> Toggle Columns
        </button>
        @* <button class="btn btn-secondary dropdown-toggle" type="button" id="columnToggleDropdown" data-bs-toggle="dropdown" aria-expanded="false">
            Toggle Columns
        </button> *@
        <ul class="dropdown-menu" aria-labelledby="columnToggleDropdown" id="columnVisibilityOptions">
            <li>
                <label class="dropdown-item">
                    <input type="checkbox" id="selectAllColumns" checked> Select All
                </label>
            </li>
            <li><hr class="dropdown-divider" /></li>
            <!-- Column checkboxes will be added dynamically -->
        </ul>
    </div>

    @* class="table-responsive mx-auto" style="overflow-x: auto; width: 100%;" *@
    <div class="div1">
        <table class="table table-bordered display nowrap" style="width:100%" id="estimationTable">
            <thead>
                <tr>
                </tr>
            </thead>
            <tbody>
                @foreach (var estimation in Model)
                {
                    <tr>
                        <td>
                            @{
                                var status1 = (EstimationStatusEnum)estimation.EstimationStepStatus;

                                string actionName = status1 switch
                                {
                                    EstimationStatusEnum.PileSetupPending => "EstimationPileWizard",
                                    EstimationStatusEnum.ValuationPending => "EstimationValWizard",
                                    EstimationStatusEnum.SubmissionPending => "EstimationSubmissionWizard",
                                    EstimationStatusEnum.ResultPending => "EstimationResultWizard",
                                    EstimationStatusEnum.ResultSubmitted => "EstimationResultWizard",
                                    _ => null
                                };

                                bool isReview = status1 == EstimationStatusEnum.ResultSubmitted;
                                if (!string.IsNullOrEmpty(actionName))
                                {
                                    if (isReview || estimation.EstimationStatus == false)
                                    {
                                        <a href="@Url.Action(actionName, "Estimation", new { estID = estimation.EstID, tenderID = ViewBag.SelectedTenderID })"
                                           class="btn btn-sm"
                                           title="Review or update completed estimation"
                                           style="background-color: yellow; color: black; border: 1px solid #ccc;">
                                            <i class="fas fa-eye me-1"></i> Review
                                        </a>
                                    }
                                    else
                                    {
                                        <a href="@Url.Action(actionName, "Estimation", new { estID = estimation.EstID, tenderID = ViewBag.SelectedTenderID })"
                                           class="btn btn-primary btn-sm"
                                           title="Proceed or update incomplete estimation record">
                                            <i class="fas fa-step-forward me-1"></i> Proceed
                                        </a>
                                    }
                                }
                            }
                        </td>
                        <td>@estimation.EstID</td>
                        <td>@estimation.EstRefID</td>
                        <td>@estimation.TenderProjName</td>
                        <td>
                            @{
                                var status = (EstimationStatusEnum)estimation.EstimationStepStatus;
                                var stepLabels = new Dictionary<EstimationStatusEnum, (string Text, string Badge)>
                                {
                                { EstimationStatusEnum.PileSetupPending, ("Pile Setup Pending", "info") },
                                { EstimationStatusEnum.ValuationPending, ("Valuation Pending", "primary") },
                                { EstimationStatusEnum.SubmissionPending, ("Submission Pending", "success") },
                                { EstimationStatusEnum.ResultPending, ("Result Pending", "secondary") },
                                { EstimationStatusEnum.ResultSubmitted, ("Completed", "dark") }
                                };

                                var (label, badgeClass) = stepLabels[status];
                            }
                            <span class="badge bg-@badgeClass">@label</span>
                        </td>
                        <td>
                            @{
                                string result = estimation.Result?.ToString() ?? "";
                                string displayText = result;
                                string resultBadgeClass = result switch
                                {
                                    "Won" => "success",
                                    "Lost" => "danger",
                                    "In Abeyance" => "warning",
                                    "Cancelled" => "secondary",
                                    _ => "info"
                                };

                                if (string.IsNullOrWhiteSpace(result) || !new[] { "Won", "Lost", "In Abeyance", "Cancelled" }.Contains(result))
                                {
                                    displayText = "NAY";
                                }
                            }
                            <span class="badge bg-@resultBadgeClass">@displayText</span>
                        </td>
                        <td>
                            @if (estimation.EstimationStatus == true)
                            {
                                <a asp-controller="ChangeRequest" asp-action="ChangeRequest" asp-route-estID="@estimation.EstID" asp-route-tenderID="@estimation.TenderID"
                                   class="btn btn-sm btn-outline-secondary change-request-link"
                                   title="Access Change Requests for this Estimation">
                                    <i class="fas fa-exchange-alt me-1"></i> @estimation.ChangeRequestCount
                                </a>
                            }
                            else
                            {
                                @estimation.ChangeRequestCount
                            }
                        </td>
                        <td>@estimation.CompanyName</td>
                        <td>@($"{estimation.CompPhoneCode} {estimation.CompPhone}")</td>
                        <td>@estimation.CompEmail</td>
                        <td>@estimation.RepFirstName @estimation.RepLastName</td>
                        <td>@($"{estimation.RepPhoneCode} {estimation.RepPhone}")</td>
                        <td>@estimation.RepEmail</td>
                        <td style="text-align: right; color:@(estimation.ClientBudget == 0.00M ? "red" : "black")">
                            @(estimation.ClientBudget == 0.00M ? "Not Provided" : estimation.ClientBudget.ToString("#,0.00"))
                        </td>
                        <td style="text-align: right; color:@((estimation.MaterialValue == null || estimation.MaterialValue == 0.00M) ? "red" : "black")">
                            @((estimation.MaterialValue == null || estimation.MaterialValue == 0.00M)
                                                ? "NAY"
                                                : estimation.MaterialValue?.ToString("#,0.00"))
                        </td>
                        <td style="text-align: right; color:@((estimation.TenderValue == null || estimation.TenderValue == 0.00M) ? "red" : "black")">
                            @((estimation.TenderValue == null || estimation.TenderValue == 0.00M)
                                                ? "NAY"
                                                : estimation.TenderValue?.ToString("#,0.00"))
                        </td>
                        <td style="text-align: right; color:@((estimation.MarkUpPercentage == null || estimation.MarkUpPercentage == 0.00M) ? "red" : "black")">
                            @((estimation.MarkUpPercentage == null || estimation.MarkUpPercentage == 0.00M)
                                                ? "NAY"
                                                : estimation.MarkUpPercentage?.ToString("F2"))
                        </td>
                        <td style="text-align: right; color:@((estimation.EstimatedDurationDays == null || estimation.EstimatedDurationDays == 0.00M) ? "red" : "black")">
                            @((estimation.EstimatedDurationDays == null || estimation.EstimatedDurationDays == 0.00M)
                                                ? "NAY"
                                                : estimation.EstimatedDurationDays?.ToString("F2"))
                        </td>
                        <td>@estimation.TenderReceiptDate.ToShortDateString()</td>
                        <td>@estimation.TenderDueDate.ToShortDateString()</td>
                        <td>@(estimation.SubmissionDate?.ToShortDateString() ?? "-")</td>
                        <td>@estimation.EstFirstName @estimation.EstLastName</td>
                        <td>@estimation.TLFirstName @estimation.TLLastName</td>
                        <td>@(estimation.EstResReceivedDate?.ToShortDateString() ?? "-")</td>
                        <td>@estimation.WonCompName</td>
                        <td>@estimation.SuccessPrice?.ToString("#,0.00")</td>
                        <td>
                            <span title="@estimation.EstFeedback" style="max-width:200px; white-space:nowrap; overflow:hidden; text-overflow:ellipsis; display: inline-block;">
                                @estimation.EstFeedback
                            </span>
                        </td>
                        <td>@estimation.EstContractRefID</td>
                        <td data-status="@(estimation.EstimationStatus ? "Active" : "Inactive")">
                            @if (estimation.EstimationStatus == true)
                            {
                                <span class="badge bg-success">Active</span>
                            }
                            else
                            {
                                <span class="badge bg-secondary">Inactive</span>
                            }
                        </td>

                        <td>
                            @if (estimation.HasSoilProfile)
                            {
                                <button type="button" title="View soil profile for this estimation based tender"
                                class="btn btn-info btn-sm view-soil-btn"
                                style="background-color: darkgrey; color: black; border: 1px solid #ccc;"
                                data-tender-id="@estimation.TenderID">
                                    <i class="fas fa-seedling me-1"></i> View Soil Profile
                                </button>
                            }
                        </td>
                        <td>
                            @if (estimation.HasPileReq)
                            {
                                <button type="button" title="View a comparison of client requested and estimation suggested pile"
                                        class="btn btn-info btn-sm view-pile-btn"
                                        style="background-color: darkgrey; color: black; border: 1px solid #ccc;"
                                        data-tender-id="@estimation.EstID">
                                    <i class="fas fa-hammer me-1"></i> View Pile Setup
                                </button>
                            }
                        </td>
                        

                        
                    </tr>
                }
            </tbody>
        </table>
    </div>

    @* Modal to view soil profile *@

    <div class="modal fade" id="soilProfileModal" tabindex="-1" role="dialog" aria-labelledby="soilProfileModalLabel" aria-hidden="true">
        <div class="modal-dialog modal-lg" role="document">
            <div class="modal-content" id="soilProfileContent">
                <!-- Partial View here -->
            </div>

        </div>
    </div>

    @* Modal to view requested pile *@
    <div class="modal fade" id="pileReqModal" tabindex="-1" role="dialog" aria-labelledby="pileReqModalLabel" aria-hidden="true">
        <div class="modal-dialog modal-lg" role="document">
            <div class="modal-content" id="pileReqContent">
                <!-- Partial View here -->
            </div>

        </div>
    </div>

}

<style>
    .dataTables_wrapper .dataTables_scroll {
        overflow: auto;
    }

    th, td {
        white-space: nowrap;
    }

    .dt-button {
        margin-right: 5px;
    }

    .estimation-link:hover {
        background-color: lightgreen !important;
        color: black !important;
    }

    .change-request-link:hover {
        background-color: #e91e63 !important;
        color: black !important;
    }

    /* Initially hide optional columns */
    th.toggle-optional,
    td.toggle-optional {
        display: none;
    }

    /* Add here */
    #columnVisibilityOptions {
        max-height: 250px;
        overflow-y: auto;
        overflow-x: hidden;
        padding-right: 10px;
    }

    .div1 th {
        background: #198754 !important;
    }
</style>

@section Scripts {
    <script src="https://code.jquery.com/jquery-3.7.0.min.js"></script>
    <script src="https://cdn.datatables.net/1.13.4/js/jquery.dataTables.min.js"></script>
    <script src="https://cdn.datatables.net/fixedcolumns/4.2.2/js/fixedColumns.dataTables.min.js"></script>
    <script src="https://cdn.datatables.net/buttons/2.4.1/js/dataTables.buttons.min.js"></script>
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jszip/3.10.1/jszip.min.js"></script>
    <script src="https://cdn.datatables.net/buttons/2.4.1/js/buttons.html5.min.js"></script>

    <script>
        $(document).ready(function () {

            // Setup filter for Active Inactive Status
            let statusFilterValue = '';

            $.fn.dataTable.ext.search.push(function (settings, data, dataIndex) {
                if (!statusFilterValue) return true;

                const row = settings.aoData[dataIndex].nTr;
                const status = $('td:eq(28)', row).data('status');
                return status === statusFilterValue;
            });

            // Export file title setup
            var now = new Date();
            var formattedDateTime = now.toLocaleString('en-GB', {
                day: '2-digit', month: '2-digit', year: 'numeric',
                hour: '2-digit', minute: '2-digit', second: '2-digit',
                hour12: false
            }).replace(/[/:]/g, '-').replace(',', '');

            var exportTitle = 'Estimation Master List - Exported Date Time: ' + formattedDateTime;
            var exportFileName = 'Estimation_Master_Export_ ' + formattedDateTime;

            // Tender status dictionary
            const tenderStatuses = @Html.Raw(Json.Serialize(ViewBag.TenderStatuses));

            function updateAddEstimationButtonVisibility() {
                const selectedTenderId = $('#tenderSelect').val();
                const addBtn = $('#addEstimationBtn');

                // If "All Tenders" (value 0) is selected, hide the button
                if (selectedTenderId === "0") {
                    addBtn.hide();
                    return;
                }

                // Convert selectedTenderId to a number for lookup
                const tenderIdNum = parseInt(selectedTenderId, 10);
                const status = tenderStatuses[tenderIdNum]; // Look up using the numeric ID

                if (status === true) {
                    addBtn.show();
                } else {
                    addBtn.hide();
                }
            }

            // --- Event Handlers ---

            // Handle Tender Dropdown Change
            $('#tenderSelect').on('change', function () {
                const selectedValue = $(this).val();
                const url = selectedValue === "0"
                    ? "/Estimation/Index"
                    : `/Estimation/Index?tenderID=${selectedValue}`;
                window.location.href = url;
            });

            // Handle Add Estimation Button Click
            $('#addEstimationBtn').on('click', function () {
                const selectedTenderId = $('#tenderSelect').val();
                if (!selectedTenderId || selectedTenderId === "0") {
                    alert("Please select a valid Tender to create an estimation.");
                    return;
                }
                window.location.href = `/Estimation/EstimationCreateWizard?tenderID=${selectedTenderId}&estID=0`;
            });

            // --- Initializations ---

            // Initial check on page load to set button visibility
            updateAddEstimationButtonVisibility();

            // Initialize DataTable if table exists
            if ($('#estimationTable').length > 0) {
                let $filterRow = $('<tr class="filters"></tr>');
                $('#estimationTable thead tr:eq(0) th').each(function () {
                    $filterRow.append('<th><input type="text" placeholder="Search" style="width: 100%;" /></th>');
                });
                $('#estimationTable thead').append($filterRow);

                var table = $('#estimationTable').DataTable({
                    dom: 'lfrtipB',
                    buttons: [
                        {
                            extend: 'csvHtml5',
                            text: '<i class="fas fa-file-csv me-1"></i> Export CSV',
                            className: 'btn btn-sm btn-secondary export-btn',
                            title: exportTitle,
                            filename: exportFileName,
                            exportOptions: {
                                columns: function (idx, data, node) {
                                    var columnCount = table.columns().count();
                                    return $(node).is(':visible') && idx !== 0 && idx !== columnCount - 2 && idx !== columnCount - 1;
                                },
                                    modifier: { search: 'applied', order: 'current' }
                                }
                        },
                        {
                            extend: 'excelHtml5',
                            text: '<i class="fas fa-file-excel me-1"></i> Export Excel',
                            className: 'btn btn-sm btn-secondary export-btn',
                            title: exportTitle,
                            filename: exportFileName,
                            exportOptions: {
                                columns: function (idx, data, node) {
                                    var columnCount = table.columns().count();
                                    return $(node).is(':visible') && idx !== 0 && idx !== columnCount - 2 && idx !== columnCount - 1;
                                },
                                modifier: { search: 'applied', order: 'current' }
                            }
                        }
                    ],
                    scrollX: true,
                    scrollY: '500px',
                    fixedColumns: {
                        leftColumns: 4 
                    },
                    scrollCollapse: true,
                    paging: true,
                    //fixedHeader: true,
                    orderCellsTop: true,
                    order: [[1, 'asc']],
                    columnDefs: [
                        {
                            targets: [13, 14, 15, 16, 17, 25], // Client Budget, Material Value, Tender Value, MarkUp %, Estimated Duration (Days), Success Price
                    render: function (data, type, row) {

                        if (type === 'filter' || type === 'sort') {
                            let cleanData = data.toString().replace(/£/g, '').replace(/,/g, '');
                            if (cleanData === "NAY" || cleanData === "Not Provided" || cleanData.trim() === "" || isNaN(parseFloat(cleanData))) {
                                return -Infinity;
                            }
                            return parseFloat(cleanData);
                        }
                        // For display, return the data as is (with commas)
                        return data;
                    },
                    type: 'num-fmt'
                            // targets: [12,13,14,24],
                            // createdCell: function (td, cellData) {
                            //     var plainText = cellData ? cellData.toString().trim() : "";
                            //     $(td).html('<span class="d-none">' + plainText + '</span>' + plainText);
                            // }
                        },
                        { orderable: false, targets: 0 }
                    ],
                    initComplete: function () {
                        var api = this.api();

                        api.columns().eq(0).each(function (colIdx) {
                            var cell = $('.filters th').eq(colIdx);
                            var colHeader = $('#estimationTable thead tr:eq(0) th').eq(colIdx).text().trim(); // For debugging

        //console.log(`Column Index: ${colIdx}, Header: "${colHeader}"`);
                            if (colIdx === 4) { 
                                $(cell).html(`
                                    <select class="form-select form-select-sm" style="width: 100%;">
                                        <option value="">All</option>
                                        <option value="Pile Setup Pending">Pile Setup Pending</option>
                                        <option value="Valuation Pending">Valuation Pending</option>
                                        <option value="Submission Pending">Submission Pending</option>
                                        <option value="Result Pending">Result Pending</option>
                                        <option value="Completed">Completed</option>
                                    </select>`);
                            }
                            else if (colIdx === 5) { // Column index for "Result"
                                $(cell).html(`
                                    <select class="form-select form-select-sm" style="width: 100%;">
                                        <option value="">All</option>
                                        <option value="Won">Won</option>
                                        <option value="Lost">Lost</option>
                                        <option value="In Abeyance">In Abeyance</option>
                                        <option value="Cancelled">Cancelled</option>
                                        <option value="NAY">NAY</option>
                                    </select>
                                `);
                            }
                            // Active/ Inactive filter
                            else if (colIdx === 28) {
                                $(cell).html(`
                                    <select class="form-select form-select-sm" style="width: 100%;">
                                        <option value="">All</option>
                                        <option>Active</option>
                                        <option>Inactive</option>
                                    </select>`);

                                    $('select', cell).on('change', function () {
                                        statusFilterValue = this.value;
                                        api.draw(); // ⬅ trigger redraw with updated filter
                                    });
                            }
                            else if (colIdx !== 0 && colIdx !== 29 && colIdx !== 30 ) { // Not the 'Action' column
                                $(cell).html('<input type="text" placeholder="Search" style="width: 100%;" />');
                            } 
                            else { // 'Action' column
                                $(cell).html('');
                            }

                            $('input, select', cell).on('keyup change', function () {
                                api.column(colIdx).search(this.value).draw();
                            });
                        });

                       
                        
                    }
                });
            }
        });
    </script>
}

Change Multiselect With Group Options Based On Input Of Another Select Option

I have Two Select Options in that Based on first select options i need to load second select options values. Second select option having multiselect option The HTML code is below.

<div>
  <select name="Control" id="ctrl_typ" onchange="Control_change()">
    <option selected="selected" disabled="disabled">COMMAND</option> 
    <option  disabled="disabled"></option>
    <option  value="1">First</option>
    <option  value="2">Second</option>                  
  </select> 
  <select name="Names[]" multiple id="nameid" size="30">
  </select>
</div>

JS part is below.

var maxSelect=1000;
$( document ).ready(function()
   {
    $('#nameid').multiselect({
    columns: 1,
    texts: {
        placeholder: 'Name',
        search     : 'Search Names'      
       },
    search: true,
    maxWidth:467,
    maxHeight:400, 
    selectAll: true,
    selectGroup: true,
    onOptionClick: function( element, option ) 
       {
         if( $(element).val().length > maxSelect ) {
                    if( $(option).is(':checked') ) {
                        var thisVals = $(element).val();

                        thisVals.splice(
                            thisVals.indexOf( $(option).val() ), 1
                        );

                        $(element).val( thisVals );

                        $(option).prop( 'checked', false ).closest('li')
                            .toggleClass('selected');
                    }
                }
           else if( $(element).val().length == maxSelect ) 
                {
                    $(element).next('.ms-options-wrap')
                        .find('li:not(.selected)').addClass('disabled')
                        .find('input[type="checkbox"]')
                            .attr( 'disabled', 'disabled' );
                }
                // max select not reached, make sure any disabled
                // checkboxes are available
                else {
                    $(element).next('.ms-options-wrap')
                        .find('li.disabled').removeClass('disabled')
                        .find('input[type="checkbox"]')
                            .removeAttr( 'disabled' );
                }
            }
 });

function Control_change()
   {
    var ctrlval=$("#ctrl_typ option:selected").val();
    var Gload="";
    if(ctrlval==1)
       {
        Gload="<select name="Names[]" multiple id="nameid" size="30">
                 <optgroup label='Label 1'>
                   <option value='1'>One</option>
                   <option value'2'>Two</option>
                   <option value='3'>Three</option>
                 </optgroup>
                 <optgroup label='Label 2'>
                   <option value='4'>Four</option>
                   <option value'5'>Five</option>
                   <option value='6'>Six</option>
                 </optgroup>
              </select>"; 
        $('#nameid').replaceWith(Gload);
       }
    else if(ctrlval==2)
       {
        Gload="<select name="Names[]" multiple id="nameid" size="30">
                 <optgroup label='Label 3'>
                   <option value='1'>One</option>
                   <option value'2'>Two</option>
                   <option value='3'>Three</option>
                 </optgroup>
                 <optgroup label='Label 4'>
                   <option value='4'>Four</option>
                   <option value'5'>Five</option>
                   <option value='6'>Six</option>
                 </optgroup>
              </select>";
        $('#nameid').replaceWith(Gload);
       }

   }

I am tried the above code but is not working well while select multiselect dropdown it giving error guide me to resolve the issue. I want to load the multiselect dropdown with group values.

Postgres.js problem with double quotes when creating an Enum

So I’m using Postgres.js to build an application and I’m creating an Enum for user roles like the following

import { PostgresError } from 'postgres';
import sql from './database/index.js';


const RolesEnum = {
    user : 'user',
    moderator : 'moderator',
    admin : 'admin'
} as const;

const Enum = ['user', 'moderator', 'admin'] as const;

async function Build() {
    try {
        await sql`DROP TYPE F_that;`.catch(() => {}); // IF DOESN'T EXIST THEN PASS
        const { statement } = await sql`CREATE TYPE F_that AS ENUM (${sql(
            Enum
        )});`;
        console.log(statement);
    } catch (error) {
        console.error(
            (error as PostgresError).query ?? (error as Error).message ?? error
        );
        /* Logs the following
          CREATE TYPE F_that AS ENUM ( "user", "moderator" , "admin" );*/
    }
}

await Build() // Logs the following: CREATE TYPE F_that AS ENUM ( "user", "moderator" , "admin" );`

as you see the problem is the double quotes in the Enum values which gives an Error in postgres my question is how to make them single quotes in the query

the closest solution I got was adding this to the catch block

if (error instanceof postgres.PostgresError) {
            error.query.replaceAll('"', "'"); // gets query with single quotes for the Enum
   }

which gives the right query but i can’t use it like above
(whenever i try get an error about a positional parameter i guess it runs at run time?)

Search in text only works partially

Need a friendly advise. I have this script to search and filter a list of data.
However, it seems that the search for keyword only works when entering one character.

Try to search for “John” below. It returns hits when hitting “j”, but then nothing when entering “jo”.

What’s the reason for this?

document.addEventListener('DOMContentLoaded', () => {

  'use strict';

  /**
   * Script to handle filtering and pagination out of a list of items 
   */
  const pagination = (el) => {
    const list = el.querySelector('.filter-items');
    const searchinput = el.querySelector('.search-input');
    const pagination = el.querySelector('.pagination-nav');
    const items = Array.from(el.querySelectorAll('.item'));
    const pagesize = parseInt(el.dataset.pagesize, 10) || 10;
    let filteredItems = items;
    let currPage = 1;

    /**
     * Search by keyword
     */
    const searchInput = () => {
      const keyword = el.querySelector('input[name=keyword]').value.trim();
      if (keyword) {
        filteredItems = items.filter(el => {
          return el.innerText.indexOf(keyword) > -1;
        });
      } else {
        filteredItems = items;
      }

      currPage = 1;

      if (filteredItems.length !== 0) {
        pagination.style.display = 'block';
        setHTML(filteredItems);
      } else {
        pagination.style.display = 'none';
        list.innerHTML = 'No data';
      }
    }

    /**
     * Search by input
     */
    searchinput.addEventListener('keyup', e => {
      searchInput();
    })

    const paginate = (totalItems, currentPage = 1, pageSize = 2, maxPages = 3) => {
      let totalPages = Math.ceil(totalItems / pageSize);
      if (currentPage < 1) {
        currentPage = 1;
      } else if (currentPage > totalPages) {
        currentPage = totalPages;
      }

      let startPage, endPage;
      if (totalPages <= maxPages) {
        startPage = 1;
        endPage = totalPages;
      } else {
        let maxPagesBeforeCurrentPage = Math.floor(maxPages / 2);
        let maxPagesAfterCurrentPage = Math.ceil(maxPages / 2) - 1;
        if (currentPage <= maxPagesBeforeCurrentPage) {
          startPage = 1;
          endPage = maxPages;
        } else if (currentPage + maxPagesAfterCurrentPage >= totalPages) {
          startPage = totalPages - maxPages + 1;
          endPage = totalPages;
        } else {
          startPage = currentPage - maxPagesBeforeCurrentPage;
          endPage = currentPage + maxPagesAfterCurrentPage;
        }
      }

      let startIndex = (currentPage - 1) * pageSize;
      let endIndex = Math.min(startIndex + pageSize - 1, totalItems - 1);

      let pages = Array.from(Array((endPage + 1) - startPage).keys()).map(i => startPage + i);

      // Add ellipsis after first page
      if (startPage > 1) {
        pages.unshift(startPage > 1 ? '...' : 2);
        pages.unshift(1);
      }
      // Add ellipsis before last page
      if (endPage < totalPages - 1) {
        pages.push(endPage < totalPages - 1 ? '...' : totalPages - 1);
      }
      if (endPage < totalPages) {
        pages.push(totalPages);
      }

      return {
        totalItems: totalItems,
        currentPage: currentPage,
        pageSize: pageSize,
        totalPages: totalPages,
        startPage: startPage,
        endPage: endPage,
        startIndex: startIndex,
        endIndex: endIndex,
        pages: pages
      };
    }

    const setHTML = (items) => {
      list.innerHTML = '';
      pagination.innerHTML = '';
      const {
        totalItems,
        currentPage,
        pageSize,
        totalPages,
        startPage,
        endPage,
        startIndex,
        endIndex,
        pages
      } = paginate(items.length, currPage, pagesize, 3)

      const ul = document.createElement('ul');
      ul.classList.add('pagination');
      let paginationHTML = '';

      paginationHTML += `<li class='page-item ${currentPage === 1 ? 'd-none' : 'previous'}'><a class='page-link' href='#' title='Previous'>Previous</a></li>`
      pages.forEach(page => {
        if (currentPage === page) {
          paginationHTML += `<li class='page page-item active' data-page='${page}'><a class='page-link' href='#' aria-current='page'>${page}</a></li>`;
        } else if (page === '...') {
          paginationHTML += `<li class='page-item-dots'><div>${page}</div></li>`;
        } else {
          paginationHTML += `<li class='page-item page' data-page='${page}'><a class='page-link' href='#'>${page}</a></li>`;
        }
      })

      paginationHTML += `<li class='page-item ${currentPage === endPage ? 'd-none' : 'next'}'><a class='page-link' href='#' title='Next'>Next</a></li>`

      ul.innerHTML = paginationHTML;
      pagination.append(ul);

      const start = (currentPage - 1) * pageSize,
        end = currentPage * pageSize;
      items.slice(start, end).forEach(el => {
        list.append(el);
      });
    }

    el.addEventListener('click', function(e) {
      e.preventDefault();
      const $this = e.target;

      if ($this.parentNode.classList.contains('page')) {
        currPage = parseInt($this.parentNode.getAttribute('data-page'));
        setHTML(filteredItems);
      }
      if ($this.parentNode.classList.contains('next')) {
        currPage += 1;
        setHTML(filteredItems);
      }
      if ($this.parentNode.classList.contains('previous')) {
        currPage -= 1;
        setHTML(filteredItems);
      }
    });

    setHTML(filteredItems);

  }

  const paginationblock = document.querySelectorAll('.pagination-block');
  if (paginationblock) {
    Array.from(paginationblock).forEach((el) => {
      pagination(el);
    });
  }
});
.items {
  display: flex;
  flex-direction: column;
  row-gap: .5rem;
  width: 100%;
  list-style: none;
  padding-left: 0;
}

.item {
  display: grid;
  grid-auto-rows: 1fr;
  grid-template-columns: 1fr 1fr 1fr;
  column-gap: 1rem;
  padding: .5rem 0;
}

h3 {
  margin: 0;
}

.pagination {
  display: flex;
  list-style-type: none;
  column-gap: 0.5rem;
}

.active .page-link {
  color: red;
}
<div class="pagination-block" data-pagesize="6">
  <form id="filters">
    <input type="search" name="keyword" class="search-input" placeholder="Search by keyword...">
  </form>

  <ul class="items filter-items">
    <li class="item">
      <h3>Jane Smith</h3>
      <div>Software Engineer</div>
      <div>(123) 456-7890</div>
    </li>
    <li class="item">
      <h3>John Doe</h3>
      <div>Project Manager</div>
      <div>(987) 654-3210</div>
    </li>
    <li class="item">
      <h3>John Dough</h3>
      <div>Project Coordinator</div>
      <div>(+45) 6167-3210</div>
    </li>
    <li class="item">
      <h3>John Dubois</h3>
      <div>UI Designer</div>
      <div>(321) 654-0987</div>
    </li>
    <li class="item">
      <h3>Jürgen Müller</h3>
      <div>DevOps Engineer</div>
      <div>(654) 321-7890</div>
    </li>
    <li class="item">
      <h3>Emily Johnson</h3>
      <div>UX Designer</div>
      <div>(555) 123-4567</div>
    </li>
    <li class="item">
      <h3>Michael Brown</h3>
      <div>Data Analyst</div>
      <div>(444) 987-6543</div>
    </li>
    <li class="item">
      <h3>Sarah Lee</h3>
      <div>Marketing Specialist</div>
      <div>(222) 333-4444</div>
    </li>
    <li class="item">
      <h3>David Kim</h3>
      <div>IT Support Technician</div>
      <div>(111) 222-3333</div>
    </li>
    <li class="item">
      <h3>Linda Martínez</h3>
      <div>HR Coordinator</div>
      <div>(777) 888-9999</div>
    </li>
    <li class="item">
      <h3>Renée Faure</h3>
      <div>Legal Advisor</div>
      <div>(888) 777-6666</div>
    </li>
    <li class="item">
      <h3>James Wilson</h3>
      <div>Network Engineer</div>
      <div>(666) 555-4444</div>
    </li>
    <li class="item">
      <h3>Olívia García</h3>
      <div>Financial Analyst</div>
      <div>(999) 888-7777</div>
    </li>
    <li class="item">
      <h3>Robert Patel</h3>
      <div>Product Owner</div>
      <div>(333) 444-5555</div>
    </li>
    <li class="item">
      <h3>Åsa Nordin</h3>
      <div>Cloud Architect</div>
      <div>(212) 343-4545</div>
    </li>
    <li class="item">
      <h3>Søren Kjær</h3>
      <div>AI Researcher</div>
      <div>(565) 787-8989</div>
    </li>
    <li class="item">
      <h3>Chloé Moreau</h3>
      <div>Quality Assurance</div>
      <div>(434) 232-1212</div>
    </li>
    <li class="item">
      <h3>Nikołaj Kowalski</h3>
      <div>Security Specialist</div>
      <div>(555) 666-7777</div>
    </li>
    <li class="item">
      <h3>André Silva</h3>
      <div>Scrum Master</div>
      <div>(888) 222-1111</div>
    </li>
    <li class="item">
      <h3>Zoë Chen</h3>
      <div>Technical Writer</div>
      <div>(999) 111-2222</div>
    </li>
    <li class="item">
      <h3>Frédéric Lemoine</h3>
      <div>Database Administrator</div>
      <div>(444) 123-9876</div>
    </li>
    <li class="item">
      <h3>Lucas Moretti</h3>
      <div>Business Analyst</div>
      <div>(777) 555-8888</div>
    </li>
    <li class="item">
      <h3>Mia Hernández</h3>
      <div>Content Strategist</div>
      <div>(666) 444-2222</div>
    </li>
    <li class="item">
      <h3>Léo Martin</h3>
      <div>Front-End Developer</div>
      <div>(123) 321-1234</div>
    </li>
    <li class="item">
      <h3>Isabella Rossi</h3>
      <div>Graphic Designer</div>
      <div>(432) 234-5432</div>
    </li>
    <li class="item">
      <h3>Olivier Dubois</h3>
      <div>Marketing Manager</div>
      <div>(555) 789-0123</div>
    </li>
    <li class="item">
      <h3>Fatima Al-Hassan</h3>
      <div>HR Manager</div>
      <div>(321) 654-4321</div>
    </li>
    <li class="item">
      <h3>Tomáš Novák</h3>
      <div>Software Tester</div>
      <div>(654) 987-1234</div>
    </li>
    <li class="item">
      <h3>Sophia Becker</h3>
      <div>Customer Success</div>
      <div>(789) 456-0987</div>
    </li>
    <li class="item">
      <h3>Mateo Fernández</h3>
      <div>Operations Manager</div>
      <div>(987) 654-3219</div>
    </li>
    <li class="item">
      <h3>Amélie Laurent</h3>
      <div>Sales Representative</div>
      <div>(123) 456-6789</div>
    </li>
    <li class="item">
      <h3>Jan Kowalski</h3>
      <div>Technical Support</div>
      <div>(321) 654-7890</div>
    </li>
    <li class="item">
      <h3>Yara Haddad</h3>
      <div>Project Coordinator</div>
      <div>(555) 333-2222</div>
    </li>
    <li class="item">
      <h3>Lucas Thompson</h3>
      <div>IT Manager</div>
      <div>(444) 123-4567</div>
    </li>
    <li class="item">
      <h3>Ingrid Svensson</h3>
      <div>Business Development</div>
      <div>(777) 987-6543</div>
    </li>
    <li class="item">
      <h3>Daniela Petrova</h3>
      <div>Data Scientist</div>
      <div>(222) 444-5555</div>
    </li>
    <li class="item">
      <h3>Akira Tanaka</h3>
      <div>Software Architect</div>
      <div>(111) 555-6666</div>
    </li>
    <li class="item">
      <h3>Clara Jensen</h3>
      <div>Content Writer</div>
      <div>(888) 999-0000</div>
    </li>
  </ul>
  <div class="pagination-nav"></div>
</div>

How to retrieve key backup on matrix-js-sdk?

I’m trying to code a groupchat app with the matrix js sdk. I’m a bit struggling with the keybackup. I create it but when i logout and login again, i have the error below. Idk if the problem comes from how i created it, how i recover it or the secret storage etc …

Here is my function:


    private async bootstrapKeyBackup(): Promise<void> {
        const crypto = this.client.getCrypto();
        if (!crypto) throw new Error('[CryptoService] Crypto API not available!');
    
        console.log('[CryptoService] Vérification du backup de clés...');
    
        const backupInfo = await crypto.checkKeyBackupAndEnable();
    
        if (backupInfo) {
          console.log('[CryptoService] Backup existant détecté : ', backupInfo);
    
          const passphrase = CryptoService.getPassphrase();
    
          const restoreResult = await crypto.restoreKeyBackupWithPassphrase(
            passphrase
          );
          console.log(
            '[CryptoService] Clés restaurées depuis le backup :',
            restoreResult
          );
          return;
        }
    
        console.log('[CryptoService] Aucun backup trouvé, création...');
    
        try {
          await crypto.resetKeyBackup();
          console.log('[CryptoService] Nouveau Key Backup configuré !');
        } catch (err) {
          console.error('[CryptoService] Échec création Key Backup :', err);
        }
      }

logs on the first login

logs after logout+login

Error: Uncaught (in promise) Error: Salt and/or iterations not found: this backup cannot be restored with a passphrase

Thanks a lot !

Invalid_client when using sign in with apple in browser

I am new to sign in wtih apple. When I trying to call sign in method, it will show a popup window, and show me the invalid_client error, here is my steps:

  1. create app id: com.yuhan.test.app
    create app id
  2. create service id: com.yuhan.test.service
    create service id
  3. configure my domain and returnURLs
    domain: tts.perterpon.com
    returnURL: https://tts.perterpon.com/login
    domain and returnURLs
  4. create keys for sign in with apple.
    keys
  5. code html, you can visit it here: https://tts.perterpon.com/login.html

Does anybody has some ideas? Thank you so so so much.

<html lang="en">
  <head>
    <meta charset="UTF-8">
    <meta name="description" content="">
    <meta name="keywords" content="">
    <title>Apple Login</title>
  </head>
  <body>
    <script type="text/javascript"
            src="https://appleid.cdn-apple.com/appleauth/static/jsapi/appleid/1/en_US/appleid.auth.js"></script>
    <div id="appleid-signin"
         data-color="black"
         data-border="true"
         data-type="sign in"></div>
    <script type="text/javascript">
    AppleID.auth.init({
      clientId: 'com.yuhan.test.service',
      scope: 'name email',
      redirectURI: 'https://tts.perterpon.com/login',
      usePopup: true
    });

    const buttonElementNew = document.getElementById('appleid-signin');
    buttonElementNew.addEventListener('click', async () => {
      try {
        const data = await AppleID.auth.signIn()

        console.log('Try/Catch Data', data.authorization.id_token);
        const formData = new FormData();
        formData.append("token", data.authorization.id_token);
        await fetch("", {
            method: "POST",
            body: formData,
        });          
        // Handle successful response.
      } catch (error) {
        // Handle error.
      }
    });
    </script>
  </body>
</html>

How can Redux-Toolkit Query send a variable to request headers?

I currently want to add meta data to the RKT Query request header and it’s a variable. I want to pass it from the API request.

export const api = createApi({
  reducerPath: 'api',
  baseQuery: fetchBaseQuery({
    baseUrl: '/api/',
    prepareHeaders: (headers, api) => {
      const { apiSource } = api
      // currently want to pass apiSource from the request somewhere, 
      // then get this value and add it to header
      headers.set('api-source', apiSource);
      return headers;
    },
  }),
  endpoints: (builder) => ({
    getUsers: builder.query({
      query: () => `/getUsers`,
    }),
  }),
});

I want to have some ways I can pass it from the request, like:

// but this is not working now, I can't pass the variable
const { data } = useGetUsers(body, {apiSource})

On my scenario, I have multiple APIs, not only this getUsers. That’s why I want to find a way on prepareHeaders.

Is there a way can to this?

How can RTK Query send an variable to header

I currently want to add a meta data to the RKT Query header and it’s an variable and want to pass it from the api request.

export const api = createApi({
    reducerPath: 'api',
    baseQuery: fetchBaseQuery({
        baseUrl: '/api/',
        prepareHeaders: (headers, api) => {
            const {apiSource} = api
            // currently want to pass apiSource from the request somewhere, 
            // then get this value and add it to header
            headers.set('api-source', apiSource);
            return headers;
        },
    }),
    endpoints: (builder) => ({
        getUsers: builder.query({
            query: () => `/getUsers`,
        }),
    }),
});

So want to have some ways can pass it from the request, like

// but this is not working now, I can't pass the variable
const { data } = useGetUsers(body, {apiSource})

Is there a way can to this?

How to copy google map marker design?

I am writing a JavaScript code using Google Map API. I want to display specific places using pins and I want the pins to be like in the actual google maps. How do I achieve it?

enter image description here

Basically, what I’m trying to do is draw every location in my array of static locations and that’s it.

Example code:

const map = new google.maps.Map(document.getElementById("map"), { zoom: 4, { lat: 8.3450235, lng: 124.430038}});
let places = [
    {
        displayName: 'Bukidnon Province Welcome Marker',
        lat: 8.395121,
        long: 124.7823285
    },
    {
        displayName: 'Ogaong View Deck',
        lat: 8.395121,
        lng: 124.7823285
    }
];

places.forEach((place) => {
    new google.maps.Marker({
        position: { lat: place.lat, lng: place.lng },
        map,
        title: "Hello World!",
    });
});

JS execution in Chromium slower than PhantomJS

I’m migrating from PhantomJS to Chromium headless (via Puppeteer) for HTML/CSS manipulation. Surprisingly, the JS execution in Chromium are is about 10x slower than in PhantomJS.

  • The code primarily iterates through the DOM and calls simple functions (I have commented out browser API calls)
  • JS execution is direct in PhantomJS
  • In Chromium, TypeScript is compiled to JS using webpack (not minified/obfuscated)
  • Performance testing shows operations are ~10x slower in Chromium

Additional Observation

The Puppeteer auto-downloaded Chromium binary performs about 2x faster than my locally built version on Linux. I am building with is_debug = false.

Questions

  1. Why is Chromium significantly slower than PhantomJS for these operations?
  2. What could explain the performance difference between the auto-downloaded and locally built Chromium?

Angular stand alone components with nested child and routing

I am refactoring my existing code base to standalone architecture. I am facing issue with the routing. Below is my current route module

  const ROUTES: Routes = [
  {
    path: '',
    component: ParentComponent,
    children: [
      {
        path: 'alt',
        component: HomePageComponent,
        children: [
          {
            path: 'abc',
            loadChildren: () =>
              import(
                '<path to ABC Module>'
              ).then((m) => m.AbcModule)
          },
          {
            path: 'xyz',
            loadChildren: () =>
              import('path to xyz module').then(
                (m) => m.XyzModule
              )
          },
          
        ]
      }
    ]
  }
];

ABC module routing

  const ROUTES: Routes = [
  {
    path: '',
    component: AbcComponent,
    children: [
      {
        path: 'Child1',
        component: Child1Component
      },
      {
        path: 'child2',
        component: Child2Component
      },
      {
        path: '',
        pathMatch: 'full',
        redirectTo: 'child1'
      }
    ]
  }
];

The above two are part of my MFE and have a side bar which is in the parent project with the following routing configuration

[
    {
      label: 'ABC',
      route: `/alt/abc`,
    },
    {
      label: 'Xyz',
      route: `/alt/xyz`,
    }
  ];

This is working as expected in the existing ngModule architecture. Issue comes when I changed it to standalone architecture with the following routes and main.ts

app.route.ts

export const APP_ROUTES: Routes = [
  {
    path: '',
    component: AppComponent,
    children: [
      {
        path: 'alt',
        loadChildren: () =>
          import('<path to parent component>').then((m) => m.PARENT_ROUTES)
      }
    ]
  }
];

Parent Component route

export const PARENT_ROUTES: Routes = [
  {
    path: '',
    loadComponent: () =>
      import('./parent.component').then((m) => m.ParentComponent),
    children: [
      {
        path: 'abc',
        loadChildren: () =>
          import('<path to ABC route>').then((m) => m.ROUTES)
      },
      {
        path: '',
        redirectTo: 'abc',
        pathMatch: 'full'
      },
      {
        path: '**',
        redirectTo: 'error',
        pathMatch: 'full'
      }
    ]
  }
];

ABC Component Route

export const ROUTES: Routes = [{
  path: '',
  loadComponent: () =>
  import('<Path to ABC component >').then(
    (m) => m.ABCComponent
  ),
  children: [
    {
        path: '',
        pathMatch: 'full',
        redirectTo: 'child1'
      },

    {
      path: 'child1',
      loadComponent: () =>
        import('path to child 1 component').then((m) => m.Child 1 component),
    }
  ]
}
];

I just added the routes only for ABC and its child child1.

Expectation and Current behaviour

When the user clicks on ABC from the side bar it should navigate to the ABC component and load its child child1 with the route alt/abc/child1. but its navigating only till alt/abc. I have added <router-outlet></router-outlet> in ParentCOmponent, ABC components as well.

main.ts

bootstrapApplication(AppComponent, appConfig)

Also I have added provideRouter(APP_ROUTES) in app.config.ts.

A new web framework Canonicaljs [closed]

I heard about a new web framework called CanonicalJS, I would like to download it and try it out. where could I find it ?

I tried canonicaljs.com but it seems they didn’t put the download link yet, after downloading it, I would like to create a hello word project, what steps should I follow?

I tried the following steps:

  1. Create a folder under www I named it hello_word
  2. I unzipped canonicaljs inside it
  3. I created an index.html file with the html, head and body tags in it

What else?

placing clickable element on top of google map (created with google maps api) and beneath infoWindow

i’m creating a map with google maps api. i have a custom button that i need to hover over top of the map, but i also need for any infoWindows that are opened to be displayed in a higher stacking order than this button. i have tried creating it as an overlay, but when i do, it is displayed in the vertical center of the map due to one of the parent div’s transform. i tried adding it to the maps controls array and, it is displayed on top of the infoWindow.

This is the closest I’ve come where I insert it into the layer with the infoWindow just beneath it in the stacking order. While this displays correctly, all clicks are intercepted by the map object.

  _setupMapModeOverlay() {
    if (!this.isMobile) return;
    const $overlay = document.createElement('div');
    $overlay.className = 'map-mode-overlay-bar';
    $overlay.innerHTML = `<p><strong>Enhanced Map View</strong> active. <button aria-label="exit map view">Back to default page view</button></p>`;

    google.maps.event.addListenerOnce(this.map, 'idle', () => {
      const gmStyleDiv = this.$mapwrap.querySelector(
        '.gm-style > div:nth-child(1)'
      );
      if (gmStyleDiv) {
        gmStyleDiv.appendChild($overlay);
        google.maps.OverlayView.preventMapHitsFrom($overlay);
        google.maps.OverlayView.preventMapHitsAndGesturesFrom($overlay);
        console.log('Map mode overlay added to the target map pane.');
        $overlay.addEventListener('click', (e) => {
          e.stopPropagation(); // Prevent map click event
          document.body.classList.remove('isMapMode');
        });
      } else {
        console.warn('Could not find the target map pane.');
      }
    });
  }

I’m kind of out of ideas – anyone have any suggestions?

How do I process an image as a R2Object Body in areact-router loader function?

I have built a react-router “framework mode” application to test some Cloudflare bindings. The KV and D1 bindings work great and I was able to pull a text file from R2 in loader() and process it fine. I am not able to come up with the secret sauce to work with an image file however. I have this working fine in a React SPA, the Cloudflare fetch() just processes the file in client mode. But on the server side, there is no URL.createObjectURL().

My current code is pasted below, with some typescript errors inline. What do I need to do to make the image file available in component code?

import type { Route } from "./+types/r2_image";

export function meta({ }: Route.MetaArgs) {
  return [
    { title: "Data Tests: R2 image file" },
    { name: "description", content: "Testing cloudflare bindings." },
  ];
}

export async function loader({ context }: Route.LoaderArgs) {

  // Type 'R2ObjectBody | null' is not assignable to type 'R2ObjectBody'.
  // Type 'null' is not assignable to type 'R2ObjectBody'.ts(2322)
  //    vvvvvvv
  const r2Image: R2ObjectBody = await context.cloudflare.env.CROMR2.get('conan.png');

  var image = await r2Image.blob();
  return { data: image };

}

export default function R2Image({ loaderData }: Route.ComponentProps) {

  // Types of property 'arrayBuffer' are incompatible.
  //   Type 'undefined' is not assignable to type '() => Promise<ArrayBuffer>'.ts(2345)
  //                                vvvvvvvvvv
  const image = URL.createObjectURL(loaderData.data);
  // or
  // const image = URL.createObjectURL(loaderData.arrayBuffer);


  return (
    <>
      <h1>R2 Image File</h1>
      <div>
        <img src={image} />
      </div>
    </>
  )
}

This code dumps an error to the console. I believe it is due to the type mismatch in the argument to createObjectUrl()?

Failed to execute 'createObjectURL' on 'URL': Overload resolution failed.

Note: The cloudflare worker fetch() does not seem to be available to clientLoader(). Plus, I would rather get this right from the server side.