Own Media query wont behave Im using tailwind CSS and Next JS

Hello Im using nextJS application and Tailwind CSS, in the local it working properly and nothing problem however when I deploy it on the vercel the media query is not taking effect as if they being ignored. Thanks a lot

 <div className="flex grow items-center justify-end sm:hidden mobile">
      <PopoverButton className="inline-flex items-center justify-center rounded-md p-2 hover:bg-gray-100 hover:text-gray-500 focus:outline-none focus:ring-2 focus:ring-inset focus:ring-indigo-500 openmenu">
        {({ open }) => (
          <>
            <span className="sr-only">
              {open ? "Close Menu" : "Open Menu"}
            </span>
            {open ? (
              <XMarkIcon className="h-6 w-6" aria-hidden="true" />
            ) : (
              <Bars3Icon className="h-6 w-6" aria-hidden="true" />
            )}
          </>
        )}
      </PopoverButton>
    </div>

This is my html structure and below is the my code for media query when they browse on small screen

@media screen and (max-width: 1023px) {
  .menu-link {
    display: none;
  }

  .mobile {
    display: inherit;
    justify-content: left;
    margin-left: -10rem;
  }
}

@media screen and (max-width: 768px) {
  .mobile {
    display: inherit;
    margin-left: -2rem;
  }
}

@media screen and (max-width: 639px) {
  .mobile {
    justify-content: end;
  }
}

In local It display this one and seems it working fineIt display this one and seems it working fine

However when I uploaded it to the vercel tru github it wont show properly enter image description here

VSCode JS Library autocomplete not working when imported on TS file

I installed PURE JS library on npm
it dont have @types or d.ts file
and i imported it. library works as excepted but VSCode autocomplete wont work

I tried just importing it with tsconfig as following:
“compilerOptions”: {
“target”: “es6”,
“module”: “Node16”,
“strict”: false,
“esModuleInterop”: true,
“skipLibCheck”: true,
“forceConsistentCasingInFileNames”: true,
“allowJs”: true,
“checkJs”: false,
“moduleResolution”: “node16”,
“outDir”: “./dist”,
“baseUrl”: “.”
} …
it all worked as excepted
using ES6 style importing works, CommonJS style import works
but vscode wont autocomplete anything
i googled it but i only get JS autocomplete not working on JS file

Full calendar: initialization, funtionalities doon’t work properly on MI os

The js code is not loading properly on MI os, on all others it works.

I have created a calendar that has a month view with green and gray day slots, green are clicktable and TimegridDay view that show the working time of day and slots that are clicktable and not(green and gray).TimegridDay is not working properly only on Xiaomi devices. Onall computer type (OS) and another mobile os is working fine, except Xiaomi!

Initialization of colors of timegridDay is totaly wrong, not controled by the code, the working hours are not set as i tried in code. PointerEvents- none is not placed on all needed slots.

I have overided the touch logic, because of user experience…

This is the code of calendar:

$(document).ready(function() {
    window.addEventListener('pageshow', function(event) {
        if (sessionStorage.getItem('refreshCalendar') === 'true') {
            sessionStorage.removeItem('refreshCalendar'); 
            window.location.reload();
        }
    });
    const disabledSlots = new Set();
    const disabledDates = new Set()
    let isTouchDevice = ('ontouchstart' in window) || (navigator.maxTouchPoints > 0);
    console.log("da lie je touch", isTouchDevice);
    var calendar;
    var csrfToken = document.querySelector('[name=csrfmiddlewaretoken]').value;
    // Globalne promenljive za touch događaje
                    
    let touchStartTime = 0;
    let touchStartX = 0;
    let touchStartY = 0;
    let isScrolling = false;
    let touchDuration = 0;
    let deltaX = 0;
    let deltaY = 0;
    const moveThreshold = 10;
    const tapThreshold = 100; // Vremenski prag za tap

    // Funkcija za dodavanje touch događaja
    function addTouchEvents() {
        const calendarElement = document.getElementById('calendar');

        // touchstart događaj
        calendarElement.addEventListener('touchstart', function(e) {
            touchStartTime = Date.now();
            touchStartX = e.changedTouches[0].screenX;
            touchStartY = e.changedTouches[0].screenY;
            isScrolling = false;
            console.log('pocetak dodira');
            // Pribavljanje vremena iz klika
            let targetCell = e.target.closest('.fc-timegrid-slot');
            
            if (targetCell) {
                let timeString = targetCell.getAttribute('data-time'); // Dobijanje vremena
                let activeDate = calendar.getDate(); // Trenutni datum u kalendaru

                // Kombinovanje datuma i vremena u jedan Date objekat
                selectedDateTime = new Date(activeDate.toDateString() + ' ' + timeString);
                console.log('Izabrano vreme:', selectedDateTime);
                
                
            }
            
        }, { passive: true });

        // touchmove događaj
        calendarElement.addEventListener('touchmove', function(e) {
            isScrolling = true; // Ako korisnik pomera prst, setuj da je skrolovanje u toku
            console.log('skroluje');
        }, { passive: true });

        // touchend događaj
        calendarElement.addEventListener('touchend', function(e) {
            let touchEndTime = Date.now();
            touchDuration = touchEndTime - touchStartTime;

            let touchEndX = e.changedTouches[0].screenX;
            let touchEndY = e.changedTouches[0].screenY;
            deltaX = Math.abs(touchEndX - touchStartX);
            deltaY = Math.abs(touchEndY - touchStartY);
        }, { passive: true });
    }

    $('#locations').change(function() {
        var location = $('#locations').val();
        console.log(location)
        //on every change hide calendar because procedure field is reseted
        hideAndDestroyCalendar();
        if (location) {
            // Fetch available procedures for the selected location
            $.ajax({
                url: reservationUrl,  // Update with the correct URL for fetching procedures
                method: 'POST',
                data: {
                    'action': 'get_procedures',
                    'location': location,
                    csrfmiddlewaretoken: csrfToken
                },
                success: function(response) {
                    // Populate the procedures dropdown
                    $('#procedures').html('<option value="">'+ selectLocationText +'</option>');
                    if (response.procedures.length > 0) {
                        response.procedures.forEach(function(procedure) {
                            $('#procedures').append('<option value="' + procedure.name + '">' + procedure.name + '</option>');
                        });
                        $('#procedures').prop('disabled', false); // Enable the procedures dropdown
                    } else {
                        $('#procedures').html('<option value="">'+ErrorText+'</option>');
                        $('#procedures').prop('disabled', true);
                        hideAndDestroyCalendar();
                    }
                },
                error: function() {
                    $('#procedures').html('<option value="">'+ErrorText+'</option>');
                    hideAndDestroyCalendar();
                }
            });
        } else {
            // Reset procedures dropdown if no location is selected
            $('#procedures').html('<option value="">'+selectLocationText+'</option>');
            $('#procedures').prop('disabled', true);
            hideAndDestroyCalendar()
        }
    });




    $('#procedures').change(function() {
    
        var location = $('#locations').val();
        
        var procedure = $('#procedures').val();
        
        
        
        if(location && procedure) {
            
            $('#calendar').show();
            document.querySelector(".status-container").style.display = "flex";
            if(calendar) {
                calendar.destroy();
            }
            disabledDates.clear()
            disabledSlots.clear()
            $.ajax({
                url: reservationUrl,
                method: 'POST',
                data: {
                    'action': 'get_available_dates',
                    'location': location,
                    'procedure': procedure,
                    csrfmiddlewaretoken: csrfToken
                },
                
                success: function(response) {
                    var availableDates = response.available_dates;
                    var events = response.events;
                    var proDuration = response.slottime
                    let formatedSlottime = "00:" + String(proDuration).padStart(2, '0');    
                    console.log("dostupni dani",availableDates)
                    // Pronalaženje prvog dostupnog termina
                    let firstAvailableSlotTime = null;

                    for (let i = 0; i < availableDates.length; i++) {
                        let startDate = new Date(availableDates[i].start);
                        if (!firstAvailableSlotTime || startDate < firstAvailableSlotTime) {
                            firstAvailableSlotTime = startDate;
                        }
                    }

                    // Formatiraj vreme prvog dostupnog slota za `scrollTime`
                    let scrollTime = firstAvailableSlotTime
                        ? `${String(firstAvailableSlotTime.getHours()).padStart(2, '0')}:${String(firstAvailableSlotTime.getMinutes()).padStart(2, '0')}:00`
                        : '09:00:00'; // Defaultno vreme ako nema dostupnih slotova

                    //
                    calendar = new FullCalendar.Calendar(document.getElementById('calendar'), {
                        locale: 'sr-latn',
                        buttonText: {
                            today: 'Danas',  
                            month: 'Mjesec',  
                            week: 'Sedmica', 
                            day: 'Dan'       
                          },


                          dayHeaderContent: function(info) {
                            const dayNames = [
                                'Nedelja',
                                'Ponedeljak',
                                'Utorak',
                                'Srijeda',
                                'Četvrtak',
                                'Petak',
                                'Subota'
                            ];
                            const dayNamesShort = ['Ned', 'Pon', 'Uto', 'Sri', 'Čet', 'Pet', 'Sub'];
                            const dayIndex = info.date.getDay(); // Dobija indeks dana od 0 (Nedjelja) do 6 (Subota)
                            if (info.view.type === 'dayGridMonth') {
                                // Ako je mjesečni prikaz, koristi skraćenice
                                return dayNamesShort[dayIndex];
                              } else {
                                // U ostalim prikazima, koristi pune nazive
                                return dayNames[dayIndex];
                              }
                        }, 

                        allDaySlot: false,
                        height: 650,
                        scrollTime: scrollTime,
                        eventDisplay: 'auto',
                        //nowIndicator: true, 
                        
                        views: {
                            dayGridMonth: {  
                              eventDisplay: 'none',
                            },
                            timeGridDay: { 
                                eventDisplay: 'auto',
                            }
                          },
                        timeZone: 'local',
                        
                        headerToolbar: {
                            left: 'prev,next today',
                            center: 'title',
                            right: 'dayGridMonth,timeGridDay'
                        },
                        selectable: true,

                        selectOverlap: false,
                        editable: false,
                        eventDurationEditable: false,
                        slotDuration: "00:05",//formatedSlottime,
                        //height: 'auto',  
                        contentHeight: 'auto',  
                        initialDate: availableDates[0].start,
                        //callback koji se poziva kada se klikne na neki datum ili na slot
                        dateClick: function(info) {
                            console.log('dateclicked')
                            now = new Date()
                            nowLocale = new Date(now.toLocaleString())
                            const clickedLocale = info.date.toLocaleString()
                            const clickedLocaleForm = new Date (clickedLocale);
                            const clickedLocaleDate = clickedLocale.split(',')[0]
                            /*console.log(nowLocale);
                            console.log(clickedLocaleForm);
                            console.log(disabledSlots)
                            console.log(disabledDates)
                            console.log(clickedLocaleForm <= nowLocale)
                            console.log(disabledDates.has(clickedLocaleDate))
                            console.log(disabledSlots.has(clickedLocale))*/

                            // Provera sa `disabledSlots`
                            if (disabledSlots.has(clickedLocale)  || disabledDates.has(clickedLocaleDate)) {
                                alert(forbbidenMessage)
                                console.log("Kliknut je onemogućen slot provera disableslots.");
                                return;
                            }
                            else {
                                let currentView = calendar.view.type;
                            
                                if (currentView === 'dayGridMonth') {
                                    // Ako je trenutni prikaz 'month', prebacujemo na 'timeGridDay'
                                    calendar.changeView('timeGridDay', info.dateStr);

                                }
                                //ako je view timgridday i nije mobilni i ako je kliknuto vreme manje od trenutnog return
                                else if (currentView === 'timeGridDay' && !isTouchDevice) {
                                    info.dayEl.style.pointerEvents = 'none'
                                    if (clickedLocaleForm < nowLocale) {
                                        console.log("Kliknut je onemogućen slot.");
                                        return;

                                    }

                                    //console.log(info.dayEl.style.pointerEvents);
                                    //console.log(!isTouchDevice)
                                    // Ako je već 'timeGridDay', izvršavamo AJAX zahtev
                                    if(confirm(questionMessage)){
                                        $.ajax({
                                            url: reservationUrl,
                                            method: 'POST',
                                            data: {
                                                'action': 'create_reservation',
                                                'start': info.dateStr,
                                            'location': $('#locations').val(),
                                            'procedure': $('#procedures').val(),
                                            'note': note.value,
                                            csrfmiddlewaretoken: csrfToken
                                            },
                                            success: function(response, end) {
                                                
                                                if (response.status === 'success') {
                                                    calendar.addEvent({
                                                        start: info.dateStr,
                                                        end: end, 
                                                        title: "",
                                                    });
                                                    alert(successMessage);
                                                    info.dayEl.style.pointerEvents = 'auto'
                                                    //console.log(info.dayEl.style.pointerEvents);
                                                    window.location.href = response.redirect_url; 
                                                } else {
                                                    alert(response.message);
                                                }
                                            }
                                        });
                                    }
                            
                                    else{
                                        console.log('Rezervacija je otkazana od strane korisnika.');
                                    }
                                    
                                }
                                //ako nije zabelezno skrolovanje nije dugi pritisak i jeste mob, generise se klik
                                else if (!isScrolling && touchDuration < tapThreshold && deltaX < moveThreshold && deltaY < moveThreshold && calendar.view.type === 'timeGridDay' && isTouchDevice) {
                                    console.log('generiše se klik');
                                    info.dayEl.style.pointerEvents = 'none'
                                    if (clickedLocaleForm < nowLocale) {
                                        console.log("Kliknut je onemogućen slot.");
                                        return;

                                    }
                                    console.log(info.dayEl.style.pointerEvents);
                                    console.log(touchDuration);
                                    console.log(deltaX);
                                    console.log(deltaY);
                                    if(confirm(questionMessage)){
                                        // Izvršavanje AJAX poziva
                                        $.ajax({
                                            url: reservationUrl,
                                            method: 'POST',
                                            data: {
                                                'action': 'create_reservation',
                                                'start': selectedDateTime.toISOString(), // Start vreme iz info objekta
                                                'location': $('#locations').val(), // Uzimanje vrednosti lokacije
                                                'procedure': $('#procedures').val(), // Uzimanje vrednosti procedure
                                                'note': note.value, // Vrednost beleške
                                                csrfmiddlewaretoken: csrfToken // CSRF token
                                            },
                                            success: function(response, end) {
                                                console.log(response.status);
                                                if (response.status === 'success') {
                                                    calendar.addEvent({
                                                        start: selectedDateTime.toISOString(),
                                                        end: end,
                                                        title: "", 
                                                    });
                                                    alert(successMessage);
                                                    info.dayEl.style.pointerEvents = 'auto'
                                                    console.log(info.dayEl.style.pointerEvents);
                                                    window.location.href = response.redirect_url; 
                                                } else {
                                                    calendar.refetchEvents();
                                                    alert(response.message);
                                                }
                                            }
                                        });
                                    }
                            
                                    else{
                                        console.log('Rezervacija je otkazana od strane korisnika.');
                                    }
                                }
                            }
                            
                        },
                        

                        //poziva se pre nego se <td> element doda DOM-u za prikaz dana
                        dayCellDidMount: function(info) {
                            
                            var cellDate = info.date; 
                            var isAvailable = availableDates.some(function(item) {
                                startDate = new Date(item.start);
                                endDate = new Date(item.end);
                              
                                //proverava da li je neki dan u proslosti i vraca true za taj dan, dodaj mu zelenu boju
                                return (cellDate.getFullYear() === startDate.getFullYear() &&
                                        cellDate.getMonth() === startDate.getMonth() &&
                                        cellDate.getDate() >= startDate.getDate() &&
                                        cellDate.getDate() <= endDate.getDate());
                            });
                            
                            if (isAvailable) {
                                
                                info.el.style.backgroundColor = '#d1e8c2';  
                                info.el.style.color = 'black'; 

                            } // ukoliko nije true dodaje mu sivu boju, dodaje taj datum u disabledDates i pointerevnets postavlja na none
                            else {
                                localCellDate =cellDate.toLocaleString()
                                info.el.style.backgroundColor = '#d3d3d3'; 
                                info.el.style.pointerEvents = "none";                                
                                disabledDates.add(localCellDate.split(',')[0])
                                console.log("disabledDates:", disabledDates)
                                
                            }
                        },
                        events: events,
                        //ne prikazuje eventove u month view-u
                        eventDidMount: function(info) {
                            if (calendar.view.type === 'dayGridMonth') {
                                info.el.style.display = 'none';  
                            }
                        },
       
                    });
                    console.log(isTouchDevice);
                    //dodaje touchevents listner ukoliko je touch uredjaj
                    if (isTouchDevice === true) {
                        console.log("jeste touch", isTouchDevice);

                        addTouchEvents();
                    }
                     
                    calendar.on('datesSet', function(info) {
                        //reset promenljivih kada se promeni datum ili prikaz
                        
                        
                        touchStartTime = 0;
                        touchStartX = 0;
                        touchStartY = 0;
                        selectedDateTime = null;
                        isScrolling = false;
                        calendar.render()
                        calendar.refetchEvents();
                        console.log('dateclicked')
                        if (calendar.view.type === 'timeGridDay') {
                            addTouchEvents();
                            const currentDate = new Date(info.startStr).toLocaleString().split(',')[0]; 
                            let slots = document.querySelectorAll('.fc-timegrid-slots tr ');;
                            let nowDate = new Date(); 
                            let lastAvbailableDate = new Date(Math.max(...availableDates.map(item => new Date(item.end).getTime())));
                            let firstAvailableDate = new Date(Math.min(...availableDates.map(item => new Date(item.start).getTime())));
                         
                            //vraca eventove za dan na kojem se nalazi
                            let events = calendar.getEvents().filter(event => {
                            let eventDate = event.start.toLocaleString().split(',')[0];
                            
                            return eventDate == currentDate;
                            });
                          
    
                            slots.forEach(function(slot) {
                                
                                let timeString = slot.querySelector('td').getAttribute('data-time');
                                let activeDate = info.view.currentStart; 
                                
                                let slotDateTime = new Date(activeDate.toDateString() + ' ' + timeString);
                                let slotDateTimeLocal  = slotDateTime.toLocaleString()
                                console.log("slotDateTime",slotDateTime)
                                console.log("slotDateTimeLocal",slotDateTimeLocal)
                                console.log("lastAvbailableDate",lastAvbailableDate)
                                console.log("firstAvailableDate",firstAvailableDate)
                                console.log("slot manji od sada",slotDateTime < nowDate)
                                console.log("slot veci od poslednjeg dostupnog",slotDateTime > lastAvbailableDate )
                                console.log("slot manji od prvog dostupnog",slotDateTime < firstAvailableDate)
                                console.log("datum slota se nalazi u nedozvoljenim datumima",disabledDates.has(slotDateTimeLocal.split(',')[0]))
                                console.log(disabledSlots)
                                //onemogucava kliktanje na slotove u proslosti i buducnosti(u odnosu na poslednji dostupni dan)
                                if (disabledDates.has(slotDateTimeLocal.split(',')[0]) || slotDateTime < nowDate || slotDateTime > lastAvbailableDate || slotDateTime < firstAvailableDate) {
                                    
                                    slot.style.backgroundColor = '#d3d3d3';
                                    slot.style.pointerEvents = 'none';
                                    console.log('kliknuli ste onemogucen slot');
                                    console.log('ehej')
                                    
                                    
                                }
                                else {
                                    let availableSlot = true;
                                    

                                    let procedureEndTime = new Date(slotDateTime.getTime() + proDuration * 60000); 
                            
                                    for (let i = 0; i < events.length; i++) {
                                        let eventStart = (events[i].start);
                                        let eventEnd = (events[i].end);
                    
                                        //proverava da li je slot dostupan, tacnije da li trajanje procedure predugacko da bi slot bio dostupan,da ne upada u postojecu rez, dodaje se zelena tamnija boja i pointer-events none.
                                        if ((eventStart <= slotDateTime && eventEnd > slotDateTime) ||
                                            (procedureEndTime > eventStart && procedureEndTime <= eventEnd) ||  
                                            (slotDateTime < eventStart && procedureEndTime > eventStart)) {  
                                            slot.style.backgroundColor = '#8fdf82';
                                            slot.style.setProperty('pointer-events', 'none', 'important');
                                            let formatSlotDateTime = slotDateTime.toLocaleString()
                                            disabledSlots.add(formatSlotDateTime)
                                       
                                            
                                            availableSlot = false;
                                            break;
                                        }
                                        if (availableSlot) {
                                            slot.style.backgroundColor = '#d1e8c2';
                                            slot.style.pointerEvents = 'auto';
                                        }
                                    
                                    }  
                                }
                                
                            
                            });
                            //dinamicki radno vreme postavlja
                            availableDates.forEach(date => {
                            
                                if (date.start.split('T')[0]==currentDate) {
                                    console.log("radno vreme" ,date.start.split('T')[1])
                                    calendar.setOption('slotMinTime', date.start.split('T')[1] || '09:00');
                                    calendar.setOption('slotMaxTime', date.end.split('T')[1] || '18:00'); 
                                    
                                } 
                            
                            })
                         
                    }
                    });
                    
                    calendar.render();
                    
                 
                    
                }

                
            });
        } else {
            $('#calendar').hide();
            hideAndDestroyCalendar();
        }
       
    });
    $('#calendar').hide();
    hideAndDestroyCalendar();

    // Function to hide and destroy calendar
    function hideAndDestroyCalendar() {
        $('#calendar').hide();
        if (calendar) {
            calendar.destroy();
        }
    }


});

