Convert list of numeric range into list of dates from one timezone to another timezone (Not sustem local timezone) using luxon

I am trying to create timeslot for my availability by providing each days working hours in numeric range. i provide some specific days availability in list of date range form. also providing blocked days as list of dates and booked date ranges as list of date ranges. i am also passing meeting duration, minute interval, month offset. and 2 time zones one is source time zone and another is target time zone. all i want to create a list of dates after removing all the booked datetimes and blocked days from source time zone to target time zone using luxon library.

like if i provide 540-720 as a numeric range with time interval of 30 minutes and duration of 30 mins with month offset as 0 (for current month) as a monday availability, it will return me list of dates for all the Monday of upcoming month like below

Jun 16, 2025 9:00 pm, Jun 16, 2025 9:30 pm, Jun 16, 2025 10:30 pm, Jun 16, 2025 11:00 pm,Jun 16, 2025 11:30 pm, Jun 16, 2025 12:00 pm.. here we dont have date Jun 16, 2025 10:00 pm as that time is booked.

In below code instance.publishState will return values from my bubble.io plugin to bubble.io app. Below code is converting my numeric ranges into list of dates when i provide Australia/Adelaide as timezone to my system’s current timezone but not to targetted timezone. you can see the actual working on calendly’s calendar link of event for the reference.

