Can’t add trigger on insert in auth.Users in Supabase

I tried to create a trigger on auth.users to add information to a public table, but for some reason, it caused issues during registration. I get the following error:
“AuthApiError: Database error saving new user”.

Before this, I also had an issue where I called a procedure for auth.users (policies were fine), but it didn’t work. It worked in the SQL editor but failed through api requests (I still don’t know how to fix this), and maybe it’s somehow related.

Here is my function:

const handleSignUp = async (e) => {
  e.preventDefault();

  try {
    const { user, error } = await supabase.auth.signInWithPassword({
      email: formData.email,
      password: formData.password,
    });
    if (user) {
      alert("An account with this email already exists. Please log in.");
    } else if (error) {
      const { data, error } = await supabase.auth.signUp({
        email: formData.email,
        password: formData.password,
      });
      if (error) throw error;
      alert("Check your email for the verification link");
    }
  } catch (error) {
    alert(error);
  }
};

Here are the function and trigger:

CREATE OR REPLACE FUNCTION handle_new_user()
RETURNS TRIGGER AS $$
BEGIN
  INSERT INTO public.users (user_id, email, Role)
  VALUES (NEW.id, NEW.email, 'Low')
  ON CONFLICT (user_id) DO NOTHING;
  RETURN NEW;
END;
$$ LANGUAGE plpgsql SECURITY DEFINER;

CREATE TRIGGER on_auth_user_insert
AFTER INSERT ON auth.users
FOR EACH ROW
EXECUTE FUNCTION handle_new_user();

and table schema: https://i.sstatic.net/IvUq71Wk.png

Has anyone encountered a similar problem?

Unable to Toggle Between Locked and Unlocked States with SVG Icons in JavaScript for Disabling/Enabling Table Buttons

I’ve got data displayed using tabulator and I’ve added some buttons to customize the table such as highlighting, saving changes, adding rows etc… and i’m now trying to implement a locked/unlocked feature. It’s exactly how it sounds, when the page is locked, you can’t update the table with those buttons. When the page is unlocked, you can update the table with those buttons.

Right now, upon page load, i’ve got a locked icon svg that sits next to the buttons and you are restricted from making changes with the other buttons. however, the toggle function to get it to unlock those buttons isn’t working. it’s meant to switch to an unlocked icon svg and, of course, allow me to use all the buttons and edit the table. does anyone see where i’m going wrong in my code? all feedback is welcome and appreciated! thanks in advance.

Here’s my html & js:

<div class="table-controls">
    <button id="add-row">Add Row</button>
    <button id="del-row">Delete Row</button>
    <button id="saveChanges">Save Changes</button>
    <button id="highlightButton">Highlight</button>
    <button id="remove-highlight">Remove Highlight</button>
    <button id="remove-font-style">Remove Text Color</button>
    <button id="lock-toggle" style="cursor: pointer;">
        <svg id="lock-icon" style="width: 24; height: 24;vertical-align: middle;fill: currentColor;overflow: hidden;" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg">
            <path d="M295.424 831.36512h450.56a20.48 20.48 0 0 0 20.48-20.48v-350.4128a20.48 20.48 0 0 0-20.48-20.48h-9.70752v-51.36384c-1.26976-117.248-97.95584-212.62336-215.552-212.62336-117.94432 0-214.6304 95.66208-215.57248 213.4016v50.5856h-9.70752a20.48 20.48 0 0 0-20.48 20.48v350.4128a20.43904 20.43904 0 0 0 20.45952 20.48z m430.08-40.96h-409.6v-309.4528h409.6v309.4528zM346.09152 389.57056c0.75776-95.17056 79.07328-172.60544 174.61248-172.60544 95.25248 0 173.58848 77.2096 174.592 172.01152v51.01568H346.07104l0.02048-50.42176z" fill="#004F6E" />
            <path d="M476.01664 721.69472a20.48 20.48 0 0 0 20.48 20.48h48.37376a20.48 20.48 0 0 0 20.48-20.48v-66.17088a70.73792 70.73792 0 0 0 26.64448-55.1936 71.33184 71.33184 0 0 0-142.62272 0c0 21.62688 9.95328 41.82016 26.64448 55.1936v66.17088z m44.68736-151.552a30.3104 30.3104 0 0 1 30.35136 30.208c0 10.87488-6.10304 20.97152-15.95392 26.3168a20.48 20.48 0 0 0-10.69056 17.98144v56.56576h-7.41376v-56.56576c0-7.49568-4.096-14.41792-10.69056-17.98144a30.1056 30.1056 0 0 1-15.95392-26.3168 30.28992 30.28992 0 0 1 30.35136-30.208z" fill="#004F6E" />
        </svg>
        <svg id="unlock-icon" fill="#004F6E" height="24" width="24" version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 512 512" xml:space="preserve">
            <g id="SVGRepo_bgCarrier" stroke-width="0"></g>
            <g id="SVGRepo_tracerCarrier" stroke-linecap="round" stroke-linejoin="round"></g>
            <g id="SVGRepo_iconCarrier"> <g> <g> <path d="M409.6,204.8h-256V128c0-56.559,45.841-102.4,102.4-102.4S358.4,71.441,358.4,128H384C384,57.421,326.579,0,256,0 S128,57.421,128,128v76.8h-25.6c-14.14,0-25.6,11.46-25.6,25.6v256c0,14.14,11.46,25.6,25.6,25.6h307.2 c14.14,0,25.6-11.46,25.6-25.6v-256C435.2,216.26,423.74,204.8,409.6,204.8z M409.6,486.4H102.4v-256h307.2V486.4z"></path> </g> </g> 
            <g> <g> <path d="M256,307.2c-21.171,0-38.4,17.229-38.4,38.4c0,16.666,10.735,30.737,25.6,36.045V422.4h25.6v-40.755 c14.865-5.299,25.6-19.379,25.6-36.045C294.4,324.429,277.171,307.2,256,307.2z M256,358.4c-7.066,0-12.8-5.734-12.8-12.8 c0-7.066,5.734-12.8,12.8-12.8c7.066,0,12.8,5.734,12.8,12.8C268.8,352.666,263.066,358.4,256,358.4z"></path> </g> </g> </g>
        </svg>
    </button>
