How to prepend line number cells as a HTML column to a given table matrix?

What I have so far:

I’m working on a very generic table based on a configuration. This table is generated based on a graph, the final calculated data structure is

type TableMatrix = Cell[][];

interface Cell {
  isCoveredByPreviousCell: boolean;
  rowspan: number;
  offset: number;
  hasContent: boolean;
  columnIndex: number;
}

I grabbed data from a correct table to test with ( Playground with test data )

Sidenote: Based on this data I know there will be 14 columns to render

When it comes to rendering I decided to use VueJs ( Playground Link )

<script setup>
import { tableMatrix } from './data.ts'
</script>

<template>
  <table>
    <thead>
      <tr>
        <th v-for="headerIndex in 14">
          Header {{ headerIndex }}
        </th>
      </tr>
    </thead>
    <tbody>
      <tr v-for="(row, rowIndex) in tableMatrix" :key="rowIndex">
        <!-- Inser line number cell here -->
        <template v-for="(cell, columnIndex) in row" :key="columnIndex">
          <td v-if="cell.isCoveredByPreviousCell" :style="{ 'display': 'none' }"/>
          <td v-else :rowspan="cell.rowspan">
            <template v-if="cell.hasContent">
              <div>I belong to col {{ cell.columnIndex + 1 }}</div>
            </template>
          </td>
        </template>
      </tr>
    </tbody>
  </table>
</template>

<style>
table, th, td {
  border: 1px solid black;
}
</style>

What I want to achieve:

I want to add line numbers to every leading row.

A row is a leading row if every cell in that row is not covered by a previous cell. Pseudo example: (row /* Cell[] */ ).every((cell) => !cell.isCoveredByPreviousCell);

So for this example ( the first leading row in the first data column might have multiple cells but not in this example ) the desired output would be

enter image description here

What I’ve tried so far:

I thought I could create a structure that knows the correct row index for a line number cell and the required rowspan.

const lineNumbersMatrix = computed(() => {
  interface LineNumberInfo {
    index: number;
    rowspan: number;
  }

  const matrix = new Map<number, LineNumberInfo>();

  let currentLineIndex = 0;

  for (let rowIndex = 0; rowIndex < tableMatrix.value.length; rowIndex++) {
    const row = tableMatrix.value[rowIndex];
    
    // skip if not a leading row
    if (row.some((cell) => cell.isCoveredByPreviousCell)) {
      continue;
    }

    let lineNumberRowspan = 1;

    for (const cell of row) {
      if (lineNumberRowspan >= cell.rowspan) {
        continue;
      }

      lineNumberRowspan = cell.rowspan;
    }

    matrix.set(rowIndex, {
      index: currentLineIndex,
      rowspan: lineNumberRowspan
    });

    currentLineIndex ++;
  }

  return matrix;
});

After that I thought I could add a new table header

<th>Line</th>

and replace the comment

<!-- Inser line number cell here -->

with

<td v-if="lineNumbersMatrix.has(rowIndex)" :rowspan="lineNumbersMatrix.get(rowIndex)!.rowspan">{{ lineNumbersMatrix.get(rowIndex)!.index }}</td>

to get this Result, which seems to work as expected.

Unfortunately my real project renders an additional blank column at the end of the table

enter image description here

and I can’t reproduce it in the sandbox…

I’m assuming my approach is not 100% correct and there might be better ways. Do you have any ideas how to solve this?

Data is loaded after Fullcalendar ends

I’m using fullcalendar to create an agenda with teachers’ classes, but when entering the schedule screen the data does not appear (even though it was loaded in the request). When I change the week the data appears, but it is all at the end, after the calendar. Anyone knows why?

`items: [{region: 'center',xtype: 'panel',html: '<div id="calendar"></div>',listeners: {render: function() {var calendarEl = document.getElementById('calendar');
            var calendar = new FullCalendar.Calendar(calendarEl, {               plugins: ['bootstrap', 'interaction', 'dayGrid', 'timeGrid'],               locale: 'pt-br',               themeSystem: 'standard',               titleFormat: { year: 'numeric', month: 'numeric' },               header: {                 left: 'prev,next today',                 center: 'title',                 right: 'timeGridWeek,timeGridDay'               },               dayMaxEvents: true,               margin: '10',               contentHeight: '550',               defaultView: 'timeGridWeek',               slotDuration: '00:15:00',                   slotLabelInterval: '00:15:00',                allDaySlot: false,                   showNonCurrentDates: false,               minTime: '07:00:00',               maxTime: '18:00:00',               firstDay: 1,               events: function (info, successCallback, failureCallback) {                 Ext.Ajax.request({                     url: sUrlContabil('public') + '?func=buscaGradeProf',                     method: 'POST',                     params: {                         codUnidade: codUnidade,                         codProf: codOrigem,                         ano: nbAno.getValue(),                         inicio: dtObsInicio.getValue(),                         fim: dtObsFim.getValue()                     },                     success: function (response) {                         var dados = Ext.decode(response.responseText);                       let events = [];                                              console.log(response.responseText);                                    dados.results.forEach(function (grade) {                         // Partes do horário de início e fim                         let startTimeParts = grade.HORA.trim().split(':');                           let endTimeParts = grade.HORAFIM.trim().split(' ')[1].split(':');                           for (let dia = 1; dia <= 7; dia++) {                             if (grade[dia]) {                                 let title = grade[dia].replace(/<brs*/?>/g, '').trim();                                  let eventStart = adjustDateToDayOfWeek(new Date(info.start), dia);                                 let eventEnd = new Date(eventStart);                                  eventStart.setHours(parseInt(startTimeParts[0]), parseInt(startTimeParts[1]), 0);                                 eventEnd.setHours(parseInt(endTimeParts[0]), parseInt(endTimeParts[1]), 0);                                if (eventStart >= info.start && eventEnd <= info.end) {                                 events.push({                                   title: title,                                   start: eventStart.toISOString(),                                   end: eventEnd.toISOString(),                                   color: grade[`COR_${dia}`] || '#FFA500',                                   allDay: false                                 });                               }                               console.log('eventos:',eventStart, eventEnd);                               console.log("Event Start:", eventStart.toISOString());                               console.log("Event End:", eventEnd.toISOString());                              }                         }                         });                                      successCallback(events);                     },                     error: function() {                         failureCallback();                       }                 });             }                             });             calendar.render();         }     } }
],`
Data format:"HORA_DATA":"2024-01-26 07:00:00""HORAFIM":"2024-01-26 08:00:00""DATAINI":"01/02/2024""DATAFIM":"20/12/2024"

I already tried to first load the events and then call the calendar, but it didn’t work, the screen was blank.

Loop referencing again [closed]

I keep getting the same error message and it is not letting me save my script once written.
Unexpected loop error!

Trying to add a simple “Double Click” to edit table on an elearning module and produce a pdf report of the information captured.

snip of code

How to solve memory leak when updating CreateJS Text class at high speed?

I am getting a memory leak when updating Text class at high speed.

Here is my code:

var count = 0;
document.addEventListener( 'DOMContentLoaded', function() {
    var stage = new createjs.Stage("canvas");
    var func = function() {
        var text = count % 2 == 1 ? "C" :  "D"
        for(var i = 1; i <= 26; i++) {
            for(var j = 1; j <= 19; j++) {
                var t = new createjs.Text(text, "24px sans-serif", "DarkRed");
                var con_name = i.toString() + '_' + j.toString();
                var con = stage.getChildByName(con_name);
                if(con == null) {
                    con = new createjs.Container();
                    con.name = con_name;
                }else{
                    con.removeAllChildren();
                }
                con.x = i * 24;
                con.y = j * 24;
                con.addChild(t);
                stage.addChild(con);
                var a = con.bitmapCache;
                var bounds = con.getBounds();
                var margin = 4;
                if(con.cacheID == 0) {
                    con.cache(
                        bounds.x - margin,
                        bounds.y - margin,
                        bounds.width + margin * 2,
                        bounds.height + margin * 2
                    );
                }else{
                    con.updateCache();
                }
            }
        }
        count++;
    };
    setInterval(func, 100);
    createjs.Ticker.framerate = 15;
    createjs.Ticker.addEventListener("tick", stage);
});

