Why 2 parameters in sort

More specifically, why the 2 parameters in the .sort() function and why does subtracting them
sort them?

const speciesArray = 
[ {speciesName:'shark', numTeeth:50}, 
{speciesName:'dog', numTeeth:42}, 
{speciesName:'alligator', numTeeth:80}, 
{speciesName:'human', numTeeth:32}];


const sortSpeciesByTeeth = arr => arr.sort(
 (a, b) => {return a.numTeeth - b.numTeeth}
)

Unable to retrieve instance from Eureka server using JS client

I’m having a problem trying to get a service URL discover by eureka.

I’m using eureka-js-client to connect to Eureka and for testing purposes I’ve created two microservices, I’ve called it: ms1 and ms2.

What I’ve tried is:

  • Start Eureka server to allow services register into it
  • Start ms1 and register into Eureka
  • Start ms2, register into Eureka and get ms1 URL.

To accomplish this I’ve launched eureka server as a Spring Boot app using @EnableEurekaServer. This part works fine, I can access http://localhost:8761/ and see the dashboard.

Then, in my microservices I’ve this configuration

this._client = new Eureka({
                instance: {
                    app: 'ms1',
                    instanceId: 'ms1',
                    hostName: 'localhost',
                    ipAddr: '127.0.0.1',
                    statusPageUrl: `http://localhost:${port ? port : this._port}`,
                    healthCheckUrl: `http://localhost:${port? port : this._port}/health`,
                    port: {
                        '$': port? port: this._port,
                        '@enabled': true,
                    },
                    vipAddress: 'myvip',
                    dataCenterInfo: {
                        '@class': 'com.netflix.appinfo.InstanceInfo$DefaultDataCenterInfo',
                        name: 'MyOwn',
                    },
                },
                eureka: {
                    host: 'localhost',
                    port: 8761,
                    servicePath: '/eureka/apps/'
                },
            })

And the same for ms2 changing the name.

When I run the project it output registered with eureka: ms1/ms1 and services seems to be registered in eureka correctly:
enter image description here

But now the problem is trying to get the URL of one of the two services. From either of the two services, if I try to get the Eureka instances I always get an empty list.

I have this code:

let instances: any = this.getClient().getInstancesByAppId(microserviceName);
let instance = null;
let url = ''
if (instances != null && instances.length > 0) {
    instance = instances[0];
    let protocol = instance.securePort["@enabled"] == "true" ? "https" : "http";
    url = `${protocol}//${instance.ipAddr}:${instance.port.$}/`
}

Where in “microserviceName” variable I’ve tried:

  • “ms1”
  • “MS1”
  • “ms1/ms1”

But the response is always an empty array with this output:

Unable to retrieve instances for appId: ms1

So, what’s the problem? Have I missed something? I think the flow is correct:

  1. Start Eureka server.
  2. Register services into server.
  3. Look for instances in the server.

Thanks in advance.

React Card does not expand with search dropdown results

As you can see the search results contain around ten items. However, the result set is cut off at the bottom of the div of class div-summary

How can I modify the div-summary such that the search result set is shown?

enter image description here
enter image description here

.div-summary{
    text-overflow: ellipsis;
    white-space: nowrap;
    overflow: hidden;
    width: 25%;
    float:left;
    height: 100%;
    argin-top: 0px;
    margin-left: 2%;
    margin-bottom: 10px;
    display: inline;
}

Vue Js get h2 tags with

There are h2 tags in the article. I’m aiming to use H2 tags as a table of contents.

Is there a way to get these tags because the H2 tags come within the article? I tried methods like Replace, substring, but I couldn’t get only the h2 tags.

The post content comes as json like this