function(instance, properties, context) {
    if (null !== properties.sunday_pattern ? sunRanges = properties.sunday_pattern.get(0, properties.sunday_pattern.length()) : sunRanges = [],
    null !== properties.monday_pattern ? monRanges = properties.monday_pattern.get(0, properties.monday_pattern.length()) : monRanges = [],
    null !== properties.tuesday_pattern ? tueRanges = properties.tuesday_pattern.get(0, properties.tuesday_pattern.length()) : tueRanges = [],
    null !== properties.wednesday_pattern ? wedRanges = properties.wednesday_pattern.get(0, properties.wednesday_pattern.length()) : wedRanges = [],
    null !== properties.thursday_pattern ? thuRanges = properties.thursday_pattern.get(0, properties.thursday_pattern.length()) : thuRanges = [],
    null !== properties.friday_pattern ? friRanges = properties.friday_pattern.get(0, properties.friday_pattern.length()) : friRanges = [],
    null !== properties.saturday_pattern ? satRanges = properties.saturday_pattern.get(0, properties.saturday_pattern.length()) : satRanges = [],
    null !== properties.available_date_ranges ? availRanges = properties.available_date_ranges.get(0, properties.available_date_ranges.length()) : availRanges = [],
    null !== properties.blocked_date_ranges ? blockedDays = properties.blocked_date_ranges.get(0, properties.blocked_date_ranges.length()) : blockedDays = [],
    null !== properties.booked_date_ranges ? bookedDays = properties.booked_date_ranges.get(0, properties.booked_date_ranges.length()) : bookedDays = [],
    "Calendar Month" == properties.typeAvail)
        var localCalStart = luxon.DateTime.now().setZone(properties.time_zone).plus({
            months: properties.offset
        }), monthStart = localCalStart.startOf("month"), endMonthCal = localCalStart.endOf("month").plus({
            days: 1
        }), startFilter, setStart = startFilter = monthStart.minus({
            days: 1
        }), setEnd = endMonthCal;
    if ("Between These Dates" == properties.typeAvail)
        var setStart = luxon.DateTime.fromMillis(properties.set_start.getTime())
          , setEnd = luxon.DateTime.fromMillis(properties.set_end.getTime())
          , startFilter = setStart.setZone(properties.time_zone).minus({
            days: 1
        })
          , monthStart = setStart.setZone(properties.time_zone)
          , endMonthCal = setEnd.setZone(properties.time_zone).plus({
            days: 1
        });
    var sundayLuxonDates = [];
    if (sunRanges.length > 0)
        for (var sunday = monthStart.startOf("week").plus({
            days: 6
        }); sunday <= endMonthCal; )
            sunday >= startFilter && sundayLuxonDates.push(sunday),
            sunday = sunday.plus({
                weeks: 1
            });
    var mondayLuxonDates = [];
    if (monRanges.length > 0)
        for (var monday = monthStart.startOf("week"); monday <= endMonthCal; )
            monday >= startFilter && mondayLuxonDates.push(monday),
            monday = monday.plus({
                weeks: 1
            });
    var tuesdayLuxonDates = [];
    if (tueRanges.length > 0)
        for (var tuesday = monthStart.startOf("week").plus({
            days: 1
        }); tuesday <= endMonthCal; )
            tuesday >= startFilter && tuesdayLuxonDates.push(tuesday),
            tuesday = tuesday.plus({
                weeks: 1
            });
    var wednesdayLuxonDates = [];
    if (wedRanges.length > 0)
        for (var wednesday = monthStart.startOf("week").plus({
            days: 2
        }); wednesday <= endMonthCal; )
            wednesday >= startFilter && wednesdayLuxonDates.push(wednesday),
            wednesday = wednesday.plus({
                weeks: 1
            });
    var thursdayLuxonDates = [];
    if (thuRanges.length > 0)
        for (var thursday = monthStart.startOf("week").plus({
            days: 3
        }); thursday <= endMonthCal; )
            thursday >= monthStart && thursdayLuxonDates.push(thursday),
            thursday = thursday.plus({
                weeks: 1
            });
    var fridayLuxonDates = [];
    if (friRanges.length > 0)
        for (var friday = monthStart.startOf("week").plus({
            days: 4
        }); friday <= endMonthCal; )
            friday >= monthStart && fridayLuxonDates.push(friday),
            friday = friday.plus({
                weeks: 1
            });
    var saturdayLuxonDates = [];
    if (satRanges.length > 0)
        for (var saturday = monthStart.startOf("week").plus({
            days: 5
        }); saturday <= endMonthCal; )
            saturday >= monthStart && saturdayLuxonDates.push(saturday),
            saturday = saturday.plus({
                weeks: 1
            });
    var minuteInterval = properties.minute_interval;
    if (null == minuteInterval || minuteInterval <= 0)
        return console.log("Please enter a Minute Interval value. Must be a number value greater than zero"),
        !1;
    var availTimeSlots = []
      , blockedLuxonDates = []
      , bookingLength = properties.booking_duration;
    if (null == bookingLength || bookingLength <= 0)
        return console.log("Please enter a Booking Duration value. Must be a number value greater than zero"),
        !1;
    for (var x = 0; x < availRanges.length; x++) {
        blockedLuxonDates.push(luxon.DateTime.fromMillis(availRanges[x][0].getTime()).setZone(properties.time_zone));
        for (var i = 0; i <= availRanges[x][1].getTime() - availRanges[x][0].getTime() - 6e4 * bookingLength; i += 6e4 * minuteInterval)
            availTimeSlots.push(availRanges[x][0].getTime() + i)
    }
    for (var x = 0; x < blockedDays.length; x++)
        blockedLuxonDates.push(luxon.DateTime.fromMillis(blockedDays[x].getTime()).setZone(properties.time_zone));
    for (var x = 0; x < blockedLuxonDates.length; x++)
        for (var i = 0; i < sundayLuxonDates.length; i++)
            sundayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && sundayLuxonDates.splice([i], 1);
    for (var x = 0; x < blockedLuxonDates.length; x++)
        for (var i = 0; i < mondayLuxonDates.length; i++)
            mondayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && mondayLuxonDates.splice([i], 1);
    for (var x = 0; x < blockedLuxonDates.length; x++)
        for (var i = 0; i < tuesdayLuxonDates.length; i++)
            tuesdayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && tuesdayLuxonDates.splice([i], 1);
    for (var x = 0; x < blockedLuxonDates.length; x++)
        for (var i = 0; i < wednesdayLuxonDates.length; i++)
            wednesdayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && wednesdayLuxonDates.splice([i], 1);
    for (var x = 0; x < blockedLuxonDates.length; x++)
        for (var i = 0; i < thursdayLuxonDates.length; i++)
            thursdayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && thursdayLuxonDates.splice([i], 1);
    for (var x = 0; x < blockedLuxonDates.length; x++)
        for (var i = 0; i < fridayLuxonDates.length; i++)
            fridayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && fridayLuxonDates.splice([i], 1);
    for (var x = 0; x < blockedLuxonDates.length; x++)
        for (var i = 0; i < saturdayLuxonDates.length; i++)
            saturdayLuxonDates[i].hasSame(blockedLuxonDates[x], "day") && saturdayLuxonDates.splice([i], 1);
    for (var sunTimeSlots = [], sunLuxonIntervals = [], x = 0; x < sunRanges.length; x++)
        for (var i = 0; i < sundayLuxonDates.length; i++)
            sunLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
                year: sundayLuxonDates[i].year,
                month: sundayLuxonDates[i].month,
                day: sundayLuxonDates[i].day,
                hour: Math.floor(sunRanges[x][0] / 60),
                minute: sunRanges[x][0] % 60
            }, {
                zone: properties.time_zone
            }), luxon.DateTime.fromObject({
                year: sundayLuxonDates[i].year,
                month: sundayLuxonDates[i].month,
                day: sundayLuxonDates[i].day,
                hour: Math.floor(sunRanges[x][1] / 60),
                minute: sunRanges[x][1] % 60
            }, {
                zone: properties.time_zone
            })));
    for (var x = 0; x < sunLuxonIntervals.length; x++)
        for (var i = 0; i <= sunLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
            sunLuxonIntervals[x].start.plus({
                minutes: i
            }) >= setStart && sunLuxonIntervals[x].start.plus({
                minutes: i
            }) <= setEnd && sunTimeSlots.push(sunLuxonIntervals[x].start.plus({
                minutes: i
            }).toMillis());
    for (var monTimeSlots = [], monLuxonIntervals = [], x = 0; x < monRanges.length; x++)
        for (var i = 0; i < mondayLuxonDates.length; i++)
            monLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
                year: mondayLuxonDates[i].year,
                month: mondayLuxonDates[i].month,
                day: mondayLuxonDates[i].day,
                hour: Math.floor(monRanges[x][0] / 60),
                minute: monRanges[x][0] % 60
            }, {
                zone: properties.time_zone
            }), luxon.DateTime.fromObject({
                year: mondayLuxonDates[i].year,
                month: mondayLuxonDates[i].month,
                day: mondayLuxonDates[i].day,
                hour: Math.floor(monRanges[x][1] / 60),
                minute: monRanges[x][1] % 60
            }, {
                zone: properties.time_zone
            })));
    for (var x = 0; x < monLuxonIntervals.length; x++)
        for (var i = 0; i <= monLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
            monLuxonIntervals[x].start.plus({
                minutes: i
            }) >= setStart && monLuxonIntervals[x].start.plus({
                minutes: i
            }) <= setEnd && monTimeSlots.push(monLuxonIntervals[x].start.plus({
                minutes: i
            }).toMillis());
    for (var tueTimeSlots = [], tueLuxonIntervals = [], x = 0; x < tueRanges.length; x++)
        for (var i = 0; i < tuesdayLuxonDates.length; i++)
            tueLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
                year: tuesdayLuxonDates[i].year,
                month: tuesdayLuxonDates[i].month,
                day: tuesdayLuxonDates[i].day,
                hour: Math.floor(tueRanges[x][0] / 60),
                minute: tueRanges[x][0] % 60
            }, {
                zone: properties.time_zone
            }), luxon.DateTime.fromObject({
                year: tuesdayLuxonDates[i].year,
                month: tuesdayLuxonDates[i].month,
                day: tuesdayLuxonDates[i].day,
                hour: Math.floor(tueRanges[x][1] / 60),
                minute: tueRanges[x][1] % 60
            }, {
                zone: properties.time_zone
            })));
    for (var x = 0; x < tueLuxonIntervals.length; x++)
        for (var i = 0; i <= tueLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
            tueLuxonIntervals[x].start.plus({
                minutes: i
            }) >= setStart && tueLuxonIntervals[x].start.plus({
                minutes: i
            }) <= setEnd && tueTimeSlots.push(tueLuxonIntervals[x].start.plus({
                minutes: i
            }).toMillis());
    for (var wedTimeSlots = [], wedLuxonIntervals = [], x = 0; x < wedRanges.length; x++)
        for (var i = 0; i < wednesdayLuxonDates.length; i++)
            wedLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
                year: wednesdayLuxonDates[i].year,
                month: wednesdayLuxonDates[i].month,
                day: wednesdayLuxonDates[i].day,
                hour: Math.floor(wedRanges[x][0] / 60),
                minute: wedRanges[x][0] % 60
            }, {
                zone: properties.time_zone
            }), luxon.DateTime.fromObject({
                year: wednesdayLuxonDates[i].year,
                month: wednesdayLuxonDates[i].month,
                day: wednesdayLuxonDates[i].day,
                hour: Math.floor(wedRanges[x][1] / 60),
                minute: wedRanges[x][1] % 60
            }, {
                zone: properties.time_zone
            })));
    for (var x = 0; x < wedLuxonIntervals.length; x++)
        for (var i = 0; i <= wedLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
            wedLuxonIntervals[x].start.plus({
                minutes: i
            }) >= setStart && wedLuxonIntervals[x].start.plus({
                minutes: i
            }) <= setEnd && wedTimeSlots.push(wedLuxonIntervals[x].start.plus({
                minutes: i
            }).toMillis());
    for (var thuTimeSlots = [], thuLuxonIntervals = [], x = 0; x < thuRanges.length; x++)
        for (var i = 0; i < thursdayLuxonDates.length; i++)
            thuLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
                year: thursdayLuxonDates[i].year,
                month: thursdayLuxonDates[i].month,
                day: thursdayLuxonDates[i].day,
                hour: Math.floor(thuRanges[x][0] / 60),
                minute: thuRanges[x][0] % 60
            }, {
                zone: properties.time_zone
            }), luxon.DateTime.fromObject({
                year: thursdayLuxonDates[i].year,
                month: thursdayLuxonDates[i].month,
                day: thursdayLuxonDates[i].day,
                hour: Math.floor(thuRanges[x][1] / 60),
                minute: thuRanges[x][1] % 60
            }, {
                zone: properties.time_zone
            })));
    for (var x = 0; x < thuLuxonIntervals.length; x++)
        for (var i = 0; i <= thuLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
            thuLuxonIntervals[x].start.plus({
                minutes: i
            }) >= setStart && thuLuxonIntervals[x].start.plus({
                minutes: i
            }) <= setEnd && thuTimeSlots.push(thuLuxonIntervals[x].start.plus({
                minutes: i
            }).toMillis());
    for (var friTimeSlots = [], friLuxonIntervals = [], x = 0; x < friRanges.length; x++)
        for (var i = 0; i < fridayLuxonDates.length; i++)
            friLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
                year: fridayLuxonDates[i].year,
                month: fridayLuxonDates[i].month,
                day: fridayLuxonDates[i].day,
                hour: Math.floor(friRanges[x][0] / 60),
                minute: friRanges[x][0] % 60
            }, {
                zone: properties.time_zone
            }), luxon.DateTime.fromObject({
                year: fridayLuxonDates[i].year,
                month: fridayLuxonDates[i].month,
                day: fridayLuxonDates[i].day,
                hour: Math.floor(friRanges[x][1] / 60),
                minute: friRanges[x][1] % 60
            }, {
                zone: properties.time_zone
            })));
    for (var x = 0; x < friLuxonIntervals.length; x++)
        for (var i = 0; i <= friLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
            friLuxonIntervals[x].start.plus({
                minutes: i
            }) >= setStart && friLuxonIntervals[x].start.plus({
                minutes: i
            }) <= setEnd && friTimeSlots.push(friLuxonIntervals[x].start.plus({
                minutes: i
            }).toMillis());
    for (var satTimeSlots = [], satLuxonIntervals = [], x = 0; x < satRanges.length; x++)
        for (var i = 0; i < saturdayLuxonDates.length; i++)
            satLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromObject({
                year: saturdayLuxonDates[i].year,
                month: saturdayLuxonDates[i].month,
                day: saturdayLuxonDates[i].day,
                hour: Math.floor(satRanges[x][0] / 60),
                minute: satRanges[x][0] % 60
            }, {
                zone: properties.time_zone
            }), luxon.DateTime.fromObject({
                year: saturdayLuxonDates[i].year,
                month: saturdayLuxonDates[i].month,
                day: saturdayLuxonDates[i].day,
                hour: Math.floor(satRanges[x][1] / 60),
                minute: satRanges[x][1] % 60
            }, {
                zone: properties.time_zone
            })));
    for (var x = 0; x < satLuxonIntervals.length; x++)
        for (var i = 0; i <= satLuxonIntervals[x].length("minutes") - bookingLength; i += minuteInterval)
            satLuxonIntervals[x].start.plus({
                minutes: i
            }) >= setStart && satLuxonIntervals[x].start.plus({
                minutes: i
            }) <= setEnd && satTimeSlots.push(satLuxonIntervals[x].start.plus({
                minutes: i
            }).toMillis());
    for (var allAvailTimeSlots = availTimeSlots.concat(sunTimeSlots, monTimeSlots, tueTimeSlots, wedTimeSlots, thuTimeSlots, friTimeSlots, satTimeSlots), bookedLuxonIntervals = [], i = 0; i < bookedDays.length; i++)
        bookedLuxonIntervals.push(luxon.Interval.fromDateTimes(luxon.DateTime.fromMillis(bookedDays[i][0].getTime()).setZone(properties.time_zone).minus({
            minutes: bookingLength - 1
        }), luxon.DateTime.fromMillis(bookedDays[i][1].getTime()).setZone(properties.time_zone)));
    for (var i = 0; i < bookedLuxonIntervals.length; i++)
        for (var x = allAvailTimeSlots.length - 1; x >= 0; x--)
            bookedLuxonIntervals[i].contains(luxon.DateTime.fromMillis(allAvailTimeSlots[x])) && allAvailTimeSlots.splice([x], 1);
    instance.publishState("available_date_times", allAvailTimeSlots),
    instance.publishState("is_loaded", "done")
    
}

