How to add calendar using fullcalendar js in content section blade

I want to create a booking list using full calendar js, but I have a problem that makes the calendar not appear.

enter image description here

This is the code I used..

@extends('layouts.main')

@section('konten')
    <div class="konten-layout py-3 px-2">
        <div class="text-center mb-10">
            <h2 class="font-semibold text-lg lg:text-2xl">Daftar Pesanan</h2>
        </div>
        <div class="my-10 lg:px-10">
            <span>
                <a class="bg-primary text-white px-2 py-1 rounded-lg hover:bg-primary/50"
                   href="/pesan-lapangan/create/{{auth()->user()->id}}">
                    Pesanan Baru +
                </a>
            </span>
        </div>
        <div class="px-[2px] lg:px-10 mb-[100px] lg:mb-[143px]">
            <!-- Elemen kalender -->
            <div id="calendar"></div>
        </div>
    </div>
@endsection

@section('scripts')
    <!-- FullCalendar CSS -->
    <script src="https://cdn.jsdelivr.net/npm/[email protected]/index.global.min.js"></script>

    <script>
        document.addEventListener('DOMContentLoaded', function () {
            var calendarEl = document.getElementById('calendar');

            var calendar = new FullCalendar.Calendar(calendarEl, {
                plugins: ['dayGrid', 'interaction'],
                initialView: 'dayGridMonth',
                events: [
                    @foreach ($pesanan as $psn)
                        {
                            title: '{{ $psn->kode . $psn->numInvoice }} - {{ $psn->status == "1" ? "Belum Bayar" : ($psn->status == "2" ? "Sedang diproses" : ($psn->status == "3" ? "Berhasil" : "Gagal")) }}',
                            start: '{{ $psn->tanggal }}',
                            url: '{{ $psn->status == "1" ? "/pesan-lapangan/konfirmasi-pembayaran/" . $psn->numInvoice : "/detail-pesanan/" . $psn->numInvoice }}',
                            backgroundColor: '{{ $psn->status == "1" ? "#fbbf24" : ($psn->status == "2" ? "#94a3b8" : ($psn->status == "3" ? "#14b8a6" : "#f87171")) }}',
                            borderColor: '#ffffff',
                        },
                    @endforeach
                ],
                dateClick: function (info) {
                    alert('Anda mengklik tanggal: ' + info.dateStr);
                },
            });

            calendar.render();
        });
    </script>
@endsection

I have followed various tutorials but the problem is still not solved. Can anyone tell me where my mistake is

I want to use the calendar in the content section to display the order list, please help me

Why does my Vercel serverless function fail to read request headers in production?

I am working on a Vercel project with both a local development environment (using vercel dev) and a production environment deployed on Vercel’s serverless platform.

I need to send credentials (email, pwd, and id) via request headers in a GET request. This works perfectly in my local environment, but in production, I get the following error:

Error: TypeError: Cannot read properties of undefined (reading 'email')
    at module.exports (/var/task/api/user/user.js:16:16)
    at Server.<anonymous> (/opt/rust/nodejs.js:2:11027)
    at process.processTicksAndRejections (node:internal/process/task_queues:95:5)
    at async Server.<anonymous> (/opt/rust/nodejs.js:9:6760)

The error occurs here:

  let email, pwd, userId;

  try {
    email = req.body.email || req.headers["email"] || req.headers?.email || null;
    pwd = req.body.pwd || req.headers["pwd"] || req.headers?.pwd || null;
    userId = req.body.participantId || req.headers["id"] || req.headers?.id || null;
  } catch (err) {
    console.error("Error parsing input:", err);
    return res.status(400).json("Bad Request");
  }

Client side GET-request:

axios.get(`/api/user/user/`, {headers: { "email": email, "pwd": pwd, id: participantId }}, { withCredentials: true })
                    .then((res) => res.data.fullName)
                    .catch((err) => {
                        console.log(err);
                        return "Error loading participant";
                    })

Debugging steps I tried:

  1. Logging req.headers both locally and in prod
    • I see email, pwd and id both with the correct value
  2. I ensured header names were case-insensitive in the serverless function
  3. Verified that Axios is sending headers correctly

Observations:

  • The headers are being sent to the backend
  • In prod the serverless function cant access them for some reason
  • Locally in the dev enviornment it works without problems

Questions:

  1. Why does the serverless function fail to read headers in production?
  2. Is there a known issue with Vercel’s serverless environment
    stripping certain headers?
  3. How can I ensure the headers (email, pwd,
    id) are properly passed and read in production?

Thanks in advance I appriciate your help

How to prevent a button from interfering with letter animations in JavaScript?

I’m trying to animate letters so they fly in from random positions using JavaScript. It works perfectly. However, when I add a button to the HTML, the animations no longer work as expected—the letters appear in the center instead of flying in from random positions. How can I fix this while keeping the button in the same container?

Here’s my code:

HTML:

<div class="container">
  <span class="letter">L</span>
  <span class="letter">e</span>
  <span class="letter">t</span>
  <span class="letter">'</span>
  <span class="letter">s</span>
  <span class="letter">&nbsp;</span>
  <span class="letter">o</span>
  <span class="letter">r</span>
  <span class="letter">g</span>
  <span class="letter">a</span>
  <span class="letter">n</span>
  <span class="letter">i</span>
  <span class="letter">z</span>
  <span class="letter">e</span>
  <span class="letter">&nbsp;</span>
  <span class="letter">e</span>
  <span class="letter">v</span>
  <span class="letter">e</span>
  <span class="letter">r</span>
  <span class="letter">y</span>
  <span class="letter">t</span>
  <span class="letter">h</span>
  <span class="letter">i</span>
  <span class="letter">n</span>
  <span class="letter">g</span>