Help me please:kostarasovic25gmail.com

I have created a calendar that has a month view with green and gray day slots, green are clicktable and TimegridDay view that show the working time of day and slots that are clicktable and not(green and gray).TimegridDay is not working properly only on Xiaomi devices.

Initialization of colors of timegridDay is totaly wrong, not controled by the code, the working hours are not set as i tried in code. PointerEvents- none is not placed on all needed slots.

I am having re rendering issue with react pdf. Can someone please help me?

const [numPagesState, setNumPagesState] = useState(0);
const allCertificates = formik?.values?.certificate_file || [];

{allCertificates?.length > 0 &&
allCertificates?.map((el) => {
return (
<>

Course Completion Certificate

<div
onClick={() => {
setSelectedPreview(el);
setOpenCertificateModal(true);
}}
>

<div
onClick={() => {
setDeleteCertificateModal(true);
}}
>

                  <div className="document-preview-form" ref={resizeRef}>
                    <Document
                      file={el?.file_url || "/blank.pdf"}
                      onLoadSuccess={({ numPages }) =>
                        setNumPagesState(numPages ?? 0)
                      }
                    >
                      {Array?.apply(null, Array(numPagesState))
                        ?.map((x, i) => i + 1)
                        ?.map((page) => (
                          <Page
                            key={page}
                            width={width ? width : 1}
                            pageNumber={page}
                          />
                        ))}
                    </Document>
                  </div>
                </div>
              </>
            );
          })}

Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

