An error occur during the compilation process

Tags: #<Tag:0x00007f8b2a12e780> #<Tag:0x00007f8b2a12e280>

I’m facing an issue when compiling the code (ReactJS)

ERROR in chunk vendor.handsontable
vendor.handsontable-1ab9a60b6795d9d8d8a4.css
Cannot read properties of undefined (reading ‘pop’)

package.json

{
      "name": "frontend",
      "version": "1.0.0",
      "description": "",
      "main": "index.js",
      "scripts": {
        "dev": "webpack serve --config webpack.dev.js",
        "watch": "webpack --config webpack.dev.js --watch",
        "build-stg": "webpack --config webpack.stg.js",
        "build-prod": "webpack --config webpack.prod.js",
        "lint": "eslint .",
        "lint:fix": "eslint --fix ."
      },
      "keywords": [],
      "author": "",
      "license": "ISC",
      "dependencies": {
        "@babel/core": "^7.20.7",
        "@babel/helper-define-polyfill-provider": "^0.2.4",
        "@babel/preset-env": "^7.20.2",
        "@babel/preset-react": "^7.18.6",
        "@emotion/react": "^11.10.5",
        "@emotion/styled": "^11.10.5",
        "@engineerforce/frappe-gantt": "^0.0.3",
        "@formkit/auto-animate": "^1.0.0-beta.5",
        "@hookform/error-message": "^2.0.1",
        "@hookform/resolvers": "^2.9.10",
        "@material-ui/core": "^5.0.0-beta.5",
        "@material-ui/data-grid": "4.0.0-alpha.33",
        "@material-ui/icons": "^5.0.0-beta.5",
        "@material-ui/lab": "^5.0.0-alpha.44",
        "@material-ui/styles": "^4.11.5",
        "@mui/material": "^5.11.1",
        "@react-pdf/font": "2.2.1",
        "@react-pdf/renderer": "^3.0.1",
        "@sentry/react": "^7.28.1",
        "@sentry/tracing": "^7.28.1",
        "@tinymce/tinymce-react": "^3.14.0",
        "autosuggest-highlight": "^3.3.4",
        "axios": "^0.21.4",
        "babel-loader": "^8.3.0",
        "babel-plugin-polyfill-regenerator": "^0.2.3",
        "babel-plugin-react-html-attrs": "^3.0.5",
        "babel-plugin-transform-imports": "^2.0.0",
        "browserslist": "^4.21.4",
        "chart.js": "^2.9.4",
        "chartjs-plugin-datalabels": "^1.0.0",
        "clean-webpack-plugin": "4.0.0",
        "clsx": "^1.2.1",
        "core-js": "^3.27.0",
        "css-loader": "^4.3.0",
        "css-what": "^5.1.0",
        "date-fns": "^2.29.3",
        "django-react-loader": "^0.1.7",
        "dotenv-webpack": "^8.0.1",
        "file-saver": "^2.0.5",
        "filesize": "^7.0.0",
        "formik": "^2.2.9",
        "glob-parent": "^6.0.2",
        "history": "^5.3.0",
        "html-react-parser": "^1.4.14",
        "html2canvas": "^1.4.1",
        "iframe-resizer-react": "^1.1.0",
        "install": "^0.13.0",
        "json-loader": "^0.5.7",
        "jspdf": "^2.5.1",
        "lodash": "^4.17.21",
        "logrocket": "^3.0.1",
        "lottie-react": "^2.3.1",
        "material-ui-dropzone": "Right-Angle-Engineering/material-ui-dropzone",
        "material-ui-numeric-input": "^3.1.1",
        "material-ui-popup-state": "^1.9.3",
        "mathjs": "^11.5.0",
        "mini-css-extract-plugin": "^0.11.3",
        "moment": "^2.29.4",
        "moment-duration-format": "^2.3.2",
        "moment-range": "^4.0.2",
        "node-gyp": "^8.4.1",
        "node-sass": "^7.0.3",
        "normalize-url": "^6.1.0",
        "npm": "^7.24.2",
        "nprogress": "^0.2.0",
        "po2json": "^0.4.5",
        "postcss": "^8.4.20",
        "pptxgenjs": "^3.11.0",
        "print-js": "^1.6.0",
        "prop-types": "^15.8.1",
        "qrcode.react": "^3.1.0",
        "react": "^18.2.0",
        "react-beautiful-dnd": "^13.1.1",
        "react-butterfiles": "^1.3.3",
        "react-chartjs-2": "^2.11.2",
        "react-circular-progressbar": "^2.1.0",
        "react-color": "^2.19.3",
        "react-countup": "^6.4.2",
        "react-data-grid": "7.0.0-beta.12",
        "react-dom": "^18.2.0",
        "react-feather": "^2.0.10",
        "react-frappe-gantt": "^0.2.3",
        "react-ga4": "^1.4.1",
        "react-gauge-chart": "^0.4.1",
        "react-google-recaptcha": "^2.1.0",
        "react-helmet": "^6.1.0",
        "react-hook-form": "^7.41.1",
        "react-intl": "^5.25.1",
        "react-loadable": "^5.5.0",
        "react-loadable-visibility": "^3.0.2",
        "react-multi-email": "^0.5.3",
        "react-number-format": "^4.9.4",
        "react-pdf-html": "^1.1.15",
        "react-perfect-scrollbar": "^1.5.8",
        "react-pro-sidebar": "^1.1.0-alpha.1",
        "react-redux": "^7.2.9",
        "react-router": "6.3.0",
        "react-router-dom": "6.3.0",
        "react-router-prompt": "^0.3.0",
        "react-scripts": "^4.0.3",
        "react-table": "^7.8.0",
        "react-to-print": "^2.14.10",
        "react-window": "^1.8.8",
        "recharts": "^2.6.2",
        "redux-devtools-extension": "^2.13.9",
        "redux-logger": "^3.0.6",
        "redux-saga": "^1.2.2",
        "regenerator-runtime": "^0.13.11",
        "sass": "^1.57.1",
        "sass-loader": "^10.4.1",
        "styled-components": "^5.3.6",
        "tsparticles-preset-fireworks": "^2.7.1",
        "uuid": "^8.3.2",
        "v8-compile-cache": "^2.3.0",
        "validator": "^13.7.0",
        "webpack": "^4.46.0",
        "webpack-bundle-tracker": "1.0.0",
        "webpack-cli": "^4.10.0",
        "xlsx": "^0.18.5",
        "yup": "^0.32.11"
      },
      "devDependencies": {
        "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
        "eslint": "^8.30.0",
        "eslint-plugin-react": "^7.31.11",
        "file-loader": "^6.2.0",
        "react-dnd": "16.0.1",
        "react-dnd-html5-backend": "16.0.1",
        "react-refresh": "^0.10.0",
        "webpack-dev-server": "^3.11.3"
      }
    }

Hi @jason1

Would you be able to send over the project?

The pop error message points more on the JS than CSS. It would be unusual to see this in the CSS file, but the file you’ve pointed out is already after running build, so we should also check if the error is also thrown for the pre-build version when you run the server.

This is the main code. The error only happens when running ‘webpack --config webpack.prod.js’