{
"link": "http://wordpress.example.com/2022/02/16/example/",
"title": { "rendered": "This is a test blog post" },
"content": {
   "rendered": "n<h2><strong>This is a blog test thread</strong></h2>nnnn<p>This is a blog test content This is a blog test content This is a blog test content This is a blog test content This is a blog test content This is a blog test content</p>nnnn<h2><strong>this is a blog test thread 2</strong></h2>nnnn<p>This is a blog test content This is a blog test content....
}

 <div itemprop="articleBody" class="post-text" v-html="post.content.rendered" />

i get the data with v-html as post.content.rendered variable

html

This is a blog test thread

This is a blog test content This is a blog test content ….

As you can see, I couldn’t get these tags because the h2 tags are embedded in the article. Is there a way to get the H2 tags?

Why array element is treated as proxy as raise error when mapping it?

In Laravel 8 / livewire 2.5 / alpinejs 3 application I make context menu for listing of data
and to show only 1 context menu on server side I senerate on server side array :

foreach( $this->newsDataRows as $nextNewsDataRow ) {
    $this->selectedNews[$nextNewsDataRow->id] = false;
}

in blade file :

selectedNews: @json($selectedNews),

and condition for context menu :

 <div class="show_context_menu_container" x-show="selectedNews[{{$news->id}}]"

 

Problem is that when context menu is selected and operation completed I trigger event to set all elements of
selectedNews into false :

window.addEventListener('hideAllSelectedNewsItems', event => {
    console.log('hideAllSelectedNewsItems::')
    console.log( this.selectedNews )

    this.selectedNews.map((nextSelectedNews, index) => {
        console.log('nextSelectedNews::')
        console.log(nextSelectedNews)
        this.selectedNews[index]= false
    })

})

  

But I got error : https://prnt.sc/YKDvN_MfbYBN
I wonder why this.selectedNews is treated as proxy and in which way it can be fixed ?

Thanks!

How can we add “`js“` using discord.js using typescript

In the
message.channel.send(output, {code: "js"});
code it always says Expected 1 arguments, but got 2. what the {code: “js”} do is that it adds “““ to the code how do i fix this?
keep in mind i am using WOKCommand handler.

import { ICommand } from "wokcommands";
import { Client, Message, MessageEmbed } from "discord.js";
import { inspect } from "util"

export default {
    category: 'Moderation',
    description: 'Deletes multiple messages at once.',

    ownerOnly: true,

    maxArgs: 1,
    expectedArgs: '[amount]',

    slash: false,

    callback: async ({client, message, args}) => {
        if (message.author.id !== '763857069413367878') return;

        const code = args.join(" ");
        if(!code) return `Please provide some code to evaluate`

        try {
            const result = await eval(code);
            let output = result;
            if(typeof result !== 'string') {
                output = inspect(result)
            }
            message.channel.send(output, {code: "js"});
        } catch (error) {
            message.channel.send('Evaluated some code but to long!')
        };
    },
} as ICommand

How to remove string datatype in append array of multiple objects through form data in react.js

I am integrating varientform.i have recieving array of object in state.but when im trying to submit data so im getting this kind of data in console

varientDetails: [
‘{“index”:0.2731402713862625,”sort”:”45″,”sku”:”5″,”waightorquantity”:”1″,”Unit”:””,”mrp”:””,”discount”:””,”price”:””,”stock”:””,”minstock”:””,”outofstock”:””}’
]

im using this code……
varientDetails.forEach(varient =>{formData.append(varientDetails[],JSON.stringify(varient))})

Error: Actions must be plain objects. Use custom middleware for async actions.?

I’m struggling to figure out what I’m doing wrong in my usernames autofill search field.

I’m trying to get the users from an API using thunk and to return them.

However for some reason, I keep getting this error. I have gone through a lot of similar problems and none of the solutions worked for me.

Maybe you can help?

Here is my code:

store/main.js

import { combineReducers, createStore, compose, applyMiddleware } from "redux";
import thunk from "redux-thunk";

import usernamesReducer from "./usernames";

const rootReducer = combineReducers({
  usernames: usernamesReducer,
});

const store = createStore(
  rootReducer,
  compose(
    applyMiddleware(thunk),
    window.__REDUX_DEVTOOLS_EXTENSION__ && window.__REDUX_DEVTOOLS_EXTENSION__()
  )
);

export default store;

store/usernames.js

export const LOADING_USERNAMES = "LOADING_USERNAMES";
export const USERNAMES_LOADED = "USERNAMES_LOADED";

const initialState = {
  loading: false,
  usernames: [],
};

const usernamesReducer = (state = initialState, action) => {
  switch (action.type) {
    case LOADING_USERNAMES:
      return {
        ...state,
        loading: true,
      };
    case USERNAMES_LOADED:
      return {
        ...state,
        loading: false,
        usernames: action.payload.usernames,
      };
    default:
      return state;
  }
};

export const loadingUsernames = () => ({
  type: LOADING_USERNAMES,
});

export const usernamesLoaded = (usernames) => ({
  type: USERNAMES_LOADED,
  payload: {
    usernames,
  },
});

export const getUsernameList = () => async (dispatch) => {
  dispatch(loadingUsernames());
  const response = await fetch("https://jsonplaceholder.typicode.com/users");
  return dispatch(usernamesLoaded(response.json()));
};

export default usernamesReducer;

And components/UsernameAutofill

import React, { useEffect } from "react";
import { useDispatch, connect } from "react-redux";
import {
  loadingUsernames,
  usernamesLoaded,
  getUsernameList,
} from "../../store/usernames";

const UsernameAutofill = ({
  isLoading,
  loadingUsernames,
  usernamesLoaded,
  getUsernameList,
  usernames,
}) => {
  const dispatch = useDispatch();

  useEffect(() => {
    dispatch(getUsernameList());
    // console.log(usernames);
  }, []);

  return (
    <>
      {isLoading && <div>loading</div>}
      <form autoComplete="off">
        <div>
          <input
            id="myInput"
            type="text"
            name="myCountry"
            placeholder="Country"
          />
        </div>
        <input type="submit" />
      </form>
    </>
  );
};

const mapStateToProps = (state) => ({
  isLoading: state.usernames.loading,
  usernames: state.usernames.usernames,
});

const mapDispatchToProps = {
  getUsernameList,
  loadingUsernames,
  usernamesLoaded,
};

export default connect(mapStateToProps, mapDispatchToProps)(UsernameAutofill);

Can you tell what I’m doing wrong? Any help is greatly appreciated 🙂

How do google services detect change in user session on web? [closed]

I noticed when I sign out from one of google services like google mail, all the other google services (for example calendar, drive) that I’m using in other browser tabs show session expired popup that redirects to login page.
The session expired popup shows almost immediately after I sign out.

I assume google auth server somehow sends notifications to google frontends when user session is changed. So, I looked into chromes network tab to see if websockets were used to implement such bidirectional communication, but no websockets seemed to be used.

How do google services implemented bidirectional communication between their frontends and auth server??

If server sent events were used, how can I see server sent events in chromes network tab?

Vue CLI build target lib -> externalize images (Vue 3)

I have a question regarding vue-cli’s target build lib. I have an app that produces a custom element following this documentation. Check ->

/* ce.js */
import { defineCustomElement } from 'vue';
import Chat from './App.ce.vue';
const element = defineCustomElement(Chat);
customElements.define('chat-widget', element);

The build command looks as followed ->

/* package.json */
"build-wc": "vue-cli-service build --target lib --inline-vue --name chat-widget --dest ./wwwroot src/ce.js"

This is actually working all fine but not exactly how i want it. My images are all generated inline which totally bloats my generated umd file. Also when i put my app on a server it refuses to load the images when inline because of Content-Security-Policy issues (another discussion).

Is there a way to tell webpack / vue-cli that i want my images in separate folder? Preferably in the destination folder under /img.

Any help would be much appreciated, thanks in advance!

How do I get the innerHTML while ignoring child elements? [duplicate]

I have an HTML div element with an innerHTML of x number. Through javascript, I add children to the element with their own innerHTML and append it to the existing div. But I can no longer call innerHTML to get the x in an easy way. This is probably a popular question but I haven’t found a solution that actually works. Is there any elegant way to do this with vanilla js or jquery? If there are any workaround like creating a variable that would be fine with me, but innerHTML I think is a good way to have both a variable and a visual representation.

Add aria-label to table header in datatable

let checkboxColumn = `<div class="checkbox c-checkbox" >
                          <label> <input type="checkbox"
                              ng-model="self.headerCheckBox" ng-change="self.headerSelected()"
                               /> <span
                              class="fa fa-check"></span>
                          </label>
                        </div>`;

   self.dtColumns = [
      DTColumnBuilder.newColumn("select").withTitle(checkboxColumn).notSortable(),
      DTColumnBuilder.newColumn("type").withTitle("Type").notSortable().withClass('min-width-100'),
      DTColumnBuilder.newColumn("ip").withTitle("Value").notSortable().withClass('min-width-250'),
      ]

I am creating a data table in which there is a checkbox in the header, clicking on it will select all the checkboxes of the row. The issue is that checkboxcolumn is getting rendered in aria-label as well. The snippet below will tell you what is getting rendered:

<th class="sorting_disabled" rowspan="1" colspan="1" style="width: 0px;" aria-label="
   <input type=&quot;checkbox&quot;
   ng-model=&quot;self.headerCheckBox&quot; ng-change=&quot;self.headerSelected()&quot;
   /> <span
   class=&quot;fa fa-check&quot;>
   ">
   <div class="checkbox c-checkbox">
      <label> <input type="checkbox" ng-model="self.headerCheckBox" ng-change="self.headerSelected()"> <span class="fa fa-check"></span>
      </label>
   </div>
</th>

You can see the content of the header and aria-label value are the same. How can I assign a different value to the aria-label? Help will be appreciated.

Angular: Testing apis from component

I have started learning how to test angular projects. So far basic unit testing is working fine for me but for the dependency testing especially when API services are injected into the component I am facing issue for providing HttpClient. I have tried different solutions but none is working for me.

Service

// Present in HttpClientService file
getDisposition() {
  return this.http.get<{ message: string, data: { dispositionList: Disposition[] } }>(`${this.URL}/disposition/get`);
}

// Present in FileProcessService file
deleteMedia(media: string) {
    return this.http.delete<{ message: string }>(`${this.URL}/certificate/delete?certificate=${media}`);
}

add-edit-activity.component.ts

import { HttpEventType } from '@angular/common/http';
import { Component, ElementRef, Inject, OnInit, ViewChild } from '@angular/core';
import { FormBuilder, Validators } from '@angular/forms';
import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { DomSanitizer, SafeUrl } from '@angular/platform-browser';
import { AssignedPerson } from '@model/assigned-person.model';
import { Disposition } from '@model/disposition.model';
import { mimeTypes } from '@model/mime-type';
import { FileProcessService } from '@service/file-process.service';
import { HttpClientService } from '@service/http-client.service';
import { DeleteComponent } from '@shared/delete/delete.component';
import { CustomErrorStateMatcher } from '@validator/error-state-matcher';
import { ToastrService } from 'ngx-toastr';

@Component({
  selector: 'app-add-edit-activity',
  templateUrl: './add-edit-activity.component.html',
  styleUrls: ['./add-edit-activity.component.css']
})
export class AddEditActivityComponent implements OnInit {

  constructor(private fb: FormBuilder, private sanitizer: DomSanitizer, private dialogRef: MatDialogRef<AddEditActivityComponent>, @Inject(MAT_DIALOG_DATA) public data: any,
    private _http: HttpClientService, private _fileService: FileProcessService, private toastr: ToastrService, private dialog: MatDialog,) { }

  re = new RegExp(/^[a-zA-Z-]*/, 'g');
  ISOstamp = { T: ' ', Z: '000' };
  matcher = new CustomErrorStateMatcher();
  dispositionList: Disposition[] = [];
  assignedPersonList: AssignedPerson[] = [];
  filterAssignedPersonList: AssignedPerson[] = [];
  uploaded = false;
  uploadProgress = false;
  fileURL: SafeUrl;
  activityForm = this.fb.group({
    gaugeId: [this.data.gaugeId, Validators.maxLength(9)], createTimeStamp: [{ value: new Date().toISOString().replace(/[TZ]/g, m => this.ISOstamp[m]), disabled: true }],
    user: [{ value: sessionStorage.getItem('username'), disabled: true }], disposition: ['', [Validators.required, Validators.maxLength(30)]],
    assignedPersonName: ['', Validators.maxLength(30)], department: ['', Validators.maxLength(20)],
    shift: ['', Validators.maxLength(1)], remark: ['', Validators.maxLength(50)],
    calibrationDate: ['', Validators.maxLength(10)], attachment: ['', Validators.maxLength(255)]
  });

  @ViewChild('file') certificate: ElementRef;

  ngOnInit(): void {
    this.data.type.match(this.re)[0] === 'Update' && this.setFormValues();
    this._http.getDisposition().subscribe(response => this.dispositionList = response.data.dispositionList);
    this._http.getAssignedPerson().subscribe(response => this.assignedPersonList = this.filterAssignedPersonList = response.data.assignedToList);
  }

  get GaugeId() {
    return this.activityForm.get('gaugeId');
  }

  get TimeStamp() {
    return this.activityForm.get('createTimeStamp');
  }

  get Attachment() {
    return this.activityForm.get('attachment');
  }

  get Disposition() {
    return this.activityForm.get('disposition');
  }

  get DispositionValue() {
    return this.dispositionList.map(e => e.dispositionType).indexOf(this.Disposition.value) < 0;
  }

  get AssignedTo() {
    return this.activityForm.get('assignedPersonName');
  }

  get AssignedToValue() {
    return this.assignedPersonList.map(e => `${e.firstName} ${e.lastName}`).indexOf(this.Disposition.value) < 0;
  }

  private async setFormValues() {
    this.activityForm.patchValue({ ...this.data });
    if (this.data.attachment) {
      this.uploadProgress = true;
      this.uploaded = true;
      await this.fetchUploadedFile(this.data.attachment, mimeTypes[this.data.attachment.split('.')[1]]);
      this.activityForm.markAsPristine();
    }
  }

  searchAssignedPerson(event) {
    if (event.target.value) {
      this.filterAssignedPersonList = [];
      for (let person of this.assignedPersonList) {
        if (person.firstName.toLowerCase().startsWith(event.target.value.toLowerCase())) {
          this.filterAssignedPersonList.push(person);
        }
      }
    } else { this.filterAssignedPersonList = this.assignedPersonList }
  }

  upload(event) {
    const file: File = event.target.files[0];
    this.certificate.nativeElement.value = '';
    if (file.size > (20 * 1000 * 1000)) { // Checking if File size is above 20MB
      this.toastr.error('Size of ' + file.name + ' is above 20MB');
      return;
    }
    const fd = new FormData();
    fd.append('certificate', file, file.name);
    this.processAttachment(fd);
  }

  private processAttachment(file: FormData) {
    this._fileService.uploadMedia(file).subscribe(event => {
      if (event.type === HttpEventType.UploadProgress) { this.uploadProgress = true }
      if (event.type === HttpEventType.Response) {
        let media = event.body.data.Certificate;
        this.fetchUploadedFile(media.fileName, media.fileType);
        this.toastr.info(event.body.message);
      }
    }, error => {
      this.toastr.error(error.error.message);
      this.uploadProgress = false;
    });
  }

  private async fetchUploadedFile(file: string, mimeType: string) {
    try {
      let response = await this._fileService.getMedia(file).toPromise();
      this.fileURL = this.sanitizer.bypassSecurityTrustUrl(URL.createObjectURL(new Blob([response], { type: mimeType })));
      this.uploaded = true;
      this.uploadProgress = false;
      this.activityForm.patchValue({ attachment: file });
      this.Attachment.markAsDirty();
    } catch (error) {
      this.uploadProgress = false;
      this._fileService.processError(error.error)
    }
  }

  deleteFile() {
    this.dialog.open(DeleteComponent, {
      width: '350px', disableClose: true
    }).afterClosed().subscribe(response => {
      if (response) {
        this._fileService.deleteMedia(this.Attachment.value).subscribe(response => {
          this.toastr.info(response.message);
          this.activityForm.patchValue({ attachment: '' });
          this.Attachment.markAsDirty();
          this.uploaded = false;
        }, error => this.toastr.error(error.error.message));
      }
    });
  }

  async doAction() {
    let message = '';
    if (this.data.type.match(this.re)[0] === 'Add') {
      message = await (await this._http.addActivityLog(this.activityForm.getRawValue()).toPromise()).message;
    } else {
      message = await (await this._http.updateActivityLog(this.GaugeId.value, this.TimeStamp.value, this.activityForm.getRawValue()).toPromise()).message;
    }
    this.dialogRef.close(message);
  }
}

add-edit-activity.component.spec.ts

import { ComponentFixture, TestBed, tick } from '@angular/core/testing';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog';
import { FileProcessService } from '@service/file-process.service';
import { HttpClientService } from '@service/http-client.service';
import { of } from 'rxjs';
import { delay } from 'rxjs/operators';
import { AddEditActivityComponent } from './add-edit-activity.component';

describe('AddEditActivityComponent', () => {
  let component: AddEditActivityComponent;
  let fixture: ComponentFixture<AddEditActivityComponent>;
  let _http: HttpClientService, _file: FileProcessService;

  beforeEach(async () => {
    await TestBed.configureTestingModule({
      declarations: [AddEditActivityComponent],
      imports: [FormsModule, ReactiveFormsModule],
      providers: [
        { provide: MatDialogRef, useValue: {} },
        { provide: MAT_DIALOG_DATA, useValue: { type: 'Add Activity Log', gaugeId: 'W-001' } },,
        { provider: HttpClientService, useValue: null },
        { provider: FileProcessService, useValue: null }
      ]
    }).compileComponents();
  });

  beforeEach(() => {
    fixture = TestBed.createComponent(AddEditActivityComponent);
    component = fixture.debugElement.componentInstance;
    _http = fixture.debugElement.injector.get(HttpClientService);
    _file = fixture.debugElement.injector.get(FileProcessService);
    fixture.detectChanges();
  });
  
  // These 2 tests throwing error.
  it('SHOULD mock http service', () => {
    let spy = spyOn(_http, 'getDisposition').and.callFake(() => {
      return of({
        message: '',
        data: { dispositionList: [] }
      }).pipe(delay(100));
    });
    component.ngOnInit();
    tick(100);
    expect(component.dispositionList).toEqual([]);
  });

  it('SHOULD mock file service', () => {
    let spy = spyOn(_file, 'deleteMedia').and.callFake((file: string) => {
      return of({ message: '' }).pipe(delay(100));
    });
    component.deleteFile();
    tick(100);
    expect(component.uploaded).toBe(false);
  });
});

The error that am getting for those 2 tests (I’m providing the error of 1 test case, the same is coming for the 2nd one also):

  1. SHOULD mock http service
    AddEditActivityComponent
    Error: Invalid provider for the NgModule ‘DynamicTestModule’ – only instances of Provider and Type are allowed, got: […, …, …,
    …, ?undefined?, …, …]
    at throwInvalidProviderError (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:240:1)
    at providerToFactory (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11550:1)
    at providerToRecord (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11521:1)
    at R3Injector.processProvider (node_modules/@angular/core/ivy_ngcc/fesm2015/core.js:11424:1)
    Error: : could not find an object to spy upon for getDisposition() Usage: spyOn(, ) Error: :
    could not find an object to spy upon for getDisposition() Usage:
    spyOn(, )
    at

Anyone, please help what’s the exact error happening with my test cases? Stuck here for almost 2 days.

Out of memory issue with Chrome and unit tests

I have a test suite of around 800 tests in a repository.
When I run the tests locally I end up with an Aw Snap screen at some point during the tests running.
It’s more or less running on the build server (running headless), but locally it breaks almost every time.

Is there any way to free up memory to make it possible to run through the tests locally again?
It’s an issue that recently started to appear and we haven’t changed the unit tests to something that should cause this issue so I am afraid that it has to do with a Chrome update or so.

We have an index file that runs
WCT.loadSuites([arrayOfSuites]) and each unit test have their own file
with suite(), setup and tests.

Thanks

React – Function calling API uses default state parameters instead of correct state parameters

I am having trouble with the below code. This is a basic table which displays users and I am trying to implement some sort functionality however it is not working.

When running I can click the sort buttons and they will update the sortParameters state accordingly, however, the function which requests the data from the API (getUsersFromAPI) does not recognise this state change. When running this function is supposed to fetch the data and then use the reducer to sort according to the updated sortParameters, but it instead is returning the usersList as it is received from the API and the console.log(sortParameters) returns the defaultSortParameters. I am not sure why sortParameters isn’t updating in this function when a sort button is clicked?

Any help would be greatly appreciated.

Thanks,

Greg

    useState,
    useReducer,
    useEffect,
    useLayoutEffect,
    useContext,
} from "react";
import { useUser } from "../../contexts/UserContext";
import { usersListReducer } from "../../reducers/usersListReducer";
import {
    API_users_usersGetAllUsers,
    API_users_DeleteUser,
} from "../../publicFunctions/usersAPI";