</div>
      body {
      background-color: #282c34;
      color: #fff;
      font-family: Arial, sans-serif;
      display: flex;
      justify-content: center;
      align-items: center;
      height: 100vh;
      margin: 0;
      overflow: hidden;
    }

    .container {
      display: flex;
      flex-wrap: wrap;
      gap: 5px; 
      font-size: 2rem;
      font-weight: bold;
      text-align: center;
      position: relative;
    }

    .letter {
      position: relative; 
      opacity: 0;
      animation: flyIn 1.5s ease forwards;
    }
    button {
        position: relative;
        margin-top: 20px;
        padding: 10px 20px;
        background-color: #61dafb;
        color: #282c34;
        border: none;
        border-radius: 5px;
        font-size: 1rem;
        cursor: pointer;
      }
      
      button:focus {
        outline: none;
      }
      
      button:hover {
        background-color: #21a1f1;
      }
      

    @keyframes flyIn {
      from {
        opacity: 0;
        transform: translate(var(--random-x), var(--random-y));
      }
      to {
        opacity: 1;
        transform: translate(0, 0);
      }  
    } 
const letters = document.querySelectorAll('.letter');

function animateLetters() {
  letters.forEach((letter, index) => {
    // Generate rarndom initial positions
    const randomX = (Math.random() - 0.5) * window.innerWidth; // Random X
    const randomY = (Math.random() - 0.5) * window.innerHeight; // Random Y

    // Apply CSS variables for animation
    letter.style.setProperty('--random-x', `${randomX}px`);
    letter.style.setProperty('--random-y', `${randomY}px`);

    // Add staggered delay for smooth animation
    letter.style.animationDelay = `${index * 0.2}s`;
  });
  
}
  // Trigger animation on load
  animateLetters(); 



Tradingview Widget Set Different colors to differnt moving average

function LoadCharts() {
let symbol = document.getElementById('pairs').value;

// List of symbols for each chart
const symbols = ['BTCUSDT.P', 'ETHUSDT.P', 'BCHUSDT.P', 'XRPUSDT.P', 'LTCUSDT.P', 'TRXUSDT.P', 'ETCUSDT.P', 'LINKUSDT.P', 'XLMUSDT.P',
          'ADAUSDT.P', 'XMRUSDT.P', 'DASHUSDT.P', 'ZECUSDT.P', 'BNBUSDT.P', 'ATOMUSDT.P', 'VETUSDT.P', 'NEOUSDT.P', 'QTUMUSDT.P',
          'IOSTUSDT.P', 'THETAUSDT.P', 'KNCUSDT.P', 'ZRXUSDT.P', 'COMPUSDT.P', 'OMGUSDT.P', 'DOGEUSDT.P', 'SXPUSDT.P', 'KAVAUSDT.P',
          'BANDUSDT.P', 'RLCUSDT.P', 'MKRUSDT.P', 'DOTUSDT.P', 'DEFIUSDT.P', 'YFIUSDT.P', 'BALUSDT.P', 'TRBUSDT.P',
          'RUNEUSDT.P', 'SUSHIUSDT.P', 'EGLDUSDT.P', 'SOLUSDT.P', 'STORJUSDT.P', 'BLZUSDT.P', 'UNIUSDT.P', 'AVAXUSDT.P', 'FTMUSDT.P',
          'ENJUSDT.P', 'KSMUSDT.P', 'NEARUSDT.P', 'AAVEUSDT.P', 'FILUSDT.P', 'RSRUSDT.P'];

// Add delay between each chart load with a different symbol
for (let i = 0; i < 1; i++) {
    setTimeout(function() {
        var widget = window.tvWidget = new TradingView.widget({
            "width": chartWidth, // Chart width
            "height": chartHeight, // Chart height
            "symbol": "BINANCE:" + symbols[i % symbols.length], // Use a different symbol for each chart
            "interval": "5", // You can vary intervals if needed
            "timezone": "Asia/Kolkata",
            "hide_side_toolbar": false,
            "hide_volume": false,
            "theme": "Dark",
            "style": "1",
            "locale": "out",
            "toolbar_bg": "#f1f3f6",
            "enable_publishing": false,
            "allow_symbol_change": true,
            "details": true,
            "container_id": 'tvchart' + i,
            "studies": [
                {id: "MAExp@tv-basicstudies",inputs: {length: 7}},
                {id: "MAExp@tv-basicstudies",inputs: {length: 25}},
                {id: "MAExp@tv-basicstudies",inputs: {length: 99}}],
        });
    }, i * 2000); // Delay of 2 seconds (2000 milliseconds) between each chart
}

}

This is my code

I tried this….

{id: "MAExp@tv-basicstudies",inputs: {length: 7},style:{color:"#FFFF00"}}

I have searched alot to solve this problem but iam unable to do it.please guid me how to plot multiple emas with different colours

javascript record getusermedia video and save the last 2 Minutes in a floating way

i want to record the video stream from getUserMedia ob my web page constantly, but only the last two minutes be available for a replay or an analasys.
I found some solutions where i can rocord for a certain time and cut i afterwards, but here i want to record for a lang time and only save if something special happened. For my scenario i dont have any way to solve it, so i cant provide my code.

Background is that i want to record the video when playing billard over a long time and be able to recap the last 2 minutes when there was something special or tr

The session is not sent from the server

then upon authorization, we receive a session and go through authorization. If I use ip 192.168.0.100:8080 both when creating the server and when redirecting, then we receive user data, but we do not send anything in response. I mean something related to /profile. It’s as if it can’t handle IP requests.

main.go

package main

import (
    global "DiscordBotGo/Global"
    "DiscordBotGo/auth" // Добавлен пакет авторизации
    "DiscordBotGo/coins"
    "DiscordBotGo/utils"
    "fmt"
    "log"
    "net/http"

    "github.com/bwmarrin/discordgo"
    "github.com/gorilla/mux"
)

var serverip = "localhost:8080"

