Issue with Viewing Joined Students and Assigning Assignments in Teacher Dashboard

I am developing a Flask-based school hub app where teachers can view students who joined a class using a class code and assign assignments. However, the following features in my teacher dashboard are not working properly:

Expected Behavior:

  1. View Joined Students

    • A teacher enters a class code, clicks “View Joined Students”, and sees a list of students who have joined the class.
  2. Assign Assignments

    • The class codes should be populated dynamically in a dropdown.
    • When a teacher selects a class, the students in that class should populate the student dropdown.
    • The teacher should be able to assign an assignment to the selected student.

Current Issues:

  • When I enter a class code and click View Joined Students, the student list does not update.
  • The class code dropdown does not populate dynamically.
  • The student dropdown does not populate when selecting a class.
  • Assigning assignments is not working.

Code Implementation:

Flask Route (Fetching Joined Students & Assignments)

@app.route('/get_joined_students', methods=['POST'])
@login_required
def get_joined_students():
    data = request.json
    class_code = data.get('class_code')
    
    students = User.query.join(ClassEnrollment).filter(ClassEnrollment.class_code == class_code).all()
    
    return jsonify([{'id': student.id, 'name': student.name} for student in students])

@app.route('/get_class_codes', methods=['GET'])
@login_required
def get_class_codes():
    classes = Forum.query.filter_by(created_by=current_user.id).all()
    return jsonify([{'id': cls.id, 'class_code': cls.class_code} for cls in classes])

@app.route('/assign_assignment', methods=['POST'])
@login_required
def assign_assignment():
    data = request.json
    student_id = data.get('student_id')
    class_code = data.get('class_code')
    title = data.get('title')
    details = data.get('details')

    if not (student_id and class_code and title and details):
        return jsonify({'error': 'All fields are required'}), 400

    new_assignment = Assignment(student_id=student_id, class_code=class_code, title=title, details=details)
    db.session.add(new_assignment)
    db.session.commit()

    return jsonify({'message': 'Assignment assigned successfully'})

JavaScript (Fetching Data & Updating UI)

document.getElementById("viewJoinedBtn").addEventListener("click", function() {
    const classCode = document.getElementById("classCode").value;
    
    fetch("/get_joined_students", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ class_code: classCode })
    })
    .then(response => response.json())
    .then(data => {
        const studentList = document.getElementById("joinedStudentsList");
        studentList.innerHTML = "";
        data.forEach(student => {
            const li = document.createElement("li");
            li.textContent = student.name;
            studentList.appendChild(li);
        });
    })
    .catch(error => console.error("Error fetching students:", error));
});

document.addEventListener("DOMContentLoaded", function() {
    fetch("/get_class_codes")
        .then(response => response.json())
        .then(data => {
            const classSelect = document.getElementById("classCodeSelect");
            data.forEach(cls => {
                const option = document.createElement("option");
                option.value = cls.class_code;
                option.textContent = cls.class_code;
                classSelect.appendChild(option);
            });
        })
        .catch(error => console.error("Error fetching class codes:", error));
});

document.getElementById("assignBtn").addEventListener("click", function() {
    const classCode = document.getElementById("classCodeSelect").value;
    const studentId = document.getElementById("studentSelect").value;
    const title = document.getElementById("assignmentTitle").value;
    const details = document.getElementById("assignmentDetails").value;

    fetch("/assign_assignment", {
        method: "POST",
        headers: { "Content-Type": "application/json" },
        body: JSON.stringify({ class_code: classCode, student_id: studentId, title, details })
    })
    .then(response => response.json())
    .then(data => alert(data.message))
    .catch(error => console.error("Error assigning assignment:", error));
});

Troubleshooting Done So Far:

  1. Verified database queries – students and class codes exist.
  2. Debugged JavaScript using console.log() – API calls are being made but not updating the UI correctly.
  3. Checked network requests – /get_joined_students and /get_class_codes return expected results, but UI does not update.

Question:

What might be causing:

  • Student list not updating when entering a class code?
  • Dropdowns not populating dynamically?
  • Assignments not being saved properly?

Any suggestions on debugging or improving this logic?

Why aren’t my dynamically rendered Instagram posts loading, while hardcoded ones do in Vue?

I was wanting to display several Instagram posts by holding their Ids in an array to later just bind the required URL in the embed code provided by Instagram themselves. The problem is I’m finding that the post/images don’t actually get loaded when trying to dynamically render them, but they load fine when hardcoded in.

I’ve tried making sure my component’s data is getting populated correctly across different points in its lifecycle but nothing seems to work. I believe the problem has something to do with the attribute binding in the actual HTML but I can’t figure out how to get around it.

Has anyone come across this issue before? Any help would be super appreciated!!

This is what my code looks like

<template>
  <div>
