designing to overcome 3rd party API rate limits in firebase functions

My product tracks the user’s stock portfolio. I have created a Firebase function that is called every 24 hours. This function goes over the user’s stock data and makes a call to a 3rd party API to get the latest stock prices etc. My code used to work fine until recently when the API vendor imposed a max 5 calls per minute rate limit. Now, with the current code, I start getting failures after the first 5 calls. How do I re-architect my code to add this wait after every 5 calls I make? Below is what the code looks like

exports.dailyNetworth = onSchedule("every 24 hours", async (event:any) => {
  ...

   for(let uid of validUsers.keys()){
      const finColRef = db.collection("users/" + uid + "/finance")
      const fQuerySnapshot = await finColRef.orderBy("Date",'desc').limit(1).get()
      fQuerySnapshot.forEach(async(doc:any) => {
        let data:any 
        data = doc.data()        
          const finance = new Finance()
          finance.assets = data.assets
               
          for(let ast of Object.keys(finance.assets['Stocks'])){
              const asset:Stock = finance.assets['Stocks'][ast]
              let lastUpdateDate = asset.lastUpdateDate.seconds

              const stkPrice:any = await findStockPrice(asset.Name, asset["Share Count"])
              if(stkPrice !== null){
                finance.assets[astClass][ast].value = stkPrice
                finance.assets[astClass][ast].lastUpdateDate = Timestamp.now()
              }
          }
      });
   }
});

The third-party API call method is like the below:

async function findStockPrice(symbol:string, quantity:number){
    let value = null
    try {
      const url = 'https://someurl.com?' + symbol
      const  header = {
        ...
      }

      const response = await fetch(url, {headers: header})
      const json = await response.json()

      const rate = json["rate"]
      value = quantity*rate

    } catch (error:any) {
      logger.error(error);
    }

  return value
}

Give buttons an active styling for change content javascript

I am creating a section in Elementor that has five buttons. Each one changes the content inside an adjacent section using Javascript. The functioning code is:

var $ = jQuery

$(document).ready(function() {
  $('[data-showme]').on('click', function() {
    var showme = $(this).attr('data-showme')

    $('.all-content').hide()
    $('#' + showme).show()
  })
})
.all-content {
  display: none;
}

#professionals-content {
  display: block;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.6.0/jquery.min.js"></script>

There is also an attribute that doesn’t seem to fit into this forum’s code structure:

data-showme|ID-NAME-content

The attribute tells the content which button to respond to. I already have css code working to tell the button how to react to hover, but I couldn’t get any css to make the button that is currently active to remain in a style, until another one is clicked and that one gains the active style. I know it likely requires javascript so I came here. Something like what the hover code has:

selector a:hover {
  border-width: 0px 0px 0px 3px;
}

How can I make this work?

Why can’t I access my backend file that is used for routing(app.php)?

I am making a chat application using PHP on backend and React.js on frontend. However, I am encountering a problem with fetch API.

My fetch that calls an endpoint

    fetch('http://localhost/registration-processing',{
                    method:'POST',
                    headers:{
                        'Content-Type':'application/json'
                    },
                    body:JSON.stringify({
                        firstName:firstName,
                        lastName:lastName,
                        username:username,
                        email:email,
                        password:password
                    })
                })
                .then((response)=>{
                    if(response.ok){ 
                        return response.json();
                    }
                })
                .then((data)=>{
                    console.log('Response:'+data);
                })
                .catch((err)=>{
                    console.error('Error is this:'+err);
                })

I didn’t explicitly write the port in ‘localhost’ since Apache runs on port 80.Fetch worked absolutely fine before installing React, where I used plain JS. Now I get message: Access to fetch at 'http://localhost/registration-processing' from origin 'http://localhost:3000' has been blocked by CORS policy: Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. If an opaque response serves your needs, set the request's mode to 'no-cors' to fetch the resource with CORS disabled.

But I tried with using mode:'no-cors' in fetch, and I get successful response(.then is executed, not catch) but result is Response:undefined,even after I tried with echo json_encode(object) to return value , because I think that app.php is unreachable, which is made for handling all routes and fetch requests.

I tried with configuring .htaccess like this, but no results

RewriteEngine On
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ /back-end/app.php [QSA,L]

This is my project structure:

├── back-end/
│   ├── app.php
│   └── .htaccess
├── front-end/
│   ├── react-environment(where fetch is)