</div>

<script>
document.addEventListener('DOMContentLoaded', function () {
    let isLocked = true;

    function toggleLock() {
        const lockIcon = document.getElementById('lock-icon');
        const unlockIcon = document.getElementById('unlock-icon');
        
        if (isLocked) {
            lockIcon.style.display = 'inline';  
            unlockIcon.style.display = 'none'; 
        } else {
            lockIcon.style.display = 'none';
            unlockIcon.style.display = 'inline';
        }

        const buttons = [
            document.getElementById('add-row'),
            document.getElementById('del-row'),
            document.getElementById('saveChanges'),
            document.getElementById('highlightButton'),
            document.getElementById('remove-highlight'),
            document.getElementById('remove-font-style')
        ];

        buttons.forEach(button => {
            button.disabled = isLocked; 
        });

        table.updateOptions({
            editable: !isLocked,
            selectable: !isLocked  
        });

        document.body.style.cursor = isLocked ? 'not-allowed' : 'default';
    }

    toggleLock();

    document.getElementById('lock-toggle').addEventListener('click', function() {
        isLocked = !isLocked;
        toggleLock();
    });
});
</script>

Javascript image lightbox gallery transalte from inline script

My script is working or the script from w3schools, but I want to use it many times on diffrent gallerys/pages and therefore I need help to transalte the script to a function, so that I do not have to repeat myself many times.

Someone told me it is bad practices to use javascript onclick functions or functions in html and I have to avoid them.

function openModal() {
    document.getElementById("myModal").style.display = "block";
  }

  function closeModal() {
    document.getElementById("myModal").style.display = "none";
  }

  var slideIndex = 1;
  showSlides(slideIndex);

  function plusSlides(n) {
    showSlides((slideIndex += n));
  }

  function currentSlide(n) {
    showSlides((slideIndex = n));
  }

  function showSlides(n) {
    var i;
    var slides = document.getElementsByClassName("mySlides");
    var dots = document.getElementsByClassName("demo");
    var captionText = document.getElementById("caption");
    if (n > slides.length) {
      slideIndex = 1;
    }
    if (n < 1) {
      slideIndex = slides.length;
    }
    for (i = 0; i < slides.length; i++) {
      slides[i].style.display = "none";
    }
    for (i = 0; i < dots.length; i++) {
      dots[i].className = dots[i].className.replace(" active", "");
    }
    slides[slideIndex - 1].style.display = "block";
    dots[slideIndex - 1].className += " active";
    captionText.innerHTML = dots[slideIndex - 1].alt;
  }
