scrolling snapping to next item instead of moving continuously

I have a navigation menu with overflow-y: scroll property, this navigation menu consists of multiple items.

I want to know if there is a way that each time user moves the scrollbar, it moves exactly to next item of menu.

this is my menu

enter image description here

and once the user moves the scroll bar I want the navigation menu to snap to second item like this:

enter image description here

scrolling snaping to next item instead of moving continuously

I have a navigation menu with overflow-y: scroll property, this navigation menu consists of multiple items.

I want to know if there is a way that each time user moves the scrollbar, it moves exactly to next item of menu.

this my menu

enter image description here

and once the user moves the scroll bar I want the navigation menu to snap to second item like this:

enter image description here

How to simulate click after extracting data using playwright

I’m trying to simulate a next page using playwright but I have to gather data first. The data per page only returns 50 posts. I need to get the first 150 post. The first 50 i can get data but on the next post I’m having a hard time because of the for loop because its not async. This is my approach:

  const browser = await chromium.launch({headless: false});
  const context = await browser.newContext();
  const page = await context.newPage();

  // go to Hacker News
  await page.goto("localhost:3000/posts");
  
  // This data only returns the first 50 posts
  const data = await page
    .getByRole("table")
    .nth(2)
    .locator("tbody")
    .locator(".age")
    .all();

  const strings = [];

  for (const row of data) {
    strings.push(await row.innerText());
    console.log(await row.innerText());
  }

  await page.locator(".morelink").click({clickCount: 2, delay: 5}); <-- after click I wanted to run the `const data = await page...` code again 2 times to get the first 150.

  console.log(strings);

I’m not familiar with playwright so I’m trying to figure out if there’s a way to chain locator and simulate click.

Best Practices for Encapsulating jQuery Selectors: Static Methods vs. Alternatives [closed]

I’m maintaining a jQuery-based project at my company, focusing on improving code readability and centralized management. I’ve implemented a method to encapsulate jQuery selectors, but I’m unsure if it’s the optimal approach. Here’s a simplified version of my current implementation:

class Field {
  static init() {
    this.abc = $('input#abc');
    this.ccc = $('input#ccc');
  }
}

$(function () {
  Field.init();
  getName();
});

const getName = () => {
  const nameabc = Field.abc.val();
  console.log(nameabc);
};

Based on this implementation, I have the following questions:

  1. What are the specific advantages and drawbacks of encapsulating jQuery selectors in this manner? Are there any performance implications or maintainability issues I should be aware of?
  2. How does this static encapsulation method compare to other approaches like lazy loading or dependency injection for jQuery selectors? In what scenarios might each approach be more appropriate?
  3. Are there any well-regarded design patterns or best practices for managing jQuery selectors in large-scale applications? I’m particularly interested in approaches that balance performance with code organization and maintainability.
  4. Considering the potential for dynamic DOM changes, how can this approach be improved to ensure that selector references remain valid throughout the application’s lifecycle?
  5. Are there any case studies or benchmarks comparing different selector management strategies in jQuery projects, especially for applications requiring high performance and maintainability?
  6. Can you recommend any technical articles, discussions, or tutorials specifically about jQuery selector encapsulation or management? I’m especially interested in resources that explore the pros and cons of different encapsulation methods or provide real-world implementation examples.

I’m looking for answers supported by technical reasoning, performance data, or real-world experience with similar implementations. Code examples demonstrating alternative approaches would be particularly helpful. Additionally, links to relevant technical articles, forum discussions, or tutorials would be greatly appreciated.

Invoking Google Finance API from app script with schedule run. WEIRD RESULT

So basically, I need to use googlefinance api to fetch over 100 dow jones indicies daily price update with around 3 years of data.

Now you may ask why don’t I simply use:
=GOOGLEFINANCE("ticker", "all","START_DATE",TODAY(), "DAILY")
directly in my google sheet.Since the function is using TODAY(), it should be able to refresh and update with daily frequency.

That’s because I find out that the google sheet only returns [[”NA”]] when I connect the sheets to my google colab notebook. In short, after some testing, I learnt that the function is returning more like a preview of data rather than a snapshot of data on the google sheet.

Therefore, i come up with a workaround: to use app script to call the function and fill up the google sheet. (If you are confused here, the difference it makes is there’s no google function observed in any cells from the sheet, only the data itself)

So far the logic works for me, it allows me to update the sheet while getting its data from my colab notebook :