So, I don’t know what could be a problem and how to fix this. Also one note: There are not problems when I use php -S localhost:8000, but errors keep showing when I use Apache server via Laragon.

Paragraph Not Appearing in DOCX.js When Using Conditional Statement

I am using DOCX.js to generate a word document which has an address section. I am trying to create a paragraph that always shows the first paragraph with the text “Address” and then insert a paragraph below to build the address. I am using a conditional statement to enter a new paragraph if a PO Box is supplied or skip if no PO Box is supplied. Here is the code:

//...Other Paragraphs
new TableRow({
        children: [
          new TableCell({
            children: [
              new Paragraph({
                spacing: {
                  before: 200,
                },
                children: [
                  new TextRun({
                    text: "Address",
                    size: 24,
                    color: "848484",
                  }),
                ],
              }),
              element["BO/RLE/DBO/CT PO Box"] &&
                new Paragraph({
                  children: [
                    new TextRun({
                      text: element["BO/RLE/DBO/CT PO Box"],
                      size: 24,
                      color: "000000",
                    }),
                  ],
                }),
                ],
              }),
            ],
          }),
//...Other Paragraphs

The problem I’m having is that the Paragraph with the “Address” TextRun is also acting conditionally, even though it’s outside of the conditional statement below it (i.e. If there is no PO Box, the “Address” paragraph also doesn’t appear.)

Is there anything wrong with my code?

TIA

Order of adding ice-candidates to webrtc-connection

MDN documentation for RTCPeerConnection.addIceCandidate() says that it returns a promise.
So an ice-candidate will likely is being processed yet when this methods returns.
I get several ice-candidates from a remote and do not wait – whether addIceCandidate() gets resolved or not.

The question is: should I necessarily wait when the previous promise p = addIceCandidate will be rejected or resolved ? Or it doesn’t matter ?

Precondition Failed (412) error when using Phone pe Web API for getting transaction list

I am using the POST method on a Phone Pe Web API endpoint to get a list of transactions. I include cookies for authorization, but I’m encountering a 412 Precondition Failed error after a few hours. Specifically, the cookies _X52F70K3N and _CKB2N1BHVZ refresh every time, and I’m able to get the transaction list for about 2-4 hours. After that, the cookies expire, and I have to manually refresh them.

Here is the code I am using to make the request, along with the response I’m getting:

Code I am using:

   
const axios = require('axios');
const mysql = require('mysql2');
require('dotenv').config(); // Load environment variables from .env file

   
const dbConfig = {
    host: process.env.DB_HOST,
    user: process.env.DB_USER,
    password: process.env.DB_PASSWORD,
    database: process.env.DB_NAME
};

// PhonePe API URL and headers
const PHONEPE_API_URL = 'https://web-api.phonepe.com/apis/mi-web/v3/transactions/list';
const headers = {
    'Host': 'web-api.phonepe.com',
    'Content-Type': 'application/json',
    'Accept': 'application/json, text/plain, */*',
    'X-Csrf-Token': 'X7M1l************YQhI', // Use the actual CSRF token here
    'X-App-Id': 'oculus',
    'X-Source-Type': 'WEB',
    'User-Agent': 'Mozilla/5.0',
    'Namespace': 'insights',
    'X-Source-Platform': 'WEB',
    'Sec-Fetch-Site': 'same-site',
    'Sec-Fetch-Mode': 'cors',
    'Sec-Fetch-Dest': 'empty',
    'Referer': 'https://business.phonepe.com/',
    'Accept-Encoding': 'gzip, deflate, br',
    'Priority': 'u=4, i',
    'Cookie': '_ppabwduref=PA***********00; MERCHANT_USER_A_TOKEN=eyJ0*************9.ey*****************************************bSgo***M-y90******wg; MERCHANT_USER_R_TOKEN=f8*******-1****-4****-a***3-6*****3; _ppabwdcid=ZX***********FRR*********************WV************************zhFVU********==; _ppabwdsid=*****-***-****-***-****; _CKB2N1BHVZ=1s**************************xKStt/S*****kQ; _X52F70K3N=X7M*******QhI' // Replace with actual cookies // Replace with actual cookies
};

// Calculate timestamps for "from" and "to"
const now = Date.now();
const fromTimestamp = now - 24 * 60 * 60 * 1000; // 24 hours before now
const toTimestamp = now;