I am getting above error

I am expecting my PDF files to load one below the other as I have used map method

Service Worker Caching Works on Localhost, But Not in Production

I’ve created a Progressive Web App (PWA) using React, and I’m facing an issue with caching in my service worker. The caching functionality works perfectly when running the app locally (localhost), but it doesn’t seem to work when deployed to production.

this is a code


const CACHE_NAME = 'v1_cache';


this.addEventListener("install", (event) => {
    console.log("coming in the cache store event ")
    event.waitUntil(
        caches.open(CACHE_NAME).then((cache) => {
            console.log('caching files')

              cache.addAll([
                "/2.d7a32088.chunk.js", "/main.cace1e96.chunk.js", "react_devtools_backend.js", "/inject.js","/index.html"
            ])
        })
    )
    
})


this.addEventListener("fetch", (event) => { 
    console.log("coming in the fetch function")
    if (!navigator.onLine) { 
        event.respondWith(
            caches.match(event.request).then((response) => {
                 if(response) return response
            })
        )
    }
  
})

Why i cannot store the data into localStorage in javascript? [duplicate]

I am a beginner in web development, and I am working on a simple bookshelf app. I want to store the book data in localStorage using JavaScript, so the data persists even when the page is refreshed. However, I am facing an issue where the data cannot be saved in localStorage.