func main() {

    // Проверяем инициализацию сессии перед запуском
    if global.BotSession == nil {
        var err error
        global.BotSession, err = discordgo.New("Bot тут мой токен")
        if err != nil {
            log.Fatal("Ошибка при создании сессии Discord:", err)
        }
    }

    // Обработчик для статических файлов
    http.FileServer(http.Dir("./"))                                                               // Указываем корневую директорию проекта
    http.Handle("/css/", http.StripPrefix("/css/", http.FileServer(http.Dir("./css"))))           // Разрешаем загрузку файлов из папки /css                                         // Разрешаем загрузку файлов из папки /js
    http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets/")))) // Для изображений
    http.Handle("/js/", http.StripPrefix("/js/", http.FileServer(http.Dir("./js/"))))

    // Открываем сессию
    err := global.BotSession.Open()
    if err != nil {
        fmt.Println("Ошибка при подключении к Discord:", err)
        return
    }
    defer global.BotSession.Close()

    coins.StartAutoReward(global.BotSession)

    // Добавляем маршруты для авторизации
    http.HandleFunc("/login", auth.LoginHandler)
    http.HandleFunc("/callback", auth.CallbackHandler)
    http.HandleFunc("/profile", auth.ProfileHandler)
    http.HandleFunc("/logout", auth.LogoutHandler)
    http.HandleFunc("/distribute", utils.DistributeTeams)
    http.HandleFunc("/create-product", utils.CreateProductHandler)
    http.HandleFunc("/api/products/{id}", utils.GetProductByIDHandler)
    http.HandleFunc("/check-auth", auth.CheckAuthHandler)
    http.HandleFunc("/api/products/", utils.GetProductByIDHandler)
    http.HandleFunc("/check-role-for-tournament", checkRoleForTournamentHandler)
    http.HandleFunc("/roulette-images/", utils.GetRouletteImagesHandler)

    // Обрабатываем POST запрос на /send-links
    http.HandleFunc("/send-links", utils.SendMatchLinks)
    http.HandleFunc("/buy-product", utils.BuyProductHandler)
    http.HandleFunc("/api/user-roles", utils.UserRolesHandler)
    http.HandleFunc("/profile-redirect", utils.ProfileRedirectHandler)
    http.HandleFunc("/api/products", utils.GetProductsByCategoryHandler)
    http.HandleFunc("/api/coins", utils.CoinsHandler)
    http.HandleFunc("/save-news", utils.SaveNewsHandler)
    http.Handle("/images/", http.StripPrefix("/images/", http.FileServer(http.Dir("images"))))
    http.HandleFunc("/get-news", utils.GetNewsHandler)
    http.HandleFunc("/delete-news", utils.DeleteNewsHandler)

    r := mux.NewRouter()
    r.HandleFunc("/api/products/{id}", utils.GetProductByIDHandler).Methods("GET")
    // Дальше идёт ваш HTTP-сервер
    http.HandleFunc("/", serveHTML)
    http.HandleFunc("/command", utils.HandleCommand)

    fmt.Println("Сервер запущен на", serverip)
    err = http.ListenAndServe(serverip, nil)
    if err != nil {
        fmt.Println("Ошибка при запуске HTTP-сервера:", err)
    }
}

func serveHTML(w http.ResponseWriter, r *http.Request) {
    // Если путь запроса "/" (корень), возвращаем index.html
    if r.URL.Path == "/" {
        http.ServeFile(w, r, "index.html")
        return
    }

    // Если запрашивается другой файл, пытаемся его найти
    http.FileServer(http.Dir("./")).ServeHTTP(w, r)
}

func checkRoleForTournamentHandler(w http.ResponseWriter, r *http.Request) {
    userID := auth.GetUserIdFromSessionOrHeader(r)
    if userID == "" {
        http.Error(w, "Пользователь не аутентифицирован", http.StatusUnauthorized)
        return
    }

    // Получаем роли пользователя из Discord
    userRoles := auth.GetUserRolesFromDiscord(userID)
    if userRoles == nil {
        http.Error(w, "Ошибка при получении ролей пользователя", http.StatusInternalServerError)
        return
    }

    // Определяем роли администраторов
    adminRoles := []string{"1318188643944501289", "1318188870415679508", "1318189013823389711"}

    // Проверяем, есть ли у пользователя одна из ролей админа
    isAdmin := false
    for _, role := range userRoles {
        for _, adminRole := range adminRoles {
            if role == adminRole {
                isAdmin = true
                break
            }
        }
        if isAdmin {
            break
        }
    }

    // Если пользователь является админом, предоставляем доступ
    if isAdmin {
        w.WriteHeader(http.StatusOK)
    } else {
        http.Error(w, "У вас нет прав доступа к этой странице", http.StatusForbidden)
    }
}

auth.go

package auth

import (
    global "DiscordBotGo/Global"
    "DiscordBotGo/db"
    "encoding/json"
    "fmt"
    "log"
    "net/http"
    "strconv"

    "github.com/gorilla/sessions"
    "golang.org/x/oauth2"
)

// Определяем OAuth2 Endpoint для Discord
var discordEndpoint = oauth2.Endpoint{
    AuthURL:  "https://discord.com/api/oauth2/authorize",
    TokenURL: "https://discord.com/api/oauth2/token",
}

// Переменные для OAuth2
var (
    oauthConfig = &oauth2.Config{
        ClientID:     "тут мой клиент id",              // Замените на ваш Client ID
        ClientSecret: "тут секретный ключ", // Замените на ваш Client Secret
        RedirectURL:  "http://localhost/callback",        // URL перенаправления после авторизации
        Scopes:       []string{"identify"},               // Указываем необходимые права
        Endpoint:     discordEndpoint,                    // Используем Discord OAuth2 Endpoint
    }
)

var SessionStore = sessions.NewCookieStore([]byte("a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6q7r8s9t0")) // Хранилище сессий

// Инициализация базы данных
func init() {
    if err := db.InitDB(); err != nil {
        log.Fatalf("Ошибка при инициализации базы данных: %v", err)
    }
}

// LoginHandler — перенаправляет пользователя на Discord для авторизации
func LoginHandler(w http.ResponseWriter, r *http.Request) {
    url := oauthConfig.AuthCodeURL("state", oauth2.AccessTypeOffline) // Генерируем URL для авторизации
    http.Redirect(w, r, url, http.StatusTemporaryRedirect)            // Перенаправляем на Discord
}

