Add node modules to Webpack virtually

https://www.npmjs.com/package/webpack-virtual-modules


var virtualModules = new VirtualModulesPlugin({
  'node_modules/module-foo.js': 'module.exports = { foo: "foo" };',
  'node_modules/module-bar.js': 'module.exports = { bar: "bar" };'
});

module.exports = {
  // ...
  plugins: [
    virtualModules
  ]
};

I would like to add a node module to my app

node_modules/axios : https://cdnjs.cloudflare.com/ajax/libs/axios/1.7.7/axios.min.js

just wanted to add bundled node modules to my app
like vue2, lodash ,Moment.js,vue-loader ,vue-template-compiler, axios etc

yea I know about the externals in webpack.config that can’t be used in this case as I need to add vue-loader and vue-template-compiler in my virtual node modules

Module parse failed: Unexpected token (1:3)

I build a SSR project using webpack and it runs totally fine. But when I apply SCSS in my project, it give me an error:

ERROR in ./src/client/styles/Calculator.scss 1:3
Module parse failed: Unexpected token (1:3)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
h1 {
color: aqua;
| }
 @ ./src/client/pages/Calculator.tsx 40:0-35
 @ ./src/client/App.tsx 3:0-44 13:33-43
 @ ./src/server/server.tsx 9:0-32 23:40-43

I put the webpack config and package.json below:

webpack.config.cjs

const path = require('path')
const webpack = require('webpack')
const HtmlWebpackPlugin = require('html-webpack-plugin')

const babelLoader = {
  test: /.(ts|tsx)$/,
  exclude: /node_modules/,
  use: {
    loader: 'babel-loader',
    options: {
      presets: ['@babel/preset-env', ['@babel/preset-react', { runtime: 'automatic' }]]
    }
  }
}

const scssLoader = {
  test: /.s[ac]ss$/i,
  use: ['style-loader', 'css-loader', 'sass-loader']
}

const resolve = {
  extensions: ['.js', '.jsx', '.ts', '.tsx', '.scss']
}

const serverConfig = {
  target: 'node',
  mode: 'development',
  entry: './src/server/server.tsx',
  output: {
    path: path.join(__dirname, 'dist'),
    filename: 'server.cjs'
  },
  module: {
    rules: [babelLoader]
  },
  plugins: [
    new webpack.EnvironmentPlugin({
      PORT: 3000
    })
  ],
  resolve
}

const clientConfig = {
  target: 'web',
  mode: 'development',
  entry: './src/client/index.tsx',
  output: {
    path: path.join(__dirname, '/dist'),
    publicPath: '/static',
    filename: 'client.js'
  },
  module: {
    rules: [babelLoader, scssLoader]
  },
  plugins: [
    new HtmlWebpackPlugin({
      template: `${__dirname}/src/client/index.html`
    })
  ],
  resolve
}

module.exports = [clientConfig, serverConfig]

package.json

{
  "name": "udt-react-test",
  "version": "1.0.0",
  "main": "index.js",
  "scripts": {
    "clean": "rimraf ./dist",
    "build": "npm run clean && webpack --config webpack.config.cjs",
    "start": "npm run build && node ./dist/server.cjs",
    "lint": "eslint .",
    "lint:fix": "eslint . --fix",
    "prettier": "prettier --check .",
    "prettier:fix": "prettier --write .",
    "prepare": "husky",
    "lint-staged": "lint-staged",
    "test": "jest"
  },
  "keywords": [],
  "author": "",
  "license": "ISC",
  "description": "",
  "dependencies": {
    "@reduxjs/toolkit": "^2.3.0",
    "babel-loader": "^9.2.1",
    "express": "^4.21.1",
    "react": "^18.3.1",
    "react-dom": "^18.3.1",
    "react-redux": "^9.1.2",
    "react-router-dom": "^6.27.0",
    "ts-loader": "^9.5.1"
  },
  "devDependencies": {
    "@babel/core": "^7.26.0",
    "@babel/preset-env": "^7.26.0",
    "@babel/preset-react": "^7.25.9",
    "@babel/preset-typescript": "^7.26.0",
    "@eslint/js": "^9.14.0",
    "@types/express": "^4.17.21",
    "@types/jest": "^29.5.14",
    "@types/react": "^18.3.12",
    "@types/react-dom": "^18.3.1",
    "css-loader": "^7.1.2",
    "eslint": "^9.14.0",
    "eslint-config-prettier": "^9.1.0",
    "eslint-plugin-prettier": "^5.2.1",
    "html-webpack-plugin": "^5.6.3",
    "husky": "^9.1.6",
    "jest": "^29.7.0",
    "lint-staged": "^15.2.10",
    "mini-css-extract-plugin": "^2.9.2",
    "prettier": "^3.3.3",
    "rimraf": "^6.0.1",
    "sass": "^1.80.6",
    "sass-loader": "^16.0.3",
    "style-loader": "^4.0.0",
    "ts-jest": "^29.2.5",
    "tsc-alias": "^1.8.10",
    "tsx": "^4.19.2",
    "typescript": "^5.6.3",
    "typescript-eslint": "^8.13.0",
    "webpack": "^5.96.1",
    "webpack-cli": "^5.1.4",
    "webpack-dev-server": "^5.1.0"
  },
  "lint-staged": {
    "*.{ts,tsx}": [
      "eslint --fix",
      "prettier --write"
    ]
  }
}

I know the reason is it doesn’t process SCSS file, but I have installed sass-loader and put it in webpack.config.cjs but still have the error.

chosen js: Unselect an item by clicking on it

There is a drop-down multy select in which I would like to click on an element to remove it from the selected ones. Out of the box chosen.js apparently can not do this, how can you modify it a bit?

I tried to bind the standard function to elements, but it didn’t work. Please give me a hint