Problem: Whenever I try to add a new book using the form in my app, the book should be saved in localStorage, and the data should be displayed on the page, even after a page reload. However, after submitting the form, the data does not appear in localStorage

I’ve written a script that attempts to retrieve and save data to localStorage, but it seems like the data isn’t being saved properly.

here is my code :

index.html

<!DOCTYPE html>
<html lang="en">

<head>
  <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
  <title>Bookshelf App</title>

  <link rel="stylesheet" href="style.css">

  <script defer src="main.js"></script>
</head>

<body>
  <header>
    <h1>Bookshelf App</h1>
  </header>

  <main>
    <section>
      <h2>Tambah Buku Baru</h2>
      <form id="bookForm" data-testid="bookForm">
        <div>
          <label for="bookFormTitle">Judul</label>
          <input id="bookFormTitle" type="text" required data-testid="bookFormTitleInput" />
        </div>
        <div>
          <label for="bookFormAuthor">Penulis</label>
          <input id="bookFormAuthor" type="text" required data-testid="bookFormAuthorInput" />
        </div>
        <div>
          <label for="bookFormYear">Tahun</label>
          <input id="bookFormYear" type="number" required data-testid="bookFormYearInput" />
        </div>
        <div>
          <label for="bookFormIsComplete">Selesai dibaca</label>
          <input id="bookFormIsComplete" type="checkbox" data-testid="bookFormIsCompleteCheckbox" />
        </div>
        <button id="bookFormSubmit" type="submit" data-testid="bookFormSubmitButton">
          Masukkan Buku ke rak <span>Belum selesai dibaca</span>
        </button>
      </form>
    </section>

    <section>
      <h2>Cari Buku</h2>
      <form id="searchBook" data-testid="searchBookForm">
        <label for="searchBookTitle">Judul</label>
        <input id="searchBookTitle" type="text" data-testid="searchBookFormTitleInput" />
        <button id="searchSubmit" type="submit" data-testid="searchBookFormSubmitButton">
          Cari
        </button>
      </form>
    </section>

    <section>
      <h2>Belum selesai dibaca</h2>

      <div id="incompleteBookList" data-testid="incompleteBookList">
       
        <div data-bookid="123123123" data-testid="bookItem">
          <h3 data-testid="bookItemTitle">Judul Buku 1</h3>
          <p data-testid="bookItemAuthor">Penulis: Penulis Buku 1</p>
          <p data-testid="bookItemYear">Tahun: 2030</p>
          <div>
            <button data-testid="bookItemIsCompleteButton">Selesai dibaca</button>
            <button data-testid="bookItemDeleteButton">Hapus Buku</button>
            <button data-testid="bookItemEditButton">Edit Buku</button>
          </div>
        </div>
      </div>
    </section>

    <section>
      <h2>Selesai dibaca</h2>

      <div id="completeBookList" data-testid="completeBookList">
        
        <div data-bookid="456456456" data-testid="bookItem">
          <h3 data-testid="bookItemTitle">Judul Buku 2</h3>
          <p data-testid="bookItemAuthor">Penulis: Penulis Buku 2</p>
          <p data-testid="bookItemYear">Tahun: 2030</p>
          <div>
            <button data-testid="bookItemIsCompleteButton">Selesai dibaca</button>
            <button data-testid="bookItemDeleteButton">Hapus Buku</button>
            <button data-testid="bookItemEditButton">Edit Buku</button>
          </div>
        </div>
      </div>
    </section>
  </main>
</body>

</html>

main.js :

const incompleteBookList = document.getElementById('incompleteBookList');
const completeBookList = document.getElementById('completeBookList');
const bookForm = document.getElementById('bookForm');
const bookFormTitleInput = document.getElementById('bookFormTitle');
const bookFormAuthorInput = document.getElementById('bookFormAuthor');
const bookFormYearInput = document.getElementById('bookFormYear');
const bookFormIsCompleteCheckbox = document.getElementById('bookFormIsComplete');
const bookFormSubmitButton = document.getElementById('bookFormSubmit');

// Retrieve data from localStorage
let books = JSON.parse(localStorage.getItem('books')) || [];

// Function to save book data to localStorage
function saveBooks() {
  localStorage.setItem('books', JSON.stringify(books));
}

// Function to generate a unique ID using a timestamp
function generateId() {
  return new Date().getTime().toString();
}

// Function to create a book object
function createBookObject(id, title, author, year, isComplete) {
  return { id, title, author, year, isComplete };
}

// Function to create a book element in HTML
function createBookElement(book) {
  const { id, title, author, year, isComplete } = book;
  const bookElement = document.createElement('div');
  bookElement.setAttribute('data-bookid', id);
  bookElement.setAttribute('data-testid', 'bookItem');

  bookElement.innerHTML = `
    <h3 data-testid="bookItemTitle">${title}</h3>
    <p data-testid="bookItemAuthor">Author: ${author}</p>
    <p data-testid="bookItemYear">Year: ${year}</p>
    <div>
      <button data-testid="bookItemIsCompleteButton">${isComplete ? 'Not yet read' : 'Read'}</button>
      <button data-testid="bookItemDeleteButton">Delete Book</button>
      <button data-testid="bookItemEditButton">Edit Book</button>
    </div>
  `;

  const isCompleteButton = bookElement.querySelector('[data-testid="bookItemIsCompleteButton"]');
  const deleteButton = bookElement.querySelector('[data-testid="bookItemDeleteButton"]');
  const editButton = bookElement.querySelector('[data-testid="bookItemEditButton"]');

  // Event to move the book between shelves
  isCompleteButton.addEventListener('click', () => {
    toggleBookStatus(id);
  });

  // Event to delete a book
  deleteButton.addEventListener('click', () => {
    deleteBook(id);
  });

  // Event to edit a book
  editButton.addEventListener('click', () => {
    editBook(id);
  });

  return bookElement;
}