<!--this hardcoded embed works correctly-->
   <blockquote class="instagram-media" data-instgrm-permalink="https://www.instagram.com/p/DDEZ59hIM55/?utm_source=ig_embed&amp;utm_campaign=loading" data-instgrm-version="14" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:16px;"> <a href="https://www.instagram.com/p/DDEZ59hIM55/?utm_source=ig_embed&amp;utm_campaign=loading" style=" background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;" target="_blank"> <div style=" display: flex; flex-direction: row; align-items: center;"> <div style="background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;"></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;"></div></div></div><div style="padding: 19% 0;"></div> <div style="display:block; height:50px; margin:0 auto 12px; width:50px;"><svg width="50px" height="50px" viewBox="0 0 60 60" version="1.1" ><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g transform="translate(-511.000000, -20.000000)" fill="#000000"><g><path d="M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631"></path></g></g></g></svg></div><div style="padding-top: 8px;"> <div style=" color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;">View this post on Instagram</div></div><div style="padding: 12.5% 0;"></div> <div style="display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;"><div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);"></div> <div style="background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;"></div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);"></div></div><div style="margin-left: 8px;"> <div style=" background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;"></div> <div style=" width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)"></div></div><div style="margin-left: auto;"> <div style=" width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);"></div> <div style=" background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);"></div> <div style=" width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);"></div></div></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center; margin-bottom: 24px;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 224px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 144px;"></div></div></a><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a href="https://www.instagram.com/p/DDEZ59hIM55/?utm_source=ig_embed&amp;utm_campaign=loading" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A post shared by Stephania (@stephania_girlcat)</a></p></div></blockquote>

<!--the following displays the IG placeholder but never actually loads the post-->
<!--i'm outputting the url to confirm it gets constructed correctly-->

    <div  v-for="post in posts" 
        v-bind:posts="posts"
        v-bind:key="post.id">
        <p> {{ post.url }}</p>
         <blockquote class="instagram-media" :data-instgrm-permalink="post.link" data-instgrm-version="14" style=" background:#FFF; border:0; border-radius:3px; box-shadow:0 0 1px 0 rgba(0,0,0,0.5),0 1px 10px 0 rgba(0,0,0,0.15); margin: 1px; max-width:540px; min-width:326px; padding:0; width:99.375%; width:-webkit-calc(100% - 2px); width:calc(100% - 2px);"><div style="padding:16px;"> <a :href="post.link" style=" background:#FFFFFF; line-height:0; padding:0 0; text-align:center; text-decoration:none; width:100%;" target="_blank"> <div style=" display: flex; flex-direction: row; align-items: center;"> <div style="background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 40px; margin-right: 14px; width: 40px;"></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 100px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 60px;"></div></div></div><div style="padding: 19% 0;"></div> <div style="display:block; height:50px; margin:0 auto 12px; width:50px;"><svg width="50px" height="50px" viewBox="0 0 60 60" version="1.1" ><g stroke="none" stroke-width="1" fill="none" fill-rule="evenodd"><g transform="translate(-511.000000, -20.000000)" fill="#000000"><g><path d="M556.869,30.41 C554.814,30.41 553.148,32.076 553.148,34.131 C553.148,36.186 554.814,37.852 556.869,37.852 C558.924,37.852 560.59,36.186 560.59,34.131 C560.59,32.076 558.924,30.41 556.869,30.41 M541,60.657 C535.114,60.657 530.342,55.887 530.342,50 C530.342,44.114 535.114,39.342 541,39.342 C546.887,39.342 551.658,44.114 551.658,50 C551.658,55.887 546.887,60.657 541,60.657 M541,33.886 C532.1,33.886 524.886,41.1 524.886,50 C524.886,58.899 532.1,66.113 541,66.113 C549.9,66.113 557.115,58.899 557.115,50 C557.115,41.1 549.9,33.886 541,33.886 M565.378,62.101 C565.244,65.022 564.756,66.606 564.346,67.663 C563.803,69.06 563.154,70.057 562.106,71.106 C561.058,72.155 560.06,72.803 558.662,73.347 C557.607,73.757 556.021,74.244 553.102,74.378 C549.944,74.521 548.997,74.552 541,74.552 C533.003,74.552 532.056,74.521 528.898,74.378 C525.979,74.244 524.393,73.757 523.338,73.347 C521.94,72.803 520.942,72.155 519.894,71.106 C518.846,70.057 518.197,69.06 517.654,67.663 C517.244,66.606 516.755,65.022 516.623,62.101 C516.479,58.943 516.448,57.996 516.448,50 C516.448,42.003 516.479,41.056 516.623,37.899 C516.755,34.978 517.244,33.391 517.654,32.338 C518.197,30.938 518.846,29.942 519.894,28.894 C520.942,27.846 521.94,27.196 523.338,26.654 C524.393,26.244 525.979,25.756 528.898,25.623 C532.057,25.479 533.004,25.448 541,25.448 C548.997,25.448 549.943,25.479 553.102,25.623 C556.021,25.756 557.607,26.244 558.662,26.654 C560.06,27.196 561.058,27.846 562.106,28.894 C563.154,29.942 563.803,30.938 564.346,32.338 C564.756,33.391 565.244,34.978 565.378,37.899 C565.522,41.056 565.552,42.003 565.552,50 C565.552,57.996 565.522,58.943 565.378,62.101 M570.82,37.631 C570.674,34.438 570.167,32.258 569.425,30.349 C568.659,28.377 567.633,26.702 565.965,25.035 C564.297,23.368 562.623,22.342 560.652,21.575 C558.743,20.834 556.562,20.326 553.369,20.18 C550.169,20.033 549.148,20 541,20 C532.853,20 531.831,20.033 528.631,20.18 C525.438,20.326 523.257,20.834 521.349,21.575 C519.376,22.342 517.703,23.368 516.035,25.035 C514.368,26.702 513.342,28.377 512.574,30.349 C511.834,32.258 511.326,34.438 511.181,37.631 C511.035,40.831 511,41.851 511,50 C511,58.147 511.035,59.17 511.181,62.369 C511.326,65.562 511.834,67.743 512.574,69.651 C513.342,71.625 514.368,73.296 516.035,74.965 C517.703,76.634 519.376,77.658 521.349,78.425 C523.257,79.167 525.438,79.673 528.631,79.82 C531.831,79.965 532.853,80.001 541,80.001 C549.148,80.001 550.169,79.965 553.369,79.82 C556.562,79.673 558.743,79.167 560.652,78.425 C562.623,77.658 564.297,76.634 565.965,74.965 C567.633,73.296 568.659,71.625 569.425,69.651 C570.167,67.743 570.674,65.562 570.82,62.369 C570.966,59.17 571,58.147 571,50 C571,41.851 570.966,40.831 570.82,37.631"></path></g></g></g></svg></div><div style="padding-top: 8px;"> <div style=" color:#3897f0; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:550; line-height:18px;">View this post on Instagram</div></div><div style="padding: 12.5% 0;"></div> <div style="display: flex; flex-direction: row; margin-bottom: 14px; align-items: center;"><div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(0px) translateY(7px);"></div> <div style="background-color: #F4F4F4; height: 12.5px; transform: rotate(-45deg) translateX(3px) translateY(1px); width: 12.5px; flex-grow: 0; margin-right: 14px; margin-left: 2px;"></div> <div style="background-color: #F4F4F4; border-radius: 50%; height: 12.5px; width: 12.5px; transform: translateX(9px) translateY(-18px);"></div></div><div style="margin-left: 8px;"> <div style=" background-color: #F4F4F4; border-radius: 50%; flex-grow: 0; height: 20px; width: 20px;"></div> <div style=" width: 0; height: 0; border-top: 2px solid transparent; border-left: 6px solid #f4f4f4; border-bottom: 2px solid transparent; transform: translateX(16px) translateY(-4px) rotate(30deg)"></div></div><div style="margin-left: auto;"> <div style=" width: 0px; border-top: 8px solid #F4F4F4; border-right: 8px solid transparent; transform: translateY(16px);"></div> <div style=" background-color: #F4F4F4; flex-grow: 0; height: 12px; width: 16px; transform: translateY(-4px);"></div> <div style=" width: 0; height: 0; border-top: 8px solid #F4F4F4; border-left: 8px solid transparent; transform: translateY(-4px) translateX(8px);"></div></div></div> <div style="display: flex; flex-direction: column; flex-grow: 1; justify-content: center; margin-bottom: 24px;"> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; margin-bottom: 6px; width: 224px;"></div> <div style=" background-color: #F4F4F4; border-radius: 4px; flex-grow: 0; height: 14px; width: 144px;"></div></div></a><p style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; line-height:17px; margin-bottom:0; margin-top:8px; overflow:hidden; padding:8px 0 7px; text-align:center; text-overflow:ellipsis; white-space:nowrap;"><a :href="post.link" style=" color:#c9c8cd; font-family:Arial,sans-serif; font-size:14px; font-style:normal; font-weight:normal; line-height:17px; text-decoration:none;" target="_blank">A post shared by @divadelbloque</a></p></div></blockquote>

    </div>

  </div>
