diff --git a/apps/backend/package.json b/apps/backend/package.json index a1b420a2..b6a60825 100644 --- a/apps/backend/package.json +++ b/apps/backend/package.json @@ -5,7 +5,7 @@ "@aws-sdk/client-cognito-identity-provider": "^3.496.0", "@aws-sdk/client-s3": "^3.496.0", "aws-sdk-mock": "^5.1.0", - "axios": "^1.6.0", + "axios": "^1.18.1", "body-parser": "^1.19.0", "cors": "^2.8.5", "express": "^4.17.1", @@ -13,17 +13,16 @@ "express-fileupload": "^1.2.0", "helmet": "^7.1.0", "join-images": "^1.1.5", - "lodash": "^4.17.21", + "lodash": "^4.18.1", "loglevel": "^1.7.1", "mongodb-memory-server": "^7.4.0", "mongoose": "^6.0.6", "mongoose-encryption": "^2.1.0", "node-2fa": "^2.0.2", - "omit-deep-lodash": "^1.1.5", "pad": "^3.2.0", "pdf2pic": "^3.1.3", "supertest": "^6.1.3", - "twilio": "^3.71.1" + "twilio": "^6.0.2" }, "devDependencies": { "@3dp4me/types": "workspace:*", @@ -35,10 +34,14 @@ "@types/lodash": "^4.14.202", "@types/node": "^20.9.4", "@types/pad": "^1.0.2", + "canvas": "^3.2.3", + "cross-env": "^7.0.3", + "dotenv": "^17.4.2", "eslint-config-3dp4me": "workspace:*", "exceljs": "^4.4.0", "jest": "^27.0.6", "node-loader": "^2.1.0", + "omit-deep-lodash": "^1.1.5", "rimraf": "^5.0.5", "ts-loader": "^9.5.1", "typescript": "^5.3.3", diff --git a/apps/backend/src/middleware/authentication.ts b/apps/backend/src/middleware/authentication.ts index 9c47b44b..35bd4606 100644 --- a/apps/backend/src/middleware/authentication.ts +++ b/apps/backend/src/middleware/authentication.ts @@ -41,8 +41,7 @@ export const requireRole = // If there isn't a user, authenticate if (!req.user) { return requireAuthentication(req, res, () => { - requireRole(role) - next() + requireRole(role)(req, res, next) }) } if (!req.user.roles.includes(role)) return sendResponse(res, 403, ERR_NOT_APPROVED) diff --git a/apps/frontend/package.json b/apps/frontend/package.json index c8e21982..42b60cb0 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -19,22 +19,19 @@ "@emotion/styled": "^11.14.1", "@mui/icons-material": "^6.4.5", "@mui/material": "^6.4.5", - "@sweetalert/with-react": "^0.1.1", "@tanstack/react-query": "^5.17.19", "@vis.gl/react-google-maps": "^1.4.2", - "Chip": "link:@mui/material/Chip", "aws-amplify": "4.2.11", - "axios": "^1.6.0", + "axios": "^1.18.1", "date-fns": "^3.3.0", "js-file-download": "^0.4.12", - "lodash": "^4.17.21", - "lodash.omit": "^4.5.0", + "lodash": "^4.18.1", "mic-recorder-to-mp3": "^2.2.2", "object-hash": "^3.0.0", "react": "^18.2.0", "react-app-rewired": "^2.2.1", "react-code-input": "^3.10.1", - "react-datepicker": "^4.25.0", + "react-datepicker": "^9.1.0", "react-dom": "^18.2.0", "react-html5-camera-photo": "^1.5.5", "react-image-gallery": "^1.2.7", @@ -45,7 +42,7 @@ "react-promise-tracker": "^2.1.0", "react-router-dom": "^5.2.0", "react-scripts": "^5.0.1", - "react-signature-canvas": "^1.0.6", + "react-signature-canvas": "1.1.0-alpha.2", "react-switch": "^6.0.0", "signature_pad": "^2.3.2", "styled-components": "^6.1.8", @@ -57,18 +54,12 @@ "@babel/core": "^7.23.3", "@babel/preset-env": "^7.23.3", "@babel/preset-react": "^7.23.3", - "@rollup/plugin-commonjs": "^25.0.7", - "@rollup/plugin-image": "^3.0.3", - "@rollup/plugin-json": "^6.0.1", - "@rollup/plugin-node-resolve": "^15.2.3", - "@rollup/plugin-typescript": "^11.1.5", "@types/jest": "^29.5.8", "@types/lodash": "^4.14.202", - "@types/lodash.omit": "^4.5.9", "@types/node": "^20.9.4", "@types/object-hash": "^3.0.6", "@types/react": "^18.2.37", - "@types/react-datepicker": "^4.19.3", + "@types/react-datepicker": "^7.0.0", "@types/react-dom": "^18.2.15", "@types/react-html5-camera-photo": "^1.5.3", "@types/react-image-gallery": "^1.2.4", @@ -83,17 +74,9 @@ "css-loader": "^6.8.1", "dotenv-webpack": "^8.0.1", "eslint-config-3dp4me": "workspace:*", - "file-loader": "^6.2.0", "html-webpack-plugin": "^5.5.3", - "image-webpack-loader": "^8.1.0", - "react-app-env": "^1.2.3", "react-app-rewired": "^2.2.1", "rimraf": "^5.0.5", - "rollup": "^4.5.0", - "rollup-plugin-babel": "^4.4.0", - "rollup-plugin-postcss": "^4.0.2", - "rollup-plugin-terser": "^7.0.2", - "rollup-plugin-visualizer": "^5.9.2", "sass": "^1.69.5", "sass-loader": "^13.3.2", "source-map-explorer": "^2.5.3", diff --git a/apps/frontend/rollup.config.mjs b/apps/frontend/rollup.config.mjs deleted file mode 100644 index 6a6a78c9..00000000 --- a/apps/frontend/rollup.config.mjs +++ /dev/null @@ -1,39 +0,0 @@ -import resolve from '@rollup/plugin-node-resolve'; -import commonjs from '@rollup/plugin-commonjs'; -import typescript from '@rollup/plugin-typescript'; -import babel from "rollup-plugin-babel" -import image from '@rollup/plugin-image' -import json from '@rollup/plugin-json' - -import postcss from "rollup-plugin-postcss"; -import { terser } from 'rollup-plugin-terser'; - -// const extensions = ['.js', '.ts', '.jsx', '.tsx']; - -export default { - input: './src/index.tsx', - output: { - file: 'dist/bundle.js', - format: 'iife', - }, - plugins: [ - resolve({ - browser: true - }), - image(), - json(), - babel({ - exclude: 'node_modules/**', - presets: ['@babel/preset-react'] - }), - commonjs(), - typescript({ - tsconfig: './tsconfig.json', - declaration: true, - declarationDir: 'dist', - }), - postcss(), - // terser(), - ], - external: ['react', 'react-dom'], -}; \ No newline at end of file diff --git a/apps/frontend/src/components/ControlledSignatureCanvas/ControlledSignatureCanvas.tsx b/apps/frontend/src/components/ControlledSignatureCanvas/ControlledSignatureCanvas.tsx index bde0f091..135ad4c6 100644 --- a/apps/frontend/src/components/ControlledSignatureCanvas/ControlledSignatureCanvas.tsx +++ b/apps/frontend/src/components/ControlledSignatureCanvas/ControlledSignatureCanvas.tsx @@ -1,10 +1,13 @@ import { Nullish, Signature, SignaturePoint } from '@3dp4me/types' -import omit from 'lodash.omit' +import omit from 'lodash/omit' import { FC, useEffect, useRef, useState } from 'react' import ReactSignatureCanvas from 'react-signature-canvas' +import SignaturePad from 'signature_pad' interface ControlledSignatureCanvasProps { value: Nullish + width: number + height: number } /** @@ -12,7 +15,11 @@ interface ControlledSignatureCanvasProps { * we can make it mount and unmout on every value update * @returns */ -export const ControlledSignatureCanvas: FC = ({ value }) => { +export const ControlledSignatureCanvas: FC = ({ + value, + width, + height, +}) => { const sigCanvas = useRef(null) const [doesCanvasHaveData, setDoesCanvasHaveData] = useState(false) @@ -31,7 +38,7 @@ export const ControlledSignatureCanvas: FC = ({ ) setDoesCanvasHaveData(true) - setImmediate(() => sigCanvas.current?.fromData(data as any)) + setTimeout(() => sigCanvas.current?.fromData(data as SignaturePad.Point[][]), 0) }, [value, sigCanvas.current, doesCanvasHaveData]) /** @@ -67,5 +74,13 @@ export const ControlledSignatureCanvas: FC = ({ return formattedData } - return + // Giving the canvas explicit dimensions makes the library skip its own + // offsetWidth-based resize on mount, which reads 0 if layout hasn't settled + return ( + + ) } diff --git a/apps/frontend/src/components/Fields/DateField.tsx b/apps/frontend/src/components/Fields/DateField.tsx index 475ac1ed..7793f4da 100644 --- a/apps/frontend/src/components/Fields/DateField.tsx +++ b/apps/frontend/src/components/Fields/DateField.tsx @@ -23,8 +23,8 @@ registerLocale(LANGUAGES.AR, arSA) const DateField = ({ displayName, isDisabled, fieldId, value, onChange }: DateFieldProps) => { const selectedLang = useTranslations()[1] - const sendChanges = (date: Date) => { - onChange(fieldId, date.toString()) + const sendChanges = (date: Date | null) => { + onChange(fieldId, date?.toString() || '') } const className = isDisabled ? 'input-field datepicker' : 'active-input datepicker' diff --git a/apps/frontend/src/components/Fields/FieldGroup/FieldGroupTable.tsx b/apps/frontend/src/components/Fields/FieldGroup/FieldGroupTable.tsx index 37cea194..17996480 100644 --- a/apps/frontend/src/components/Fields/FieldGroup/FieldGroupTable.tsx +++ b/apps/frontend/src/components/Fields/FieldGroup/FieldGroupTable.tsx @@ -63,6 +63,10 @@ const CellEditContainer = styled(TableCell)` width: 100%; } + .react-datepicker-popper { + z-index: 10; + } + // Take full width for date picker .react-datepicker-wrapper { width: 100%; diff --git a/apps/frontend/src/components/Fields/SignatureField.tsx b/apps/frontend/src/components/Fields/SignatureField.tsx index 25b2fffe..2b42eb58 100644 --- a/apps/frontend/src/components/Fields/SignatureField.tsx +++ b/apps/frontend/src/components/Fields/SignatureField.tsx @@ -5,7 +5,7 @@ import './SignatureField.scss' import { Nullish, Path, PathValue, Signature, TranslatedString } from '@3dp4me/types' import Button from '@mui/material/Button' import hash from 'object-hash' -import { useState } from 'react' +import { useLayoutEffect, useRef, useState } from 'react' import { useTranslations } from '../../hooks/useTranslations' import { ControlledSignatureCanvas } from '../ControlledSignatureCanvas/ControlledSignatureCanvas' @@ -31,6 +31,23 @@ const SignatureField = ({ const [isModalOpen, setIsModalOpen] = useState(false) const [isDocumentVisible, setIsDocumentVisible] = useState(false) const translations = useTranslations()[0] + const sigContainerRef = useRef(null) + const [canvasWidth, setCanvasWidth] = useState(0) + + // Watch the container's size so the canvas gets a real width even if the + // container had no layout (width 0) when this component first mounted + useLayoutEffect(() => { + const container = sigContainerRef.current + if (!container) return undefined + + const observer = new ResizeObserver(() => + setTimeout(() => setCanvasWidth(container.offsetWidth), 0) + ) + observer.observe(container) + return () => { + observer.disconnect() + } + }, []) /** * Saves signature data points along with the canvas width and height so that @@ -89,9 +106,15 @@ const SignatureField = ({ onSave={onNewSignature} isOpen={isModalOpen} /> -
- {/* We need to keep changing the key to force a rerender */} - +
+ {canvasWidth > 0 && ( + + )}