// Function to display books on the appropriate shelf
function renderBooks() {
  books = JSON.parse(localStorage.getItem('books')) || [];
  console.log("Books to render:", books);

  incompleteBookList.innerHTML = '';
  completeBookList.innerHTML = '';

  books.forEach(book => {
    const bookElement = createBookElement(book);
    if (book.isComplete) {
      completeBookList.appendChild(bookElement);
    } else {
      incompleteBookList.appendChild(bookElement);
    }
  });
}

// Function to add a new book
function addBook(event) {
  event.preventDefault();
  const title = bookFormTitleInput.value;
  const author = bookFormAuthorInput.value;
  const year = parseInt(bookFormYearInput.value);
  const isComplete = bookFormIsCompleteCheckbox.checked;

  const newBook = createBookObject(generateId(), title, author, year, isComplete);
  books.push(newBook);
  saveBooks();
  renderBooks();
  bookForm.reset();
}

// Function to move a book between shelves
function toggleBookStatus(bookId) {
  const book = books.find(book => book.id === bookId);
  if (book) {
    book.isComplete = !book.isComplete;
    saveBooks();
    renderBooks();
  }
}

// Function to delete a book
function deleteBook(bookId) {
  books = books.filter(book => book.id !== bookId);
  saveBooks();
  renderBooks();
}

// Function to edit a book
function editBook(bookId) {
  const book = books.find(book => book.id === bookId);
  if (book) {
    bookFormTitleInput.value = book.title;
    bookFormAuthorInput.value = book.author;
    bookFormYearInput.value = book.year;
    bookFormIsCompleteCheckbox.checked = book.isComplete;

    deleteBook(bookId);
  }
}

// Function to search for books by title
function searchBooks(event) {
  event.preventDefault();

  const searchTitle = document.getElementById('searchBookTitle').value.toLowerCase();
  incompleteBookList.innerHTML = '';
  completeBookList.innerHTML = '';

  // Filter books that have titles matching the search query
  const filteredBooks = books.filter(book => book.title.toLowerCase().includes(searchTitle));

  // Render only books that match the search results
  filteredBooks.forEach(book => {
    const bookElement = createBookElement(book);
    if (book.isComplete) {
      completeBookList.appendChild(bookElement);
    } else {
      incompleteBookList.appendChild(bookElement);
    }
  });
}

// Event listener for the book search form
const searchBookForm = document.getElementById('searchBook');
searchBookForm.addEventListener('submit', searchBooks);

// Event listener for the reset search button
const resetSearchButton = document.getElementById('resetSearch');
resetSearchButton.addEventListener('click', renderBooks);

// Event listener to add a new book
bookForm.addEventListener('submit', addBook);

// Display books when the page is first loaded
document.addEventListener('DOMContentLoaded', renderBooks);

document.addEventListener('DOMContentLoaded', () => {
  books = JSON.parse(localStorage.getItem('books')) || []; // Ensure data is retrieved from Local Storage
  renderBooks();
});


// the error in console

main.js:159 Uncaught TypeError: Cannot read properties of null (reading 'addEventListener')
    at main.js:159:19

can anyone help me to find the solution of this issue ? thank you

How do I make cors policy on hapi.js working?

I have this hapi.js restful api server demo that tried to be accessed by external domain but failed. here is the Hapi instance:

const init = async () => {
  const server = Hapi.server({
    port: 5000,
    host: 'localhost',
    routes: {
      cors: {
        origin: ['*'],
      },
    },
  });

  server.route(routes);

  await server.start();
  console.log(`Server running on port ${server.info.uri}`);
};

What could be the problem?

I tried to access it from external domain. I tried to modify my browser security, checked for mixed content, and even modify my macos network setting include disabling firewalls, etc. but this error kept coming:

Access to fetch at 'http://localhost:5000/notes' from origin 'http://notesapp-v1.dicodingacademy.com' has been blocked by CORS policy: The request client is not a secure context and the resource is in more-private address space `local`.Understand this errorAI
abe5c84d8f779a0d6876d1a20ed7bd32e2302c43.3c745b2819eb12ef4f8b.js:1 
        
        
POST http://localhost:5000/notes net::ERR_FAILED

prisma : how can i get transactions of a specifc user?

I have the following schema :

model User {
  id            String       @id @default(uuid())
  email         String       @unique
  createdAt     DateTime     @default(now())
  budgets       Budget[]
}

model Budget {
  id           String       @id @default(uuid())
  name         String
  amount       Float
  userId       String
  user         User         @relation(fields: [userId], references: [id])
  emoji        String?   
  transactions Transaction[]
  createdAt    DateTime     @default(now())
}

model Transaction {
  id           String     @id @default(uuid())
  description  String
  amount       Float
  budgetId     String?
  budget       Budget?    @relation(fields: [budgetId], references: [id])
  emoji        String?  
  createdAt    DateTime   @default(now())
}

My goal is to make a query that displays ONLY the transactions that the user created when he logged in to the web app, and NOT the transactions created by other users. How can I do that? (I am new to prisma)

global variables in javascript being undefined? [closed]

let inLobby = true; //if in main lobby where you can buy stuff
let layers = [g,g,g,g,g];//all layers

var depth = 1;

function startmining() {
  inLobby = false;
  const layer = document.getElementsByClassName("layerprint")[0];
  const lobby = document.getElementsByClassName("lobby");
  const mining = document.getElementsByClassName("in-mine");

  // Set opacity for "lobby" to 0
  if (lobby) {
    for (let i = 0; i < lobby.length; i++) {
     lobby[i].style.opacity = 0;
    }
  }

  // Set opacity for "mining" to 1
  if (mining) {
    for (let i = 0; i < mining.length; i++) {
     mining[i].style.opacity = 1;
    }
  }
    layer.innerText = depth;
}

the parts I’m talking about are the ‘var depth = 1;’ and the layer.innerText = depth;’ I’ve already debugged everything else. if I replace depth with anything not global or just a string, it works fine, but only with these global variables, it says undefined.