$("body").on("click", ".result-selected", function() {
    let index = $(this).attr("data-option-array-index");
    let container = $(this).closest(".chosen-container");
    let select = container.prev();
    
    if ( select != null && select.hasClass("chosen-select") ) {
        console.log(index);
        select
            .find("option:eq(" + index + ")")
            .prop("selected", false);
        select.trigger("chosen:updated").trigger('chosen:open');
    }
});

function is not available on component vue3

This is my app.vue js source code, I upgrade this code vue2 to vue 3 , but now when i try to access a function class from base.vue there is not found function error. how can i solve this ?i using this vue in laravel 11 project, thanks in advance

<template>
  <div class="container-test">
    <div id="loader-bg" v-show="loading">
      <div id="loader">
        <img src="/images/result/gif-load.gif" width="120" height="120" alt="Now Loading..." />
        <br><span>calculation</span><br><span>please wait some time。</span>
      </div>
    </div>
    <transition name="fade">
      <layout-common
        :timer="timer"
        :show="show"
        :storage="storage"
        :minutesdata="minutesdata"
        :secondsdata="secondsdata"
        :questioncategoryDataType="questioncategoryDataType"
        @time-up="timeUp"
        @trigger-push-result="timeUp">
        <router-view
          ref="contents"
          :rawData="rawData"
          :questionnaireData="questionnaireData"
          :exampleId="exampleId"
          :errored="errored"
          :loading="loading"
          :sessionData="sessionData"
          @data-post="dataPost"
          @start-timer="startTimer"
          @stop-timer="stopTimer"
          @storage-false="storageFalse"
          @stopping-timer="stoppingTimer"
          @storage-countdown="storageCountdown"
          @show-on="showOn"
          @show-off="showOff"
          @min="min"
          @sec="sec"
          @question-category="questionCategory">
        </router-view>
      </layout-common>
    </transition>
  </div>
</template>

<script>
import { ref, onMounted } from 'vue';
import axios from 'axios';
import { useRouter } from 'vue-router';

export default {
  setup() {
    const router = useRouter();
    const rawData = ref([]);
    const sessionData = ref(null);
    const questionnaireData = ref([]);
    const errored = ref(false);
    const loading = ref(true);
    const timer = ref(false);
    const show = ref(false);
    const storage = ref(false);
    const secondsdata = ref(0);
    const minutesdata = ref(0);
    const questioncategoryDataType = ref(null);
    const contents = ref(null);
    const surveyFlag = ref(null);
    const sessionResult = ref(null);
    const exampleId = ref(null); 

    // timeUp function
    const timeUp = (timeup) => {
        if (contents.value && contents.value.pushResult) {
          console.log("Calling pushResult function from app.js");
          contents.value.pushResult(timeup); // Correct function call
        } else {
          console.warn('pushResult not available in contents');
        }
      };

    const fetchData = async () => {
      loading.value = true;
      try {
        const response = await axios.get('/api/v1/test');
        rawData.value = response.data.categories;
        sessionData.value = response.data.meta.session_id;
        questionnaireData.value = response.data.surveys;
        localStorage.setItem('raw-data', JSON.stringify(rawData.value));
        exampleId.value = response.data.diagnosed;
        localStorage.setItem('session-data', JSON.stringify(sessionData.value));
        loading.value = false;
      } catch (error) {
        errored.value = true;
        window.alert('問題の取得に失敗しました。' + error);
        loading.value = false;
      }
    };

    onMounted(fetchData);

    const dataPost = async () => {
      loading.value = true;
      const sessionId = {
        session_id: JSON.parse(localStorage.getItem('session-data'))
      };
      const sendData = {
        meta: sessionId,
        categories: JSON.parse(localStorage.getItem('answer-data')),
        surveys: JSON.parse(localStorage.getItem('questionnaire-data')),
      };

      try {
        await axios.post('/api/v1/test', sendData);
        const surveyResponse = await axios.get('/api/v1/survey');
        surveyFlag.value = surveyResponse.data.result;
        sessionResult.value = surveyResponse.data.session_id;
        localStorage.setItem('survey-flag', JSON.stringify(surveyFlag.value));
        localStorage.setItem('session-result', JSON.stringify(sessionResult.value));
        errored.value = false;
        loading.value = false;
        goNext();
      } catch (error) {
        errored.value = true;
        loading.value = false;
        goNext();
      }
    };

    const startTimer = () => { timer.value = true; };
    const stopTimer = () => { timer.value = false; };
    const stoppingTimer = () => { timer.value = false; };
    const storageFalse = () => { storage.value = false; };
    const storageCountdown = () => { storage.value = true; };
    const showOn = () => { show.value = true; };
    const showOff = () => { show.value = false; };
    const min = (time) => { minutesdata.value = time; };
    const sec = (time) => { secondsdata.value = time; };
    const questionCategory = (category) => { questioncategoryDataType.value = category; };
    const goNext = () => { router.replace({ name: 'result' }); };

    return {
      rawData,
      sessionData,
      questionnaireData,
      errored,
      loading,
      timer,
      show,
      storage,
      secondsdata,
      minutesdata,
      questioncategoryDataType,
      contents,
      dataPost,
      startTimer,
      stopTimer,
      stoppingTimer,
      storageFalse,
      storageCountdown,
      showOn,
      showOff,
      min,
      sec,
      questionCategory,
      timeUp,
      goNext,
      exampleId
    };
  },
};
</script>

base.vue this is another base.vue name file here,when i timeup app.vue i want to call this function