</template>
<script>


export default {
  components: {
    // InstagramEmbed,
  },  
  beforeCreate()
  {
  }, 
  created()
  {
    console.log('created');
    console.log('this.posts:: ', JSON.stringify(this.posts));
    for(let i in this.posts)
      { 
        this.posts[i].url = this.linkStart + this.posts[i].id + this.linkEnd;
      }
  },  
  beforeMount()
  {
  },
    mounted() {
      console.log('mounted');
      console.log('this.posts:: ', JSON.stringify(this.posts));

      let igscript = document.createElement('script')
      igscript.setAttribute('src', 'https://www.instagram.com/embed.js')
      document.head.appendChild(igscript)
      
      // this.$forceUpdate();
    },
    data()
        {
            return {
        linkStart: 'https://www.instagram.com/p/',
        linkEnd: '/?utm_source=ig_embed&amp;utm_campaign=loading',
                posts: [{id: 'DDEZ59hIM55', url: ''}, {id: 'DDEZ59hIM55', url: ''}],
            }
        },
};

</script>

Leaflet custom control with active/inactive states for performing action

I am using Leaflet, and I want to have a button labelled “My Control” in the upper-left corner (as shown below) that when I click it, the following happens:

  1. The cursor becomes a crosshair, and the “My Control” button is an “active” state.
  2. I can click once anywhere on the map. When a I click on the map, some action is performed (e.g., printing to the console the lat-lon coordinates of where the user clicked).
  3. After that action is performed, the “My Control” button goes back to an “inactive” state, and the cursor is no longer a crosshair.

How can I implement something like this?