layer, is a html element, an element to be exact, I want it to say the current depth of the player, by using javascript to feed it the layer, it doesn’t work, I’ve already told you the basics, please someone help 🙁

How to use a selected file in javascript function in my Blazor Server App?

In my Blazor Server App the user can select a File from his local drive.
Now this file should be analysed locally in Javascript and the results should be send back to Blazor.
Calling Javascript functions is not the problem, but I have problems to use the file in Javascript. On the C# part it is a IBrowserFile.

I need to do this because the files are very big, so I need to prevent unecessary uploads. On the javascript side I use Mediainfo.js to analyse the file.

I am new to Blazor Server, but as I understand this, the C# code runns completly on the server, so the solutions I found did not work, because they always do some file conversion in C# and that mees the file has to be uploaded fist.

It would be great to get a solution for this.

Should the IDL definition of a callback throw an error if the return type does not match?

There is such a definition of IDL:

callback NavigationInterceptHandler = Promise<undefined> ();

This corresponds to the property of the handler argument passed to the intercept method of the navigate event:

navigation.addEventListener('navigate', (event) => {
    event.intercept({handler(){
      /// For corresponding with return type of IDL
      return Promise.resolve(undefined);
    }});
  });

The question is: should an error occur if the return type is other than Promise, and if the result is a Promise, but its result is not undefined, should an error occur?

As another example, consider this IDL:

callback FrameRequestCallback = undefined (DOMHighResTimeStamp time);

This corresponds to the callback argument in the requestAnimationFrame function:

requestAnimationFrame(function(){
  /// For corresponding with return type of IDL
  return undefined;
})

What happens if in this case we return a value other than undefined? Does this break the interface? Should this throw an error?

I tried to find information about return value matching, but WebIDL doesn’t seem to say anything like that. If I missed anything let me know.

If in both cases we return inappropriate value types, then no errors will occur, why?

Node.js/Sharp: Text not wrapping when compositing SVG text on image

I’m trying to create an image generator that overlays text on background images using Node.js with Sharp and the canvas library. My issue is that the text always renders on a single line, extending beyond the image boundaries, despite my attempts to wrap it.

Here’s what I’m trying to achieve:

  • Load a background image
  • Add a semi-transparent black overlay
  • Add text on top that:
    • Is centered horizontally and vertically
    • Wraps to multiple lines when exceeding the image width
    • Has a drop shadow
    • Uses ~80% of the image width

The issue: Despite using wrapText() function and setting maxWidth, the text always renders on a single line, extending beyond the image boundaries.

What I’ve tried:

  • Using canvas context to measure text and create line breaks
  • Setting maxWidth in the SVG text element
  • Adjusting font size dynamically

Current behavior: Text renders on one line, extending beyond image boundaries
Expected behavior: Text should wrap to multiple lines when reaching ~80% of image width

Environment:

  • Node.js
  • Sharp
  • canvas

At the same level of this index.js I have a background folder with some images with 9:16 format
And I have a mysteries.json which is an array of :

{
   "text": "my text here"
}

Any help would be appreciated!

Here’s my current code:

const fs = require('fs');
const path = require('path');
const sharp = require('sharp');
const {createCanvas, loadImage} = require('canvas');

function getBackgroundFiles(dirPath) {
    return fs.readdirSync(dirPath)
        .filter(file => {
            const ext = path.extname(file).toLowerCase();
            return (ext === '.jpg' || ext === '.jpeg') && file.startsWith('bg-');
        })
        .map(file => path.join(dirPath, file));
}

function readMysteries(filePath) {
    const data = fs.readFileSync(filePath, 'utf8');
    return JSON.parse(data);
}

function getRandomElement(array) {
    return array[Math.floor(Math.random() * array.length)];
}

function wrapText(context, text, maxWidth) {
    const words = text.split(' ');
    const lines = [];
    let currentLine = '';

    words.forEach(word => {
        const testLine = currentLine ? currentLine + ' ' + word : word;
        const testWidth = context.measureText(testLine).width;

        if (testWidth > maxWidth && currentLine !== '') {
            lines.push(currentLine);
            currentLine = word;
        } else {
            currentLine = testLine;
        }
    });

    if (currentLine !== '') {
        lines.push(currentLine);
    }

    return lines;
}