<template>
  <router-view
    ref="getData"
    :currentTitle="currentData.title"
    :currentContent="currentData.content"
    :answerLength="currentData.answerLength"
    :currentDirection="currentData.attributes.direction"
    :correctText="currentData.attributes.popup_correct"
    :incorrectText="currentData.attributes.popup_incorrect"
    :buttonValue="buttonValue"
    :popupStatus="popupStatus"
    @memorized="memorized"
    @answered="answered"
    @reset-popup="resetPopup"
    @count-timer-question="countTimerQuestion"
    @count-timer-answer="countTimerAnswer">
  </router-view>
</template>


<script>
export default {
  props: {
    rawData: Array,
    isDemo: Boolean,
    questionCategory: String,
  },
  data: function() {
    return {
      categoryId: 1,
      groupsId: null,
      questionId: null,
      categoryData: [],
      groupsData: [],
      currentData: {
        id: null,
        title: '',
        content: [],
        choices: [],
        type: null,
        attributes: {
          direction: 0,
          popup_correct: '',
          popup_incorrect: ''
        }
      },
      buttonValue: [1, 2, 3, 4, 5, 6, 7, 8, 9, 0],
      answerData: [],
      start: false,
      allMinutes: null,
      min: null,
      sec: null,
      questioncategoryData: null,
      question_duration: 0,
      answer_duration: 0,
      countTimerQuestionData: 0,
      countTimerAnswerData: 0,
      popupStatus: '',
      isRetry: null,
      audioElement: {
        decision: null,
        correct: null,
        incorrect: null
      },
    }
  },
  created: function() {
    this.isRetry = localStorage.getItem('isRetry');
    this.isSkip = localStorage.getItem('isSkip');
    var answerData = JSON.parse(localStorage.getItem('answer-data'));
    var len = answerData.length;
    var isData = false;
    var index = null;
    for (var i = 0; i < len; i++) {
      if (answerData[i] && Number(answerData[i].id) === this.categoryId) {
        isData = true;
        index = i;
        break;
      }
    }
    if (isData) {
      this.groupsId = localStorage.getItem('groupsId');
      this.questionId = localStorage.getItem('questionId');
      if(this.groupsId !== null) {
        this.groupsId = Number(this.groupsId);
        this.questionId = Number(this.questionId);
      }
      var answerData = JSON.parse(localStorage.getItem('answer-data'));
      this.answerData = answerData[index].questions;
    } else {
      localStorage.removeItem('groupsId');
      localStorage.removeItem('questionId');
    }

    if(this.rawData.length > 0) {
      this.getCategory(this.rawData);
    }

    var lookIOS = navigator.userAgent.match(/iPad/i)|| navigator.userAgent.match(/iPhone/i);
    var loading = lookIOS ? "pagehide" : "beforeunload";
    var thisVue = this;
      window.addEventListener(loading, function(e) {
      thisVue.pushResult();
    }, false);

    this.soundLoad();
  },
  mounted: function() {
    if(this.isDemo == false){
      this.$emit('show-on');
      this.$emit('start-timer');
      this.$emit('question-category',this.questionCategory);
    }
     
      this.pushResult(false); 
  
  },
  methods: {
    getCategory: function(rawData) {
      localStorage.setItem('categoryId', String(this.categoryId));

      this.allMinutes = Number(rawData[this.categoryId - 1].duration);
      this.min = this.allMinutes/1000/60;
      this.sec = this.allMinutes/1000%60;
      this.$emit('min',this.min);
      this.$emit('sec',this.sec);

      this.categoryData = rawData[this.categoryId - 1].groups;
      if(this.groupsId == null) {
        this.groupsId = Number(this.categoryData[0].id);
      }
      this.getGroups();
    },
    getGroups: function() {
      var len = this.categoryData.length;
      var isEnd = true;
      for (var i = 0; i < len; i++) {
        if(Number(this.categoryData[i]['id']) === this.groupsId) {
          localStorage.setItem('groupsId', String(this.groupsId));
          if (this.isDemo && this.categoryData[i].attributes.is_real === 1) {
            break;
          } else {
            isEnd = false;
            this.groupsData = [];

            var thisQuestions = this.categoryData[i].questions;
            this.groupsData = thisQuestions;
            this.setCurrent();
            break;
          }
        }
      }
      return isEnd;
    },
    setCurrent: function(next) {
      if(this.questionId === null) {
        this.questionId = Number(this.groupsData[0].id);
      } else if (next) {
        this.questionId++;
      }
      localStorage.setItem('questionId', String(this.questionId));

      var isEnd = true;
      var len = this.groupsData.length;
      for (var i = 0; i < len; i++) {
        if (Number(this.groupsData[i]['id']) === this.questionId) {
          var isEnd = false;
          this.currentData = this.groupsData[i];

          if(Number(this.currentData.type) === 1) {
            var brIndex = this.currentData.title.indexOf('<br>');
            if(brIndex === -1) {
              this.currentData.title = this.currentData.title.replace(/。/,"$&<br>");
            }
            if(this.isDemo) {
              this.$router.replace({name: 'number-memory-demo-memorize'});
            } else {
              this.$router.replace({name: 'number-memory-memorize'});
            }
          } else if (Number(this.currentData.type) === 3) {
            var brIndex = this.currentData.title.indexOf('<br>');
            if(brIndex === -1) {
              this.currentData.title = this.currentData.title.replace(/test/, "$&<span>");
              this.currentData.title = this.currentData.title.replace(/left toright/g,"<span class='left'>$&</span>").replace(/右から順に/g,"<span class='right'>$&</span>").replace(/。/,"$&</span><br>");
            }
            if(this.isDemo) {
              var popupBrIndex = this.currentData.attributes.popup_incorrect.indexOf('<br>');
              if(popupBrIndex === -1) {
                this.currentData.attributes.popup_incorrect = this.currentData.attributes.popup_incorrect.replace(/test、/,"<span>$&</span><span>")

                this.currentData.attributes.popup_incorrect = this.currentData.attributes.popup_incorrect.replace(/。/,"$&</span><br>").replace(/「/,"<br>$&");
              }
            }

            this.currentData.answerLength = this.currentData.choices.length;

            if(this.isDemo) {
              this.$router.replace({name: 'number-memory-demo-answer'});
            } else {
              this.$router.replace({name: 'number-memory-answer'});
            }
          }
          break;
        }
      }
      return isEnd;
    },
    memorized: function(data) {
      if(this.isSkip == 0){
        try{
          this.audioElement.decision.pause();
          this.audioElement.decision.currentTime = 0;
          this.audioElement.decision.play();
        } catch( e ) { }
      }
      this.question_duration = this.countTimerQuestionData * 1000;

      if(this.isDemo && this.isRetry === '1') {
        var len = this.answerData.length;
        for(var i = 0; i < len; i++) {
          if (this.answerData[i] && Number(this.answerData[i]['id']) === this.questionId) {
            this.answerData[i]["duration"] += this.question_duration;
            break;
          }
        }
      } else {
        var thisAnswerData = {
          'id': String(this.currentData.id),
          "answer": null,
          'duration': this.question_duration
        }
        console.log(thisAnswerData);
        this.answerData.push(thisAnswerData);
      }
      this.setCurrent(true);
    },
    answered: function(data) {
      if (this.isDemo && this.isSkip != 1 && this.popupStatus !== 'correct') {
        var len = this.currentData.choices.length;
        var isCorrect = true;
        for (var i = 0; i < len; i++) {
          if(this.currentData.choices[i] !== data[i]) {
            isCorrect = false;
            break;
          }
        }
        if (isCorrect) {
          try{
            this.audioElement.correct.pause();
            this.audioElement.correct.currentTime = 0;
            this.audioElement.correct.play();
          } catch( e ) { }
          this.popupStatus = 'correct';
        } else {
          try{
            this.audioElement.incorrect.pause();
            this.audioElement.incorrect.currentTime = 0;
            this.audioElement.incorrect.play();
          } catch( e ) { }
          this.popupStatus = 'incorrect';
        }
      } else {
        if(this.isSkip == 0){
          try{
            this.audioElement.decision.pause();
            this.audioElement.decision.currentTime = 0;
            this.audioElement.decision.play();
          } catch( e ) { }
        }
        this.answer_duration = this.countTimerAnswerData * 1000;

        if(this.isDemo) {
          if(this.isRetry === '1') {
            var len = this.answerData.length;
            for(var i = 0; i < len; i++) {
              if (this.answerData[i] && Number(this.answerData[i]['id']) === this.questionId) {
                this.answerData[i]["duration"] += this.answer_duration;
                break;
              }
            }
          } else {
            var thisAnswerData = {
              "id": String(this.currentData.id),
              "answer": null,
              "duration": this.answer_duration
            }
            console.log(thisAnswerData);
            this.answerData.push(thisAnswerData);
          }
          this.resetPopup();
        } else {
          var thisAnswerData = {
            "id": String(this.currentData.id),
            "answer": data.concat(),
            "duration": this.answer_duration
          }
            console.log(thisAnswerData);
          this.answerData.push(thisAnswerData);
        }
        this.questionId++;

        var isQuestionsEnd = this.setCurrent();
        if (isQuestionsEnd) {
          this.questionId = null;
          this.groupsId++;
          var isGroupsEnd = this.getGroups();
          if (isGroupsEnd) {
            var answerData = JSON.parse(localStorage.getItem('answer-data'));
            var len = answerData.length;
            for (var i = 0; i < len; i++) {
              if (answerData[i] && Number(answerData[i].id) === this.categoryId) {
                answerData[i].questions = this.answerData;
                break;
              }
            }
            localStorage.setItem('answer-data', JSON.stringify(answerData));
            if(this.isDemo) {
              this.$router.replace({name: 'number-memory-ready'});
            } else {
              localStorage.removeItem('groupsId');
              localStorage.removeItem('questionId');
              this.$router.replace({name: 'number-memory-end'});
            }
          }
        }
      }
    },
    countTimerQuestion: function(time){
      this.countTimerQuestionData = time;
    },
    countTimerAnswer: function(time){
      this.countTimerAnswerData = time;
    },
      
        pushResult(timeup) {
        console.log("Push result triggered with timeup:", timeup);

        var answerData = JSON.parse(localStorage.getItem('answer-data'));
        console.log("Existing answer-data in localStorage:", answerData);

        var duration;
        if (Number(this.currentData.type) === 1) {
          duration = this.countTimerQuestionData * 1000;
        } else {
          duration = this.countTimerAnswerData * 1000;
        }

        var thisAnswerData = {
          id: String(this.currentData.id),
          answer: null,
          duration: duration,
        };

        console.log("Data to be pushed: ", thisAnswerData);
        this.answerData.push(thisAnswerData);
        console.log("Updated answerData array: ", this.answerData);

        this.$emit('trigger-push-result', timeup);
      },


    resetPopup: function() {
      if(this.isSkip == 0){
        try{
          this.audioElement.decision.pause();
          this.audioElement.decision.currentTime = 0;
          this.audioElement.decision.play();
        } catch( e ) { }
      }
      this.popupStatus = '';
    },
    soundLoad: function() {
      this.audioElement.decision = new Audio('../../../sound/decision.mp3?v=20220227');
      this.audioElement.correct = new Audio('../../../sound/correct.mp3?v=20220227');
      this.audioElement.incorrect = new Audio('../../../sound/incorrect.mp3?v=20220227');
    },
  },
  watch: {
    rawData: function(rawData) {
      this.getCategory(rawData);
    }
  }
}
</script>

