diff --git a/_reference/reportFiltersAndSorters.md b/_reference/reportFiltersAndSorters.md index b7dfc508c..3d680f387 100644 --- a/_reference/reportFiltersAndSorters.md +++ b/_reference/reportFiltersAndSorters.md @@ -3,6 +3,9 @@ This documentation details the schema required for `.filters` files on the report server. It is used to dynamically modify the graphQL query and provide the user more power over their reports. +# Special Notes +- When passing the data to the template server, the property filters and sorters is added to the data object and will reflect the filters and sorters the user has selected + ## High level Schema Overview ```javascript @@ -36,6 +39,35 @@ const schema = { Filters effect the where clause of the graphQL query. They are used to filter the data returned from the server. A note on special notation used in the `name` field. +## Reflection +Filters can make use of reflection to pre-fill select boxes, the following is an example of that in the filters file. + +``` + { + "name": "jobs.status", + "translation": "jobs.fields.status", + "label": "Status", + "type": "string", + "reflector": { + "type": "internal", + "name": "special.job_statuses" + } + }, +``` + +in this example, a reflector with the type 'internal' (all types at the moment require this, and it is used for future functionality), with a name of `special.job_statuses` + +The following cases are available + +- `special.job_statuses` - This will reflect the statuses of the jobs table `bodyshop.md_ro_statuses.statuses'` +- `special.cost_centers` - This will reflect the cost centers `bodyshop.md_responsibility_centers.costs` +- `special.categories` - This will reflect the categories `bodyshop.md_categories` +- `special.insurance_companies` - This will reflect the insurance companies `bodyshop.md_ins_cos`' +- `special.employee_teams` - This will reflect the employee teams `bodyshop.employee_teams` +- `special.employees` - This will reflect the employees `bodyshop.employees` +- `special.first_names` - This will reflect the first names `bodyshop.employees` +- `special.last_names` - This will reflect the last names `bodyshop.employees` +- ### Path without brackets, multi level `"name": "jobs.joblines.mod_lb_hrs",` @@ -124,3 +156,20 @@ query gendoc_hours_sold_detail_open($starttz: timestamptz!, $endtz: timestamptz! would be added at the `joblines` level. - Most of the reports currently do sorting on a template level, this will need to change to actually see the results using the sorters. + +### Default Sorters +- A sorter can be given a default object containing a `order` and `direction` key value. This will be used to sort the report if the user does not select any of the sorters themselves. +- The `order` key is the order in which the sorters are applied, and the `direction` key is the direction of the sort, either `asc` or `desc`. + +```json +{ + "name": "jobs.joblines.mod_lb_hrs", + "translation": "jobs.joblines.mod_lb_hrs_1", + "label": "mod_lb_hrs_1", + "type": "number", + "default": { + "order": 1, + "direction": "asc" + } +} +``` \ No newline at end of file diff --git a/client/package-lock.json b/client/package-lock.json index fe03b8009..3dfacb8b5 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -15,13 +15,13 @@ "@craco/craco": "^7.1.0", "@fingerprintjs/fingerprintjs": "^4.2.2", "@jsreport/browser-client": "^3.1.0", - "@reduxjs/toolkit": "^2.2.0", + "@reduxjs/toolkit": "^2.2.1", "@sentry/cli": "^2.28.6", - "@sentry/react": "^7.101.0", - "@sentry/tracing": "^7.101.0", + "@sentry/react": "^7.102.1", + "@sentry/tracing": "^7.102.1", "@splitsoftware/splitio-react": "^1.11.0", "@tanem/react-nprogress": "^5.0.51", - "antd": "^5.14.1", + "antd": "^5.14.2", "apollo-link-logger": "^2.0.1", "apollo-link-sentry": "^3.3.0", "axios": "^1.6.7", @@ -29,17 +29,17 @@ "dayjs": "^1.11.10", "dayjs-business-days2": "^1.2.2", "dinero.js": "^1.9.1", - "dotenv": "^16.4.4", + "dotenv": "^16.4.5", "enquire-js": "^0.2.1", "env-cmd": "^10.1.0", "exifr": "^7.1.3", "firebase": "^10.8.0", "graphql": "^16.6.0", - "i18next": "^23.8.2", + "i18next": "^23.10.0", "i18next-browser-languagedetector": "^7.0.2", "jsoneditor": "^10.0.1", "jsreport-browser-client-dist": "^1.3.0", - "libphonenumber-js": "^1.10.55", + "libphonenumber-js": "^1.10.57", "logrocket": "^8.0.1", "markerjs2": "^2.32.0", "normalize-url": "^8.0.0", @@ -50,9 +50,9 @@ "rc-queue-anim": "^2.0.0", "rc-scroll-anim": "^2.7.6", "react": "^18.2.0", - "react-big-calendar": "^1.10.1", + "react-big-calendar": "^1.10.3", "react-color": "^2.19.3", - "react-cookie": "^7.0.2", + "react-cookie": "^7.1.0", "react-dom": "^18.2.0", "react-drag-listview": "^2.0.0", "react-grid-gallery": "^1.0.0", @@ -60,23 +60,23 @@ "react-i18next": "^14.0.5", "react-icons": "^5.0.1", "react-image-lightbox": "^5.1.4", - "react-intersection-observer": "^9.8.0", + "react-intersection-observer": "^9.8.1", "react-markdown": "^9.0.1", "react-number-format": "^5.1.4", "react-redux": "^9.1.0", "react-resizable": "^3.0.5", - "react-router-dom": "^6.22.0", + "react-router-dom": "^6.22.1", "react-scripts": "^5.0.1", "react-sticky": "^6.0.3", "react-sublime-video": "^0.2.5", "react-virtualized": "^9.22.5", - "recharts": "^2.12.0", + "recharts": "^2.12.1", "redux": "^5.0.1", "redux-persist": "^6.0.0", "redux-saga": "^1.3.0", "redux-state-sync": "^3.1.4", "reselect": "^5.1.0", - "sass": "^1.70.0", + "sass": "^1.71.1", "socket.io-client": "^4.7.4", "styled-components": "^6.1.8", "subscriptions-transport-ws": "^0.11.0", @@ -88,13 +88,13 @@ "workbox-precaching": "^7.0.0", "workbox-routing": "^7.0.0", "workbox-strategies": "^7.0.0", - "yauzl": "^2.10.0" + "yauzl": "^3.1.0" }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "@sentry/webpack-plugin": "^2.14.0", + "@sentry/webpack-plugin": "^2.14.2", "@testing-library/cypress": "^10.0.1", - "cypress": "^13.6.4", + "cypress": "^13.6.6", "eslint-plugin-cypress": "^2.15.1", "react-error-overlay": "6.0.11", "redux-logger": "^3.0.6", @@ -4200,9 +4200,9 @@ "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==" }, "node_modules/@rc-component/color-picker": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-1.5.1.tgz", - "integrity": "sha512-onyAFhWKXuG4P162xE+7IgaJkPkwM94XlOYnQuu69XdXWMfxpeFi6tpJBsieIMV7EnyLV5J3lDzdLiFeK0iEBA==", + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/@rc-component/color-picker/-/color-picker-1.5.2.tgz", + "integrity": "sha512-YJXujYzYFAEtlXJXy0yJUhwzUWPTcniBZto+wZ/vnACmFnUTNR7dH+NOeqSwMMsssh74e9H5Jfpr5LAH2PYqUw==", "dependencies": { "@babel/runtime": "^7.23.6", "@ctrl/tinycolor": "^3.6.1", @@ -4362,9 +4362,9 @@ "integrity": "sha512-1dgmkh+3so0+LlBWRhGA33ua4MYr7tUOj+a9Si28vUi0IUFNbff1T3sgpeDJI/LaC75bBYnQ0A3wXjn0OrRNBA==" }, "node_modules/@reduxjs/toolkit": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.0.tgz", - "integrity": "sha512-ZvPYKfu4kDnAqPhJ1bsis8QFbiQRz3Q2HxW3tw9tVGusPzYKRG7ju1FA+34PGcwCoemjGGv+f/7fEygcRZIwmA==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/@reduxjs/toolkit/-/toolkit-2.2.1.tgz", + "integrity": "sha512-8CREoqJovQW/5I4yvvijm/emUiCCmcs4Ev4XPWd4mizSO+dD3g5G6w34QK5AGeNrSH7qM8Fl66j4vuV7dpOdkw==", "dependencies": { "immer": "^10.0.3", "redux": "^5.0.1", @@ -4394,9 +4394,9 @@ } }, "node_modules/@remix-run/router": { - "version": "1.15.0", - "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.0.tgz", - "integrity": "sha512-HOil5aFtme37dVQTB6M34G95kPM3MMuqSmIRVCC52eKV+Y/tGSqw9P3rWhlAx6A+mz+MoX+XxsGsNJbaI5qCgQ==", + "version": "1.15.1", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.1.tgz", + "integrity": "sha512-zcU0gM3z+3iqj8UX45AmWY810l3oUmXM7uH4dt5xtzvMhRtYVhKGOmgOd1877dOPPepfCjUv57w+syamWIYe7w==", "engines": { "node": ">=14.0.0" } @@ -4500,189 +4500,173 @@ "integrity": "sha512-2/U3GXA6YiPYQDLGwtGlnNgKYBSwCFIHf8Y9LUY5VATHdtbLlU0Y1R3QoBnT0aB4qv/BEiVVsj7LJXoQCgJ2vA==" }, "node_modules/@sentry-internal/feedback": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.101.0.tgz", - "integrity": "sha512-uQBMYhZp/qkBEA/GXRMm1OfSkRkZojxBrCrFmzkWhJzXT+YbL57/M1uCcwkKmorKlg393Soh7MLULInwmcwWkA==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/feedback/-/feedback-7.102.1.tgz", + "integrity": "sha512-vY4hpLLMNLjICtWiizc7KeGbWOTUMGrF7C+9dPCztZww3CLgzWy9A7DvPj5hodRiYzpdRnAMl8yQnMFbYXh7bA==", "dependencies": { - "@sentry/core": "7.101.0", - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/core": "7.102.1", + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=12" } }, "node_modules/@sentry-internal/feedback/node_modules/@sentry/core": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.101.0.tgz", - "integrity": "sha512-dRNrNV5OLGARkOGgxJsVDhA98Pev5G1LVJcud5E83cRg49BCUx2riqEtDP6iIS1nvem6cApkSnLC1kvl/T5/Cw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.102.1.tgz", + "integrity": "sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg==", "dependencies": { - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry-internal/feedback/node_modules/@sentry/types": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.101.0.tgz", - "integrity": "sha512-YC+ltO/AlbEyJHjCUYQ4is1HcDT2zSMuLkIAcyQmK7fUdlGT4iR5sfENriY9ZopYHgjPdJKfhI8ohScam7zp/A==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.102.1.tgz", + "integrity": "sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA==", "engines": { "node": ">=8" } }, "node_modules/@sentry-internal/feedback/node_modules/@sentry/utils": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.101.0.tgz", - "integrity": "sha512-px1NUkCLsD9UKLE4W4DghpyzmAVHgYhskrjRt30ubyUKqlggtHkOXRvS8MjuWowR/i0wF0GuTCbU9StBd7JMrw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.102.1.tgz", + "integrity": "sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg==", "dependencies": { - "@sentry/types": "7.101.0" + "@sentry/types": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry-internal/replay-canvas": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.101.0.tgz", - "integrity": "sha512-fiz4kPpz/j6ZaD+vOcUXuO1HqD49djs4QwyTsRwCCi77EKZOGAaijpqWckDWyZs0dOOnbGGGC5x3o+CfTJcjKA==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/replay-canvas/-/replay-canvas-7.102.1.tgz", + "integrity": "sha512-GUX4RWI10uRjdjeyvCLtAAhWRVqnAnG6+yNxWfqUQ3qMA7B7XxG43KT2UhSnulmErNzODQ6hA68rGPwwYeRIww==", "dependencies": { - "@sentry/core": "7.101.0", - "@sentry/replay": "7.101.0", - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/core": "7.102.1", + "@sentry/replay": "7.102.1", + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=12" } }, "node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/core": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.101.0.tgz", - "integrity": "sha512-dRNrNV5OLGARkOGgxJsVDhA98Pev5G1LVJcud5E83cRg49BCUx2riqEtDP6iIS1nvem6cApkSnLC1kvl/T5/Cw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.102.1.tgz", + "integrity": "sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg==", "dependencies": { - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/types": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.101.0.tgz", - "integrity": "sha512-YC+ltO/AlbEyJHjCUYQ4is1HcDT2zSMuLkIAcyQmK7fUdlGT4iR5sfENriY9ZopYHgjPdJKfhI8ohScam7zp/A==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.102.1.tgz", + "integrity": "sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA==", "engines": { "node": ">=8" } }, "node_modules/@sentry-internal/replay-canvas/node_modules/@sentry/utils": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.101.0.tgz", - "integrity": "sha512-px1NUkCLsD9UKLE4W4DghpyzmAVHgYhskrjRt30ubyUKqlggtHkOXRvS8MjuWowR/i0wF0GuTCbU9StBd7JMrw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.102.1.tgz", + "integrity": "sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg==", "dependencies": { - "@sentry/types": "7.101.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry-internal/tracing": { - "version": "7.100.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.100.0.tgz", - "integrity": "sha512-qf4W1STXky9WOQYoPSw2AmCBDK4FzvAyq5yeD2sLU7OCUEfbRUcN0lQljUvmWRKv/jTIAyeU5icDLJPZuR50nA==", - "dev": true, - "dependencies": { - "@sentry/core": "7.100.0", - "@sentry/types": "7.100.0", - "@sentry/utils": "7.100.0" + "@sentry/types": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/babel-plugin-component-annotate": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.14.0.tgz", - "integrity": "sha512-FWU4+Lx6fgxjAkwmc3S9j1Q/6pqKZyZzfi52B+8WMNw7a5QjGXgxc5ucBazZYgrcsJKCFBp4QG3PPxNAieFimQ==", + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/@sentry/babel-plugin-component-annotate/-/babel-plugin-component-annotate-2.14.2.tgz", + "integrity": "sha512-mFBVnIZmdMrpxo61rG5yf0WFt5VrRpy8cpIpJtT3mYkX9vDmcUZaZaD1ctv73iZF3QwaieVdn05Na5mWzZ8h/A==", "dev": true, "engines": { "node": ">= 14" } }, "node_modules/@sentry/browser": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.101.0.tgz", - "integrity": "sha512-wj9YLfS/caR20Yq0hdEjsZHuhnYLU7Ht0SlcJx5MNMnArtmW1k2CWZz3PCqcW/rTZe53npVTe6eMqMccB4aPrQ==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/browser/-/browser-7.102.1.tgz", + "integrity": "sha512-7BOfPBiM7Kp6q/iy0JIbsBTxIASV+zWXByqqjuEMWGj3X2u4oRIfm3gv4erPU/l+CORQUVQZLSPGoIoM1gbB/A==", "dependencies": { - "@sentry-internal/feedback": "7.101.0", - "@sentry-internal/replay-canvas": "7.101.0", - "@sentry-internal/tracing": "7.101.0", - "@sentry/core": "7.101.0", - "@sentry/replay": "7.101.0", - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry-internal/feedback": "7.102.1", + "@sentry-internal/replay-canvas": "7.102.1", + "@sentry-internal/tracing": "7.102.1", + "@sentry/core": "7.102.1", + "@sentry/replay": "7.102.1", + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/browser/node_modules/@sentry-internal/tracing": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.101.0.tgz", - "integrity": "sha512-rp9oOLQs6vMuzvAnAHRRCNu5Z0o/ZVRI3WPYedxpdMWKD1Z3G9o+0joP+ZIUqHsamWWYiIgPqXgL9AK6AWjFRg==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.102.1.tgz", + "integrity": "sha512-RkFlFyAC0fQOvBbBqnq0CLmFW5m3JJz9pKbZd5vXPraWAlniKSb1bC/4DF9SlNx0FN1LWG+IU3ISdpzwwTeAGg==", "dependencies": { - "@sentry/core": "7.101.0", - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/core": "7.102.1", + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/browser/node_modules/@sentry/core": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.101.0.tgz", - "integrity": "sha512-dRNrNV5OLGARkOGgxJsVDhA98Pev5G1LVJcud5E83cRg49BCUx2riqEtDP6iIS1nvem6cApkSnLC1kvl/T5/Cw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.102.1.tgz", + "integrity": "sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg==", "dependencies": { - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/browser/node_modules/@sentry/types": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.101.0.tgz", - "integrity": "sha512-YC+ltO/AlbEyJHjCUYQ4is1HcDT2zSMuLkIAcyQmK7fUdlGT4iR5sfENriY9ZopYHgjPdJKfhI8ohScam7zp/A==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.102.1.tgz", + "integrity": "sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA==", "engines": { "node": ">=8" } }, "node_modules/@sentry/browser/node_modules/@sentry/utils": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.101.0.tgz", - "integrity": "sha512-px1NUkCLsD9UKLE4W4DghpyzmAVHgYhskrjRt30ubyUKqlggtHkOXRvS8MjuWowR/i0wF0GuTCbU9StBd7JMrw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.102.1.tgz", + "integrity": "sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg==", "dependencies": { - "@sentry/types": "7.101.0" + "@sentry/types": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/bundler-plugin-core": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.14.0.tgz", - "integrity": "sha512-jVM47EPs8Na2z5HOWgthLFhpHLU9hwL2wY4TzHEnS1Bj+ODgXFa8QcIxQR2SO+W+L8YhSbY7z+BpPsYTpeZWUg==", + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/@sentry/bundler-plugin-core/-/bundler-plugin-core-2.14.2.tgz", + "integrity": "sha512-HgOFWYdq87lSmeVW1w8K2Vf2DGzRPvKzHTajZYLTPlrZ1jbajq9vwuqhrJ9AnDkjl0mjyzSPEy3ZTeG1Z7uRNA==", "dev": true, "dependencies": { "@babel/core": "7.18.5", - "@sentry/babel-plugin-component-annotate": "2.14.0", + "@sentry/babel-plugin-component-annotate": "2.14.2", "@sentry/cli": "^2.22.3", - "@sentry/node": "^7.60.0", - "@sentry/utils": "^7.60.0", "dotenv": "^16.3.1", "find-up": "5.0.0", "glob": "9.3.2", @@ -4874,43 +4858,15 @@ "node": ">=10" } }, - "node_modules/@sentry/core": { - "version": "7.100.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.100.0.tgz", - "integrity": "sha512-eWRPuP0Zdj4a2F7SybqNjf13LGOVgGwvW6sojweQp9oxGAfCPp/EMDGBhlpYbMJeLbzmqzJ4ZFHIedaiEC+7kg==", - "dev": true, - "dependencies": { - "@sentry/types": "7.100.0", - "@sentry/utils": "7.100.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/node": { - "version": "7.100.0", - "resolved": "https://registry.npmjs.org/@sentry/node/-/node-7.100.0.tgz", - "integrity": "sha512-8cf9wFNo/9I++60MVAf6tuKn/N5JCZ/Z8NDUzutnWWdQBLSx+LhZYNPntN3WkHl6Q7PBHGw3mU1Bc+rF48MeSQ==", - "dev": true, - "dependencies": { - "@sentry-internal/tracing": "7.100.0", - "@sentry/core": "7.100.0", - "@sentry/types": "7.100.0", - "@sentry/utils": "7.100.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@sentry/react": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.101.0.tgz", - "integrity": "sha512-EjzkuUzLhE1glUsLPcLhmc8Wg7D5qfKQ08Vbcw+GsE7aLnhWrPTHexkf2NPNeTkgUxbWToJn6PWCodICo5YYbw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/react/-/react-7.102.1.tgz", + "integrity": "sha512-X4j2DgbktlEifnd21YJKCayAmff5hnaS+9MNz9OonEwD0ARi0ks7bo0wtWHMjPK20992MO+JwczVg/1BXJYDdQ==", "dependencies": { - "@sentry/browser": "7.101.0", - "@sentry/core": "7.101.0", - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0", + "@sentry/browser": "7.102.1", + "@sentry/core": "7.102.1", + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1", "hoist-non-react-statics": "^3.3.2" }, "engines": { @@ -4921,177 +4877,156 @@ } }, "node_modules/@sentry/react/node_modules/@sentry/core": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.101.0.tgz", - "integrity": "sha512-dRNrNV5OLGARkOGgxJsVDhA98Pev5G1LVJcud5E83cRg49BCUx2riqEtDP6iIS1nvem6cApkSnLC1kvl/T5/Cw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.102.1.tgz", + "integrity": "sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg==", "dependencies": { - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/react/node_modules/@sentry/types": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.101.0.tgz", - "integrity": "sha512-YC+ltO/AlbEyJHjCUYQ4is1HcDT2zSMuLkIAcyQmK7fUdlGT4iR5sfENriY9ZopYHgjPdJKfhI8ohScam7zp/A==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.102.1.tgz", + "integrity": "sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA==", "engines": { "node": ">=8" } }, "node_modules/@sentry/react/node_modules/@sentry/utils": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.101.0.tgz", - "integrity": "sha512-px1NUkCLsD9UKLE4W4DghpyzmAVHgYhskrjRt30ubyUKqlggtHkOXRvS8MjuWowR/i0wF0GuTCbU9StBd7JMrw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.102.1.tgz", + "integrity": "sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg==", "dependencies": { - "@sentry/types": "7.101.0" + "@sentry/types": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/replay": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.101.0.tgz", - "integrity": "sha512-DSWkGKI/QhCAY+qm4mBnPob3/YsewisskVTak7KMDotJ75H85WFJhVwOMtvaEWIzVezCOItPv7ql51jTwhR3wA==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/replay/-/replay-7.102.1.tgz", + "integrity": "sha512-HR/j9dGIvbrId8fh8mQlODx7JrhRmawEd9e9P3laPtogWCg/5TI+XPb2VGSaXOX9VWtb/6Z2UjHsaGjgg6YcuA==", "dependencies": { - "@sentry-internal/tracing": "7.101.0", - "@sentry/core": "7.101.0", - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry-internal/tracing": "7.102.1", + "@sentry/core": "7.102.1", + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=12" } }, "node_modules/@sentry/replay/node_modules/@sentry-internal/tracing": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.101.0.tgz", - "integrity": "sha512-rp9oOLQs6vMuzvAnAHRRCNu5Z0o/ZVRI3WPYedxpdMWKD1Z3G9o+0joP+ZIUqHsamWWYiIgPqXgL9AK6AWjFRg==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.102.1.tgz", + "integrity": "sha512-RkFlFyAC0fQOvBbBqnq0CLmFW5m3JJz9pKbZd5vXPraWAlniKSb1bC/4DF9SlNx0FN1LWG+IU3ISdpzwwTeAGg==", "dependencies": { - "@sentry/core": "7.101.0", - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/core": "7.102.1", + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/replay/node_modules/@sentry/core": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.101.0.tgz", - "integrity": "sha512-dRNrNV5OLGARkOGgxJsVDhA98Pev5G1LVJcud5E83cRg49BCUx2riqEtDP6iIS1nvem6cApkSnLC1kvl/T5/Cw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.102.1.tgz", + "integrity": "sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg==", "dependencies": { - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/replay/node_modules/@sentry/types": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.101.0.tgz", - "integrity": "sha512-YC+ltO/AlbEyJHjCUYQ4is1HcDT2zSMuLkIAcyQmK7fUdlGT4iR5sfENriY9ZopYHgjPdJKfhI8ohScam7zp/A==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.102.1.tgz", + "integrity": "sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA==", "engines": { "node": ">=8" } }, "node_modules/@sentry/replay/node_modules/@sentry/utils": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.101.0.tgz", - "integrity": "sha512-px1NUkCLsD9UKLE4W4DghpyzmAVHgYhskrjRt30ubyUKqlggtHkOXRvS8MjuWowR/i0wF0GuTCbU9StBd7JMrw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.102.1.tgz", + "integrity": "sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg==", "dependencies": { - "@sentry/types": "7.101.0" + "@sentry/types": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/tracing": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.101.0.tgz", - "integrity": "sha512-/TRn3KRuRAamCstw8rRVyL5nlXlL+zf9QNN4IxGFwc7lka8c4d7i5neIWaOrUQvrAlh2gbxbHwt7POIguoed2g==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-7.102.1.tgz", + "integrity": "sha512-9VQEox0R7ouhhUVHtBwlGlXG5beDCM/Uo0BY+G0M1H03aFJsLAwnxPNeWnK3WvPejxf94EgdimKMjDjv9l2Sbg==", "dependencies": { - "@sentry-internal/tracing": "7.101.0" + "@sentry-internal/tracing": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/tracing/node_modules/@sentry-internal/tracing": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.101.0.tgz", - "integrity": "sha512-rp9oOLQs6vMuzvAnAHRRCNu5Z0o/ZVRI3WPYedxpdMWKD1Z3G9o+0joP+ZIUqHsamWWYiIgPqXgL9AK6AWjFRg==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry-internal/tracing/-/tracing-7.102.1.tgz", + "integrity": "sha512-RkFlFyAC0fQOvBbBqnq0CLmFW5m3JJz9pKbZd5vXPraWAlniKSb1bC/4DF9SlNx0FN1LWG+IU3ISdpzwwTeAGg==", "dependencies": { - "@sentry/core": "7.101.0", - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/core": "7.102.1", + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/tracing/node_modules/@sentry/core": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.101.0.tgz", - "integrity": "sha512-dRNrNV5OLGARkOGgxJsVDhA98Pev5G1LVJcud5E83cRg49BCUx2riqEtDP6iIS1nvem6cApkSnLC1kvl/T5/Cw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-7.102.1.tgz", + "integrity": "sha512-QjY+LSP3du3J/C8x/FfEbRxgZgsWd0jfTJ4P7s9f219I1csK4OeBMC3UA1HwEa0pY/9OF6H/egW2CjOcMM5Pdg==", "dependencies": { - "@sentry/types": "7.101.0", - "@sentry/utils": "7.101.0" + "@sentry/types": "7.102.1", + "@sentry/utils": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/tracing/node_modules/@sentry/types": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.101.0.tgz", - "integrity": "sha512-YC+ltO/AlbEyJHjCUYQ4is1HcDT2zSMuLkIAcyQmK7fUdlGT4iR5sfENriY9ZopYHgjPdJKfhI8ohScam7zp/A==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.102.1.tgz", + "integrity": "sha512-htKorf3t/D0XYtM7foTcmG+rM47rDP6XdbvCcX5gBCuCYlzpM1vqCt2rl3FLktZC6TaIpFRJw1TLfx6m+x5jdA==", "engines": { "node": ">=8" } }, "node_modules/@sentry/tracing/node_modules/@sentry/utils": { - "version": "7.101.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.101.0.tgz", - "integrity": "sha512-px1NUkCLsD9UKLE4W4DghpyzmAVHgYhskrjRt30ubyUKqlggtHkOXRvS8MjuWowR/i0wF0GuTCbU9StBd7JMrw==", + "version": "7.102.1", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.102.1.tgz", + "integrity": "sha512-+8WcFjHVV/HROXSAwMuUzveElBFC43EiTG7SNEBNgOUeQzQVTmbUZXyTVgLrUmtoWqvnIxCacoLxtZo1o67kdg==", "dependencies": { - "@sentry/types": "7.101.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/types": { - "version": "7.100.0", - "resolved": "https://registry.npmjs.org/@sentry/types/-/types-7.100.0.tgz", - "integrity": "sha512-c+RHwZwpKeBk7h8sUX4nQcelxBz8ViCojifnbEe3tcn8O15HOLvZqRKgLLOiff3MoErxiv4oxs0sPbEFRm/IvA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@sentry/utils": { - "version": "7.100.0", - "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-7.100.0.tgz", - "integrity": "sha512-LAhZMEGq3C125prZN/ShqeXpRfdfgJkl9RAKjfq8cmMFsF7nsF72dEHZgIwrZ0lgNmtaWAB83AwJcyN83RwOxQ==", - "dev": true, - "dependencies": { - "@sentry/types": "7.100.0" + "@sentry/types": "7.102.1" }, "engines": { "node": ">=8" } }, "node_modules/@sentry/webpack-plugin": { - "version": "2.14.0", - "resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-2.14.0.tgz", - "integrity": "sha512-6lYxabSSkoMqz+zsACamYfu8amLSIiYFj+CQBLvWKSW7N6wJvaZKfFVHj5bGlmU7K0X0eJyQdxdk6VfLg129Jw==", + "version": "2.14.2", + "resolved": "https://registry.npmjs.org/@sentry/webpack-plugin/-/webpack-plugin-2.14.2.tgz", + "integrity": "sha512-BEWF5qerGG/xX0ixEOCYh9gCkc+FHDzXxRMCFkM8yQNGH361ELF578KtuoZxXDy0kWa9QGZxSoP6/HZSnJEF9A==", "dev": true, "dependencies": { - "@sentry/bundler-plugin-core": "2.14.0", + "@sentry/bundler-plugin-core": "2.14.2", "unplugin": "1.0.1", "uuid": "^9.0.0" }, @@ -6681,16 +6616,16 @@ } }, "node_modules/antd": { - "version": "5.14.1", - "resolved": "https://registry.npmjs.org/antd/-/antd-5.14.1.tgz", - "integrity": "sha512-P0Bwt9NKSZqnEJ0QAyAb13ay34FjOKsz+KEp/ts+feYsynhUxF7/Ay6d1jS6ZcNpcs+JWTlLKO59YFZ3tX07wQ==", + "version": "5.14.2", + "resolved": "https://registry.npmjs.org/antd/-/antd-5.14.2.tgz", + "integrity": "sha512-ur0oBI9U7hAeON4ZRs1cAF1suIpTR+uj3YliTZacWkiVxNTZYPaaTdnLuAZDRMT9P2IZ007dCQTqxn5t1Z+Dxw==", "dependencies": { "@ant-design/colors": "^7.0.2", "@ant-design/cssinjs": "^1.18.4", "@ant-design/icons": "^5.3.0", "@ant-design/react-slick": "~1.0.2", "@ctrl/tinycolor": "^3.6.1", - "@rc-component/color-picker": "~1.5.1", + "@rc-component/color-picker": "~1.5.2", "@rc-component/mutate-observer": "^1.1.0", "@rc-component/tour": "~1.12.3", "@rc-component/trigger": "^1.18.3", @@ -6713,7 +6648,7 @@ "rc-motion": "^2.9.0", "rc-notification": "~5.3.0", "rc-pagination": "~4.0.4", - "rc-picker": "~4.1.1", + "rc-picker": "~4.1.4", "rc-progress": "~3.5.1", "rc-rate": "~2.12.0", "rc-resize-observer": "^1.4.0", @@ -6729,7 +6664,7 @@ "rc-tree": "~5.8.5", "rc-tree-select": "~5.17.0", "rc-upload": "~4.5.2", - "rc-util": "^5.38.1", + "rc-util": "^5.38.2", "scroll-into-view-if-needed": "^3.1.0", "throttle-debounce": "^5.0.0" }, @@ -8928,9 +8863,9 @@ "integrity": "sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw==" }, "node_modules/cypress": { - "version": "13.6.4", - "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.4.tgz", - "integrity": "sha512-pYJjCfDYB+hoOoZuhysbbYhEmNW7DEDsqn+ToCLwuVowxUXppIWRr7qk4TVRIU471ksfzyZcH+mkoF0CQUKnpw==", + "version": "13.6.6", + "resolved": "https://registry.npmjs.org/cypress/-/cypress-13.6.6.tgz", + "integrity": "sha512-S+2S9S94611hXimH9a3EAYt81QM913ZVA03pUmGDfLTFa5gyp85NJ8dJGSlEAEmyRsYkioS1TtnWtbv/Fzt11A==", "dev": true, "hasInstallScript": true, "dependencies": { @@ -8941,7 +8876,7 @@ "arch": "^2.2.0", "blob-util": "^2.0.2", "bluebird": "^3.7.2", - "buffer": "^5.6.0", + "buffer": "^5.7.1", "cachedir": "^2.3.0", "chalk": "^4.1.0", "check-more-types": "^2.24.0", @@ -8959,7 +8894,7 @@ "figures": "^3.2.0", "fs-extra": "^9.1.0", "getos": "^3.2.1", - "is-ci": "^3.0.0", + "is-ci": "^3.0.1", "is-installed-globally": "~0.4.0", "lazy-ass": "^1.6.0", "listr2": "^3.8.3", @@ -9005,6 +8940,16 @@ "url": "https://github.com/chalk/supports-color?sponsor=1" } }, + "node_modules/cypress/node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/d3-array": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/d3-array/-/d3-array-1.2.4.tgz", @@ -9708,9 +9653,9 @@ } }, "node_modules/dotenv": { - "version": "16.4.4", - "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.4.tgz", - "integrity": "sha512-XvPXc8XAQThSjAbY6cQ/9PcBXmFoWuw1sQ3b8HqUCR6ziGXjkTi//kB9SWa2UwqlgdAIuRqAa/9hVljzPehbYg==", + "version": "16.4.5", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.4.5.tgz", + "integrity": "sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==", "engines": { "node": ">=12" }, @@ -11003,6 +10948,16 @@ "@types/yauzl": "^2.9.1" } }, + "node_modules/extract-zip/node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/extsprintf": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", @@ -11092,6 +11047,7 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "dev": true, "dependencies": { "pend": "~1.2.0" } @@ -12318,9 +12274,9 @@ } }, "node_modules/i18next": { - "version": "23.8.2", - "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.8.2.tgz", - "integrity": "sha512-Z84zyEangrlERm0ZugVy4bIt485e/H8VecGUZkZWrH7BDePG6jT73QdL9EA1tRTTVVMpry/MgWIP1FjEn0DRXA==", + "version": "23.10.0", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-23.10.0.tgz", + "integrity": "sha512-/TgHOqsa7/9abUKJjdPeydoyDc0oTi/7u9F8lMSj6ufg4cbC1Oj3f/Jja7zj7WRIhEQKB7Q4eN6y68I9RDxxGQ==", "funding": [ { "type": "individual", @@ -14745,9 +14701,9 @@ } }, "node_modules/libphonenumber-js": { - "version": "1.10.55", - "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.55.tgz", - "integrity": "sha512-MrTg2JFLscgmTY6/oT9vopYETlgUls/FU6OaeeamGwk4LFxjIgOUML/ZSZICgR0LPYXaonVJo40lzMvaaTJlQA==" + "version": "1.10.57", + "resolved": "https://registry.npmjs.org/libphonenumber-js/-/libphonenumber-js-1.10.57.tgz", + "integrity": "sha512-OjsEd9y4LgcX+Ig09SbxWqcGESxliDDFNVepFhB9KEsQZTrnk3UdEU+cO0sW1APvLprHstQpS23OQpZ3bwxy6Q==" }, "node_modules/lilconfig": { "version": "2.1.0", @@ -18632,9 +18588,9 @@ } }, "node_modules/rc-picker": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.1.1.tgz", - "integrity": "sha512-H99qaHUepHjHnAqMLiftJEATXRuHJZcUyFoRkyIqUvTHVGnx/uHxFFNm7QIu1valCpfwdsGWQxiWgn9CAxvlvA==", + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/rc-picker/-/rc-picker-4.1.4.tgz", + "integrity": "sha512-sAdgj1kW9wvuoS5p2Zw3pT52iUYxidYaqXVLooaKxTqgYbhe8cG8Ld3b8cgwYfKrIkm/j+qp9nDQlrFPSl16lQ==", "dependencies": { "@babel/runtime": "^7.10.1", "@rc-component/trigger": "^1.5.0", @@ -18959,9 +18915,9 @@ } }, "node_modules/rc-util": { - "version": "5.38.1", - "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.38.1.tgz", - "integrity": "sha512-e4ZMs7q9XqwTuhIK7zBIVFltUtMSjphuPPQXHoHlzRzNdOwUxDejo0Zls5HYaJfRKNURcsS/ceKVULlhjBrxng==", + "version": "5.38.2", + "resolved": "https://registry.npmjs.org/rc-util/-/rc-util-5.38.2.tgz", + "integrity": "sha512-yRGRPKyi84H7NkRSP6FzEIYBdUt4ufdsmXUZ7qM2H5qoByPax70NnGPkfo36N+UKUnUBj2f2Q2eUbwYMuAsIOQ==", "dependencies": { "@babel/runtime": "^7.18.3", "react-is": "^18.2.0" @@ -19092,9 +19048,9 @@ } }, "node_modules/react-big-calendar": { - "version": "1.10.1", - "resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.10.1.tgz", - "integrity": "sha512-VRa6FkjGDqGrO9A9hWqwtEq1x3hL7oz1JG8NCjdV0O7TZ8lCFef/tJNUbidjlWj9FS1PxA7y94sAYDSsGUDa1w==", + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/react-big-calendar/-/react-big-calendar-1.10.3.tgz", + "integrity": "sha512-LmIWlFfGUn8yt4RxcVkGNmjM3GcWynr1bfDwKrrz4KKj517+DH3OGmQzErURN6Zb0OB88HF4oH2dvDHpBQJgIw==", "dependencies": { "@babel/runtime": "^7.20.7", "clsx": "^1.2.1", @@ -19141,9 +19097,9 @@ } }, "node_modules/react-cookie": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.0.2.tgz", - "integrity": "sha512-UnW1rZw1VibRdTvV8Ksr0BKKZoajeUxYLE89sIygDeyQgtz6ik89RHOM+3kib36G9M7HxheORggPoLk5DxAK7Q==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.1.0.tgz", + "integrity": "sha512-n2+Gt07/xxuShXary+SImk1sw5l7a1UguQOQEN55YewEW5LoA0opbR4nbeo8sY6OYwR37iCFJtqJ0AGEywqAtg==", "dependencies": { "@types/hoist-non-react-statics": "^3.3.5", "hoist-non-react-statics": "^3.3.2", @@ -19313,9 +19269,9 @@ } }, "node_modules/react-intersection-observer": { - "version": "9.8.0", - "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.8.0.tgz", - "integrity": "sha512-wXHvMQUsTagh3X0Z6jDtGkIXc3VVCd2tjDRYR9kII3GKrZr0XF0xtpfdamo2n8BSF+zzfeeBVOTjxZWpBp9X0g==", + "version": "9.8.1", + "resolved": "https://registry.npmjs.org/react-intersection-observer/-/react-intersection-observer-9.8.1.tgz", + "integrity": "sha512-QzOFdROX8D8MH3wE3OVKH0f3mLjKTtEN1VX/rkNuECCff+aKky0pIjulDhr3Ewqj5el/L+MhBkM3ef0Tbt+qUQ==", "peerDependencies": { "react": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0", "react-dom": "^15.0.0 || ^16.0.0 || ^17.0.0 || ^18.0.0" @@ -19457,11 +19413,11 @@ } }, "node_modules/react-router": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.0.tgz", - "integrity": "sha512-q2yemJeg6gw/YixRlRnVx6IRJWZD6fonnfZhN1JIOhV2iJCPeRNSH3V1ISwHf+JWcESzLC3BOLD1T07tmO5dmg==", + "version": "6.22.1", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.1.tgz", + "integrity": "sha512-0pdoRGwLtemnJqn1K0XHUbnKiX0S4X8CgvVVmHGOWmofESj31msHo/1YiqcJWK7Wxfq2a4uvvtS01KAQyWK/CQ==", "dependencies": { - "@remix-run/router": "1.15.0" + "@remix-run/router": "1.15.1" }, "engines": { "node": ">=14.0.0" @@ -19471,12 +19427,12 @@ } }, "node_modules/react-router-dom": { - "version": "6.22.0", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.0.tgz", - "integrity": "sha512-z2w+M4tH5wlcLmH3BMMOMdrtrJ9T3oJJNsAlBJbwk+8Syxd5WFJ7J5dxMEW0/GEXD1BBis4uXRrNIz3mORr0ag==", + "version": "6.22.1", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.1.tgz", + "integrity": "sha512-iwMyyyrbL7zkKY7MRjOVRy+TMnS/OPusaFVxM2P11x9dzSzGmLsebkCvYirGq0DWB9K9hOspHYYtDz33gE5Duw==", "dependencies": { - "@remix-run/router": "1.15.0", - "react-router": "6.22.0" + "@remix-run/router": "1.15.1", + "react-router": "6.22.1" }, "engines": { "node": ">=14.0.0" @@ -19706,13 +19662,13 @@ } }, "node_modules/recharts": { - "version": "2.12.0", - "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.0.tgz", - "integrity": "sha512-rVNcdNQ5b7+40Ue7mcEKZJyEv+3SUk2bDEVvOyXPDXXVE7TU3lrvnJUgAvO36hSzhRP2DnAamKXvHLFIFOU0Ww==", + "version": "2.12.1", + "resolved": "https://registry.npmjs.org/recharts/-/recharts-2.12.1.tgz", + "integrity": "sha512-35vUCEBPf+pM+iVgSgVTn86faKya5pc4JO6cYJL63qOK2zDEyzDn20Tdj+CDI/3z+VcpKyQ8ZBQ9OiQ+vuAbjg==", "dependencies": { "clsx": "^2.0.0", "eventemitter3": "^4.0.1", - "lodash": "^4.17.19", + "lodash": "^4.17.21", "react-is": "^16.10.2", "react-smooth": "^4.0.0", "recharts-scale": "^0.4.4", @@ -20394,9 +20350,9 @@ "integrity": "sha512-ZRwKbh/eQ6w9vmTjkuG0Ioi3HBwPFce0O+v//ve+aOq1oeCy7jMV2qzzAlpsNuqpqCBjjriM1lbtZbF/Q8jVyA==" }, "node_modules/sass": { - "version": "1.70.0", - "resolved": "https://registry.npmjs.org/sass/-/sass-1.70.0.tgz", - "integrity": "sha512-uUxNQ3zAHeAx5nRFskBnrWzDUJrrvpCPD5FNAoRvTi0WwremlheES3tg+56PaVtCs5QDRX5CBLxxKMDJMEa1WQ==", + "version": "1.71.1", + "resolved": "https://registry.npmjs.org/sass/-/sass-1.71.1.tgz", + "integrity": "sha512-wovtnV2PxzteLlfNzbgm1tFXPLoZILYAMJtvoXXkD7/+1uP41eKkIt1ypWq5/q2uT94qHjXehEYfmjKOvjL9sg==", "dependencies": { "chokidar": ">=3.0.0 <4.0.0", "immutable": "^4.0.0", @@ -24206,12 +24162,15 @@ } }, "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.1.0.tgz", + "integrity": "sha512-zbff6SaAPyewVextulqeBjJm+1ZhS69vSN7cRpqVD7jMNSE9oXEdQ1SGF+ydfB+gKE2a3GiWfXf/pnwVZ1/tOA==", "dependencies": { "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/yn": { diff --git a/client/package.json b/client/package.json index 532554bd6..e13d024b6 100644 --- a/client/package.json +++ b/client/package.json @@ -11,13 +11,13 @@ "@craco/craco": "^7.1.0", "@fingerprintjs/fingerprintjs": "^4.2.2", "@jsreport/browser-client": "^3.1.0", - "@reduxjs/toolkit": "^2.2.0", + "@reduxjs/toolkit": "^2.2.1", "@sentry/cli": "^2.28.6", - "@sentry/react": "^7.101.0", - "@sentry/tracing": "^7.101.0", + "@sentry/react": "^7.102.1", + "@sentry/tracing": "^7.102.1", "@splitsoftware/splitio-react": "^1.11.0", "@tanem/react-nprogress": "^5.0.51", - "antd": "^5.14.1", + "antd": "^5.14.2", "apollo-link-logger": "^2.0.1", "apollo-link-sentry": "^3.3.0", "axios": "^1.6.7", @@ -25,17 +25,17 @@ "dayjs": "^1.11.10", "dayjs-business-days2": "^1.2.2", "dinero.js": "^1.9.1", - "dotenv": "^16.4.4", + "dotenv": "^16.4.5", "enquire-js": "^0.2.1", "env-cmd": "^10.1.0", "exifr": "^7.1.3", "firebase": "^10.8.0", "graphql": "^16.6.0", - "i18next": "^23.8.2", + "i18next": "^23.10.0", "i18next-browser-languagedetector": "^7.0.2", "jsoneditor": "^10.0.1", "jsreport-browser-client-dist": "^1.3.0", - "libphonenumber-js": "^1.10.55", + "libphonenumber-js": "^1.10.57", "logrocket": "^8.0.1", "markerjs2": "^2.32.0", "normalize-url": "^8.0.0", @@ -46,9 +46,9 @@ "rc-queue-anim": "^2.0.0", "rc-scroll-anim": "^2.7.6", "react": "^18.2.0", - "react-big-calendar": "^1.10.1", + "react-big-calendar": "^1.10.3", "react-color": "^2.19.3", - "react-cookie": "^7.0.2", + "react-cookie": "^7.1.0", "react-dom": "^18.2.0", "react-drag-listview": "^2.0.0", "react-grid-gallery": "^1.0.0", @@ -56,23 +56,23 @@ "react-i18next": "^14.0.5", "react-icons": "^5.0.1", "react-image-lightbox": "^5.1.4", - "react-intersection-observer": "^9.8.0", + "react-intersection-observer": "^9.8.1", "react-markdown": "^9.0.1", "react-number-format": "^5.1.4", "react-redux": "^9.1.0", "react-resizable": "^3.0.5", - "react-router-dom": "^6.22.0", + "react-router-dom": "^6.22.1", "react-scripts": "^5.0.1", "react-sticky": "^6.0.3", "react-sublime-video": "^0.2.5", "react-virtualized": "^9.22.5", - "recharts": "^2.12.0", + "recharts": "^2.12.1", "redux": "^5.0.1", "redux-persist": "^6.0.0", "redux-saga": "^1.3.0", "redux-state-sync": "^3.1.4", "reselect": "^5.1.0", - "sass": "^1.70.0", + "sass": "^1.71.1", "socket.io-client": "^4.7.4", "styled-components": "^6.1.8", "subscriptions-transport-ws": "^0.11.0", @@ -84,7 +84,7 @@ "workbox-precaching": "^7.0.0", "workbox-routing": "^7.0.0", "workbox-strategies": "^7.0.0", - "yauzl": "^2.10.0" + "yauzl": "^3.1.0" }, "scripts": { "analyze": "source-map-explorer 'build/static/js/*.js'", @@ -123,9 +123,9 @@ }, "devDependencies": { "@babel/plugin-proposal-private-property-in-object": "^7.21.11", - "@sentry/webpack-plugin": "^2.14.0", + "@sentry/webpack-plugin": "^2.14.2", "@testing-library/cypress": "^10.0.1", - "cypress": "^13.6.4", + "cypress": "^13.6.6", "eslint-plugin-cypress": "^2.15.1", "react-error-overlay": "6.0.11", "redux-logger": "^3.0.6", diff --git a/client/src/components/contract-form/contract-form.component.jsx b/client/src/components/contract-form/contract-form.component.jsx index 8dbf991cd..ae983a6c6 100644 --- a/client/src/components/contract-form/contract-form.component.jsx +++ b/client/src/components/contract-form/contract-form.component.jsx @@ -66,7 +66,30 @@ export default function ContractFormComponent({ )} - + {create && ( + p.scheduledreturn !== c.scheduledreturn} + > + {() => { + const insuranceOver = + selectedCar && + selectedCar.insuranceexpires && + dayjs(selectedCar.insuranceexpires) + .endOf("day") + .isBefore(dayjs(form.getFieldValue("scheduledreturn"))); + if (insuranceOver) + return ( + + + + {t("contracts.labels.insuranceexpired")} + + + ); + return <>; + }} + + )} {() => { const mileageOver = - selectedCar && - selectedCar.nextservicekm <= form.getFieldValue("kmstart"); - + selectedCar && selectedCar.nextservicekm + ? selectedCar.nextservicekm <= form.getFieldValue("kmstart") + : false; const dueForService = selectedCar && selectedCar.nextservicedate && - dayjs(selectedCar.nextservicedate).isBefore( + dayjs(selectedCar.nextservicedate) + .endOf("day") + .isSameOrBefore( dayjs(form.getFieldValue("scheduledreturn")) - ); - + ); if (mileageOver || dueForService) return ( diff --git a/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx b/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx index 97e0a7979..e7cb3dc00 100644 --- a/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx +++ b/client/src/components/courtesy-cars-list/courtesy-cars-list.component.jsx @@ -64,18 +64,29 @@ export default function CourtesyCarsList({loading, courtesycars, refetch}) { sortOrder: state.sortedInfo.columnKey === "status" && state.sortedInfo.order, render: (text, record) => { - const {nextservicedate, nextservicekm, mileage} = record; + const {nextservicedate, nextservicekm, mileage, insuranceexpires} = + record; const mileageOver = nextservicekm ? nextservicekm <= mileage : false; const dueForService = nextservicedate && dayjs(nextservicedate).endOf('day').isSameOrBefore(dayjs()); + const insuranceOver = + insuranceexpires && + dayjs(insuranceexpires).endOf("day").isBefore(dayjs()); return ( {t(record.status)} - {(mileageOver || dueForService) && ( - + {(mileageOver || dueForService || insuranceOver) && ( + )} diff --git a/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx b/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx index e9977d934..5277ca0ec 100644 --- a/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx +++ b/client/src/components/dashboard-components/scheduled-in-today/scheduled-in-today.component.jsx @@ -1,221 +1,469 @@ -import {BranchesOutlined, ExclamationCircleFilled, PauseCircleOutlined,} from "@ant-design/icons"; -import {Card, Space, Table, Tooltip} from "antd"; -import dayjs from "../../../utils/day"; -import React, {useState} from "react"; -import {useTranslation} from "react-i18next"; -import {Link} from "react-router-dom"; +import { + BranchesOutlined, + ExclamationCircleFilled, + PauseCircleOutlined, +} from "@ant-design/icons"; +import { Card, Space, Switch, Table, Tooltip, Typography } from "antd"; +import moment from "moment"; +import React, { useState } from "react"; +import { useTranslation } from "react-i18next"; +import { Link } from "react-router-dom"; +import { TimeFormatter } from "../../../utils/DateFormatter"; +import { onlyUnique } from "../../../utils/arrayHelper"; +import { alphaSort, dateSort } from "../../../utils/sorters"; +import useLocalStorage from "../../../utils/useLocalStorage"; import ChatOpenButton from "../../chat-open-button/chat-open-button.component"; -import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component"; +import OwnerNameDisplay, { + OwnerNameDisplayFunction, +} from "../../owner-name-display/owner-name-display.component"; import DashboardRefreshRequired from "../refresh-required.component"; -import {pageLimit} from "../../../utils/config"; -export default function DashboardScheduledInToday({data, ...cardProps}) { - const {t} = useTranslation(); - const [state, setState] = useState({ - sortedInfo: {}, - }); - if (!data) return null; - if (!data.scheduled_in_today) - return ; +export default function DashboardScheduledInToday({ data, ...cardProps }) { + const { t } = useTranslation(); + const [state, setState] = useState({ + sortedInfo: {}, + filteredInfo: {}, + }); - const appt = []; // Flatten Data - data.scheduled_in_today.forEach((item) => { - if (item.job) { - var i = { - canceled: item.canceled, - id: item.id, - alt_transport: item.job.alt_transport, - clm_no: item.job.clm_no, - jobid: item.job.jobid, - ins_co_nm: item.job.ins_co_nm, - iouparent: item.job.iouparent, - ownerid: item.job.ownerid, - ownr_co_nm: item.job.ownr_co_nm, - ownr_ea: item.job.ownr_ea, - ownr_fn: item.job.ownr_fn, - ownr_ln: item.job.ownr_ln, - ownr_ph1: item.job.ownr_ph1, - ownr_ph2: item.job.ownr_ph2, - production_vars: item.job.production_vars, - ro_number: item.job.ro_number, - suspended: item.job.suspended, - v_make_desc: item.job.v_make_desc, - v_model_desc: item.job.v_model_desc, - v_model_yr: item.job.v_model_yr, - v_vin: item.job.v_vin, - vehicleid: item.job.vehicleid, - note: item.note, - start: dayjs(item.start).format("hh:mm a"), - title: item.title, - }; - appt.push(i); - } - }); - appt.sort(function (a, b) { - return new dayjs(a.start) - new dayjs(b.start); - }); + const [isTvModeScheduledIn, setIsTvModeScheduledIn] = useLocalStorage( + "isTvModeScheduledIn", + false + ); - const columns = [ - { - title: t("jobs.fields.ro_number"), - dataIndex: "ro_number", - key: "ro_number", - render: (text, record) => ( - e.stopPropagation()} - > - - {record.ro_number || t("general.labels.na")} - {record.production_vars && record.production_vars.alert ? ( - - ) : null} - {record.suspended && ( - - )} - {record.iouparent && ( - - - - )} - - - ), - }, - { - title: t("jobs.fields.owner"), - dataIndex: "owner", - key: "owner", - ellipsis: true, - responsive: ["md"], - render: (text, record) => { - return record.ownerid ? ( - e.stopPropagation()} - > - - - ) : ( - - - - ); - }, - }, - { - title: t("jobs.fields.ownr_ph1"), - dataIndex: "ownr_ph1", - key: "ownr_ph1", - ellipsis: true, - responsive: ["md"], - render: (text, record) => ( - - ), - }, - { - title: t("jobs.fields.ownr_ph2"), - dataIndex: "ownr_ph2", - key: "ownr_ph2", - ellipsis: true, - responsive: ["md"], - render: (text, record) => ( - - ), - }, - { - title: t("jobs.fields.ownr_ea"), - dataIndex: "ownr_ea", - key: "ownr_ea", - ellipsis: true, - responsive: ["md"], - render: (text, record) => ( - - ), - }, - { - title: t("jobs.fields.vehicle"), - dataIndex: "vehicle", - key: "vehicle", - ellipsis: true, - render: (text, record) => { - return record.vehicleid ? ( - e.stopPropagation()} - > - {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ - record.v_model_desc || "" - }`} - - ) : ( - {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ - record.v_model_desc || "" - }`} - ); - }, - }, - { - title: t("jobs.fields.ins_co_nm"), - dataIndex: "ins_co_nm", - key: "ins_co_nm", - ellipsis: true, - responsive: ["md"], - }, - { - title: t("appointments.fields.time"), - dataIndex: "start", - key: "start", - ellipsis: true, - responsive: ["md"], - }, - { - title: t("appointments.fields.alt_transport"), - dataIndex: "alt_transport", - key: "alt_transport", - ellipsis: true, - responsive: ["md"], - }, - ]; + if (!data) return null; + if (!data.scheduled_in_today) + return ; - const handleTableChange = (sorter) => { - setState({...state, sortedInfo: sorter}); - }; + const appt = []; // Flatten Data + data.scheduled_in_today.forEach((item) => { + if (item.job) { + var i = { + canceled: item.canceled, + id: item.id, + alt_transport: item.job.alt_transport, + clm_no: item.job.clm_no, + jobid: item.job.jobid, + joblines_body: item.job.joblines + .filter((l) => l.mod_lbr_ty !== "LAR") + .reduce((acc, val) => acc + val.mod_lb_hrs, 0), + joblines_ref: item.job.joblines + .filter((l) => l.mod_lbr_ty === "LAR") + .reduce((acc, val) => acc + val.mod_lb_hrs, 0), + ins_co_nm: item.job.ins_co_nm, + iouparent: item.job.iouparent, + ownerid: item.job.ownerid, + ownr_co_nm: item.job.ownr_co_nm, + ownr_ea: item.job.ownr_ea, + ownr_fn: item.job.ownr_fn, + ownr_ln: item.job.ownr_ln, + ownr_ph1: item.job.ownr_ph1, + ownr_ph2: item.job.ownr_ph2, + production_vars: item.job.production_vars, + ro_number: item.job.ro_number, + suspended: item.job.suspended, + v_make_desc: item.job.v_make_desc, + v_model_desc: item.job.v_model_desc, + v_model_yr: item.job.v_model_yr, + v_vin: item.job.v_vin, + vehicleid: item.job.vehicleid, + note: item.note, + start: item.start, + title: item.title, + }; + appt.push(i); + } + }); + appt.sort(function (a, b) { + return new moment(a.start) - new moment(b.start); + }); - return ( - dateSort(a.start, b.start), + sortOrder: + state.sortedInfo.columnKey === "start" && state.sortedInfo.order, + render: (text, record) => ( + + {record.start} + + ), + }, + { + title: t("jobs.fields.ro_number"), + dataIndex: "ro_number", + key: "ro_number", + sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), + sortOrder: + state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, + render: (text, record) => ( + e.stopPropagation()} > -
- - - - ); + + + {record.ro_number || t("general.labels.na")} + {record.production_vars && record.production_vars.alert ? ( + + ) : null} + {record.suspended && ( + + )} + {record.iouparent && ( + + + + )} + + + + ), + }, + { + title: t("jobs.fields.owner"), + dataIndex: "owner", + key: "owner", + ellipsis: true, + sorter: (a, b) => + alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)), + sortOrder: + state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, + render: (text, record) => { + return record.ownerid ? ( + e.stopPropagation()} + > + + + + + ) : ( + + + + ); + }, + }, + { + title: t("jobs.fields.vehicle"), + dataIndex: "vehicle", + key: "vehicle", + ellipsis: true, + sorter: (a, b) => + alphaSort( + `${a.v_model_yr || ""} ${a.v_make_desc || ""} ${ + a.v_model_desc || "" + }`, + `${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}` + ), + sortOrder: + state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order, + render: (text, record) => { + return record.vehicleid ? ( + e.stopPropagation()} + > + + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + + + ) : ( + {`${ + record.v_model_yr || "" + } ${record.v_make_desc || ""} ${record.v_model_desc || ""}`} + ); + }, + }, + { + title: t("appointments.fields.alt_transport"), + dataIndex: "alt_transport", + key: "alt_transport", + ellipsis: true, + sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport), + sortOrder: + state.sortedInfo.columnKey === "alt_transport" && + state.sortedInfo.order, + filters: + (appt && + appt + .map((j) => j.alt_transport) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Alt. Transport", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [], + onFilter: (value, record) => value.includes(record.alt_transport), + render: (text, record) => ( + + {record.alt_transport} + + ), + }, + { + title: t("jobs.fields.lab"), + dataIndex: "joblines_body", + key: "joblines_body", + sorter: (a, b) => a.joblines_body - b.joblines_body, + sortOrder: + state.sortedInfo.columnKey === "joblines_body" && + state.sortedInfo.order, + align: "right", + render: (text, record) => ( + + {record.joblines_body.toFixed(1)} + + ), + }, + { + title: t("jobs.fields.lar"), + dataIndex: "joblines_ref", + key: "joblines_ref", + sorter: (a, b) => a.joblines_ref - b.joblines_ref, + sortOrder: + state.sortedInfo.columnKey === "joblines_ref" && state.sortedInfo.order, + align: "right", + render: (text, record) => ( + + {record.joblines_ref.toFixed(1)} + + ), + }, + ]; + + const columns = [ + { + title: t("appointments.fields.time"), + dataIndex: "start", + key: "start", + ellipsis: true, + sorter: (a, b) => dateSort(a.start, b.start), + sortOrder: + state.sortedInfo.columnKey === "start" && state.sortedInfo.order, + render: (text, record) => {record.start}, + }, + { + title: t("jobs.fields.ro_number"), + dataIndex: "ro_number", + key: "ro_number", + sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), + sortOrder: + state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, + render: (text, record) => ( + e.stopPropagation()} + > + + {record.ro_number || t("general.labels.na")} + {record.production_vars && record.production_vars.alert ? ( + + ) : null} + {record.suspended && ( + + )} + {record.iouparent && ( + + + + )} + + + ), + }, + { + title: t("jobs.fields.owner"), + dataIndex: "owner", + key: "owner", + ellipsis: true, + sorter: (a, b) => + alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)), + sortOrder: + state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, + render: (text, record) => { + return record.ownerid ? ( + e.stopPropagation()} + > + + + ) : ( + + + + ); + }, + }, + { + title: t("dashboard.labels.phone"), + dataIndex: "ownr_ph", + key: "ownr_ph", + ellipsis: true, + responsive: ["md"], + render: (text, record) => ( + + + + + ), + }, + { + title: t("jobs.fields.ownr_ea"), + dataIndex: "ownr_ea", + key: "ownr_ea", + ellipsis: true, + responsive: ["md"], + render: (text, record) => ( + {record.ownr_ea} + ), + }, + { + title: t("jobs.fields.vehicle"), + dataIndex: "vehicle", + key: "vehicle", + ellipsis: true, + sorter: (a, b) => + alphaSort( + `${a.v_model_yr || ""} ${a.v_make_desc || ""} ${ + a.v_model_desc || "" + }`, + `${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}` + ), + sortOrder: + state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order, + render: (text, record) => { + return record.vehicleid ? ( + e.stopPropagation()} + > + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + + ) : ( + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + ); + }, + }, + { + title: t("jobs.fields.ins_co_nm"), + dataIndex: "ins_co_nm", + key: "ins_co_nm", + ellipsis: true, + responsive: ["md"], + sorter: (a, b) => alphaSort(a.ins_co_nm, b.ins_co_nm), + sortOrder: + state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order, + filters: + (appt && + appt + .map((j) => j.ins_co_nm) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Ins. Co.*", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [], + onFilter: (value, record) => value.includes(record.ins_co_nm), + }, + { + title: t("appointments.fields.alt_transport"), + dataIndex: "alt_transport", + key: "alt_transport", + ellipsis: true, + sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport), + sortOrder: + state.sortedInfo.columnKey === "alt_transport" && + state.sortedInfo.order, + filters: + (appt && + appt + .map((j) => j.alt_transport) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Alt. Transport", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [], + onFilter: (value, record) => value.includes(record.alt_transport), + }, + ]; + + const handleTableChange = (pagination, filters, sorter) => { + setState({ ...state, filteredInfo: filters, sortedInfo: sorter }); + }; + return ( + + {t("general.labels.tvmode")} + setIsTvModeScheduledIn(!isTvModeScheduledIn)} + defaultChecked={isTvModeScheduledIn} + /> + + } + {...cardProps} + > +
+
+ + + ); } export const DashboardScheduledInTodayGql = ` - scheduled_in_today: appointments(where: {start: {_gte: "${dayjs() + scheduled_in_today: appointments(where: {start: {_gte: "${moment() .startOf("day") - .toISOString()}", _lte: "${dayjs() - .endOf("day") - .toISOString()}"}, canceled: {_eq: false}, block: {_neq: true}}) { + .toISOString()}", _lte: "${moment() + .endOf("day") + .toISOString()}"}, canceled: {_eq: false}, block: {_neq: true}}) { canceled id job { alt_transport clm_no jobid: id + joblines(where: {removed: {_eq: false}}) { + mod_lb_hrs + mod_lbr_ty + } ins_co_nm iouparent ownerid diff --git a/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx b/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx index 4d155d12a..1639c8fe9 100644 --- a/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx +++ b/client/src/components/dashboard-components/scheduled-out-today/scheduled-out-today.component.jsx @@ -1,37 +1,273 @@ import {BranchesOutlined, ExclamationCircleFilled, PauseCircleOutlined,} from "@ant-design/icons"; -import {Card, Space, Table, Tooltip} from "antd"; +import {Card, Space, Switch, Table, Tooltip, Typography} from "antd"; import dayjs from "../../../utils/day"; import React, {useState} from "react"; import {useTranslation} from "react-i18next"; import {Link} from "react-router-dom"; +import {TimeFormatter} from "../../../utils/DateFormatter"; +import {onlyUnique} from "../../../utils/arrayHelper"; +import {alphaSort, dateSort} from "../../../utils/sorters"; +import useLocalStorage from "../../../utils/useLocalStorage"; import ChatOpenButton from "../../chat-open-button/chat-open-button.component"; -import OwnerNameDisplay from "../../owner-name-display/owner-name-display.component"; +import OwnerNameDisplay, {OwnerNameDisplayFunction,} from "../../owner-name-display/owner-name-display.component"; import DashboardRefreshRequired from "../refresh-required.component"; -import {pageLimit} from "../../../utils/config"; export default function DashboardScheduledOutToday({data, ...cardProps}) { const {t} = useTranslation(); const [state, setState] = useState({ sortedInfo: {}, - }); + filteredInfo: {},}); + const [isTvModeScheduledOut, setIsTvModeScheduledOut] = useLocalStorage( + "isTvModeScheduledOut", + false + ); if (!data) return null; if (!data.scheduled_out_today) return ; - const filteredScheduledOutToday = data.scheduled_out_today.map((item) => { + const scheduledOutToday = data.scheduled_out_today.map((item) => { + const joblines_body = item.joblines + ? item.joblines + .filter((l) => l.mod_lbr_ty !== "LAR") + .reduce((acc, val) => acc + val.mod_lb_hrs, 0) + : 0; + const joblines_ref = item.joblines + ? item.joblines + .filter((l) => l.mod_lbr_ty === "LAR") + .reduce((acc, val) => acc + val.mod_lb_hrs, 0) + : 0; return { ...item, - scheduled_completion: dayjs(item.scheduled_completion).format("hh:mm a"), - timestamp: dayjs(item.scheduled_completion).valueOf(), - } - }).sort((a, b) => a.timestamp - b.timestamp); + joblines_body, + joblines_ref, + }; + }); - const columns = [ + console.log('Scheduled Out Today') + console.dir(scheduledOutToday); + + const tvFontSize = 18; + const tvFontWeight = "bold"; + + const tvColumns = [ + { + title: t("jobs.fields.scheduled_completion"), + dataIndex: "scheduled_completion", + key: "scheduled_completion", + ellipsis: true, + sorter: (a, b) => + dateSort(a.scheduled_completion, b.scheduled_completion), + sortOrder: + state.sortedInfo.columnKey === "scheduled_completion" && + state.sortedInfo.order, + render: (text, record) => ( + + {record.scheduled_completion} + + ), + }, { title: t("jobs.fields.ro_number"), dataIndex: "ro_number", key: "ro_number", + sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), + sortOrder: + state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, render: (text, record) => ( + e.stopPropagation()} + > + + + {record.ro_number || t("general.labels.na")} + {record.production_vars && record.production_vars.alert ? ( + + ) : null} + {record.suspended && ( + + )} + {record.iouparent && ( + + + + )} + + + + ), + }, + { + title: t("jobs.fields.owner"), + dataIndex: "owner", + key: "owner", + ellipsis: true, + sorter: (a, b) => + alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)), + sortOrder: + state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, + render: (text, record) => { + console.log('Render record out today'); + console.dir(record); + return record.ownerid ? ( + e.stopPropagation()} + > + + + + + ) : ( + + + + ); + }, + }, + { + title: t("jobs.fields.vehicle"), + dataIndex: "vehicle", + key: "vehicle", + ellipsis: true, + sorter: (a, b) => + alphaSort( + `${a.v_model_yr || ""} ${a.v_make_desc || ""} ${ + a.v_model_desc || "" + }`, + `${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}` + ), + sortOrder: + state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order, + render: (text, record) => { + return record.vehicleid ? ( + e.stopPropagation()} + > + + {`${record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + + + ) : ( + {`${ + record.v_model_yr || ""} ${record.v_make_desc || ""} ${ + record.v_model_desc || "" + }`} + ); + }, + }, + { + title: t("appointments.fields.alt_transport"), + dataIndex: "alt_transport", + key: "alt_transport", + ellipsis: true, + sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport), + sortOrder: + state.sortedInfo.columnKey === "alt_transport" && + state.sortedInfo.order, + filters: + (scheduledOutToday && + scheduledOutToday + .map((j) => j.alt_transport) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Alt. Transport*", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [],onFilter: (value, record) => value.includes(record.alt_transport), + render: (text, record) => ( + + {record.alt_transport} + + ), + }, + { + title: t("jobs.fields.status"), + dataIndex: "status", + key: "status", + ellipsis: true, + sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport), + sortOrder: + state.sortedInfo.columnKey === "status" && state.sortedInfo.order, + filters: + (scheduledOutToday && + scheduledOutToday + .map((j) => j.status) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Status*", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [], + onFilter: (value, record) => value.includes(record.status),render: (text, record) => ( + + {record.status} + + ), + }, + { + title: t("jobs.fields.lab"), + dataIndex: "joblines_body", + key: "joblines_body", + sorter: (a, b) => a.joblines_body - b.joblines_body, + sortOrder: + state.sortedInfo.columnKey === "joblines_body" && + state.sortedInfo.order, + align: "right", + render: (text, record) => ( + + {record.joblines_body.toFixed(1)} + + ), + }, + { + title: t("jobs.fields.lar"), + dataIndex: "joblines_ref", + key: "joblines_ref", + sorter: (a, b) => a.joblines_ref - b.joblines_ref, + sortOrder: + state.sortedInfo.columnKey === "joblines_ref" && state.sortedInfo.order, + align: "right", + render: (text, record) => ( + + {record.joblines_ref.toFixed(1)} + + ), + }, + ]; + + const columns = [ + { + title: t("jobs.fields.scheduled_completion"), + dataIndex: "scheduled_completion", + key: "scheduled_completion", + ellipsis: true, + sorter: (a, b) => + dateSort(a.scheduled_completion, b.scheduled_completion), + sortOrder: + state.sortedInfo.columnKey === "scheduled_completion" && + state.sortedInfo.order, + render: (text, record) => ( + {record.scheduled_completion} + ), + }, + { + title: t("jobs.fields.ro_number"), + dataIndex: "ro_number", + key: "ro_number", + sorter: (a, b) => alphaSort(a.ro_number, b.ro_number), + sortOrder: + state.sortedInfo.columnKey === "ro_number" && state.sortedInfo.order, render: (text, record) => ( e.stopPropagation()} @@ -58,7 +294,10 @@ export default function DashboardScheduledOutToday({data, ...cardProps}) { dataIndex: "owner", key: "owner", ellipsis: true, - responsive: ["md"], + sorter: (a, b) => + alphaSort(OwnerNameDisplayFunction(a), OwnerNameDisplayFunction(b)), + sortOrder: + state.sortedInfo.columnKey === "owner" && state.sortedInfo.order, render: (text, record) => { return record.ownerid ? ( ( + render: (text, record) => ( - ), - }, - { - title: t("jobs.fields.ownr_ph2"), - dataIndex: "ownr_ph2", - key: "ownr_ph2", - ellipsis: true, - responsive: ["md"], - render: (text, record) => ( + - ), + ), }, { title: t("jobs.fields.ownr_ea"), @@ -101,7 +332,7 @@ export default function DashboardScheduledOutToday({data, ...cardProps}) { ellipsis: true, responsive: ["md"], render: (text, record) => ( - + {record.ownr_ea} ), }, { @@ -109,7 +340,15 @@ export default function DashboardScheduledOutToday({data, ...cardProps}) { dataIndex: "vehicle", key: "vehicle", ellipsis: true, - render: (text, record) => { + sorter: (a, b) => + alphaSort( + `${a.v_model_yr || ""} ${a.v_make_desc || ""} ${ + a.v_model_desc || "" + }`, + `${b.v_model_yr || ""} ${b.v_make_desc || ""} ${b.v_model_desc || ""}` + ), + sortOrder: + state.sortedInfo.columnKey === "vehicle" && state.sortedInfo.order, render: (text, record) => { return record.vehicleid ? ( alphaSort(a.ins_co_nm, b.ins_co_nm), + sortOrder: + state.sortedInfo.columnKey === "ins_co_nm" && state.sortedInfo.order, + filters: + (scheduledOutToday && + scheduledOutToday + .map((j) => j.ins_co_nm) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Ins. Co.*", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [], + onFilter: (value, record) => value.includes(record.ins_co_nm), }, { title: t("appointments.fields.alt_transport"), dataIndex: "alt_transport", key: "alt_transport", ellipsis: true, - responsive: ["md"], - }, + sorter: (a, b) => alphaSort(a.alt_transport, b.alt_transport), + sortOrder: + state.sortedInfo.columnKey === "alt_transport" && + state.sortedInfo.order, + filters: + (scheduledOutToday && + scheduledOutToday + .map((j) => j.alt_transport) + .filter(onlyUnique) + .map((s) => { + return { + text: s || "No Alt. Transport*", + value: [s], + }; + }) + .sort((a, b) => alphaSort(a.text, b.text))) || + [], + onFilter: (value, record) => value.includes(record.alt_transport),}, ]; - const handleTableChange = (sorter) => { - setState({...state, sortedInfo: sorter}); + const handleTableChange = (pagination, filters, sorter) => { + setState({...state, filteredInfo: filters, sortedInfo: sorter}); }; return ( + {t("general.labels.tvmode")} + setIsTvModeScheduledOut(!isTvModeScheduledOut)} + defaultChecked={isTvModeScheduledOut} + /> + + }{...cardProps} >
@@ -185,6 +459,10 @@ export const DashboardScheduledOutTodayGql = ` alt_transport clm_no jobid: id + joblines(where: {removed: {_eq: false}}) { + mod_lb_hrs + mod_lbr_ty + } ins_co_nm iouparent ownerid @@ -197,6 +475,7 @@ export const DashboardScheduledOutTodayGql = ` production_vars ro_number scheduled_completion + status suspended v_make_desc v_model_desc diff --git a/client/src/components/dashboard-grid/dashboard-grid.component.jsx b/client/src/components/dashboard-grid/dashboard-grid.component.jsx index fbeb7655b..3b21370ca 100644 --- a/client/src/components/dashboard-grid/dashboard-grid.component.jsx +++ b/client/src/components/dashboard-grid/dashboard-grid.component.jsx @@ -270,26 +270,22 @@ const componentList = { h: 2, }, ScheduleInToday: { - label: i18next.t("dashboard.titles.scheduledintoday", { - date: dayjs().startOf("day").format("MM/DD/YYYY"), - }), + label: i18next.t("dashboard.titles.scheduledintoday"), component: DashboardScheduledInToday, gqlFragment: DashboardScheduledInTodayGql, - minW: 10, + minW: 6, minH: 2, w: 10, - h: 2, + h: 3, }, ScheduleOutToday: { - label: i18next.t("dashboard.titles.scheduledouttoday", { - date: dayjs().startOf("day").format("MM/DD/YYYY"), - }), + label: i18next.t("dashboard.titles.scheduledouttoday"), component: DashboardScheduledOutToday, gqlFragment: DashboardScheduledOutTodayGql, - minW: 10, + minW: 6, minH: 2, w: 10, - h: 2, + h: 3, }, }; @@ -301,8 +297,7 @@ const createDashboardQuery = (state) => { .map((item, index) => componentList[item.i].gqlFragment || "") .join(""); return gql` - query QUERY_DASHBOARD_DETAILS { - ${componentBasedAdditions || ""} + query QUERY_DASHBOARD_DETAILS { ${componentBasedAdditions || ""} monthly_sales: jobs(where: {_and: [ { voided: {_eq: false}}, {date_invoiced: {_gte: "${dayjs() @@ -312,7 +307,7 @@ const createDashboardQuery = (state) => { .endOf("month") .endOf("day") .toISOString()}"}}]}) { - id + id ro_number date_invoiced job_totals @@ -376,6 +371,5 @@ const createDashboardQuery = (state) => { } } } - } - `; + }`; }; diff --git a/client/src/components/dashboard-grid/dashboard-grid.styles.scss b/client/src/components/dashboard-grid/dashboard-grid.styles.scss index ceedfbca9..4bd16a5e3 100644 --- a/client/src/components/dashboard-grid/dashboard-grid.styles.scss +++ b/client/src/components/dashboard-grid/dashboard-grid.styles.scss @@ -145,7 +145,7 @@ width: 100%; .ant-card-body { - height: 80%; + height: calc(100% - 2rem); width: 100%; // // background-color: red; // height: 90%; diff --git a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx index 0415f19b8..abab27521 100644 --- a/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx +++ b/client/src/components/jobs-detail-header/jobs-detail-header.component.jsx @@ -119,11 +119,14 @@ export function JobsDetailHeader({job, bodyshop, disabled}) { {job?.cccontracts?.length > 0 && ( - {job.cccontracts.map((c) => ( + {job.cccontracts.map((c, index) => ( + {`${c.agreementnumber} - ${c.courtesycar.fleetnumber} ${c.courtesycar.year} ${c.courtesycar.make} ${c.courtesycar.model}`} + >{`${c.agreementnumber} - ${c.courtesycar.fleetnumber} ${c.courtesycar.year} ${c.courtesycar.make} ${c.courtesycar.model}`}{index !== job.cccontracts.length - 1 ? "," : null} + + ))} )} diff --git a/client/src/components/report-center-modal/report-center-modal-filters-sorters-component.jsx b/client/src/components/report-center-modal/report-center-modal-filters-sorters-component.jsx index 656e9c553..1fc7a4700 100644 --- a/client/src/components/report-center-modal/report-center-modal-filters-sorters-component.jsx +++ b/client/src/components/report-center-modal/report-center-modal-filters-sorters-component.jsx @@ -1,52 +1,356 @@ import {Button, Card, Checkbox, Col, Form, Input, InputNumber, Row, Select} from "antd"; -import React, {useEffect, useState} from "react"; +import React, {useCallback, useEffect, useMemo, useState} from "react"; import {fetchFilterData} from "../../utils/RenderTemplate"; import {DeleteFilled} from "@ant-design/icons"; import {useTranslation} from "react-i18next"; -import {getOperatorsByType} from "../../utils/graphQLmodifier"; +import {getOrderOperatorsByType, getWhereOperatorsByType} from "../../utils/graphQLmodifier"; import LoadingSkeleton from "../loading-skeleton/loading-skeleton.component"; +import {generateInternalReflections} from "./report-center-modal-utils"; -export default function ReportCenterModalFiltersSortersComponent({form}) { + +export default function ReportCenterModalFiltersSortersComponent({form, bodyshop}) { return ( {() => { const key = form.getFieldValue("key"); - return ; + return ; }} ); } -function RenderFilters({templateId, form}) { +/** + * Filters Section + * @param filters + * @param form + * @param bodyshop + * @returns {JSX.Element} + * @constructor + */ +function FiltersSection({filters, form, bodyshop}) { + const {t} = useTranslation(); + + return ( + + + {(fields, {add, remove, move}) => { + return ( +
+ {fields.map((field, index) => ( + + +
+ + trigger.parentNode} + options={getWhereOperatorsByType(type)}/> + + } + } + + + + + { + () => { + const name = form.getFieldValue(['filters', field.name, "field"]); + const type = filters.find(f => f.name === name)?.type; + const reflector = filters.find(f => f.name === name)?.reflector; + + return + { + (() => { + const generateReflections = (reflector) => { + if (!reflector) return []; + + const {name} = reflector; + const path = name?.split('.'); + const upperPath = path?.[0]; + const finalPath = path?.slice(1).join('.'); + + return generateInternalReflections({ + bodyshop, + upperPath, + finalPath + }); + }; + + const reflections = reflector ? generateReflections(reflector) : []; + const fieldPath = [[field.name, "value"]]; + + if (reflections.length > 0) { + return ( + form.setFieldValue(fieldPath, e.target.value)}/> + ); + })() + } + + } + } + + + + + { + remove(field.name); + }} + /> + + + + ))} + + + + + ); + }} + + + ); +} + +/** + * Sorters Section + * @param sorters + * @param form + * @returns {JSX.Element} + * @constructor + */ +function SortersSection({sorters, form}) { + const {t} = useTranslation(); + return ( + + + {(fields, {add, remove, move}) => { + return ( +
+ Sorters + {fields.map((field, index) => ( + + +
+ + trigger.parentNode} + /> + + + + + { + remove(field.name); + }} + /> + + + + ))} + + + + + ); + }} + + + ); +} + +/** + * Render Filters + * @param templateId + * @param form + * @param bodyshop + * @returns {JSX.Element|null} + * @constructor + */ +function RenderFilters({templateId, form, bodyshop}) { const [state, setState] = useState(null); const [visible, setVisible] = useState(false); const [isLoading, setIsLoading] = useState(false); const {t} = useTranslation(); - useEffect(() => { - const fetch = async () => { - setIsLoading(true); - const data = await fetchFilterData({name: templateId}); - if (data?.success) { - setState(data.data); - } else { - setState(null); - } - setIsLoading(false); - }; + const fetch = useCallback(async () => { + // Reset all the filters and Sorters. + form.resetFields(['filters']); + form.resetFields(['sorters']); + form.resetFields(['defaultSorters']); + setIsLoading(true); + + const data = await fetchFilterData({name: templateId}); + + // We have Success + if (data?.success) { + if (data?.data?.sorters && data?.data?.sorters.length > 0) { + const defaultSorters = data?.data?.sorters.filter((sorter) => sorter.hasOwnProperty('default')).map((sorter) => { + return { + field: sorter.name, + direction: sorter.default.direction + }; + }).sort((a, b) => a.default.order - b.default.order); + + form.setFieldValue('defaultSorters', JSON.stringify(defaultSorters)); + } + // Set the state + setState(data.data); + } + // Something went wrong fetching filter data + else { + setState(null); + } + setIsLoading(false); + }, [templateId, form]); + + useEffect(() => { if (templateId) { fetch(); + } - }, [templateId]); + }, [templateId, fetch]); + const filters = useMemo(() => state?.filters || [], [state]); + const sorters = useMemo(() => state?.sorters || [], [state]); - // Conditional display of filters and sorters if (!templateId) return null; if (isLoading) return ; if (!state) return null; - // Filters and Sorters data available return (
{visible && (
- {state.filters && state.filters.length > 0 && ( - - - {(fields, {add, remove, move}) => { - return ( -
- {fields.map((field, index) => ( - - -
- - - - } - } - - - - - - { - () => { - const name = form.getFieldValue(['filters', field.name, "field"]); - const type = state.filters.find(f => f.name === name)?.type; - - return - {type === 'number' ? - { - form.setFieldsValue({[field.name]: {value: parseInt(value)}}); - }} - /> - : - { - form.setFieldsValue({[field.name]: {value: value.toString()}}); - }} - /> - } - - } - } - - - - - { - remove(field.name); - }} - /> - - - - ))} - - - - - ); - }} - - - + {filters.length > 0 && ( + )} - {state.sorters && state.sorters.length > 0 && ( - - - {(fields, {add, remove, move}) => { - return ( -
- Sorters - {fields.map((field, index) => ( - - -
- - - - - - - { - remove(field.name); - }} - /> - - - - ))} - - - - - ); - }} - - + {sorters.length > 0 && ( + )} )} diff --git a/client/src/components/report-center-modal/report-center-modal-utils.js b/client/src/components/report-center-modal/report-center-modal-utils.js new file mode 100644 index 000000000..2f9fc5e87 --- /dev/null +++ b/client/src/components/report-center-modal/report-center-modal-utils.js @@ -0,0 +1,121 @@ +import {uniqBy} from "lodash"; + +/** + * Get value from path + * @param obj + * @param path + * @returns {*} + */ +const getValueFromPath = (obj, path) => path.split('.').reduce((prev, curr) => prev?.[curr], obj); + +/** + * Valid internal reflections + * Note: This is intended for future functionality + * @type {{special: string[], bodyshop: [{name: string, type: string}]}} + */ +const VALID_INTERNAL_REFLECTIONS = { + bodyshop: [ + { + name: 'md_ro_statuses.statuses', + type: 'kv-to-v' + } + ], +}; + +/** + * Generate options + * @param bodyshop + * @param path + * @param labelPath + * @param valuePath + * @returns {{label: *, value: *}[]} + */ +const generateOptionsFromObject = (bodyshop, path, labelPath, valuePath) => { + const options = getValueFromPath(bodyshop, path); + return uniqBy(Object.values(options).map((value) => ({ + label: value[labelPath], + value: value[valuePath], + })), 'value'); +} + +/** + * Generate special reflections + * @param bodyshop + * @param finalPath + * @returns {{label: *, value: *}[]|{label: *, value: *}[]|{label: string, value: *}[]|*[]} + */ +const generateSpecialReflections = (bodyshop, finalPath) => { + switch (finalPath) { + case 'cost_centers': + return generateOptionsFromObject(bodyshop, 'md_responsibility_centers.costs', 'name', 'name'); + // Special case because Categories is an Array, not an Object. + case 'categories': + const catOptions = getValueFromPath(bodyshop, 'md_categories'); + return uniqBy(catOptions.map((value) => ({ + label: value, + value: value, + })), 'value'); + case 'insurance_companies': + return generateOptionsFromObject(bodyshop, 'md_ins_cos', 'name', 'name'); + case 'employee_teams': + return generateOptionsFromObject(bodyshop, 'employee_teams', 'name', 'id'); + // Special case because Employees uses a concatenation of first_name and last_name + case 'employees': + const employeesOptions = getValueFromPath(bodyshop, 'employees'); + return uniqBy(Object.values(employeesOptions).map((value) => ({ + label: `${value.first_name} ${value.last_name}`, + value: value.id, + })), 'value'); + case 'last_names': + return generateOptionsFromObject(bodyshop, 'employees', 'last_name', 'last_name'); + case 'first_names': + return generateOptionsFromObject(bodyshop, 'employees', 'first_name', 'first_name'); + case 'job_statuses': + const statusOptions = getValueFromPath(bodyshop, 'md_ro_statuses.statuses'); + return Object.values(statusOptions).map((value) => ({ + label: value, + value + })); + default: + console.error('Invalid Special reflection provided by Report Filters'); + return []; + } +} + +/** + * Generate bodyshop reflections + * @param bodyshop + * @param finalPath + * @returns {{label: *, value: *}[]|*[]} + */ +const generateBodyshopReflections = (bodyshop, finalPath) => { + const options = getValueFromPath(bodyshop, finalPath); + const reflectionRenderer = VALID_INTERNAL_REFLECTIONS.bodyshop.find(reflection => reflection.name === finalPath); + if (reflectionRenderer?.type === 'kv-to-v') { + return Object.values(options).map((value) => ({ + label: value, + value + })); + } + return []; +} + +/** + * Generate internal reflections based on the path and bodyshop + * @param bodyshop + * @param upperPath + * @param finalPath + * @returns {{label: *, value: *}[]|[]|{label: *, value: *}[]|{label: string, value: *}[]|{label: *, value: *}[]|*[]} + */ +const generateInternalReflections = ({bodyshop, upperPath, finalPath}) => { + switch (upperPath) { + case 'special': + return generateSpecialReflections(bodyshop, finalPath); + case 'bodyshop': + return generateBodyshopReflections(bodyshop, finalPath); + default: + return []; + } +}; + +export {generateInternalReflections,} \ No newline at end of file diff --git a/client/src/components/report-center-modal/report-center-modal.component.jsx b/client/src/components/report-center-modal/report-center-modal.component.jsx index 51e268465..3e835e88f 100644 --- a/client/src/components/report-center-modal/report-center-modal.component.jsx +++ b/client/src/components/report-center-modal/report-center-modal.component.jsx @@ -16,9 +16,11 @@ import EmployeeSearchSelect from "../employee-search-select/employee-search-sele import VendorSearchSelect from "../vendor-search-select/vendor-search-select.component"; import "./report-center-modal.styles.scss"; import ReportCenterModalFiltersSortersComponent from "./report-center-modal-filters-sorters-component"; +import {selectBodyshop} from "../../redux/user/user.selectors"; const mapStateToProps = createStructuredSelector({ reportCenterModal: selectReportCenter, + bodyshop: selectBodyshop, }); const mapDispatchToProps = (dispatch) => ({ //setUserLanguage: language => dispatch(setUserLanguage(language)) @@ -28,7 +30,7 @@ export default connect( mapDispatchToProps )(ReportCenterModalComponent); -export function ReportCenterModalComponent({reportCenterModal}) { +export function ReportCenterModalComponent({reportCenterModal, bodyshop}) { const [form] = Form.useForm(); const [search, setSearch] = useState(""); @@ -64,7 +66,7 @@ export function ReportCenterModalComponent({reportCenterModal}) { const end = values.dates ? values.dates[1] : null; const {id} = values; - await GenerateDocument( + const templateConfig = { name: values.key, variables: { @@ -81,7 +83,14 @@ export function ReportCenterModalComponent({reportCenterModal}) { }, filters: values.filters, sorters: values.sorters, - }, + }; + + if (_.isString(values.defaultSorters) && !_.isEmpty(values.defaultSorters)) { + templateConfig.defaultSorters = JSON.parse(values.defaultSorters); + } + + await GenerateDocument( + templateConfig, { to: values.to, subject: Templates[values.key]?.subject, @@ -119,6 +128,7 @@ export function ReportCenterModalComponent({reportCenterModal}) { onChange={(e) => setSearch(e.target.value)} value={search} /> +