Finally, if the button is in the “active” state, how can I implement it so that clicking the button again makes it go back to the “inactive” state?

var map = L.map('map').setView([48.86, 2.35], 11);

L.Control.MyControl = L.Control.extend({
  onAdd: function(map) {
    var el = L.DomUtil.create('div', 'leaflet-bar my-control');

    el.innerHTML = 'My Control';

    return el;
  },

  onRemove: function(map) {
    // Nothing to do here
  }
});

L.control.myControl = function(opts) {
  return new L.Control.MyControl(opts);
}

L.control.myControl({
  position: 'topleft'
}).addTo(map);

L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
  attribution: '&copy; <a href="http://osm.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
.my-control {
  background: #fff;
  padding: 5px;
}
<link rel="stylesheet" href="https://unpkg.com/[email protected]/dist/leaflet.css" integrity="sha512-Rksm5RenBEKSKFjgI3a41vrjkw4EVPlJ3+OiI65vTjIdo9brlAacEuKOiQ5OFh7cOI1bkDwLqdLw3Zg0cRJAAQ==" crossorigin="" />
<script src="https://unpkg.com/[email protected]/dist/leaflet-src.js" integrity="sha512-2h9aokfcaYW7k0VPn1JqbQDQCaNQRrZJwetlnQ88yJrtIzGLVW/2StdQKoE+TIVNNTUxf6SVa+2vW2KB2EXnnA==" crossorigin=""></script>

<div id="map" style="height: 200px"></div>

What is the cause of this async bug and why is it fixed with a blocked scoped const variable

I’ve been reading through the Asynchronous Programming chapter of Eloquent JavaScript and came across this async bug problem. The solution offered here was to do a .join() at the end, but I came across another solution through using a blocked scoped const. The problem is I can’t figure out why it fixes the bug, if someone could offer an explanation it would be much appreciated.

const resolveName = (fruitname) => {
  return new Promise((resolve) => {
    resolve(fruitname);
  });
};

async function countLetters(fruitList) {
  let result = "";

  await Promise.all(
    fruitList.map(async (fruit) => {
      result += fruit + ": " + (await resolveName(fruit)).length + "n";

      // Fix: storing the string into a variable fixes this.
      // const s = fruit + ": " + (await resolveName(fruit)).length + "n";
      // result += s;
    })
  );

  return result;
}

const arr = ["apple", "banana", "cherry"];
const p = countLetters(arr);
p.then((r) => console.log(r));

I’ve tried debugging and scouring SO for a solution. The expected and actual output is as below:

Expected:
apple: 5 banana: 6 cherry: 6

Actual:
cherry: 6

Here’s a link to a runnable that reproduces the bug.

NextJS layout folder clash with not-found.jsx

I’m currently creating a NextJS site. I have two distinct layouts with the following file structure

 app
│  (main_content)
│ │  content
│ │ ╰  page.jsx
│ │  layout.jsx
│ │  not-found.jsx
│ ╰  page.jsx
│  (other_content)
│ │  content
│ │ ╰  subpath
│ │   ╰  page.jsx
│ ╰  layout.jsx
│  favicon.ico
╰  globals.css

and I’ve not been able to have my website render the not-found.jsx 404. Any unrecognized route will render the default NextJS 404 page.

I’ve tried to move not-found.jsx file to immediately under app/, which caused an error saying it’s not attached to a layout. Adding a layout in app causes hydration error.

WebStorm Debugger Not Working with Next.js (Breakpoints Not Being Hit)

I’ve been trying to set up the debugger for my Next.js project in WebStorm, but it’s not working as expected. I’ve read the Next.js and WebStorm documentation multiple times but couldn’t get it to work.

Issue:

enter image description here

I set up an NPM run/debug configuration, but breakpoints don’t work correctly. Instead of stopping at the expected breakpoint, the debugger stops at the <main> tag, which shouldn’t happen.

Here’s an example of my configuration:

enter image description here

This is the default NPM configuration that WebStorm generates when creating a Next.js project. I’ve also tried other configurations, but they either don’t work at all or have the same issue.

What I’ve Tried:

enter image description here

  • Adding environment variables like:
    NODE_ENV=development;NODE_OPTIONS=--inspect
    
  • Using Node.js debug configurations instead of NPM:
  • Running a JavaScript/Browser/Live Edit debugger alongside them.
  • Searching online and asking ChatGPT for solutions.

System Information:

  • OS: Manjaro Linux
  • WebStorm Installation: tar.gz (tarball)
  • Package Manager: pnpm (installed using pacman)
  • Language: TypeScript
  • App router and turbopack not enabled (for now)

Does anyone know how to properly configure WebStorm’s debugger for Next.js so that breakpoints work correctly? Any help would be greatly appreciated!

infer type of Higher order function return value when function parameter returns a promise

Basically I am trying to create a function defined like this:

function result<T>(cb: () => T): Result<T,Error> {
  try {
    return Ok(fn());
  } catch (error) {
    return Err(
      error instanceof Error ? (error as E) : (new Error(String(error)) as E)
    );
  }
}

it is a function that takes in another function that could throw e.g JSON.parse, and returns a rust-like result accordingly, so that when e.g result(() => JSON.parse(...)) fails it doesnt crash the program but it returns a Result with the error.