I try to post data direactly from base.vue that is also no possible

What if the step attribute and stepUp(step) method both are defined

I was experimenting with the step property and stepDown , stepUp method of a date input.
While experimenting I stumbled upon the following. But I can not rationalize the output.

Here is the code I tried.

const date = document.querySelector("input[type='date")
const stepUp = document.querySelector("input#stepUp")                            
const stepDown = document.querySelector("input#stepDown")

stepDown.addEventListener("click",()=>{
    date.step = 10
    date.stepDown(20)
})
stepUp.addEventListener("click",()=>{
    date.step = 10
    date.stepUp(20)
})

calculate shift hours with break time deductions starting from current night to next day morning using google apps script

I’m trying to calculate the shift hours in a day, accounting for different types of shifts (Normal, Evening, and Night), and also considering overlapping break times during those shifts. Here’s the breakdown of the shifts:

  • Normal Hours: From 06:00 – 18:00. A 30-minute break occurs between
    12:00 – 12:30.
  • Evening Hours: From 18:00 – 21:00. A 30-minute break
    occurs between 20:00 – 20:30.
  • Night Hours: From 21:00 – 06:00. A
    30-minute break occurs between 04:00 – 04:30.

Break Time Rules:

  • If the shift is less than 6 hours, no break is given.
  • If the shift is 6 hours, a 20-minute break is provided.
  • For shifts that are longer than 6 hours, a 30-minute break is provided.