How i can click youtube ads skip button programmatically?

i try many ways to click youtube ads skip button via js but it is not clicked even i successfully get the button and i also try pointer events.

(function clickSkipAd() {
        var interval = setInterval(function() {
            var skipButton = document.querySelector('.ytp-skip-ad-button');
            if (skipButton) {
                skipButton.click(); // Simulate a mouse click on the Skip Ad button
                console.log("Ad skipped!");
                clearInterval(interval); // Stop checking once clicked
            } else {
                console.log("No ad to skip.");
            }
        }, 500); // Check every 500ms
    })();

React Chart.js not updating after applying timezone conversion

I’m using React.js and Chart.js to render analytics data. The backend provides timestamps in UTC, and I convert these timestamps to the user’s local timezone before rendering the chart.

After applying timezone conversion using toLocaleString() and a convertTZ() helper, the chart fails to render — it either stays blank or doesn’t update.

I’ve confirmed that the API receives and returns valid data. Here’s the function responsible for fetching and formatting that data:

const getChartDataCampId = async (campId) => {
  if (!campId) return;

  handleLoader(true);
  const now = Date.now();
  let { from, to } = dateRange;

  from = from ? new Date(from) : subDays(now, DEFAULT_DAYS_ON_CHART);
  to = to ? new Date(to) : new Date(now);

  if (Date.parse(from) > Date.parse(to)) {
    setDateRangeErr("Invalid date range");
    return;
  }

  setDateRangeErr("");
  const userTz = Intl.DateTimeFormat().resolvedOptions().timeZone;

  const fromInUserTZ = convertTZ(from, userTz);
  const toInUserTZ = convertTZ(to, userTz);

  const formattedFrom1 = fromInUserTZ.toLocaleString("sv-SE", {
    timeZone: userTz,
  }).replace(" ", "T");

  const formattedTo1 = toInUserTZ.toLocaleString("sv-SE", {
    timeZone: userTz,
  }).replace(" ", "T");

  try {
    const params = {
      campaignIds: campId,
      from: formattedFrom1,
      to: formattedTo1,
    };

    let emailSentChartData = await jobsApi.getChartData(null, campId, params);
    let multiChannelChartData = await jobsApi.getMultichannelChartData(
      user.email,
      null,
      params
    );

    const augRes = getAugmentRes({
      clicks: emailSentChartData.clicks,
      conversations: emailSentChartData.conversations,
      meetings: emailSentChartData.meetings,
      emailsSent: emailSentChartData?.dailySends,
      leadsGenerated: emailSentChartData?.dailyLeads,
      emailsDelivered: emailSentChartData?.dailyDelivered,
      emailsOpened: emailSentChartData?.dailyOpened,
      allReplies: emailSentChartData?.allReplies,
      repliedEmails: emailSentChartData?.repliedEmails,
      unSubscribeCount: emailSentChartData?.unSubscribeCount,
      webSiteVisit: emailSentChartData?.webSiteVisit,
      replies: emailSentChartData?.replies,
      pageViews: emailSentChartData?.pageViews,
      videoViews: emailSentChartData?.videoViews,
      avgVideoViews: emailSentChartData?.avgVideoViews,
      avgVideoViewPercentage: emailSentChartData?.avgVideoViewPercentage,
      invitationSent: multiChannelChartData.dailyInvitationSends,
      invitationAccepted: multiChannelChartData.dailyAccepted,
      invitationRejected: multiChannelChartData.dailyRejected,
      messageSent: multiChannelChartData.dailyMessagesSent,
      messageSeen: multiChannelChartData.messageSeen,
      params,
    });

    if (filterData === "Total") {
      const totalDataRes = await jobsApi.getLeadsTotalData([campId], dateRange);
      const augResTotal = getAugmentResTotal({
        clicks: totalDataRes?.clicks,
        emailsOpened: totalDataRes?.opens,
        params,
      });

      augRes.clicks = augResTotal.clicks;
      augRes.emailsOpened = augResTotal.emailsOpened;
    }

    handleLoader(false);
    return augRes;
  } catch (err) {
    console.log("Error fetching chart data!");
    console.log("Error here", err);
  }
};