* {
  margin: 0;
  padding: 0;
  text-decoration: none;
  font-size: 20px;
}

/***********************************************
  TEMPLATE CONTAINER
***********************************************/
.container {
  display: grid;
  height: 100vh;
  margin: 0 auto;
  grid-template-columns: 1fr;
  grid-template-areas:
    "header"
    "main"
    "footer";
}

main {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas:
    "slug-wrapper"
    "img-shadow"
    "category-box"
    "row"
}

.img-shadow {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas:
    "main-content-2"
    "main-content"
    "main-content-3"
    "main-content-4"
    "main-content-5"
    "main-content-6";
}

.row {
  display: grid;
  grid-template-columns: 1fr;
  grid-template-areas:
    "category-box"
    "image-wrapper-1"
    "image-wrapper-2"
    "image-wrapper-3";
}

.row {
  grid-area: row;
}

.category-box {
  grid-area: category-box;
}

/* Main Container */
header {
  grid-area: header;
  background-color: white;
  text-align: center;
  z-index: -3;
}

main {
  grid-area: main;
  text-align: center;
  padding: 30px;
}

footer {
  text-align: center;
  grid-area: footer;
  background-color: cornflowerblue;
}

/* Image Gallery */
.category-box {
  background-color: var(--secondary);
  color: white;
  margin-top: 55px;
  grid-area: category-box;
}

.image-slide {
  width: 100%;
}

.demo {
  width: 100%;
}

.image-wrapper-1 {
  grid-area: image-wrapper-1;
  margin-bottom: 35px;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}

.image-wrapper-2 {
  grid-area: image-wrapper-2;
  margin-bottom: 35px;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}

.image-wrapper-3 {
  grid-area: image-wrapper-3;
  margin-bottom: 35px;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}



/***********************************************
  IMAGE - GALLERY
***********************************************/
.row > .column {
  padding: 0 8px;
}

.row:after {
  content: "";
  display: table;
  clear: both;
}

.column {
  float: left;
  width: 25%;
}

/* The Modal (background) */
.modal {
  display: none;
  position: fixed;
  z-index: 555;
  padding-top: 100px;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  overflow: auto;
  background-color: black;
}

/* Modal Content */
.modal-content {
  position: relative;
  background-color: #fefefe;
  margin: auto;
  padding: 0;
  width: 90%;
  max-width: 1200px;
}

/* The Close Button */
.close {
  color: white;
  position: absolute;
  top: 10px;
  right: 25px;
  font-size: 35px;
  font-weight: bold;
}

.close:hover,
.close:focus {
  color: #999;
  text-decoration: none;
  cursor: pointer;
}

.mySlides {
  display: none;
}

.cursor {
  cursor: pointer;
}

/* Next & previous buttons */
.prev,
.next {
  cursor: pointer;
  position: absolute;
  top: 50%;
  width: auto;
  padding: 16px;
  margin-top: -50px;
  color: white;
  font-weight: bold;
  font-size: 20px;
  transition: 0.6s ease;
  border-radius: 0 3px 3px 0;
  user-select: none;
  -webkit-user-select: none;
}

/* Position the "next button" to the right */
.next {
  right: 0;
  border-radius: 3px 0 0 3px;
}

/* On hover, add a black background color with a little bit see-through */
.prev:hover,
.next:hover {
  background-color: rgba(0, 0, 0, 0.8);
}

/* Number text (1/3 etc) */
.numbertext {
  color: #f2f2f2;
  font-size: 12px;
  padding: 8px 12px;
  position: absolute;
  top: 0;
}

img {
  margin-bottom: -4px;
}

.caption-container {
  text-align: center;
  background-color: black;
  padding: 2px 16px;
  color: white;
}

.demo {
  opacity: 0.6;
}

.active,
.demo:hover {
  opacity: 1;
}

.hover-shadow {
  width: 100%;
  height: 100%;
}

img.hover-shadow {
  transition: 0.3s;
}

.hover-shadow:hover {
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
}