I need to calculate the hours worked in each shift category. Hers is the code which partly works:

function calculateShiftHours(startShift, endShift) {
  const parseTime = (timeStr) => {
    const [hours, minutes] = timeStr.split(":").map(Number);
    return hours * 60 + minutes;
  };

  const formatHours = (minutes) => (minutes / 60).toFixed(2);

  const NORMAL_START = parseTime("06:00");
  const NORMAL_END = parseTime("18:00");
  const EVENING_START = parseTime("18:00");
  const EVENING_END = parseTime("21:00");
  const NIGHT_START = parseTime("21:00");
  const NIGHT_END = parseTime("06:00");

  const BREAK_TIMES = {
    normal: { start: parseTime("12:00"), end: parseTime("12:30") },
    evening: { start: parseTime("20:00"), end: parseTime("20:30") },
    night: { start: parseTime("04:00"), end: parseTime("04:30") },
  };

  const startTime = parseTime(startShift);
  const endTime = parseTime(endShift) < startTime ? parseTime(endShift) + 1440 : parseTime(endShift);
  const shiftDuration = endTime - startTime;

  let breakDuration = 0;
  if (shiftDuration >= 6 * 60 && shiftDuration < 6.5 * 60) breakDuration = 20;
  else if (shiftDuration >= 6.5 * 60) breakDuration = 30;

  const calculateOverlap = (start, end, rangeStart, rangeEnd) => {
    const overlapStart = Math.max(start, rangeStart);
    const overlapEnd = Math.min(end, rangeEnd);
    return Math.max(0, overlapEnd - overlapStart);
  };

  const calculateShiftHoursInRange = (rangeStart, rangeEnd) => {
    return calculateOverlap(startTime, endTime, rangeStart, rangeEnd);
  };

  let normalHours = calculateShiftHoursInRange(NORMAL_START, NORMAL_END);
  let eveningHours = calculateShiftHoursInRange(EVENING_START, EVENING_END);
  let nightHours = calculateShiftHoursInRange(NIGHT_START, NIGHT_END + 1440); 

  if (breakDuration > 0) {
    if (calculateOverlap(startTime, endTime, BREAK_TIMES.normal.start, BREAK_TIMES.normal.end) > 0) {
      normalHours -= breakDuration;
    } if (calculateOverlap(startTime, endTime, BREAK_TIMES.evening.start, BREAK_TIMES.evening.end) > 0) {
      eveningHours -= breakDuration;
    } if (calculateOverlap(startTime, endTime, BREAK_TIMES.night.start, BREAK_TIMES.night.end) > 0) {
      nightHours -= breakDuration;
    }
  }

  return {
    normal: formatHours(normalHours),
    evening: formatHours(eveningHours),
    night: formatHours(nightHours)
  };
}

function testShiftCalculation(){
  const result = calculateShiftHours("22:00", "07:00"); 
  console.log(result); // Test for a normal shift with break (Expected output should adjust with break time)

}

It works well when start time is less than end time. for example if shift is between 06:00-24:00. But it calculates incorrectly when the shift starts from midnight, for example:

const result = calculateShiftHours("22:00", "07:00"); 

The output should be:

Normal hours:  1 hrs (06:00-07:00) 
Evening hours: 0
Night hours:   8 hrs (22:00-06:00) 

Since shift is 9 hour long and 04:00-04:30 overlaps with Night hours so, 30 minutes deduction.

Final output should be: {normal:'1',evening:'0',night: '8.5'}

But the script output is: { normal: '0.00', evening: '0.00', night: '8.00' }

So, the issue is, it does not calculate hours and break time deduction correctly when shift start time is in current day and shift end time is in next day. Any suggestion would be much appreciated.

Can I deserialize a string into an enum with a different value using interfaces?

I’m receiving values in JSON from an API, which can be erroneous because of legacy code that apparently won’t be fixed. These values are being deserialized pretty simply by casting the JSON to an interface. This is all well and good. The problem is I’d like to fix those erroneous values straight at the source during deserialization so that I don’t have to deal with them. An example to illustrate:

enum Bar {
  bar = "bar",
  qux = "qux",
}

// Assume we have multiple interfaces using Bar such that it'd be better for the serialization to be defined in Bar
interface Data1 {
  foo: Bar[];
}
interface Data2 {
  foo: Bar[];
}