What I want:
I want to show chart data in local timezone rather than UTC using Chart.js in React.

What happens:
After timezone conversion, the chart becomes blank or does not update.

What am I doing wrong? How should I fix this?

How to redirect after user submission? [closed]

On my business’ website, I would like to prompt users to leave a google review on my business.
Getting the link to the review form was easy through Google My Business.

However, I would like that after the user submits their review, they get redirected to a thank you page on my website. Is there a way to have that behavior ? Maybe a query parameter on the review form URL ?

If not, would it be possible to open the review form in a popup that closes once the user is done submitting ?

I have tried embedding the review form on the website, but the same-origin restriction prevents me from doing that.
I have not found a way to programmatically send the user review.

Google Review : redirect after user submission

On my business’ website, I would like to prompt users to leave a google review on my business.
Getting the link to the review form was easy through Google My Business.

However, I would like that after the user submits their review, they get redirected to a thank you page on my website. Is there a way to have that behavior ? Maybe a query parameter on the review form URL ?

If not, would it be possible to open the review form in a popup that closes once the user is done submitting ?

I have tried embedding the review form on the website, but the same-origin restriction prevents me from doing that.
I have not found a way to programmatically send the user review.

Stripe subscription and one time payment response

So, on one time payment, I am creating intent and returning to the frontend: paymentIntent.client_secret

But my question is, what if in my business logic, I need to create a few subscriptions? For each one, I will create a priceId, so what must I return to the frontend?

First I was trying it on a single subscription,

added these options to subscription creation:

        payment_behavior: "default_incomplete",
        payment_settings: { save_default_payment_method: "on_subscription" },
        expand: ["latest_invoice.confirmation_secret"],

and wanted to return to the front confirmation_secret, but it is null.

so yeah my questions is if I will create for example 5 subscriptions, what I must return to frontend so he can process, so client can do first payment.

Docker (compose) php apache. Imagick with heic support [closed]