/* Small devices (landscape phones, 576px and up) */
@media (min-width: 576px) {
  /* TEMPLATE CONTAINER */
  .container {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    grid-template-areas:
      "header header header header header header header header"
      "main main main main main main main main"
      "footer footer footer footer footer footer footer footer";
  }

  main {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    grid-gap: 10px;
    grid-template-areas:
      "row row row row row row row row"
      "slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper"
      "img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow";
  }

  .row {
    display: grid;
    grid-template-columns: repeat(8, 1fr);
    grid-template-areas:
      ". . category-box category-box category-box category-box . ."
      ". . image-wrapper-1 image-wrapper-1 image-wrapper-1 image-wrapper-1 . ."
      ". . image-wrapper-2 image-wrapper-2 image-wrapper-2 image-wrapper-2 . ."
      ". . image-wrapper-3 image-wrapper-3 image-wrapper-3 image-wrapper-3 . .";
  }
  
  
  /* Medium devices (tablets, 768px and up) */
@media (min-width: 768px) {

  /* TEMPLATE CONTAINER */
  .container {
    display: grid;
    grid-gap: 25px;
    grid-template-columns: repeat(12, 1fr);
    grid-template-areas:
      "header header header header header header header header header header header header"
      "main main main main main main main main main main main main"
      "footer footer footer footer footer footer footer footer footer footer footer footer";
  }

  main {
    display: grid;
    grid-gap: 25px;
    grid-template-columns: repeat(12, 1fr);
    grid-template-areas:
      "row row row row row row row row row row row row"
      "slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper"
      "img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow";
  }

  .row {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    grid-gap: 25px;
    grid-template-areas:
      ". category-box category-box category-box category-box category-box image-wrapper-1 image-wrapper-1 image-wrapper-1 image-wrapper-1 image-wrapper-1 ."
      ". image-wrapper-2 image-wrapper-2 image-wrapper-2 image-wrapper-2 image-wrapper-2 image-wrapper-3 image-wrapper-3 image-wrapper-3 image-wrapper-3 image-wrapper-3 .";
  }
  
  .image-wrapper-1,
  .image-wrapper-2,
  .image-wrapper-3 {
    margin-bottom: 0;
  }
  
   .category-box {
    margin-top: 0;
    align-content: center;
    text-align: center;
  }
  
  /* Large devices (desktops, 992px and up) */
@media (min-width: 992px) {
  /* IMAGES */
  .card:hover {
    color: #fff;
    transform: translateY(-4px);
    box-shadow: 0 5px 20px 0 rgba(0, 0, 0, 0.2);
    transition-delay: 0;
  }
}

/* X-Large devices (large desktops, 1200px and up) */
@media (min-width: 1200px) {
  /* TEMPLATE CONTAINER */
  .container {
    display: grid;
    grid-gap: 25px;
    grid-template-columns: repeat(12, 1fr);
    grid-template-areas:
      "header header header header header header header header header header header header"
      "main main main main main main main main main main main main"
      "footer footer footer footer footer footer footer footer footer footer footer footer";
  }

  main {
    display: grid;
    grid-gap: 25px;
    grid-template-columns: repeat(12, 1fr);
    grid-template-areas:
      "row row row row row row row row row row row row"
      "slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper slug-wrapper"
      "img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow img-shadow";
  }

  .row {
    display: grid;
    grid-template-columns: repeat(12, 1fr);
    grid-gap: 25px;
    grid-template-areas: ". category-box category-box image-wrapper-1 image-wrapper-1 image-wrapper-2 image-wrapper-2 image-wrapper-3 image-wrapper-3 . . .";
  }
/* IMAGES */
  .card-headline {
    padding: 0;
  }

  .logo-link {
    width: 100%;
    height: 100%;
  }
<div class="row">
  <div class="category-box">
    <h2 class="box-headline">Inexorablez</h2>
  </div>

  <div class="image-wrapper-1">
    <img
      src="https://picsum.photos/id/6/360/260"
      onclick="openModal();currentSlide(1)"
      class="hover-shadow cursor"
    />
  </div>
  <div class="image-wrapper-2">
    <img
      src="https://picsum.photos/id/8/360/260"
      onclick="openModal();currentSlide(2)"
      class="hover-shadow cursor"
    />
  </div>
  <div class="image-wrapper-3">
    <img
      src="https://picsum.photos/id/10/360/260"
      onclick="openModal();currentSlide(3)"
      class="hover-shadow cursor"
    />
  </div>
</div>

<div id="myModal" class="modal">
  <span class="close cursor" onclick="closeModal()">&times;</span>
  <div class="modal-content">
    <div class="mySlides">
      <div class="numbertext">1 / 3</div>
      <img
        class="image-slide"
        src="https://picsum.photos/id/6/600/350"
      />
    </div>

    <div class="mySlides">
      <div class="numbertext">2 / 3</div>
      <img
        class="image-slide"
        src="https://picsum.photos/id/8/600/350"
      />
    </div>

    <div class="mySlides">
      <div class="numbertext">3 / 3</div>
      <img
        class="image-slide"
        src="https://picsum.photos/id/10/600/350"
      />
    </div>

    <a class="prev" onclick="plusSlides(-1)">&#10094;</a>
    <a class="next" onclick="plusSlides(1)">&#10095;</a>

    <div class="caption-container">
      <p id="caption"></p>
    </div>

    <div class="column">
      <img
        class="demo cursor"
        src="https://picsum.photos/id/6/600/350"
        onclick="currentSlide(1)"
        alt="Inerorablez #1"
      />
    </div>
    <div class="column">
      <img
        class="demo cursor"
        src="https://picsum.photos/id/8/600/350"
        onclick="currentSlide(2)"
        alt="Inerorablez #2"
      />
    </div>
    <div class="column">
      <img
        class="demo cursor"
        src="https://picsum.photos/id/10/600/350"
        onclick="currentSlide(3)"
        alt="Inerorablez #3"
      />
    </div>
  </div>
</div>

Would be nice, if someone can help me to translate it.

How can I properly stop an Incisor particle system

I am working with Incisor particle systems, and I’m encountering an issue with the stop() function on the playback controller. When I call stop(), it immediately halts the particle effect, but I want the emitter to stop emitting new particles while allowing the existing particles to finish their current movement.

From what I can gather in the playback controller API documentation, there doesn’t seem to be an option to achieve this behavior. Does anyone know if there is a way to stop the emitter but let the current particles continue until they have completed their animations?

Here is a simplified version of the code I am using:

class ProjectMain {
    init() {
        let particles = nc.addParticleSystem(nc.particleSystemDefinitions.Fountain, nc.mainScene, "MyParticles");
        particles.playbackController.play();

        nc.waitThen(1, this, "stopParticles");

        this.stopParticles = function() {
            particles.playbackController.stop(); // This stops the particles immediately
        }
    }
}

I would appreciate any advice on how to achieve this behavior or work around it.

Material UI Date Picker Typescript, Property ‘target’ does not exist on type ‘Dayjs’

I am implementing a material UI datepicker into a TypeScript application. The code functions properly however I am getting a warning in VS code regarding the e.target.value.toISUString() the error says “Property ‘target’ does not exist on type ‘Dayjs'”

I am not exactly sure why I get this message as I am only looking for ‘target’ on the onChange event and it does exist.

            <MuiDatePicker
              id="reminderDate"
              name="reminderDate"
              label="Reminder Date"
              fullWidth={true}
              value={reminderDate}
              onChange={(e) => setReminderDate(e.target.value.toISOString())}
              views={["month", "year", "day"]}
              disablePast={true}
              // onClose={onClose}
              sx={{ paddingBottom: "12px" }}
            />

I have tried tweaking the naming conventions (using event instead of e) but that didn’t fix anything.

getting an ERROR #95313 HTML.COMPILATION when running gatsby build

I’m working on my first website using gatsbyjs and I’m running into an issue when I run gatsby build. My local is running perfectly, but build is giving me a ERROR #95313 I’ve tried a number of different fixes, but haven’t had luck. From what I’ve found is it’s something involving how my images are being used, but I haven’t found a fix for it yet.

This is the full site as it stands now https://github.com/lucisiousluke/the-shark-fighter

this is the full error I’m getting


Truncated page data information for the failed page "/blog/hubble-by-insightsoftware/": {
  "errors": {},
  "path": "/blog/hubble-by-insightsoftware/",
  "slicesMap": {},
  "pageContext": {
    "id": "9c28efd6-8a48-5591-8aeb-68bff3deb4aa",
    "frontmatter__slug": "hubble-by-insightsoftware",
    "__params": {
      "frontmatter__slug": "hubble-by-insightsoftware"
    },
    "frontmatter": {
      "title": "Hubble",
      "date": "2021-07-23",
      "slug": "hubble-by-insightsoftware",
      "hero_image": "./hubble-desktop.webp",
      "hero_image_thumbnail": "./process-background.webp",
      "hero_image_alt": "shirts, notbooks, and other items from insightsoftware.com",
      "hero_image_credit_text": "Luke Hinrichs"
    }
  }
}

failed Building static HTML for pages - 2.625s

 ERROR #95313  HTML.COMPILATION

Building static HTML failed for path "/blog/hubble-by-insightsoftware/"```

Issue with P5.remove() p5.erase() and createX

I am working on a visualization app and since it has multiple files I decided to give ES6 modules a go, so to have a more organized, modular code. The structure is:

  1. sketch.js – type module -, which creates an instance of P5 and calls/draws each of the visualizations on the canvas upon a button click

  2. individual module files, each exporting a single visualization Constructor class to load(data), setup(p5) and draw(p5) the visualization and then reset() it when another one is called.

Everything works as expected, except for the removal part. For example, one of the Classes creates a select dropdown as follows:

setup(p5) {
    this.selector = p5.createSelect();

 // a for loop adds options to the select with:
    for (let i = 0; i < optionsArray.length; i++){
        this.selector.option('optionsArray[i]')
     }
}

so far so good, but when another visualization is called, this selector should disappear from the screen. I tried:

// Idea 1
reset() {
     //if some condition:
     this.selector.remove()
 }
//Idea 2
reset(p5) {
//if some condition:
    p5.erase();
      p5.rect(100, 100, 40)// Test rect. It gets deleted, not the selector
      this.selector
    p5.noErase();
}
//Idea 3
this.reset(p5) {
//if some condition:
    p5.clear()
}

I also tried calling the selector via DOM getElementById() and then using remove() on that variable, but none of this seems to work. I’ve checked that the constructor’s reset method is being called correctly, it is the built-in remove() or p5 remove() / erase() and clear() what’s not working. Any ideas?

Modal dialog not appearing – Issue with JavaScript

I’m using an ASP.NET WebForms application using ASPX pages.

After tariff names are changed and the user clicks the Save button, a modal dialog should appear, prompting the user to enter a ticket number (required) and a comment (optional).

While editing, text-changed events are fired. Since the GridView (for the tariff names) is dynamically populated, a hidden TextBox is used to capture changes. Modified tariff names are stored in a dictionary, which is then saved in the session.

Protected Sub TariffNameChangesTextbox_OnTextChanged(sender As Object, e As EventArgs) Handles TariffNameChangesTextbox.TextChanged
    Dim txtBox As TextBox = TryCast(sender, TextBox)
    If txtBox IsNot Nothing Then

        Dim textboxNr As Integer = DirectCast(txtBox.DataItemContainer, System.Web.UI.WebControls.GridViewRow).RowIndex
        Dim textboxId As String = txtBox.ID
        Dim keyForDict As String = $"{textboxId}_{textboxNr.ToString().PadLeft(3, "0"c)}"

        Dim dictionaryChangedTariffNames As Dictionary(Of String, String) = TryCast(Session("ChangedTariffNames"), Dictionary(Of String, String))
        If dictionaryChangedTariffNames Is Nothing Then
            dictionaryChangedTariffNames = New Dictionary(Of String, String)()
            dictionaryChangedTariffNames.Add(keyForDict, txtBox.Text)
        ElseIf dictionaryChangedTariffNames.Count > 0 AndAlso dictionaryChangedTariffNames.ContainsKey(keyForDict) Then
            dictionaryChangedTariffNames(keyForDict) = txtBox.Text
        Else
            dictionaryChangedTariffNames.Add(keyForDict, txtBox.Text)
        End If

        Session("ChangedTariffNames") = dictionaryChangedTariffNames
    End If
End Sub

Unfortunately, the Save button is global across all pages and cannot be modified. However, it calls a method that only executes saving-stuff for the intended webpage but is in MasterPage.master.vb.
I attempt to retrieve the dictionary from the session. I wanted to register a startup script for the modal dialog but the script does not execute, preventing the modal from appearing. The script file is correctly located in the project folder.

Dim dictionaryChangedTariffNames As Dictionary(Of String, String) = TryCast(Session("ChangedTariffNames"), Dictionary(Of String, String))
If dictionaryChangedTariffNames IsNot Nothing AndAlso dictionaryChangedTariffNames.Count > 0 Then
    Dim cs As ClientScriptManager = Page.ClientScript
    If Not cs.IsStartupScriptRegistered(Me.GetType(), "ModalDialogForTariffRename") Then
        'Page.ClientScript.RegisterStartupScript(Me.GetType(), "ModalDialogForTariffRename", "handleSaveButtonClick();", True)
        Page.ClientScript.RegisterStartupScript(Me.GetType(), "ModalDialogForTariffRename", "$(""Content1"").handleSaveButtonClick();", True)
    End If
    dictionaryChangedTariffNames.Clear()
End If
Session("ChangedTariffNames") = dictionaryChangedTariffNames

I have written the css and js file in the head of the MainPage.

    <link rel="stylesheet" type="text/css" href="css/ModalDialogForTariffRename.css" />
    <script type="text/javascript" src="js/ModalDialogForTariffRename.js"></script>

In the page that displays the names, I write this dialog:

    <!-- Modal Dialog für Tarifumbenennung -->
 <div id="tariffRenameModal" class="tariff-name-modaldialog">
    <div class="tariff-name-modaldialog-content">
    <span class="modaldialog-close-btn">&times;</span>
    <h2>Tarif umbenennen – Bitte Ticketnummer und Kommentar angeben</h2>
    
    <label for="ticketNumber">Ticketnummer (Pflichtfeld):</label>
    <input type="text" id="ticketNumber" name="ticketNumber" required />
    <br />

    <label for="comment">Kommentar (optional):</label>
    <textarea id="comment" name="comment" rows="4" cols="50"></textarea>
    <br />

    <button id="saveTicketButton" onclick="saveTariffRenameTicket()">Speichern</button>
    <button onclick="closeTariffRenameModal()">Abbrechen</button>
</div>

The css file (ModalDialogForTariffRename.css) looks like this:

/* Tarifumbenennung Modaldialog */
.tariff-name-modaldialog {
    display: none;
    position: fixed;
    z-index: 1;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.4);
    overflow: auto;
}

/* Modaldialog-Inhalt */
.tariff-name-modaldialog-content {
    background-color: #fff;
    margin: 15% auto;
    padding: 20px;
    border: 1px solid #888;
    width: 60%;
    max-width: 500px;
}

.modaldialog-close-btn {
    color: #aaa;
    float: right;
    font-size: 28px;
    font-weight: bold;
}

    .modaldialog-close-btn:hover,
    .modaldialog-close-btn:focus {
        color: black;
        text-decoration: none;
        cursor: pointer;
    }

.tariff-name-modaldialog input[type="text"],
.tariff-name-modaldialog textarea {
    width: 100%;
    padding: 10px;
    margin: 5px 0;
    border: 1px solid #ccc;
    border-radius: 5px;
}

.tariff-name-modaldialog textarea {
    resize: vertical;
    min-height: 100px;
}


.tariff-name-modaldialog button {
    background-color: #4CAF50; /* green for saving */
    color: white;
    padding: 10px 20px;
    margin-top: 10px;
    border: none;
    cursor: pointer;
    border-radius: 5px;
}

    .tariff-name-modaldialog button:hover {
        background-color: #45a049; /* darker green when hovering */
    }

    /* Abbrechen-Button */
    .tariff-name-modaldialog button:last-child {
        background-color: #f44336; /* red for cancel */
    }

        .tariff-name-modaldialog button:last-child:hover {
            background-color: #e53935; /* darker red when hovering */
        }

The js file (ModalDialogForTariffRename.js) looks like this:

function handleSaveButtonClick()
{
    if (window.location.pathname.includes("tarifnamen.aspx")) {
        openModal();
    }
    else
    {
        console.log("Button wird auf einer anderen Seite aufgerufen, Modaldialog wird nicht geöffnet.");
    }
}

function openModal() {
    var modal = document.getElementById('myModal');
    modal.style.display = 'block';
}

function showModal()
{
    var modal = document.getElementById("myModal");
    modal.style.display = "block";
}

function closeModal()
{
    var modal = document.getElementById("myModal");
    modal.style.display = "none";
}

function saveChanges()
{
    var ticketNumber = document.getElementById("ticketNumber").value;
    var comment = document.getElementById("comment").value;

    if (!ticketNumber)
    {
        alert("Bitte geben Sie eine Ticketnummer ein.");
        return false; // avoids sending when no ticket number is present
    }

    /*alert("Änderungen wurden gespeichert!");*/
    closeModal();
    return true;
}

Angular – How to unit test an RxJS timer used with AsyncPipe

I am unsuccessful in writing a unit test for an RxJS timer that is displayed in the template with an AsyncPipe.

Component:

import { Component } from '@angular/core';
import { AsyncPipe, NgIf } from '@angular/common';
import { map, timer } from 'rxjs';

@Component({
  selector: 'app-root',
  template: `
    Timer should fire after {{ delay }} seconds:
    <ng-container *ngIf="timer$ | async as timer; else waiting">Timer fired {{ timer }} times</ng-container>
    <ng-template #waiting>Waiting for Timer…</ng-template>
  `,
  imports: [AsyncPipe, NgIf],
})
export class AppComponent {
  delay = 3;
  timer$ = timer(this.delay * 1000, 1000).pipe(map((n) => n + 1));
}

Test:

import { AppComponent } from './main';
import {
  ComponentFixture,
  TestBed,
} from '@angular/core/testing';
import { firstValueFrom } from 'rxjs';

describe('AppComponent', () => {
  let component: AppComponent;
  let fixture: ComponentFixture<AppComponent>;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      imports: [AppComponent],
    }).compileComponents();

    fixture = TestBed.createComponent(AppComponent);
    component = fixture.componentInstance;
    fixture.detectChanges();
  });

  it('should initially render waiting', () => {
    expect(fixture.nativeElement.textContent).toContain('Waiting'); //passes
  });

  it('should emit timer', async () => {
    expect(await firstValueFrom(component.timer$)).toBe(1); //passes
  });

  it('should show "Timer fired"', () => {
    jest.useFakeTimers();
    jest.advanceTimersByTime(5000);

    expect(fixture.nativeElement.textContent).toContain('Timer fired'); //fails
  });
});

Stackblitz: https://stackblitz.com/edit/angular-rxjs-timer-asyncpipe-unit-test?file=src%2Fmain.ts,src%2Ftest.spec.ts

The most similar question here on StackOverflow is this one: Angular testing async pipe does not trigger the observable
Unfortunately, the solution described in the most upvoted answer does not solve the problem.

How to create a recursive JS function to keep taking curried arguments until the will? [closed]

I would like to create a multiply function like:

multiplyBy(2) => 2
multiplyBy(2)(3) => 6
multiplyBy(2)(3)(6) => 36
multiplyBy(2)(3)...(N) => 2*3*...N
...

Code I wrote:

function multiplyBy(x) {
  let product = x;
  function multiply(y) {
    product *= y;
    return multiply;
  }
  
  multiply.valueOf = function() {
    return product;
  };
  
  return multiply;
}

How do I achieve this?

EDIT:
I was being asked this on an interview guys and I tried doing valueOf thing but failed. The interviewer asked how much you want to rate yourself in JS I said 5 and then he asks this. I did currying like this: example(2)(3)() but I am not able to make example(2)(3)

Window in not defined using Playwright and custom web components

I have a component library that exports a lot of custom web components. The components are registered using:

window.customElements.define('my-component', MyComponent);

When I try to run playwright tests I get the following error:

ReferenceError: window is not defined

 at ../../../../component-lib/packages/my-component/src/my-component.ts:26

 window.customElements.define('my-component', MyComponent);

I tried to override window.customElements.define in a global-fixture that runs before each test but it didn’t work:

window.customElements.define = (tagName: string, constructor: CustomElementConstructor) => {
        page.evaluate(() => {
          window.customElements.define(tagName, constructor);
        })
      }

How to invert regex in JavaScript? [duplicate]

I need to invert a JS regex expression /([A-Z]|[a-z]|[0-9]|[@.])/. Thus, my string must contains only English letters, numbers and some special characters.

I am going to use this regex in the following context to validate a string:

str = str.replace(/([A-Z]|[a-z]|[0-9]|[@.])/, '')

I looked through this answer and tried /^(?!.*([A-Z]|[a-z]|[0-9]|[@.]))/, but it didn’t work.

How to Determine if the last text in Ink.js is from a user option?

I’m using Ink.js to create an interactive story, and I want to style the paragraphs differently based on whether the last text displayed is from a user option or not.
I need to determine if the last piece of text that was displayed to the user was a choice they made. If it was, I want to apply a specific style to that paragraph. If it wasn’t, I want to apply a different style.
A simple comparison does not pass the test, since the displayed option may differ from the one that is selected (for example, be shorter).

InkJS repo: https://github.com/y-lohse/inkjs
https://github.com/inkle/ink

Best option will be compare ID (eg. parent==child, lastOption==currentItem), but I don’t know where to get it. I get only _componentString but cant find it in current story element.

this.storyContent.currentChoices[optionIndex].targetPath._componentsString