// We expect bar but sometimes receive baz, which would be better deserialized as bar
const json = '{"foo": [ "bar", "baz", "qux"]}';
const data1: Data1 = JSON.parse(json);
const data2: Data2 = JSON.parse(json);

I unfortunately can’t define the enum as having multiple values for the same key. I could make different interfaces for what we receive and what we actually work with so that the data is transformed properly but that would require a rework of many things so I’m looking for a way to only affect that specific enum deserialization.

Updation of an object as a value of a field not happening in mongo db

const  Users = require('../mongodb/models/users')

const normalize = async function(email,genre){
    const user = await Users.find({email:email})
    console.log(email)
    console.log(user)
    const minLikes = user[0].normalizationlikes[0].min
    const maxLikes = user[0].normalizationlikes[0].max
    const updatedLikes = { ...user[0].likes[0].raw };  // Spread the existing likes into a new object
    console.log(minLikes)
    console.log(maxLikes)
    console.log(updatedLikes)
    console.log(genre)
    console.log(updatedLikes[genre])
    console.log(updatedLikes.educational)
    console.log(isNaN(updatedLikes[genre])); // Check if it's NaN
    updatedLikes[genre] += 1;  // Increment the like count for the specified category
    console.log(updatedLikes)
    const normalizedLikes = {};
    for (let category in updatedLikes) {
        normalizedLikes[category] = (updatedLikes[category] - minLikes) / (maxLikes - minLikes);
    }
    console.log(updatedLikes)
    console.log(normalizedLikes)
   await Users.updateOne(
        {email:email},
        {$set:{"likes.0.raw": updatedLikes }}
    )
     await Users.updateOne(
        {email:email},
        {$set:{"likes.0.normalized":normalizedLikes}}
    )
    return normalizedLikes;
}
module.exports = normalize

In this everything is going perfect normalization and all going perfectly. But the updation in the database is not happening. Even the output shows everything correctly

{
  adventure: 0,
  action: 1,
  comedy: 0,
  drama: 0,
  fantasy: 0,
  horror: 0,
  romance: 0,
  sciencefiction: 0,
  thriller: 0,
  mystery: 0,
  documentary: 0,
  musical: 0,
  western: 0,
  anime: 0,
  educational: 0
}
{
  adventure: 0,
  action: 0.01,
  comedy: 0,
  drama: 0,
  fantasy: 0,
  horror: 0,
  romance: 0,
  sciencefiction: 0,
  thriller: 0,
  mystery: 0,
  documentary: 0,
  musical: 0,
  western: 0,
  anime: 0,
  educational: 0
}

But in the mongo db atlas it is not updating. Everything console logs perfectly. Just in the updation part it is not working

“Access” using Password to see webpage content that is under accordion class

Here is my webpage:
https://www.calgarysignsbanners.com/Sandwich-Boards-Calgary.htm
I wanted everything that toggles under (only!) the title below to only show after the visitor inputs a password

  • PRICES & DISCOUNTS For ‘Category 1 ECONOMY’ Double-Sided Wooden Sandwich Board Signs With Full Color Printed, Permanent Graphics
    (yes I know the source code won’t hide the content)
    Looking for something simple, along the lines of:
    Input password to show a div?

I TRIED AND TRIED TO NO AVAIL. If I used the div solution above, it gets “crushed” by the class that content is under.

  • what do I need to add to the page HTML?
  • what do I need to add to my style.css file?
  • what do I need to add to my scripts.js?
  • what do I need to add to my jquerry.js?
    to make that happen. I would prefer to create a new, separate named “accordion-PRICES”, if possible, to keep it independent from rest of site. Any help would be much appreciated!

Input password to show a div?

I TRIED AND TRIED TO NO AVAIL. If I used the div solution above, it gets “crushed” by the class that content is under.

JavaScript ( V8 ) VS C++ ( Understanding Stack and Heap Memory Allocation )

I come from a C++ background, where I’m used to local variables typically being stored on the stack, while dynamically allocated objects are stored in the heap. I’m trying to understand if JavaScript handles stack and heap memory similarly.

In JavaScript, I wrote the following code:

const test = {};
test.x = 4;
test.y = 5;
test.z = 6;

C++ code :

 class temp{
   public:
      int x;
      int y;
      int z;
    };
    int main (){
    temp*test=new temp(); // test is stored in stack    
    return 0;
    }

In C++, always test ( a pointer to an object ) to be stored on the stack, with the actual object data residing in the heap . ( like in this link ) that means the function scope can not see what in the heap ( the function scope can access the heap only through a pointer and that pointer in the stack ( the function scope can only see what in the stack ) )
But, from this answer
in JavaScript ( V8 ) , in some cases the test reference itself can sometimes be placed in the heap.

my question : in those cases in JavaScript ( V8 ) how can the function scope see what in the heap ( because the test reference itself in those cases is placed in the heap ) ?

Does that mean in some cases in JavaScript ( V8 ) the heap and the stack can be the same thing so the function scope can access the heap (which is also the stack in those cases ) ?

Session cookie is not being stored in browser, using next.js and custom server configuration

I am using Next.js on frontend, and using Express.js as backend, so I decided to connect express with next.js using custom server configuration of next.js, here is the basic setup:

on backend side, I got app.js:

const express = require('express')
//const proxy = require('express-http-proxy')
const parser = require('body-parser')
const session = require('express-session')
const dotenv = require('dotenv').config()
const {pool} = require('./routes/routes')

const pgSessionStore = require('connect-pg-simple')(session);