// Function to update a single index sheet
function updateSingleIndex(ticker, sheetName) {
  var sheet = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(sheetName);
  if (!sheet) {
    Logger.log("Sheet '" + sheetName + "' not found");
    return;
  }
  
  var startDate = new Date();
  startDate.setFullYear(startDate.getFullYear() - 3);  // 3 years ago
  var endDate = new Date();
  
  Logger.log("Updating " + ticker + " from " + startDate.toDateString() + " to " + endDate.toDateString());
  
  // Clear existing content
  sheet.clear();
  
  // Insert the GOOGLEFINANCE formula
  var formula = '=GOOGLEFINANCE("' + ticker + '", "all", DATE(' + startDate.getFullYear() + ',' + (startDate.getMonth()+1) + ',' + startDate.getDate() + '), DATE(' + endDate.getFullYear() + ',' + (endDate.getMonth()+1) + ',' + endDate.getDate() + '), "DAILY")';
  sheet.getRange("A1").setFormula(formula);
  
  // Wait for the formula to calculate (this is not guaranteed to be enough time)
  Utilities.sleep(10000);
  
  // Get the values
  var values = sheet.getDataRange().getValues();
  
  if (values.length > 1) {  // Check if we have data (more than just the header row)
    // Remove the formula and set the values
    sheet.getRange(1, 1, values.length, values[0].length).setValues(values);
    // Check if the last row is today's date
    var lastRowDate = new Date(values[values.length - 1][0]);
    var today = new Date();
    if (lastRowDate.toDateString() !== today.toDateString()) {
      Logger.log(ticker + " might be missing today's data. Last date: " + lastRowDate.toDateString());
    }
    Logger.log(ticker + " updated successfully. Rows: " + values.length);
  } else {
    Logger.log("No data fetched for " + ticker);
  }
}

// Store the list of indices in a separate function for easy management
function getIndices() {
  return [
    {ticker: "pseudo", sheet: "pseudo"},
  ];
}

Now this part is getting weird,the schedule run will invoke the function to update all sheets every morning at 7am(or 8am, i modify it). It works, it updates all the sheets, but not working intentionally:

// Main function to update all indices
function updateAllIndices() {
  var indices = getIndices();
  var scriptProperties = PropertiesService.getScriptProperties();
  var lastProcessedIndex = parseInt(scriptProperties.getProperty('lastProcessedIndex') || '-1');
  
  var startTime = new Date().getTime();
  var timeLimit = 4 * 60 * 1000; // 4 minutes in milliseconds
  
  for (var i = lastProcessedIndex + 1; i < indices.length; i++) {
    if (new Date().getTime() - startTime > timeLimit) {
      // We're approaching the time limit, let's stop and schedule the next run
      scriptProperties.setProperty('lastProcessedIndex', i - 1);
      scheduleNextRun();
      return;
    }
    
    updateSingleIndex(indices[i].ticker, indices[i].sheet);
    
    // Update the last processed index after each successful update
    scriptProperties.setProperty('lastProcessedIndex', i);
  }
  
  // If we've processed all indices, reset the counter and schedule next day's run
  if (lastProcessedIndex >= indices.length - 1) {
    scriptProperties.setProperty('lastProcessedIndex', '-1');
    Logger.log("All indices have been updated.");
    scheduleNextDayRun();
  } else {
    // If we haven't finished all indices, schedule the next run
    scheduleNextRun();
  }
}

function scheduleNextRun() {
  // Delete any existing triggers for updateAllIndices
  deleteTriggers('updateAllIndices');
  
  // Schedule the next run in 1 minute
  ScriptApp.newTrigger('updateAllIndices')
    .timeBased()
    .after(60 * 1000) // 1 minute
    .create();
  
  Logger.log("Next run scheduled in 1 minute.");
}

function scheduleNextDayRun() {
  // Delete any existing triggers for updateAllIndices
  deleteTriggers('updateAllIndices');
  
  // Schedule the next run for tomorrow at 8 AM
  var tomorrow = new Date();
  tomorrow.setDate(tomorrow.getDate() + 1);
  tomorrow.setHours(8, 0, 0, 0);
  
  ScriptApp.newTrigger('updateAllIndices')
    .timeBased()
    .at(tomorrow)
    .create();
  
  Logger.log("Next run scheduled for tomorrow at 8 AM.");
}

function deleteTriggers(functionName) {
  var triggers = ScriptApp.getProjectTriggers();
  for (var i = 0; i < triggers.length; i++) {
    if (triggers[i].getHandlerFunction() == functionName) {
      ScriptApp.deleteTrigger(triggers[i]);
    }
  }
}

function setupDailyTrigger() {
  // Delete any existing triggers for updateAllIndices
  deleteTriggers('updateAllIndices');
  
  // Create a new trigger to run updateAllIndices every day at 8 AM
  ScriptApp.newTrigger('updateAllIndices')
    .timeBased()
    .everyDays(1)
    .atHour(8)
    .create();
  
  Logger.log("Daily trigger set to run updateAllIndices at 8 AM.");
}

Note that my timezone is different, so i actually was expecting to get Tue Sep 24 data as the latest update,the market should close at around 5am at my timezone. Now, looking at the log:

Sep 25, 2024, 7:38:41 AM Info Updating INDEXDJX:DJUSNS from Sat Sep 25 2021 to Wed Sep 25 2024 Sep 25, 2024, 7:38:57 AM Info INDEXDJX:DJUSNS might be missing today's data. Last date: Mon Sep 23 2024