// CallbackHandler — обработчик для обработки ответа Discord
func CallbackHandler(w http.ResponseWriter, r *http.Request) {
    code := r.URL.Query().Get("code")
    if code == "" {
        http.Error(w, "Ошибка: нет кода в запросе", http.StatusBadRequest)
        return
    }

    token, err := oauthConfig.Exchange(r.Context(), code)
    if err != nil {
        http.Error(w, "Не удалось получить токен: "+err.Error(), http.StatusInternalServerError)
        return
    }

    client := oauthConfig.Client(r.Context(), token)

    resp, err := client.Get("https://discord.com/api/users/@me")
    if err != nil {
        http.Error(w, "Ошибка получения данных пользователя: "+err.Error(), http.StatusInternalServerError)
        return
    }
    defer resp.Body.Close()

    var userData struct {
        ID            string `json:"id"`
        Username      string `json:"username"`
        Discriminator string `json:"discriminator"`
        Avatar        string `json:"avatar"`
    }
    if err := json.NewDecoder(resp.Body).Decode(&userData); err != nil {
        http.Error(w, "Ошибка декодирования данных пользователя: "+err.Error(), http.StatusInternalServerError)
        return
    }

    // Генерация URL аватара
    var avatarURL string
    if userData.Avatar != "" {
        avatarURL = fmt.Sprintf("https://cdn.discordapp.com/avatars/%s/%s.png", userData.ID, userData.Avatar)
    } else {
        discriminator, err := strconv.Atoi(userData.Discriminator)
        if err != nil {
            http.Error(w, "Ошибка обработки данных пользователя: "+err.Error(), http.StatusInternalServerError)
            return
        }
        defaultAvatarIndex := discriminator % 5
        avatarURL = fmt.Sprintf("https://cdn.discordapp.com/embed/avatars/%d.png", defaultAvatarIndex)
    }

    // Логируем URL аватара для отладки
    log.Printf("Avatar URL: %s", avatarURL)

    // Сохраняем данные пользователя и аватар в сессии
    session, _ := SessionStore.Get(r, "discord-session")
    session.Values["userID"] = userData.ID
    session.Values["username"] = userData.Username
    session.Values["avatar"] = avatarURL
    err = session.Save(r, w)
    log.Printf("Session saved with userID: %s", userData.ID)
    if err != nil {
        log.Printf("Ошибка сохранения сессии: %v", err)
        http.Error(w, "Ошибка сохранения сессии", http.StatusInternalServerError)
        return
    }

    // Добавляем пользователя в базу данных
    err = db.AddUser(userData.ID, userData.Username)
    if err != nil {
        http.Error(w, "Ошибка при добавлении пользователя в базу данных: "+err.Error(), http.StatusInternalServerError)
        return
    }

    // Перенаправляем на страницу профиля
    http.Redirect(w, r, "/index.html", http.StatusSeeOther)

}

func AddRoleToUser(userID string, roleID int64) error {
    guildID := global.ServerID // Замените на ID вашего сервера

    session := global.BotSession
    if session == nil {
        return fmt.Errorf("сессия бота не инициализирована")
    }

    // Преобразуем roleID в строку, так как GuildMemberRoleAdd ожидает строку
    err := session.GuildMemberRoleAdd(guildID, userID, strconv.FormatInt(roleID, 10))
    if err != nil {
        return fmt.Errorf("не удалось выдать роль: %v", err)
    }

    log.Printf("Роль %d успешно выдана пользователю %s", roleID, userID)
    return nil
}

// ProfileHandler — возвращает данные профиля в формате JSON

func ProfileHandler(w http.ResponseWriter, r *http.Request) {

    session, _ := SessionStore.Get(r, "discord-session")
    userID, ok := session.Values["userID"].(string)
    if userID, ok := session.Values["userID"].(string); ok {
        log.Printf("Session userID: %s", userID)
    } else {
        log.Printf("Session userID: not found")
    }
    if !ok {
        http.Error(w, "Не авторизован", http.StatusUnauthorized)
        return
    }

    // Получаем данные пользователя (nickname, coins, status) из базы
    nickname, coins, status, err := db.GetUser(userID)
    if err != nil {
        http.Error(w, "Ошибка при получении данных пользователя: "+err.Error(), http.StatusInternalServerError)
        return
    }

    // Получаем аватар пользователя из сессии
    avatar, ok := session.Values["avatar"].(string)
    if !ok || avatar == "" {
        avatar = "/default-avatar.png" // Если аватар не найден, используем дефолтный
    }

    // Получаем роли пользователя из Discord
    userRoles := GetUserRolesFromDiscord(userID)

    // Проверяем, имеет ли пользователь роль VIP
    vipRole := "1320294095020621844" // ID VIP роли
    isVIP := false
    for _, role := range userRoles {
        if role == vipRole {
            isVIP = true
            break
        }
    }

    // Создаем объект с данными профиля для отправки в JSON
    profileData := map[string]interface{}{
        "username": nickname,
        "avatar":   avatar,
        "coins":    coins,
        "status":   status,
        "roles":    userRoles,
        "isVIP":    isVIP, // Добавляем поле VIP
    }

    // Устанавливаем заголовок ответа для JSON
    w.Header().Set("Content-Type", "application/json")

    // Отправляем профиль в формате JSON
    err = json.NewEncoder(w).Encode(profileData)
    if err != nil {
        http.Error(w, "Ошибка отправки данных профиля: "+err.Error(), http.StatusInternalServerError)
    }
}

func GetUserRolesFromDiscord(userID string) []string {
    guildID := global.ServerID // ID вашего сервера
    session := global.BotSession
    if session == nil {
        return nil
    }

    member, err := session.GuildMember(guildID, userID)
    if err != nil {
        log.Printf("Ошибка при получении ролей пользователя: %v", err)
        return nil
    }

    var roles []string
    for _, roleID := range member.Roles {
        roles = append(roles, roleID)
    }
    return roles
}

