Yelp api bug – Building a website with flask, js, and html – CS50x project

first post here.
I’m existed to join and be a part of this community.
I’m finishing now the final project of Harvard CS50x project.

I’ve created a website which uses Yelp Fusion API for getting a Itineraries in Colorado.
In Itineraries info page, when I pick CO city and itinerary category. Only food results displayed in the map. Even though I pick something else.

I’ll attach here the git repo, app.py and related html pages.

Related part of app.py:

# Web page that ask from user to pick a Colorado city and type of itinerary that he looking for
@app.route('/itinerary', methods=["GET", "POST"])
def itinerary():
    # Get a list of all zip codes in Colorado
    co_zipcodes = zipcodes.filter_by(state='CO')

    # Extract the list of city names from the zip codes
    cities = [zipcode['city'] for zipcode in co_zipcodes]

    # Remove duplicates and sort the list
    cities = sorted(set(cities))

    # Creating a list of all itinerary types
    itinerary_aliases = {
        "Accommodations": "hotels",
        "Adult": "adult",
        "Amusements": "amusementparks, aquariums, arcades",
        "Architecture": "landmarks, monuments",
        "Cultural": "culturalcenter",
        "Historic": "museums",
        "Industrial facilities": "factories",
        "Natural": "parks",
        "Religion": "religiousorgs",
        "Sport": "active",
        "Banks": "banks",
        "Food": "food",
        "Shops": "shopping",
        "Transport": "transport"
    }
    
    if request.method == 'POST':
        # Get the name of the city from the user input
        city = request.form.get('city')
        city = city + " Colorado"

        # Get the type of itinerary
        category_alias = request.form.get('itinerary_type')


        url = f"https://api.yelp.com/v3/businesses/search?location={city}&radius=30000&categories={category_alias}&sort_by=best_match&limit=20"

        headers = {
            "accept": "application/json",
            "Authorization": "Bearer pUf2JzXR1iDFAvIGBsmPvQAyVdXBQCodSnID9Z5sT59BcRYkkWvg_VoXZsfeo0Nj8odHJ1lJYcr6h0AwURBOVqRI-SDMTY5iks0_CRpHznpFz-MXz_Xg3PmHpOQ2ZHYx"
        }

        response = requests.get(url, headers=headers)
        data = response.json()

        return render_template('itinerary_r.html', data=data, city=city)


    else:
        return render_template('itinerary.html', cities=cities, itinerary_aliases=itinerary_aliases)

itinerary.html:

{% extends "layout.html" %}

{% block title %}
    Search for city itineraries
{% endblock %}

{% block main %}
<div class="instructions">
    <h3><p>Please select one of the Colorado's cities and then select a type of itinerary that you are looking for.</p></h3>
</div>
<form action="/itinerary" method="post">
    <div class="mb-3">
        <select name="city" id="city">
            <option value="" disabled selected>Select a city</option>
            {% for city in cities %}
            <option value="{{ city }}">{{ city }}</option>
            {% endfor %}
        </select>
    </div>

    <div class="mb-3">
        <select name="itinerary_type" id="itinerary_type">
            <option value="" disabled selected>Select an itinerary</option>
            {% for itinerary_aliases in itinerary_aliases %}
            <option value="{{ itinerary_aliases }}">{{ itinerary_aliases }}</option>
            {% endfor %}
        </select>
    </div>
    <button class="btn btn-primary" type="submit">Submit</button>
</form>

{% endblock %}

itinerary_r.html

{% extends "layout.html" %}

{% block title %}
    Itineraries search result
{% endblock %}

{% block head %}
    <script src='https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.js'></script>
    <link href='https://api.mapbox.com/mapbox-gl-js/v2.5.1/mapbox-gl.css' rel='stylesheet' />
    <style>
        #map {
            height: 500px;
            width: 100%;
        }
    </style>
{% endblock %}

{% block main %}