It actually only download the data from previous day. Normally I would question whether my getallindicies() function is bugging out for me. However, when I manually execute the function, it somehow works again:

Sep 25, 2024, 10:10:15 AM Info Updating INDEXDJX:DJUSNS from Sat Sep 25 2021 to Wed Sep 25 2024 Sep 25, 2024, 10:10:26 AM Info INDEXDJX:DJUSNS might be missing today's data. Last date: Tue Sep 24 2024 Sep 25, 2024, 10:10:26 AM Info INDEXDJX:DJUSNS updated successfully. Rows: 754

Am i missing something, here? Why does my manual execution works while automatic schedule doesn’t?
Is it only because of the time delay?
Also, one minor thing is that I set it to run at 7am before(YES, i know my code shows 8am, but that was after modification), the schedule run starts only at 7:38AM, which seems like a huge discrepancy.

Webhook Error: Webhook payload must be provided as a string or a Buffer

I’m stuck on this, could someone help me?
I put it in postman: https://my-server/api/webhook

and i receive this:

Webhook Error: Webhook payload must be provided as a string or a
Buffer (https://nodejs.org/api/buffer.html) instance representing the
raw request body.Payload was provided as a parsed JavaScript object instead. Signature verification is impossible without access to the
original signed material.

Learn more about webhook signing and explore webhook integration
examples for various frameworks at
https://github.com/stripe/stripe-node#webhook-signing

i’m using ReactJS and Node

const express = require('express');
const Stripe = require('stripe');
const bodyParser = require('body-parser');
const cors = require('cors');
const webhookRoutes = require('./routes/webhook');

const app = express();
const stripe = Stripe(
  'sk_test_51Q1x2cRraDIE2N6qLbzeQgMBnW5xSG7gCB6W3tMxCfEWUz8p7vhjnjCAPXHkT2Kr50i6rgAC646BmqglaGWp5dhd00SZi9vWQg',
);

app.use(cors());
app.use(express.json());
app.use('/webhook', express.raw({ type: 'application/json' }));
app.use('/api', webhookRoutes);

app.post('/create-checkout-session', async (req, res) => {
  try {
    console.log('Received request to create checkout session');

    const session = await stripe.checkout.sessions.create({
      payment_method_types: ['card'],
      line_items: [
        {
          price_data: {
            currency: 'usd',
            product_data: {
              name: 'Nome do Produto',
            },
            unit_amount: 2000,
          },
          quantity: 1,
        },
      ],
      mode: 'payment',
      success_url: 'http://localhost:5173/success',
      cancel_url: 'http://localhost:5173/cancel',
    });

    console.log('Checkout session created successfully:', session);
    res.json({ id: session.id });
  } catch (error) {
    console.error('Error creating checkout session:', error.message);
    res.status(500).json({
      error: 'Failed to create checkout session',
    });
  }
});

const PORT = process.env.PORT || 4242;
app.listen(PORT, () => console.log(`Server running on port ${PORT}`));
const express = require('express');
const Stripe = require('stripe');
const router = express.Router();
const stripe = Stripe('sk_test');

router.post('/webhook', async (req, res) => {
  const sig = req.headers['stripe-signature'];

  let event;

  try {
    event = stripe.webhooks.constructEvent(req.body, sig, 'whsec_');
  } catch (err) {
    console.log(`Webhook Error: ${err.message}`);
    return res.status(400).send(`Webhook Error: ${err.message}`);
  }

  if (event.type === 'checkout.session.completed') {
    const session = event.data.object;

    console.log('Pagamento confirmado:', session);
  }

  res.json({ received: true });
});

module.exports = router;

I am working on a group project where we have a live website. I want to add functionality where the requests to reset password goes to a log file [closed]

Our front end is in react/node js and the backend is in python. Firstly, would that go in the backend, frontend, or both. Secondly whats the best approach. Also, we have created a logger.py file which is in our models folder which is in the backend folder.

I honestly don’t know where to start which is the issue. I am assuming that I have to create a new file and import our own logger class, but I don’t know if that is the right approach.

Centering issue with gesture-based bubble grid

I’m working on a React Native app with a grid of bubbles that can be panned and centered. I’m using react-native-gesture-handler and react-native-reanimated, but I’m having issues with the centering logic.

I’m developing a React Native app with a pannable grid of bubbles. After panning, I’m trying to select the bubble closest to the screen’s center, but my findMostCenteredBubble function is not working as expected.

The findMostCenteredBubble function is selecting bubbles that are visually far from the center instead of the bubble that appears most centered on the screen.

import React, {useState, useRef, useEffect} from 'react';
import {
  View,
  TouchableOpacity,
  Text,
  StyleSheet,
  Dimensions,
} from 'react-native';
import Animated, {
  ZoomIn,
  useSharedValue,
  useAnimatedStyle,
  withSpring,
  runOnJS,
} from 'react-native-reanimated';
import {
  GestureHandlerRootView,
  GestureDetector,
  Gesture,
} from 'react-native-gesture-handler';

const bubbles = Array.from({length: 60}, (_, i) => ({
  id: i + 1,
  label: `Bubble ${i + 1}`,
}));

interface BubblePosition {
  id: number;
  x: number;
  y: number;
}

const BUBBLE_SIZE = 80;
const BUBBLE_MARGIN = 10;
const BUBBLES_PER_ROW = 6;

const {width: SCREEN_WIDTH, height: SCREEN_HEIGHT} = Dimensions.get('window');

const BubbleEffect = () => {
  const [selectedBubble, setSelectedBubble] = useState<number | null>(null);
  const translateX = useSharedValue(0);
  const translateY = useSharedValue(0);
  const offsetX = useSharedValue(0);
  const offsetY = useSharedValue(0);

  const totalWidth = BUBBLES_PER_ROW * (BUBBLE_SIZE + BUBBLE_MARGIN * 2);
  const totalHeight =
    Math.ceil(bubbles.length / BUBBLES_PER_ROW) *
    (BUBBLE_SIZE + BUBBLE_MARGIN * 2);

  const initialOffsetX = (SCREEN_WIDTH - totalWidth) / 2;
  const initialOffsetY = (SCREEN_HEIGHT - totalHeight) / 2;

  console.log('Screen Dimensions:', {SCREEN_WIDTH, SCREEN_HEIGHT});
  console.log('Grid Dimensions:', {totalWidth, totalHeight});
  console.log('Initial Offset:', {initialOffsetX, initialOffsetY});

  const bubblePositions: BubblePosition[] = bubbles.map((_, index) => ({
    id: index + 1,
    x: (index % BUBBLES_PER_ROW) * (BUBBLE_SIZE + BUBBLE_MARGIN * 2),
    y: Math.floor(index / BUBBLES_PER_ROW) * (BUBBLE_SIZE + BUBBLE_MARGIN * 2),
  }));

  const handleBubblePress = (id: number) => {
    setSelectedBubble(id);
    centerBubble(id);
  };

  const findMostCenteredBubble = () => {
    'worklet';
    const centerX = SCREEN_WIDTH / 2;
    const centerY = SCREEN_HEIGHT / 2;

    console.log('Screen Center:', {centerX, centerY});
    console.log('Current Translation:', {
      x: translateX.value,
      y: translateY.value,
    });

    return bubblePositions.reduce(
      (closest, bubble) => {
        const bubbleCenterX =
          bubble.x + translateX.value + BUBBLE_SIZE / 2 + initialOffsetX;
        const bubbleCenterY =
          bubble.y + translateY.value + BUBBLE_SIZE / 2 + initialOffsetY;

        const distance = Math.sqrt(
          Math.pow(bubbleCenterX - centerX, 2) +
            Math.pow(bubbleCenterY - centerY, 2),
        );

        console.log(`Bubble ${bubble.id}:`, {
          centerX: bubbleCenterX,
          centerY: bubbleCenterY,
          distance,
        });

        if (distance < closest.distance) {
          return {id: bubble.id, distance};
        }
        return closest;
      },
      {id: -1, distance: Infinity},
    );
  };

  const centerBubble = (bubbleId: number) => {
    'worklet';
    const bubble = bubblePositions.find(b => b.id === bubbleId);
    if (bubble) {
      const targetX =
        SCREEN_WIDTH / 2 - (bubble.x + BUBBLE_SIZE / 2 + initialOffsetX);
      const targetY =
        SCREEN_HEIGHT / 2 - (bubble.y + BUBBLE_SIZE / 2 + initialOffsetY);

      console.log('Centering Bubble:', {id: bubbleId, targetX, targetY});

      translateX.value = withSpring(targetX);
      translateY.value = withSpring(targetY);
    }
  };

  const panGesture = Gesture.Pan()
    .onStart(() => {
      offsetX.value = translateX.value;
      offsetY.value = translateY.value;
    })
    .onUpdate(event => {
      translateX.value = offsetX.value + event.translationX;
      translateY.value = offsetY.value + event.translationY;
    })
    .onEnd(() => {
      console.log('Pan Gesture Ended');
      const mostCentered = findMostCenteredBubble();
      if (mostCentered.id !== -1) {
        console.log('Most Centered Bubble:', mostCentered);
        centerBubble(mostCentered.id);
        runOnJS(setSelectedBubble)(mostCentered.id);
      }
    });

  const animatedStyle = useAnimatedStyle(() => {
    return {
      transform: [
        {translateX: translateX.value},
        {translateY: translateY.value},
      ],
    };
  });

  // Initial centering
  useEffect(() => {
    const initialCenterId = Math.ceil(bubbles.length / 2);
    console.log('Initial Centering:', initialCenterId);
    centerBubble(initialCenterId);
    setSelectedBubble(initialCenterId);
  }, []);

  return (
    <GestureHandlerRootView style={styles.safeArea}>
      <GestureDetector gesture={panGesture}>
        <Animated.View style={[styles.scrollContainer, animatedStyle]}>
          <View
            style={[
              styles.container,
              {
                width: totalWidth,
                height: totalHeight,
                left: initialOffsetX,
                top: initialOffsetY,
              },
            ]}>
            {bubblePositions.map(bubble => (
              <TouchableOpacity
                key={bubble.id}
                onPress={() => handleBubblePress(bubble.id)}
                activeOpacity={0.7}
                style={[
                  styles.bubbleWrapper,
                  {
                    left: bubble.x,
                    top: bubble.y,
                  },
                ]}>
                <Animated.View
                  entering={ZoomIn.duration(300)}
                  style={[
                    styles.bubble,
                    selectedBubble === bubble.id && styles.selectedBubble,
                  ]}>
                  <Text style={styles.label}>
                    {bubbles[bubble.id - 1].label}
                  </Text>
                </Animated.View>
              </TouchableOpacity>
            ))}
          </View>
        </Animated.View>
      </GestureDetector>
    </GestureHandlerRootView>
  );
};

const styles = StyleSheet.create({
  safeArea: {
    flex: 1,
    backgroundColor: '#fff',
  },
  scrollContainer: {
    flex: 1,
  },
  container: {
    position: 'absolute',
  },
  bubbleWrapper: {
    position: 'absolute',
    width: BUBBLE_SIZE + BUBBLE_MARGIN * 2,
    height: BUBBLE_SIZE + BUBBLE_MARGIN * 2,
    justifyContent: 'center',
    alignItems: 'center',
  },
  bubble: {
    width: BUBBLE_SIZE,
    height: BUBBLE_SIZE,
    borderRadius: BUBBLE_SIZE / 2,
    backgroundColor: '#D3D3D3',
    justifyContent: 'center',
    alignItems: 'center',
    shadowColor: '#000',
    shadowOpacity: 0.2,
    shadowRadius: 5,
    shadowOffset: {width: 2, height: 2},
  },
  selectedBubble: {
    backgroundColor: '#87CEEB',
  },
  label: {
    color: '#000',
    fontSize: 14,
    fontWeight: 'bold',
  },
});

export default BubbleEffect;

How do I add multiple eventListener scripts that don’t listen to each other?

I am developing a rubric as a sort of pet project. The rubric contains two sections – one called
“Report Admin” and one called “Report Text”. Each portion has three sub-sections with radio buttons, and each sub-section’s radio buttons have values of “1”, “0.5”, and “0”. I have an eventListener script written in that counts the values of the radio buttons for the “Report Admin” section and displays the cumulative value:

        adminButtons.addEventListener('change', () => {
  adminResult.textContent = [...document.querySelectorAll('input[type=radio]:checked')]
    .reduce(
      (acc, val) => acc + Number(val.value)
      , 0
    )
  }
)

However, when I add in the second section, “Report Text,” and alter the script accordingly (or so I thought), the eventListener scripts seem to listen to each other and, despite being separate portions, and the displayed cumulative value is for all radio buttons clicked on the whole page, and not just a section.

In the picture, you can see that when clicking the green buttons, the grade for the first section is 2, but the grade for the second section is 4. I’m trying to figure out how to display them both as 2.

This is the code used for the Report Text section:

        textButtons.addEventListener('change', () => {
  textResult.textContent = [...document.querySelectorAll('input[type=radio]:checked')]
    .reduce(
      (acc, val) => acc + Number(val.value)
      , 0
    )
  }
)

I was hoping someone might know how to help me rectify this issue.

The “Report Admin” radio buttons are all within the same div, <div id="adminButtons"></div> and the “Report Text” radio buttons are all within the same div, <div id="textButtons"></div>. I cannot figure out how to get them to stop adding together.

Datepicker won’t show the picked date [React Native]

Why the datepicker on JobSearch is show current date not the date i picked on welcome page? i did the useLocalSearchParams and try to pass the picked date from Welcome to jobSearch, im begginner please help me (most of the code i got from chatgpt)

Welcome.js

import React, { useState } from "react";
import {
  View,
  Text,
  TouchableOpacity,
  Image,
  FlatList,
  Platform,
} from "react-native";
import DateTimePicker from "@react-native-community/datetimepicker";
import { useRouter } from "expo-router";

import styles from "./welcome.style";
import { icons, SIZES } from "../../../constants";

const jobTypes = ["Single bed", "Double bed", "Queen bed"];

const Welcome = ({ searchTerm, setSearchTerm, handleClick }) => {
  const router = useRouter();
  const [activeJobType, setActiveJobType] = useState("Single bed");
  const [date, setDate] = useState(new Date());
  const [showDatePicker, setShowDatePicker] = useState(false);

  const onChange = (event, selectedDate) => {
    if (event.type === "set") {
      const currentDate = selectedDate || date;
      setShowDatePicker(false);
      setDate(currentDate);
      setSearchTerm(currentDate.toDateString());
    } else {
      setShowDatePicker(false);
    }
  };

  const showDatepicker = () => {
    setShowDatePicker(true);
  };

  return (
    <View>
      <View style={styles.container}>
        <Text style={styles.userName}>Hello Rey</Text>
        <Text style={styles.welcomeMessage}>
          Enjoy your holiday with wikuhotel!
        </Text>
      </View>

      <View style={styles.searchContainer}>
        <View style={styles.searchWrapper}>
          <TouchableOpacity onPress={showDatepicker} style={styles.searchInput}>
            <Text style={styles.dateText}>
              {date ? date.toDateString() : "Select a date"}
            </Text>
          </TouchableOpacity>
        </View>

        <TouchableOpacity style={styles.searchBtn} onPress={handleClick}>
          <Image
            source={icons.search}
            resizeMode="contain"
            style={styles.searchBtnImage}
          />
        </TouchableOpacity>

        {showDatePicker && (
          <DateTimePicker
            value={date}
            mode="date"
            display={Platform.OS === "ios" ? "spinner" : "default"}
            onChange={onChange}
            maximumDate={new Date(2100, 0, 1)}
            minimumDate={new Date()}
          />
        )}
      </View>

      <View style={styles.tabsContainer}>
        <FlatList
          data={jobTypes}
          renderItem={({ item }) => (
            <TouchableOpacity
              style={styles.tab(activeJobType, item)}
              onPress={() => {
                setActiveJobType(item);
                const selectedDate = date.toISOString();
                router.push(`/search/${item}?selectedDate=${selectedDate}`);
              }}
            >
              <Text style={styles.tabText(activeJobType, item)}>{item}</Text>
            </TouchableOpacity>
          )}
          keyExtractor={(item) => item}
          contentContainerStyle={{ columnGap: SIZES.small }}
          horizontal
        />
      </View>
    </View>
  );
};

export default Welcome;

[id].js (jobSearch)

import React, { useEffect, useState } from "react";
import {
  ActivityIndicator,
  FlatList,
  Image,
  TouchableOpacity,
  View,
  Platform,
} from "react-native";
import { Stack, useRouter, useLocalSearchParams } from "expo-router";
import { Text, SafeAreaView } from "react-native";
import DateTimePicker from "@react-native-community/datetimepicker";
import axios from "axios";

import { ScreenHeaderBtn, NearbyJobCard } from "../../components";
import { COLORS, icons, SIZES } from "../../constants";
import styles from "../../styles/search";

const JobSearch = () => {
  const router = useRouter();
  const { selectedDate, jobType } = useLocalSearchParams();

  const [date, setDate] = useState(
    selectedDate ? new Date(selectedDate) : new Date()
  );
  const [searchResult, setSearchResult] = useState([]);
  const [searchLoader, setSearchLoader] = useState(false);
  const [searchError, setSearchError] = useState(null);
  const [page, setPage] = useState(1);
  const [showDatePicker, setShowDatePicker] = useState(false);

  const handleSearch = async () => {
    setSearchLoader(true);
    setSearchResult([]);
    setSearchError(null);

    try {
      const formattedDate = date.toISOString().split("T")[0];
      const options = {
        method: "GET",
        url: "https://jsearch.p.rapidapi.com/search",
        headers: {
          "x-rapidapi-key":
            "0c5bd52945msh21fcc24b54ab482p1060bejsn1483b70ea4ac",
          "x-rapidapi-host": "jsearch.p.rapidapi.com",
        },
        params: {
          query: jobType || "hotel",
          date: formattedDate,
          page: page.toString(),
        },
      };

      const response = await axios.request(options);
      setSearchResult(response.data.data);
    } catch (error) {
      setSearchError(error);
      console.log(error);
    } finally {
      setSearchLoader(false);
    }
  };

  const handlePagination = (direction) => {
    if (direction === "left" && page > 1) {
      setPage(page - 1);
    } else if (direction === "right") {
      setPage(page + 1);
    }
  };

  const onChange = (event, selectedDate) => {
    if (event.type === "set") {
      const currentDate = selectedDate || date;
      setShowDatePicker(Platform.OS === "ios");
      setDate(currentDate);
    } else {
      setShowDatePicker(false);
    }
  };

  useEffect(() => {
    handleSearch();
  }, [date, page, jobType]);

  return (
    <SafeAreaView style={{ flex: 1, backgroundColor: COLORS.lightWhite }}>
      <Stack.Screen
        options={{
          headerStyle: { backgroundColor: COLORS.lightWhite },
          headerShadowVisible: false,
          headerLeft: () => (
            <ScreenHeaderBtn
              iconUrl={icons.left}
              dimension="60%"
              handlePress={() => router.back()}
            />
          ),
          headerTitle: "",
        }}
      />

      <View style={styles.container}>
        <Text style={styles.searchTitle}>All rooms available for:</Text>
        <TouchableOpacity
          onPress={() => setShowDatePicker(true)}
          style={styles.datePickerButton}
        >
          <Text style={styles.datePickerText}>
            {date ? date.toDateString() : "Select a date"}
          </Text>
        </TouchableOpacity>
      </View>

      {showDatePicker && (
        <DateTimePicker
          value={date}
          mode="date"
          display="default"
          onChange={onChange}
          maximumDate={new Date(2100, 0, 1)}
          minimumDate={new Date()}
        />
      )}

      <FlatList
        data={searchResult}
        renderItem={({ item }) => (
          <NearbyJobCard
            job={item}
            handleNavigate={() => router.push(`/job-details/${item.job_id}`)}
          />
        )}
        keyExtractor={(item) => item.job_id}
        contentContainerStyle={{ padding: SIZES.medium, rowGap: SIZES.medium }}
        ListHeaderComponent={() => (
          <>
            <View style={styles.container}>
              <Text style={styles.noOfSearchedJobs}>
                {searchResult.length > 0
                  ? `${
                      searchResult.length
                    } Job Opportunities for ${date.toDateString()}`
                  : "No job opportunities found"}
              </Text>
            </View>
            <View style={styles.loaderContainer}>
              {searchLoader ? (
                <ActivityIndicator size="large" color={COLORS.primary} />
              ) : (
                searchError && (
                  <Text>Oops, something went wrong. Please try again.</Text>
                )
              )}
            </View>
          </>
        )}
        ListFooterComponent={() => (
          <View style={styles.footerContainer}>
            <TouchableOpacity
              style={styles.paginationButton}
              onPress={() => handlePagination("left")}
            >
              <Image
                source={icons.chevronLeft}
                style={styles.paginationImage}
                resizeMode="contain"
              />
            </TouchableOpacity>
            <View style={styles.paginationTextBox}>
              <Text style={styles.paginationText}>{page}</Text>
            </View>
            <TouchableOpacity
              style={styles.paginationButton}
              onPress={() => handlePagination("right")}
            >
              <Image
                source={icons.chevronRight}
                style={styles.paginationImage}
                resizeMode="contain"
              />
            </TouchableOpacity>
          </View>
        )}
      />
    </SafeAreaView>
  );
};

export default JobSearch;

where should i change the code?

Why is the chat window opening before I click open chat?

So, initally, I had no issues. It seemed to be working fine. I load the script and it was giving just the ‘open chat’ option.

Now, it loads the whole chat window already open, but not the content page of the chat. I have to click ‘exit chat’ and ‘open chat’ for it to actually open up the content.

I don’t want the content window to even open until I click ‘open chat’.

(for the sake of ease, I left the css as a style tag in the webpage for now. Typically, I have a stylesheet href in the head)

HTML

<!DOCTYPE html>
    <html lang="en">
    <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
   <style>
#forumWindow {
    padding: 10px;
    height: 350px;
    width: 250px;
    opacity:0.9;
    filter:alpha(opacity=50);
    -moz-border-radius: 20px 20px 20px 20px; 
    -webkit-border-radius: 20px 20px 20px 20px;
    border: 1px #000 dotted
    z-index: 1000;
        }
    .button { background:url('http://www.haikimura.com/images/b.png');
    -moz-border-radius: 1em;
    -webkit-border-radius: 1em; 
    z-index:4; 
    border-radius: 1em; 
    border:1px dotted #FFF0F5; 
    font-family: Garamond; 
    color: #fff; 
    font-size: 15px; }

     .box { 
    position: fixed; 
    bottom: 5px; 
    right: 40px; 
    width: 250px; 
    padding: 0px; 
    text-align: center; 
    cursor: default;
    background:url('http://www.haikimura.com/images/b.png') repeat; opacity:.80; 
    border:1px dotted #FFF0F5; 
    color: #fff; 
    font-family: Garamond; 
    font-size: 15px; 
    border-radius:5px; }

    .prettyFrame { 
    width: 100%; 
    height: 90%; 
    -moz-border-radius: 20px 20px 20px 20px; 
    -webkit-border-radius: 20px 20px 20px 20px; 
    border: none; }