// reactstrap components
import {
    Card,
    CardHeader,
    CardBody,
    CardTitle,
    Table,
    Row,
    Col,
    ButtonGroup,
    Button,
} from "reactstrap";

import NewUser from "../Users/NewUser";
import classNames from "classnames";

const defaultSortParameters = { sortType: "NAME", sortDirection: true };

const Users = () => {
    //------------------------- STATE -------------------------
    const currentUser = useUser();
    const [showNewUser, setShowNewUser] = useState(false);
    const [sortParameters, setSortParameters] = useState(defaultSortParameters);
    const [usersList, usersDispatch] = useReducer(usersListReducer, []);

    //-------------------- CLICK HANDLING ---------------------
    const deleteUser = async (id, name) => {
        //check if trying to delete self
        if (currentUser.id === id) return alert("Cannot delete self");

        //check for delete confirmation
        if (
            window.confirm("Are you sure you want to delete user " + name) === true
        ) {
            //remove user from userList if OK status is returned
            const result = await API_users_DeleteUser(id);
            if (result.statusText === "OK") {
                usersDispatch({
                    type: "DELETE_USER_BY_ID",
                    payload: id,
                });
            }
        }
    };

    //sorting by column
    const sortUsers = (sortType) => {
        setSortParameters({
            ...sortParameters,
            sortType: sortType,
            sortDirection: !sortParameters.sortDirection,
        });
        usersDispatch({
            type: "SORT",
            payload: {
                sortType: sortType,
                sortDirection: sortParameters.sortDirection,
            },
        });

        console.log(sortParameters);
    };

    //----------------------- MOUNTING ------------------------
    //get users from API when component mounts
    useEffect(() => {
        //get initial data on mount
        getUsersFromAPI();
        //set interval to refresh data periodically
        const interval = setInterval(() => {
            getUsersFromAPI();
        }, 5000);
        return () => {
            clearInterval(interval);
        };
    }, []);

    //----------------------- UPDATING ------------------------
    const newUser = () => {
        setShowNewUser(!showNewUser);
    };

    //----------------------- FUNCTIONS -----------------------
    const getUsersFromAPI = async () => {
        let result = await API_users_usersGetAllUsers();
        if (result === undefined) {
            console.log("ERROR: Cannot get users in Users.js");
        } else {
            usersDispatch({
                type: "GET_USERS_FROM_API",
                payload: result,
            });
            usersDispatch({
                type: "SORT",
                payload: {
                    sortType: sortParameters.sortType,
                    sortDirection: sortParameters.sortDirection,
                },
            });
        }
        console.log(sortParameters);
    };

    //------------------------ RENDER -------------------------
    return (
        <>
            <div className="content">
                <button onClick={() => console.log(currentUser)}>user</button>
                <button onClick={() => console.log(sortParameters)}>sort params</button>
                <Row>
                    <Col md="12">
                        <Card>
                            <Col sm="6">
                                <ButtonGroup
                                    className="btn-group-toggle float-left"
                                    data-toggle="buttons"
                                >
                                    <Button
                                        className={classNames("btn-simple", {
                                            active: showNewUser === true,
                                        })}
                                        color="info"
                                        id="0"
                                        size="sm"
                                        onClick={newUser}
                                    >
                                        New user
                                    </Button>
                                </ButtonGroup>
                            </Col>

                            <Col>
                                {showNewUser ? (
                                    <NewUser
                                        setShowNewUser={setShowNewUser}
                                        getUsersFromAPI={getUsersFromAPI}
                                    />
                                ) : (
                                    <></>
                                )}
                                <h2>Need a search Bar</h2>
                            </Col>

                            <CardHeader>
                                <CardTitle tag="h4">Users</CardTitle>
                            </CardHeader>

                            <CardBody>
                                <Table className="tablesorter" responsive>
                                    <thead className="text-primary">
                                        <tr>
                                            <th>
                                                Name{" "}
                                                <button onClick={() => sortUsers("NAME")}>sort</button>
                                            </th>
                                            <th>
                                                Email
                                                <button onClick={() => sortUsers("EMAIL")}>sort</button>
                                            </th>
                                            <th>
                                                Description
                                                <button onClick={() => sortUsers("DESCRIPTION")}>
                                                    sort
                                                </button>
                                            </th>
                                            <th>
                                                Online
                                                <button onClick={() => sortUsers("ONLINE")}>
                                                    sort
                                                </button>
                                            </th>
                                            <input type="text" />
                                        </tr>
                                    </thead>
                                    <tbody>
                                        {usersList?.map((user) => {
                                            return (
                                                <tr>
                                                    <td>{user.name}</td>
                                                    <td>{user.email}</td>
                                                    <td>{user.description}</td>
                                                    {user.is_online === false ? (
                                                        <td> false </td>
                                                    ) : (
                                                        <>
                                                            {" "}
                                                            <td>true</td>{" "}
                                                        </>
                                                    )}
                                                    <td>
                                                        <Button
                                                            className={classNames("btn-simple")}
                                                            color="info"
                                                            id="deleteUser"
                                                            size="sm"
                                                            onClick={() => deleteUser(user.id, user.name)}
                                                        >
                                                            Delete
                                                        </Button>
                                                    </td>
                                                </tr>
                                            );
                                        })}
                                    </tbody>
                                </Table>
                            </CardBody>
                        </Card>
                    </Col>
                </Row>
            </div>
        </>
    );
};