// LogoutHandler — удаляет данные сессии пользователя
func LogoutHandler(w http.ResponseWriter, r *http.Request) {
    // Удаляем сессию
    session, _ := SessionStore.Get(r, "discord-session")
    session.Options = &sessions.Options{
        Path:     "/",
        MaxAge:   86400 * 30, // 30 дней
        HttpOnly: true,
        Secure:   true,                 // Включите, если используете HTTPS
        SameSite: http.SameSiteLaxMode, // Рекомендуется для защиты от CSRF атак
    }

    session.Values = make(map[interface{}]interface{}) // Полностью очищаем значения
    session.Save(r, w)

    // Перенаправляем на главную страницу
    http.Redirect(w, r, "/", http.StatusSeeOther)
}

func CheckAuthHandler(w http.ResponseWriter, r *http.Request) {
    session, _ := SessionStore.Get(r, "discord-session")
    if _, ok := session.Values["userID"].(string); ok {
        w.WriteHeader(http.StatusOK)
    } else {
        w.WriteHeader(http.StatusUnauthorized)
    }
}

func GetUserIdFromSessionOrHeader(r *http.Request) string {
    session, _ := SessionStore.Get(r, "discord-session")
    if userID, ok := session.Values["userID"].(string); ok {
        return userID
    }

    userIDFromHeader := r.Header.Get("X-User-ID")
    if userIDFromHeader != "" {
        return userIDFromHeader
    }

    return ""
}

index.js

document.addEventListener('DOMContentLoaded', () => {
    document.getElementById('news-section').classList.remove('loading');

    // Сначала скрываем окно авторизации
    document.getElementById('unauthorized-message').classList.remove('show');

    function preloadPage(url) {
        let xhr = new XMLHttpRequest();
        xhr.open('GET', url, true);
        xhr.send();
    }

    // Предзагрузка страниц при загрузке текущей страницы
    window.addEventListener('load', function() {
        ['profile.html', 'shop.html'].forEach(url => {
            const link = document.createElement('link');
            link.rel = 'prefetch';
            link.href = url;
            document.head.appendChild(link);
        });
    });

    document.getElementById('news-section').classList.remove('blurred');

    fetch('/profile', {
        method: 'GET',
        credentials: 'include'
    })

    .then(response => {
            if (!response.ok) {
                // Показываем окно только если пользователь не авторизован
                document.getElementById('blur-overlay').classList.add('blurred');
                document.getElementById('nav-links').classList.add('hidden');
                document.getElementById('unauthorized-message').classList.add('show');
                throw new Error('Пользователь не авторизован');
            }
            return response.json();
        })
        .then(data => {
            // Логирование полученных данных пользователя
            console.log('Полученные данные пользователя:', data);

            // Проверка ключей данных
            if (!data.username) {
                console.error('Отсутствует ключ "username" в данных пользователя');
            } else {
                console.log(`Имя пользователя: ${data.username}`);
            }

            if (!data.roles || !Array.isArray(data.roles)) {
                console.error('Отсутствует ключ "roles" или он не является массивом');
            } else {
                console.log(`Роли пользователя: ${data.roles.join(', ')}`);
            }

            if (data.isVIP === undefined) {
                console.error('Отсутствует ключ "isVIP" в данных пользователя');
            } else {
                console.log(`Статус VIP: ${data.isVIP ? 'Да' : 'Нет'}`);
            }

            // Убираем классы, когда пользователь авторизован
            document.getElementById('blur-overlay').classList.remove('blurred');
            document.getElementById('nav-links').classList.remove('hidden');
            document.getElementById('unauthorized-message').classList.remove('show');

            const newsSection = document.getElementById('news-section');
            const userRoles = data.roles || [];
            const adminRoles = ["1318188643944501289", "1318188870415679508", "1318189013823389711"];
            const isAdmin = userRoles.some(role => adminRoles.includes(role));

            document.getElementById('tournaments-link').style.display = isAdmin ? 'inline' : 'none';

            // Функция для обновления новостей
            function updateNews() {
                fetch('/get-news')
                    .then(response => response.json())
                    .then(news => {
                        console.log('Загруженные новости:', news);

                        newsSection.innerHTML = '';
                        if (news.length === 0) {
                            console.log('Новости не загружены или пусты');
                        } else {
                            news.forEach((newsData) => {
                                const newsBlock = document.createElement('div');
                                newsBlock.classList.add('news-block');

                                const deleteButton = document.createElement('span');
                                deleteButton.textContent = '❌';
                                deleteButton.classList.add('delete-btn');

                                if (isAdmin) {
                                    deleteButton.style.display = 'block';
                                    deleteButton.onclick = () => {
                                        fetch(`/delete-news?id=${newsData.id}`, { method: 'DELETE' })
                                            .then(response => {
                                                if (response.ok) {
                                                    newsSection.removeChild(newsBlock);
                                                    console.log('Новость успешно удалена');
                                                } else {
                                                    console.error('Ошибка при удалении новости');
                                                }
                                            })
                                            .catch(err => console.error('Ошибка:', err));
                                    };
                                } else {
                                    deleteButton.style.display = 'none';
                                }

                                newsBlock.innerHTML = `
                  <img src="${newsData.image}" alt="News image" loading="lazy">
                  <h3>${newsData.title}</h3>
                  <p>${newsData.text}</p>
                `;

                                newsBlock.appendChild(deleteButton);
                                newsSection.insertBefore(newsBlock, newsSection.firstChild);
                            });
                        }
                    })
                    .catch(err => console.error('Ошибка загрузки новостей:', err));
            }

            // Первоначальная загрузка новостей
            updateNews();

            // Интервал для повторной загрузки новостей каждые 10 секунд
            setInterval(updateNews, 10000);
        })
        .catch(err => {
            console.error('Ошибка:', err.message);
        });

    // Функция для показа окна авторизации
    const authPrompt = document.getElementById('unauthorized-message');

    function showAuthPrompt() {
        authPrompt.classList.add('show');
    }


});

I used different methods and opened ports. And used CORS settings.

On page load a button must reflect its database state. How to do?

A page includes buttons that have state. When the page loads, how to query the database during the onLoad event so the actual button state is reflected in the button as drawn?

I expected this to work:

window.addEventListener("DOMContentLoaded", ready);
function ready(event) {
   document.getElementById(THE_BUTTON).click();
}

Any suggestions welcome.
Ron

How can i optimize these repetitive if with queryselectors inside?