</style>
    </head>
    <body>

    <button id="openForumBtn" class="box">open chat</button>
   
   
    <div id="overlay"></div>
    <div id="forumWindow" class="box">
    <iframe id="forumFrame" src="" class="prettyFrame"></iframe>
    <button id="closeForumBtn" class="button">exit chat</button>
    </div> 

<script>
document.getElementById('openForumBtn').addEventListener('click', function() {
    document.getElementById('forumWindow').style.display = 'block';
    document.getElementById('forumFrame').src = 'https://www.haikimura.com/'; // Replace with your forum URL
    });

    document.getElementById('closeForumBtn').addEventListener('click', function () {
    document.getElementById('forumWindow').style.display = 'none';
    document.getElementById('forumFrame').src = '';
    }); 
</script>

    </body>
    </html>

css

#forumWindow {
    padding: 10px;
    height: 350px;
    width: 250px;
    opacity:0.9;
    filter:alpha(opacity=50);
    -moz-border-radius: 20px 20px 20px 20px; 
    -webkit-border-radius: 20px 20px 20px 20px;
    border: 1px #000 dotted
    z-index: 1000;
        }
    .button { background:url('http://www.haikimura.com/images/b.png');
    -moz-border-radius: 1em;
    -webkit-border-radius: 1em; 
    z-index:4; 
    border-radius: 1em; 
    border:1px dotted #FFF0F5; 
    font-family: Garamond; 
    color: #fff; 
    font-size: 15px; }

     .box { 
    position: fixed; 
    bottom: 5px; 
    right: 40px; 
    width: 250px; 
    padding: 0px; 
    text-align: center; 
    cursor: default;
    background:url('http://www.haikimura.com/images/b.png') repeat; opacity:.80; 
    border:1px dotted #FFF0F5; 
    color: #fff; 
    font-family: Garamond; 
    font-size: 15px; 
    border-radius:5px; }

    .prettyFrame { 
    width: 100%; 
    height: 90%; 
    -moz-border-radius: 20px 20px 20px 20px; 
    -webkit-border-radius: 20px 20px 20px 20px; 
    border: none; }