import { useEffect, useRef, useState } from "react";
import { HotTable, HotColumn } from "@handsontable/react";
import { registerAllModules } from "handsontable/registry";
import { useIntl } from "react-intl";
import { useDispatch } from "react-redux";
import { Button, Checkbox } from "@material-ui/core";
import * as TaskAction from "src/redux/actions/TaskAction";
import * as ManHourListAction from "src/redux/actions/ManHourListAction";
import * as ProjectTagAction from "src/redux/actions/ProjectTagAction";
import * as EstimationSettingAction from "src/redux/actions/EstimationSettingAction";
import Handsontable from "handsontable";
import SaveAsTemplateDialog from "src/components/estimate/SaveAsTemplateDialog";
import UnitPriceSetting from "src/pages/Estimate/components/UnitPriceSetting/UnitPriceSetting";
import "./handsongrid.css";
import { timeUnitMap, difficulty, difficultyMap, colorMap } from "src/data/Maps";
import { getRiskTimeValueString } from "src/utils/taskCalculation";
import { currencyList } from "src/data/Currency";
import moment from "moment";
import * as Style from "./style";
import editIcon from "src/images/pen.svg";
import arrowLeftIcon from "src/images/arrow-left.svg";
import arrowRightIcon from "src/images/arrow-right.svg";
import visibilityOffIcon from "src/images/visibility-off.svg";
import { DragDropContext, Droppable, Draggable } from "react-beautiful-dnd";
import { DndProvider, useDrag, useDrop } from "react-dnd";
import { DragIndicator } from "@material-ui/icons";

// register Handsontable's modules
registerAllModules();