In actual use, the numbers are displayed on multiple Text objects, we want to achieve fast changes.
Imagine something that displays multiple clocks in milliseconds.

At this time, the Text class needs to change at high speed, Chrome’s memory usage will gradually increase.

In the above code, though, it is cached and used, if you lose the cache, you will get memory leaks as well.

Is there any way to update more characters faster without memory leaks?

Updating text without cache or updating characters with cache both of them cause memory leakage.

How to Creating a Conditional Question in HTML [closed]

I want to create a conditional question. For example, I create a question (1) like this “Are you already working” with the answer options already or not yet.
then I created another question (2) like this “Where do you work”, where question 2 will be displayed if in question 1 the user answered yes.

My problem is, how to design the form, where for each question that is created there is a conditions menu that displays the name of each question that has been created, where later the creator can choose this question, the conditions are question X with answer Y

this is capture of my form :
enter image description here
in the image there is a button to add questions, and in the image there is a dropdown “Pilih pertanyaan Syarat” here I want to display all the questions that have been created

I have tried several methods, such as displaying the many indexes that have been created, but it doesn’t work either
line of code in question in line 116 – 120 or 224 – 256

<div class="mb-3">
    <label for="syarat" class="form-label">Syarat Pertanyaan</label>
    <select class="form-select form-select-lg mb-3" name="questions[{{ $index }}][condition_question]" id="syarat" required>
        <option value="" disabled selected>Pilih Pertanyaan Syarat</option>
        @foreach (old('questions', []) as $i => $question)
            @if($i !== $index)
                <option value="{{ $i }}" {{ old('questions.'.$index.'.condition_question') == $i ? 'selected' : '' }}>
                    {{ $question['question'] }}
                </option>
            @endif
        @endforeach
    </select>
</div>

this is my table code

<?php

use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('jawabans', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('form_id');
            $table->unsignedBigInteger('user_id');
            $table->unsignedBigInteger('template_id');
            $table->Integer('nowSection')->default(0);
            $table->Integer('maxSection')->default(0);

            $table->timestamps();
            

            $table->foreign('form_id')->references('id')->on('forms');
            $table->foreign('user_id')->references('id')->on('users');
            $table->foreign('template_id')->references('id')->on('templates');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('jawabans');
    }
};


<?php

use IlluminateDatabaseMigrationsMigration;
use IlluminateDatabaseSchemaBlueprint;
use IlluminateSupportFacadesSchema;

return new class extends Migration
{
    /**
     * Run the migrations.
     */
    public function up(): void
    {
        Schema::create('questions', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('template_id'); // Foreign key for the form to which the question belongs
            $table->text('question'); // The question text
            $table->string('type'); // Type of the question (text, radio, dropdown, etc.)
            $table->json('options')->nullable(); // Options for questions like radio, checkbox, dropdown
            $table->boolean('required')->default(false); // Whether the question is required or not
            $table->integer('section')->default(1);
            $table->timestamps();

            // Foreign key constraint
            $table->foreign('template_id')->references('id')->on('templates')->onDelete('cascade');
        });
    }

    /**
     * Reverse the migrations.
     */
    public function down(): void
    {
        Schema::dropIfExists('questions');
    }
};

this is my controller (Store) code

public function store(Request $request)
    {
        $user = Auth::user();
        $request->validate([ 
            'kategori_id' => 'required|numeric',
            'nama' => 'required|unique:templates|string|max:255',
            'questions' => 'required|array',
            'questions.*.question' => 'required|string',
            'questions.*.type' => 'required|string',
            'questions.*.section' => 'required|numeric',

        ]);

        // Generate the initial slug
        $slug = Str::of($request->nama)->slug('-');
        $originalSlug = $slug;

        // Check if slug already exists and modify it if necessary
        $counter = 1;
        while (DB::table('templates')->where('slug', $slug)->exists()) {
            // Add a counter to the slug if it already exists
            $slug = $originalSlug . '-' . $counter;
            $counter++;
        }

        $form = Template::create([
            'nama' => $request->nama,
            'slug' => $slug,
            'kategori_id' => $request->kategori_id,
            'user_id' => $user->id
        ]);

        foreach ($request->questions as $question) {
            $form->questions()->create($question);
        }

        $back = Kategori::findOrFail($request->kategori_id);
        return redirect()->route('templateDetail', ['kategori' => $back->slug])->with('success', 'Template Berhasil Dibuat.');
    }

this is my index.blade.php code

@extends('dashboard.Layout.main')

@section('main')
<div class="d-flex justify-content-between flex-wrap flex-md-nowrap align-items-center pt-3 pb-2 mb-3 border-bottom">
    <h1 class="h2">Create Formulir</h1>
</div>
<a href="/dashboard/menutemplate" class="btn btn-success mt-2 mb-2">Kembali</a>

<form method="POST" action="/dashboard/menutemplate/template">
    @csrf
    <div class="mb-3">
        <label for="form-title-input" class="form-label fs-2 fw-bold">Judul Template</label>
        <input type="text" class="form-control @error('nama') is-invalid @enderror" id="form-title-input" name="nama"
        value="{{ old('nama') }}" required>
        @error('nama')
            <div class="alert alert-danger">{{ $message }}</div>
        @enderror
    </div>
    
    <div class="mb-3">
        <label for="kategori_id" class="form-label">Kategori</label>
        <select class="form-select form-select-lg mb-3 @error('kategori_id') is-invalid @enderror" aria-label="Large select example" id="kategori_id" name="kategori_id" required>
            <option selected disabled>Pilih Kategori</option>
            @foreach ($kategori as $k)
                <option value="{{ $k->id }}" {{ old('kategori_id') == $k->id ? 'selected' : '' }}>{{ $k->nama }}</option>
            @endforeach
        </select>
        @error('kategori_id')
            <div class="alert alert-danger">{{ $message }}</div>
        @enderror
    </div>
    
    <div id="questions-container">
        @if(old('questions'))
            @foreach(old('questions') as $index => $question)
                <div class="question-item mb-4" data-index="{{ $index }}">
                    <div class="mb-3">
                        <div class="row">
                            <div class="col text-start">
                                <label class="form-label">Judul Pertanyaan</label>
                            </div>
                            <div class="col text-end">
                                <button type="button" class="btn btn-danger remove-question">Remove Question</button>
                            </div>
                        </div>
                        <div class="text-bg-light toolbar">
                            <button type="button" class="btn" data-command="bold"><b>B</b></button>
                            <button type="button" class="btn" data-command="italic"><i>I</i></button>
                            <button type="button" class="btn" data-command="underline"><u>U</u></button>
                            <button type="button" class="btn" data-command="createLink">Link</button>
                        </div>
                        <div contenteditable="true" class="editor form-control">{{ old('questions.'.$index.'.question') }}</div>
                        <input type="hidden" name="questions[{{ $index }}][question]" class="question-content" value="{{ old('questions.'.$index.'.question') }}">
                    </div>
                    <div class="mb-3">
                        <label class="form-label">Type</label>
                        <select class="form-control type-select" name="questions[{{ $index }}][type]" required>
                            <option value="text" {{ old('questions.'.$index.'.type') == 'text' ? 'selected' : '' }}>Text</option>
                            <option value="radio" {{ old('questions.'.$index.'.type') == 'radio' ? 'selected' : '' }}>Radio</option>
                            <option value="dropdown" {{ old('questions.'.$index.'.type') == 'dropdown' ? 'selected' : '' }}>Dropdown</option>
                            <option value="checkbox" {{ old('questions.'.$index.'.type') == 'checkbox' ? 'selected' : '' }}>Checkbox</option>
                            <option value="date" {{ old('questions.'.$index.'.type') == 'date' ? 'selected' : '' }}>Date</option>
                            <option value="email" {{ old('questions.'.$index.'.type') == 'email' ? 'selected' : '' }}>Email</option>
                            <option value="number" {{ old('questions.'.$index.'.type') == 'number' ? 'selected' : '' }}>Number</option>
                            <option value="range" {{ old('questions.'.$index.'.type') == 'range' ? 'selected' : '' }}>Range</option>
                            <option value="time" {{ old('questions.'.$index.'.type') == 'time' ? 'selected' : '' }}>Time</option>
                        </select>
                    </div>
                    <div class="mb-3 options-container" style="display: {{ in_array(old('questions.'.$index.'.type'), ['radio', 'dropdown', 'checkbox']) ? 'block' : 'none' }};">
                        <label class="form-label">Options</label>
                        <div class="options-list">
                            @if(old('questions.'.$index.'.options'))
                                @foreach(old('questions.'.$index.'.options') as $option)
                                    <div class="input-group mb-2">
                                        <input type="text" class="form-control" name="questions[{{ $index }}][options][]" value="{{ $option }}" placeholder="Option">
                                        <button type="button" class="btn btn-danger remove-option">Remove</button>
                                    </div>
                                @endforeach
                            @endif
                        </div>
                        <button type="button" class="btn btn-secondary add-option">Add Option</button>
                    </div>
                    <div class="mb-3">
                        <div class="row text-start">
                            <div class="col-sm-1 col-md-1">
                                <div class="form-check">
                                    <label class="form-label">Required</label>
                                </div>
                            </div>
                            <div class="col-sm-1 col-md-1">
                                <div class="form-check">
                                    <input class="form-check-input" type="radio" name="questions[{{ $index }}][required]" value="1" {{ old('questions.'.$index.'.required') == '1' ? 'checked' : '' }}>
                                    <label class="form-check-label">Ya </label>
                                </div>
                            </div>
                            <div class="col-sm-1 col-md-1">
                                <div class="form-check">
                                    <input class="form-check-input" type="radio" name="questions[{{ $index }}][required]" value="0" {{ old('questions.'.$index.'.required') == '0' ? 'checked' : '' }}>
                                    <label class="form-check-label">Tidak </label>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="mb-3">
                        <label for="section" class="form-label">Section</label>
                        <select class="form-select form-select-lg mb-3 @error('section') is-invalid @enderror" aria-label="Large select example" id="section" name="questions[{{ $index }}][section]" required>
                            @for ($i = 1; $i <= 10; $i++)
                                <option value="{{ $i }}" {{ old('questions.'.$index.'.section') == $i ? 'selected' : '' }}>{{ $i }}</option> 
                            @endfor    
                        </select>
                        @error('section')
                            <div class="alert alert-danger">{{ $message }}</div>
                        @enderror
                    </div>
                    {{-- Bagian dalam form HTML --}}
                    <div class="mb-3">
                        <label for="syarat" class="form-label">Syarat Pertanyaan</label>
                        <select class="form-select form-select-lg mb-3" name="questions[{{ $index }}][condition_question]" id="syarat" required>
                            <option value="" disabled selected>Pilih Pertanyaan Syarat</option>
                            @foreach (old('questions', []) as $i => $question)
                                @if($i !== $index)
                                    <option value="{{ $i }}" {{ old('questions.'.$index.'.condition_question') == $i ? 'selected' : '' }}>
                                        {{ $question['question'] }}
                                    </option>
                                @endif
                            @endforeach
                        </select>
                    </div>

                </div>
            @endforeach
        @endif
    </div>
    <button type="button" class="btn btn-secondary" id="add-question">Add Question</button>
    <button type="submit" class="btn btn-primary">Create</button>