js

document.getElementById('openForumBtn').addEventListener('click', function() {
    document.getElementById('forumWindow').style.display = 'block';
    document.getElementById('forumFrame').src = 'https://www.haikimura.com/'; 
    });

    document.getElementById('closeForumBtn').addEventListener('click', function () {
    document.getElementById('forumWindow').style.display = 'none';
    document.getElementById('forumFrame').src = '';
    }); 

How to emit on-change event for the e8-form-table-column with a lookup in the column template with Every8.Cloud framework?

I am using Every8.cloud framework. Grid column with lookup.

<e8-form-table
    class="my-3"
    internal-id="tscSalesInvoicesItems"
    v-model="object.tscSalesInvoicesItems"
    :deletion-confirmation="true"
    :label="false"
    on-row-change="onChangeItemRow"
    on-row-add="onAddRow"
    on-row-delete="onChangeRow"
    on-change="onChangeRow">
    <e8-form-table-column
       internal-id="uom"
       width="100px"
       min-width="100px"
       max-width="100px"
       label-horizontal-alignment="center">
    </e8-form-table-column>
</e8-form-table>

In this option onChangeRow event works fine. If I’am using template:

<e8-form-table-column
    internal-id="uom"
    width="100px"
    min-width="100px"
    max-width="100px"
    label-horizontal-alignment="center">
    <template #default="{ row, index }">
        <e8-form-lookup
            v-model="row.uom"
            :lazy-sync="false"
            :label="false"
            source="lstUnitsOfMeasure"
            placeholder="UOM">
        </e8-form-lookup>
    </template>