// API request payload
const requestData = {
    offset: 0,
    size: 10,
    filters: {},
    transactionType: "FORWARD",
    from: fromTimestamp,
    to: toTimestamp,
    selectedDateType: "today"
};

// Function to fetch transaction data from PhonePe API
async function fetchTransactionData() {
    try {
        const response = await axios.post(PHONEPE_API_URL, requestData, { headers });
        if (response.data.success) {
            console.log("Transaction Data Retrieved Successfully");
            return response.data.data.results || []; // Extract results from the response
        } else {
            console.error("Failed to fetch transaction data.");
            return [];
        }
    } catch (error) {
        console.error("Error during API request:", error);
        return [];
    }
}

// Function to check if a transaction ID exists in the database
async function transactionExists(transactionId, connection) {
    const query = 'SELECT COUNT(*) AS count FROM transactions WHERE transaction_id = ?';
    try {
        const [rows] = await connection.promise().query(query, [transactionId]);
        return rows[0].count > 0;
    } catch (error) {
        console.error('Error checking transaction existence:', error);
        return false;
    }
}

// Function to insert transaction data into the database
async function insertTransaction(transaction, connection) {
    const transactionData = {
        transaction_id: transaction.transactionId,
        transaction_type: transaction.transactionType,
        payment_state: transaction.paymentState,
        amount: transaction.amount / 100, // Convert paise to rupees
        merchant_transaction_id: transaction.merchantTransactionId,
        instrument_type: transaction.instrumentDetails[0]?.instrumentType || null,
        vpa: transaction.instrumentDetails[0]?.vpa || null,
        transaction_note: transaction.transactionNote,
        transaction_date: new Date(transaction.transactionDate).toISOString(),
        customer_name: transaction.customerDetails?.userName || null,
        payment_app: transaction.paymentApp?.paymentApp || null,
        settlement_status: transaction.settlement?.status || null,
        settlement_text: transaction.settlement?.settlementText || null,
        utr: transaction.utr || null
    };

    const query = `
        INSERT INTO transactions (transaction_id, transaction_type, payment_state, amount, 
            merchant_transaction_id, instrument_type, vpa, transaction_note, transaction_date, 
            customer_name, payment_app, settlement_status, settlement_text, utr)
        VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?);
    `;

    try {
        await connection.promise().query(query, [
            transactionData.transaction_id,
            transactionData.transaction_type,
            transactionData.payment_state,
            transactionData.amount,
            transactionData.merchant_transaction_id,
            transactionData.instrument_type,
            transactionData.vpa,
            transactionData.transaction_note,
            transactionData.transaction_date,
            transactionData.customer_name,
            transactionData.payment_app,
            transactionData.settlement_status,
            transactionData.settlement_text,
            transactionData.utr
        ]);
        console.log(`Transaction ID ${transactionData.transaction_id} inserted successfully.`);
    } catch (error) {
        console.error(`Error inserting transaction ID ${transactionData.transaction_id}:`, error);
    }
}

// Main function to fetch and update transaction data
async function main() {
    const transactions = await fetchTransactionData();
    if (!transactions.length) {
        console.log("No transactions found.");
        return;
    }

    const connection = mysql.createConnection(dbConfig);

    try {
        for (const transaction of transactions) {
            const exists = await transactionExists(transaction.transactionId, connection);
            if (exists) {
                console.log(`Transaction ID ${transaction.transactionId} already exists. Skipping.`);
            } else {
                await insertTransaction(transaction, connection);
            }
        }
    } catch (error) {
        console.error("Error processing transactions:", error);
    } finally {
        connection.end();
    }
}

main();

Response of 412 is