async function generateImage() {
    const outputDir = path.join(__dirname, 'output');
    if (!fs.existsSync(outputDir)) {
        fs.mkdirSync(outputDir);
    }

    const backgroundsDir = path.join(__dirname, 'backgrounds');
    const mysteriesFile = path.join(__dirname, 'mysteries.json');

    const backgrounds = getBackgroundFiles(backgroundsDir);
    const mysteries = readMysteries(mysteriesFile);

    const selectedBackground = getRandomElement(backgrounds);
    const selectedMystery = getRandomElement(mysteries);

    const outputFileName = `output-${Date.now()}.jpg`;
    const outputPath = path.join(outputDir, outputFileName);

    try {
        const metadata = await sharp(selectedBackground).metadata();

        const canvas = createCanvas(metadata.width, metadata.height);
        const context = canvas.getContext('2d');

        // Font settings
        const fontSize = 60;
        const maxWidth = metadata.width * 0.8; // 80% of the canvas width

        context.font = `bold ${fontSize}px Arial`;

        // Get multiple lines
        const lines = wrapText(context, selectedMystery.text, maxWidth);

        // Calc total height
        const lineHeight = fontSize * 1.2;
        const totalHeight = lines.length * lineHeight;
        const startY = (metadata.height - totalHeight) / 2;

        // Create SVG with text
        const svgText = `
            <svg width="${metadata.width}" height="${metadata.height}">
                <defs>
                    <filter id="shadow">
                        <feDropShadow dx="0" dy="0" stdDeviation="4" flood-opacity="0.8"/>
                    </filter>
                </defs>
                <rect width="100%" height="100%" fill="rgba(0,0,0,0.75)"/>
                <style>
                    .text {
                        fill: white;
                        font-size: ${fontSize}px;
                        font-family: Arial, sans-serif;
                        text-anchor: middle;
                        filter: url(#shadow);
                        font-weight: bold;
                    }
                </style>
                <g transform="translate(${metadata.width / 2}, ${startY})">
                    ${lines.map((line, index) => `
                        <text 
                            x="0" 
                            y="${index * lineHeight + fontSize}"
                            class="text"
                            dominant-baseline="middle"
                        >${line}</text>
                    `).join('')}
                </g>
            </svg>`;

        // Generate image
        await sharp(selectedBackground)
            .modulate({
                brightness: 0.3
            })
            .composite([{
                input: Buffer.from(svgText),
                top: 0,
                left: 0
            }])
            .toFile(outputPath);

        return outputPath;
    } catch (error) {
        console.error('Erreur lors de la génération de l'image:', error);
        throw error;
    }
}

const express = require('express');
const app = express();
const port = 3000;

app.get('/generate', async (req, res) => {
    try {
        const imagePath = await generateImage();
        res.json({success: true, path: imagePath});
    } catch (error) {
        res.status(500).json({success: false, error: error.message});
    }
});

app.listen(port, () => {
    console.log(`Server running on http://localhost:${port}`);
});

Using the ejs var from one file to another ejs file for displaying data

I am trying to move the variable from one ejs file to another but its working in one file but not showing in another file

below is my code:
file1.ejs

                <tr class="d-flex">
                <td><%= DList[loop].Data %></td>
                <td><%= DList[loop].Category %></td>
                <% if(DList[loop].Category =="Num") { %>
                    <%= total = total + DList[loop].Data  %>
               <td> <%= total %></td>
                  <% } %>

It is showing me total on file1, when i move to file2.ejs and run the following line is show me error:

<% total %>

Error saying:

Reference Error, total is not defined

react infinite image scroll animation

I am trying to create an infinite scroll animation for some images. The issue is that the images have a weird jump behaviour when the animation duration is completed. I implemented the same on text and it works without issues but not for the images.

CSS:

.outter-scroller{
    overflow: hidden;
    -webkit-mask: linear-gradient(
        90deg,
        transparent,
        white 20%,
        white 80%,
        transparent
      );
      mask: linear-gradient(90deg, transparent, white 20%, white 80%, transparent);
    }
    .inner-scroller{
        width: max-content;
        flex-wrap: nowrap;
        animation: scroll 20s linear infinite;
        animation-fill-mode: forwards;
        }

JSX:

import { useEffect } from "react"


const sourceImages = ['src/assets/ic1.jpg', 'src/assets/ic2.jpg', 'src/assets/ic3.jpg', 'src/assets/ic4.jpg', 'src/assets/ic4.jpg', 'src/assets/ic5.jpg', 'src/assets/ic6.jpg', 'src/assets/ic7.jpg', 'src/assets/ic8.jpg']

const ImageSectionAnimatedAuto = () => {
    useEffect(() => {
        const handleAnimation = () => {
            const scrollerInner = document.querySelector('.inner-scroller')
            const scrollerContent = Array.from(scrollerInner.children)
            scrollerContent.forEach((item) => {
                const duplicatedItem = item.cloneNode(true)
                scrollerInner.appendChild(duplicatedItem)
            })
        }
        handleAnimation()
    }, [])
    return (
        <section className="bg-[#1d283a] w-full h-[100vh] flex flex-col justify-center items-center">
            <h1 className="text-6xl text-white text-center">Infinite scroll</h1>
            <div className="outter-scroller md:w-[60vw] w-[600px] py-10">
                <ul className="inner-scroller flex gap-5">
                    {["HTML", "CSS", "JAVASCRIPT", "SSG", "WEBDEV", "ANIMATION", "UI/UX"].map((item) => (
                        <li className="m-0 text-white rounded-2xl px-6 py-2 bg-slate-500 shadow-lg" key={item}>{item}</li>
                    ))}
                </ul>
            </div>
             <div className="outter-scroller py-5 flex w-[80vw] overflow-hidden">
                <div className="inner-scroller flex gap-5">
                    {sourceImages.map((image, _i) => (
                        <img key={_i} src={image} className="aspect-video w-[200px] rounded-xl" />
                    ))}
                </div>
            </div>
        </section>
    )
}

export default ImageSectionAnimatedAuto

I am just trying to recreate this: https://codepen.io/kevinpowell/pen/BavVLra

react-native-webrtc build with preview profile crashes

I’m having a problem with react-native-webrtc:

My eas build in profile development works completely, but my builds in preview crash as soon as they open.
The eas.json has the default content.

I don’t understand what justifies the difference in results between the two build profiles, so I don’t have any clues on how to solve the problem. If anyone has any ideas to help me, I’d be grateful.

I’ve tried checking my package.json and metro.config.js, but nothing works. Here they are:

package.json:

{
  "config-plugins/react-native-webrtc": "^9.0.0",
  "@expo/metro-config": "^0.18.11",
  "expo": "~51.0.38",
  "react-native": "0.74.5",
  "react-native-webrtc": "^124.0.4"
}

metro.config.js:

Copier le code
const { getDefaultConfig } = require('expo/metro-config');
const resolveFrom = require("resolve-from");

const config = getDefaultConfig(__dirname);
config.resolver.extraNodeModules = {
  "event-target-shim": require.resolve("event-target-shim")
};

config.resolver.resolveRequest = (context, moduleName, platform) => {
  if (moduleName.startsWith("event-target-shim") && context.originModulePath.includes("react-native-webrtc")) {
    const eventTargetShimPath = resolveFrom(context.originModulePath, moduleName);
    return { filePath: eventTargetShimPath, type: "sourceFile" };
  }
  return context.resolveRequest(context, moduleName, platform);
};

config.resolver.assetExts = [...config.resolver.assetExts, 'db', 'sqlite'];
const { transformer, resolver } = config;

config.transformer = {
  ...transformer,
  babelTransformerPath: require.resolve("react-native-svg-transformer")
};

config.resolver = {
  ...resolver,
  assetExts: resolver.assetExts.filter((ext) => ext !== "svg"),
  sourceExts: [...resolver.sourceExts, "svg"]
};

module.exports = config;