</e8-form-table-column>

On-change event for lookup doesn’t emit onChangeRow event for the <e8-form-table>.

How to emit on-change event for the <e8-form-table> with a value row.uom from <e8-form-lookup>?

Accessing a user file through an injected script (Safari App Extension macOS)

I am trying to use an otf file that the user selects from their computer in an injected script from my Safari App Extension (macOS), but currently, I can not get Safari to load the file due to permission issues.

This is what I am currently doing:

First I allow the user to select a file (code below):

var newFileURL = "";
let openPanel = NSOpenPanel()
openPanel.allowedFileTypes = ["otf"]
openPanel.allowsMultipleSelection = false
openPanel.canChooseDirectories = false
openPanel.canChooseFiles = true
openPanel.runModal()
        
newFileURL = openPanel.url?.absoluteString ?? ""
NSLog(newFileURL)

Next I send the variable ‘newFileUrl’ as a message to the default script.js and I handle the message with this javascript code:

function handleMessage(event) {
var css = "@font-face {font-family: userFont; src: url('file:///" + event.name + "');}",
head = document.head || document.getElementsByTagName('head')[0],
style = document.createElement('style');

    console.log(event.name);
    
    head.appendChild(style);
    
    style.type = 'text/css';
    if (style.styleSheet){
        style.styleSheet.cssText = css;
    } else {
        style.appendChild(document.createTextNode(css));
    }

}

When I do this I get the an error in the safari developer console, “Not allowed to load local resource: file:///Users/macuser/Downloads/file.otf”

I have allowed read/write permissions for user files for my extension so I am not sure how I would go about allowing user files for the injected script.

Thanks!

“Clear” button of input type=date, datepicker

I need to control the behavior and properties of the “Clear” button that appears in the bottom of the datepicker of the input type=date, for example, if I want to change the word “Clear” for “Delete” or if I want it to show a message every time I execute the clear button.

Does anybody lnows how to do something like this?

enter image description here

I cant find documentation about how to do it.