Now, this function works fine, but the problems arrive when i try to do async stuff.

i have this async variant:

async function resultrAsync<V, E extends Error>(
  fn: () => Promise<V>
): Promise<Result<V, E>> {
  try {
    return Ok(await fn());
  } catch (error) {
    return Err(
      error instanceof Error ? (error as E) : (new Error(String(error)) as E)
    );
  }
}

that should be used like this:

const result = await resultAsync(() => fetch(...))

and it works fine.
But i want to overcomplicate things and join these 2 functions and do some generic magic, to handle cases where the callback functions is synchronous or not (JSON.parse vs fetch)

I tried with this:

type SyncOrAsync<V> = V | Promise<V>;

function resultr<V, E extends Error>(fn: () => V): Result<V, E>;
function resultr<V, E extends Error>(
 fn: () => Promise<V>
): Promise<Result<V, E>>;
function resultr<V, E extends Error>(
 fn: () => SyncOrAsync<V>
): SyncOrAsync<Result<V, E>> {
 try {
   const result = fn();
   if (result instanceof Promise) {
     return result
       .then(Ok)
       .catch(error =>
         Err(
           error instanceof Error
             ? (error as E)
             : (new Error(String(error)) as E)
         )
       ) as Promise<Result<V, E>>;
   } else {
     return Ok(result);
   }
 } catch (error) {
   return Err(
     error instanceof Error ? (error as E) : (new Error(String(error)) as E)
   );
 }
}

But when i use the async variant const res = result(() => fetch(url))
its return type is Result<Promise<Response>, Error> when it should be Promise<Result<V,Error>>

is this possible? i know its not probably a good idea but i want to get a grasp on generics.

Passing a constructer as a parameter as if it were a regular function in JavaScript?

I am using ANTLR 4, and it is common to instantiate a class and immediately pass it as the argument of the next class’ constructor, as per the docs for the ANTLR 4 JavaScript target:

import antlr4 from 'antlr4';
import MyGrammarLexer from './QueryLexer.js';
import MyGrammarParser from './QueryParser.js';
import MyGrammarListener from './QueryListener.js';

const input = "field = 123 AND items in (1,2,3)"
const chars = new antlr4.InputStream(input);
const lexer = new MyGrammarLexer(chars);
const tokens = new antlr4.CommonTokenStream(lexer);
const parser = new MyGrammarParser(tokens);
const tree = parser.MyQuery();

Clearly this is a situation suited for function composition, and I am using Lodash (FP).

At first I tried the following:

// imports...
import _ from "lodash/fp.js";

const parse = _.compose([
  _.invoke("MyQuery"),
  MyGrammarParser,
  antlr4.CommonTokenStream,
  MyGrammarLexer,
  antlr4.InputStream,
]);

But this fails with TypeError: Class constructor ke cannot be invoked without 'new'.

Inserting new on each line inside _.compose (other than _.invoke) fails with TypeError: Cannot read properties of undefined (reading 'length') at new antlr4.InputStream.

Instead, I can use a helper function:

// imports...
import _ from "lodash/fp.js";

_.construct = Constructor => param => new Constructor(param);

const parse = _.compose([
  _.invoke("MyQuery"),
  _.construct(MyGrammarParser),
  _.construct(antlr4.CommonTokenStream),
  _.construct(MyGrammarLexer),
  _.construct(antlr4.InputStream),
]);

const tree = parse("field = 123 AND items in (1,2,3)");

This works, and IMO looks way better than the original, but I want to know if there is a way to write this using built-in Javascript or Lodash functions. As far as I know, it is not possible to pass a constructor as a parameter as if it were a regular function, hence _.construct.

In summary, I want to pass a constructor as a parameter as if it were a regular function without having to first wrap it in a helper function.

For those unfamiliar with Lodash, see the Lodash Documentation and the Lodash FP Guide, however the solution to this question should be Lodash/ANTLR 4 independent.

Move elements inside array while looping it

How can I loop trough a very large array, and still be able to move elements to the end of the array without breaking the loop?

In the loop I am consuming the elements, but some of them depend on others, and those I want to move at the end. Eventually all elements will be consumed, so the loop will not be infinite.

I know the easy way is to just use a secondary array and not alter original array in the loop, but data is very large and it would be very slow

How can I store utm values in the shopify cart through a theme app extension?

I’ve created a block targeting the body so that the code will check the url whenever the shop is opened, and the goal is to loop through all url parameters and store any utm sources in the cart for later use even if they are no longer in the url.

Here is my current block:

{% comment %}
  Embed to provide global js to check the utms when user enters shop
{% endcomment %}

<script>
    const newAttributes = {{ cart.attributes }}
    if (!newAttributes.UTMs) newAttributes.UTMs = []
    const urlParams = new URLSearchParams(window.location.search);
    for (const [key, value] of urlParams.entries()) {
        if (key === "utm_source" && !newAttributes.UTMs.includes(value)) {
            newAttributes.UTMs.push(value)
            console.log(newAttributes, 'other attributes')
        }
    }
    fetch(window.Shopify.routes.root + 'cart/update.js', {
        method: 'POST',
        headers: {
            'Content-Type': 'application/json',
        },
        body: JSON.stringify({
            attributes: newAttributes
        })
    })
    .then(function(res) {
        return res.json();
    })
    .then(function(result) {
        console.log(result);
    })
    .catch(function(err) {
        console.error(err);
    });