We’re running a website for our community on docker (compose) and now we would like to enable heic support for imagick such that apple users can upload images which are then converted to jpeg.

We have been able to enable imagick and gd support but heic is another issue.

FROM php:8.1-apache

RUN a2enmod rewrite 

# Install system dependencies
RUN apt-get update && apt-get install -y 
      git 
      libicu-dev 
      libc-client-dev
      zlib1g-dev 
      g++
      libpq-dev 
      libmcrypt-dev 
      git 
      libkrb5-dev

# Install system dependencies for zipping
RUN apt-get install -y 
      libzip-dev 
      zip 
      unzip

# Install system deps for imagick
RUN apt-get install -y 
      libpng-dev 
      libjpeg-dev 
      libwebp-dev 
      libfreetype6-dev 
      libonig-dev 
      libxml2-dev 
      libheif-dev 
      libde265-dev 
      imagemagick 
      libmagickwand-dev 
      && rm -rf /var/lib/apt/lists/*

# Install PHP extensions
RUN docker-php-ext-install pdo pdo_mysql mysqli
RUN docker-php-ext-configure intl
RUN docker-php-ext-install 
      intl
      # mbstring 
      # curl 
      # json 
      # zip
RUN docker-php-ext-configure imap --with-kerberos --with-imap-ssl && docker-php-ext-install imap && docker-php-ext-enable imap

# Install PHP GD extension
RUN apt-get install -y zlib1g-dev libpng-dev libjpeg-dev
RUN docker-php-ext-configure gd 
      --with-jpeg 
      --with-freetype 
      && docker-php-ext-install gd

# Install PHP Imagick extension via PECL and enable it
RUN apt-get update && apt-get install -y libmagickwand-dev 
    && pecl install imagick 
    && docker-php-ext-enable imagick

RUN convert -list format | grep HEIC || echo "HEIC format not found in ImageMagick"


# RUN usermod -u ${uid} www-data 
#     && groupmod -g ${uid} www-data;

My test code:

<?php
if ($_SERVER['REQUEST_METHOD'] === 'POST' && isset($_FILES['image'])) {
    $fileTmp = $_FILES['image']['tmp_name'];
    $fileExt = strtolower(pathinfo($_FILES['image']['name'], PATHINFO_EXTENSION));

    $imagick = new Imagick($fileTmp);
    
}
?>

<!DOCTYPE html>
<html>
<head><title>HEIC to JPEG</title></head>
<body>
    <h1>Upload HEIC Image</h1>
    <form method="post" enctype="multipart/form-data">
        <input type="file" name="image" accept=".heic">
        <button type="submit">Convert to JPEG</button>
    </form>
</body>
</html>

The issue:
Fatal error: Uncaught ImagickException: Invalid filename provided in /var/www/html/upload-test.php:8 Stack trace: #0 /var/www/html/upload-test.php(8): Imagick->__construct('') #1 {main} thrown in /var/www/html/upload-test.php on line 8

We can’t find “a simple solution” that is like enabling gd or imagick with heic but only solutions that require us to build imagick from source and then enable heic.

Is doing so, by building from source and enabling heic, the only possible method?
What is a good alternative for converting iphone images to jpeg / png on our website with ease or enabling imagick to convert heic to jpeg format with ease?

Import variable products in Wocommers

I’m importing variable products into WooCommerce using a CSV file.
Each product uses a global attribute called pa_size and variations have their values like S, M, L, L-XL, etc.

The problem I’m facing is this:

When I define the parent product with Attribute 1 value(s): S|M|L and set:

Attribute 1 global: 1

Attribute 1 variation: 1

WooCommerce adds all those values (S|M|L) to the global attribute, which is already created.
The global attribute ends up “polluted” with extra sizes.

Because of this, the variants get set to any-size for pa_size.

Attribute: pa_color: Blue
Attribute: pa_size: M
Attribute: pa_size: S

This will be any_size Blue
This will be any_size Blue

I would like to:
Each variation should be linked to only the exact global term (like S, M)
The parent should not “inject” extra values into the global attribute term list
No custom attributes — everything should stay linked to pre-created global values

Did anyone else have a similar problem?

Attribute Accessors not working after laravel app upgrade

Laravel accessor not working after I upgraded from v8.75 to v11.31

I had this LARAVEL v8.75 App where in the User model I created an Accessor for photo like below

public function getPhotoAttribute($value)
    {
        // Default Photo
        $defaultPhotoUrl = mediaUrl('assets/user.png');

        return !empty($value) && $value != 'undefined' ? (new FileHelper)->getFileUrl($value) : $defaultPhotoUrl;
    }

with the intention of setting the user default image where it’s null, but after my upgrade to v11.31, I noticed empty photos are returned as null instead of with the default image

I went through the documentation and noticed that the syntax was changed. then I upgraded the function to:

     /**
     * Interact with the user's photo.
     */
    protected function photo(): Attribute
    {
        return Attribute::make(
            get: fn (string $value) => (!empty($value) && $value != 'undefined' ? (new FileHelper)->getFileUrl($value) :mediaUrl('assets/user.png')),
        );
    }

but yet no result

Filamentphp v3 hide RenderHook if sidebar is collapsed

How do I hide a custom blade (PanelsRenderHook) on my sidebar if sidebar is collapsed.

class AppServiceProvider extends ServiceProvider
{
   