</form>

<script>
    var oldQuestions = @json(old('questions', []));
    document.addEventListener('DOMContentLoaded', function () {
        let questionIndex = {{ old('questions') ? count(old('questions')) : 0 }};
        
        function toggleOptions(container, type) {
            const optionsContainer = container.querySelector('.options-container');
            optionsContainer.style.display = ['radio', 'dropdown', 'checkbox'].includes(type) ? 'block' : 'none';
        }

        function initializeToolbar(editor) {
            const toolbar = editor.previousElementSibling;
            toolbar.querySelectorAll('button').forEach(button => {
                button.addEventListener('click', () => {
                    const command = button.getAttribute('data-command');
                    if (command === 'createLink') {
                        const url = prompt('Enter the link here: ', 'http://');
                        document.execCommand(command, false, url);
                    } else {
                        document.execCommand(command, false, null);
                    }
                });
            });
        }

        function initializeEditor(editor) {
            editor.addEventListener('input', () => {
                const hiddenInput = editor.nextElementSibling;
                hiddenInput.value = editor.innerHTML;
            });
            initializeToolbar(editor);
        }

        function updateRemoveButtons() {
            const removeButtons = document.querySelectorAll('.remove-question');
            removeButtons.forEach(button => button.disabled = removeButtons.length === 1);
        }

        function createQuestionTemplate(index) {
            const questionContent = oldQuestions[index] ? oldQuestions[index].question : '';
            const questionType = oldQuestions[index] ? oldQuestions[index].type : 'text'
            return `
                <div class="question-item mb-4" data-index="${index}">
                    <div class="mb-3">
                        <div class="row">
                            <div class="col text-start">
                                <label class="form-label">Judul Pertanyaan</label>
                            </div> 
                            <div class="col text-end">
                                <button type="button" class="btn btn-danger remove-question">Remove Question</button>
                            </div> 
                        </div>  
                        <div class="text-bg-light toolbar">
                            <button type="button" class="btn" data-command="bold"><b>B</b></button>
                            <button type="button" class="btn" data-command="italic"><i>I</i></button>
                            <button type="button" class="btn" data-command="underline"><u>U</u></button>
                            <button type="button" class="btn" data-command="createLink">Link</button>
                        </div>
                        <div contenteditable="true" class="editor form-control"></div>
                        <input type="hidden" name="questions[${index}][question]" class="question-content">         
                    </div>  
                    <div class="mb-3">
                        <label class="form-label">Type</label>
                        <select class="form-control type-select" name="questions[${index}][type]" required>
                            <option value="text">Text</option>
                            <option value="radio">Radio</option>
                            <option value="dropdown">Dropdown</option>
                            <option value="checkbox">Checkbox</option>
                            <option value="date">Date</option>
                            <option value="email">Email</option>
                            <option value="number">Number</option>
                            <option value="range">Range</option>
                            <option value="time">Time</option>
                        </select>
                    </div>
                    <div class="mb-3 options-container" style="display: none;">
                        <label class="form-label">Options</label>
                        <div class="options-list">
                            <div class="input-group mb-2">
                                <input type="text" class="form-control" name="questions[${index}][options][]" placeholder="Option">
                                <button type="button" class="btn btn-danger remove-option">Remove</button>
                            </div>
                        </div>
                        <button type="button" class="btn btn-secondary add-option">Add Option</button>
                    </div>
                    <div class="mb-3">
                        <div class="row text-start">
                            <div class="col-sm-1 col-md-1">
                                <div class="form-check">
                                    <label class="form-label">Required</label>
                                </div>
                            </div>
                            <div class="col-sm-1 col-md-1">
                                <div class="form-check">
                                    <input class="form-check-input" type="radio" name="questions[${index}][required]" value="1">
                                    <label class="form-check-label">Ya </label>
                                </div>
                            </div>
                            <div class="col-sm-1 col-md-1">
                                <div class="form-check">
                                    <input class="form-check-input" type="radio" name="questions[${index}][required]" value="0">
                                    <label class="form-check-label">Tidak </label>
                                </div>
                            </div>
                        </div>
                    </div>
                    <div class="mb-3">
                        <label for="syarat" class="form-label">Syarat Pertanyaan</label>
                        <select class="form-select form-select-lg mb-3" name="questions[${index}][condition_question]" id="syarat" required>
                            <option value="" disabled selected>Pilih Pertanyaan Syarat</option>
                            @foreach (old('questions', []) as $i => $question)
                                @if($i !== $index)
                                    <option value="{{ $i }}" {{ old('questions.'.$index.'.condition_question') == $i ? 'selected' : '' }}>
                                        {{ $question['question'] }}
                                    </option>
                                @endif
                            @endforeach
                        </select>
                    </div>
                </div>
            `;
        }

        document.getElementById('add-question').addEventListener('click', function () {
            const questionsContainer = document.getElementById('questions-container');
            const newQuestion = document.createElement('div');
            newQuestion.innerHTML = createQuestionTemplate(questionIndex);
            questionsContainer.appendChild(newQuestion);
            initializeEditor(newQuestion.querySelector('.editor'));
            questionIndex++;
            updateRemoveButtons();
        });

        document.getElementById('questions-container').addEventListener('change', function (e) {
            if (e.target && e.target.classList.contains('type-select')) {
                const container = e.target.closest('.question-item');
                toggleOptions(container, e.target.value);
            }
        });

        document.getElementById('questions-container').addEventListener('click', function (e) {
            if (e.target && e.target.classList.contains('remove-question')) {
                e.target.closest('.question-item').remove();
                updateRemoveButtons();
            }

            if (e.target && e.target.classList.contains('add-option')) {
                const container = e.target.closest('.question-item');
                const optionsList = container.querySelector('.options-list');
                const newOption = document.createElement('div');
                newOption.classList.add('input-group', 'mb-2');
                newOption.innerHTML = `
                    <input type="text" class="form-control" name="questions[${container.dataset.index}][options][]" placeholder="Option">
                    <button type="button" class="btn btn-danger remove-option">Remove</button>
                `;
                optionsList.appendChild(newOption);
            }

            if (e.target && e.target.classList.contains('remove-option')) {
                e.target.parentElement.remove();
            }
        });

        document.querySelector('form').addEventListener('submit', () => {
            document.querySelectorAll('.editor').forEach(editor => {
                const hiddenInput = editor.nextElementSibling;
                hiddenInput.value = editor.innerHTML;
            });

            // Remove empty options
            document.querySelectorAll('input[name^="questions"][name$="[options][]"]').forEach(input => {
                if (!input.value.trim()) {
                    input.parentElement.remove();
                }
            });
        });

        updateRemoveButtons();

        // Initialize existing editors
        document.querySelectorAll('.editor').forEach(editor => initializeEditor(editor));
    });