<div id="map" style="height: 500px; width: 800px; margin: auto;"></div>
<script>
    var data = {{ data|tojson|safe }};
    var city = "{{ city }}";

    // Create the map and set the view to the center of the city
    var map = L.map('map').setView([data.region.center.latitude, data.region.center.longitude], 11);

    // Add the base map layer
    L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
        attribution: 'Map data &copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors',
        maxZoom: 18
    }).addTo(map);

    // Loop through the search results and add a marker for each business
    for (var i = 0; i < data.businesses.length; i++) {
        var business = data.businesses[i];
        var marker = L.marker([business.coordinates.latitude, business.coordinates.longitude]).addTo(map);
        var popup = "<b>" + business.name + "</b><br>" + business.location.address1 + "<br>" + business.location.city + ", " + business.location.state + " " + business.location.zip_code;
        marker.bindPopup(popup);
    }

    // Add a marker for the center of the city
    var cityMarker = L.marker([data.region.center.latitude, data.region.center.longitude]).addTo(map);
    cityMarker.bindPopup("<b>" + city + "</b>");
</script>

{% endblock %}

Any help would be greatly appreciated.
Boris.

Did the testing through Yelp fusion API website

Everything works fine, probably there is something wrong in app.py function.

How do I use a variable to find a json value?

I am programming a small program that loads a specific localStorage variable. Here’s my code:

function loadInfo(index) {
  var div = document.getElementById('report');
  if (localStorage.getItem("stardata") != null) {
    // read the data saved in localStorage
    var data = localStorage.getItem("stardata");
    // turn the data from JSON into an array.
    var rdata = JSON.parse(data);
    // stringify data
    var afdata = JSON.stringify(rdata);
    var nfdata = rdata[index];
    var adfdata = JSON.stringify(nfdata);
    var aafdata = JSON.parse(adfdata);
    var keys = Object.keys(aafdata);
    var fdata = aafdata[keys];
    div.innerHTML += fdata;
    return;
  }
}

This takes in the data and converts it into strings a few times to get just 1 value. But for some reason, whenever I try to get the value for keys, it does not output anything. I am coding this on a school computer so I can’t access the console. Any help?

How can a object in typescript get one or more unassigned property?

type ObjectDescriptor<D, M> = {
  data?: D;
  methods?: M & ThisType<D & M>; // Type of 'this' in methods is D & M
};

function makeObject<D, M>(desc: ObjectDescriptor<D, M>): D & M {
  let data: object = desc.data || {};
  let methods: object = desc.methods || {};
  return { ...data, ...methods } as D & M;
}

let obj123 = makeObject({
  data: {
    x: 20,
    y: 50,
    moveBy(dx: number, dy: number) {
      this.x += dx; // Strongly typed this
      this.y += dy; // Strongly typed this
    },
  },
  methods: { x: 0, y: 0 },
});

console.dir(obj123);

const nemObj = { method: obj123.moveBy };

nemObj.method(5, 5);
# console.dir(nemObj); // here newObj also getting two extra property x and y but how????

I created an Object called obj123 using the function called makeObject() and then I am assigning the method of this object to another Object called nemObj but this nemObj also getting the x and y property from the previous object. How is this happening?

place null values at last while sorting the data

I have this data which i’m displaying in the table

[
    {
        "ID": 9,
        "Status": "Pending",
    },
    {
        "ID": 8,
        "Status": null,
    },
    {
        "ID": 7,
        "Status": "Pending",
    },
    {
        "ID": 10,
        "Status": null,
    },
    {
        "ID": 18,
        "Status": "Completed",
    },
    {
        "ID": 17,
        "Status": "In Progress",
    }
]

Sort Method:

Ascending order :

 this.List.sort((a, b) => a[columnname] < b[columnname] ? 1 : a[columnname] > b[columnname]` ? -1 : 0);

Descending order :

 this.List.sort((a, b) => a[columnname] > b[columnname] ? 1 : a[columnname] < b[columnname] ? -1 : 0);

and using sort function to sort the data in table issue is when i sort it, i want to place null values at the last.

How to sort the dates from different objects with multiples dates inside of an array of object

I have this array of objects that I gather from an API:

  const schedules = [
    {
        "_id": "6436b48b875967d0bea245b4",
        "service": "64246dc9a2d61593d103c749",
        "scheduledDates": [
            {
                "date": "2023-04-17T18:00:00.000Z",
                "user": "643701f7e5f61e6760f4d1f3"
            },
            {
                "date": "2023-04-12T18:00:00.000Z",
                "user": "643701f7e5f61e6760f4d1f3"
            }
        ]
    },
    {
        "_id": "6436b48b875967d0bea245b5",
        "service": "64246dc9a2d61593d103c749",
        "scheduledDates": [
            {
                "date": "2023-04-19T10:30:00.000Z",
                "user": "64217a8dcc69c5fa48a5b484"
            },
            {
                "date": "2023-04-12T18:00:00.000Z",
                "user": "6414be936b0bbf2bd8fa964f"
            }
        ]
    },
]

How can I sort this array by the date inside schedulesDates array, keep the same array of objects structure, looking like this array:

const newSchedules = [
  {
      "_id": "6436b48b875967d0bea245b4",
      "service": "64246dc9a2d61593d103c749",
      "scheduledDates": [
          {
              "date": "2023-04-12T18:00:00.000Z",
              "user": "643701f7e5f61e6760f4d1f3"
          }
      ]
  },
  {
    "_id": "6436b48b875967d0bea245b5",
    "service": "64246dc9a2d61593d103c749",
    "scheduledDates": [
        {
            "date": "2023-04-12T18:00:00.000Z",
            "user": "6414be936b0bbf2bd8fa964f"
        }
    ]
},
  {
      "_id": "6436b48b875967d0bea245b4",
      "service": "64246dc9a2d61593d103c749",
      "scheduledDates": [
          {
              "date": "2023-04-17T18:00:00.000Z",
              "user": "643701f7e5f61e6760f4d1f3"
          }
      ]
  },
  {
      "_id": "6436b48b875967d0bea245b5",
      "service": "64246dc9a2d61593d103c749",
      "scheduledDates": [
          {
              "date": "2023-04-19T10:30:00.000Z",
              "user": "64217a8dcc69c5fa48a5b484"
          }
      ]
  },
]

I have this function to handle the sort:

  const handleSort = () => {
    if (sortOrder === "asc") {
      setSortOrder("desc");
      return schedules.map((schedule) =>
      schedule.scheduledDates.sort((a, b) => new Date(a.date) - new Date(b.date))
      );
    } else {
      setSortOrder("asc");
      return schedules.map((schedule) =>
      schedule.scheduledDates.sort((a, b) => new Date(b.date) - new Date(a.date))
    }
  };

But it sorts only within each array of scheduledDates. Thanks for helping.

Update user details using React and firebase

Using react and firebase, I want to update my users details using their document id.

the user variable is attempting to find an object in an array of objects (props.users) that matches a certain condition (element.id === props.docID).

The docRef variable is creating a reference to a Firestore document by providing the collection path (“users”) and the document ID (props.docID)

Can anyone help me fix the issue. and thanks.

import React, { useEffect, useState } from "react";
import { doc, updateDoc } from "firebase/firestore";
import { db } from "../../Firebase.js";

import TextInput from "../../shared/components/UIElements/TextInput";

const ViewUserDetails = (props) => {
  const user = props.users?.find((element) => element.id === props.docID);
  const [updatedUser, setUpdatedUser] = useState({});
  const docRef = doc(db, "users", props.docID);

  const handleInputChange = (event) => {
    const { name, value } = event.target;
    setUpdatedUser({ ...updatedUser, [name]: value });
  };

  const updateUserDetails = () => {
    updateDoc(docRef, updatedUser)
      .then((docRef) => {
        console.log("updated");
      })
      .catch((error) => {
        console.log(error);
      });
  };
return (
    <div className="overflow-hidden bg-white shadow sm:rounded-lg">
      <div className="px-4 py-5 sm:px-6">
        <div className=" inline-flex w-full justify-between">
          <h3 className="text-base font-semibold leading-6 text-gray-900 justify-start">
            User details
          </h3>
          <div className="inline-flex">
            <p className="my-auto mr-3 max-w-2xl text-sm text-gray-500">
              Status
            </p>
            <div className={`badge badge-${statusColor} badge-outline m-auto`}>
              active
            </div>
          </div>
        </div>
        <p className="mt-1 max-w-2xl text-sm text-gray-500">Account details.</p>
      </div>

      <div className="border-t border-gray-200 grid grid-cols-1 md:grid-cols-2 px-8 pb-4 gap-y-3">
        <div>
          <TextInput label="First Name">{user?.firstName}</TextInput>
        </div>
        <div>
          <TextInput label="Last Name">{user?.lastName}</TextInput>
        </div>
        <div>
          <TextInput label="Email">{user?.email}</TextInput>
        </div>
        <div>
          <TextInput label="Company">{user?.company}</TextInput>
        </div>
        <div>
          <TextInput label="Job title">{user?.jobTitle}</TextInput>
        </div>
        <div>
          <TextInput label="Country">{user?.country}</TextInput>
        </div>
        <div>
          <TextInput label="Phone">{user?.phone}</TextInput>
        </div>
        <div>
          <div className="form-control w-full max-w-xs">
            <label className="justify-start label">
              <span className="label-text">Role</span>
            </label>
            <select
              className="select select-primary select-bordered"
              onChange={
                props.onChange
                  ? (e) => {
                      props.onChange(e.target.value);
                    }
                  : null
              }
            >
              <option disabled selected>
                {user?.role}
              </option>
              <option>admin</option>
              <option>client</option>
              <option>human resources</option>
              <option>webmaster</option>
            </select>
          </div>
        </div>
        <button className="btn btn-primary" onClick={updateUserDetails}>
          Save
        </button>
      </div>
    </div>
  );
};

export default ViewUserDetails;

so when a user selects an option i want that option to be the Id from and array of objects

i want the functionality to be when i select and option i want that option to contain one of the ID’s from the array of objects

 <label for="selectTechCompany">Tech Company</label>
              <select
                class="form-select"
                id="selectTechCompany"
                aria-label="Default select example"
              >
                <option selected>Select</option>
                <option value="1">One</option>
                <option value="2">Two</option>
                <option value="3">three</option>
                <option value="4">four</option>
                <option value="5">five</option>
              </select>
            </div>

*** below is the array of objects
0
: 
{id: 84114, slug: '54146064986150486516464', statusId: 'Active', name: 'techCompany5', headline: 'headline5', …}
1
: 
{id: 84113, slug: '5414606498615086516464', statusId: 'Active', name: 'techCompany4', headline: 'headline4', …}
2
: 
{id: 84112, slug: '541460649861508651646', statusId: 'Active', name: 'techCompany3', headline: 'headline3', …}
3
: 
{id: 84111, slug: '541460649861508651645', statusId: 'Active', name: 'techCompany2', headline: 'headline2', …}
4
: 
{id: 84110, slug: '30110974915602206', statusId: 'Active', name: 'techCompany1', headline: 'headline1', …}
5
: 
{id: 84108, slug: '130166580319', statusId: 'Active', name: 'techCompany1', headline: 'headline1', …}
6
: 
{id: 84104, slug: '0916503104153049', statusId: 'Active', name: 'techCompany1', headline: 'headline1', …}
7
: 
{id: 84100, slug: '79841562130', statusId: 'Active', name: 'techCompany1', headline: 'headline1', …}
8
: 
{id: 84098, slug: 'slug1', statusId: 'Active', name: 'techCompany1', headline: 'headline1', …}

i know that ill have to use jquery i just dont know what the end results supposed to look like

how to access json object that is in a form of array in javascript

I am trying to access merchant name in the following json:

[{“merchant_id”:”90151″,”merchant_name”:”Wimpy”}]

the above json formt is returned by an api, I tried access merchant_name value but all I got was none

below is what I tried:

fetch("http://127.0.0.1:8000/vcl/retrieve/"+url, {
          method: "GET",
          headers: {
            "Content-Type": "application/json"
          },
        }).then((res) => res.json()).then((response) => {  
          console.log (response);
          const data = JSON.stringify(response);
          const info = JSON.parse(data);
          var merchant_name = info.merchant_name;
          console.log(merchant_name);
        })

all I am getting is undefined

Finding out order of properties for a given object

A Json web service returns an object like { "id": 12, "name": "xxx", "age": 12 }. How do I find out the order of the property keys or retrieve each property in the shown sequence?

I need to copy the properties into an array in the same order. This is for a generic web service, so I don’t know in advance what the properties will be in the object”.

TypeORM: How to always format database value in where condition for strings

I would like to use a generic method “exist” given by TypeORM to verify if name is already inserted in database.

My probleme is that I can’t know if name have been inserted in Uppercase or Lowercase and so I can face some false validation and end up inserting twice the same name.

I would like TypeORM and PostgreSQL to always put in lower case the database value (here name) while doing the WHERE condition : WHERE “myname” = valueOfDatabase

Why is my element disappearing for no reason?

Tthe plus sign is placed inside .pokemon-list as a ::before, and I need .pokemon-list to have overflow-y: scroll in order to be able to scroll through all the pokemons. However, the + is still getting hidden as if I had set overflow-x: hidden which I didn’t.
Any idea of what’s happening ?

don’t pay attention to the small images not displaying, they are stored locally

const url = "https://pokeapi.co/api/v2/pokemon/";

const sprite3dElement = document.querySelector(".pokemon-3Dmodel img");

const pokemonList = document.querySelector(".pokemon-list");
const shinyButton = document.querySelector(".shiny-button");

const premierPokemon = 1;
const dernierPokemon = 151;
const nbPokemon = dernierPokemon - premierPokemon + 1;

window.addEventListener("load", getPokedata);

function getPokedata() {
  const pokemonData = []; // array to store each Pokemon's data

  for (let i = premierPokemon; i <= dernierPokemon; i++) {
    const finalUrl = url + i;
    fetch(finalUrl)
      .then((response) => response.json())
      .then((data) => {
        pokemonData[i - premierPokemon + 1] = data; // store the data for each Pokemon in the correct order
      })
      .then(() => {
        if (pokemonData.length === nbPokemon + 1) {
          // if we have fetched all the Pokemon data, generate the cards in the correct order
          pokemonData.forEach((data) => {
            generateCard(data);
          });
          betterPokemonCards();
          toggleShiny();
        }
      });
  }
}

function generateCard(data) {
  console.log(data);
  const dex_number = data.id;
  const name = data.name;
  const sprite3D = data.sprites.other["official-artwork"].front_default;
  const sprite3DShiny = data.sprites.other["official-artwork"].front_shiny;
  const sprite2D = data.sprites.versions["generation-viii"].icons.front_default;

  pokemonList.innerHTML += ` <li class="pokemon${
    dex_number == dernierPokemon ? " pokemon-active" : ""
  }" data-sprite3D="${sprite3D}" data-shiny3D="${sprite3DShiny}" data-id="${dex_number}">
  <div>
  <div class="pokemon__sprite">
  <img src="${sprite2D}" alt="sprite">
  </div>
  <p class="pokemon__num">No. <span class="pokemon__num--field">${dex_number}</span></p>
  </div>
  <p class="pokemon__name">${name}</p>
  <div class="pokeball">
  <img src="images/pokeball.png" alt="pokeball">
  </div>
  </li>
  `;
  sprite3dElement.src = sprite3D;
}

function betterPokemonCards() {
  let pokemons = document.querySelectorAll(".pokemon");

  //adds one or two 0 to the dex number if it is less than 10 or 100
  pokemons.forEach((pokemon) => {
    let dex_entry =
      pokemon.firstElementChild.lastElementChild.lastElementChild.innerText;
    if (dex_entry.length == 1) {
      pokemon.firstElementChild.lastElementChild.lastElementChild.innerText =
        "00" + dex_entry;
    } else if (dex_entry.length == 2) {
      pokemon.firstElementChild.lastElementChild.lastElementChild.innerText =
        "0" + dex_entry;
    }
    //adds an event listener to each pokemon so that when you click on it, it adds the class pokemon-active
    pokemon.addEventListener("click", () => {
      sprite3dElement.src = pokemon.getAttribute("data-sprite3D");
      pokemons.forEach((pokemon) => {
        pokemon.classList.remove("pokemon-active");
      });
      pokemon.classList.add("pokemon-active");
    });
  });
}
:root {
  --attachment: fixed;
  --degrees: 115deg;
  --colorStop-1: 50%;
  --colorStop-2: calc(var(--colorStop-1) + 100px);
}
*,
*::before,
*::after {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
}
img {
  width: 100%;
}
body {
  font-family: "Noto Sans KR", sans-serif;
  background-color: #fff;
  color: #282828;
  background-image: linear-gradient(
      var(--degrees),
      transparent var(--colorStop-1),
      #fe4d3e var(--colorStop-1) var(--colorStop-2),
      #fa7246 var(--colorStop-2)
    ),
    radial-gradient(
      circle at 20% 70%,
      rgb(239 208 234 / 0.9),
      rgb(234 201 242 / 0.6),
      rgb(231 238 197 / 0.6),
      rgb(205 240 219 / 0.6),
      rgb(231 237 245 / 0.6)
    );
  background-attachment: var(--attachment);
  min-height: 100vh;
  overflow: hidden;
}
.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  background-image: linear-gradient(
    115deg,
    rgba(50 0 0 / 0.15) var(--colorStop-1),
    rgb(53 53 53) var(--colorStop-1)
  );
  background-attachment: var(--attachment);
  margin-top: 10px;
}
.header h1 {
  font-size: 2rem;
  padding-left: 1rem;
}
.header p {
  font-size: 1.5rem;
  color: white;
  padding-right: 13%;
}
.pokedex {
  position: relative;
  display: flex;
  justify-content: flex-end;
}
.shiny-button {
  position: absolute;
  left: 20px;
  top: 20px;
  width: 5rem;
  filter: drop-shadow(5px 5px 0 rgba(130, 217, 211, 0.408));
  cursor: pointer;
}
div:has(.pokemon-3Dmodel) {
  display: flex;
  justify-content: center;
  align-items: center;
  margin-inline: auto;
}
.pokemon-3Dmodel {
  filter: drop-shadow(8px 8px 3px #39393951);
}
.pokemon-list {
  width: clamp(360px, 100%, 520px);
  height: calc(100vh - 56px);
  display: flex;
  overflow-y: scroll;
  overflow-x: visible;
  flex-direction: column;
  gap: 15px;
  padding: 20px 10px 20px 0;
  margin-right: 20px;
  border: 3px solid gold;
  position: relative;
}
.pokemon-list::before {
  content: "+";
  position: absolute;
  top: 0;
  left: -1rem;
  font-size: 3rem;
  border: 3px solid blue;
  line-height: 1em;
}
.pokemon {
  display: flex;
  justify-content: space-between;
  align-items: center;
  text-transform: capitalize;
  padding: 0 10px 0 0;
  color: black;
  font-weight: 500;
  font-size: 1.3rem;
  border-radius: 100vw;
  cursor: pointer;
}
.pokemon:hover,
.pokemon-active {
  background-image: linear-gradient(115deg, #f04f17 45%, black 45%);
  color: white;
}
.pokemon-active {
  position: relative;
}
.pokemon-active .pokeball {
  animation: rotate 1600ms infinite linear;
}

@keyframes rotate {
  to {
    rotate: 360deg;
  }
}
.pokemon > div:first-of-type {
  display: flex;
  align-items: center;
}
.pokemon__sprite {
  width: 60px;
  scale: 1.2;
  transform-origin: bottom;
  margin-inline: 10px;
}
.pokemon__num {
  display: flex;
}
.pokemon__num--field {
  margin-left: 0.5rem;
}
.pokeball {
  width: 25px;
  height: 25px;
  display: flex;
}
<main>
  <div class="header">
    <h1>Pokédex</h1>
    <p>By Number</p>
  </div>
  <section class="pokedex">
    <div>
      <div class="pokemon-3Dmodel">
        <img src="">
      </div>
    </div>
    <ul class="pokemon-list">
    </ul>
  </section>
</main>

Download iCal files does not working on mobile Chrome

I have a simple code that generate an ics file and automatically opens calendar app on ios
I tested works well on MacOS desktop and safari on ios mobile phones, however my code does not works for chrome browser on mobile,

  var test =
        "BEGIN:VCALENDARn" +
        "CALSCALE:GREGORIANn" +
        "METHOD:PUBLISHn" +
        "PRODID:-//Test Cal//ENn" +
        "VERSION:2.0n" +
        "BEGIN:VEVENTn" +
        "UID:test-1n" +
        "DTSTART;VALUE=DATE:" +
        convertDate(starting_date_html) +
        "n" +
        "DTEND;VALUE=DATE:" +
        convertDate(ending_date_html) +
        "n" +
        "SUMMARY:" +
        event_title +
        "n" +
        "DESCRIPTION:" +
        event_location +
        "n" +
        "END:VEVENTn" +
        "END:VCALENDAR";
    window.open("data:text/Calendar;charset=utf8," + escape(test)); 

I cannot get my head around on this issue …

I also tried the encodeURIComponent but no luck :/


window.open("data:text/Calendar;charset=utf8," + encodeURIComponent(test)); 

Trying to get a filter button to click on page load

I have a set of filters. I want the first button to be selected and clicked on at the loading of the page, so as to show all projects. Unfortunately I cannot seem to make this happen and I’m stumped.
If someone could help me understand where the problem lies I’d be very grateful.

function getProjects(event) {
    console.log("get projects");
    fetch('http://localhost:5678/api/works') // récuparation des données de l'API
        .then((resp) => resp.json()) // transforme la réponse en JSON
        .then(projects => { // réception de la réponse et exécute le code contenu après réception
            // on récupère toutes les boutons de catégories pour pouvoir filtrer les travaux
            const btnsCategory = document.querySelectorAll('.filter-btn');
            // pour chaque bouton récupéré
            btnsCategory.forEach(btnCategory => {
                // on clique sur une catégorie
                btnCategory.addEventListener('click', event => {
                    // récupération de la catégorie sélectionnée auparavant 
                    // et on lui enlève la classe active pour lui enlever la surbrillance
                    document.querySelector('.filter-btn.active').classList.remove("active");
                    event.target.classList.add("active");
                    // on exécute le code
                    //filterProjects(event, projects); // on affiche les projets correspondant au filtre actif
                    const gallery = document.querySelector('.gallery'); // Sélection de l'élément HTML qui contient les projets
                    gallery.innerHTML = ''; // Vide la galerie afin de pouvoir la remplir avec les éléments filtrés
                    let filterSelected = event.target.dataset.filter;
                    projects.forEach(project => {// on crée une boucle  
                        if (project.categoryId == filterSelected || filterSelected == "all") { // on vérifie si le projet doit être affiché en fonction du filtre choisi
                            const figure = document.createElement('figure'); // on crée un élément figure pour chaque objet
                            const image = document.createElement('img'); // même chose avec img
                            image.setAttribute('src', project.imageUrl); // on attribue l'url de l'image à l'élément img
                            image.setAttribute('alt', project.title); // on donne le titre du projet à l'attribut alt de l'img 
                            const title = document.createElement('figcaption'); // on crée un élément figcaption pour chaque projet
                            title.innerHTML = project.title; // on ajoute le titre du projet à figcaption
                            figure.appendChild(image); // on attache image à figure
                            figure.appendChild(title); // on attache figcaption à figure
                            gallery.appendChild(figure); // on attche figure à la galerie
                        }
                    });
                });
            });
        });
}


async function getCategories() {
    await fetch('http://localhost:5678/api/categories')
        .then(resp => resp.json())
        .then(categories => {
            const filterButtons = document.querySelector('.filter-buttons');
            // Ajoute un bouton de filtre pour chaque catégorie
            categories.forEach(category => {
                const button = document.createElement('button');
                button.classList.add('filter-btn');
                button.dataset.filter = category.id;
                button.textContent = category.name;
                filterButtons.appendChild(button);
            });
            // Ajoute un bouton "Tous" pour afficher tous les projets
            const allButton = document.createElement('button');
            allButton.classList.add('filter-btn');
            allButton.dataset.filter = 'all';
            allButton.id = "allWorks";
            allButton.textContent = 'Tous';
            filterButtons.prepend(allButton);
            allButton.click();
            // Sélectionne tous les boutons de filtre et ajoute un écouteur d'événement sur chacun
            const filterBtns = document.querySelectorAll('.filter-btn');
            filterBtns.forEach(filterBtn => {                
                filterBtn.addEventListener('click', event => {
                    // Enlève la classe "active" du bouton de filtre actif et ajoute-la au bouton sélectionné
                    let elementActive = document.querySelector('.filter-btn.active');
                    if (elementActive != null) {
                        elementActive.classList.remove('active');
                    }
                    event.target.classList.add('active');
                    // Filtre les projets selon la catégorie sélectionnée
                    getProjects(event);
                });
            });
        });

}


document.addEventListener('DOMContentLoaded', async (event) => { // vérifie si la page est chargée
    await getCategories();
    await document.querySelector("#allWorks").click(); 
     


});


I’ve tried to have a list of filters that help the user search through the projects available on the website. Unfortunately while the “Tous” (all) button is indeed selected at page load, it isn’t clicked and the display of all the projects needs to be done manually which isn’t what I want.

Unable to set angle in transform, rotate using an anom.function

I cannot figure out what is wrong with the following code:

 svg.selectAll(".axislabel")
            .data(featureData)
            .join(
                enter => enter.append("text")
                    .attr("x", d => d.label_coord.x)
                    .attr("y", d => d.label_coord.y)
                    .attr("transform", "rotate(" + (d => d.angle)+ ")" )  //ERROR
                    .text(d => d.name)
            );

I’m getting this error:

Error: <text> attribute transform: Expected number, "rotate(d =u003E d.angle)".

If I use .attr("transform", "rotate(90)" ) then it works.

Thank you

I’ve tried various versions of the syntax.

How are dot relative paths and rootDir paths treated differently in my projects jest configuration?

Problem Description

I have a question about the jest.config.ts that I am setting up.
There I use a moduleNameMapper, so that imports a la import ... from "@base/..." also work in the tests. For this I use a function of ts-jest, which returns the corresponding regular expressions from the tsconfig.json.compilerOptions.paths. tsconfig.json is a json file which I am importing with typescript’s resolveJsonModule option. See here for the documentation on moduleNameMapper.

The function from ts-jest returns one of the following two objects to be used as the moduleNameWrapper, depending on other parameters passed. How that works is not relevant for the question, as I am also able to just enter the keys and values directly, not using the ts-jest function.

First Object: { '^@base/(.*)$': './src/$1' }
Second Object: { '^@base/(.*)$': '<rootDir>/src/$1' }
The first does not work (the modules are then not resolved), the second works correctly.

Question

Hence my question: What does . mean as a path for jest? What is the difference with <rootDir>? Why do they both not point to the same directory? My jest config is in the root directory of the project, so I would assume that both . and <rootDir would point to the root directory (the folder where the jest config is located). Also see the documentation for rootDir.

What I’ve tried so far

I tried putting both objects in as moduleNameWrapper in the jest.config, but only the one with rootDir worked. I would have expected ./src/$1 to have worked as well, but it did not.

The rootDir solution is unsatisfying, as producing the object including <rootDir> requires an additional options parameter, which sets a prefix. This prefix is hard coded, and could e.g. result in problems further in this project or when using this project as a foundation for the next.

This is the call to create the moduleNameMapper object via ts-jest:

pathsToModuleNameMapper(compilerOptions.paths, {prefix: "<rootDir>/src/"}),

If the second parameter is omitted, then the dot version is produced, which does not work.

Thanks.