    public function register(): void
    {
   
    }

    
    public function boot(): void
    {
       FilamentView::registerRenderHook(
            PanelsRenderHook::SIDEBAR_NAV_START,
            fn (): string => Blade::render(
                <<<'BLADE'
                    @livewire('panel-title', ['panelId' => $panelId])
                BLADE,
                ['panelId' => Filament::getCurrentPanel()->getId()]
            ),
        );

NOT NEED TO Answer

Ck5vZGUganMgQ2xpZW50Cgpqcy9tYWluLmpzCi8vSW5pdGlhbGl6ZSBmdW5jdGlvbgoKdmFyIGluaXQgPSBmdW5jdGlvbiAoKSB7ICAKICAgIC8vIGFkZCBldmVudExpc3RlbmVyIGZvciBrZXlkb3duCiAgICBkb2N1bWVudC5hZGRFdmVudExpc3RlbmVyKCdrZXlkb3duJywgZnVuY3Rpb24oZSkgewogICAgCXN3aXRjaChlLmtleUNvZGUpewogICAgCWNhc2UgMzc6IC8vTEVGVCBhcnJvdwogICAgCQlicmVhazsKICAgIAljYXNlIDEwMDA5OiAvL1JFVFVSTiBidXR0b24KCQl0aXplbi5hcHBsaWNhdGlvbi5nZXRDdXJyZW50QXBwbGljYXRpb24oKS5leGl0KCk7CiAgICAJCWJyZWFrOwogICAgCWRlZmF1bHQ6CiAgICAJCWNvbnNvbGUubG9nKCdLZXkgY29kZSA6ICcgKyBlLmtleUNvZGUpOwogICAgCQlicmVhazsKICAgIAl9CiAgICB9KTsKICAgIAogICAgLy8gR2l2ZSBTZXJ2ZXIgZGV2aWNlIElQIAogICAgY29uc3Qgc29ja2V0ID0gaW8oImh0dHA6Ly8xMDcuMTA5LjIwMy4yMzk6MzAwMC8iKTsKCiAgICBzb2NrZXQub24oJ2Nvbm5lY3QnLCBmdW5jdGlvbigpIHsKICAgICAgY29uc29sZS5sb2coIkNvbm5lY3RlZCB0byBTb2NrZXQgc2VydmVyIik7CiAgICAgIGNvbnNvbGUubG9nKHNvY2tldC5jb25uZWN0ZWQpOyAKCiAgICB9KTsKCiAgICBzb2NrZXQub24oIm5ld3MiLCBmdW5jdGlvbihkKSB7CiAgICAgIGNvbnNvbGUubG9nKCJDb21tb24gSlMiLGQpOwogICAgICBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCgnbXNnJykuaW5uZXJIVE1MID0gZC5oZWxsbzsKICAgIH0pOwp9OwovLyB3aW5kb3cub25sb2FkIGNhbiB3b3JrIHdpdGhvdXQgPGJvZHkgb25sb2FkPSIiPgp3aW5kb3cub25sb2FkID0gaW5pdDsKCgoKCmluZGV4Lmh0bWwKCjwhRE9DVFlQRSBodG1sPgo8aHRtbD4KPGhlYWQ+CiAgICA8bWV0YSBjaGFyc2V0PSJ1dGYtOCIgLz4KICAgIDxtZXRhIG5hbWU9InZpZXdwb3J0IiBjb250ZW50PSJ3aWR0aD1kZXZpY2Utd2lkdGgsIGluaXRpYWwtc2NhbGU9MS4wLCBtYXhpbXVtLXNjYWxlPTEuMCI+CiAgICA8bWV0YSBuYW1lPSJkZXNjcmlwdGlvbiIgY29udGVudD0iVGl6ZW4gYmFzaWMgdGVtcGxhdGUgZ2VuZXJhdGVkIGJ5IFRpemVuIFdlYiBJREUiLz4KCiAgICA8dGl0bGU+VGl6ZW4gV2ViIElERSAtIFRpemVuIC0gU2Ftc3VuZyBUaXplbiBUViBiYXNpYyBBcHBsaWNhdGlvbjwvdGl0bGU+CgogICAgPGxpbmsgcmVsPSJzdHlsZXNoZWV0IiB0eXBlPSJ0ZXh0L2NzcyIgaHJlZj0iY3NzL3N0eWxlLmNzcyIvPgogICAgPHNjcmlwdCBzcmM9ImpzL21haW4uanMiPjwvc2NyaXB0PgogICAgPHNjcmlwdCB0eXBlPSJ0ZXh0L2phdmFzY3JpcHQiIHNyYz0ianMvc29ja2V0LWlvLmpzIj48L3NjcmlwdD4KPC9oZWFkPgoKPGJvZHk+CiAgPGhlYWRlcj4KICAgIDxoZ3JvdXA+CiAgICAgIDxoMT5UaXplbiBhcHA8L2gxPgogICAgICA8aDI+U29ja2V0IGNsaWVudCBBcHA8L2gyPgoKICAgIDwvaGdyb3VwPgogIDwvaGVhZGVyPgoKPHAgaWQ9Im1zZyI+PC9wPiAgCjwvYm9keT4KPC9odG1sPgoKCgpiMmIgbm9kZSBqcyBzZXJ2ZXIgCgpqcy9sb2dpYy5qcwovKioKICogQEFVVEhPUjoganVhbi5jb3J0ZXNAc2Ftc3VuZy5jb20KICogQGRlc2NyaXB0aW9uOiBTYW1wbGUgZGVtbyBvZiB3ZWJzZXJ2ZXIuCiAqLwoKdmFyIExvZ2ljID0gZnVuY3Rpb24oKSB7CgogICAgdmFyIGV4cHJlc3MgPSByZXF1aXJlKCdleHByZXNzJyk7CiAgICB2YXIgYXBwID0gZXhwcmVzcygpOwogICAgdmFyIGNvcnMgPSByZXF1aXJlKCJjb3JzIik7CiAgICAKICAgIHZhciBzZXJ2ZXIgPSByZXF1aXJlKCdodHRwJykuU2VydmVyKGFwcCk7CiAgICB2YXIgaW8gPSByZXF1aXJlKCdzb2NrZXQuaW8nKShzZXJ2ZXIpOwoKICAgIHZhciBwb3J0U2VydmVyID0gMzAwMDsKCgoKICAgIHZhciBib2R5UGFyc2VyID0gcmVxdWlyZSgiYm9keS1wYXJzZXIiKTsKICAgIGFwcC51c2UoY29ycygpKTsKICAgIGFwcC51c2UoYm9keVBhcnNlci51cmxlbmNvZGVkKHsKICAgICAgICBleHRlbmRlZDogZmFsc2UKICAgIH0pKTsKCiAgICAvL01haW4gR2V0IGZvciBoYW5kbGVyIHRoZSAicm9vdCIgcmVxdWVzdDsKICAgIGFwcC5nZXQoJy8nLCBmdW5jdGlvbihyZXEsIHJlcykgewogICAgICAgIHJlcy5zZW5kKCJIRWxsbyIpOwogICAgfSk7CgoKICAgIC8vU3RhcnQgc2VydmVycwogICAgc2VydmVyLmxpc3Rlbihwb3J0U2VydmVyLCBmdW5jdGlvbigpIHsKICAgICAgICBjb25zb2xlLmxvZygnTm9kZSBzZXJ2ZXIgaXMgcnVubmluZy4uJyk7CiAgICB9KTsKCiAgICBpby5vbignY29ubmVjdGlvbicsIGZ1bmN0aW9uKHNvY2tldCkgewogICAgICAgIHNvY2tldC5lbWl0KCduZXdzJywgewogICAgICAgICAgICBoZWxsbzogJ1dPT09PT09PT09PUkxMTExMTExMTExMREREREREREREREREREREREQnCiAgICAgICAgfSk7CiAgICAgICAgICBzb2NrZXQub24oJ25ld3MnLCBmdW5jdGlvbihkYXRhKXsKICAgICAgICAJICBjb25zb2xlLmxvZyhkYXRhKTsKICAgICAgICAgIH0pOwogICAgICAgIC8vICBzb2NrZXQub24oJ3VwZGF0ZU51bWJlcicsdXBkYXRlSGFuZGxlcik7CiAgICAgICAgLy8gIHNvY2tldC5vbignZmlsZU5hbWUnLHNob3dmaWxlTmFtZSk7CiAgICAgICAgLy8gIHNvY2tldC5vbignZG9uZScsIHVwZGF0ZW1lc3NhZ2UpOwogICAgICAgIC8vICBzb2NrZXQub24oJ3VwZGF0ZV9tZXNzYWdlJywgdXBkYXRlbWVzc2FnZUhhbmRsZXIpOwoKICAgIH0pOwoKICAgIC8vUmV0dXJuIGEgc2FtcGxlIHN0cmluZy4KICAgIHJldHVybiAnTG9hZGVkJzsKfTsKCm1vZHVsZS5leHBvcnRzID0gTG9naWM7CgoKanMvbWFpbi5qcwoKLyoqCiAqIEBBVVRIT1I6IGp1YW4uY29ydGVzQHNhbXN1bmcuY29tCiAqIEBkZXNjcmlwdGlvbjogU2FtcGxlIGRlbW8gb2Ygd2Vic2VydmVyLgogKi8KCnZhciBMb2dpYyA9IGZ1bmN0aW9uKCkgewoKICAgIHZhciBleHByZXNzID0gcmVxdWlyZSgnZXhwcmVzcycpOwogICAgdmFyIGFwcCA9IGV4cHJlc3MoKTsKICAgIHZhciBjb3JzID0gcmVxdWlyZSgiY29ycyIpOwogICAgCiAgICB2YXIgc2VydmVyID0gcmVxdWlyZSgnaHR0cCcpLlNlcnZlcihhcHApOwogICAgdmFyIGlvID0gcmVxdWlyZSgnc29ja2V0LmlvJykoc2VydmVyKTsKCiAgICB2YXIgcG9ydFNlcnZlciA9IDMwMDA7CgoKCiAgICB2YXIgYm9keVBhcnNlciA9IHJlcXVpcmUoImJvZHktcGFyc2VyIik7CiAgICBhcHAudXNlKGNvcnMoKSk7CiAgICBhcHAudXNlKGJvZHlQYXJzZXIudXJsZW5jb2RlZCh7CiAgICAgICAgZXh0ZW5kZWQ6IGZhbHNlCiAgICB9KSk7CgogICAgLy9NYWluIEdldCBmb3IgaGFuZGxlciB0aGUgInJvb3QiIHJlcXVlc3Q7CiAgICBhcHAuZ2V0KCcvJywgZnVuY3Rpb24ocmVxLCByZXMpIHsKICAgICAgICByZXMuc2VuZCgiSEVsbG8iKTsKICAgIH0pOwoKCiAgICAvL1N0YXJ0IHNlcnZlcnMKICAgIHNlcnZlci5saXN0ZW4ocG9ydFNlcnZlciwgZnVuY3Rpb24oKSB7CiAgICAgICAgY29uc29sZS5sb2coJ05vZGUgc2VydmVyIGlzIHJ1bm5pbmcuLicpOwogICAgfSk7CgogICAgaW8ub24oJ2Nvbm5lY3Rpb24nLCBmdW5jdGlvbihzb2NrZXQpIHsKICAgICAgICBzb2NrZXQuZW1pdCgnbmV3cycsIHsKICAgICAgICAgICAgaGVsbG86ICdXT09PT09PT09PT1JMTExMTExMTExMTEREREREREREREREREREREREJwogICAgICAgIH0pOwogICAgICAgICAgc29ja2V0Lm9uKCduZXdzJywgZnVuY3Rpb24oZGF0YSl7CiAgICAgICAgCSAgY29uc29sZS5sb2coZGF0YSk7CiAgICAgICAgICB9KTsKICAgICAgICAvLyAgc29ja2V0Lm9uKCd1cGRhdGVOdW1iZXInLHVwZGF0ZUhhbmRsZXIpOwogICAgICAgIC8vICBzb2NrZXQub24oJ2ZpbGVOYW1lJyxzaG93ZmlsZU5hbWUpOwogICAgICAgIC8vICBzb2NrZXQub24oJ2RvbmUnLCB1cGRhdGVtZXNzYWdlKTsKICAgICAgICAvLyAgc29ja2V0Lm9uKCd1cGRhdGVfbWVzc2FnZScsIHVwZGF0ZW1lc3NhZ2VIYW5kbGVyKTsKCiAgICB9KTsKCiAgICAvL1JldHVybiBhIHNhbXBsZSBzdHJpbmcuCiAgICByZXR1cm4gJ0xvYWRlZCc7Cn07Cgptb2R1bGUuZXhwb3J0cyA9IExvZ2ljOwoKanMvc2VydmVyLmpzCgovL1NhbXBsZSBTZXJ2ZXIhCnZhciBub2RlSlNTZXJ2ZXIgPSByZXF1aXJlKCcuL2xvZ2ljLmpzJyk7Cgpjb25zb2xlLmxvZyhub2RlSlNTZXJ2ZXIoKSk7CgppbmRleC5odG1sCgo8IURPQ1RZUEUgaHRtbD4KPGh0bWw+CjxoZWFkPgogICAgPG1ldGEgY2hhcnNldD0idXRmLTgiIC8+CiAgICA8bWV0YSBuYW1lPSJ2aWV3cG9ydCIgY29udGVudD0id2lkdGg9ZGV2aWNlLXdpZHRoLCBpbml0aWFsLXNjYWxlPTEuMCwgbWF4aW11bS1zY2FsZT0xLjAiPgogICAgPG1ldGEgbmFtZT0iZGVzY3JpcHRpb24iIGNvbnRlbnQ9IlRpemVuIGJhc2ljIHRlbXBsYXRlIGdlbmVyYXRlZCBieSBUaXplbiBXZWIgSURFIi8+CgogICAgPHRpdGxlPk5vZGUgU2VydmVyIEFwcGxpY2F0aW9uPC90aXRsZT4KCiAgICA8bGluayByZWw9InN0eWxlc2hlZXQiIHR5cGU9InRleHQvY3NzIiBocmVmPSJjc3Mvc3R5bGUuY3NzIi8+CiAgICAKICAgIDxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0IiBzcmM9IiRCMkJBUElTL2IyYmFwaXMvYjJiYXBpcy5qcyI+PC9zY3JpcHQ+Cgk8c2NyaXB0IHR5cGU9J3RleHQvamF2YXNjcmlwdCcgbGFuZ3VhZ2U9J2phdmFzY3JpcHQnIHNyYz0nJFdFQkFQSVMvd2ViYXBpcy93ZWJhcGlzLmpzJz48L3NjcmlwdD4KICAgIDxzY3JpcHQgc3JjPSJqcy9tYWluLmpzIj48L3NjcmlwdD4KPC9oZWFkPgoKPGJvZHk+CiAgIDxoMT5Ob2RlIFNlcnZlciBBcHA8L2gxPgo8L2JvZHk+CjwvaHRtbD4K

Evt.which and Evt.keyCode deprecated and JavaScript Errors

Currently I have several scripts that take the numbers the user enters into a text field for a phone number and converts the number into the standard (123) 456-7890 format. When validating my script it’s throwing errors such as evt.which and evt.keyCode are deprecated, document used before it was declined, etc.

I’m just wondering if there is a different way to do what I am trying to do that isn’t deprecated and not give the same errors?

As the user types in the number into the field, it will force it to display it in (123) 456-7890 format in both the field itself and the output.

// Format the phone number as the user types it
document.getElementById('ContactPhone').addEventListener('keyup', function(evt) {
  var ContactPhoneVal = document.getElementById('ContactPhone');
  var charCode = (evt.which) ? evt.which : evt.keyCode;
  ContactPhoneVal.value = phoneFormat(ContactPhoneVal.value);
});

// We need to manually format the phone number on page load
document.getElementById('ContactPhone').value = phoneFormat(document.getElementById('ContactPhone').value);

// A function to determine if the pressed key is an integer
function numberPressed(evt) {
  var charCode = (evt.which) ? evt.which : evt.keyCode;
  if (charCode > 31 && (charCode < 48 || charCode > 57) && (charCode < 36 || charCode > 40)) {
    return false;
  }
  return true;
}

// A function to format text to look like a phone number
function phoneFormat(input) {
  // Strip all characters from the input except digits
  input = input.replace(/D/g, '');

  // Trim the remaining input to ten characters, to preserve phone number format
  input = input.substring(0, 10);

  // Based upon the length of the string, we add formatting as necessary
  var size = input.length;
  if (size === 0) {
    input = input;
  } else if (size < 4) {
    input = '(' + input;
  } else if (size < 7) {
    input = '(' + input.substring(0, 3) + ') ' + input.substring(3, 6);
  } else {
    input = '(' + input.substring(0, 3) + ') ' + input.substring(3, 6) + ' - ' + input.substring(6, 10);
  }
  return input;
}
<input type="text" id="ContactPhone" onkeypress="return numberPressed(event);" autocomplete="off">

How to hook several state fields in React Component [closed]

useAppStore is a Zustand Store.

I made 3 assumptions based on my understand of React hooks (Please correct me if any of them is wrong)

const MessageList: React.FC<MessageListProps> = ({ popupId }) => {
 const {
    popups,
    isLoading,
    liveResponse: response,
    liveAttachments,
    updateVisibleMessages,
  } = useAppStore();
  1. popups is a list of PopupState. whenever I change a popup by id, for example at PopupState.position.x, I create a new instance of the array popups and change the popup state (new instance too) of that id. (immutability). Since I changed the array of popups, all popups on screen will re-render, which is extremely wasteful because I only want to update position x of one popup by id. Is this correct?

  2. If change the code like this, it will render only when those fields changes, which is the most efficient approach to avoid unnecessary re-render. Is my understanding correct?

    const MessageList: React.FC<MessageListProps> = ({ popupId }) => {
      const themeStyle = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.themeStyle);
      const currentNodeId = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.currentNodeId);
      const messagesMap = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.messagesMap);
      const lastUserMessage = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.lastUserMessage);
      const cachedVisibleMessages = useAppStore((state) => state.popups.find((p) => p.id === popupId)?.cachedVisibleMessages);
    
  3. I hate repeating the state.popups.find((p) => p.id === popupId) so many times, but if I change the code like below, I lose all optimization again. In fact it will be worse because I create a new object instance to wrap the value and when React compares the previous and current value of the hook, it will always think that the state changed even though the fields values itself didn’t change. Is that correct? React does not do deep comparison?

    const { position, title } = useAppStore((state) => ({
      position: state.popups[popupId]?.position,
      title: state.popups[popupId]?.title,
    }));
    

If all my assumptions are correct, how to make the code in 2. look more compact and non-repetitive (DRY)? Is caching it in the store the only solution?