function nextAppWrapper(nextApp){

const app = express()
const port = 3000

const {router}  = require('./routes/routes')
const cors = require('cors')

const corsConfig = {
  methods:'*',
  origin:['http://127.0.0.1:8000','http://localhost:8000'],
  credentials:true
}


app.set('trust proxy', 1) // trust first proxy




app.listen(port, () => {
  console.log(`Example app listening on port ${port}`)
})


app.use(cors(corsConfig))

app.use(session(
  {
  store: new pgSessionStore({
    pool:pool,
    tableName:'session',
    createTableIfMissing:true
    
  }),
  secret: process.env.SESSION_SECRET,
  resave: true,
  cookie: { maxAge: 365 * 24 * 60 * 60 * 1000,httpOnly: true , domain:'127.0.0.1:3000'},
  saveUninitialized: true,
}))




app.use(parser.urlencoded({extended:false}))
app.use(parser.json())
app.use('/api',router)
//app.use(proxy('http://127.0.0.1:3000'))

const handle = nextApp.getRequestHandler()

app.all('*', (req, res) => {
  return handle(req, res);
});


}

module.exports = nextAppWrapper



on frontend side (next), there is the server.ts file, this file need to be run when starting dev server:

const nextAppWrapper = require('../backend/app')
const next = require('next')


const dev = process.env.NODE_ENV !== 'production';
const nextApp = next({dev})
nextApp.prepare().then(()=>{
    nextAppWrapper(nextApp)

})

I am using MVC model in my backend, which means I have models functions, controllers functions and routes,

This is my code for login model to handle business logic:

Auth.login = async function ({email,password}){
    const checkEmail = await pool.query('SELECT email from _user WHERE email = $1',[email])
    

    if(checkEmail.rows[0]){
        const hashedPassword = await pool.query('SELECT password from _user WHERE email = $1',[email])

    
        const compare = await bcrypt.compare(password,hashedPassword.rows[0].password)
        if(compare){
            const userId = await pool.query('SELECT * FROM _user WHERE email = $1',[email])
            return {message:'',data:userId.rows[0].id,success:true}

        }
        else{
            return {message:'Wrong password',data:null, success:false}
        }
   
    }

    else{
        return {message:'Email does not exists in database',data:null,success:false}
    }



}

here is the function for login controller to handle request/response objects:

AuthController.login = async function (req,res){

    try{
    const {data,message,success} = await Auth.login(req.body)
    if(success){
    req.session.sessionId = data
    console.log(req.session)
    return res.status(200).json({message:`user with ID ${data} has logged in successfully`})
    }
    return res.status(400).json({message:message})

    }

    catch(e){
        return res.status(400).json({error:e.message})
    }
}

Also, note how I setup session cookie configuration using express-session in app.js above.

when i try to login with the correct email and password, the session is not stored in browser:

enter image description here

Note that this is how I handle sending a request from client side:

    const handleLoginSubmit = (e)=>{
      e.preventDefault()

      setIsLoading(true)
        fetch('http://127.0.0.1:3000/api/login/',{method:'POST',body:JSON.stringify(loginInfo),headers:{'content-type':'application/json'}}).then((res)=>{
          setIsLoading(false)
            if(res.status == 200){
                route.back()
            }
            if(res.status == 400){
                res.json().then((data)=>{
                    console.log(data)


                    setLoginError(data.message)
                })
            }


    })
  }

Note that I am using 127.0.0.1 host with port 3000 on frontend.

Morris.js Line Chart Not rendering correctly

I’m trying to render a line chart using Morris.js, but the chart is not displaying correctly. Here is my code:

var transformedData = [
  {
    y: "Gennaio",
    a: 0,
    b: 0,
    c: 0,
    d: 0,
    e: 0,
    f: 7044,
    g: 0,
    h: 0,
    i: 10338,
    j: 0,
    k: 3428
  },
  {
    y: "Febbraio",
    a: 0,
    b: 0,
    c: 0,
    d: 0,
    e: 0,
    f: 8189,
    g: 103,
    h: 0,
    i: 4779,
    j: 2006,
    k: 16654
  },
  {
    y: "Marzo",
    a: 0,
    b: 0,
    c: 0,
    d: 5377,
    e: 0,
    f: 7583,
    g: 3301,
    h: 0,
    i: 0,
    j: 337,
    k: 3736
  },
  {
    y: "Aprile",
    a: 0,
    b: 0,
    c: 0,
    d: 0,
    e: 0,
    f: 20202,
    g: 0,
    h: 0,
    i: 4868,
    j: 0,
    k: 6292
  },
  {
    y: "Maggio",
    a: 0,
    b: 1793,
    c: 0,
    d: 6106,
    e: 0,
    f: 20030,
    g: 20967,
    h: 0,
    i: 1499,
    j: 1402,
    k: 11716
  },
  {
    y: "Giugno",
    a: 0,
    b: 0,
    c: 0,
    d: 0,
    e: 0,
    f: 11274,
    g: 9798,
    h: 34021,
    i: 5998,
    j: 1032,
    k: 42561
  },
  {
    y: "Luglio",
    a: 0,
    b: 0,
    c: 0,
    d: 0,
    e: 0,
    f: 15338,
    g: 0,
    h: 42292,
    i: 4420,
    j: 0,
    k: 1452
  },
  {
    y: "Agosto",
    a: 0,
    b: 0,
    c: 0,
    d: 0,
    e: 1990,
    f: 3240,
    g: 0,
    h: 0,
    i: 7158,
    j: 517,
    k: 0
  },
  {
    y: "Settembre",
    a: 0,
    b: 0,
    c: 0,
    d: 22193,
    e: 1171,
    f: 0,
    g: 1125,
    h: 4763,
    i: 0,
    j: 0,
    k: 0
  },
  {
    y: "Ottobre",
    a: 0,
    b: 0,
    c: 0,
    d: 0,
    e: 0,
    f: 0,
    g: 0,
    h: 0,
    i: 0,
    j: 0,
    k: 9317
  },
  {
    y: "Novembre",
    a: 0,
    b: 0,
    c: 0,
    d: 0,
    e: 0,
    f: 0,
    g: 0,
    h: 0,
    i: 0,
    j: 0,
    k: 0
  },
  {
    y: "Dicembre",
    a: 0,
    b: 0,
    c: 0,
    d: 0,
    e: 0,
    f: 0,
    g: 0,
    h: 0,
    i: 0,
    j: 0,
    k: 0
  }
];

