diff --git a/apps/backend/apps/frontend/package-lock.json b/apps/backend/apps/frontend/package-lock.json new file mode 100644 index 0000000..760762c --- /dev/null +++ b/apps/backend/apps/frontend/package-lock.json @@ -0,0 +1,840 @@ +{ + "name": "frontend", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "dependencies": { + "@tailwindcss/typography": "^0.5.19", + "@tiptap/extension-color": "^3.23.1", + "@tiptap/extension-highlight": "^3.23.1", + "@tiptap/extension-image": "^3.23.1", + "@tiptap/extension-link": "^3.23.1", + "@tiptap/extension-placeholder": "^3.23.1", + "@tiptap/extension-text-align": "^3.23.1", + "@tiptap/extension-text-style": "^3.23.1", + "@tiptap/extension-underline": "^3.23.1", + "@tiptap/react": "^3.23.1", + "@tiptap/starter-kit": "^3.23.1" + } + }, + "node_modules/@floating-ui/core": { + "version": "1.7.5", + "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.7.5.tgz", + "integrity": "sha512-1Ih4WTWyw0+lKyFMcBHGbb5U5FtuHJuujoyyr5zTaWS5EYMeT6Jb2AuDeftsCsEuchO+mM2ij5+q9crhydzLhQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/dom": { + "version": "1.7.6", + "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.7.6.tgz", + "integrity": "sha512-9gZSAI5XM36880PPMm//9dfiEngYoC6Am2izES1FF406YFsjvyBMmeJ2g4SAju3xWwtuynNRFL2s9hgxpLI5SQ==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/core": "^1.7.5", + "@floating-ui/utils": "^0.2.11" + } + }, + "node_modules/@floating-ui/utils": { + "version": "0.2.11", + "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.2.11.tgz", + "integrity": "sha512-RiB/yIh78pcIxl6lLMG0CgBXAZ2Y0eVHqMPYugu+9U0AeT6YBeiJpf7lbdJNIugFP5SIjwNRgo4DhR1Qxi26Gg==", + "license": "MIT", + "optional": true + }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.19.tgz", + "integrity": "sha512-w31dd8HOx3k9vPtcQh5QHP9GwKcgbMp87j58qi6xgiBnFFtKEAgCWnDw4qUT8aHwkCp8bKvb/KGKWWHedP0AAg==", + "license": "MIT", + "dependencies": { + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders || >=4.0.0-alpha.20 || >=4.0.0-beta.1" + } + }, + "node_modules/@tiptap/core": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-3.23.1.tgz", + "integrity": "sha512-8YvSGiJTeU5wPuGiYIIYgyiyaaT1CAx+kJL0bju0w871OvbJJj0T/ywhcmxGXW6pOal2T8X2xt9ZqE+vib0VJw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "3.23.1" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-3.23.1.tgz", + "integrity": "sha512-FdVZLZOkL06j3WLXOC2UeX7++Cj3qI2vfohruMJiz4vk1Q5UUH7G4+AykFzjzBJHrdEpkiRUkRpU1KZIWdbluw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-3.23.1.tgz", + "integrity": "sha512-EAYdNzyOjlQh2VBY1EhdxtiTjVMaOAD6P0ezms60dKRjd4oj/8grfXfUqwgo4NVdFb11Ks85vXoHuXJSylfR4A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-3.23.1.tgz", + "integrity": "sha512-1advMCpPkHD/3ucZhYmNau8B4tF0L6iRAFhUOglp5bBZDuq13+rYujh3cm4vFmjH9KqThzpcUDn+ZU2c+mTMyw==", + "license": "MIT", + "optional": true, + "dependencies": { + "@floating-ui/dom": "^1.0.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1", + "@tiptap/pm": "3.23.1" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-3.23.1.tgz", + "integrity": "sha512-owWnBBI4t+jqVDY0naDjhsAmrNGldh4czouef2K+mEf032B7uGsDVCwKp1qaX1JZesyYDfvXOaIwT22hNID2mw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.23.1" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-3.23.1.tgz", + "integrity": "sha512-nGuhb4YghgTfkejwWHrD9GSpwcC5kkVmm2sN/UY4yceDw+PkyysYKJWZehRLTOC8GNgSAhq/EeQeq14Xwk6dyg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-3.23.1.tgz", + "integrity": "sha512-BdJGqM57CsKgYrQUZz78vIG8Yn7EpsE2pA7iKn5tYoSXpYtt0IaU4qB1heH7lwWD/vVCAm0YQVD7/0F+0++yhA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1", + "@tiptap/pm": "3.23.1" + } + }, + "node_modules/@tiptap/extension-color": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-3.23.1.tgz", + "integrity": "sha512-OYk/fT3h8Bz4B6GUVTQDvKGpPnpI5d6QHkuqjVhdFsgH3oo58PdLE1TdIGgeavuYPLaFxgBtEXmm3oTY9jPWxw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-text-style": "3.23.1" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-3.23.1.tgz", + "integrity": "sha512-NA5Rx59HRwG6Hb6LwLpC5lE7z6vCj6f90S7RNNsnE+CyiXNR/OhY2BcjuxiGnascHvsnsAbvxGU3ymKMDgvDVg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-3.23.1.tgz", + "integrity": "sha512-WRN7e/h9m3uI5j9/+L6jcPhHbTL6aKxfFfQWZHNf5M8TqSL1P+/2h034td0XMj3n48i4fWyzjVUV9+sz6t2fDw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "3.23.1" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-3.23.1.tgz", + "integrity": "sha512-XrYHpLn1DpLFSGTko9F9xgbNamL6fGpWkK4wqgwPVbg/SJwQCDO/9p5D3DtJTwD+xgw4sQ9as4O6rt6jx8JT+Q==", + "license": "MIT", + "optional": true, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@floating-ui/dom": "^1.0.0", + "@tiptap/core": "3.23.1", + "@tiptap/pm": "3.23.1" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-3.23.1.tgz", + "integrity": "sha512-E4hB0xquUpEXy7kboLBazrFyRCsN0j0fsTFR8udgQf5xetAVPhOexSTKuzOcU/n0kxsKJin7laYYEag/Fd2KNw==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "3.23.1" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-3.23.1.tgz", + "integrity": "sha512-XYkCKC5RVqMmmBk+nd22/6IDDx1OC54sdStH5VEHtfOrarriO0JztK8Mr0TijPPk9N4rKXsmndYZM2xyWZZytQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-3.23.1.tgz", + "integrity": "sha512-1z9yCSp8fevgX3r/4kWXO3of0WFCQWfYjWfHANvoJ4JQTYBkARjXlj1tbk5rrAJBFDDfKRkUpZOurXKgGo+h+g==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-highlight": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-3.23.1.tgz", + "integrity": "sha512-Bf5u5KBb6SLveKzPgUEbHoU5uWTUcEvSSZr/mpQZpvCpE6MlXZbvengmFj1OkKnrNZWg0Um2p9e6zKnZHH07sA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.23.1.tgz", + "integrity": "sha512-30XUHXdEZxcz1FCWjz9HW2EEq06NQcAye6rXGnvHo6Y60iJ6MRsrX5byvceFNF9DTVtOIcUFBQ/psIiRcoi0KA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1", + "@tiptap/pm": "3.23.1" + } + }, + "node_modules/@tiptap/extension-image": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-3.23.1.tgz", + "integrity": "sha512-rAyfh8HS0PfXS8PKl1VQUiDFzXtF5SlrILpOPmz+4Oc4pmI+/vN+ain4z8k6HRxWM03YVpvLvyeQ0OFwi/fq3A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-3.23.1.tgz", + "integrity": "sha512-lZB9YCjoVNDoPMguya66nBvaS/2YpGN5iAcjAGx/JQkCAZeOAtl9+ALMzbWPKH6tQP6m98YtkY1T7RXr++T0bA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-3.23.1.tgz", + "integrity": "sha512-uOeyLqYQI0WG62agpFG24kVHSn3Z48gD8Y0uLLJbtzh/nDFC3d9So2sQGWlSVyMzsgkJ4k/9jNnxxsVO8qgJOg==", + "license": "MIT", + "dependencies": { + "linkifyjs": "^4.3.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1", + "@tiptap/pm": "3.23.1" + } + }, + "node_modules/@tiptap/extension-list": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list/-/extension-list-3.23.1.tgz", + "integrity": "sha512-v1AeXPpagslgRZdOp7WdjCoO4TjjNP8RM2R6Gqx0/inGaNXnM8zCMshOxZlAb03Ad7kq/4RGJmkpM/Jjsi6dEQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1", + "@tiptap/pm": "3.23.1" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-3.23.1.tgz", + "integrity": "sha512-Fk/884un5OSLCFxe2TbOmfp3sLMB5b76CnMjaSrvgfiaZnsV2WlJZGPXxCAPbxNIATTykNlSBsVuMBO7we64Vg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.23.1" + } + }, + "node_modules/@tiptap/extension-list-keymap": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-keymap/-/extension-list-keymap-3.23.1.tgz", + "integrity": "sha512-sHbE5sxiJzhgGn94GUAzD4qKM9SyImBrOlAGS/EIe+pausjqQE7xi+YW0gRo2jG+gXhSYl4/oAGXQXzmSInSUQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.23.1" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-3.23.1.tgz", + "integrity": "sha512-3GG7YFhVJWw/HWmRxvMMUC296x7TPBQRLsH4ryEC1SMAmVJnbTIvetyvIcLqLEXGW7Rj41S7SO8qjOXVceSOTA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extension-list": "3.23.1" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-3.23.1.tgz", + "integrity": "sha512-GC7b6yAjASl1q9sNkPmukZmVYMfxx03EEhpMMrLYJY9GBz82Ald927yYQsOqf2aKA/Rjo/aZMYCGtjXkGk6aBA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-placeholder": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-3.23.1.tgz", + "integrity": "sha512-eKmQhDt+51GIPxcFXoT8wmS+ejt6evIiHtXBgsLaABG6wg9GHnpGEndPcXsDGVR1s5ZLawVB52PFCX5GqKoWlQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/extensions": "3.23.1" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-3.23.1.tgz", + "integrity": "sha512-+R5LG0ZW9SDZc4weA79uq6uUduVsCEph9tRcoQCRA82IVIiPYSTxTLew9odalmk/Mc7vdZvOK5jjtO5jUVw/rg==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-3.23.1.tgz", + "integrity": "sha512-k1Ki9bBV6mLz1mFP+Laqh1YHJ2MY0P8XzaMqpkgMndEBIJQ3XcpWQc5bfAlRnYcOI9ZXDbAgQ8CwgArxHmQWCQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-text-align": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-3.23.1.tgz", + "integrity": "sha512-ap4ZN31v57mVX2P+0OoW5iO+ehsUNe0C5MgF/Ta2F/HRmTCc1M1mFqYUCk8zJYX1TFRV18vqK2j6STRBk0R8ng==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-text-style": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-3.23.1.tgz", + "integrity": "sha512-q3GQQo+lBhrtNkqdbhYWnv/byG/RYAxVnNhYPQMubRzavGdXBU8NhpJ/47YYjPimG1sahzcs2aqy7amVd8ri/A==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-3.23.1.tgz", + "integrity": "sha512-+PvHyVozHyxJ9oWCIQx5JHBZ7LAa/sFJUOFaKyfmel4gL9AbP52MmvrciXARlZHd1WCULJtdbLan0+x5/D/9hQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1" + } + }, + "node_modules/@tiptap/extensions": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/extensions/-/extensions-3.23.1.tgz", + "integrity": "sha512-7UIn+idaVTVhdlP0KmgzBh8Csmwck357Dq4te5DuAxhSkN1gsXHlq39mpx907UYKJdSOgd+GMFeyOziPwSmbOQ==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1", + "@tiptap/pm": "3.23.1" + } + }, + "node_modules/@tiptap/pm": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-3.23.1.tgz", + "integrity": "sha512-8G+TkNsUHHAAJYREpA6fw+Dw/m2Y3Go4/QMQM8RYepid+wTeE1wSv7sBA/CBrphhYmJSWeTyCPtgQIxnTJXMCA==", + "license": "MIT", + "dependencies": { + "prosemirror-changeset": "^2.3.0", + "prosemirror-commands": "^1.6.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.4.1", + "prosemirror-keymap": "^1.2.2", + "prosemirror-model": "^1.24.1", + "prosemirror-schema-list": "^1.5.0", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.6.4", + "prosemirror-transform": "^1.10.2", + "prosemirror-view": "^1.38.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/react": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-3.23.1.tgz", + "integrity": "sha512-43zUwKOcsxRIcgiDbcEUagojhPIez2OIryaNG/uiDcRzkrUteiTu2wSJndkQqwouwh3wJEm+KOw8xybNYvU+qA==", + "license": "MIT", + "dependencies": { + "@types/use-sync-external-store": "^0.0.6", + "fast-equals": "^5.3.3", + "use-sync-external-store": "^1.4.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "optionalDependencies": { + "@tiptap/extension-bubble-menu": "^3.23.1", + "@tiptap/extension-floating-menu": "^3.23.1" + }, + "peerDependencies": { + "@tiptap/core": "3.23.1", + "@tiptap/pm": "3.23.1", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "@types/react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "3.23.1", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-3.23.1.tgz", + "integrity": "sha512-CURePHQagBaZIDJrHH3of4Nmi0VYGpZ6yBlkdFxFHBxY9aeG2/h5kn+oHo8GbzkSFsRV+9olzRgDTOULVgs8pQ==", + "license": "MIT", + "dependencies": { + "@tiptap/core": "^3.23.1", + "@tiptap/extension-blockquote": "^3.23.1", + "@tiptap/extension-bold": "^3.23.1", + "@tiptap/extension-bullet-list": "^3.23.1", + "@tiptap/extension-code": "^3.23.1", + "@tiptap/extension-code-block": "^3.23.1", + "@tiptap/extension-document": "^3.23.1", + "@tiptap/extension-dropcursor": "^3.23.1", + "@tiptap/extension-gapcursor": "^3.23.1", + "@tiptap/extension-hard-break": "^3.23.1", + "@tiptap/extension-heading": "^3.23.1", + "@tiptap/extension-horizontal-rule": "^3.23.1", + "@tiptap/extension-italic": "^3.23.1", + "@tiptap/extension-link": "^3.23.1", + "@tiptap/extension-list": "^3.23.1", + "@tiptap/extension-list-item": "^3.23.1", + "@tiptap/extension-list-keymap": "^3.23.1", + "@tiptap/extension-ordered-list": "^3.23.1", + "@tiptap/extension-paragraph": "^3.23.1", + "@tiptap/extension-strike": "^3.23.1", + "@tiptap/extension-text": "^3.23.1", + "@tiptap/extension-underline": "^3.23.1", + "@tiptap/extensions": "^3.23.1", + "@tiptap/pm": "^3.23.1" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@types/react": { + "version": "19.2.14", + "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.14.tgz", + "integrity": "sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==", + "license": "MIT", + "peer": true, + "dependencies": { + "csstype": "^3.2.2" + } + }, + "node_modules/@types/react-dom": { + "version": "19.2.3", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-19.2.3.tgz", + "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "@types/react": "^19.2.0" + } + }, + "node_modules/@types/use-sync-external-store": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz", + "integrity": "sha512-zFDAD+tlpf2r4asuHEj0XH6pY6i0g5NeAHPn+15wk3BV6JA69eERFXC1gyGThDkVa1zCyKr5jox1+2LbV/AMLg==", + "license": "MIT" + }, + "node_modules/cssesc": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", + "integrity": "sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==", + "license": "MIT", + "bin": { + "cssesc": "bin/cssesc" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/csstype": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT", + "peer": true + }, + "node_modules/fast-equals": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/fast-equals/-/fast-equals-5.4.0.tgz", + "integrity": "sha512-jt2DW/aNFNwke7AUd+Z+e6pz39KO5rzdbbFCg2sGafS4mk13MI7Z8O5z9cADNn5lhGODIgLwug6TZO2ctf7kcw==", + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/linkifyjs": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.3.2.tgz", + "integrity": "sha512-NT1CJtq3hHIreOianA8aSXn6Cw0JzYOuDQbOrSPe7gqFnCpKP++MQe3ODgO3oh2GJFORkAAdqredOa60z63GbA==", + "license": "MIT" + }, + "node_modules/orderedmap": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/orderedmap/-/orderedmap-2.1.1.tgz", + "integrity": "sha512-TvAWxi0nDe1j/rtMcWcIj94+Ffe6n7zhow33h40SKxmsmozs6dz/e+EajymfoFcHd7sxNn8yHM8839uixMOV6g==", + "license": "MIT" + }, + "node_modules/postcss-selector-parser": { + "version": "6.0.10", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.0.10.tgz", + "integrity": "sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==", + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/prosemirror-changeset": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.4.1.tgz", + "integrity": "sha512-96WBLhOaYhJ+kPhLg3uW359Tz6I/MfcrQfL4EGv4SrcqKEMC1gmoGrXHecPE8eOwTVCJ4IwgfzM8fFad25wNfw==", + "license": "MIT", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.7.1.tgz", + "integrity": "sha512-rT7qZnQtx5c0/y/KlYaGvtG411S97UaL6gdp6RIZ23DLHanMYLyfGBV5DtSnZdthQql7W+lEVbpSfwtO8T+L2w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.10.2" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.2", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.2.tgz", + "integrity": "sha512-CCk6Gyx9+Tt2sbYk5NK0nB1ukHi2ryaRgadV/LvyNuO3ena1payM2z6Cg0vO1ebK8cxbzo41ku2DE5Axj1Zuiw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.4.1.tgz", + "integrity": "sha512-pMdYaEnjNMSwl11yjEGtgTmLkR08m/Vl+Jj443167p9eB3HVQKhYCc4gmHVDsLPODfZfjr/MmirsdyZziXbQKw==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.0.0", + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-view": "^1.0.0" + } + }, + "node_modules/prosemirror-history": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.5.0.tgz", + "integrity": "sha512-zlzTiH01eKA55UAf1MEjtssJeHnGxO0j4K4Dpx+gnmX9n+SHNlDqI2oO1Kv1iPN5B1dm5fsljCfqKF9nFL6HRg==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.3.tgz", + "integrity": "sha512-4HucRlpiLd1IPQQXNqeo81BGtkY8Ai5smHhKW9jjPKRc2wQIxksg7Hl1tTI2IfT2B/LgX6bfYvXxEpJl7aKYKw==", + "license": "MIT", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.25.4", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.25.4.tgz", + "integrity": "sha512-PIM7E43PBxKce8OQeezAs9j4TP+5yDpZVbuurd1h5phUxEKIu+G2a+EUZzIC5nS1mJktDJWzbqS23n1tsAf5QA==", + "license": "MIT", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.5.1.tgz", + "integrity": "sha512-927lFx/uwyQaGwJxLWCZRkjXG0p48KpMj6ueoYiu4JX05GGuGcgzAy62dfiV8eFZftgyBUvLx76RsMe20fJl+Q==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.4.tgz", + "integrity": "sha512-6jiYHH2CIGbCfnxdHbXZ12gySFY/fz/ulZE333G6bPqIZ4F+TXo9ifiR86nAHpWnfoNjOb3o5ESi7J8Uz1jXHw==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.8.5", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.8.5.tgz", + "integrity": "sha512-V/0cDCsHKHe/tfWkeCmthNUcEp1IVO3p6vwN8XtwE9PZQLAZJigbw3QoraAdfJPir4NKJtNvOB8oYGKRl+t0Dw==", + "license": "MIT", + "dependencies": { + "prosemirror-keymap": "^1.2.3", + "prosemirror-model": "^1.25.4", + "prosemirror-state": "^1.4.4", + "prosemirror-transform": "^1.10.5", + "prosemirror-view": "^1.41.4" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.12.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.12.0.tgz", + "integrity": "sha512-GxboyN4AMIsoHNtz5uf2r2Ru551i5hWeCMD6E2Ib4Eogqoub0NflniaBPVQ4MrGE5yZ8JV9tUHg9qcZTTrcN4w==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.41.8", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.41.8.tgz", + "integrity": "sha512-TnKDdohEatgyZNGCDWIdccOHXhYloJwbwU+phw/a23KBvJIR9lWQWW7WHHK3vBdOLDNuF7TaX98GObUZOWkOnA==", + "license": "MIT", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, + "node_modules/react": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react/-/react-19.2.6.tgz", + "integrity": "sha512-sfWGGfavi0xr8Pg0sVsyHMAOziVYKgPLNrS7ig+ivMNb3wbCBw3KxtflsGBAwD3gYQlE/AEZsTLgToRrSCjb0Q==", + "license": "MIT", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/react-dom": { + "version": "19.2.6", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.6.tgz", + "integrity": "sha512-0prMI+hvBbPjsWnxDLxlCGyM8PN6UuWjEUCYmZhO67xIV9Xasa/r/vDnq+Xyq4Lo27g8QSbO5YzARu0D1Sps3g==", + "license": "MIT", + "peer": true, + "dependencies": { + "scheduler": "^0.27.0" + }, + "peerDependencies": { + "react": "^19.2.6" + } + }, + "node_modules/rope-sequence": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/rope-sequence/-/rope-sequence-1.3.4.tgz", + "integrity": "sha512-UT5EDe2cu2E/6O4igUr5PSFs23nvvukicWHx6GnOPlHAiiYbzNuCRQCuiUdHJQcqKalLKlrYJnjY0ySGsXNQXQ==", + "license": "MIT" + }, + "node_modules/scheduler": { + "version": "0.27.0", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.27.0.tgz", + "integrity": "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q==", + "license": "MIT", + "peer": true + }, + "node_modules/tailwindcss": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.3.0.tgz", + "integrity": "sha512-y6nxMGB1nMW9R6k96e5gdIFzcfL/gTJRNaqGes1YvkLnPVXzWgbqFF2yLC0T8G774n24cx3Pe8XrKoniCOAH+Q==", + "license": "MIT", + "peer": true + }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" + }, + "node_modules/w3c-keyname": { + "version": "2.2.8", + "resolved": "https://registry.npmjs.org/w3c-keyname/-/w3c-keyname-2.2.8.tgz", + "integrity": "sha512-dpojBhNsCNN7T82Tm7k26A6G9ML3NkhDsnw9n/eoxSRlVBB4CEtIQ/KTCLI2Fwf3ataSXRhYFkQi3SlnFwPvPQ==", + "license": "MIT" + } + } +} diff --git a/apps/backend/apps/frontend/package.json b/apps/backend/apps/frontend/package.json new file mode 100644 index 0000000..b250cbb --- /dev/null +++ b/apps/backend/apps/frontend/package.json @@ -0,0 +1,15 @@ +{ + "dependencies": { + "@tailwindcss/typography": "^0.5.19", + "@tiptap/extension-color": "^3.23.1", + "@tiptap/extension-highlight": "^3.23.1", + "@tiptap/extension-image": "^3.23.1", + "@tiptap/extension-link": "^3.23.1", + "@tiptap/extension-placeholder": "^3.23.1", + "@tiptap/extension-text-align": "^3.23.1", + "@tiptap/extension-text-style": "^3.23.1", + "@tiptap/extension-underline": "^3.23.1", + "@tiptap/react": "^3.23.1", + "@tiptap/starter-kit": "^3.23.1" + } +} diff --git a/apps/backend/create-test-booking.js b/apps/backend/create-test-booking.js index cf0bd73..2740f62 100644 --- a/apps/backend/create-test-booking.js +++ b/apps/backend/create-test-booking.js @@ -1,114 +1,113 @@ -/** - * Script pour créer un booking de test avec statut PENDING - * Usage: node create-test-booking.js - */ - -const { Client } = require('pg'); -const { v4: uuidv4 } = require('uuid'); - -async function createTestBooking() { - const client = new Client({ - host: process.env.DATABASE_HOST || 'localhost', - port: parseInt(process.env.DATABASE_PORT || '5432'), - database: process.env.DATABASE_NAME || 'xpeditis_dev', - user: process.env.DATABASE_USER || 'xpeditis', - password: process.env.DATABASE_PASSWORD || 'xpeditis_dev_password', - }); - - try { - await client.connect(); - console.log('✅ Connecté à la base de données'); - - const bookingId = uuidv4(); - const confirmationToken = uuidv4(); - const userId = '8cf7d5b3-d94f-44aa-bb5a-080002919dd1'; // User demo@xpeditis.com - const organizationId = '199fafa9-d26f-4cf9-9206-73432baa8f63'; - - // Create dummy documents in JSONB format - const dummyDocuments = JSON.stringify([ - { - id: uuidv4(), - type: 'BILL_OF_LADING', - fileName: 'bill-of-lading.pdf', - filePath: 'https://dummy-storage.com/documents/bill-of-lading.pdf', - mimeType: 'application/pdf', - size: 102400, // 100KB - uploadedAt: new Date().toISOString(), - }, - { - id: uuidv4(), - type: 'PACKING_LIST', - fileName: 'packing-list.pdf', - filePath: 'https://dummy-storage.com/documents/packing-list.pdf', - mimeType: 'application/pdf', - size: 51200, // 50KB - uploadedAt: new Date().toISOString(), - }, - { - id: uuidv4(), - type: 'COMMERCIAL_INVOICE', - fileName: 'commercial-invoice.pdf', - filePath: 'https://dummy-storage.com/documents/commercial-invoice.pdf', - mimeType: 'application/pdf', - size: 76800, // 75KB - uploadedAt: new Date().toISOString(), - }, - ]); - - const query = ` - INSERT INTO csv_bookings ( - id, user_id, organization_id, carrier_name, carrier_email, - origin, destination, volume_cbm, weight_kg, pallet_count, - price_usd, price_eur, primary_currency, transit_days, container_type, - status, confirmation_token, requested_at, notes, documents - ) VALUES ( - $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, - $11, $12, $13, $14, $15, $16, $17, NOW(), $18, $19 - ) RETURNING id, confirmation_token; - `; - - const values = [ - bookingId, - userId, - organizationId, - 'Test Carrier', - 'test@carrier.com', - 'NLRTM', // Rotterdam - 'USNYC', // New York - 25.5, // volume_cbm - 3500, // weight_kg - 10, // pallet_count - 1850.50, // price_usd - 1665.45, // price_eur - 'USD', // primary_currency - 28, // transit_days - 'LCL', // container_type - 'PENDING', // status - IMPORTANT! - confirmationToken, - 'Test booking created by script', - dummyDocuments, // documents JSONB - ]; - - const result = await client.query(query, values); - - console.log('\n🎉 Booking de test créé avec succès!'); - console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); - console.log(`📦 Booking ID: ${bookingId}`); - console.log(`🔑 Token: ${confirmationToken}`); - console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); - console.log('🔗 URLs de test:'); - console.log(` Accept: http://localhost:3000/carrier/accept/${confirmationToken}`); - console.log(` Reject: http://localhost:3000/carrier/reject/${confirmationToken}`); - console.log('\n📧 URL API (pour curl):'); - console.log(` curl http://localhost:4000/api/v1/csv-bookings/accept/${confirmationToken}`); - console.log('\n✅ Ce booking est en statut PENDING et peut être accepté/refusé.\n'); - - } catch (error) { - console.error('❌ Erreur:', error.message); - console.error(error); - } finally { - await client.end(); - } -} - -createTestBooking(); +/** + * Script pour créer un booking de test avec statut PENDING + * Usage: node create-test-booking.js + */ + +const { Client } = require('pg'); +const { v4: uuidv4 } = require('uuid'); + +async function createTestBooking() { + const client = new Client({ + host: process.env.DATABASE_HOST || 'localhost', + port: parseInt(process.env.DATABASE_PORT || '5432'), + database: process.env.DATABASE_NAME || 'xpeditis_dev', + user: process.env.DATABASE_USER || 'xpeditis', + password: process.env.DATABASE_PASSWORD || 'xpeditis_dev_password', + }); + + try { + await client.connect(); + console.log('✅ Connecté à la base de données'); + + const bookingId = uuidv4(); + const confirmationToken = uuidv4(); + const userId = '8cf7d5b3-d94f-44aa-bb5a-080002919dd1'; // User demo@xpeditis.com + const organizationId = '199fafa9-d26f-4cf9-9206-73432baa8f63'; + + // Create dummy documents in JSONB format + const dummyDocuments = JSON.stringify([ + { + id: uuidv4(), + type: 'BILL_OF_LADING', + fileName: 'bill-of-lading.pdf', + filePath: 'https://dummy-storage.com/documents/bill-of-lading.pdf', + mimeType: 'application/pdf', + size: 102400, // 100KB + uploadedAt: new Date().toISOString(), + }, + { + id: uuidv4(), + type: 'PACKING_LIST', + fileName: 'packing-list.pdf', + filePath: 'https://dummy-storage.com/documents/packing-list.pdf', + mimeType: 'application/pdf', + size: 51200, // 50KB + uploadedAt: new Date().toISOString(), + }, + { + id: uuidv4(), + type: 'COMMERCIAL_INVOICE', + fileName: 'commercial-invoice.pdf', + filePath: 'https://dummy-storage.com/documents/commercial-invoice.pdf', + mimeType: 'application/pdf', + size: 76800, // 75KB + uploadedAt: new Date().toISOString(), + }, + ]); + + const query = ` + INSERT INTO csv_bookings ( + id, user_id, organization_id, carrier_name, carrier_email, + origin, destination, volume_cbm, weight_kg, pallet_count, + price_usd, price_eur, primary_currency, transit_days, container_type, + status, confirmation_token, requested_at, notes, documents + ) VALUES ( + $1, $2, $3, $4, $5, $6, $7, $8, $9, $10, + $11, $12, $13, $14, $15, $16, $17, NOW(), $18, $19 + ) RETURNING id, confirmation_token; + `; + + const values = [ + bookingId, + userId, + organizationId, + 'Test Carrier', + 'test@carrier.com', + 'NLRTM', // Rotterdam + 'USNYC', // New York + 25.5, // volume_cbm + 3500, // weight_kg + 10, // pallet_count + 1850.5, // price_usd + 1665.45, // price_eur + 'USD', // primary_currency + 28, // transit_days + 'LCL', // container_type + 'PENDING', // status - IMPORTANT! + confirmationToken, + 'Test booking created by script', + dummyDocuments, // documents JSONB + ]; + + const result = await client.query(query, values); + + console.log('\n🎉 Booking de test créé avec succès!'); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━'); + console.log(`📦 Booking ID: ${bookingId}`); + console.log(`🔑 Token: ${confirmationToken}`); + console.log('━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━\n'); + console.log('🔗 URLs de test:'); + console.log(` Accept: http://localhost:3000/carrier/accept/${confirmationToken}`); + console.log(` Reject: http://localhost:3000/carrier/reject/${confirmationToken}`); + console.log('\n📧 URL API (pour curl):'); + console.log(` curl http://localhost:4000/api/v1/csv-bookings/accept/${confirmationToken}`); + console.log('\n✅ Ce booking est en statut PENDING et peut être accepté/refusé.\n'); + } catch (error) { + console.error('❌ Erreur:', error.message); + console.error(error); + } finally { + await client.end(); + } +} + +createTestBooking(); diff --git a/apps/backend/debug-email-flow.js b/apps/backend/debug-email-flow.js index 7d3f365..d105a92 100644 --- a/apps/backend/debug-email-flow.js +++ b/apps/backend/debug-email-flow.js @@ -1,321 +1,324 @@ -/** - * Script de debug pour tester le flux complet d'envoi d'email - * - * Ce script teste: - * 1. Connexion SMTP - * 2. Envoi d'un email simple - * 3. Envoi avec le template complet - */ - -require('dotenv').config(); -const nodemailer = require('nodemailer'); - -console.log('\n🔍 DEBUG - Flux d\'envoi d\'email transporteur\n'); -console.log('='.repeat(60)); - -// 1. Afficher la configuration -console.log('\n📋 CONFIGURATION ACTUELLE:'); -console.log('----------------------------'); -console.log('SMTP_HOST:', process.env.SMTP_HOST); -console.log('SMTP_PORT:', process.env.SMTP_PORT); -console.log('SMTP_SECURE:', process.env.SMTP_SECURE); -console.log('SMTP_USER:', process.env.SMTP_USER); -console.log('SMTP_PASS:', process.env.SMTP_PASS ? '***' + process.env.SMTP_PASS.slice(-4) : 'NON DÉFINI'); -console.log('SMTP_FROM:', process.env.SMTP_FROM); -console.log('APP_URL:', process.env.APP_URL); - -// 2. Vérifier les variables requises -console.log('\n✅ VÉRIFICATION DES VARIABLES:'); -console.log('--------------------------------'); -const requiredVars = ['SMTP_HOST', 'SMTP_PORT', 'SMTP_USER', 'SMTP_PASS']; -const missing = requiredVars.filter(v => !process.env[v]); -if (missing.length > 0) { - console.error('❌ Variables manquantes:', missing.join(', ')); - process.exit(1); -} else { - console.log('✅ Toutes les variables requises sont présentes'); -} - -// 3. Créer le transporter avec la même configuration que le backend -console.log('\n🔧 CRÉATION DU TRANSPORTER:'); -console.log('----------------------------'); - -const host = process.env.SMTP_HOST; -const port = parseInt(process.env.SMTP_PORT); -const user = process.env.SMTP_USER; -const pass = process.env.SMTP_PASS; -const secure = process.env.SMTP_SECURE === 'true'; - -// Même logique que dans email.adapter.ts -const useDirectIP = host.includes('mailtrap.io'); -const actualHost = useDirectIP ? '3.209.246.195' : host; -const serverName = useDirectIP ? 'smtp.mailtrap.io' : host; - -console.log('Configuration détectée:'); -console.log(' Host original:', host); -console.log(' Utilise IP directe:', useDirectIP); -console.log(' Host réel:', actualHost); -console.log(' Server name (TLS):', serverName); -console.log(' Port:', port); -console.log(' Secure:', secure); - -const transporter = nodemailer.createTransport({ - host: actualHost, - port, - secure, - auth: { - user, - pass, - }, - tls: { - rejectUnauthorized: false, - servername: serverName, - }, - connectionTimeout: 10000, - greetingTimeout: 10000, - socketTimeout: 30000, - dnsTimeout: 10000, -}); - -// 4. Tester la connexion -console.log('\n🔌 TEST DE CONNEXION SMTP:'); -console.log('---------------------------'); - -async function testConnection() { - try { - console.log('Vérification de la connexion...'); - await transporter.verify(); - console.log('✅ Connexion SMTP réussie!'); - return true; - } catch (error) { - console.error('❌ Échec de la connexion SMTP:'); - console.error(' Message:', error.message); - console.error(' Code:', error.code); - console.error(' Command:', error.command); - if (error.stack) { - console.error(' Stack:', error.stack.substring(0, 200) + '...'); - } - return false; - } -} - -// 5. Envoyer un email de test simple -async function sendSimpleEmail() { - console.log('\n📧 TEST 1: Email simple'); - console.log('------------------------'); - - try { - const info = await transporter.sendMail({ - from: process.env.SMTP_FROM || 'noreply@xpeditis.com', - to: 'test@example.com', - subject: 'Test Simple - ' + new Date().toISOString(), - text: 'Ceci est un test simple', - html: '
Ceci est un test simple
', - }); - - console.log('✅ Email simple envoyé avec succès!'); - console.log(' Message ID:', info.messageId); - console.log(' Response:', info.response); - console.log(' Accepted:', info.accepted); - console.log(' Rejected:', info.rejected); - return true; - } catch (error) { - console.error('❌ Échec d\'envoi email simple:'); - console.error(' Message:', error.message); - console.error(' Code:', error.code); - return false; - } -} - -// 6. Envoyer un email avec le template transporteur complet -async function sendCarrierEmail() { - console.log('\n📧 TEST 2: Email transporteur avec template'); - console.log('--------------------------------------------'); - - const bookingData = { - bookingId: 'TEST-' + Date.now(), - origin: 'FRPAR', - destination: 'USNYC', - volumeCBM: 15.5, - weightKG: 1200, - palletCount: 6, - priceUSD: 2500, - priceEUR: 2250, - primaryCurrency: 'USD', - transitDays: 18, - containerType: '40FT', - documents: [ - { type: 'Bill of Lading', fileName: 'bol-test.pdf' }, - { type: 'Packing List', fileName: 'packing-test.pdf' }, - { type: 'Commercial Invoice', fileName: 'invoice-test.pdf' }, - ], - }; - - const baseUrl = process.env.APP_URL || 'http://localhost:3000'; - const acceptUrl = `${baseUrl}/api/v1/csv-bookings/${bookingData.bookingId}/accept`; - const rejectUrl = `${baseUrl}/api/v1/csv-bookings/${bookingData.bookingId}/reject`; - - // Template HTML (version simplifiée pour le test) - const htmlTemplate = ` - - - - - -Ceci est un test simple
', + }); + + console.log('✅ Email simple envoyé avec succès!'); + console.log(' Message ID:', info.messageId); + console.log(' Response:', info.response); + console.log(' Accepted:', info.accepted); + console.log(' Rejected:', info.rejected); + return true; + } catch (error) { + console.error("❌ Échec d'envoi email simple:"); + console.error(' Message:', error.message); + console.error(' Code:', error.code); + return false; + } +} + +// 6. Envoyer un email avec le template transporteur complet +async function sendCarrierEmail() { + console.log('\n📧 TEST 2: Email transporteur avec template'); + console.log('--------------------------------------------'); + + const bookingData = { + bookingId: 'TEST-' + Date.now(), + origin: 'FRPAR', + destination: 'USNYC', + volumeCBM: 15.5, + weightKG: 1200, + palletCount: 6, + priceUSD: 2500, + priceEUR: 2250, + primaryCurrency: 'USD', + transitDays: 18, + containerType: '40FT', + documents: [ + { type: 'Bill of Lading', fileName: 'bol-test.pdf' }, + { type: 'Packing List', fileName: 'packing-test.pdf' }, + { type: 'Commercial Invoice', fileName: 'invoice-test.pdf' }, + ], + }; + + const baseUrl = process.env.APP_URL || 'http://localhost:3000'; + const acceptUrl = `${baseUrl}/api/v1/csv-bookings/${bookingData.bookingId}/accept`; + const rejectUrl = `${baseUrl}/api/v1/csv-bookings/${bookingData.bookingId}/reject`; + + // Template HTML (version simplifiée pour le test) + const htmlTemplate = ` + + + + + +Configuration standard fonctionne.
', - }); - - console.log('✅ Test 1 RÉUSSI - Configuration standard OK\n'); - } catch (error) { - console.error('❌ Test 1 ÉCHOUÉ:', error.message); - console.error(' Code:', error.code); - console.error(' Timeout?', error.message.includes('ETIMEOUT')); - console.log(''); - } - - // Test 2: Configuration avec IP directe (devrait toujours fonctionner) - console.log('Test 2: Configuration avec IP directe...'); - try { - const useDirectIP = config.host.includes('mailtrap.io'); - const actualHost = useDirectIP ? '3.209.246.195' : config.host; - const serverName = useDirectIP ? 'smtp.mailtrap.io' : config.host; - - console.log(` Utilisation IP directe: ${useDirectIP}`); - console.log(` Host réel: ${actualHost}`); - console.log(` Server name (TLS): ${serverName}`); - - const transporter2 = nodemailer.createTransport({ - host: actualHost, - port: config.port, - secure: false, - auth: { - user: config.user, - pass: config.pass, - }, - tls: { - rejectUnauthorized: false, - servername: serverName, - }, - connectionTimeout: 10000, - greetingTimeout: 10000, - socketTimeout: 30000, - dnsTimeout: 10000, - }); - - const result = await transporter2.sendMail({ - from: 'noreply@xpeditis.com', - to: 'test@xpeditis.com', - subject: 'Test Email - Configuration IP Directe', - html: 'Configuration avec IP directe fonctionne.
', - }); - - console.log('✅ Test 2 RÉUSSI - Configuration IP directe OK'); - console.log(` Message ID: ${result.messageId}`); - console.log(` Response: ${result.response}\n`); - } catch (error) { - console.error('❌ Test 2 ÉCHOUÉ:', error.message); - console.error(' Code:', error.code); - console.log(''); - } - - // Test 3: Template HTML de booking transporteur - console.log('Test 3: Envoi avec template HTML complet...'); - try { - const useDirectIP = config.host.includes('mailtrap.io'); - const actualHost = useDirectIP ? '3.209.246.195' : config.host; - const serverName = useDirectIP ? 'smtp.mailtrap.io' : config.host; - - const transporter3 = nodemailer.createTransport({ - host: actualHost, - port: config.port, - secure: false, - auth: { - user: config.user, - pass: config.pass, - }, - tls: { - rejectUnauthorized: false, - servername: serverName, - }, - connectionTimeout: 10000, - greetingTimeout: 10000, - socketTimeout: 30000, - dnsTimeout: 10000, - }); - - const bookingData = { - bookingId: 'TEST-' + Date.now(), - origin: 'FRPAR', - destination: 'USNYC', - volumeCBM: 10.5, - weightKG: 850, - palletCount: 4, - priceUSD: 1500, - priceEUR: 1350, - primaryCurrency: 'USD', - transitDays: 15, - containerType: '20FT', - documents: [ - { type: 'Bill of Lading', fileName: 'bol.pdf' }, - { type: 'Packing List', fileName: 'packing_list.pdf' }, - ], - acceptUrl: 'http://localhost:3000/carrier/booking/accept', - rejectUrl: 'http://localhost:3000/carrier/booking/reject', - }; - - const htmlTemplate = ` - - - - - - - - `; - - const result = await transporter3.sendMail({ - from: 'noreply@xpeditis.com', - to: 'carrier@test.com', - subject: `Nouvelle demande de réservation - ${bookingData.origin} → ${bookingData.destination}`, - html: htmlTemplate, - }); - - console.log('✅ Test 3 RÉUSSI - Email complet avec template envoyé'); - console.log(` Message ID: ${result.messageId}`); - console.log(` Response: ${result.response}\n`); - } catch (error) { - console.error('❌ Test 3 ÉCHOUÉ:', error.message); - console.error(' Code:', error.code); - console.log(''); - } - - console.log('📊 Résumé des tests:'); - console.log(' ✓ Vérifiez Mailtrap inbox: https://mailtrap.io/inboxes'); - console.log(' ✓ Recherchez les emails de test ci-dessus'); - console.log(' ✓ Si Test 2 et 3 réussissent, le backend doit être corrigé avec la configuration IP directe\n'); -} - -// Run test -testEmailConfig() - .then(() => { - console.log('✅ Tests terminés avec succès'); - process.exit(0); - }) - .catch((error) => { - console.error('❌ Erreur lors des tests:', error); - process.exit(1); - }); +/** + * Script de test pour vérifier l'envoi d'email aux transporteurs + * + * Usage: node test-carrier-email-fix.js + */ + +const nodemailer = require('nodemailer'); + +async function testEmailConfig() { + console.log('🔍 Test de configuration email Mailtrap...\n'); + + const config = { + host: process.env.SMTP_HOST || 'sandbox.smtp.mailtrap.io', + port: parseInt(process.env.SMTP_PORT || '2525'), + user: process.env.SMTP_USER || '2597bd31d265eb', + pass: process.env.SMTP_PASS || 'cd126234193c89', + }; + + console.log('📧 Configuration SMTP:'); + console.log(` Host: ${config.host}`); + console.log(` Port: ${config.port}`); + console.log(` User: ${config.user}`); + console.log(` Pass: ${config.pass.substring(0, 4)}***\n`); + + // Test 1: Configuration standard (peut échouer avec timeout DNS) + console.log('Test 1: Configuration standard...'); + try { + const transporter1 = nodemailer.createTransport({ + host: config.host, + port: config.port, + secure: false, + auth: { + user: config.user, + pass: config.pass, + }, + connectionTimeout: 10000, + greetingTimeout: 10000, + socketTimeout: 30000, + }); + + await transporter1.sendMail({ + from: 'noreply@xpeditis.com', + to: 'test@xpeditis.com', + subject: 'Test Email - Configuration Standard', + html: 'Configuration standard fonctionne.
', + }); + + console.log('✅ Test 1 RÉUSSI - Configuration standard OK\n'); + } catch (error) { + console.error('❌ Test 1 ÉCHOUÉ:', error.message); + console.error(' Code:', error.code); + console.error(' Timeout?', error.message.includes('ETIMEOUT')); + console.log(''); + } + + // Test 2: Configuration avec IP directe (devrait toujours fonctionner) + console.log('Test 2: Configuration avec IP directe...'); + try { + const useDirectIP = config.host.includes('mailtrap.io'); + const actualHost = useDirectIP ? '3.209.246.195' : config.host; + const serverName = useDirectIP ? 'smtp.mailtrap.io' : config.host; + + console.log(` Utilisation IP directe: ${useDirectIP}`); + console.log(` Host réel: ${actualHost}`); + console.log(` Server name (TLS): ${serverName}`); + + const transporter2 = nodemailer.createTransport({ + host: actualHost, + port: config.port, + secure: false, + auth: { + user: config.user, + pass: config.pass, + }, + tls: { + rejectUnauthorized: false, + servername: serverName, + }, + connectionTimeout: 10000, + greetingTimeout: 10000, + socketTimeout: 30000, + dnsTimeout: 10000, + }); + + const result = await transporter2.sendMail({ + from: 'noreply@xpeditis.com', + to: 'test@xpeditis.com', + subject: 'Test Email - Configuration IP Directe', + html: 'Configuration avec IP directe fonctionne.
', + }); + + console.log('✅ Test 2 RÉUSSI - Configuration IP directe OK'); + console.log(` Message ID: ${result.messageId}`); + console.log(` Response: ${result.response}\n`); + } catch (error) { + console.error('❌ Test 2 ÉCHOUÉ:', error.message); + console.error(' Code:', error.code); + console.log(''); + } + + // Test 3: Template HTML de booking transporteur + console.log('Test 3: Envoi avec template HTML complet...'); + try { + const useDirectIP = config.host.includes('mailtrap.io'); + const actualHost = useDirectIP ? '3.209.246.195' : config.host; + const serverName = useDirectIP ? 'smtp.mailtrap.io' : config.host; + + const transporter3 = nodemailer.createTransport({ + host: actualHost, + port: config.port, + secure: false, + auth: { + user: config.user, + pass: config.pass, + }, + tls: { + rejectUnauthorized: false, + servername: serverName, + }, + connectionTimeout: 10000, + greetingTimeout: 10000, + socketTimeout: 30000, + dnsTimeout: 10000, + }); + + const bookingData = { + bookingId: 'TEST-' + Date.now(), + origin: 'FRPAR', + destination: 'USNYC', + volumeCBM: 10.5, + weightKG: 850, + palletCount: 4, + priceUSD: 1500, + priceEUR: 1350, + primaryCurrency: 'USD', + transitDays: 15, + containerType: '20FT', + documents: [ + { type: 'Bill of Lading', fileName: 'bol.pdf' }, + { type: 'Packing List', fileName: 'packing_list.pdf' }, + ], + acceptUrl: 'http://localhost:3000/carrier/booking/accept', + rejectUrl: 'http://localhost:3000/carrier/booking/reject', + }; + + const htmlTemplate = ` + + + + + + + + `; + + const result = await transporter3.sendMail({ + from: 'noreply@xpeditis.com', + to: 'carrier@test.com', + subject: `Nouvelle demande de réservation - ${bookingData.origin} → ${bookingData.destination}`, + html: htmlTemplate, + }); + + console.log('✅ Test 3 RÉUSSI - Email complet avec template envoyé'); + console.log(` Message ID: ${result.messageId}`); + console.log(` Response: ${result.response}\n`); + } catch (error) { + console.error('❌ Test 3 ÉCHOUÉ:', error.message); + console.error(' Code:', error.code); + console.log(''); + } + + console.log('📊 Résumé des tests:'); + console.log(' ✓ Vérifiez Mailtrap inbox: https://mailtrap.io/inboxes'); + console.log(' ✓ Recherchez les emails de test ci-dessus'); + console.log( + ' ✓ Si Test 2 et 3 réussissent, le backend doit être corrigé avec la configuration IP directe\n' + ); +} + +// Run test +testEmailConfig() + .then(() => { + console.log('✅ Tests terminés avec succès'); + process.exit(0); + }) + .catch(error => { + console.error('❌ Erreur lors des tests:', error); + process.exit(1); + }); diff --git a/apps/backend/test-carrier-email.js b/apps/backend/test-carrier-email.js index 41833f5..5b44776 100644 --- a/apps/backend/test-carrier-email.js +++ b/apps/backend/test-carrier-email.js @@ -5,25 +5,28 @@ const transporter = nodemailer.createTransport({ port: 2525, auth: { user: '2597bd31d265eb', - pass: 'cd126234193c89' - } + pass: 'cd126234193c89', + }, }); -console.log('🔄 Tentative d\'envoi d\'email...'); +console.log("🔄 Tentative d'envoi d'email..."); -transporter.sendMail({ - from: 'noreply@xpeditis.com', - to: 'test@example.com', - subject: 'Test Email depuis Portail Transporteur', - text: 'Email de test pour vérifier la configuration' -}).then(info => { - console.log('✅ Email envoyé:', info.messageId); - console.log('📧 Response:', info.response); - process.exit(0); -}).catch(err => { - console.error('❌ Erreur:', err.message); - console.error('Code:', err.code); - console.error('Command:', err.command); - console.error('Stack:', err.stack); - process.exit(1); -}); +transporter + .sendMail({ + from: 'noreply@xpeditis.com', + to: 'test@example.com', + subject: 'Test Email depuis Portail Transporteur', + text: 'Email de test pour vérifier la configuration', + }) + .then(info => { + console.log('✅ Email envoyé:', info.messageId); + console.log('📧 Response:', info.response); + process.exit(0); + }) + .catch(err => { + console.error('❌ Erreur:', err.message); + console.error('Code:', err.code); + console.error('Command:', err.command); + console.error('Stack:', err.stack); + process.exit(1); + }); diff --git a/apps/backend/test-email-ip.js b/apps/backend/test-email-ip.js index 1ea3bbf..e7d7093 100644 --- a/apps/backend/test-email-ip.js +++ b/apps/backend/test-email-ip.js @@ -31,7 +31,8 @@ const transporter = nodemailer.createTransport(config); console.log('\n1️⃣ Verifying SMTP connection...'); -transporter.verify() +transporter + .verify() .then(() => { console.log('✅ SMTP connection verified!'); console.log('\n2️⃣ Sending test email...'); @@ -40,17 +41,17 @@ transporter.verify() from: 'noreply@xpeditis.com', to: 'test@example.com', subject: 'Test Xpeditis - Envoi Direct IP', - html: 'Ce test utilise l\'IP directe pour contourner le DNS.
', + html: "Ce test utilise l'IP directe pour contourner le DNS.
", }); }) - .then((info) => { + .then(info => { console.log('✅ Email sent successfully!'); console.log('📧 Message ID:', info.messageId); console.log('📬 Response:', info.response); console.log('\n🎉 SUCCESS! Email sending works with IP directly.'); process.exit(0); }) - .catch((error) => { + .catch(error => { console.error('\n❌ ERROR:', error.message); console.error('Code:', error.code); console.error('Command:', error.command); diff --git a/apps/backend/test-email-service.js b/apps/backend/test-email-service.js index 973e401..94da3f0 100644 --- a/apps/backend/test-email-service.js +++ b/apps/backend/test-email-service.js @@ -6,7 +6,8 @@ const axios = require('axios'); const API_URL = 'http://localhost:4000/api/v1'; // Token d'authentification (admin@xpeditis.com) -const AUTH_TOKEN = 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5MTI3Y2M0Zi04Yzg4LTRjNGUtYmU1ZC1hNmY1ZTE2MWZlNDMiLCJlbWFpbCI6ImFkbWluQHhwZWRpdGlzLmNvbSIsInJvbGUiOiJBRE1JTiIsIm9yZ2FuaXphdGlvbklkIjoiMWZhOWE1NjUtZjNjOC00ZTExLTliMzAtMTIwZDEwNTJjZWYwIiwidHlwZSI6ImFjY2VzcyIsImlhdCI6MTc2NDg3NDQ2MSwiZXhwIjoxNzY0ODc1MzYxfQ.l_-97_rikGj-DP8aA14CK-Ab-0Usy722MRe1lqi0u9I'; +const AUTH_TOKEN = + 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiI5MTI3Y2M0Zi04Yzg4LTRjNGUtYmU1ZC1hNmY1ZTE2MWZlNDMiLCJlbWFpbCI6ImFkbWluQHhwZWRpdGlzLmNvbSIsInJvbGUiOiJBRE1JTiIsIm9yZ2FuaXphdGlvbklkIjoiMWZhOWE1NjUtZjNjOC00ZTExLTliMzAtMTIwZDEwNTJjZWYwIiwidHlwZSI6ImFjY2VzcyIsImlhdCI6MTc2NDg3NDQ2MSwiZXhwIjoxNzY0ODc1MzYxfQ.l_-97_rikGj-DP8aA14CK-Ab-0Usy722MRe1lqi0u9I'; async function testCsvBookingEmail() { console.log('🧪 Test envoi email via CSV booking...\n'); @@ -19,7 +20,10 @@ async function testCsvBookingEmail() { // Créer un fichier de test temporaire const testFile = Buffer.from('Test document content'); - form.append('documents', testFile, { filename: 'test-document.pdf', contentType: 'application/pdf' }); + form.append('documents', testFile, { + filename: 'test-document.pdf', + contentType: 'application/pdf', + }); // Ajouter les champs du formulaire form.append('carrierName', 'Test Carrier Email'); @@ -41,8 +45,8 @@ async function testCsvBookingEmail() { const response = await axios.post(`${API_URL}/csv-bookings`, form, { headers: { ...form.getHeaders(), - 'Authorization': `Bearer ${AUTH_TOKEN}` - } + Authorization: `Bearer ${AUTH_TOKEN}`, + }, }); console.log('✅ Réponse reçue:', response.status); @@ -51,11 +55,10 @@ async function testCsvBookingEmail() { console.log('1. Les logs du backend pour voir "Email sent to carrier:"'); console.log('2. Votre inbox Mailtrap: https://mailtrap.io/inboxes'); console.log('3. Email destinataire: test-carrier@example.com'); - } catch (error) { console.error('❌ Erreur:', error.response?.data || error.message); if (error.response?.status === 401) { - console.error('\n⚠️ Token expiré. Connectez-vous d\'abord avec:'); + console.error("\n⚠️ Token expiré. Connectez-vous d'abord avec:"); console.error('POST /api/v1/auth/login'); console.error('{ "email": "admin@xpeditis.com", "password": "..." }'); } diff --git a/apps/backend/test-email.js b/apps/backend/test-email.js index f2a1a5e..ed32a70 100644 --- a/apps/backend/test-email.js +++ b/apps/backend/test-email.js @@ -31,7 +31,8 @@ const transporter = nodemailer.createTransport(config); console.log('\nVerifying SMTP connection...'); -transporter.verify() +transporter + .verify() .then(() => { console.log('✅ SMTP connection verified successfully!'); console.log('\nSending test email...'); @@ -43,13 +44,13 @@ transporter.verify() html: 'If you see this, email sending works!
', }); }) - .then((info) => { + .then(info => { console.log('✅ Email sent successfully!'); console.log('Message ID:', info.messageId); console.log('Response:', info.response); process.exit(0); }) - .catch((error) => { + .catch(error => { console.error('❌ Error:', error.message); console.error('Full error:', error); process.exit(1); diff --git a/apps/backend/test-smtp-simple.js b/apps/backend/test-smtp-simple.js index 1b2b4d4..b80b4ce 100644 --- a/apps/backend/test-smtp-simple.js +++ b/apps/backend/test-smtp-simple.js @@ -1,74 +1,74 @@ -#!/usr/bin/env node - -// Test SMTP ultra-simple pour identifier le problème -const nodemailer = require('nodemailer'); -require('dotenv').config(); - -console.log('🔍 Test SMTP Simple\n'); -console.log('Configuration:'); -console.log(' SMTP_HOST:', process.env.SMTP_HOST || 'NON DÉFINI'); -console.log(' SMTP_PORT:', process.env.SMTP_PORT || 'NON DÉFINI'); -console.log(' SMTP_USER:', process.env.SMTP_USER || 'NON DÉFINI'); -console.log(' SMTP_PASS:', process.env.SMTP_PASS ? '***' : 'NON DÉFINI'); -console.log(''); - -const host = process.env.SMTP_HOST; -const port = parseInt(process.env.SMTP_PORT || '2525'); -const user = process.env.SMTP_USER; -const pass = process.env.SMTP_PASS; - -// Appliquer le même fix DNS que dans email.adapter.ts -const useDirectIP = host && host.includes('mailtrap.io'); -const actualHost = useDirectIP ? '3.209.246.195' : host; -const serverName = useDirectIP ? 'smtp.mailtrap.io' : host; - -console.log('Fix DNS:'); -console.log(' Utilise IP directe:', useDirectIP); -console.log(' Host réel:', actualHost); -console.log(' Server name:', serverName); -console.log(''); - -const transporter = nodemailer.createTransport({ - host: actualHost, - port, - secure: false, - auth: { user, pass }, - tls: { - rejectUnauthorized: false, - servername: serverName, - }, - connectionTimeout: 10000, - greetingTimeout: 10000, - socketTimeout: 30000, - dnsTimeout: 10000, -}); - -async function test() { - try { - console.log('Test 1: Vérification de la connexion...'); - await transporter.verify(); - console.log('✅ Connexion SMTP OK\n'); - - console.log('Test 2: Envoi d\'un email...'); - const info = await transporter.sendMail({ - from: 'noreply@xpeditis.com', - to: 'test@example.com', - subject: 'Test - ' + new Date().toISOString(), - html: 'Ce message confirme que l\'envoi d\'email fonctionne.
', - }); - - console.log('✅ Email envoyé avec succès!'); - console.log(' Message ID:', info.messageId); - console.log(' Response:', info.response); - console.log(''); - console.log('✅ TOUS LES TESTS RÉUSSIS - Le SMTP fonctionne!'); - process.exit(0); - } catch (error) { - console.error('❌ ERREUR:', error.message); - console.error(' Code:', error.code); - console.error(' Command:', error.command); - process.exit(1); - } -} - -test(); +#!/usr/bin/env node + +// Test SMTP ultra-simple pour identifier le problème +const nodemailer = require('nodemailer'); +require('dotenv').config(); + +console.log('🔍 Test SMTP Simple\n'); +console.log('Configuration:'); +console.log(' SMTP_HOST:', process.env.SMTP_HOST || 'NON DÉFINI'); +console.log(' SMTP_PORT:', process.env.SMTP_PORT || 'NON DÉFINI'); +console.log(' SMTP_USER:', process.env.SMTP_USER || 'NON DÉFINI'); +console.log(' SMTP_PASS:', process.env.SMTP_PASS ? '***' : 'NON DÉFINI'); +console.log(''); + +const host = process.env.SMTP_HOST; +const port = parseInt(process.env.SMTP_PORT || '2525'); +const user = process.env.SMTP_USER; +const pass = process.env.SMTP_PASS; + +// Appliquer le même fix DNS que dans email.adapter.ts +const useDirectIP = host && host.includes('mailtrap.io'); +const actualHost = useDirectIP ? '3.209.246.195' : host; +const serverName = useDirectIP ? 'smtp.mailtrap.io' : host; + +console.log('Fix DNS:'); +console.log(' Utilise IP directe:', useDirectIP); +console.log(' Host réel:', actualHost); +console.log(' Server name:', serverName); +console.log(''); + +const transporter = nodemailer.createTransport({ + host: actualHost, + port, + secure: false, + auth: { user, pass }, + tls: { + rejectUnauthorized: false, + servername: serverName, + }, + connectionTimeout: 10000, + greetingTimeout: 10000, + socketTimeout: 30000, + dnsTimeout: 10000, +}); + +async function test() { + try { + console.log('Test 1: Vérification de la connexion...'); + await transporter.verify(); + console.log('✅ Connexion SMTP OK\n'); + + console.log("Test 2: Envoi d'un email..."); + const info = await transporter.sendMail({ + from: 'noreply@xpeditis.com', + to: 'test@example.com', + subject: 'Test - ' + new Date().toISOString(), + html: "Ce message confirme que l'envoi d'email fonctionne.
", + }); + + console.log('✅ Email envoyé avec succès!'); + console.log(' Message ID:', info.messageId); + console.log(' Response:', info.response); + console.log(''); + console.log('✅ TOUS LES TESTS RÉUSSIS - Le SMTP fonctionne!'); + process.exit(0); + } catch (error) { + console.error('❌ ERREUR:', error.message); + console.error(' Code:', error.code); + console.error(' Command:', error.command); + process.exit(1); + } +} + +test(); diff --git a/apps/backend/tsconfig.build.json b/apps/backend/tsconfig.build.json index 1d7acd8..64f86c6 100644 --- a/apps/backend/tsconfig.build.json +++ b/apps/backend/tsconfig.build.json @@ -1,4 +1,4 @@ -{ - "extends": "./tsconfig.json", - "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] -} +{ + "extends": "./tsconfig.json", + "exclude": ["node_modules", "test", "dist", "**/*spec.ts"] +} diff --git a/apps/backend/upload-test-documents.js b/apps/backend/upload-test-documents.js index 69e3323..95d358a 100644 --- a/apps/backend/upload-test-documents.js +++ b/apps/backend/upload-test-documents.js @@ -1,185 +1,185 @@ -/** - * Script to upload test documents to MinIO - */ - -const { S3Client, PutObjectCommand, CreateBucketCommand } = require('@aws-sdk/client-s3'); -const { Client: PgClient } = require('pg'); -const fs = require('fs'); -const path = require('path'); -require('dotenv').config(); - -const MINIO_ENDPOINT = process.env.AWS_S3_ENDPOINT || 'http://localhost:9000'; -const BUCKET_NAME = 'xpeditis-documents'; - -// Initialize MinIO client -const s3Client = new S3Client({ - region: 'us-east-1', - endpoint: MINIO_ENDPOINT, - credentials: { - accessKeyId: process.env.AWS_ACCESS_KEY_ID || 'minioadmin', - secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || 'minioadmin', - }, - forcePathStyle: true, -}); - -// Create a simple PDF buffer (minimal valid PDF) -function createTestPDF(title) { - return Buffer.from( - `%PDF-1.4 -1 0 obj -<< -/Type /Catalog -/Pages 2 0 R ->> -endobj -2 0 obj -<< -/Type /Pages -/Kids [3 0 R] -/Count 1 ->> -endobj -3 0 obj -<< -/Type /Page -/Parent 2 0 R -/MediaBox [0 0 612 792] -/Contents 4 0 R -/Resources << -/Font << -/F1 << -/Type /Font -/Subtype /Type1 -/BaseFont /Helvetica ->> ->> ->> ->> -endobj -4 0 obj -<< -/Length 100 ->> -stream -BT -/F1 24 Tf -100 700 Td -(${title}) Tj -ET -endstream -endobj -xref -0 5 -0000000000 65535 f -0000000009 00000 n -0000000058 00000 n -0000000115 00000 n -0000000300 00000 n -trailer -<< -/Size 5 -/Root 1 0 R ->> -startxref -450 -%%EOF`, - 'utf-8' - ); -} - -async function uploadTestDocuments() { - const pgClient = new PgClient({ - host: process.env.DATABASE_HOST || 'localhost', - port: process.env.DATABASE_PORT || 5432, - user: process.env.DATABASE_USER || 'xpeditis', - password: process.env.DATABASE_PASSWORD || 'xpeditis_dev_password', - database: process.env.DATABASE_NAME || 'xpeditis_dev', - }); - - try { - // Connect to database - await pgClient.connect(); - console.log('✅ Connected to database'); - - // Create bucket if it doesn't exist - try { - await s3Client.send(new CreateBucketCommand({ Bucket: BUCKET_NAME })); - console.log(`✅ Created bucket: ${BUCKET_NAME}`); - } catch (error) { - if (error.name === 'BucketAlreadyOwnedByYou' || error.Code === 'BucketAlreadyOwnedByYou') { - console.log(`✅ Bucket already exists: ${BUCKET_NAME}`); - } else { - console.log(`⚠️ Could not create bucket (might already exist): ${error.message}`); - } - } - - // Get all CSV bookings with documents - const result = await pgClient.query( - `SELECT id, documents FROM csv_bookings WHERE documents IS NOT NULL` - ); - - console.log(`\n📄 Found ${result.rows.length} bookings with documents\n`); - - let uploadedCount = 0; - - for (const row of result.rows) { - const bookingId = row.id; - const documents = row.documents; - - console.log(`\n📦 Processing booking: ${bookingId}`); - - for (const doc of documents) { - if (!doc.filePath || !doc.filePath.includes(MINIO_ENDPOINT)) { - console.log(` ⏭️ Skipping document (not a MinIO URL): ${doc.fileName}`); - continue; - } - - // Extract the S3 key from the URL - const url = new URL(doc.filePath); - const key = url.pathname.substring(1).replace(`${BUCKET_NAME}/`, ''); - - // Create test PDF content - const pdfContent = createTestPDF(doc.fileName || 'Test Document'); - - try { - // Upload to MinIO - await s3Client.send( - new PutObjectCommand({ - Bucket: BUCKET_NAME, - Key: key, - Body: pdfContent, - ContentType: doc.mimeType || 'application/pdf', - }) - ); - - console.log(` ✅ Uploaded: ${doc.fileName}`); - console.log(` Path: ${key}`); - uploadedCount++; - } catch (error) { - console.error(` ❌ Failed to upload ${doc.fileName}:`, error.message); - } - } - } - - console.log(`\n🎉 Successfully uploaded ${uploadedCount} test documents to MinIO`); - console.log(`\n📍 MinIO Console: http://localhost:9001`); - console.log(` Username: minioadmin`); - console.log(` Password: minioadmin`); - } catch (error) { - console.error('❌ Error:', error); - throw error; - } finally { - await pgClient.end(); - console.log('\n👋 Disconnected from database'); - } -} - -uploadTestDocuments() - .then(() => { - console.log('\n✅ Script completed successfully'); - process.exit(0); - }) - .catch((error) => { - console.error('\n❌ Script failed:', error); - process.exit(1); - }); +/** + * Script to upload test documents to MinIO + */ + +const { S3Client, PutObjectCommand, CreateBucketCommand } = require('@aws-sdk/client-s3'); +const { Client: PgClient } = require('pg'); +const fs = require('fs'); +const path = require('path'); +require('dotenv').config(); + +const MINIO_ENDPOINT = process.env.AWS_S3_ENDPOINT || 'http://localhost:9000'; +const BUCKET_NAME = 'xpeditis-documents'; + +// Initialize MinIO client +const s3Client = new S3Client({ + region: 'us-east-1', + endpoint: MINIO_ENDPOINT, + credentials: { + accessKeyId: process.env.AWS_ACCESS_KEY_ID || 'minioadmin', + secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY || 'minioadmin', + }, + forcePathStyle: true, +}); + +// Create a simple PDF buffer (minimal valid PDF) +function createTestPDF(title) { + return Buffer.from( + `%PDF-1.4 +1 0 obj +<< +/Type /Catalog +/Pages 2 0 R +>> +endobj +2 0 obj +<< +/Type /Pages +/Kids [3 0 R] +/Count 1 +>> +endobj +3 0 obj +<< +/Type /Page +/Parent 2 0 R +/MediaBox [0 0 612 792] +/Contents 4 0 R +/Resources << +/Font << +/F1 << +/Type /Font +/Subtype /Type1 +/BaseFont /Helvetica +>> +>> +>> +>> +endobj +4 0 obj +<< +/Length 100 +>> +stream +BT +/F1 24 Tf +100 700 Td +(${title}) Tj +ET +endstream +endobj +xref +0 5 +0000000000 65535 f +0000000009 00000 n +0000000058 00000 n +0000000115 00000 n +0000000300 00000 n +trailer +<< +/Size 5 +/Root 1 0 R +>> +startxref +450 +%%EOF`, + 'utf-8' + ); +} + +async function uploadTestDocuments() { + const pgClient = new PgClient({ + host: process.env.DATABASE_HOST || 'localhost', + port: process.env.DATABASE_PORT || 5432, + user: process.env.DATABASE_USER || 'xpeditis', + password: process.env.DATABASE_PASSWORD || 'xpeditis_dev_password', + database: process.env.DATABASE_NAME || 'xpeditis_dev', + }); + + try { + // Connect to database + await pgClient.connect(); + console.log('✅ Connected to database'); + + // Create bucket if it doesn't exist + try { + await s3Client.send(new CreateBucketCommand({ Bucket: BUCKET_NAME })); + console.log(`✅ Created bucket: ${BUCKET_NAME}`); + } catch (error) { + if (error.name === 'BucketAlreadyOwnedByYou' || error.Code === 'BucketAlreadyOwnedByYou') { + console.log(`✅ Bucket already exists: ${BUCKET_NAME}`); + } else { + console.log(`⚠️ Could not create bucket (might already exist): ${error.message}`); + } + } + + // Get all CSV bookings with documents + const result = await pgClient.query( + `SELECT id, documents FROM csv_bookings WHERE documents IS NOT NULL` + ); + + console.log(`\n📄 Found ${result.rows.length} bookings with documents\n`); + + let uploadedCount = 0; + + for (const row of result.rows) { + const bookingId = row.id; + const documents = row.documents; + + console.log(`\n📦 Processing booking: ${bookingId}`); + + for (const doc of documents) { + if (!doc.filePath || !doc.filePath.includes(MINIO_ENDPOINT)) { + console.log(` ⏭️ Skipping document (not a MinIO URL): ${doc.fileName}`); + continue; + } + + // Extract the S3 key from the URL + const url = new URL(doc.filePath); + const key = url.pathname.substring(1).replace(`${BUCKET_NAME}/`, ''); + + // Create test PDF content + const pdfContent = createTestPDF(doc.fileName || 'Test Document'); + + try { + // Upload to MinIO + await s3Client.send( + new PutObjectCommand({ + Bucket: BUCKET_NAME, + Key: key, + Body: pdfContent, + ContentType: doc.mimeType || 'application/pdf', + }) + ); + + console.log(` ✅ Uploaded: ${doc.fileName}`); + console.log(` Path: ${key}`); + uploadedCount++; + } catch (error) { + console.error(` ❌ Failed to upload ${doc.fileName}:`, error.message); + } + } + } + + console.log(`\n🎉 Successfully uploaded ${uploadedCount} test documents to MinIO`); + console.log(`\n📍 MinIO Console: http://localhost:9001`); + console.log(` Username: minioadmin`); + console.log(` Password: minioadmin`); + } catch (error) { + console.error('❌ Error:', error); + throw error; + } finally { + await pgClient.end(); + console.log('\n👋 Disconnected from database'); + } +} + +uploadTestDocuments() + .then(() => { + console.log('\n✅ Script completed successfully'); + process.exit(0); + }) + .catch(error => { + console.error('\n❌ Script failed:', error); + process.exit(1); + }); diff --git a/apps/frontend/app/[locale]/about/page.tsx b/apps/frontend/app/[locale]/about/page.tsx index 105bf0d..a49585e 100644 --- a/apps/frontend/app/[locale]/about/page.tsx +++ b/apps/frontend/app/[locale]/about/page.tsx @@ -90,7 +90,10 @@ export default function AboutPage() {- {t('mission.body')} -
+{t('mission.body')}
- {t('vision.body')} -
+{t('vision.body')}
@@ -186,11 +185,7 @@ export default function AboutPage() { >- {t('valuesSubtitle')} -
+{t('valuesSubtitle')}
{t(`values.${value.key}.description`)}
); @@ -259,10 +256,10 @@ export default function AboutPage() { transition={{ duration: 0.8 }} className="text-center mb-16" > -- {t('timelineSubtitle')} -
+{t('timelineSubtitle')}
{t(`timeline.${year}.description`)}
- {t('teamSubtitle')} -
+{t('teamSubtitle')}
{t(`team.${member.key}.role`)}
++ {t(`team.${member.key}.role`)} +
{t(`team.${member.key}.bio`)}
- {t('cta.body')} -
+{t('cta.body')}
+ Cet article n'existe pas ou n'est pas encore publié. +
+ + +{post.excerpt}
+ +- {t('successHeadline')} -
-- {t('successBody')} -
+{t('successHeadline')}
+{t('successBody')}
- {t('errorContact')} -
+{t('errorContact')}
- {t('rejectedHeadline')} -
-- {t('rejectedBody')} -
+{t('rejectedHeadline')}
+{t('rejectedBody')}
- {t('infoBody')} -
+{t('infoBody')}
- {t('formIntro')} -
+{t('formIntro')}
- {t('reasonHint')} -
- - {reason.length}/500 - +{t('reasonHint')}
+ {reason.length}/500- {t('helpText')} -
+{t('helpText')}
); diff --git a/apps/frontend/app/[locale]/careers/page.tsx b/apps/frontend/app/[locale]/careers/page.tsx index 1af1e75..5437892 100644 --- a/apps/frontend/app/[locale]/careers/page.tsx +++ b/apps/frontend/app/[locale]/careers/page.tsx @@ -64,15 +64,76 @@ type JobRecord = { }; const JOBS: JobRecord[] = [ - { id: 1, key: 'frontend', department: 'Engineering', location: 'Paris', type: 'CDI', remote: true, salary: '65K - 85K €', icon: Code }, - { id: 2, key: 'backend', department: 'Engineering', location: 'Paris', type: 'CDI', remote: true, salary: '55K - 75K €', icon: Code }, - { id: 3, key: 'pm', department: 'Product', location: 'Paris', type: 'CDI', remote: true, salary: '60K - 80K €', icon: LineChart }, - { id: 4, key: 'ae', department: 'Sales', location: 'Rotterdam', type: 'CDI', remote: false, salary: '50K - 70K € + variable', icon: Megaphone }, - { id: 5, key: 'csm', department: 'Customer Success', location: 'Paris', type: 'CDI', remote: true, salary: '45K - 60K €', icon: Headphones }, - { id: 6, key: 'data', department: 'Data', location: 'Hambourg', type: 'CDI', remote: true, salary: '50K - 65K €', icon: LineChart }, + { + id: 1, + key: 'frontend', + department: 'Engineering', + location: 'Paris', + type: 'CDI', + remote: true, + salary: '65K - 85K €', + icon: Code, + }, + { + id: 2, + key: 'backend', + department: 'Engineering', + location: 'Paris', + type: 'CDI', + remote: true, + salary: '55K - 75K €', + icon: Code, + }, + { + id: 3, + key: 'pm', + department: 'Product', + location: 'Paris', + type: 'CDI', + remote: true, + salary: '60K - 80K €', + icon: LineChart, + }, + { + id: 4, + key: 'ae', + department: 'Sales', + location: 'Rotterdam', + type: 'CDI', + remote: false, + salary: '50K - 70K € + variable', + icon: Megaphone, + }, + { + id: 5, + key: 'csm', + department: 'Customer Success', + location: 'Paris', + type: 'CDI', + remote: true, + salary: '45K - 60K €', + icon: Headphones, + }, + { + id: 6, + key: 'data', + department: 'Data', + location: 'Hambourg', + type: 'CDI', + remote: true, + salary: '50K - 65K €', + icon: LineChart, + }, ]; -const DEPARTMENT_VALUES: DepartmentValue[] = ['all', 'Engineering', 'Product', 'Sales', 'Customer Success', 'Data']; +const DEPARTMENT_VALUES: DepartmentValue[] = [ + 'all', + 'Engineering', + 'Product', + 'Sales', + 'Customer Success', + 'Data', +]; const LOCATION_VALUES: LocationValue[] = ['all', 'Paris', 'Rotterdam', 'Hambourg']; const JOB_REQ_KEYS = ['req1', 'req2', 'req3', 'req4'] as const; @@ -92,7 +153,7 @@ export default function CareersPage() { const isJobsInView = useInView(jobsRef, { once: true }); const isCultureInView = useInView(cultureRef, { once: true }); - const filteredJobs = JOBS.filter((job) => { + const filteredJobs = JOBS.filter(job => { const departmentMatch = selectedDepartment === 'all' || job.department === selectedDepartment; const locationMatch = selectedLocation === 'all' || job.location === selectedLocation; return departmentMatch && locationMatch; @@ -124,7 +185,10 @@ export default function CareersPage() {- {t('benefitsSubtitle')} -
+{t('benefitsSubtitle')}
{t(`benefits.${benefit.key}.description`)}
); @@ -254,7 +318,10 @@ export default function CareersPage() {- {t('cultureBody')} -
+{t('cultureBody')}
- {t('jobsSubtitle')} -
+{t('jobsSubtitle')}
{/* Filters */} @@ -332,12 +395,14 @@ export default function CareersPage() {{t(`jobs.${job.key}.description`)}
-- {t('cta.body')} -
+{t('cta.body')}
{ - setCountdown((prev) => { + setCountdown(prev => { if (prev <= 1) { clearInterval(timer); router.push('/'); @@ -91,12 +94,8 @@ export default function CarrierAcceptPage() {- {t('accept.loadingMessage')} -
+{t('accept.loadingMessage')}
- {t('accept.successHeadline')} -
-- {t('accept.successBody')} -
+{t('accept.successHeadline')}
+{t('accept.successBody')}
- {t('common.redirecting', { countdown })} -
+{t('common.redirecting', { countdown })}
- {t('password.intro')} -
+{t('password.intro')}
{requirements.bookingNumber && (- {t('password.bookingLabel')} {requirements.bookingNumber} + {t('password.bookingLabel')}{' '} + {requirements.bookingNumber}
)}{t('summary.transit')}
-{t('summary.transitDays', { count: booking.transitDays })}
++ {t('summary.transitDays', { count: booking.transitDays })} +
{t('list.empty')}
-- {t('list.emptyHint')} -
+{t('list.emptyHint')}
- {t('footerNote')} -
+{t('footerNote')}
{/* Footer */} diff --git a/apps/frontend/app/[locale]/carrier/reject/[token]/page.tsx b/apps/frontend/app/[locale]/carrier/reject/[token]/page.tsx index be68300..9e0b3e4 100644 --- a/apps/frontend/app/[locale]/carrier/reject/[token]/page.tsx +++ b/apps/frontend/app/[locale]/carrier/reject/[token]/page.tsx @@ -55,7 +55,10 @@ export default function CarrierRejectPage() { errorMessage = t('common.bookingAlreadyRejected'); } else if (errorMessage.includes('status ACCEPTED')) { errorMessage = t('common.bookingAlreadyAccepted'); - } else if (errorMessage.includes('not found') || errorMessage.includes('Booking not found')) { + } else if ( + errorMessage.includes('not found') || + errorMessage.includes('Booking not found') + ) { errorMessage = t('common.bookingNotFound'); } @@ -65,7 +68,7 @@ export default function CarrierRejectPage() { setLoading(false); const timer = setInterval(() => { - setCountdown((prev) => { + setCountdown(prev => { if (prev <= 1) { clearInterval(timer); router.push('/'); @@ -91,12 +94,8 @@ export default function CarrierRejectPage() {- {t('reject.loadingMessage')} -
+{t('reject.loadingMessage')}
- {t('reject.successHeadline')} -
-- {t('reject.successBody')} -
+{t('reject.successHeadline')}
+{t('reject.successBody')}
- {t('common.redirecting', { countdown })} -
+{t('common.redirecting', { countdown })}