</script>

@endsection

PHP: PHPSpreadSheet can’t save a file

I’m having trouble with an Excel file generation and download system. I need to generate an .xlsx file from the data in a table, and then have the user download it. However, I can’t manage to save the file. Do you know why?

There is my html and my js script:

        <form action="demandereparations_export.php" method="post">
            <input type="hidden" name="NomFichier" value="<?php echo 'DemRep'.date('Y-m-d_His').'.xlsx'?>"/>
            <div class="filtre">
                <?php echo isset($NomFichier) ? 'NomFichier est défini' : 'NomFichier n'est pas défini'; ?>
                <?php if(!empty($NomFichier)) { ?>
                    <a href="./components/com_teamsmart/views/dashboard/tmpl/<?php echo $NomFichier; ?>" download="<?php echo $NomFichier; ?>" id="downloadLink" class="btn btn-success" >
                        En cours
                    </a>
                <?php }?>
            </div>
        </form>
    </div>

    <script type="text/javascript">
        console.log('Script démarré');
         window.addEventListener('DOMContentLoaded', function() {
            var downloadLink = document.getElementById('downloadLink');
            console.log(downloadLink);
            if (downloadLink) {
                downloadLink.addEventListener('click', function(event) {
                    console.log('Téléchargement')
                });
            } else {
                console.log('Bouton introuvable');
            }
        })
    </script>

Then there’s my export file:

<script src="demandereparations.php"></script>

<?php
require 'vendor/autoload.php';

use PhpOfficePhpSpreadsheetSpreadsheet;
use PhpOfficePhpSpreadsheetWriterXlsx;

$filename =  'DemRep'.date('Y-m-d_His').'.xlsx';

$spreadsheet = new Spreadsheet();
$spreadsheet->getDefaultStyle()->getFont()->setName('Arial Nova');
$spreadsheet->getDefaultStyle()->getFont()->setSize(9);
$sheet = $spreadsheet->getActiveSheet();    

$sheet->setCellValue('A1', 'Employé.e');
$sheet->setCellValue('B1', 'Date');
$sheet->setCellValue('C1', 'N° TRPO');
$sheet->setCellValue('D1', 'N°SR');
$sheet->setCellValue('E1', 'Statut');

// $demandereparations = $this->demandereparations;
// echo $demandereparations;
// $rowIndex = 2;

// foreach ($demandereparations as $demande) {
//     if (in_array($demande['user_id'], $employes_ids)) {
//         $sheet->setCellValue('A' . $rowIndex, $demande['user_name']);
//         $sheet->setCellValue('B' . $rowIndex, $demande['date']);

//         $trpo = array_filter($demande['fields'], function($v) { return $v['alias'] == 'trpo'; });
//         $sr = array_filter($demande['fields'], function($v) { return $v['alias'] == 'sr'; });
//         $sheet->setCellValue('C' . $rowIndex, reset($trpo)['reponse']);
//         $sheet->setCellValue('D' . $rowIndex, reset($sr)['reponse']);
//         $statut = ($demande['statut'] == -1) ? "Non valide" : (($demande['statut'] == 0) ? "En attente" : (($demande['statut'] == 1) ? "En cours" : "Traitée"));
//         $sheet->setCellValue('E' . $rowIndex, $statut);
        
//         $rowIndex++;
//     }
// }

$writer = new Xlsx($spreadsheet);
$writer->save('./components/com_teamsmart/views/dashboard/tmpl/' . $filename);
?>

Multi-select issue on php , javascript and jquery [closed]

I am trying to have a multi select in my code, for example i want to have multi select on learning pathway, and i want to show qualifications based on learning_pathway selected and i want to show unit standards based on the qualifications selected and they all must be able to be multi selected and then stored back to database, please help, I think i have a conflict on my code with jquery or bootstrap how can i solve this problem. Please note I am very new on the industry

I tried to write the code on separate page and it worked fine but once I integrate it with main project its does not work as it should

How do I distinguish a user hitting Ctrl-c vs EOF using Deno’s prompt function?

Let’s say I have a simple program like this:

while (true) {
  const command = prompt('Enter something');
  if (!command) {
    Deno.exit(0);
  }
}

What I want to do is replicate bash’s behavior where:

  1. Hitting Ctrl-d exits the process
  2. Hitting Ctrl-c is effectively just a continue so it goes to the next iteration of the loop

But it looks like by default the prompt function returns null in either case. Since the signal handler is async, I can’t see how I’d use one to distinguish whether the input was Ctrl-c vs Ctrl-d. Is there another way to do this?

How can I extract [object Object] that I received from my request.query

I am trying to send a object to my backend through a object, but when I try to extract that I am getting [object Object].

These are the filters:
public filters: { [key: string]: any } = {};

And I add a key with a object as value to it as filter:
this.filters[key] = {my object};

Now in the backend when I try to get the object like this:

export async function myFunction(
    request: Request,
    response: Response,
) {
    const chartFilters = request.query?.chartFilters;
}

the chartFilters will display [object Object].

I’ve tried lots of things but kept getting errors.

Hope someone can help me.

Leaflet heatmap ‘getImageData’ Javascript error in Android Webview (chromium)

Each time I attempt to display a Leaflet heatmap in an Android WebView object I get the following error in logcat from chromium and the map never displays:

“Uncaught IndexSizeError: Failed to execute ‘getImageData’ on ‘CanvasRenderingContext2D’: The source height is 0.”, source: https://cdn.jsdelivr.net/gh/python-visualization/folium@main/folium/templates/leaflet_heat.min.js