export default Users;

reducer function sort code:

case "SORT":
            //Switch by sort type
            switch (action.payload.sortType) {
                case "NAME":
                    if (action.payload.sortDirection === true) {
                        sortedUsersList.sort((a, b) => (a.name > b.name ? 1 : -1));
                        return sortedUsersList;
                    } else {
                        sortedUsersList.sort((a, b) => (a.name < b.name ? 1 : -1));
                        return sortedUsersList;
                    }
                case "EMAIL":
                    if (action.payload.sortDirection === true) {
                        sortedUsersList.sort((a, b) => (a.email > b.email ? 1 : -1));
                        return sortedUsersList;
                    } else {
                        sortedUsersList.sort((a, b) => (a.email < b.email ? 1 : -1));
                        return sortedUsersList;
                    }
                case "DESCRIPTION":
                    if (action.payload.sortDirection === true) {
                        sortedUsersList.sort((a, b) =>
                            a.description > b.description ? 1 : -1
                        );
                        return sortedUsersList;
                    } else {
                        sortedUsersList.sort((a, b) =>
                            a.description < b.description ? 1 : -1
                        );
                        return sortedUsersList;
                    }

                case "ONLINE":
                    if (action.payload.sortDirection === true) {
                        sortedUsersList.sort((a, b) =>
                            a.is_online > b.is_online ? 1 : -1
                        );
                        return sortedUsersList;
                    } else {
                        sortedUsersList.sort((a, b) =>
                            a.is_online < b.is_online ? 1 : -1
                        );
                        return sortedUsersList;
                    }
            }