i need to simplify these lines of code of react, i see is a bit repetitive, but i dont know if theres any way to improve this

Any idea will be received!!!

useEffect(() => {
    document.addEventListener("scroll", () => {
      // Prices
      if(document.querySelector("#prices").getBoundingClientRect().top == 78){
        document.querySelector(".pricesArr").style.opacity = 1
        document.querySelector(".pricesArr").classList.add("pricesArrAnim")
      }
      else if(document.querySelector("#prices").getBoundingClientRect().top > 660){
        document.querySelector(".pricesArr").style.opacity = 0
        document.querySelector(".pricesArr").classList.remove("pricesArrAnim")
      }
      // Contact
      if(document.querySelector("#contact").getBoundingClientRect().top == 78){
        document.querySelector(".contact").style.opacity = 1
        document.querySelector(".contact").classList.add("contactAnim")
      }
      else if(document.querySelector("#contact").getBoundingClientRect().top > 660){
        document.querySelector(".contact").style.opacity = 0
        document.querySelector(".contact").classList.remove("contactAnim")
      }
      // Moreinfo
      if(document.querySelector("#moreinfo").getBoundingClientRect().top == 78){
        document.querySelector(".moreinfo").style.opacity = 1
        document.querySelector(".moreinfo").classList.add("moreinfoAnim")
      }
      else if(document.querySelector("#moreinfo").getBoundingClientRect().top > 660){
        document.querySelector(".moreinfo").style.opacity = 0
        document.querySelector(".moreinfo").classList.remove("moreinfoAnim")
      }
    })
  })

react-date-picker tile disabled not working

I have a project https://www.motoschool.co.nz/ with a booking section using react-date-picker. This was previously working with blocked off dates coming from datocms, but now even passing an inline tileDisabled function isn’t working e.g.

<DatePicker tileDisabled={({ activeStartDate, date, view }) => date.getDay() === 0} />

Has anyone else had a similar experience or know what could be going wrong/how to troubleshoot? I’ve already commented out and rebuilt everything based on the old website which is working and tried inline functions, clearing cache with gatsby clean, re-installing packages etc but I’m not sure why the tiles wouldn’t be disabled.

Any tips or suggestions for alternative calendar component would be appreciated!

Full page code below:

import React, {useRef, useEffect, useState} from "react"
import styled from "@emotion/styled"
import { useForm } from "react-hook-form"
import ReCAPTCHA from "react-google-recaptcha";
import { navigate } from "gatsby";
import { isWithinInterval } from "date-fns";
import DatePicker from 'react-date-picker'
import 'react-date-picker/dist/DatePicker.css';
import 'react-calendar/dist/Calendar.css';

const FormDiv = styled.div`
width: 694px;
// margin-right: 100px;
box-sizing: border-box;
display: flex;
justify-content: center;
align-items: center;
form {
    // padding: 50px;
    width: 694px;
    // margin-right: 20px;
    // max-width: 800px;
    min-height: 500px;
    height: 100%;
    max-height: 800px;
    // background-color: white;
    border-radius: 2px;
    display: flex;
    flex-direction: column;
    justify-content: center;
    .select-style {
        // background-color: white;
        padding: 5px;
        border: solid 1px black;
        :hover {
            cursor: pointer;
        }
    }
    .button-style {
        margin-top: 40px;
        padding: 20px;
        background-color: white;
        color: black;
        border: none;
        border-radius: 10px;
        font-size: 14px;
        font-weight: 600;
        transition: .3s;
        :hover {
            cursor: pointer;
            // background-color: #635bff;
        }
    }
           .back {
    width: 28%;
    margin-right: 2%;
    }
    .next {
    width: 70%;
    }

    label {
        font-weight: 600;
        margin-top: 50px;
        // margin-left: 10px;
        margin-bottom: 4px;
    }
    input{
        font-size: 20px;
        padding: 15px;
        background-color: black;
        color: white;
        border-radius: 10px;
        border: 2px solid rgba(255,255,255,1);
        transition: .3s;
    }
    // input:hover {
    //     border: 1px solid rgba(0,0,0,0.5);
    // }
    input:focus-visible, textarea:focus-visible {
        border: 2px solid rgba(255,255,255,0.5);
        outline: 0;
    }
}
.time-selection {
        display: flex;
        flex-wrap: wrap;
        // justify-content: space-between;
        div {
            // border: solid 2px white;
            border: 2px solid rgba(255,255,255,0.2);
            border-radius: 10px;
            font-size: 16px;
            font-weight: 600;
            padding: 14px 14px;
            box-shadow: rgba(0, 0, 0, 0.12) 0px 1px 3px, rgba(0, 0, 0, 0.24) 0px 1px 2px;
            margin: 0 10px 10px 0;
            transition: .3s;
            // background-color: grey;
            :hover {
                cursor: pointer;
                border: 2px solid rgba(255,255,255,0.5);
            }
        }
        .active-time {
            background-color: white;
            color: black;
        }
}
.grecaptcha-badge { visibility: hidden!important; }
.recaptcha-sub {
font-size: 14px;
color: hsla(40,22%,92%,.6);
}
.react-date-picker__wrapper {
width: 100%;
padding: 15px;
  font-size: 20px;
  padding: 15px;
  background-color: black;
  color: white;
  border-radius: 10px;
//   border: 2px solid rgba(255,255,255,1);
  border: 2px solid rgba(255,255,255,0.2);
  -webkit-transition: .3s;
  transition: .3s;
  :hover {
  cursor: pointer;
  border: 2px solid rgba(255,255,255,0.5);
  }
  input {
  border: none;
  }
  input:focus-visible {
  border: none;
  }
}
.react-date-picker__calendar-button__icon, .react-date-picker__clear-button__icon {
stroke: white;
transition: .3s;
}
.react-date-picker__inputGroup__input {
padding: 0;
}
@media(max-width: 940px){
    width: 90vw!important;
    form {
    box-sizing: border-box;
    width: 90vw!important;
}

`