config: {
  transitional: [Object],
  adapter: [Array],
  transformRequest: [Array],
  transformResponse: [Array],
  timeout: 0,
  xsrfCookieName: 'XSRF-TOKEN',
  xsrfHeaderName: 'X-XSRF-TOKEN',
  maxContentLength: -1,
  maxBodyLength: -1,
  env: [Object],
  validateStatus: [Function: validateStatus],
  headers: [Object [AxiosHeaders]],
  method: 'post',
  url: 'https://web-api.phonepe.com/apis/mi-web/v3/transactions/list',
  data: '{"offset":0,"size":10,"filters":{},"transactionType":"FORWARD","from":1734776412434,"to":1734862812434,"selectedDateType":"today"}'
},
request: <ref *1> ClientRequest {
  _events: [Object: null prototype],
  _eventsCount: 7,
  _maxListeners: undefined,
  outputData: [],
  outputSize: 0,
  writable: true,
  destroyed: false,
  _last: true,
  chunkedEncoding: false,
  shouldKeepAlive: false,
  maxRequestsOnConnectionReached: false,
  _defaultKeepAlive: true,
  useChunkedEncodingByDefault: true,
  sendDate: false,
  _removedConnection: false,
  _removedContLen: false,
  _removedTE: false,
  strictContentLength: false,
  _contentLength: '130',
  _hasBody: true,
  _trailer: '',
  finished: true,
  _headerSent: true,
  _closed: false,
  socket: [TLSSocket],
  _header: 'POST /apis/mi-web/v3/transactions/list HTTP/1.1rn' +
    'Accept: application/json, text/plain, */*rn' +
    'Content-Type: application/jsonrn' +
    'Host: web-api.phonepe.comrn' +
    'X-Csrf-Token: X7M1l************YQhIrn' +
    'X-App-Id: oculusrn' +
    'X-Source-Type: WEBrn' +
    'User-Agent: Mozilla/5.0rn' +
    'Namespace: insightsrn' +
    'X-Source-Platform: WEBrn' +
    'Sec-Fetch-Site: same-sitern' +
    'Sec-Fetch-Mode: corsrn' +
    'Sec-Fetch-Dest: emptyrn' +
    'Referer: https://business.phonepe.com/rn' +
    'Accept-Encoding: gzip, deflate, brrn' +
    'Priority: u=4, irn' +
    'Cookie: __ppabwduref=PA***********00; MERCHANT_USER_A_TOKEN=eyJ0*************9.ey*****************************************bSgo***M-y90******wg; MERCHANT_USER_R_TOKEN=f8*******-1****-4****-a***3-6*****3; _ppabwdcid=ZX***********FRR*********************WV************************zhFVU********==; _ppabwdsid=*****-***-****-***-****; _CKB2N1BHVZ=1s**************************xKStt/S*****kQ; _X52F70K3N=X7M*******QhI
    'Content-Length: 130rn' +
    'Connection: closern' +
    'rn',
  _keepAliveTimeout: 0,
  _onPendingData: [Function: nop],
  agent: [Agent],
  socketPath: undefined,
  method: 'POST',
  maxHeaderSize: undefined,
  insecureHTTPParser: undefined,
  joinDuplicateHeaders: undefined,
  path: '/apis/mi-web/v3/transactions/list',
  _ended: true,
  res: [IncomingMessage],
  aborted: false,
  timeoutCb: null,
  upgradeOrConnect: false,
  parser: null,
  maxHeadersCount: null,
  reusedSocket: false,
  host: 'web-api.phonepe.com',
  protocol: 'https:',
  _redirectable: [Writable],
  [Symbol(kCapture)]: false,
  [Symbol(kBytesWritten)]: 0,
  [Symbol(kNeedDrain)]: false,
  [Symbol(corked)]: 0,
  [Symbol(kOutHeaders)]: [Object: null prototype],
  [Symbol(errored)]: null,
  [Symbol(kHighWaterMark)]: 16384,
  [Symbol(kRejectNonStandardBodyWrites)]: false,
  [Symbol(kUniqueHeaders)]: null
},
data: ''

},
status: 412
}

Getting this error in the below code >> Fatal error: Uncaught ArgumentCountError:

Getting this error in the below code

Warning: Cannot modify header information – headers already sent by (output started at /home/kvkprose/public_html/index.php:48) in /home/kvkprose/public_html/header.php on line 15

Warning: Cannot modify header information – headers already sent by (output started at /home/kvkprose/public_html/index.php:48) in /home/kvkprose/public_html/header.php on line 16

Warning: Cannot modify header information – headers already sent by (output started at /home/kvkprose/public_html/index.php:48) in /home/kvkprose/public_html/header.php on line 17

Warning: Cannot modify header information – headers already sent by (output started at /home/kvkprose/public_html/index.php:48) in /home/kvkprose/public_html/header.php on line 18

$connection = new mysqli("127.0.0.1","*******","******","*********");

if ($connection->connect_error) 
{
    die("Connection failed: " . $connection->connect_error);
}

function exe_query($conn, $query) 
{ 
  $result = mysqli_query($conn, $query); 

  if (!$result) 
  { 
    die("Query failed: " . mysqli_error($conn)); 
  }

  $rows = [];
  while ($row = mysqli_fetch_assoc($result))
  { 
    $rows[] = $row; 
  }

  return $rows; 
}