function HandsOnGrid({ data }) {
  const { formatMessage } = useIntl();
  const dispatch = useDispatch();
  // console.log("HandsOnGrid", data);
  const { taskList, selectedTaskFields, unitPriceSettingTags, currency, projectState, currentTeam } = data;
  const [checkAll, setCheckAll] = useState(false);
  const [openUnitPriceSetting, setOpenUnitPriceSetting] = useState(false);
  const dollorSymbol = currencyList.find((c) => c.label === currency).symbol;

  const timeUnitOptions = [
    { name: formatMessage({ id: "Hour" }), value: timeUnitMap.H },
    { name: formatMessage({ id: "Day" }), value: timeUnitMap.D },
    { name: formatMessage({ id: "Week" }), value: timeUnitMap.W },
    { name: formatMessage({ id: "Month" }), value: timeUnitMap.M }
  ];

  const difficultyOptions = [
    { name: "", value: "" },
    { name: formatMessage({ id: difficultyMap.E }), value: difficulty.Easy },
    { name: formatMessage({ id: difficultyMap.M }), value: difficulty.Medium },
    { name: formatMessage({ id: difficultyMap.H }), value: difficulty.Hard }
  ];

  const tagsList = unitPriceSettingTags.map((tag, index) => ({
    name: tag.name,
    tagId: tag.tagId,
    price: {
      hour: tag.pricings.find((price) => price.currencyUnit === currency)?.unitPriceHour,
      day: tag.pricings.find((price) => price.currencyUnit === currency)?.unitPriceDay,
      week: tag.pricings.find((price) => price.currencyUnit === currency)?.unitPriceWeek,
      month: tag.pricings.find((price) => price.currencyUnit === currency)?.unitPriceMonth
    },
    cost: {
      hour: tag.pricings.find((price) => price.currencyUnit === currency)?.costPriceHour,
      day: tag.pricings.find((price) => price.currencyUnit === currency)?.costPriceDay,
      week: tag.pricings.find((price) => price.currencyUnit === currency)?.costPriceWeek,
      month: tag.pricings.find((price) => price.currencyUnit === currency)?.costPriceMonth
    },
    color: colorMap[index] || colorMap[0]
  }));

  const sortTasks = (tasks, parent) => {
    let node = [];
    tasks
      .filter((t) => t.parent === parent)
      .forEach((d) => {
        let cd = d;
        cd.children = sortTasks(tasks, d.id);
        return node.push(cd);
      });
    return node;
  };

  const createRows = () => {
    const rows = [];
    const sortedTasks = sortTasks(JSON.parse(JSON.stringify(taskList)), null);

    const renderCanDeleteFrom = ({ sIndex, mIndex, medium }) => {
      if (sIndex == 0 && mIndex < 1) return "Large";
      else if (medium.children.length > 1 && sIndex == 0 || sIndex == 0) return "Medium";
      else return "Small";
    };
    let count = 0;

    sortedTasks.forEach((large, lIndex) => {
      large.children.forEach((medium, mIndex) => {
        medium.children.forEach((small, sIndex) => {
          // console.log("small.difficulty", small.difficulty);
          rows.push({
            idx: count,
            ltask: sIndex === 0 && mIndex < 1 ? large.taskName : (renderCanDeleteFrom({ sIndex, mIndex, medium }) === "Small" || renderCanDeleteFrom({ sIndex, mIndex, medium }) === "Medium" ? null : ""),
            mtask: (medium.children.length > 1 && sIndex === 0 || sIndex === 0) ? medium.taskName : (renderCanDeleteFrom({ sIndex, mIndex, medium }) === "Small" ? null : ""),
            stask: small.taskName,
            ltaskId: large.id,
            mtaskId: medium.id,
            staskId: small.id,
            ltaskIdAndcanDeleteFrom: `${large.id},${(large?.children[0].id === medium.id && medium?.children[0].id === small.id && 3) || (medium?.children[0].id === small.id && 2) || (small.level === 2 && 1) || small.perm || medium.perm || large.perm},ThisIsLarge,notUnitTask`,
            mtaskIdAndcanDeleteFrom: `${medium.id},${(large?.children[0].id === medium.id && medium?.children[0].id === small.id && 3) || (medium?.children[0].id === small.id && 2) || (small.level === 2 && 1) || small.perm || medium.perm || large.perm},ThisIsMedium,notUnitTask`,
            staskIdAndcanDeleteFrom: `${small.id},${(large?.children[0].id === medium.id && medium?.children[0].id === small.id && 3) || (medium?.children[0].id === small.id && 2) || (small.level === 2 && 1) || small.perm || medium.perm || large.perm},ThisIsSmall,notUnitTask`,
            perm: (large?.children[0].id === medium.id && medium?.children[0].id === small.id && 3) || (medium?.children[0].id === small.id && 2) || (small.level === 2 && 1) || small.perm || medium.perm || large.perm,
            complete: Math.min(100, Math.round(Math.random() * 110)),
            // assignee: assignees.find((ass) => ass.memberId === small.assignee.id)?.memberId,
            tag: small.tag,
            tagName: tagsList.find(tag => tag.tagId === small.tag)?.name || "",
            unitPrice: tagsList.find(tag => tag.tagId === small.tag)?.price[small.timeUnit] || "",
            timeUnit: small.timeUnit,
            timeUnitName: formatMessage({ id: small.timeUnit }),
            timeValue: small.timeValue,
            // assigneeList: assignees,
            unitPriceSettingTags: tagsList,
            currency: currency,
            riskCoefficient: small.task_risk_coe,
            riskManHour: getRiskTimeValueString(small, formatMessage({ id: "MM" }), formatMessage({ id: "WW" }), formatMessage({ id: "DD" }), formatMessage({ id: "HH" })),
            description: small.description,
            taskSize: small.taskSize,
            taskSizeUnit: small.taskSizeUnit,
            productivity: small.productivity,
            difficulty: small.difficulty,
            difficultyName: (small.difficulty !== null && small.difficulty !== "" && small.difficulty !== undefined) ? formatMessage({ id: difficultyOptions.find(d => d.value === small.difficulty).name }) : "",
            taskStartEndDateS: small.taskStartEndDate && moment(small.taskStartEndDate[0]).format("YYYY-MM-DD"),
            taskStartEndDateE: small.taskStartEndDate && moment(small.taskStartEndDate[1]).format("YYYY-MM-DD"),
            canDeleteFrom: renderCanDeleteFrom({ sIndex, mIndex, medium }),
            checkboxChecked: small.checkboxChecked,
            unitTask: false,
            lastRow: lIndex == sortedTasks.length - 1 && mIndex == large.children.length - 1 && sIndex == medium.children.length - 1,
          });
          count++;
        });
      });
      if (large.unitTask) {
        rows.push({
          idx: count,
          ltask: large.taskName,
          mtask: "",
          stask: "",
          ltaskId: large.id,
          perm: 0,
          mtaskId: null,
          staskId: null,
          ltaskIdAndcanDeleteFrom: `${large.id},0,ThisIsLarge,UnitTask`,
          mtaskIdAndcanDeleteFrom: ",,,notUnitTask",
          staskIdAndcanDeleteFrom: ",,,notUnitTask",
          complete: Math.min(100, Math.round(Math.random() * 110)),
          // assignee: assignees.find((ass) => ass.memberId === large.assignee.id)?.memberId,
          tag: typeof large.tag !== "undefined" ? large.tag : large.tag_id,
          tagName: tagsList.find(tag => tag.tagId === (typeof large.tag !== "undefined" ? large.tag : large.tag_id))?.name || "",
          unitPrice: tagsList.find(tag => tag.tagId === (typeof large.tag !== "undefined" ? large.tag : large.tag_id))?.price[large.timeUnit] || "",
          timeUnit: large.timeUnit,
          timeUnitName: formatMessage({ id: large.timeUnit }),
          timeValue: large.timeValue,
          // assigneeList: assignees,
          unitPriceSettingTags: tagsList,
          currency: currency,
          riskCoefficient: large.task_risk_coe,
          riskManHour: getRiskTimeValueString(large, formatMessage({ id: "MM" }), formatMessage({ id: "WW" }), formatMessage({ id: "DD" }), formatMessage({ id: "HH" })),
          description: large.description,
          taskSize: large.taskSize,
          taskSizeUnit: large.taskSizeUnit,
          productivity: large.productivity,
          difficulty: large.difficulty,
          difficultyName: (large.difficulty !== null && large.difficulty !== "" && large.difficulty !== undefined) ? formatMessage({ id: difficultyOptions.find(d => d.value === large.difficulty).name }) : "",
          taskStartEndDateS: large.taskStartEndDate && moment(large.taskStartEndDate[0]).format("YYYY-MM-DD"),
          taskStartEndDateE: large.taskStartEndDate && moment(large.taskStartEndDate[1]).format("YYYY-MM-DD"),
          canDeleteFrom: "Large",
          checkboxChecked: large.checkboxChecked,
          unitTask: true,
          lastRow: true,
        });
        count++;
      }
    });

    return rows;
  };

  const rows = createRows();
  const hotTableComponent = useRef(null);

  const afterChange = (changes, source) => {
    if (source !== "loadData" && hotTableComponent.current) {
      const hotInstance = hotTableComponent.current.hotInstance;

      // console.log("afterChange", changes, source);
      changes !== null && changes.forEach(([row, prop, oldVal, newVal]) => {
        const ltaskId = hotInstance.getDataAtRowProp(row, "ltaskId");
        const mtaskId = hotInstance.getDataAtRowProp(row, "mtaskId");
        const staskId = hotInstance.getDataAtRowProp(row, "staskId");
        const unitTask = hotInstance.getDataAtRowProp(row, "unitTask");
        const tagId = hotInstance.getDataAtRowProp(row, "tag");
        const timeUnit = hotInstance.getDataAtRowProp(row, "timeUnit");
        const taskStartEndDateS = hotInstance.getDataAtRowProp(row, "taskStartEndDateS");
        const taskStartEndDateE = hotInstance.getDataAtRowProp(row, "taskStartEndDateE");
        // console.log("afterChange", hotInstance.getDataAtRowProp(row, "staskId"), prop, oldVal, newVal); // log the cell value of the specified column
        switch (prop) {
          case "ltask":
            handleTaskNameChange(unitTask, ltaskId, mtaskId, ltaskId, prop, newVal);
            break;
          case "mtask":
            handleTaskNameChange(unitTask, ltaskId, mtaskId, mtaskId, prop, newVal);
            break;
          case "stask":
            handleTaskNameChange(unitTask, ltaskId, mtaskId, staskId, prop, newVal);
            break;
          case "tagName": {
            if (newVal !== "") handleTagSettingChange(unitTask, unitTask ? ltaskId : staskId, mtaskId, ltaskId, newVal, oldVal, timeUnit);
            break;
          }
          case "timeValue": {
            handleManHourChange(unitTask, unitTask ? ltaskId : staskId, mtaskId, ltaskId, newVal, oldVal);
            break;
          }
          case "timeUnitName": {
            const taskId = !unitTask ? staskId : ltaskId;
            const timeUnit = newVal === null ? timeUnitOptions.find(unit => unit.name === oldVal).value : timeUnitOptions.find(unit => unit.name === newVal).value;
            handleTimeUnitChange(unitTask, taskId, mtaskId, ltaskId, tagId, timeUnit);
            break;
          }
          case "riskCoefficient": {
            const taskId = !unitTask ? staskId : ltaskId;
            handleRiskCoeChanges(unitTask, mtaskId, ltaskId, taskId, newVal === null ? oldVal : newVal);
            break;
          }
          case "description": {
            const taskId = !unitTask ? staskId : ltaskId;
            dispatchRowValue(taskId, "description", newVal === null ? oldVal : newVal);
            break;
          }
          case "difficultyName": {
            const taskId = !unitTask ? staskId : ltaskId;
            const difficulty = newVal === null ? difficultyOptions.find(difficulty => difficulty.name === oldVal).value : difficultyOptions.find(difficulty => difficulty.name === newVal).value;
            dispatchRowValue(taskId, "difficulty", difficulty);
            break;
          }
          case "taskStartEndDateS": {
            const taskId = !unitTask ? staskId : ltaskId;
            dispatch({
              type: TaskAction.TASKLISTREVISED_SET_ROW_VALUES,
              payload: {
                index: taskId,
                name: "taskStartEndDate",
                value: [newVal === null ? oldVal : newVal, taskStartEndDateE]
              },
            });
            break;
          }
          case "taskStartEndDateE": {
            const taskId = !unitTask ? staskId : ltaskId;
            dispatch({
              type: TaskAction.TASKLISTREVISED_SET_ROW_VALUES,
              payload: {
                index: taskId,
                name: "taskStartEndDate",
                value: [taskStartEndDateS, newVal === null ? oldVal : newVal]
              },
            });
            break;
          }
          case "taskSize": {
            const taskId = !unitTask ? staskId : ltaskId;
            dispatchRowValue(taskId, "taskSize", newVal === null ? oldVal : newVal);
            break;
          }
          case "taskSizeUnit": {
            const taskId = !unitTask ? staskId : ltaskId;
            dispatchRowValue(taskId, "taskSizeUnit", newVal === null ? oldVal : newVal);
            break;
          }
          case "productivity": {
            const taskId = !unitTask ? staskId : ltaskId;
            dispatchRowValue(taskId, "productivity", newVal === null ? oldVal : newVal);
            break;
          }
          default:
            break;
        }
      });
    }
  };

  const dispatchRowValue = (taskId, column, value) => {
    dispatch({
      type: TaskAction.TASKLISTREVISED_SET_ROW_VALUES,
      payload: {
        index: taskId,
        name: column,
        value: value
      },
    });
  };

  const handleTaskNameChange = (unitTask, ltaskId, mtaskId, targetTaskId, taskType, value) => {
    if (unitTask === false || (unitTask === true && taskType === "ltask")) {
      dispatchRowValue(targetTaskId, "taskName", value === null ? "" : value);
      if (taskType === "ltask" || taskType === "mtask") {
        if (unitTask) {
          dispatch({
            type: ManHourListAction.MAN_HOUR_LIST_SET_VALUE,
            payload: {
              unitTaskId: ltaskId,
              name: taskType === "ltask" ? "largeTaskName" : "mediumTaskName",
              value: value
            }
          });
        } else {
          dispatch({
            type: ManHourListAction.MAN_HOUR_LIST_SET_VALUE,
            payload: {
              mediumTaskId: mtaskId,
              name: taskType === "ltask" ? "largeTaskName" : "mediumTaskName",
              value: value
            }
          });
        }
      }
    }
  };

  const handleManHourChange = (unitTask, staskId, mtaskId, ltaskId, newvalue, oldValue) => {
    const regex = /^[+-]?([0-9]*[.])?[0-9]+$/;
    if (regex.test(newvalue)) dispatchRowValue(staskId, "timeValue", newvalue);
    else dispatchRowValue(staskId, "timeValue", oldValue);
    if (!unitTask) {
      dispatch({
        type: ManHourListAction.MAN_HOUR_LIST_VALUE_CHANGED,
        payload: { taskType: "mediumTaskId", taskId: mtaskId }
      });
    } else {
      dispatch({
        type: ManHourListAction.MAN_HOUR_LIST_VALUE_CHANGED,
        payload: { taskType: "unitTaskId", taskId: ltaskId }
      });
    }
    dispatch({
      type: TaskAction.GANTTREVISED_REFRESH,
    });
  };

  const handleTimeUnitChange = (unitTask, targetId, mtaskId, ltaskId, tagId, timeUnit) => {
    dispatch({
      type: TaskAction.TASKLISTREVISED_RESET_TIMEVALUES,
      payload: {
        index: targetId,
        name: "timeUnit",
        value: timeUnit,
      },
    });
    dispatchRowValue(targetId, "unitPrice", tagsList.find((a) => a.tagId === tagId).price[timeUnit]);
    dispatchRowValue(targetId, "cost", tagsList.find((a) => a.tagId === tagId).cost[timeUnit]);
    dispatchRowValue(targetId, "timeUnit", timeUnit);
    if (!unitTask) {
      dispatch({
        type: ManHourListAction.MAN_HOUR_LIST_VALUE_CHANGED,
        payload: {
          taskType: "mediumTaskId",
          taskId: mtaskId,
          updateTimeUnit: true,
          timeUnit: timeUnit
        }
      });
    } else {
      dispatch({
        type: ManHourListAction.MAN_HOUR_LIST_VALUE_CHANGED,
        payload: {
          taskType: "unitTaskId",
          taskId: ltaskId,
          updateTimeUnit: true,
          timeUnit: timeUnit
        }
      });
    }
    dispatch({
      type: TaskAction.GANTTREVISED_REFRESH,
    });
  };

  const handleRiskCoeChanges = (unitTask, mtaskId, ltaskId, targetTaskId, value) => {
    dispatchRowValue(targetTaskId, "task_risk_coe", value);
    if (!unitTask) {
      dispatch({
        type: ManHourListAction.MAN_HOUR_LIST_VALUE_CHANGED,
        payload: { taskType: "mediumTaskId", taskId: mtaskId }
      });
    } else {
      dispatch({
        type: ManHourListAction.MAN_HOUR_LIST_VALUE_CHANGED,
        payload: { taskType: "unitTaskId", taskId: ltaskId }
      });
    }
    dispatch({
      type: TaskAction.GANTTREVISED_REFRESH,
    });
  };

  const handleTagSettingChange = (unitTask, targetTaskId, mtaskId, ltaskId, newValue, oldValue, timeUnit) => {
    dispatchRowValue(targetTaskId, "unitPrice", (newValue === null || newValue === "") ? tagsList.find((a) => a.name === oldValue).price[timeUnit] : tagsList.find((a) => a.name === newValue).price[timeUnit]);
    dispatchRowValue(targetTaskId, "cost", (newValue === null || newValue === "") ? tagsList.find((a) => a.name === oldValue).cost[timeUnit] : tagsList.find((a) => a.name === newValue).cost[timeUnit]);
    dispatchRowValue(targetTaskId, "tag", (newValue === null || newValue === "") ? tagsList.find(tag => tag.name === oldValue).tagId : tagsList.find(tag => tag.name === newValue).tagId);
    if (!unitTask) {
      dispatch({
        type: ManHourListAction.MAN_HOUR_LIST_VALUE_CHANGED,
        payload: { taskType: "mediumTaskId", taskId: mtaskId }
      });
    } else {
      dispatch({
        type: ManHourListAction.MAN_HOUR_LIST_VALUE_CHANGED,
        payload: { taskType: "unitTaskId", taskId: ltaskId }
      });
    }
    dispatch({
      type: TaskAction.GANTTREVISED_REFRESH,
    });
  };

  const handleTaskAdd = () => {
    dispatch({
      type: ManHourListAction.MAN_HOUR_ADD_MEDIUM_TASK,
      payload: { taskList, taskType: "ltask", isUnitTask: false }
    });
    dispatch({
      type: TaskAction.TASKLISTREVISED_ADD_ROW,
    });
  };

  const handleUnitTaskAdd = () => {
    dispatch({
      type: ManHourListAction.MAN_HOUR_ADD_MEDIUM_TASK,
      payload: { taskList, taskType: "ltask", isUnitTask: true }
    });
    dispatch({
      type: TaskAction.TASKLISTREVISED_ADD_UNIT_TASK_ROW,
    });
  };

  const CustomDropdownEditor = Handsontable.editors.DropdownEditor.prototype.extend();

  CustomDropdownEditor.prototype.prepare = function () {
    Handsontable.editors.DropdownEditor.prototype.prepare.apply(this, arguments);
    this.TEXTAREA.readOnly = true; // disable typing in the dropdown field
  };

  const buttonRenderer = (instance, td, row, col, prop, value, cellProperties) => {
    Handsontable.dom.empty(td); // First, clean cell and append button

    const data = instance.getDataAtRowProp(row, prop);
    const taskId = parseInt(data.split(",")[0]);
    const permission = parseInt(data.split(",")[1]);
    const taskType = data.split(",")[2];
    const isUnitTask = data.split(",")[3] === "UnitTask" ? true : false;
    let showAddButton = false;
    switch (permission) {
      case 3:
        showAddButton = true;
        break;
      case 2:
        if (taskType === "ThisIsLarge") showAddButton = false;
        else showAddButton = true;
        break;
      case 1:
        if (taskType === "ThisIsLarge" || taskType === "ThisIsMedium") showAddButton = false;
        else showAddButton = true;
        break;
      case 0: // unit task
        showAddButton = true;
        break;
      default:
        showAddButton = false;
        break;
    }

    if (showAddButton) {
      // Create button
      const button = document.createElement("button");
      button.innerHTML = "+";
      button.onclick = () => {
        if (taskType === "ThisIsLarge") {
          if (isUnitTask) {
            dispatch({
              type: ManHourListAction.MAN_HOUR_ADD_MEDIUM_TASK,
              payload: { taskList, taskType: "ltask", isUnitTask: true }
            });
            dispatch({
              type: TaskAction.TASKLISTREVISED_ADD_UNIT_TASK_ROW,
              payload: {
                currentTaskId: taskId,
              }
            });
          } else {
            dispatch({
              type: ManHourListAction.MAN_HOUR_ADD_MEDIUM_TASK,
              payload: { taskList, taskType: "ltask", isUnitTask: false }
            });
            dispatch({
              type: TaskAction.TASKLISTREVISED_ADD_ROW,
              payload: {
                currentTaskId: taskId,
              }
            });
          }
    
        } else if (taskType === "ThisIsMedium") {
          dispatch({
            type: ManHourListAction.MAN_HOUR_ADD_MEDIUM_TASK,
            payload: {
              taskList,
              taskType: "mtask",
              currentTaskId: taskId
            }
          });
          dispatch({
            type: TaskAction.TASKLISTREVISED_ADD_MEDIUM_TASK,
            payload: {
              currentTaskId: taskId,
              parent: taskList.find(task => task.id === taskId).parent,
              level: 1
            }
          });
        } else {
          dispatch({
            type: TaskAction.TASKLISTREVISED_ADD_SMALL_TASK,
            payload: {
              currentTaskId: taskId,
              parent: taskList.find(task => task.id === taskId).parent,
              level: 2
            }
          });
        }
      };

      // Add button styles
      button.style.display = "flex";
      button.style.justifyContent = "center";
      button.style.alignItems = "center";
      button.style.marginTop = "5px";
      button.style.width = "15px";
      button.style.height = "15px";
      button.style.borderColor = "black";
      button.style.borderRadius = "50%"; // This will make the button circular
      button.style.backgroundColor = "white"; // Change the color as per your need
      button.style.color = "black"; // Set the color of the text
      button.style.cursor = "pointer";
      td.appendChild(button);
    
      return td;
    }
  };

  const taskNameCell = {
    editor: Handsontable.editors.TextEditor,
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      if (value === "") {
        switch (prop) {
          case "ltask":
            td.innerHTML = formatMessage({ id: "Please-input-large-item" });  // Placeholder text
            break;
          case "mtask":
            td.innerHTML = formatMessage({ id: "Please-input-medium-item" });  // Placeholder text
            break;
          default:
            td.innerHTML = formatMessage({ id: "Please-input-small-item" });  // Placeholder text
            break;
        }
        td.style.color = "#888";  // Placeholder text color
      } else if (value === null) {
        td.innerHTML = "";
        td.style.color = "#888";  // Placeholder text color
        cellProperties.readOnly = true; // Disable the cell
      }
      return td;
    },
  };

  const tagDropDownCell = {
    editor: CustomDropdownEditor,
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      if (prop !== "tagName") return;

      if (value === null || value === undefined || value === "") {
        td.style.color = "#999";
        // td.innerHTML = formatMessage({ id: "Please-select" });
        const divElement = document.createElement("div");
        divElement.innerHTML = `<div class="tag-setting"><span>${formatMessage({ id: "Please-select" })}</span><span>▼</span></div>`;
        td.appendChild(divElement);
      } else {
        td.innerHTML = `<span style="display: block; color: #000; border-radius: 16px; text-align: center; background-color: ${tagsList.find(tag => tag.name === value).color}; height: 20px; margin-top: 1px;">${value}</span>`;
      }
    },
    source: tagsList.map(tag => tag.name),
  };

  const unitPriceCell = function (hotInstance, td, row, col, prop, value, cellProperties) {
    // Clear any content from the cell
    Handsontable.dom.empty(td);
    const tag = hotInstance.getDataAtRowProp(row, "tag");

    // Create button
    const iconBtn = document.createElement("img");
    iconBtn.src = editIcon;
    iconBtn.style.cursor = "pointer";
    iconBtn.style.width = "16px";
    iconBtn.style.height = "16px";
    iconBtn.style.verticalAlign = "middle";
    iconBtn.style.marginLeft = "10px";

    if (tag !== "") {
      iconBtn.addEventListener("click", function() {
        dispatch({
          type: ProjectTagAction.UPDATE_PROJECT_SELECTED_TAG,
          payload: { selectedTag: tag }
        });
        setOpenUnitPriceSetting(true);
      });
    }
    td.appendChild(iconBtn);

    // Create a span for dollor symbol
    const dollor = document.createElement("span");
    dollor.style.marginLeft = "5px";
    dollor.textContent = dollorSymbol;
    td.appendChild(dollor);

    const unitPrice = hotInstance.getDataAtRowProp(row, "unitPrice");
    // Create a span for unit price
    const timeunitprice = document.createElement("span");
    timeunitprice.style.marginLeft = "3px";
    timeunitprice.textContent = unitPrice.toLocaleString();
    td.appendChild(timeunitprice);

    // Create a span for time unit
    const timeunit_span = document.createElement("span");
    timeunit_span.style.width = "60px";
    timeunit_span.style.height = "20px";
    timeunit_span.style.borderRadius = "12px";
    timeunit_span.style.display = "inline-block";  // To make sure width and height are respected
    timeunit_span.style.verticalAlign = "middle";  // To vertically align with the button
    timeunit_span.style.textAlign = "center";
    timeunit_span.style.lineHeight = "23px";       // To vertically center the text inside the span
    timeunit_span.style.backgroundColor = "#DDDDDD";
    timeunit_span.style.marginLeft = "5px";        // Add some spacing between the button and the span
    timeunit_span.textContent = value; // You can get the text from the 'value' argument
    td.appendChild(timeunit_span);

    if (tag === "") {
      td.style.background = "#DADADA";  // cell's background color
    }

    return td;
  };

  const timeValueCell = {
    editor: Handsontable.editors.NumericEditor,
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      // console.log("timeValueCell", hotInstance.getDataAtRowProp(row, "tag"), prop, value);
      const tag = hotInstance.getDataAtRowProp(row, "tag");
      if (tag === "") {
        td.style.color = "#999";
        td.style.background = "#DADADA";  // cell's background color
        cellProperties.readOnly = true; // Disable the cell
      }

      return td;
    },
  };

  const timeUnitDropDownCell = {
    editor: CustomDropdownEditor,
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      const tag = hotInstance.getDataAtRowProp(row, "tag");
      td.innerHTML = `<div class="timeunit-setting"><span>${value}</span><span>▼</span></div>`;

      if (tag === "") {
        td.style.color = "#999";
        td.style.background = "#DADADA";  // cell's background color
        cellProperties.readOnly = true; // Disable the cell
      }

      return td;
    },
    source: timeUnitOptions.map(unit => unit.name),
  };

  const riskCoefficientCell = {
    editor: Handsontable.editors.NumericEditor,
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      const tag = hotInstance.getDataAtRowProp(row, "tag");
      td.innerHTML = `<div><span>${value}</span><span>%</span></div>`;
      if (tag === "") {
        td.style.color = "#999";
        td.style.background = "#DADADA";  // cell's background color
        cellProperties.readOnly = true; // Disable the cell
      }

      return td;
    },
  };

  const riskManHourCell = {
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      const tag = hotInstance.getDataAtRowProp(row, "tag");
      td.innerHTML = value;
      if (tag === "") {
        td.style.color = "#999";
        td.style.background = "#DADADA";  // cell's background color
        cellProperties.readOnly = true; // Disable the cell
      }

      return td;
    },
  };

  const descriptionCell = {
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      const tag = hotInstance.getDataAtRowProp(row, "tag");
      td.innerHTML = value;
      if (tag === "") {
        td.style.color = "#999";
        td.style.background = "#DADADA";  // cell's background color
        cellProperties.readOnly = true; // Disable the cell
      }

      return td;
    },
  };

  const difficultyDownCell = {
    editor: CustomDropdownEditor,
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      const tag = hotInstance.getDataAtRowProp(row, "tag");
      td.innerHTML = `<div class="timeunit-setting"><span>${value === null ? "" : value}</span><span>▼</span></div>`;

      if (tag === "") {
        td.style.color = "#999";
        td.style.background = "#DADADA";  // cell's background color
        cellProperties.readOnly = true; // Disable the cell
      }

      return td;
    },
    source: difficultyOptions.map(difficulty => difficulty.name),
  };

  const taskSizeCell = {
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      const taskSize = hotInstance.getDataAtRowProp(row, "taskSize");
      td.innerHTML = value;
      if (taskSize === "") {
        td.style.color = "#A4ACB9";
        td.innerHTML = 0;
      }

      return td;
    },
  };

  const taskSizeUnitCell = {
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      const taskSizeUnit = hotInstance.getDataAtRowProp(row, "taskSizeUnit");
      td.innerHTML = value;
      if (taskSizeUnit === "") {
        td.style.color = "#A4ACB9";
        td.innerHTML = formatMessage({ id: "Unit" });
      }

      return td;
    },
  };

  const productivityCell = {
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      const productivity = hotInstance.getDataAtRowProp(row, "productivity");
      td.innerHTML = value;
      if (productivity === "") {
        td.style.color = "#A4ACB9";
        td.innerHTML = 0;
      }

      return td;
    },
  };

  const productivityUnitCell = {
    renderer: function (hotInstance, td, row, col, prop, value, cellProperties) {
      Handsontable.renderers.TextRenderer.apply(this, arguments);
      const taskSizeUnit = hotInstance.getDataAtRowProp(row, "taskSizeUnit");
      const timeUnit = hotInstance.getDataAtRowProp(row, "timeUnit");
      
      td.innerHTML = value;
      if (taskSizeUnit && timeUnit) {
        td.innerHTML = `<span style="display: block; color: #000; border-radius: 16px; text-align: center; background-color: #F2F2F2; height: 20px; margin-top: 1px;">${taskSizeUnit}/${formatMessage({ id: timeUnit })}</span>`;
      } else {
        td.innerHTML = `<span style="display: block; color: #000; border-radius: 16px; text-align: center; background-color: #F2F2F2; height: 20px; margin-top: 1px;">${formatMessage({ id: "Unit" })}/${formatMessage({ id: timeUnit })}</span>`;
      }

      return td;
    },
  };

  Handsontable.cellTypes.registerCellType("taskNameCell", taskNameCell);
  Handsontable.cellTypes.registerCellType("tagDropDownCell", tagDropDownCell);
  Handsontable.cellTypes.registerCellType("unitPriceCell", unitPriceCell);
  Handsontable.cellTypes.registerCellType("timeValueCell", timeValueCell);
  Handsontable.cellTypes.registerCellType("timeUnitDropDownCell", timeUnitDropDownCell);
  Handsontable.cellTypes.registerCellType("riskCoefficientCell", riskCoefficientCell);
  Handsontable.cellTypes.registerCellType("riskManHourCell", riskManHourCell);
  Handsontable.cellTypes.registerCellType("descriptionCell", descriptionCell);
  Handsontable.cellTypes.registerCellType("difficultyDownCell", difficultyDownCell);
  Handsontable.cellTypes.registerCellType("taskSizeCell", taskSizeCell);
  Handsontable.cellTypes.registerCellType("taskSizeUnitCell", taskSizeUnitCell);
  Handsontable.cellTypes.registerCellType("productivityCell", productivityCell);
  Handsontable.cellTypes.registerCellType("productivityUnitCell", productivityUnitCell);
  
  let mergeCells = [];
  rows.forEach((r, index) => {
    if (r.unitTask) mergeCells.push({ row: index, col: 1, rowspan: 1, colspan: 5 });
  });

  const handleCheckboxAllChecked = (event) => {
    dispatch({
      type: TaskAction.TASKLISTREVISED_CHECKBOX_ALL_CHECKED,
      payload: { checked: event.target.checked },
    });
    dispatch({
      type: TaskAction.TASKLISTREVISED_ADD_CHECKED_ALLTASK,
      payload: {
        taskList: rows,
        checked: event.target.checked
      }
    });
    setCheckAll(event.target.checked);
  };

  const handleCheckboxChecked = (event, row) => {
    // dispatch({
    //   type: TaskAction.TASKLISTREVISED_SET_ROW_VALUES,
    //   payload: {
    //     index: !row.unitTask ? row.staskId : row.ltaskId,
    //     name: "checkboxChecked",
    //     value: event.target.checked
    //   },
    // });
    dispatch({
      type: TaskAction.TASKLISTREVISED_UPDATE_CHECKBOX,
      payload: {
        id: !row.unitTask ? row.staskId : row.ltaskId,
        checked: event.target.checked,
        canDeleteFrom: row.canDeleteFrom,
        ltaskId: row.ltaskId,
        mtaskId: row.mtaskId,
        staskId: row.staskId,
        unitTask: row.unitTask
      }
    });
    dispatch({
      type: TaskAction.TASKLISTREVISED_ADD_CHECKED_TASK,
      payload: {
        id: !row.unitTask ? row.staskId : row.ltaskId,
        checked: event.target.checked,
        canDeleteFrom: row.canDeleteFrom,
        ltaskId: row.ltaskId,
        mtaskId: row.mtaskId,
        staskId: row.staskId,
        unitTask: row.unitTask
      }
    });
  };

  function afterGetColHeader(col, TH) {
    const headerTitle = TH.textContent || TH.innerText;
    const existingDifficultyIcon = TH.querySelector(".difficulty-inner-icon-class");
    if (existingDifficultyIcon) TH.firstChild.removeChild(existingDifficultyIcon);
    const existingTaskDateIcon = TH.querySelector(".taskdate-inner-icon-class");
    if (existingTaskDateIcon) TH.firstChild.removeChild(existingTaskDateIcon);
    const existingTaskSizeIcon = TH.querySelector(".tasksize-inner-icon-class");
    if (existingTaskSizeIcon) TH.firstChild.removeChild(existingTaskSizeIcon);
    const existingTaskProductivityIcon = TH.querySelector(".taskproductivity-inner-icon-class");
    if (existingTaskProductivityIcon) TH.firstChild.removeChild(existingTaskProductivityIcon);

    switch (headerTitle) {
      case "小項目":
      case "Small Item": {
        if (selectedTaskFields.length < 4) {
          const existingInnerIcon = TH.querySelector(".inner-icon-class");
          if (!existingInnerIcon) {
            const innerIconBtn = document.createElement("img");
            innerIconBtn.src = arrowLeftIcon;
            innerIconBtn.style.backgroundColor = "#fff";
            innerIconBtn.style.cursor = "pointer";
            innerIconBtn.classList.add("inner-icon-class");
            innerIconBtn.onclick = function() {
              dispatch({
                type: EstimationSettingAction.UPDATE_ESTIMATION_SETTING,
                payload: {
                  selectedTaskFields: [
                    "Task-Start-End-Date",
                    "Task-Size",
                    "Task-Productivity",
                    "Task-Difficulty"
                  ]
                }
              });
            };
            TH.firstChild.appendChild(innerIconBtn);
          } else {
            existingInnerIcon.style.display = "block";
          }
    
          // Outer "right icon" for the right outer edge of the fifth header
          const existingOuterIcon = TH.querySelector(".outer-icon-class");
          if (!existingOuterIcon) {
            const outerIconBtn = document.createElement("img");
            outerIconBtn.src = arrowRightIcon;
            outerIconBtn.style.backgroundColor = "#fff";
            outerIconBtn.style.cursor = "pointer";
            outerIconBtn.classList.add("outer-icon-class");
            outerIconBtn.onclick = function() {
              dispatch({
                type: EstimationSettingAction.UPDATE_ESTIMATION_SETTING,
                payload: {
                  selectedTaskFields: [
                    "Task-Start-End-Date",
                    "Task-Size",
                    "Task-Productivity",
                    "Task-Difficulty"
                  ]
                }
              });
            };
            TH.firstChild.appendChild(outerIconBtn);
          } else {
            existingOuterIcon.style.display = "block";
          }
        } else {
          // If selectedTaskFields.length == 4, hide the icons (if they exist)
          const existingInnerIcon = TH.querySelector(".inner-icon-class");
          const existingOuterIcon = TH.querySelector(".outer-icon-class");
    
          if (existingInnerIcon) {
            existingInnerIcon.style.display = "none";
          }

          if (existingOuterIcon) {
            existingOuterIcon.style.display = "none";
          }
        }
        break;
      }
      case "Work Period (Start)":
      case "作業期間 (開始)": {
        if (selectedTaskFields.includes("Task-Start-End-Date")) {
          const innerIconBtn = document.createElement("img");
          innerIconBtn.src = visibilityOffIcon;
          innerIconBtn.style.width = "12px";
          innerIconBtn.style.height = "12px";
          innerIconBtn.style.backgroundColor = "#F0F0F0";
          innerIconBtn.style.cursor = "pointer";
          innerIconBtn.classList.add("taskdate-inner-icon-class");
          innerIconBtn.onclick = function() {
            dispatch({
              type: EstimationSettingAction.UPDATE_ESTIMATION_SETTING,
              payload: {
                selectedTaskFields: selectedTaskFields.filter(field => field !== "Task-Start-End-Date")
              }
            });
          };
          TH.firstChild.appendChild(innerIconBtn);
        }
        break;
      }
      case "Task Size":
      case "作業規模": {
        if (selectedTaskFields.includes("Task-Size")) {
          const innerIconBtn = document.createElement("img");
          innerIconBtn.src = visibilityOffIcon;
          innerIconBtn.style.width = "12px";
          innerIconBtn.style.height = "12px";
          innerIconBtn.style.backgroundColor = "#F0F0F0";
          innerIconBtn.style.cursor = "pointer";
          innerIconBtn.classList.add("tasksize-inner-icon-class");
          innerIconBtn.onclick = function() {
            dispatch({
              type: EstimationSettingAction.UPDATE_ESTIMATION_SETTING,
              payload: {
                selectedTaskFields: selectedTaskFields.filter(field => field !== "Task-Size")
              }
            });
          };
          TH.firstChild.appendChild(innerIconBtn);
        }
        break;
      }
      case "Task Productivity":
      case "生産性": {
        if (selectedTaskFields.includes("Task-Productivity")) {
          const innerIconBtn = document.createElement("img");
          innerIconBtn.src = visibilityOffIcon;
          innerIconBtn.style.width = "12px";
          innerIconBtn.style.height = "12px";
          innerIconBtn.style.backgroundColor = "#F0F0F0";
          innerIconBtn.style.cursor = "pointer";
          innerIconBtn.classList.add("taskproductivity-inner-icon-class");
          innerIconBtn.onclick = function() {
            dispatch({
              type: EstimationSettingAction.UPDATE_ESTIMATION_SETTING,
              payload: {
                selectedTaskFields: selectedTaskFields.filter(field => field !== "Task-Productivity")
              }
            });
          };
          TH.firstChild.appendChild(innerIconBtn);
        }
        break;
      }
      case "Difficulty":
      case "難易度": {
        if (selectedTaskFields.includes("Task-Difficulty")) {
          const innerIconBtn = document.createElement("img");
          innerIconBtn.src = visibilityOffIcon;
          innerIconBtn.style.width = "12px";
          innerIconBtn.style.height = "12px";
          innerIconBtn.style.backgroundColor = "#F0F0F0";
          innerIconBtn.style.cursor = "pointer";
          innerIconBtn.classList.add("difficulty-inner-icon-class");
          innerIconBtn.onclick = function() {
            dispatch({
              type: EstimationSettingAction.UPDATE_ESTIMATION_SETTING,
              payload: {
                selectedTaskFields: selectedTaskFields.filter(field => field !== "Task-Difficulty")
              }
            });
          };
          TH.firstChild.appendChild(innerIconBtn);
        }
        break;
      }
      default:
        break;
    }
  }

  let hiddenColumns = [];
  if (!selectedTaskFields.includes("Task-Start-End-Date")) { hiddenColumns.push(6); hiddenColumns.push(7); }
  if (!selectedTaskFields.includes("Task-Size")) { hiddenColumns.push(8); hiddenColumns.push(9); }
  if (!selectedTaskFields.includes("Task-Productivity")) { hiddenColumns.push(10); hiddenColumns.push(11); }
  if (!selectedTaskFields.includes("Task-Difficulty")) hiddenColumns.push(12);

  let nestedHeaders = ["",
    formatMessage({ id: "Large-item" }),
    "",
    formatMessage({ id: "Medium-item" }),
    "",
    formatMessage({ id: "Small-item" })
  ];
  nestedHeaders.push(formatMessage({ id: "Start-End-Date-S" }));
  nestedHeaders.push(formatMessage({ id: "Start-End-Date-E" }));
  nestedHeaders.push({label: formatMessage({ id: "Task-Size" }), colspan: 2 });
  nestedHeaders.push({label: formatMessage({ id: "Task-Productivity" }), colspan: 2 });
  nestedHeaders.push(formatMessage({ id: "Task-Difficulty" }));
  nestedHeaders.push(formatMessage({ id: "Cost-setting" }));
  nestedHeaders.push(formatMessage({ id: "Unit-price" }));
  nestedHeaders.push(formatMessage({ id: "Man-hour" }));
  nestedHeaders.push(formatMessage({ id: "Man-hour-time-unit" }));
  nestedHeaders.push(formatMessage({ id: "Risk-factor" }));
  nestedHeaders.push(formatMessage({ id: "Efforts-including-risk" }));
  nestedHeaders.push(formatMessage({ id: "Comment" }));

  const getListStyle = isDraggingOver => ({
    background: isDraggingOver ? "lightblue" : "#fff",
    padding: 0,
    maxWidth: "960px"
  });

  const getItemStyle = (isDragging, draggableStyle) => ({
    // some basic styles to make the items look a bit nicer
    userSelect: "none",
    padding: 0,
    margin: "0 0 0 0",

    // change background colour if dragging
    background: "#fff",

    // styles we need to apply on draggables
    ...draggableStyle
  });

  const onDragEnd = (result) => {
    if (result.destination !== null) {
      const source = rows.find((row) => row.idx == result.source.index);
      const destination = rows.find((row) => row.idx == result.destination.index);
      if (source.canDeleteFrom !== destination.canDeleteFrom) return;
      if (source.unitTask !== destination.unitTask) return;
  
      dispatch({
        type: TaskAction.TASKLISTREVISED_CHANGE_POSITION,
        payload: { source, destination }
      });
    }
  };

  return (
    <div>
      {
        rows.length > 0
          ? <div style={{ display: "grid", gridAutoFlow: "column", gap: 0, gridTemplateColumns: "28px 28px auto" }}>
            <div style={{ border: "1px solid #DDDDDD", width: "28px" }}>
              <Style.Checkbox>
                <Checkbox size="small" style={{ width: "20px", height: "20px" }} onChange={(event) => handleCheckboxAllChecked(event)} checked={checkAll} />
              </Style.Checkbox>
              <Style.CheckboxContainer>
                {
                  rows.map((row, index) => (
                    <Style.Checkbox key={index}>
                      <Checkbox size="small" style={{ width: "20px", height: "20px" }} onChange={(event) => handleCheckboxChecked(event, row)} checked={row.checkboxChecked} />
                    </Style.Checkbox>
                  ))
                }
              </Style.CheckboxContainer>
            </div>
            <div style={{ border: "1px solid #DDDDDD", width: "28px" }}>
              <Style.Checkbox />
              <Style.CheckboxContainer>
                <DragDropContext onDragEnd={onDragEnd}>
                  <Droppable droppableId="droppable">
                    {(provided, snapshot) => (
                      <div
                        {...provided.droppableProps}
                        ref={provided.innerRef}
                        style={getListStyle(snapshot.isDraggingOver)}
                      >
                        {
                          rows.map((data, index) => (
                            <Draggable key={index} draggableId={`${data.idx}`} index={data.idx}>
                              {(provided, snapshot) => (
                                <div
                                  ref={provided.innerRef}
                                  {...provided.draggableProps}
                                  {...provided.dragHandleProps}
                                  style={getItemStyle(
                                    snapshot.isDragging,
                                    provided.draggableProps.style
                                  )}
                                >
                                  <Style.IndicatorContainer key={index}>
                                    <DragIndicator />
                                    {snapshot.isDragging && <div style={{ position: "absolute", marginLeft: "25px", border: "1px solid #CCCCCC", backgroundColor: "#fff", minWidth: "1200px", height: "23px", fontSize: "14px", padding: "2px", display: "grid", gridAutoFlow: "column", gap: 0, gridTemplateColumns: "200px 200px 200px auto" }}>
                                      <span>{data.ltask}</span>
                                      <span>{data.mtask}</span>
                                      <span>{data.stask}</span>
                                    </div>}
                                  </Style.IndicatorContainer>
                                </div>
                              )}
                            </Draggable>
                          ))
                        }
                        {provided.placeholder}
                      </div>
                    )}
                  </Droppable>
                </DragDropContext>
              </Style.CheckboxContainer>
            </div>
            <HotTable
              ref={hotTableComponent}
              data={rows}
              nestedHeaders={[
                nestedHeaders
              ]}
              height={rows.length <= 10 ? 300 : "auto"}
              // width="auto"
              colWidths={100}
              rowHeights={25}
              manualColumnResize={true}
              autoRowSize={false}
              autoColumnSize={false}
              minSpareRows={0}
              fixedColumnsStart={6}
              licenseKey="eefeef"
              afterChange={afterChange}
              afterGetColHeader={afterGetColHeader}
              mergeCells={mergeCells}
              hiddenColumns={{
                columns: hiddenColumns,
                indicators: true,
              }}
            >
              <HotColumn data="ltaskIdAndcanDeleteFrom" width={25} readOnly={true} renderer={buttonRenderer} />
              <HotColumn data="ltask" width={200} type="taskNameCell" />
              <HotColumn data="mtaskIdAndcanDeleteFrom" width={25} readOnly={true} renderer={buttonRenderer} />
              <HotColumn data="mtask" width={200} type="taskNameCell" />
              <HotColumn data="staskIdAndcanDeleteFrom" width={25} readOnly={true} renderer={buttonRenderer} />
              <HotColumn data="stask" width={200} type="taskNameCell" />
              <HotColumn data="taskStartEndDateS" width={150} type="date" dateFormat="YYYY-MM-DD" />
              <HotColumn data="taskStartEndDateE" width={150} type="date" dateFormat="YYYY-MM-DD" />
              <HotColumn data="taskSize" width={80} type="taskSizeCell" />
              <HotColumn data="taskSizeUnit" width={100} type="taskSizeUnitCell" />
              <HotColumn data="productivity" width={80} type="productivityCell" />
              <HotColumn data="productivityUnit" width={100} type="productivityUnitCell" />
              <HotColumn data="difficultyName" width={150} type="difficultyDownCell" />
              <HotColumn data="tagName" width={200} type="tagDropDownCell" />
              <HotColumn data="timeUnitName" width={200} readOnly={true} renderer={unitPriceCell} />
              <HotColumn data="timeValue" width={100} type="timeValueCell" />
              <HotColumn data="timeUnitName" width={100} type="timeUnitDropDownCell" />
              <HotColumn data="riskCoefficient" width={100} type="riskCoefficientCell" />
              <HotColumn data="riskManHour" width={150} readOnly={true} type="riskManHourCell" />
              <HotColumn data="description" width={200} type="descriptionCell" />
            </HotTable>
          </div> : <div
            style={{
              width: "100%",
              minHeight: "525px",
              background: "#F9F9F9",
              color: "#BCBCBC",
              display: "flex",
              flexDirection: "column",
              justifyContent: "center",
              alignItems: "center",
              fontSize: "28px"
            }}
          >
            <span>+ {formatMessage({ id: "Make-Estimation" })}</span>
            <div style={{ marginTop: "20px", display: "grid", gap: "10px", gridAutoFlow: "column" }}>
              <Button
                variant="contained"
                onClick={handleUnitTaskAdd}
                style={{ width: "270px" }}
              >
                {formatMessage({ id: "Easy-Mode-Estimation" })}
              </Button>
              <Button
                variant="contained"
                onClick={handleTaskAdd}
                style={{ width: "270px" }}
              >
                {formatMessage({ id: "Detail-Mode-Estimation" })}
              </Button>
            </div>
          </div>
      }
      <div style={{
        display: "flex",
        flexDirection: "row",
        justifyContent: "space-between",
      }}>
        <div style={{ display: "grid", gridAutoFlow: "column", gap: "10px", padding: "10px 0 10px 0" }}>
          <SaveAsTemplateDialog />
        </div>
      </div>
      <UnitPriceSetting
        open={openUnitPriceSetting}
        handleCloseModal={() => setOpenUnitPriceSetting(false)}
        currency={currency}
      />
    </div>
  );
}

export default HandsOnGrid;

Solved! I have to import “handsontable/dist/handsontable.full.min.css” in this file.

1 Like

I am happy to hear that.

Thank you for the update.