</script>

{% schema %}
    {
        "name": "UtmCheck",
        "target": "body",
        "settings": []
    }
{% endschema %}```

and the error I keep getting is saying `Uncaught SyntaxError: Unexpected token '=>'`

White bars on canvas when using ScrollTrigger

I am trying to get rid of the white spaces at the bottom and on the right side of the screen. I have attached a picture for reference. Also added the entire code below. How can I resolve this problem? I tried implementing a solution here https://gsap.com/community/forums/topic/38620-gsap-and-scroll-trigger-causing-large-white-space/.

But the issue persist.

enter image description here

import React, { useRef, useMemo, useEffect } from "react";
import { Canvas } from "@react-three/fiber";
import { Background } from "./Background";
import { Cloud } from "./Cloud";
import { Stars } from "./stars";
import { gsap } from "gsap";
import { ScrollTrigger } from "gsap/ScrollTrigger";

// Register ScrollTrigger plugin
gsap.registerPlugin(ScrollTrigger);

function Destination() {
  const tl = useRef();
  const backgroundColors = useRef({
    colorA: "#001540",
    colorB: "#001C57",
  });

  const sceneOpacity = useRef(1);

  // Generate random cloud positions and scales
  const clouds = useMemo(() => {
    const numClouds = 40;
    const cloudArray = [];

    for (let i = 0; i < numClouds; i++) {
      cloudArray.push({
        position: [
          Math.random() * 60 - 20,
          Math.random() * 60 - 20,
          Math.random() * -60,
        ],
        scale: Math.random() * 1.6 + 0.3,
      });
    }

    return cloudArray;
  }, []);

  // Set up scroll-triggered animations
  useEffect(() => {
    // Select the text containers
    const destinationText = document.querySelector(".destination-text");
    const moreContent = document.querySelector(".more-content");

    // GSAP animation for "Destination Info"
    gsap.fromTo(
      destinationText,
      {
        x: -100, // Start 100px to the left
        opacity: 0, // Start invisible
      },
      {
        x: 0, // Move to the center (x: 0)
        opacity: 1, // Fade in
        duration: 1, // Animation duration
        scrollTrigger: {
          trigger: destinationText, // Element to trigger the animation
          start: "top 80%", // Start animation when the top of the element is 80% from the top of the viewport
          end: "top 20%", // End animation when the top of the element is 20% from the top of the viewport
          scrub: true, // Smoothly animate based on scroll position
          toggleActions: "play none none reverse", // Play animation on scroll in, reverse on scroll out
        },
      }
    );

    // GSAP animation for "More Content"
    gsap.fromTo(
      moreContent,
      {
        x: 100, // Start 100px to the right
        opacity: 0, // Start invisible
      },
      {
        x: 0, // Move to the center (x: 0)
        opacity: 1, // Fade in
        duration: 1, // Animation duration
        scrollTrigger: {
          trigger: moreContent, // Element to trigger the animation
          start: "top 80%", // Start animation when the top of the element is 80% from the top of the viewport
          end: "top 20%", // End animation when the top of the element is 20% from the top of the viewport
          scrub: true, // Smoothly animate based on scroll position
          toggleActions: "play none none reverse", // Play animation on scroll in, reverse on scroll out
        },
      }
    );
  }, []);

  return (
    <div style={{ position: "relative", minHeight: "100vh" }}>
      {/* Full-screen Canvas for 3D background */}
      <Canvas
        style={{
          position: "fixed", // Fix the canvas to the viewport
          top: 0,
          left: 0,
          pin: "body",
          width: "100%",
          height: "100%",
          zIndex: 0, // Ensure the canvas stays in the background
        }}
      >
        {/* Background component */}
        <Background backgroundColors={backgroundColors} />

        {/* Stars */}
        <Stars />

        {/* Randomly placed clouds */}
        {clouds.map((cloud, index) => (
          <Cloud
            key={index}
            sceneOpacity={sceneOpacity}
            position={cloud.position}
            scale={cloud.scale}
          />
        ))}
      </Canvas>

      {/* Scrollable content */}
      <div
        style={{
          position: "relative",
          zIndex: 1, // Ensure content appears above the canvas
          paddingTop: "100vh", // Add padding to push content below the canvas
          overflowY: "hidden", // Enable scrolling
          height: "100vh", // Make the container full height
          width: "100%", // Ensure the container fills the width
        }}
        className="scroll-container"
      >
        {/* Text content */}
        <div
          className="destination-text"
          style={{
            color: "#FFFFFF",
            textAlign: "center",
            padding: "20px",
          }}
        >
          <h1>Destination Info</h1>
          <p>Welcome</p>
        </div>

        {/* More content */}
        <div
          className="more-content"
          style={{
            color: "#FFFFFF",
            textAlign: "center",
            padding: "20px",
          }}
        >
          <h2>More Content</h2>
          <p>Scroll down to see more!</p>
        </div>
      </div>
    </div>
  );
}

export default Destination;

Failed to Send Data to Server Using AJAX in JavaScript [duplicate]

I’m experiencing an issue when trying to send data to the server using AJAX in JavaScript. Every time I attempt to submit a question, I receive the error message “Failed to send question: error.” I’ve checked my code, but I can’t seem to identify the cause.

Here is the JavaScript code I’m using to send the data:

let questions = [];
let itemsPerPage = 10;
let currentPage = 1;
let instansiList = [];
let captchaValue = '';

// Getting DOM elements
const alertMessage = document.querySelector('.alert-message');
const containerWidth = document.querySelector('.alert-container').offsetWidth;
const messageWidth = alertMessage.offsetWidth;

let position = containerWidth;

// Function for alert message animation
function animate() {
    position -= 2; 
    if (position < -messageWidth) {
        position = containerWidth; 
    }
    alertMessage.style.transform = `translateX(${position}px)`;
    requestAnimationFrame(animate); 
}

// Running animation on page load
window.onload = function() {
    animate();
};

// Preventing certain actions
document.addEventListener('contextmenu', e => e.preventDefault());
document.addEventListener('copy', e => {
    e.preventDefault();
    alert("Copying content is not allowed.");
});
document.addEventListener('selectstart', e => e.preventDefault());
document.addEventListener('keydown', event => {
    if (event.key === 'PrintScreen') {
        alert('Taking screenshots is not allowed.');
    }
});

// Main function when document is ready
$(document).ready(function() {
    $('#submitQuestion').click(function() {
        const topic = $('#topic').val();
        const instansi = $('#instansiInput').val();
        const nameOption = $('#nameOption').val();
        const name = nameOption === 'input' ? $('#name').val() : 'Anonymous';
        const question = $('#question').val();
        const timestamp = new Date().toISOString(); 
        const fileUpload = $('#fileUpload')[0].files[0]; 

        // Input validation
        if (!topic || !instansi || !question.trim()) {
            alert('Please fill in all required fields.');
            return;
        }

        if (countWords(question) > 1000) {
            alert('Question cannot exceed 1000 words.');
            return;
        }

        if (nameOption === 'input' && !/^[a-zA-Zs]+$/.test(name)) {
            $('#nameError').text('Name can only contain letters and spaces.').show();
            return;
        }

        // AJAX request to send data
        $.ajax({
            url: 'your-server-endpoint.php',
            type: 'POST',
            data: {
                topic: topic,
                instansi: instansi,
                name: name,
                question: question,
                timestamp: timestamp,
                file: fileUpload
            },
            success: function(response) {
                alert('Question submitted successfully!');
            },
            error: function(xhr, status, error) {
                alert('Failed to send question: ' + error);
            }
        });
    });
});

I’ve tried debugging the code, but I’m still unable to resolve the issue. Any help would be greatly appreciated! ### Additional Information:

  • Environment: I’m using jQuery version 3.5.1 and PHP 7.4 on a local server.

  • Browser: The issue occurs in both Chrome and Firefox.

  • Network: I’ve checked the network tab in the developer tools, and the request is being sent, but the response indicates a server error (500 Internal Server Error).

  • Server-Side Code: Here is a snippet of the PHP code handling the request:

    <?php 
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $topic = $_POST['topic'];
        $instansi = $_POST['instansi'];
        $name = $_POST['name'];
        $question = $_POST['question'];
        $timestamp = $_POST['timestamp'];
    
        // Validate inputs
        if (empty($topic) || empty($instansi) || empty($question)) {
            http_response_code(400);
            echo json_encode(['error' => 'All fields are required.']);
            exit;
        }
    
        // Process file upload if exists
        if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {
            // Handle file upload
        }
    
        // Simulate saving to database
        // If there's an error, return a 500 error
        if (/* some error condition */) {
            http_response_code(500);
            echo json_encode(['error' => 'Failed to save question.']);
            exit;
        }
    
        echo json_encode(['success' => 'Question saved successfully.']);
    }
    ?>
    

I’ve ensured that the server is configured correctly, but I’m still facing this issue. Any insights or suggestions on how to troubleshoot this further would be greatly appreciated!

Understanding Results from a MariaDB query in Node/Express

I’m trying to understand why I am struggling sometimes with returning results from a MariaDB query with multiple rows being returned

// connection is a MariaDB connection pool
const rows = await connection.execute("SELECT * FROM myTable WHERE id ?", [id]);
console.log("Rows returned: ", rows);

This prints out:

Rows returned:  [
  {
    original_key: key_20250110-197.jpg',
    filename: '20250110-197.jpg'
  },
  {
    original_key: 'key_20250110-208.jpg',
    filename: '20250110-208.jpg'
  }
]

If I destructure rows I get something not what I am expecting

const [rows] = await connection.execute("SELECT * FROM myTable WHERE id ?", [id]);
console.log("Rows returned: ", rows);

Output:

Rows returned: {
original_key: 'key_20250110-197.jpg',
  filename: '20250110-197.jpg'
}

I loose any subsequent objects from the array.

If I do this

const rows = await connection.execute("SELECT * FROM myTable WHERE id ?", [id]);
const files = rows[0];
console.log("Files returned: ", files);

I get:

Files returned: {
    original_key: key_20250110-197.jpg',
    filename: '20250110-197.jpg'
  },
  {
    original_key: 'key_20250110-208.jpg',
    filename: '20250110-208.jpg'
  }

From what I see destructuring would work if the returned result was like

Rows returned:
[
  [
  {
    original_key: key_20250110-197.jpg',
    filename: '20250110-197.jpg'
  },
  {
    original_key: 'key_20250110-208.jpg',
    filename: '20250110-208.jpg'
  }
  ]
]

Am I doing something wrong? Is something misconfigured?
I need to use the results and do something like

files.map((file) => {
  // do stuff
});

But of course either I have 1 item or I have a list of Objects which .map() doesnt work with.

Failed to Send Data to Server Using AJAX in JavaScript

I’m experiencing an issue when trying to send data to the server using AJAX in JavaScript. Every time I attempt to submit a question, I receive the error message “Failed to send question: error.” I’ve checked my code, but I can’t seem to identify the cause.

Here is the JavaScript code I’m using to send the data:

let questions = [];
let itemsPerPage = 10;
let currentPage = 1;
let instansiList = [];
let captchaValue = '';

// Getting DOM elements
const alertMessage = document.querySelector('.alert-message');
const containerWidth = document.querySelector('.alert-container').offsetWidth;
const messageWidth = alertMessage.offsetWidth;

let position = containerWidth;

// Function for alert message animation
function animate() {
    position -= 2; 
    if (position < -messageWidth) {
        position = containerWidth; 
    }
    alertMessage.style.transform = `translateX(${position}px)`;
    requestAnimationFrame(animate); 
}

// Running animation on page load
window.onload = function() {
    animate();
};

// Preventing certain actions
document.addEventListener('contextmenu', e => e.preventDefault());
document.addEventListener('copy', e => {
    e.preventDefault();
    alert("Copying content is not allowed.");
});
document.addEventListener('selectstart', e => e.preventDefault());
document.addEventListener('keydown', event => {
    if (event.key === 'PrintScreen') {
        alert('Taking screenshots is not allowed.');
    }
});

// Main function when document is ready
$(document).ready(function() {
    $('#submitQuestion').click(function() {
        const topic = $('#topic').val();
        const instansi = $('#instansiInput').val();
        const nameOption = $('#nameOption').val();
        const name = nameOption === 'input' ? $('#name').val() : 'Anonymous';
        const question = $('#question').val();
        const timestamp = new Date().toISOString(); 
        const fileUpload = $('#fileUpload')[0].files[0]; 

        // Input validation
        if (!topic || !instansi || !question.trim()) {
            alert('Please fill in all required fields.');
            return;
        }

        if (countWords(question) > 1000) {
            alert('Question cannot exceed 1000 words.');
            return;
        }

        if (nameOption === 'input' && !/^[a-zA-Zs]+$/.test(name)) {
            $('#nameError').text('Name can only contain letters and spaces.').show();
            return;
        }

        // AJAX request to send data
        $.ajax({
            url: 'your-server-endpoint.php',
            type: 'POST',
            data: {
                topic: topic,
                instansi: instansi,
                name: name,
                question: question,
                timestamp: timestamp,
                file: fileUpload
            },
            success: function(response) {
                alert('Question submitted successfully!');
            },
            error: function(xhr, status, error) {
                alert('Failed to send question: ' + error);
            }
        });
    });
});

I’ve tried debugging the code, but I’m still unable to resolve the issue. Any help would be greatly appreciated! ### Additional Information:

  • Environment: I’m using jQuery version 3.5.1 and PHP 7.4 on a local server.

  • Browser: The issue occurs in both Chrome and Firefox.

  • Network: I’ve checked the network tab in the developer tools, and the request is being sent, but the response indicates a server error (500 Internal Server Error).

  • Server-Side Code: Here is a snippet of the PHP code handling the request:

    <?php 
    if ($_SERVER['REQUEST_METHOD'] === 'POST') {
        $topic = $_POST['topic'];
        $instansi = $_POST['instansi'];
        $name = $_POST['name'];
        $question = $_POST['question'];
        $timestamp = $_POST['timestamp'];
    
        // Validate inputs
        if (empty($topic) || empty($instansi) || empty($question)) {
            http_response_code(400);
            echo json_encode(['error' => 'All fields are required.']);
            exit;
        }
    
        // Process file upload if exists
        if (isset($_FILES['file']) && $_FILES['file']['error'] === UPLOAD_ERR_OK) {
            // Handle file upload
        }
    
        // Simulate saving to database
        // If there's an error, return a 500 error
        if (/* some error condition */) {
            http_response_code(500);
            echo json_encode(['error' => 'Failed to save question.']);
            exit;
        }
    
        echo json_encode(['success' => 'Question saved successfully.']);
    }
    ?>
    

I’ve ensured that the server is configured correctly, but I’m still facing this issue. Any insights or suggestions on how to troubleshoot this further would be greatly appreciated!

Back-end or Front-end? [closed]

I am starting to learn web development but I am not that creative to be a front end developer and I learned alot in html and js should I continue in front end and see how it goes or should I switch since im not that creative

which path should I take or what should I base my decision on?