$company_data=exe_query($connection, "select * from tbl_admin");
 $contact_data=exe_query($connection,"select * from tbl_contact");
 $about_data=exe_query($connection,"select * from tbl_about where id=3");
 $social_data=exe_query($connection,"select * from tbl_social where id=1");
        $full_name  = $_SERVER['PHP_SELF'];
        $name_array = explode('/',$full_name);
        $count      = count($name_array);
        $page_name  = $name_array[$count-1];


        header("Strict-Transport-Security:max-age=63072000");
        header("X-XSS-Protection: 1; mode=block");
        header('X-Content-Type-Options: nosniff');
        header("Expect-CT: enforce; max-age=30; report-uri='**********");

        $mypage=explode(".",$page_name);

how a class call with this format? [duplicate]

hi I clone a php library from github but xampp get this error:
Fatal error: Uncaught Error: Class “PHPUnitFrameworkTestCase” not found in C:xampphtdocsbrowsertestsBrowserTest.php:12 Stack trace: #0 {main} thrown in C:xampphtdocsbrowsertestsBrowserTest.php on line 12

I not exper but someone can help to me

loading content thru PHP to simplify or is there a way to load it in a div? [closed]

I have my header/css and part of the body including the <div tag for the cell the content is in as one file and then the footer with the and and segment as another file and then have the following code:
How do I make it so it will load content per a PHP page with the content in it… it’s all supposed to go in one div on the page and then the header and footer (the parts of the page that stay the same which is everything except that div) saved as separate files to minimize loading time…

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

$PageTitle="Havent // Heavent Made // Hellt Mindedt // Manualt";

function customPageHeader(){?>
  <!--Arbitrary HTML Tags-->
<?php }

include_once('file:///home/chronos/u-57608984ca0e6dfae1c20c6c0ed79e71e9626f08/MyFiles/Downloads/header.php');

include_once("content");

include_once('file:///home/chronos/u-57608984ca0e6dfae1c20c6c0ed79e71e9626f08/MyFiles/Downloads/footer.php');
?>

How do I use php to add custom properties to docx

I am trying to prepare a docx template file with pre-inserted custom properties (Quick Part -> Fields) from a database. The file starts as an empty docx file created in MS Word.

I reviewed this question

But it does not answer my question.

My code is as follows:

$blank_DOCX_template = DATAPATH."formletter_blank_templates/blank.docx";
        
$zip = new ZipArchive;

if ($zip->open($blank_DOCX_template) == true) {
    $updatedXmlContent = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<Properties xmlns="http://schemas.openxmlformats.org/officeDocument/2006/custom-properties" xmlns:vt="http://schemas.openxmlformats.org/officeDocument/2006/docPropsVTypes">';
    $id_num = 2;
    foreach($properties as $prop_name=>$prop_val){
         $updatedXmlContent .= '<property fmtid="{D5CDD505-2E9C-101B-9397-08002B2CF9AE}" pid="'.$id_num.'" name="'.$prop_name.'"><vt:lpwstr>'.$prop_val.'</vt:lpwstr></property>';
         $id_num++;
    }
    $updatedXmlContent .= '</Properties>';

    $zip->addFromString('docProps/custom.xml', $updatedXmlContent);
    $zip->close();

}else{
    echo "could not open file";
}

Once the code has run (it produces no errors), I can look into the zip file ‘docProps/custom.xml’ and every thing looks right. The xml file exists and has the same content as a file with the same properties manually inserted through the MS Word interface, but when I open the file in MS Word the custom properties inserted from my php code do not appear.

What am I missing?

how to store docx files to cloudinary with multer

i am trying to upload docx file to cloudinary with multer-storage-cloudinary.

here is the code i use

const storage = new CloudinaryStorage({
    cloudinary,
    params: async (req, file) => ({
        folder: 'client-documents',
        resource_type: 'raw',
        allowed_formats: ['jpg', 'jpeg', 'png', 'pdf', 'doc', 'docx'],
        public_id: `${Date.now()}`
    })
});

const upload = multer({
    storage,
    limits: { fileSize: 10 * 1024 * 1024 } // 10 MB limit
});

but when try with postman i get error