// Build the graph
Morris.Line({
  resize: true,
  element: "morrisLine",
  data: transformedData,
  xkey: "y",
  ykeys: ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k"],
  labels: [
    "ATHENA COLLEZIONI",
    "CAMPEGGI DESIGN",
    "CESAR",
    "FLEXTEAM",
    "GLAMORA",
    "INTEC",
    "MODULA",
    "MODULA - GAGGENAU",
    "MODULA - NEFF",
    "OPINIONCIATTI",
    "RIMADESIO"
  ]
});

The chart is not rendering, and I’m not sure why. The data seems to be correctly formatted, and I’ve verified that the transformedData array contains the expected values.

Here are some additional details:

The element ID is correct and exists in the HTML.
The data values are all numbers.
The ykeys and labels arrays match the data structure.
What could be causing the chart not to render? Any help would be appreciated!

https://jsfiddle.net/7y8qot6c/1/

How can I retrieve company name and postion of a user using Linkedin developer API

After the OpenId update I can’t see any way to retrieve company name and the user position

openid
Use your name and photo
profile
Use your name and photo
w_member_social
Create, modify, and delete posts, comments, and reactions on your behalf
email
Use the primary email address associated with your LinkedIn account

These are the only scopes I can see

        const positionResponse = await axios.get('https://api.linkedin.com/v2/positions', {
            headers: { Authorization: `Bearer ${req.session.accessToken}` }
        });

and I tried to log the data I got

Error: {
  status: 404,
  code: 'RESOURCE_NOT_FOUND',
  message: 'No virtual resource found'
}

Cypress Intercept Working But Mocked Data Not Showing in UI Grid

Problem

I’m trying to test a React application using Cypress. I have a custom command that intercepts an API call and prepends a new customer to the existing data. While I can see in the Network tab that the intercept is working (the mocked data is present in the response), the UI grid doesn’t display the prepended customer.

Code

Cypress Test

describe('Create Order From Grid', () => {
  beforeEach(() => {
    cy.stubOktaAuth({ role: 'write' });
    cy.setInitialLocalStorage();
    cy.interceptRetrieveCustomerCount();
    cy.interceptRetrieveProductCounter();
    cy.interceptRetrieveCountries();
    cy.interceptRetrieveStores();
    cy.interceptConfigcat();
    cy.interceptRetrieveFeatureFlags({});
    cy.interceptRetrieveFeatureFlags({
      customerCreationEnabledFor: 'TH',
      customerCreationEnabledForEmails: 'none',
    });
    cy.visit('http://localhost:3000/customer');
  });

  it('updates customer missing info when creating a new order', () => {
    const givenCustomerWithMissingInfo: CustomerDTO = buildCustomerDTO({
      attributes: {
        firstName: 'john',
        lastName: 'doe',
        email: '[email protected]',
        errors: {
          BR: [{ message: 'error message', field: 'field error' }],
        },
      },
    });

    cy.givenRetrievedApiCustomers(givenCustomerWithMissingInfo);

    cy.interceptUpdateMissingCustomerInfo();

    cy.visit('http://localhost:3000/customer');

    cy.wait('@retrieveCustomers').then((interception) => {
      // The following expect passes, but the UI doesn't correlate
      expect(
        interception?.response?.body.data[0].attributes.firstName
      ).to.equal('john');
    });

    cy.pause();

  });
});

Custom Command

Cypress.Commands.add(
  'givenRetrievedApiCustomers',
  (givenCustomer: CustomerDTO) => {
    cy.fixture('customers.page1.json').then(
      (mockApiResponse: RetrievedCustomersApiResponse) => {
        cy.log(JSON.stringify(givenCustomer.attributes.firstName));

        cy.interceptRetrieveCustomers('retrieveCustomers', {
          ...mockApiResponse,
          data: [givenCustomer, ...mockApiResponse.data],
          links: givenCustomer.links,
        });
      }
    );
  }
);

Network Response

The response in the Network tab shows the correct data structure:

{
    "data": [
        {
            "type": "customers",
            "id": "74108646-1243-42be-bfad-66fda2ed857a",
            "attributes": {
                "type": "Customer",
                "email": "[email protected]",
                "firstName": "john",
                "lastName": "doe",
                // ... other attributes
            },
        },
        {
            "type": "customers",
            "id": "74108646-1243-42be-bfad-66fda2ed857a",
            "attributes": {
                "type": "Customer",
                "email": "[email protected]",
                "firstName": "ancelin",
                "lastName": "jacquet",
                // ... other attributes
            },
        },
    // ... other customers
    ]
}

In the UI I can see Ancelin, but not John.

What I’ve Tried

  1. Verified the intercept is working by checking Network tab
  2. Added cy.wait('@retrieveCustomers') to ensure the request completes
  3. Logged the response data which shows the correct structure
  4. The cy.log() in my custom command shows the correct firstName (‘john’)

Expected Behavior

The UI grid should show “john doe” at the top of the list, followed by the existing customers.

Actual Behavior

The UI grid only shows the existing customers from the fixture, and “john doe” is not visible despite being present in the Network response.

Questions

  1. Why isn’t the mocked data showing up in the UI even though it’s present in the Network response?
  2. Is there an issue with the timing of the intercept?
  3. Could there be something in the React component preventing the update?

Any help would be appreciated!