Yet the exact same page loads and displays the heatmap just fine in a GeckoView browser – org.mozilla.geckoview.GeckoView.
(The WebView issue happens on both of my test devices – one runs OS#9 and the other OS#14.)

Has anyone experienced and resolved this WebView issue?
Thanks.

TypeError: Cannot read properties of null (reading ‘useContext’) – Material UI

I’m using MUI(Material UI) in my project.
Component that I’m trying to use is Button.
Check its official component docs here.
Code in which error is occuring :

import * as React from 'react';
import Button from '@mui/material/Button';
import DeleteIcon from '@mui/icons-material/Delete';
import SendIcon from '@mui/icons-material/Send';
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';

export default function IconLabelButtons() {
  return (
    <>
      <Button variant="outlined" startIcon={<ShoppingCartIcon />} sx={{color:'grey'}}>
        Add To Cart
      </Button>
      <Button variant="contained" endIcon={<ShoppingCartIcon />} sx={{ color: 'white', backgroundColor:'grey' }}>
        Add To Cart
      </Button>
    </>
  );
}

Complete error msg :

Cannot read properties of null (reading 'useContext')
TypeError: Cannot read properties of null (reading 'useContext')
    at Object.useContext (http://localhost:3001/static/js/bundle.js:77496:25)
    at useDefaultProps (http://localhost:3001/static/js/bundle.js:70496:50)
    at useDefaultProps (http://localhost:3001/static/js/bundle.js:67954:91)
    at SvgIcon (http://localhost:3001/static/js/bundle.js:68093:96)
    at renderWithHooks (http://localhost:3001/static/js/bundle.js:42667:22)
    at updateForwardRef (http://localhost:3001/static/js/bundle.js:45916:24)
    at beginWork (http://localhost:3001/static/js/bundle.js:47977:20)
    at HTMLUnknownElement.callCallback (http://localhost:3001/static/js/bundle.js:32923:18)
    at Object.invokeGuardedCallbackDev (http://localhost:3001/static/js/bundle.js:32967:20)
    at invokeGuardedCallback (http://localhost:3001/static/js/bundle.js:33024:35)

This code is working fine if I remove startIcon & startIcon options from the Button tag.
Working Code:

import * as React from 'react';
import Button from '@mui/material/Button';
import DeleteIcon from '@mui/icons-material/Delete';
import SendIcon from '@mui/icons-material/Send';
import ShoppingCartIcon from '@mui/icons-material/ShoppingCart';

export default function IconLabelButtons() {
  return (
    <>
      <Button variant="outlined"  sx={{color:'grey'}}>
        Add To Cart
      </Button>
      <Button variant="contained"  sx={{ color: 'white', backgroundColor:'grey' }}>
        Add To Cart
      </Button>
    </>
  );
}

However, I want to use both the options.
I’ve installed both mui/material & mui/icons-material deps in the project. Still facing aforementioned issue.

Could anyone from the community help me out to fix this.

Nashorn hitting some sort of limit with binding values

My code loads a Nashorn Script Engine binding values representing metadata fields and allows user to write Javascript to modify those metadata fields by passing the script to the eval method and then checking for modified metadata values. It was working fine but as I have increased the number of values added to the bindings I have unexpected issues that some binding variables values are missing or changed despite the script not modifying these values.

Screenshot to give some context

enter image description here

I am now able to mimic the problem with self contained example

import javax.script.ScriptContext;
import javax.script.ScriptEngine;

public class JsTest
{
     public static void main(String[] args) throws Exception
     {
         int numberOfBindingVars = Integer.parseInt(args[0]);
         ScriptEngine engine;
         for(int i=0; i<10; i++)
         {
             engine = new org.openjdk.nashorn.api.scripting.NashornScriptEngineFactory().getScriptEngine();
             for(int k=0;k<numberOfBindingVars;k++)
             {
                 engine.put("var"+k,"1963");
             }

             System.out.println(String.format("Bindings %d", engine.getBindings(ScriptContext.ENGINE_SCOPE).size()));
             for(int k=0;k<numberOfBindingVars;k++)
             {
                 if(engine.get("var"+k)==null)
                 {
                     System.out.println("Missing var"+k);
                 }
             }
         }
     }
}

Running this with paramter 400 works fine and gives me

Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400
Bindings 400

But if I increase parameter to 500 the binding count is inconsistent , although wierdly checking for each value doesnt reveal any missing.

Bindings 500
Bindings 499
Bindings 498
Bindings 497
Bindings 496
Bindings 495
Bindings 494
Bindings 500
Bindings 499
Bindings 498

And if run with 1000 I get inconsistent count again, also unable to find certain vars around the 500 mark

Bindings 1000
Bindings 999
Missing var448
Bindings 1000
Bindings 999
Missing var448
Bindings 998
Missing var448
Missing var449
Bindings 997
Missing var448
Missing var449
Missing var450
Bindings 996
Missing var448
Missing var449
Missing var450
Missing var451
Bindings 995
Missing var448
Missing var449
Missing var450
Missing var451
Missing var452
Bindings 994
Missing var448
Missing var449
Missing var450
Missing var451
Missing var452
Missing var453
Bindings 993
Missing var448
Missing var449
Missing var450
Missing var451
Missing var452
Missing var453
Missing var454

In all cases works okay the first time, but with the 500 and 1000 calls thesubsequent iterations are incorrect.

Seems like there is some other Nashorn process trimming the size of the bindngs, this must be a bug surely?

Would love some input with deeper knowledge of nashorn

How to inject JS package in html and use it

So I have a package built that just renders a react node to an element on screen.
Built with Vite.

enter image description here

Now I want to use it in an html file like so but I have a hard time trying to find how to actually get it to call the function from my package created.
All I want to do is create a simple package that I can then host somewhere, import it into another site I have and run the code there.

What am I doing wrong?

<!doctype html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <link rel="icon" type="image/svg+xml" href="/vite.svg" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Vite + React + TS</title>
    <script type="script" src="C:UsersMyselfDocumentsprojectsTryoutPOCdistindex.umd.cjs"></script>
  </head>
  <body>
    <div id="root"></div>
    <script>
        // renderMyPage("root") // ??? how do I run this?
    </script>
  </body>
</html>

Main.tsx

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App.tsx";
import "./index.css";


const renderMyPage = (rootId: string) => {
  createRoot(document.getElementById(rootId)!).render(
    <StrictMode>
      <App />
    </StrictMode>
  );
};

// renderMyPage("root")

export { renderMyPage }

TypeError: Cannot destructure property ‘formatMessage’ of ‘this.languageManager’ as it is undefined when sending a call invitation using ZEGOCLOUD

Problem Description: I am developing a video calling feature using the ZEGOCLOUD SDK in my React application. However, when I attempt to send a call invitation, I encounter the following error:
TypeError: Cannot destructure property ‘formatMessage’ of ‘this.languageManager’ as it is undefined.
This error occurs within the sendCallInvitation method. I have checked my configuration and ensured that all variables are set correctly, but I am still uncertain about the cause of this issue.
Relevant Code: Here’s the relevant section of my code:

import { useClerk } from '@clerk/clerk-react';
import { ZegoUIKitPrebuilt } from '@zegocloud/zego-uikit-prebuilt';

// Function to get URL parameters
export function getUrlParams(
  url: string = window.location.href
): URLSearchParams {
  const urlStr = url.split("?")[1];
  return new URLSearchParams(urlStr);
}

const Header = ({ imageUrl, name, options }) => {
  const { user } = useClerk();
  const roomID = getUrlParams().get("roomID") || randomID(5); // Generate room ID

  const handleVideoCall = async () => {
    try {
      const response = await fetch(`/api/zegocloud?userID=${user?.id}`);
      const { token, appID } = await response.json();
      const username =
        user?.fullName || user?.emailAddresses[0].emailAddress.split("@")[0];

      // Generate kit token for ZEGOCLOUD
      const kitToken = ZegoUIKitPrebuilt.generateKitTokenForProduction(
        appID,
        token,
        roomID,
        user?.id!,
        username
      );

      const zp = ZegoUIKitPrebuilt.create(kitToken);
      zp.addPlugins({ ZIM });

      const targetUser = {
        userID: user?.id!,  // The target user ID
        userName: username,  // The target user name
      };

      // Sending the call invitation
      await zp.sendCallInvitation({
        callees: [targetUser],
        callType: ZegoUIKitPrebuilt.InvitationTypeVideoCall,
        timeout: 60, // Timeout duration (seconds)
      });

      console.log("Call invitation sent successfully");
    } catch (err) {
      console.error("Error in video call:", err);
    }
  };

  return (
    <Card className="w-full flex rounded-lg items-center p-2 justify-between">
      <div className="flex items-center gap-2">
        <Link href="/conversations" className="block lg:hidden">
          <CircleArrowLeft />
        </Link>
        <Avatar className="h-8 w-8">
          <AvatarImage src={imageUrl} />
          <AvatarFallback>{name.substring(0, 1)}</AvatarFallback>
        </Avatar>
        <h2 className="font-semibold">{name}</h2>
      </div>
      <div className="flex gap-2">
        <Button id="videoCall" size="icon" variant="secondary" onClick={handleVideoCall}>
          <Video />
        </Button>
        {options ? (
          <DropdownMenu>
            <DropdownMenuTrigger>
              <Button size="icon" variant="secondary">
                <Settings />
              </Button>
            </DropdownMenuTrigger>
            <DropdownMenuContent>
              {options.map((option, id) => (
                <DropdownMenuItem
                  key={id}
                  onClick={option.onClick}
                  className={cn("font-semibold", {
                    "text-destructive": option.destructive,
                  })}
                >
                  {option.label}
                </DropdownMenuItem>
              ))}
            </DropdownMenuContent>
          </DropdownMenu>
        ) : null}
      </div>
    </Card>
  );
};

Reactjs button bug – parent receives new data – child component doesn’t update properly

reactjs bug — I have a node api and react shell and I am running into some bugs I am having difficulty in figuring out how to fix.

enter image description here

+blocking and favoriting in chat buggy chat – pane and about part (props changing issue)
^handling the props and child refreshes from parent to child?

user tbl holds 2 lists blocked, fav — if you block a person that was in your fav, they are auto removed from the fav list. If favourited they are removed from block list. userid is only in ONE list or none.

Data comes back to the parent and informs the child components that the relationship has changed – but its almost like I have to click twice… as if the props havent been updated properly?

parent chat component — pushes data to input and about pane

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { Grid } from "@mui/material";
import QuestionAnswerIcon from '@mui/icons-material/QuestionAnswer';

import ChatThread from './ChatThread';
import ChatAbout from './ChatAbout';
import ChatPane from './ChatPane';

import { fetchChats, fetchChatsById, createNewChatBetweenCurrentAndOthers, markAllMessagesInChatAsSeen /*, createNewChat, createNewChatBetweenUsers, fetchChatsMessageToId, markAMessageInChatAsSeen*/ } from '../../../actions/chatsAction';


import { buildChatArray, getAbout } from './ChatHelper';
import { fetchResources } from '../../../actions/resourcesAction';

import { sortArrayByDateAsc, sortArrayByDateDesc } from '../Utility/Utility';
import { getToken, getUserDetails, isLogged } from '../UserFunctions/UserFunctions';

import { scrollToBottom } from './CommonChatFunctions.js';

//import { initTracking, logPageView } from '../_SharedGlobalComponents/TrackingFunctions/_groupTracking';
//import { audioHandler } from '../_SharedGlobalComponents/AudioHandler/AudioHandler';

import './ChatBox.scss';

class ChatBox extends Component {
    
  constructor(props, context) {
    super(props, context);
    this.state = {
      hasNewMessage: false,
      currentChatId: -1,
      chatConversation: [],
      messageList:[],
      aboutUser: {},
      messageDiffCount: 0,
      oldChatConversationLength: 0
    };

    this.paneRef = React.createRef();
    this.submitChatFormHandler = this.submitChatFormHandler.bind(this);
    this.markAsRead = this.markAsRead.bind(this);
    this.getChatConversation = this.getChatConversation.bind(this);
    this.friendshipChange = this.friendshipChange.bind(this);

    this._isMounted = false;
  }

  componentDidMount(){
    this._isMounted = true;

    //console.log("chat room time");
    //initTracking();
    //logPageView();

    if(isLogged()){
      let that = this;
      //console.log("is loggedin");

      this.getResources(function(res){
        //console.log("resourced obtained", res);
        that.getMessageThreadList(function(res){
          //console.log("getMessageThreadList");
          //console.log("-------------xxxxxxxxxx res", res);
          
          that.snapToChatThread();
        });
        that.pollingUpdates();
      });
    }
  }

  snapToChatThread(){
    //console.log("snap chat");

    const getQueryParams = () => 
      this.props.location.search
        .replace('?', '')
        .split('&')
        .reduce((r,e) => {
           r[e.split('=')[0]] = decodeURIComponent(e.split('=')[1]);
           return r;
        }, {});

    //console.log("getQueryParams", getQueryParams());

    if(Object.keys(getQueryParams()).length !== 0 && getQueryParams().id){
      //console.log("get chat")
      this.getChatConversation(getQueryParams().id);
    }
  }

  getResources(callback){
    let that = this;

    let data = {};
    this.props.fetchResources(data, function(resp) {
      if(resp){
        let resources = resp.data;

        let countryOptions = [];
        for(let i = 0; i < resources.countries.length; i++){

          let obj = {
              "label": resources.countries[i].name,
              "value": resources.countries[i].code
          }
          countryOptions.push(obj);
        }

        resources["countries"] = countryOptions;

        that.setState({ 
          resources: resources,
          isResourcesReady: true
        });
        callback(resources);  
      }
    });
  }  

  pollingUpdates(){
    //if NEW notification messages -- start checking current chat
    //call get chat conversation id every 20 seconds?
    let that = this;
    let waitToCheck = 45000;

    //console.log("pollingUpdates----------")

    //console.log("-1-this._isMounted", this._isMounted)
   
    this.timer = setTimeout(() => {

      //console.log("-----------timer", this.timer)
      //console.log("-2-this._isMounted", this._isMounted)
      
      this.intervalTimer = setInterval(() => {

        //console.log("---------this.intervalTimer", this.intervalTimer)
        //console.log("-3-this._isMounted", this._isMounted)

        //this.getMessageThreadList(function(res){console.log("res",res)})
        if(that._isMounted && that.state.currentChatId !== -1){
          //this.getChatConversation(that.state.currentChatId);      
        
          that.replenish(that.state.currentChatId);
        } else{
          that.clearClocks();
        }

      }, waitToCheck);
      
    }, waitToCheck);

  }

  clearClocks(){
    console.log("clearClocks")

    if (this.timer) {
      clearTimeout(this.timer);
      this.timer = 0;
    }

    if (this.intervalTimer) {
      clearInterval(this.intervalTimer);
      this.intervalTimer = 0;
    }   
  }

  componentWillUnmount() {
    console.log("---unmount")
    this._isMounted = false;

    this.setState({
      currentChatId : -1
    })

    this.clearClocks();
  }

  replenish(currentChatId) {
    //console.log("--------------------replenish");
    this.getChatConversation(currentChatId);//refresh current chat pane
    this.getMessageThreadList(function(res){console.log("res",res)});//refresh side pane   
  }

  submitChatFormHandler(data){
    let that = this;

    //only send text to the server if there is text
    if(data && data.text.length>0){
      this.props.createNewChatBetweenCurrentAndOthers(this.state.currentChatId, data, getToken(), function(resp){
        that.replenish(that.state.currentChatId);
      });
    }
  }
 
  buildChatMessageObj(data){
    let chatArray = buildChatArray(data, getUserDetails().id);

    this._isMounted && this.setState({ chatConversation: chatArray, oldChatConversationLength: chatArray.length });
    
    this.setAboutUser();

    let lastChatItem = chatArray[chatArray.length - 1];

    let newMessageSinceLoad = this.state.messageDiffCount > 0 && !this.state.initialChatLoad;
    let isLastMessageByLoggedInUser = lastChatItem.user_id === getUserDetails().id;

    if(newMessageSinceLoad && !isLastMessageByLoggedInUser){
      //audioHandler("new-chat-message");
    }

    //if initial load is true -- scroll to bottom
    //if message diffcount has changed and its NOT initial load - a new message has come in - scroll to bottom
    if(
        (this.state.initialChatLoad) ||
        (newMessageSinceLoad)
      )  {
      this.timer = setTimeout(() => {
        scrollToBottom(this.paneRef.current);
        //this.scrollToBottom();//scroll to the bottom after getting all the chat convo
      }, 200);
    }
  }
 
  getChatConversation(id){
    let that = this;
    this._isMounted && this.setState({ currentChatId: id });

    if(this._isMounted){
      console.log("this._isMounted", this._isMounted)

      console.log("id", id);
      window.history.pushState("", "", 'chat?id='+id);//updates the url with the current chat id

      this.props.fetchChatsById(id, getToken(), function(resp){
        if(resp){
          
          if(resp.data){
            that.setState({ 
              chatConversationLength: resp.data.messageThreads.length,
              messageDiffCount: resp.data.messageThreads.length - that.state.oldChatConversationLength,
              initialChatLoad:  that.state.oldChatConversationLength === 0? true:false
            })
            that.buildChatMessageObj(sortArrayByDateDesc(resp.data.messageThreads, "created_at"));
          }
        }
      });

    }
  }

  buildMessageThreadListObj(data){
    //console.log("buildMessageThreadListObj", data)
    this._isMounted && this.setState({ messageList: sortArrayByDateAsc(data, "lastSeen") });
  }

  getMessageThreadList(callback){
    let that = this;

    //console.log("getMessageThreadList");
    
    //fetch chats for this user
    this.props.fetchChats(getToken(), function(resp){
      //console.log("resp", resp);

      if(resp){
        if(resp.data){
          //console.log("resp", resp);
          that.buildMessageThreadListObj(resp.data.chatThreads);

          that.timer = setTimeout(() => {
            callback(resp.data.chatThreads);
          }, 1);

        }        
      }
    });
    
  }

  markAsRead(){
    //console.log("mark ALL messages as seen");
    this.props.markAllMessagesInChatAsSeen(this.state.currentChatId, {}, getToken(), function(resp){
      //console.log("--markAllMessagesInChatAsSeen--resp", resp);
    });
  }

  setAboutUser(){
    let messageThread = this.state.messageList.filter(obj => {
      return obj.id.toString() === this.state.currentChatId.toString();
    })[0];

    let aboutUser = getAbout(messageThread, this.state.resources);

    this._isMounted && this.setState({ aboutUser: aboutUser });
  }

  friendshipChange(label, value) {
    console.log("friendshipChange------",label, value);
    
    this.replenish(this.state.currentChatId);
  }

  render() {
    return (
      <div className="Page">
        <div className="common-block chat-container">
          {!isLogged() &&
            <div className="shield"></div>
          }
          
          <Grid container spacing={0}> 
            <Grid item xs={12} sm={3}>
              <ChatThread 
                messageList={this.state.messageList}
                currentChatId={this.state.currentChatId}
                getChatConversation={this.getChatConversation}
              />  
            </Grid>   
            {this.state.currentChatId === -1 ?
              (
                <Grid item xs={12} sm={9}>
                  <div className="unselected-chat-message-container">
                    <div className="unselected-chat-message-wrapper">
                      <QuestionAnswerIcon className="unselected-chat-icon" />
                      <h2>Select a Conversation</h2>
                      <p>Try selecting a conversation</p>
                    </div>
                  </div>
                </Grid>
              ):
              (
                <>                  
                  <Grid item xs={12} sm={6}>
                    <ChatPane 
                      chatConversation={this.state.chatConversation}
                      submitChatFormHandler={this.submitChatFormHandler}
                      markAsRead={this.markAsRead}
                      paneRef={this.paneRef}
                      aboutUser={this.state.aboutUser}
                      friendshipChange={this.friendshipChange}
                    />
                  </Grid>
                  <Grid item xs={12} sm={3}>
                    <ChatAbout 
                      aboutUser={this.state.aboutUser}
                      friendshipChange={this.friendshipChange}
                    />
                  </Grid>             
                </>
              )
            }
          </Grid>
        </div>
      </div>
    )
  }
}

function mapStateToProps(state) {
  return {
    chatsData: state.chats,
    resourcesData: state.resources
  };
}

function mapDispatchToProps(dispatch) {
 return bindActionCreators({fetchChats, fetchChatsById, createNewChatBetweenCurrentAndOthers, markAllMessagesInChatAsSeen, fetchResources /*fetchChatsMessageToId, createNewChat, createNewChatBetweenUsers, markAMessageInChatAsSeen*/}, dispatch);
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(ChatBox))

chat input pane

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import { Grid } from '@mui/material';
import NotificationAvatar from '../NotificationHandler/NotificationAvatar';
import ChatInput from './ChatInput';

import parse from 'html-react-parser';

import { getUserDetails } from '../UserFunctions/UserFunctions';
import { getToken } from '../../_globals/UserFunctions/UserFunctions';
import { readUsers, comparisonUsers, favUsers, blockUsers, viewedUsers } from '../../../actions/userAction';

import moment from 'moment';
import './ChatPane.scss';

class ChatPane extends Component {
    
  constructor(props, context) {
    super(props, context);
    this.state = {
      isReady: false,
      //userId: this.props.match.params.userId
    };
    this._isMounted = false;

    this.blockHandler = this.blockHandler.bind(this);
  }

  //aboutUser

  componentDidUpdate(prevProps) {
    console.log("prevProps", prevProps)
    if (prevProps.aboutUser !== this.props.aboutUser) {

      console.log("CHANGED -- aboutUser", this.props.aboutUser)

      this.setState({ 
        isReady: false
      });


      let that = this;
      this.timer = setTimeout(() => {
        that.setState({ 
          isReady: true
        });
      }, 1);

    }
  }  

  componentDidMount(){
    this.setState({ 
      isReady: true
    });

    this._isMounted = true;
  }

  componentWillUnmount() {
    this._isMounted = false;
    clearInterval(this.timer);
    clearInterval(this.intervalTimer);    
  }

  showNewMessage(item){
    //check if YOU made the NEW message --- and if so don't show it your end as a new message
    if(!item.seen && (getUserDetails().id !== item.user_id)) {
      return true;
    }

    return false;
  }

  blockHandler(state){
    //console.log("click Block");

    //let userId = this.props.match.params.userId;
    let that = this;

    let data = {"id":this.props.aboutUser.id}
    
    this.props.blockUsers(data, getToken(), function(resp) {
      if(resp){
        console.log("resp-blockUsers", resp.data);
        //that.getComparisons();
        that.props.friendshipChange("blockChange", false); 
      }
    });    
  }

  render() {

    console.log("CHATPANE-------------this.props.aboutUser", this.props.aboutUser);


    return (
      <div className="chat-message-container">
        <div 
          className="chat-pane"
          ref={this.props.paneRef}
        >
        {          
          this.props.chatConversation.map((item, i) => {
            //console.log("xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxitem", item)

            return(
              <div key={i} className="chat-row">
                {this.showNewMessage(item) && (item.hasNewFlagId === item.i)  && 
                  <div className="new-message"><div className="new-message-text">New Messages</div></div>
                }
                <div className="chat-wrapper">
                  <Grid container spacing={1}> 
                    <Grid item xs={12} sm={2} md={1}>
                      <NotificationAvatar id={item.user_id} name={item.name} image={item.image}/>
                    </Grid>
                    <Grid item xs={12} sm={10} md={11}>
                      <div><h4>{item.name}</h4> {moment(item.date).format('MMM Do YY, h:mm a')}</div>
                      <div>{parse(item.subject)}</div>
                    </Grid>
                  </Grid>
                </div>                        
              </div>
            )
          })          
        }

        {this.state.isReady && (this.props.aboutUser?.relationState?.isLoggedInUserBlocked || this.props.aboutUser?.relationState?.isParticipantUserBlocked) &&
          <div className="chat-row">
            <div className="chat-wrapper">
              <p>Only visible to you</p>
              
              <div className="system-block-message">
              {/*participant has block -- */}
              {this.props.aboutUser?.relationState?.isParticipantUserBlocked && !this.props.aboutUser?.relationState?.isLoggedInUserBlocked &&
                <p>{this.props.aboutUser.name} has blocked you, you can no longer contact them.</p>
              }

              {/*loggedin has block -- */}
              {this.props.aboutUser?.relationState?.isLoggedInUserBlocked && !this.props.aboutUser?.relationState?.isParticipantUserBlocked && 
                <p>You have blocked {this.props.aboutUser.name}. If you wish to send a message, you will need to <a onClick={()=>this.blockHandler()}>unblock</a>.</p>
              }

              {/*both block -- */}
              {this.props.aboutUser?.relationState?.isLoggedInUserBlocked && this.props.aboutUser?.relationState?.isParticipantUserBlocked && 
                <p>Communication has been disabled. {this.props.aboutUser.name} can no longer contact you. If you wish to send a message, you will need to <a onClick={()=>this.blockHandler()}>unblock</a>.</p>
              }
              </div>                 
            </div>
          </div>
        }

        </div>
        <div className="chat-input-wrapper" onClick={this.props.markAsRead}>      
          {this.state.isReady && 
            <ChatInput 
              aboutUser={this.props.aboutUser}
              submitHandler={this.props.submitChatFormHandler}
            />
          }
        </div>
      </div> 
    )
  }
}

function mapStateToProps(state) {
  return {
    userData: state.user,
  };
}

function mapDispatchToProps(dispatch) {
 return bindActionCreators({blockUsers}, dispatch);
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(ChatPane))

toggle button usage in the about pane

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import NotificationAvatar from '../NotificationHandler/NotificationAvatar';
import SimpleTable from '../SimpleTable/SimpleTable';

import ToggleButton from '../../_globals/ToggleButton/ToggleButton';

import { getToken } from '../../_globals/UserFunctions/UserFunctions';
import { readUsers, comparisonUsers, favUsers, blockUsers, viewedUsers } from '../../../actions/userAction';

import './ChatAbout.scss';

class ChatAbout extends Component {
    
  constructor(props, context) {
    super(props, context);
    this.state = {
      isReady: false,
      //userId: this.props.match.params.userId
    };
    this._isMounted = false;

    this.favHandler = this.favHandler.bind(this);
    this.blockHandler = this.blockHandler.bind(this);
  }


  componentDidMount(){
    this.setState({ 
      isReady: true
    });

    this._isMounted = true;
  }

  componentWillUnmount() {
    this._isMounted = false;
    clearInterval(this.timer);
  }


  componentDidUpdate(prevProps) {
    console.log("prevProps", prevProps)
    if (prevProps.aboutUser !== this.props.aboutUser) {

      console.log("CHANGED -- aboutUser", this.props.aboutUser)

      
      this.setState({ 
        isReady: false
      });


      let that = this;
      this.timer = setTimeout(() => {
        that.setState({ 
          isReady: true
        });
      }, 1);
      
    }
  }  

  favHandler(){
    console.log("favHandler");
    let that = this;

    let data = {id: this.props.aboutUser?.id};
    
    this.props.favUsers(data, getToken(), function(resp) {
      if(resp){
        console.log("resp", resp);
        //changed relationship -- show change
      //  that.props.friendshipChange("favChange", false); 

        //that.timer = setTimeout(() => {
          that.props.friendshipChange("favChange", false); 
        //}, 1);

      }
    });

  }

  blockHandler(){
    console.log("blockHandler");
    //console.log("click Block");

    //let userId = this.props.match.params.userId;
    let that = this;

    let data = {"id":this.props.aboutUser?.id}
    
    this.props.blockUsers(data, getToken(), function(resp) {
      if(resp){
        console.log("resp-blockUsers", resp.data);
        //that.getComparisons();
       // that.props.friendshipChange("blockChange", false); 

        //that.timer = setTimeout(() => {
          that.props.friendshipChange("blockChange", false); 
        //}, 1);

      }
    });    
  }

  render() {
    console.log("xxxxxxxxxxxxx this.props.aboutUser", this.props.aboutUser);


    return (
      <div className="user-container">             
        <h3>About</h3>
        {this.props.aboutUser && Object.keys(this.props.aboutUser).length > 0 &&
          (
            <>
              <div className="user-info">
                <NotificationAvatar id={this.props.aboutUser.id} image={this.props.aboutUser.image} name={this.props.aboutUser.name} />
                <h3><a href={"/users/"+this.props.aboutUser.id}>{this.props.aboutUser.name}</a></h3>
                <h4>{this.props.aboutUser.memberType}</h4>
              </div>        
              <SimpleTable type="plain" rows={this.props.aboutUser.data.rows} />
            </>
          )
        }

        {this.state.isReady && this.props.aboutUser && Object.keys(this.props.aboutUser).length > 0 &&
          (
            <>
              <p>isLoggedInUserFavorite {this.props.aboutUser.relationState.isLoggedInUserFavorite? "True":"False"}</p>
              <p>isLoggedInUserBlocked {this.props.aboutUser.relationState.isLoggedInUserBlocked? "True":"False"}</p>
              
              <p>isParticipantUserFavorite {this.props.aboutUser.relationState.isParticipantUserFavorite? "True":"False"}</p>
              <p>isParticipantUserBlocked {this.props.aboutUser.relationState.isParticipantUserBlocked? "True":"False"}</p>
             
              <div className="user-relations">
                <ToggleButton 
                  label1="Unfavorite"
                  label2="Favorite"
                  toggle={this.props.aboutUser.relationState.isLoggedInUserFavorite}
                  onClick={this.favHandler}
                />
                <ToggleButton 
                  label1="Unblock"
                  label2="Block"
                  toggle={this.props.aboutUser.relationState.isLoggedInUserBlocked}
                  onClick={this.blockHandler}
                />
              </div>             

              {/*             
              <ToggleButton 
                label1="Unblock"
                label2="Block"
                //disabled={(this.state.user?.id === getUserDetails().id)}
                toggle={this.props.aboutUser.blockedDetails.isParticipantUserBlocked? true:false}
                onClick={(toggled)=> this.blockUser(toggled)}
              />
              */}
            </>
          )
        }
      </div> 
    )
  }
}

function mapStateToProps(state) {
  return {
    userData: state.user,
  };
}

function mapDispatchToProps(dispatch) {
 return bindActionCreators({favUsers, blockUsers}, dispatch);
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(ChatAbout))

code for the toggle button

import React, { Component } from 'react';
import { withRouter } from 'react-router-dom';
import { connect } from 'react-redux';
import { bindActionCreators } from 'redux';

import Button from '@mui/material/Button';

//import TooltipInfoHandler from '../TooltipInfoHandler/TooltipInfoHandler';
//import { getToken, redirectUrl } from '../UserFunctions/UserFunctions';
//import { createNewChat, createNewChatBetweenCurrentAndOthers } from '../../../actions/chatsAction';

//import './ConnectButton.scss';

class ToggleButton extends Component {
    constructor(props, context) {
      super(props, context);
      this.state = {
        //labelTrue: this.props.labelTrue,
        //labelFalse: this.props.labelFalse,
        toggle: this.props.toggle,
        label: this.props.toggle? this.props.label1:this.props.label2,
      };

      this.clicked = this.clicked.bind(this);
    }

    componentDidMount(){   
    }

    componentDidUpdate(prevProps) {
      let that = this;
      if (prevProps.toggle !== this.props.toggle) {
        
        that.setState({ 
          toggle: this.props.toggle,
          label: this.props.toggle? this.props.label1 : this.props.label2
        });
      }
    }

    componentWillUnmount(){
      //this._isMounted = false;
      clearInterval(this.timer);
    }

    clicked(){
      //console.log("clicked on but", this.props);

      let that = this;
      that.setState({ 
        toggle: !this.state.toggle,
        label: !this.state.toggle? this.props.label1 : this.props.label2
      });

      this.timer = setTimeout(() => {
        //console.log("that.state.toggle", that.state.toggle);
        //console.log("that.state.label", that.state.label);
        
        that.props.onClick(that.state.toggle);
      }, 1);

    }

    render() {
      {
        //console.log("xxxxxxxxxxx----------this.state.toggle", typeof this.state.toggle)
      }
      return (        
        <div className="toggle-wrap">
          <Button variant="contained" disabled={this.props.disabled} onClick={this.clicked} color={this.state.toggle? "secondary":"primary"}>
            {this.state.label}
          </Button>
          {/*<TooltipInfoHandler helper={this.props.toolTipText} />*/}                      
        </div>
      );
    }
}

function mapStateToProps(state) {
  return {};
}

function mapDispatchToProps(dispatch) {
 return bindActionCreators({}, dispatch);
}

export default withRouter(connect(mapStateToProps, mapDispatchToProps)(ToggleButton))