{
    "status": "error",
    "error": {
        "message": "An unknown file format not allowed",
        "name": "Error",
        "http_code": 400,
        "storageErrors": [],
        "statusCode": 500,
        "status": "error"
    },
    "message": "An unknown file format not allowed"
}

and see below my preset:
preset screenshot

what should i do? thanks

Javascript shipping countdown timer not working

I am having a issue with some javascript code on a category page, here is an example of such a page. I am trying to add a javascript shipping countdown timer that says order within x amount of time and it will be delivered on this date. I found some code online but noticed it only worked on the first div element and not the others.

I looked it up on Google and found it was due to the script using byID and others said it was better to use class so I changed the code to use class and queryselector but now it’s not working. Below is the current code I have:

<div class="delivery-text">Super fast delivery available</div>
function isHoliday(date) {
    const holidays = [
        new Date(2024, 0, 1), // New Year's Day
            new Date(2024, 3, 7), // Good Friday
            new Date(2024, 3, 10), // Easter Monday
            new Date(2024, 4, 1), // Early May Bank Holiday
            new Date(2024, 4, 8), // King Charles Coronation
            new Date(2024, 4, 29), // Spring Bank Holiday
            new Date(2024, 7, 28), // Summer Bank Holiday
            new Date(2024, 11, 25), // Christmas Day
            new Date(2024, 11, 26), // Boxing Day
    ];

    for (let i = 0; i < holidays.length; i++) {
        if (date.getFullYear() === holidays[i].getFullYear() &&
                date.getMonth() === holidays[i].getMonth() &&
                date.getDate() === holidays[i].getDate()) {
            return true;
        }
    }
    return false;
}

function isBusinessDay(date) {
    const dayOfWeek = date.getUTCDay();
    return dayOfWeek > 0 && dayOfWeek < 6;
}

function nextBusinessDay(date) {

    let nextDay = new Date(date);
    nextDay.setUTCDate(date.getUTCDate() + 1);

    while (!isBusinessDay(nextDay) || isHoliday(nextDay)) {
        nextDay.setUTCDate(nextDay.getUTCDate() + 1);
    }

    return nextDay;
}