// function isWithinRange(date, range) {
//     console.log("isWithinRangeRunning", date, range)
//     return isWithinInterval(date, { start: range[0], end: range[1] });
// }
// function isWithinRanges(date, ranges) {
//     console.log("isWithinRangesRunning", date, ranges)
//     return ranges.some(range => isWithinRange(date, range));
// }
// let in3Days = new Date(2024, 11, 28);
// let in5Days = new Date(2024, 11, 28);
// let in13Days = new Date(2024, 11, 30);
// let in15Days = new Date(2024, 11, 31);

// let testDays = new Date(2024, 12, 26);

function isWithinRange(date, range) {
    console.log("isWithinRangeRunning")
    return isWithinInterval(date, { start: range[0], end: range[1] });
}
function isWithinRanges(date, ranges) {
    console.log("isWithinRangesRunning")
    return ranges.some(range => isWithinRange(date, range));
}
let in3Days = new Date(2025, 1, 28);
let in5Days = new Date(2025, 1, 28);
let in13Days = new Date(2025, 1, 26);
let in15Days = new Date(2025, 1, 26);

export default function ContactElectrical({datesUnavailable, setFormStage, timesAvailable, totalPrice, name, phone, email, adults, youth, lessonString, gearString, bikeString, hourString}){

    ///need to reformat dates here before adding to state, or do in useEffect

    const reRef = useRef();
    const [selectedDate, updateSelectedDate] = useState(new Date());
    const [serverState, setServerState] = useState({formSent: false});
    const [activeTime, setActiveTime] = useState(0)
    const [bookedDates, setBookedDates] = useState([ [in3Days, in5Days],[in13Days, in15Days],])
    // const [bookedDates, setBookedDates] = useState([ [in3Days, in5Days],[in13Days, in15Days],])

    console.log("datesUnavailable: ", datesUnavailable)
    // console.log("booked", bookedDates)

    useEffect(()=> {
        console.log("dates Unavailable running")
            let datesUnavailableRanges = []
            for(let i = 0; i < datesUnavailable.length; i++){
                let d = datesUnavailable[i].bookedDate.split("/")
                //except here they need to be in the right format so I need to seperate and make it so it's like this: new Date(2023, 11, 26);
                datesUnavailableRanges.push([new Date(Number(d[2]), Number(d[1])-1, Number(d[0])), new Date(Number(d[2]), Number(d[1])-1, Number(d[0]))])
            }
            setBookedDates(datesUnavailableRanges);
    },[datesUnavailable])

    // console.log("booked", bookedDates)
    console.log("tile", tileDisabled({date: in3Days, view: "month"}))

    // function tileDisabled({ date, view}) {
    //     console.log("test", view)
    //     console.log("date", date)
    //     console.log("test2", bookedDates)
    //     // Add class to tiles in month view only
    //     if (view === 'month') {
    //       // Check if a date React-Calendar wants to check is within any of the ranges
    //       console.log("test3", isWithinRanges(date, bookedDates))
    //       return isWithinRanges(date, bookedDates);
    //     }
    // }

    function tileDisabled({ date, view}) {
        // Add class to tiles in month view only
        if (view === 'month') {
          // Check if a date React-Calendar wants to check is within any of the ranges
          return isWithinRanges(date, bookedDates);
        }
      }


    const {
        register,
        handleSubmit,
        formState: { errors },
      } = useForm()


      async function onSubmit(data){
        // const reRef = useRef<>();
        const token = await reRef.current.executeAsync();
        reRef.current.reset();
        let dd = selectedDate.getDate();
        let mm = selectedDate.getMonth()+1;
        let yyyy = selectedDate.getFullYear();
        let reformattedDate = dd+"/"+mm+"/"+yyyy;
        fetch("/api/postmark-booking", {
          method: `POST`,
          body: JSON.stringify({
            name: name,
            email: email,
            phone: phone,
            adults: adults,
            youth: youth,
            lesson: lessonString,
            date: reformattedDate,
            time: timesAvailable[activeTime].time,
            gear: gearString,
            bike: bikeString,
            hours: hourString,
            totalPrice: totalPrice,
            token,
        }),
          headers: {
            "content-type": `application/json`,
          },
        })
          .then(res => res.json())
          .then(body => {
            console.log(`response from API:`, body);
          })
          .then(setServerState({formSent: true}))
      }
      console.log({ errors })
      useEffect(() => {
          if (serverState.formSent === true) {
            navigate("/booking-success/");
            setTimeout(() => {
                setServerState({
                    formSent: false
                })
            }, 3000)
          }
      })

  return (
            <FormDiv>
                <ReCAPTCHA 
                    sitekey={process.env.GATSBY_RECAPTCHA_SITE_KEY} 
                    size="invisible"
                    ref={reRef} 
                />
                 <form 
                 onSubmit={handleSubmit(onSubmit)} 
                 autocomplete="on">

          
                    <label htmlFor="email">BOOKING PERIOD:</label>
                    <DatePicker tileDisabled={tileDisabled} onChange={updateSelectedDate} value={selectedDate}  minDate={new Date(2025, 1, 9)} format="dd-MM-y"/>
                    {/* <DatePicker tileDisabled={({ activeStartDate, date, view }) => date.getDay() === 0} /> */}

                    <label>TIME SELECTION:</label>
                    <div className="time-selection">
                        {timesAvailable.map((time, i)=>(
                            <div key={"timeslot "+i} onClick={()=>setActiveTime(i)} className={i === activeTime ? "active-time" : ""}>{time.time}</div>
                        ))}
                    </div>

                    <h3>Total: ${totalPrice}</h3>
                    <p className="recaptcha-sub">This site is protected by reCAPTCHA and the Google <a href="https://policies.google.com/privacy">Privacy Policy</a> and <a href="https://policies.google.com/terms">Terms of Service</a> apply.</p>
                    
                    <div>
                    <button className="button-style back" onClick={(e) => {e.preventDefault();setFormStage(1)}}>BACK</button>
                    <button
                        onClick={() => setFormStage(3)}
                        type="submit" 
                        className="g-recaptcha button-style next"
                        data-sitekey="site_key"
                        data-callback='onSubmit'
                        data-action='submit'
                    >
                    SEND REQUEST</button>
                    </div>
                </form>
            </FormDiv>
  )
}

How do I write a promise chain with forks that depend on conditions?

I’m looking for a way to create a promise chain that has forks. By “forks” I mean the chain could go in two or more different directions depending on some condition. For example:

getIndexes(searchString).then(indexes => {
  if (indexes.length) return searchByIndex(indexes, 1, 5);
  else return searchAllRecords(searchString);
}.then(records => {
  // if searched all records
  return createIndexes(records, searchString);
  // else
  return records;
}).then( // how to handle multiple forks

As you can see, there are conditions in each then block and depending on the condition, it will return something different to the next then block by calling different functions. So we don’t know what each then block will receive as it depends on the outcomes of the previous conditions, and we have to do checks to see how it should be handled. This doesn’t seem ideal. What is the best way to build a promise chain when it contains forks in the flow?

How can I make an embedded HTML element behave as sticky between two specific sections in Wix velo?

How can I make an embedded HTML element behave as sticky between two specific sections in Wix?

Wix Studio Editor and Velo by Wix

I want an embedded HTML element to behave in the following way:

It should remain sticky between Section 1 and Section 2.
When scrolling to Section 2, the element should stick to a specific position (Point B).
As I scroll further to Section 3, it should remain at the same position in Section 2.
When scrolling back from Section 2 to Section 1, the element should move back to its original position (Point A).
In short, the HTML element should only act sticky between Section 1 and Section 2, and move as described when scrolling up or down.

I’ve tried several methods to achieve this, including:

Creating anchor points and manipulating the element using Velo by Wix.
Making the element sticky directly through Wix Studio.
Adjusting its position programmatically using the wix-window API.
However, none of these methods have worked to produce the desired behavior.

I was trying this method (the code below), but I am always having issues.
I tried sending the element position directly with postMessage, but I keep encountering problems.
Do you think I need to change the method, maybe by using Intersection Observer in Wix?

import wixWindowFrontend from 'wix-window-frontend';

$w.onReady(function () {
    trackScroll();
});

function trackScroll() {
    wixWindowFrontend.getBoundingRect()
        .then((windowSizeInfo) => {
            const scrollY = windowSizeInfo.scroll.y;
            const pointA = { top: 500, left: 100 }; 
            const pointB = { top: 800, left: 200 }; 
            const scrollRange = 300; 
            const htmlComponent = $w('#html1'); 

            let currentTop = 0;
            let currentLeft = 0;

            if (scrollY >= pointA.top && scrollY <= pointB.top) {
                const offset = (scrollY - pointA.top) / scrollRange;
                currentTop = pointA.top + (offset * (pointB.top - pointA.top));
                currentLeft = pointA.left + (offset * (pointB.left - pointA.left));

                htmlComponent.postMessage({
                    type: 'updatePosition',
                    style: {
                        top: `${currentTop}px`,
                        left: `${currentLeft}px`
                    },
                    additionalData: {
                        isVisible: true
                    }
                });
            } else if (scrollY > pointB.top) {
                currentTop = pointB.top;
                currentLeft = pointB.left;

                htmlComponent.postMessage({
                    type: 'updatePosition',
                    style: {
                        top: `${currentTop}px`,  
                        left: `${currentLeft}px`,
                    },
                    additionalData: {
                        isVisible: true
                    }
                });
            } else {
                htmlComponent.postMessage({
                    type: 'resetPosition',
                    style: {
                        top: '',
                        left: ''
                    },
                    additionalData: {
                        isVisible: false 
                    }
                });
            }

            setTimeout(trackScroll, 100);
        });
}

Additional information:
The element in question is an embedded HTML element. The scrolling behavior needs to be responsive and consistent across all devices. If anyone has a solution or best practices for achieving this, I’d appreciate your guidance!

How to draw HTML using vue 3 corresponding to every item in an array?

I’m trying to use Vue for the first time and I want to render HTML for each item in an array. Specifically, whenever a new item is added to the array, I would like the HTML to be updated accordingly.

Here’s what I’ve tried so far:

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

        const app = Vue.createApp({
            data() {
                return {
                    messages: []
                };
            },
            methods: {
                addMessage(message) {
                    this.messages.push(message);
                }
            },
            template: `
                <div class="list-group">
                    <div v-for="(message, index) in messages" :key="index" class="list-group-item">
                        <div class="d-flex">
                            <div class="p-2">
                                <i class="fa-solid fa-user fa-2xl text-primary"></i>
                                {{ message.role === 'user' ? '<i class="fa-solid fa-user fa-2xl text-primary"></i>' : '<i class="fa fa-robot fa-2xl text-primary"></i>' }}
                            </div>
                            <div class="p-2 flex-grow-1">{{ message.text }} <button type="button" class="copy-button">Copy</button></div>
                        </div>
                    </div>
                </div>  
            `
        });

        app.mount('#app');

        for (let i = 0; i < 10; i++) {

            app.addMessage({
                role: 'user',
                text: 'message #' + i
            });
        }
    });
<script src="https://unpkg.com/vue@3/dist/vue.global.js"></script>

<div id="app"></div>

But keep getting an error in the console

Uncaught TypeError: app.addMessage is not a function

How can I resolve this issue?

Additionally, I want to add buttons to my template. After the HTML is rendered and added to the DOM, I would like to attach event listeners to these buttons. For example, for any button with the class copy-button, I want to add an event listener so that when the user clicks any button with that class, it logs “Hello” to the console.

How can I add event listeners to dynamically added elements after they are inserted into the DOM?

Overwrite a column of an array in AppScript

Say I have a 2D array with n rows, e.g.

table = [["Team 1", 3, 3, 0, 0, 9, 1],
         ["Team 2", 3, 0, 1, 2, 1, 3],
         ["Team 3", 3, 2, 0, 1, 6, 2],
         ["Team 4", 3, 0, 1, 2, 1, 3]]

Then say I have a 1D array of length n, e.g.

newRank = [1, 4, 2, 3]

How would I overwrite column i of table with newRank? E.g. column 6

Is there such thing as column slicing as in Python’s Numpy?