function countdownToDispatch() {

    const now = new Date();
    let dispatchDate;

    //If it's before 11AM, a business day, and not a holiday, our dispatch date is today!

    if (now.getUTCHours() < 14 && isBusinessDay(now) && !isHoliday(now)) {
        dispatchDate = new Date(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate(), 14, 0, 0, 0);
    } else {
        dispatchDate = nextBusinessDay(now);
        dispatchDate.setUTCHours(14, 0, 0, 0);
    }

    const timeUntilDispatch = dispatchDate.getTime() - now.getTime();
    const days = Math.floor(timeUntilDispatch / (1000 * 60 * 60 * 24));
    const hours = Math.floor((timeUntilDispatch % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
    const minutes = Math.floor((timeUntilDispatch % (1000 * 60 * 60)) / (1000 * 60));
    const seconds = Math.floor((timeUntilDispatch % (1000 * 60)) / 1000);

    let timeString = '';

    if (days > 0) {
        timeString += days + ' days ';
    }
    if (hours > 0) {
        timeString += hours + ' hours ';
    }
    if (minutes > 0) {
        timeString += minutes + ' minutes ';
    }
    if (seconds > 0) {
        timeString += seconds + ' seconds ';
    }

    return timeString.trim();
}

function setOrderArrivalDate() {
    const now = new Date();

    let dispatchDate;
    if (now.getUTCHours() < 14 && isBusinessDay(now) && !isHoliday(now)) {
        dispatchDate = now;
    } else {
        dispatchDate = nextBusinessDay(now);
    }

    const arrivalDate = nextBusinessDay(dispatchDate);
    let result = countdownToDispatch();

    const formattedDate = arrivalDate.toLocaleDateString("en-GB", {
        weekday: 'long',
        day: 'numeric',
        month: 'long'
    });

    const finalArrivalDateString = formattedDate.split(' ')[0] + ' ' + ordinalSuffix(arrivalDate.getUTCDate()) + ' ' + formattedDate.split(' ')[2];
    //const deliveryText = document.getElementById('delivery-text');
    const deliveryText = document.getElementsByClassName("delivery-text");
    //const deliveryText = document.querySelectorAll('.delivery-text');
    deliveryText.textContent = 'Order within the next ' + result + ' to receive your order on ' + finalArrivalDateString;

}

function ordinalSuffix(i) {
    const j = i % 10, k = i % 100;
    if (j == 1 && k != 11) {
        return i + "st";
    }
    if (j == 2 && k != 12) {
        return i + "nd";
    }
    if (j == 3 && k != 13) {
        return i + "rd";
    }
    return i + "th";
}

setInterval(setOrderArrivalDate, 1000);

Scrolling Glitch

hi i am creating a website and i am experiencing a scrolling glinch. I cant seem to pin-point the problem and I was hoping someone could help me. Currently its taking me 2 scrolls to get to the bottom of the page when it should only take one smooth scroll. For some reason, it always stops at the “about me” section before I have to scroll again to reach the bottom. Same problem when I scroll from the bottom to the top. How do I solve this? What are some things I should look out for?

Website error video

Sperated Strings not picked up in javascript

I’m Using this code to merge two updates. Col c contains current status of a tenant in each cell its for a different tenant. My team ads in col F the update. At the moment code is running but no issues.

function mergeColumnsCAndF() {
const sheet = SpreadsheetApp.getActiveSpreadsheet().getActiveSheet();

// Get all data in columns C and F
const data = sheet.getRange(1, 3, sheet.getLastRow(), 2).getValues(); // Column C (Status) and Column F (Updates)

// Define categories
const categories = {
problems: “Problems”,
payments: “Payments”,
tenantCommitments: “Tenant Commitments”,
landlordObligations: “Landlord Obligations”,
ebm: “EBM Codes”
};

// Loop through each row and merge updates into the status
const mergedData = data.map(([status, update], index) => {
if (!update.trim()) return [status]; // No update, keep the original status

let updatedStatus = status || ""; // Start with existing status
let extractedCategories = {}; // Object to log categories and updates for debugging

// Normalize update string: remove extra spaces and standardize formatting
const normalizedUpdate = update.replace(/s+:s+/g, ":").trim();

// Split updates from F into individual categories
const updates = normalizedUpdate.split(/(?=b(?:Problems|Payments|Tenant Commitments|Landlord Obligations|EBM Codes):)/i);

// Helper function to update a specific category
function updateCategory(category, newInfo) {
  const regex = new RegExp(`${category}:.*?(\n|$)`, "gi"); // Match category in the existing status
  const cleanedStatus = updatedStatus.replace(regex, "").trim(); // Remove existing category info
  return `${cleanedStatus}n${category}: ${newInfo}`.trim(); // Append new category info
}

// Process each update and match it to its category
updates.forEach((item) => {
  const lowerItem = item.toLowerCase();
  if (lowerItem.includes("problems")) {
    updatedStatus = updateCategory(categories.problems, item.split(":")[1].trim());
    extractedCategories[categories.problems] = item.split(":")[1].trim(); // Log Problems
  } else if (lowerItem.includes("payments")) {
    updatedStatus = updateCategory(categories.payments, item.split(":")[1].trim());
    extractedCategories[categories.payments] = item.split(":")[1].trim(); // Log Payments
  } else if (lowerItem.includes("tenant commitments")) {
    updatedStatus = updateCategory(categories.tenantCommitments, item.split(":")[1].trim());
    extractedCategories[categories.tenantCommitments] = item.split(":")[1].trim(); // Log Tenant Commitments
  } else if (lowerItem.includes("landlord")) {
    updatedStatus = updateCategory(categories.landlordObligations, item.split(":")[1].trim());
    extractedCategories[categories.landlordObligations] = item.split(":")[1].trim(); // Log Landlord Obligations
  } else if (lowerItem.includes("ebm")) {
    updatedStatus = updateCategory(categories.ebm, item.split(":")[1].trim());
    extractedCategories[categories.ebm] = item.split(":")[1].trim(); // Log EBM Codes
  }
});

// Log the extracted categories for debugging
Logger.log(`Row ${index + 1}: Extracted Categories: ${JSON.stringify(extractedCategories)}`);

// Remove duplicate lines from the final status
const uniqueLines = [...new Set(updatedStatus.split("n"))].join("n").trim();

return [uniqueLines]; // Return the updated status

});

// Write merged data back into Column C
sheet.getRange(1, 3, mergedData.length, 1).setValues(mergedData);

// Clear all updates in Column F after processing
sheet.getRange(1, 6, data.length, 1).clearContent();

SpreadsheetApp.getUi().alert(“Columns C have been updated with merged statuses, and Column F has been cleared.”);
}