diff --git a/package-lock.json b/package-lock.json index 1e30002..fddca82 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,18 @@ "version": "0.1.0", "dependencies": { "@react-email/components": "^0.0.11", + "@tiptap/extension-link": "^2.1.12", + "@tiptap/extension-list-item": "^2.1.12", + "@tiptap/extension-placeholder": "^2.1.12", + "@tiptap/extension-subscript": "^2.1.12", + "@tiptap/extension-superscript": "^2.1.12", + "@tiptap/extension-text-style": "^2.1.12", + "@tiptap/extension-underline": "^2.1.12", + "@tiptap/pm": "^2.1.12", + "@tiptap/react": "^2.1.12", + "@tiptap/starter-kit": "^2.1.12", "bcrypt": "^5.1.1", + "html-react-parser": "^5.0.6", "mongodb": "^6.3.0", "next": "14.0.1", "next-auth": "^4.24.5", @@ -17,9 +28,12 @@ "react": "^18", "react-dom": "^18", "react-email": "^1.9.5", - "react-icons": "^4.11.0" + "react-icons": "^4.11.0", + "react-inlinesvg": "^4.1.0", + "swr": "^2.2.4" }, "devDependencies": { + "@tailwindcss/typography": "^0.5.10", "autoprefixer": "^10.0.1", "eslint": "^8", "eslint-config-next": "14.0.1", @@ -1247,6 +1261,15 @@ "node": ">=14" } }, + "node_modules/@popperjs/core": { + "version": "2.11.8", + "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", + "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/popperjs" + } + }, "node_modules/@radix-ui/react-compose-refs": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz", @@ -1502,6 +1525,50 @@ "react": "18.2.0" } }, + "node_modules/@remirror/core-constants": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@remirror/core-constants/-/core-constants-2.0.2.tgz", + "integrity": "sha512-dyHY+sMF0ihPus3O27ODd4+agdHMEmuRdyiZJ2CCWjPV5UFmn17ZbElvk6WOGVE4rdCJKZQCrPV2BcikOMLUGQ==" + }, + "node_modules/@remirror/core-helpers": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/@remirror/core-helpers/-/core-helpers-3.0.0.tgz", + "integrity": "sha512-tusEgQJIqg4qKj6HSBUFcyRnWnziw3neh4T9wOmsPGHFC3w9kl5KSrDb9UAgE8uX6y32FnS7vJ955mWOl3n50A==", + "dependencies": { + "@remirror/core-constants": "^2.0.2", + "@remirror/types": "^1.0.1", + "@types/object.omit": "^3.0.0", + "@types/object.pick": "^1.3.2", + "@types/throttle-debounce": "^2.1.0", + "case-anything": "^2.1.13", + "dash-get": "^1.0.2", + "deepmerge": "^4.3.1", + "fast-deep-equal": "^3.1.3", + "make-error": "^1.3.6", + "object.omit": "^3.0.0", + "object.pick": "^1.3.0", + "throttle-debounce": "^3.0.1" + } + }, + "node_modules/@remirror/types": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@remirror/types/-/types-1.0.1.tgz", + "integrity": "sha512-VlZQxwGnt1jtQ18D6JqdIF+uFZo525WEqrfp9BOc3COPpK4+AWCgdnAWL+ho6imWcoINlGjR/+3b6y5C1vBVEA==", + "dependencies": { + "type-fest": "^2.19.0" + } + }, + "node_modules/@remirror/types/node_modules/type-fest": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-2.19.0.tgz", + "integrity": "sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==", + "engines": { + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@rushstack/eslint-patch": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/@rushstack/eslint-patch/-/eslint-patch-1.5.1.tgz", @@ -1528,6 +1595,454 @@ "tslib": "^2.4.0" } }, + "node_modules/@tailwindcss/typography": { + "version": "0.5.10", + "resolved": "https://registry.npmjs.org/@tailwindcss/typography/-/typography-0.5.10.tgz", + "integrity": "sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==", + "dev": true, + "dependencies": { + "lodash.castarray": "^4.4.0", + "lodash.isplainobject": "^4.0.6", + "lodash.merge": "^4.6.2", + "postcss-selector-parser": "6.0.10" + }, + "peerDependencies": { + "tailwindcss": ">=3.0.0 || insiders" + } + }, + "node_modules/@tailwindcss/typography/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==", + "dev": true, + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/@tiptap/core": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.1.12.tgz", + "integrity": "sha512-ZGc3xrBJA9KY8kln5AYTj8y+GDrKxi7u95xIl2eccrqTY5CQeRu6HRNM1yT4mAjuSaG9jmazyjGRlQuhyxCKxQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.1.12.tgz", + "integrity": "sha512-Qb3YRlCfugx9pw7VgLTb+jY37OY4aBJeZnqHzx4QThSm13edNYjasokbX0nTwL1Up4NPTcY19JUeHt6fVaVVGg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.1.12.tgz", + "integrity": "sha512-AZGxIxcGU1/y6V2YEbKsq6BAibL8yQrbRm6EdcBnby41vj1WziewEKswhLGmZx5IKM2r2ldxld03KlfSIlKQZg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.1.12.tgz", + "integrity": "sha512-gAGi21EQ4wvLmT7klgariAc2Hf+cIjaNU2NWze3ut6Ku9gUo5ZLqj1t9SKHmNf4d5JG63O8GxpErqpA7lHlRtw==", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-bullet-list": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.1.12.tgz", + "integrity": "sha512-vtD8vWtNlmAZX8LYqt2yU9w3mU9rPCiHmbp4hDXJs2kBnI0Ju/qAyXFx6iJ3C3XyuMnMbJdDI9ee0spAvFz7cQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.1.12.tgz", + "integrity": "sha512-CRiRq5OTC1lFgSx6IMrECqmtb93a0ZZKujEnaRhzWliPBjLIi66va05f/P1vnV6/tHaC3yfXys6dxB5A4J8jxw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.1.12.tgz", + "integrity": "sha512-RXtSYCVsnk8D+K80uNZShClfZjvv1EgO42JlXLVGWQdIgaNyuOv/6I/Jdf+ZzhnpsBnHufW+6TJjwP5vJPSPHA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.1.12.tgz", + "integrity": "sha512-0QNfAkCcFlB9O8cUNSwTSIQMV9TmoEhfEaLz/GvbjwEq4skXK3bU+OQX7Ih07waCDVXIGAZ7YAZogbvrn/WbOw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.1.12.tgz", + "integrity": "sha512-0tT/q8nL4NBCYPxr9T0Brck+RQbWuczm9nV0bnxgt0IiQXoRHutfPWdS7GA65PTuVRBS/3LOco30fbjFhkfz/A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-floating-menu": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.1.12.tgz", + "integrity": "sha512-uo0ydCJNg6AWwLT6cMUJYVChfvw2PY9ZfvKRhh9YJlGfM02jS4RUG/bJBts6R37f+a5FsOvAVwg8EvqPlNND1A==", + "dependencies": { + "tippy.js": "^6.3.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-gapcursor": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.1.12.tgz", + "integrity": "sha512-zFYdZCqPgpwoB7whyuwpc8EYLYjUE5QYKb8vICvc+FraBUDM51ujYhFSgJC3rhs8EjI+8GcK8ShLbSMIn49YOQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-hard-break": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.1.12.tgz", + "integrity": "sha512-nqKcAYGEOafg9D+2cy1E4gHNGuL12LerVa0eS2SQOb+PT8vSel9OTKU1RyZldsWSQJ5rq/w4uIjmLnrSR2w6Yw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.1.12.tgz", + "integrity": "sha512-MoANP3POAP68Ko9YXarfDKLM/kXtscgp6m+xRagPAghRNujVY88nK1qBMZ3JdvTVN6b/ATJhp8UdrZX96TLV2w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-history": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.1.12.tgz", + "integrity": "sha512-6b7UFVkvPjq3LVoCTrYZAczt5sQrQUaoDWAieVClVZoFLfjga2Fwjcfgcie8IjdPt8YO2hG/sar/c07i9vM0Sg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-horizontal-rule": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.1.12.tgz", + "integrity": "sha512-RRuoK4KxrXRrZNAjJW5rpaxjiP0FJIaqpi7nFbAua2oHXgsCsG8qbW2Y0WkbIoS8AJsvLZ3fNGsQ8gpdliuq3A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.1.12.tgz", + "integrity": "sha512-/XYrW4ZEWyqDvnXVKbgTXItpJOp2ycswk+fJ3vuexyolO6NSs0UuYC6X4f+FbHYL5VuWqVBv7EavGa+tB6sl3A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.1.12.tgz", + "integrity": "sha512-Sti5hhlkCqi5vzdQjU/gbmr8kb578p+u0J4kWS+SSz3BknNThEm/7Id67qdjBTOQbwuN07lHjDaabJL0hSkzGQ==", + "dependencies": { + "linkifyjs": "^4.1.0" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-list-item": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.1.12.tgz", + "integrity": "sha512-Gk7hBFofAPmNQ8+uw8w5QSsZOMEGf7KQXJnx5B022YAUJTYYxO3jYVuzp34Drk9p+zNNIcXD4kc7ff5+nFOTrg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.1.12.tgz", + "integrity": "sha512-tF6VGl+D2avCgn9U/2YLJ8qVmV6sPE/iEzVAFZuOSe6L0Pj7SQw4K6AO640QBob/d8VrqqJFHCb6l10amJOnXA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.1.12.tgz", + "integrity": "sha512-hoH/uWPX+KKnNAZagudlsrr4Xu57nusGekkJWBcrb5MCDE91BS+DN2xifuhwXiTHxnwOMVFjluc0bPzQbkArsw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-placeholder": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-placeholder/-/extension-placeholder-2.1.12.tgz", + "integrity": "sha512-K52o7B1zkP4vaVy3z4ZwHn+tQy6KlXtedj1skLg+796ImwH2GYS5z6MFOTfKzBO2hLncUzLco/s0C5PLCD6SDw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.1.12.tgz", + "integrity": "sha512-HlhrzIjYUT8oCH9nYzEL2QTTn8d1ECnVhKvzAe6x41xk31PjLMHTUy8aYjeQEkWZOWZ34tiTmslV1ce6R3Dt8g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-subscript": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-subscript/-/extension-subscript-2.1.12.tgz", + "integrity": "sha512-tb1jysEvf4SIiXwEOgDTXiyrG39RVNHvn/zsGMg5wy5t9qUp9m1k7kKYTH084ktuKDAPQonCcpn3hwc+ngTFzg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-superscript": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-superscript/-/extension-superscript-2.1.12.tgz", + "integrity": "sha512-ek6L+DNsrjiJieArlgTvQt1VfJ56d8V19WAPW/ciRhq88YRlTEY9nSO3QuUCSUO1nGmE5OWQpgrsiW/XZbONVw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.1.12.tgz", + "integrity": "sha512-rCNUd505p/PXwU9Jgxo4ZJv4A3cIBAyAqlx/dtcY6cjztCQuXJhuQILPhjGhBTOLEEL4kW2wQtqzCmb7O8i2jg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-text-style": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.1.12.tgz", + "integrity": "sha512-nfjWXX0JSRHLcscfiMESh+RN+Z7bG8nio/C9+8yQASM90VxU9f8oKgF8HnnSYsSrD4lLf44Q6XjmB7aMVUuikg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.1.12.tgz", + "integrity": "sha512-NwwdhFT8gDD0VUNLQx85yFBhP9a8qg8GPuxlGzAP/lPTV8Ubh3vSeQ5N9k2ZF/vHlEvnugzeVCbmYn7wf8vn1g==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/pm": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.1.12.tgz", + "integrity": "sha512-Q3MXXQABG4CZBesSp82yV84uhJh/W0Gag6KPm2HRWPimSFELM09Z9/5WK9RItAYE0aLhe4Krnyiczn9AAa1tQQ==", + "dependencies": { + "prosemirror-changeset": "^2.2.0", + "prosemirror-collab": "^1.3.0", + "prosemirror-commands": "^1.3.1", + "prosemirror-dropcursor": "^1.5.0", + "prosemirror-gapcursor": "^1.3.1", + "prosemirror-history": "^1.3.0", + "prosemirror-inputrules": "^1.2.0", + "prosemirror-keymap": "^1.2.0", + "prosemirror-markdown": "^1.10.1", + "prosemirror-menu": "^1.2.1", + "prosemirror-model": "^1.18.1", + "prosemirror-schema-basic": "^1.2.0", + "prosemirror-schema-list": "^1.2.2", + "prosemirror-state": "^1.4.1", + "prosemirror-tables": "^1.3.0", + "prosemirror-trailing-node": "^2.0.2", + "prosemirror-transform": "^1.7.0", + "prosemirror-view": "^1.28.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/react": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.1.12.tgz", + "integrity": "sha512-RMO4QmmpL7sPR7w8o1Wq0hrUe/ttHzsn5I/eWwqg1d3fGx5y9mOdfCoQ9XBtm49Xzdejy3QVzt4zYp9fX0X/xg==", + "dependencies": { + "@tiptap/extension-bubble-menu": "^2.1.12", + "@tiptap/extension-floating-menu": "^2.1.12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0", + "react": "^17.0.0 || ^18.0.0", + "react-dom": "^17.0.0 || ^18.0.0" + } + }, + "node_modules/@tiptap/starter-kit": { + "version": "2.1.12", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.1.12.tgz", + "integrity": "sha512-+RoP1rWV7rSCit2+3wl2bjvSRiePRJE/7YNKbvH8Faz/+AMO23AFegHoUFynR7U0ouGgYDljGkkj35e0asbSDA==", + "dependencies": { + "@tiptap/core": "^2.1.12", + "@tiptap/extension-blockquote": "^2.1.12", + "@tiptap/extension-bold": "^2.1.12", + "@tiptap/extension-bullet-list": "^2.1.12", + "@tiptap/extension-code": "^2.1.12", + "@tiptap/extension-code-block": "^2.1.12", + "@tiptap/extension-document": "^2.1.12", + "@tiptap/extension-dropcursor": "^2.1.12", + "@tiptap/extension-gapcursor": "^2.1.12", + "@tiptap/extension-hard-break": "^2.1.12", + "@tiptap/extension-heading": "^2.1.12", + "@tiptap/extension-history": "^2.1.12", + "@tiptap/extension-horizontal-rule": "^2.1.12", + "@tiptap/extension-italic": "^2.1.12", + "@tiptap/extension-list-item": "^2.1.12", + "@tiptap/extension-ordered-list": "^2.1.12", + "@tiptap/extension-paragraph": "^2.1.12", + "@tiptap/extension-strike": "^2.1.12", + "@tiptap/extension-text": "^2.1.12" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, "node_modules/@types/json5": { "version": "0.0.29", "resolved": "https://registry.npmjs.org/@types/json5/-/json5-0.0.29.tgz", @@ -1539,6 +2054,21 @@ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==" }, + "node_modules/@types/object.omit": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/object.omit/-/object.omit-3.0.3.tgz", + "integrity": "sha512-xrq4bQTBGYY2cw+gV4PzoG2Lv3L0pjZ1uXStRRDQoATOYW1lCsFQHhQ+OkPhIcQoqLjAq7gYif7D14Qaa6Zbew==" + }, + "node_modules/@types/object.pick": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/@types/object.pick/-/object.pick-1.3.4.tgz", + "integrity": "sha512-5PjwB0uP2XDp3nt5u5NJAG2DORHIRClPzWT/TTZhJ2Ekwe8M5bA9tvPdi9NO/n2uvu2/ictat8kgqvLfcIE1SA==" + }, + "node_modules/@types/throttle-debounce": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@types/throttle-debounce/-/throttle-debounce-2.1.0.tgz", + "integrity": "sha512-5eQEtSCoESnh2FsiLTxE121IiE60hnMqcb435fShf4bpLRjEu1Eoekht23y6zXS9Ts3l+Szu3TARnTsA0GkOkQ==" + }, "node_modules/@types/webidl-conversions": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/@types/webidl-conversions/-/webidl-conversions-7.0.3.tgz", @@ -1804,8 +2334,7 @@ "node_modules/argparse": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/aria-query": { "version": "5.3.0", @@ -2272,6 +2801,17 @@ } ] }, + "node_modules/case-anything": { + "version": "2.1.13", + "resolved": "https://registry.npmjs.org/case-anything/-/case-anything-2.1.13.tgz", + "integrity": "sha512-zlOQ80VrQ2Ue+ymH5OuM/DlDq64mEm+B9UTdHULv5osUMD6HalNTblf2b1u/m6QecjsnOkBpqVZ+XPwIVsy7Ng==", + "engines": { + "node": ">=12.13" + }, + "funding": { + "url": "https://github.com/sponsors/mesqueeb" + } + }, "node_modules/chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", @@ -2481,6 +3021,11 @@ "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==" }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==" + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -2511,6 +3056,11 @@ "integrity": "sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==", "dev": true }, + "node_modules/dash-get": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/dash-get/-/dash-get-1.0.2.tgz", + "integrity": "sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==" + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -3102,7 +3652,6 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, "engines": { "node": ">=10" }, @@ -3592,8 +4141,7 @@ "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", - "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" }, "node_modules/fast-folder-size": { "version": "1.6.1", @@ -4174,6 +4722,47 @@ "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz", "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==" }, + "node_modules/html-dom-parser": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.0.4.tgz", + "integrity": "sha512-azy8THLKd4Ar0OVJpEgX+MSjYvKdNDWlGiRBIlovMqEQYMAnLLXBhhiSwjylDD3RDdcCYT8Utg6uoRDeLHUyHg==", + "dependencies": { + "domhandler": "5.0.3", + "htmlparser2": "9.0.0" + } + }, + "node_modules/html-dom-parser/node_modules/htmlparser2": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-9.0.0.tgz", + "integrity": "sha512-uxbSI98wmFT/G4P2zXx4OVx04qWUmyFPrD2/CNepa2Zo3GPNaCaaxElDgwUrwYWkK1nr9fft0Ya8dws8coDLLQ==", + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.1.0", + "entities": "^4.5.0" + } + }, + "node_modules/html-react-parser": { + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/html-react-parser/-/html-react-parser-5.0.6.tgz", + "integrity": "sha512-aPSzFhO2bK/L/mYQbMwFz+1QG8nhxozbwENt/52HTApasrBvDX87MFD5uSQIjq7Io0bnKzH4uh7PM42zZ4ag9A==", + "dependencies": { + "domhandler": "5.0.3", + "html-dom-parser": "5.0.4", + "react-property": "2.0.2", + "style-to-js": "1.1.9" + }, + "peerDependencies": { + "react": "0.14 || 15 || 16 || 17 || 18" + } + }, "node_modules/html-to-text": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/html-to-text/-/html-to-text-9.0.5.tgz", @@ -4314,6 +4903,11 @@ "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" }, + "node_modules/inline-style-parser": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.2.tgz", + "integrity": "sha512-EcKzdTHVe8wFVOGEYXiW9WmJXPjqi1T+234YpJr98RiFYKHV3cdy1+3mkTE+KHTHxFFLH51SfaGOoUdW+v7ViQ==" + }, "node_modules/internal-slot": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", @@ -4745,6 +5339,14 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/iterator.prototype": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/iterator.prototype/-/iterator.prototype-1.1.2.tgz", @@ -5030,6 +5632,19 @@ "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" }, + "node_modules/linkify-it": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-4.0.1.tgz", + "integrity": "sha512-C7bfi1UZmoj8+PQx22XyeXCuBlokoyWQL5pWSP+EI6nzRylyThouddufc2c1NDIcP9k5agmN9fLpA7VNJfIiqw==", + "dependencies": { + "uc.micro": "^1.0.1" + } + }, + "node_modules/linkifyjs": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/linkifyjs/-/linkifyjs-4.1.3.tgz", + "integrity": "sha512-auMesunaJ8yfkHvK4gfg1K0SaKX/6Wn9g2Aac/NwX+l5VdmFZzo/hdPGxEOETj+ryRa4/fiOPjeeKURSAJx1sg==" + }, "node_modules/lint-staged": { "version": "15.0.2", "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-15.0.2.tgz", @@ -5124,6 +5739,18 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/lodash.castarray": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/lodash.castarray/-/lodash.castarray-4.4.0.tgz", + "integrity": "sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", @@ -5235,6 +5862,42 @@ "semver": "bin/semver.js" } }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "node_modules/markdown-it": { + "version": "13.0.2", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-13.0.2.tgz", + "integrity": "sha512-FtwnEuuK+2yVU7goGn/MJ0WBZMM9ZPgU9spqlFs7/A/pDIUNSOQZhUgOqYCficIuR2QaFnrt8LHqBWsbTAoI5w==", + "dependencies": { + "argparse": "^2.0.1", + "entities": "~3.0.1", + "linkify-it": "^4.0.1", + "mdurl": "^1.0.1", + "uc.micro": "^1.0.5" + }, + "bin": { + "markdown-it": "bin/markdown-it.js" + } + }, + "node_modules/markdown-it/node_modules/entities": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-3.0.1.tgz", + "integrity": "sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q==", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/mdurl": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", + "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + }, "node_modules/memory-pager": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/memory-pager/-/memory-pager-1.5.0.tgz", @@ -5760,6 +6423,50 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/object.omit": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/object.omit/-/object.omit-3.0.0.tgz", + "integrity": "sha512-EO+BCv6LJfu+gBIF3ggLicFebFLN5zqzz/WWJlMFfkMyGth+oBkhxzDl0wx2W4GkLzuQs/FsSkXZb2IMWQqmBQ==", + "dependencies": { + "is-extendable": "^1.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.omit/node_modules/is-extendable": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", + "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", + "dependencies": { + "is-plain-object": "^2.0.4" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.omit/node_modules/is-plain-object": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", + "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object.pick": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "dependencies": { + "isobject": "^3.0.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/object.values": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/object.values/-/object.values-1.1.7.tgz", @@ -5919,6 +6626,11 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==" }, + "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==" + }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", @@ -6416,6 +7128,183 @@ "react-is": "^16.13.1" } }, + "node_modules/prosemirror-changeset": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/prosemirror-changeset/-/prosemirror-changeset-2.2.1.tgz", + "integrity": "sha512-J7msc6wbxB4ekDFj+n9gTW/jav/p53kdlivvuppHsrZXCaQdVgRghoZbSS3kwrRyAstRVQ4/+u5k7YfLgkkQvQ==", + "dependencies": { + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-collab": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/prosemirror-collab/-/prosemirror-collab-1.3.1.tgz", + "integrity": "sha512-4SnynYR9TTYaQVXd/ieUvsVV4PDMBzrq2xPUWutHivDuOshZXqQ5rGbZM84HEaXKbLdItse7weMGOUdDVcLKEQ==", + "dependencies": { + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-commands": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/prosemirror-commands/-/prosemirror-commands-1.5.2.tgz", + "integrity": "sha512-hgLcPaakxH8tu6YvVAaILV2tXYsW3rAdDR8WNkeKGcgeMVQg3/TMhPdVoh7iAmfgVjZGtcOSjKiQaoeKjzd2mQ==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-dropcursor": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/prosemirror-dropcursor/-/prosemirror-dropcursor-1.8.1.tgz", + "integrity": "sha512-M30WJdJZLyXHi3N8vxN6Zh5O8ZBbQCz0gURTfPmTIBNQ5pxrdU7A58QkNqfa98YEjSAL1HUyyU34f6Pm5xBSGw==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0", + "prosemirror-view": "^1.1.0" + } + }, + "node_modules/prosemirror-gapcursor": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/prosemirror-gapcursor/-/prosemirror-gapcursor-1.3.2.tgz", + "integrity": "sha512-wtjswVBd2vaQRrnYZaBCbyDqr232Ed4p2QPtRIUK5FuqHYKGWkEwl08oQM4Tw7DOR0FsasARV5uJFvMZWxdNxQ==", + "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.3.2", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.3.2.tgz", + "integrity": "sha512-/zm0XoU/N/+u7i5zepjmZAEnpvjDtzoPWW6VmKptcAnPadN/SStsBjMImdCEbb3seiNTpveziPTIrXQbHLtU1g==", + "dependencies": { + "prosemirror-state": "^1.2.2", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.31.0", + "rope-sequence": "^1.3.0" + } + }, + "node_modules/prosemirror-inputrules": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.3.0.tgz", + "integrity": "sha512-z1GRP2vhh5CihYMQYsJSa1cOwXb3SYxALXOIfAkX8nZserARtl9LiL+CEl+T+OFIsXc3mJIHKhbsmRzC0HDAXA==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.0.0" + } + }, + "node_modules/prosemirror-keymap": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/prosemirror-keymap/-/prosemirror-keymap-1.2.2.tgz", + "integrity": "sha512-EAlXoksqC6Vbocqc0GtzCruZEzYgrn+iiGnNjsJsH4mrnIGex4qbLdWWNza3AW5W36ZRrlBID0eM6bdKH4OStQ==", + "dependencies": { + "prosemirror-state": "^1.0.0", + "w3c-keyname": "^2.2.0" + } + }, + "node_modules/prosemirror-markdown": { + "version": "1.11.2", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.11.2.tgz", + "integrity": "sha512-Eu5g4WPiCdqDTGhdSsG9N6ZjACQRYrsAkrF9KYfdMaCmjIApH75aVncsWYOJvEk2i1B3i8jZppv3J/tnuHGiUQ==", + "dependencies": { + "markdown-it": "^13.0.1", + "prosemirror-model": "^1.0.0" + } + }, + "node_modules/prosemirror-menu": { + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/prosemirror-menu/-/prosemirror-menu-1.2.4.tgz", + "integrity": "sha512-S/bXlc0ODQup6aiBbWVsX/eM+xJgCTAfMq/nLqaO5ID/am4wS0tTCIkzwytmao7ypEtjj39i7YbJjAgO20mIqA==", + "dependencies": { + "crelt": "^1.0.0", + "prosemirror-commands": "^1.0.0", + "prosemirror-history": "^1.0.0", + "prosemirror-state": "^1.0.0" + } + }, + "node_modules/prosemirror-model": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.19.3.tgz", + "integrity": "sha512-tgSnwN7BS7/UM0sSARcW+IQryx2vODKX4MI7xpqY2X+iaepJdKBPc7I4aACIsDV/LTaTjt12Z56MhDr9LsyuZQ==", + "dependencies": { + "orderedmap": "^2.0.0" + } + }, + "node_modules/prosemirror-schema-basic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/prosemirror-schema-basic/-/prosemirror-schema-basic-1.2.2.tgz", + "integrity": "sha512-/dT4JFEGyO7QnNTe9UaKUhjDXbTNkiWTq/N4VpKaF79bBjSExVV2NXmJpcM7z/gD7mbqNjxbmWW5nf1iNSSGnw==", + "dependencies": { + "prosemirror-model": "^1.19.0" + } + }, + "node_modules/prosemirror-schema-list": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/prosemirror-schema-list/-/prosemirror-schema-list-1.3.0.tgz", + "integrity": "sha512-Hz/7gM4skaaYfRPNgr421CU4GSwotmEwBVvJh5ltGiffUJwm7C8GfN/Bc6DR1EKEp5pDKhODmdXXyi9uIsZl5A==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.7.3" + } + }, + "node_modules/prosemirror-state": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/prosemirror-state/-/prosemirror-state-1.4.3.tgz", + "integrity": "sha512-goFKORVbvPuAQaXhpbemJFRKJ2aixr+AZMGiquiqKxaucC6hlpHNZHWgz5R7dS4roHiwq9vDctE//CZ++o0W1Q==", + "dependencies": { + "prosemirror-model": "^1.0.0", + "prosemirror-transform": "^1.0.0", + "prosemirror-view": "^1.27.0" + } + }, + "node_modules/prosemirror-tables": { + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.3.4.tgz", + "integrity": "sha512-z6uLSQ1BLC3rgbGwZmpfb+xkdvD7W/UOsURDfognZFYaTtc0gsk7u/t71Yijp2eLflVpffMk6X0u0+u+MMDvIw==", + "dependencies": { + "prosemirror-keymap": "^1.1.2", + "prosemirror-model": "^1.8.1", + "prosemirror-state": "^1.3.1", + "prosemirror-transform": "^1.2.1", + "prosemirror-view": "^1.13.3" + } + }, + "node_modules/prosemirror-trailing-node": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.7.tgz", + "integrity": "sha512-8zcZORYj/8WEwsGo6yVCRXFMOfBo0Ub3hCUvmoWIZYfMP26WqENU0mpEP27w7mt8buZWuGrydBewr0tOArPb1Q==", + "dependencies": { + "@remirror/core-constants": "^2.0.2", + "@remirror/core-helpers": "^3.0.0", + "escape-string-regexp": "^4.0.0" + }, + "peerDependencies": { + "prosemirror-model": "^1.19.0", + "prosemirror-state": "^1.4.2", + "prosemirror-view": "^1.31.2" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.8.0.tgz", + "integrity": "sha512-BaSBsIMv52F1BVVMvOmp1yzD3u65uC3HTzCBQV1WDPqJRQ2LuHKcyfn0jwqodo8sR9vVzMzZyI+Dal5W9E6a9A==", + "dependencies": { + "prosemirror-model": "^1.0.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.32.4", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.32.4.tgz", + "integrity": "sha512-WoT+ZYePp0WQvp5coABAysheZg9WttW3TSEUNgsfDQXmVOJlnjkbFbXicKPvWFLiC0ZjKt1ykbyoVKqhVnCiSQ==", + "dependencies": { + "prosemirror-model": "^1.16.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", @@ -6625,6 +7514,14 @@ "url": "https://ko-fi.com/killymxi" } }, + "node_modules/react-from-dom": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/react-from-dom/-/react-from-dom-0.6.2.tgz", + "integrity": "sha512-qvWWTL/4xw4k/Dywd41RBpLQUSq97csuv15qrxN+izNeLYlD9wn5W8LspbfYe5CWbaSdkZ72BsaYBPQf2x4VbQ==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/react-icons": { "version": "4.11.0", "resolved": "https://registry.npmjs.org/react-icons/-/react-icons-4.11.0.tgz", @@ -6633,12 +7530,28 @@ "react": "*" } }, + "node_modules/react-inlinesvg": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/react-inlinesvg/-/react-inlinesvg-4.1.0.tgz", + "integrity": "sha512-S7kSE6ldKVpumEYTNXmCs4pAKoDzklP6TklI8oBx23fw8/+VBwO5X0M7/Ii0weieO1DqElUJg5EI8cq7zo5oPg==", + "dependencies": { + "react-from-dom": "^0.6.2" + }, + "peerDependencies": { + "react": "16.8 - 18" + } + }, "node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "dev": true }, + "node_modules/react-property": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.2.tgz", + "integrity": "sha512-+PbtI3VuDV0l6CleQMsx2gtK0JZbZKbpdu5ynr+lbsuvtmgbNcS3VM0tuY2QjFNOcWxvXeHjDpy42RO+4U2rug==" + }, "node_modules/read-cache": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", @@ -6897,6 +7810,11 @@ "url": "https://github.com/sponsors/isaacs" } }, + "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==" + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -7400,6 +8318,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/style-to-js": { + "version": "1.1.9", + "resolved": "https://registry.npmjs.org/style-to-js/-/style-to-js-1.1.9.tgz", + "integrity": "sha512-6bkwhOlPOx+wKiHVlPTHjUqM4zDKv9pyccehB8zetZL0hhQ7MVp7UEWUsohjiMdxwhSsEdjMrEve+8qzUVmY4w==", + "dependencies": { + "style-to-object": "1.0.4" + } + }, + "node_modules/style-to-object": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/style-to-object/-/style-to-object-1.0.4.tgz", + "integrity": "sha512-KyNO6mfijxSnypdvEjeXlhvbGPSh0l1zBJp80n+ncBQvrEbSwBHwZCpo0xz6Q4AKSPfXowWwypCBAUAdfz3rFQ==", + "dependencies": { + "inline-style-parser": "0.2.2" + } + }, "node_modules/styled-jsx": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/styled-jsx/-/styled-jsx-5.1.1.tgz", @@ -7484,6 +8418,18 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/swr": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/swr/-/swr-2.2.4.tgz", + "integrity": "sha512-njiZ/4RiIhoOlAaLYDqwz5qH/KZXVilRLvomrx83HjzCWTfa+InyfAjv05PSFxnmLzZkNO9ZfvgoqzAaEI4sGQ==", + "dependencies": { + "client-only": "^0.0.1", + "use-sync-external-store": "^1.2.0" + }, + "peerDependencies": { + "react": "^16.11.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/tailwindcss": { "version": "3.3.5", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-3.3.5.tgz", @@ -7571,6 +8517,22 @@ "node": ">=0.8" } }, + "node_modules/throttle-debounce": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/throttle-debounce/-/throttle-debounce-3.0.1.tgz", + "integrity": "sha512-dTEWWNu6JmeVXY0ZYoPuH5cRIwc0MeGbJwah9KUNYSJwommQpCzTySTpEe8Gs1J23aeWEuAobe4Ag7EHVt/LOg==", + "engines": { + "node": ">=10" + } + }, + "node_modules/tippy.js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/tippy.js/-/tippy.js-6.3.7.tgz", + "integrity": "sha512-E1d3oP2emgJ9dRQZdf3Kkn0qJgI6ZLpyS5z6ZkY1DF3kaQaBsGZsndEpHwx+eC+tYM41HaSNvNtLx8tU57FzTQ==", + "dependencies": { + "@popperjs/core": "^2.9.0" + } + }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", @@ -7810,6 +8772,11 @@ "node": ">=14.17" } }, + "node_modules/uc.micro": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", + "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -7926,6 +8893,14 @@ "punycode": "^2.1.0" } }, + "node_modules/use-sync-external-store": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz", + "integrity": "sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA==", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0" + } + }, "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", @@ -7948,6 +8923,11 @@ "spdx-expression-parse": "^3.0.0" } }, + "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==" + }, "node_modules/watchpack": { "version": "2.4.0", "resolved": "https://registry.npmjs.org/watchpack/-/watchpack-2.4.0.tgz", diff --git a/package.json b/package.json index 18421c9..4dd5764 100644 --- a/package.json +++ b/package.json @@ -19,7 +19,18 @@ }, "dependencies": { "@react-email/components": "^0.0.11", + "@tiptap/extension-link": "^2.1.12", + "@tiptap/extension-list-item": "^2.1.12", + "@tiptap/extension-placeholder": "^2.1.12", + "@tiptap/extension-subscript": "^2.1.12", + "@tiptap/extension-superscript": "^2.1.12", + "@tiptap/extension-text-style": "^2.1.12", + "@tiptap/extension-underline": "^2.1.12", + "@tiptap/pm": "^2.1.12", + "@tiptap/react": "^2.1.12", + "@tiptap/starter-kit": "^2.1.12", "bcrypt": "^5.1.1", + "html-react-parser": "^5.0.6", "mongodb": "^6.3.0", "next": "14.0.1", "next-auth": "^4.24.5", @@ -27,9 +38,12 @@ "react": "^18", "react-dom": "^18", "react-email": "^1.9.5", - "react-icons": "^4.11.0" + "react-icons": "^4.11.0", + "react-inlinesvg": "^4.1.0", + "swr": "^2.2.4" }, "devDependencies": { + "@tailwindcss/typography": "^0.5.10", "autoprefixer": "^10.0.1", "eslint": "^8", "eslint-config-next": "14.0.1", diff --git a/src/app/api/answers/[id]/route.js b/src/app/api/answers/[id]/route.js new file mode 100644 index 0000000..40eb513 --- /dev/null +++ b/src/app/api/answers/[id]/route.js @@ -0,0 +1,297 @@ +import connectClient from "@/db/client"; +import { ObjectId } from "mongodb"; +import { NextResponse } from "next/server"; + +export async function GET(request, { params }) { + try { + const id = params?.id; + + if (!id) { + return NextResponse.json( + { success: false, message: "Bad request" }, + { status: 400 }, + ); + } + + const client = await connectClient(); + const db = client.db(process.env.MONGODB_DATABSE); + const collection = db.collection(process.env.MONGODB_COLLECTION_ANSWERS); + + const answers = await collection.find({ questionId: id }).toArray(); + + return NextResponse.json( + { + success: true, + message: "Answers have been successfully fetched", + answers, + }, + { status: 200, statusText: "OK" }, + ); + } catch (e) { + console.error(e); + return NextResponse( + { success: false, error: e }, + { status: 500, statusText: "Internal Server Error" }, + ); + } +} + +export async function POST(request, { params }) { + try { + const id = params?.id; + const { userId, userName, userImage, htmlContent } = await request.json(); + + if (!id || !userId || !userName || !userImage || !htmlContent) { + return NextResponse.json( + { success: false, message: "Bad request" }, + { status: 400 }, + ); + } + + const client = await connectClient(); + const db = client.db(process.env.MONGODB_DATABSE); + const answersCollection = db.collection( + process.env.MONGODB_COLLECTION_ANSWERS, + ); + const usersCollection = db.collection(process.env.MONGODB_COLLECTION_USERS); + + // Check if the user has already answered this question + if ( + await answersCollection.findOne({ + questionId: id, + userId, + }) + ) { + return NextResponse.json( + { + success: false, + message: "User has already answered this question", + }, + { status: 409, statusText: "Conflict" }, + ); + } + + const result = await answersCollection.insertOne({ + questionId: id, + userId, + userName, + userImage, + createdAt: new Date().toUTCString(), + answer: htmlContent, + edited: false, + upvotes: 0, + upvotesHistory: [], + }); + + await usersCollection.findOneAndUpdate( + { _id: new ObjectId(userId) }, + { $inc: { providedAnswers: 1 } }, + ); + + return NextResponse.json( + { + success: true, + message: `Answer with id ${result.insertedId} has been successfully created`, + }, + { status: 201, statusText: "Created" }, + ); + } catch (e) { + console.error(e); + return NextResponse.json( + { success: false, error: e }, + { status: 500, statusText: "Internal Server Error" }, + ); + } +} + +export async function PATCH(request, { params }) { + try { + const id = params?.id; + + if (!id) { + return NextResponse.json( + { success: false, message: "Bad request" }, + { status: 400 }, + ); + } + + const client = await connectClient(); + const db = client.db(process.env.MONGODB_DATABSE); + const answersCollection = db.collection( + process.env.MONGODB_COLLECTION_ANSWERS, + ); + const usersCollection = db.collection(process.env.MONGODB_COLLECTION_USERS); + + const { questionUserId, userId, htmlContent, upvotes, upvotesDirection } = + await request.json(); + + if (!questionUserId || !userId) { + return NextResponse.json( + { success: false, message: "Bad request" }, + { status: 400 }, + ); + } + + const updateFields = {}; + + if (htmlContent !== undefined) { + updateFields.answer = htmlContent; + updateFields.edited = true; + } + + if (upvotes !== undefined) { + if (!upvotesDirection) { + return NextResponse.json( + { success: false, message: "Bad request" }, + { status: 400 }, + ); + } + + const upvotesHistory = ( + await answersCollection.findOne({ + questionId: id, + userId: questionUserId, + }) + ).upvotesHistory; + + if ( + upvotesHistory.some( + (item) => + JSON.stringify(item) === + JSON.stringify({ id: userId, direction: upvotesDirection }), + ) + ) { + return NextResponse.json( + { + success: false, + message: `User with id ${userId} has already ${ + upvotesDirection === "up" ? "upvoted" : "downvoted" + } this answer before`, + }, + { status: 409, statusText: "Conflict" }, + ); + } + + const existingVote = upvotesHistory.find((item) => item.id === userId); + if (existingVote) { + // user has upvoted or downvoted before + updateFields.upvotes = + upvotesDirection === "up" ? upvotes + 2 : upvotes - 2; + + updateFields.upvotesHistory = upvotesHistory.map((item) => + item.id === userId + ? { id: userId, direction: upvotesDirection } + : item, + ); + } else { + // user has not upvoted or downvoted before + updateFields.upvotes = + upvotesDirection === "up" ? upvotes + 1 : upvotes - 1; + + updateFields.upvotesHistory = [ + ...upvotesHistory, + { id: userId, direction: upvotesDirection }, + ]; + } + } + + if (Object.keys(updateFields).length === 0) { + return NextResponse.json( + { + success: false, + message: "No valid fields provided for update", + }, + { status: 400, statusText: "Bad Request" }, + ); + } + + const result = await answersCollection.updateOne( + { questionId: id, userId: questionUserId }, + { $set: updateFields }, + ); + + console.log(updateFields.upvotes); + await usersCollection.findOneAndUpdate( + { _id: new ObjectId(userId) }, + { $inc: { totalUpvotesReceived: updateFields.upvotes } }, + ); + + if (result.modifiedCount === 1) { + return NextResponse.json( + { + success: true, + message: `Answer with id: ${id} successfully updated`, + }, + { status: 200, statusText: "OK" }, + ); + } else { + return NextResponse.json( + { + success: false, + message: `No answer found for this question with id: ${id} provided by user with id: ${userId}`, + }, + { status: 404, statusText: "Not Found" }, + ); + } + } catch (e) { + console.error(e); + return NextResponse.json( + { success: false, error: e }, + { status: 500, statusText: "Internal Server Error" }, + ); + } +} + +export async function DELETE(request, { params }) { + try { + const id = params?.id; + const { userId } = await request.json(); + + if (!id || !userId) { + return NextResponse.json( + { success: false, message: "Bad request" }, + { status: 400 }, + ); + } + + const client = await connectClient(); + const db = client.db(process.env.MONGODB_DATABSE); + const answersCollection = db.collection( + process.env.MONGODB_COLLECTION_ANSWERS, + ); + const usersCollection = db.collection(process.env.MONGODB_COLLECTION_USERS); + + const result = await answersCollection.deleteOne({ + questionId: id, + userId, + }); + + await usersCollection.findOneAndUpdate( + { _id: new ObjectId(userId) }, + { $inc: { providedAnswers: -1 } }, + ); + + if (result.deletedCount === 1) { + return NextResponse.json( + { + success: true, + message: `Answer with id ${id} from user with id ${userId} has been successfully deleted`, + }, + { status: 200, statusText: "OK" }, + ); + } else { + return NextResponse.json( + { + success: false, + message: `No answer was found for the question with id ${id} provided by the user with id ${userId}`, + }, + { status: 404, statusText: "Not Found" }, + ); + } + } catch (e) { + return NextResponse.json( + { success: false, error: e }, + { status: 500, statusText: "Internal Server Error" }, + ); + } +} diff --git a/src/app/api/auth/[...nextauth]/options.js b/src/app/api/auth/[...nextauth]/options.js index c8d80a9..2374f2f 100644 --- a/src/app/api/auth/[...nextauth]/options.js +++ b/src/app/api/auth/[...nextauth]/options.js @@ -6,6 +6,7 @@ import * as bcrypt from "bcrypt"; import { render } from "@react-email/components"; import VerifyEmailSuccessfulEmail from "@/emails/verify-email-successful-email"; import nodemailer from "nodemailer"; +import generateAvatar from "@/utils/generateAvatar"; async function sendWelcomeEmail(account, name, email) { const verifyEmailSuccessfulEmailHtml = render( @@ -94,6 +95,7 @@ export const options = { $set: { email: user.email, name: user.name, + userName: user.email.split("@")[0], image: user.image, }, }, @@ -102,11 +104,11 @@ export const options = { await collection.insertOne({ email: user.email, name: user.name, - image: user.image, + userName: user.email.split("@")[0], + image: user.image || generateAvatar(user.name), emailVerified: true, providedAnswers: 0, totalUpvotesReceived: 0, - totalDownvotesReceived: 0, }); await sendWelcomeEmail(account, user.name, user.email); @@ -114,6 +116,22 @@ export const options = { return true; }, + + async session({ session, token, user }) { + const client = await connectClient(); + const database = client.db(process.env.MONGODB_DATABSE); + const collection = database.collection( + process.env.MONGODB_COLLECTION_USERS, + ); + + const existingUser = await collection.findOne({ email: token.email }); + if (existingUser) { + session.user.id = existingUser._id; + session.user.userName = existingUser.userName; + } + + return session; + }, }, pages: { signIn: "/signin", diff --git a/src/app/api/auth/signup/route.js b/src/app/api/auth/signup/route.js index 04d7dae..be4668d 100644 --- a/src/app/api/auth/signup/route.js +++ b/src/app/api/auth/signup/route.js @@ -5,6 +5,7 @@ import * as bcrypt from "bcrypt"; import { render } from "@react-email/components"; import WelcomeEmail from "@/emails/welcome-email"; import nodemailer from "nodemailer"; +import generateAvatar from "@/utils/generateAvatar"; export async function POST(request) { try { @@ -39,9 +40,11 @@ export async function POST(request) { await collection.insertOne({ name: fullName, + userName: email.split("@")[0], email: email, emailVerified: false, password: await bcrypt.hash(password, 10), + image: generateAvatar(fullName), verifyEmailToken: encryptedToken, verifyEmailTokenExpiry: Date.now() + 3600000, }); diff --git a/src/app/api/auth/verifyemail/route.js b/src/app/api/auth/verifyemail/route.js index c2732ff..a0a9451 100644 --- a/src/app/api/auth/verifyemail/route.js +++ b/src/app/api/auth/verifyemail/route.js @@ -43,7 +43,6 @@ export async function POST(request) { emailVerified: true, providedAnswers: 0, totalUpvotesReceived: 0, - totalDownvotesReceived: 0, }, $unset: { verifyEmailToken: "", diff --git a/src/app/contact-us/page.jsx b/src/app/contact-us/page.jsx index 2de5aa4..6a27b45 100644 --- a/src/app/contact-us/page.jsx +++ b/src/app/contact-us/page.jsx @@ -19,7 +19,7 @@ export default function ContactUs() { touch with you. If you have a simple question, check out our{" "} FAQ section diff --git a/src/app/legal-disclaimer/page.jsx b/src/app/legal-disclaimer/page.jsx index dc33cfb..52c8725 100644 --- a/src/app/legal-disclaimer/page.jsx +++ b/src/app/legal-disclaimer/page.jsx @@ -76,6 +76,7 @@ export default function LegalDisclaimer() { Free Disclaimer Generator @@ -192,7 +193,7 @@ export default function LegalDisclaimer() { By email:{" "} examshare.vercel.app@gmail.com @@ -205,7 +206,7 @@ export default function LegalDisclaimer() { href="https://examshare.vercel.app/contact-us" rel="external nofollow noopener" target="_blank" - className="text-sky-500 hover:text-slate-400 hover:underline hover:decoration-sky-500 hover:underline-offset-4" + className="font-bold text-sky-500 hover:text-slate-400 hover:underline hover:decoration-sky-500 hover:underline-offset-4" > https://examshare.vercel.app/contact-us diff --git a/src/app/page.jsx b/src/app/page.jsx index 126259f..f30f038 100644 --- a/src/app/page.jsx +++ b/src/app/page.jsx @@ -104,15 +104,15 @@ export default async function Home() {
-
+

500+

Institutions

-
+

20,000+

Questions

-
+

10,000+

Users

@@ -142,7 +142,7 @@ export default async function Home() { To access past exam questions for your school, go to the{" "} past questions archive page {" "} @@ -155,6 +155,31 @@ export default async function Home() { will be redirected to a 404 error page.

+
+

+ How can I view and add my own answers to these past exam + questions? +

+

+ To access and contribute answers to past exam questions, you + need an account with us. If you don't have one, please{" "} + + create an account + + . If you already have an account, simply{" "} + + log in + + . Remember, sharing wisdom and knowledge is valuable; + there's no benefit in keeping it to yourself. +

+

How can I share my own past exam questions? @@ -215,13 +240,13 @@ export default async function Home() {
Sign Up Now Already have an account? Log In diff --git a/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc201/page.jsx b/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc201/page.jsx index d883de3..6f4f317 100644 --- a/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc201/page.jsx +++ b/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc201/page.jsx @@ -1,212 +1,226 @@ +import Questions from "@/components/Questions"; +import Answers from "@/components/Answers"; + export default function CSC201() { return ( -
-
-

- DEPARTMENT OF COMPUTER SCIENCE -
- COLLEGE OF COMPUTING -
- WESTERN DELTA UNIVERSITY -

-

- First Semester Examination 2022/2023 SESSION -

-

- CSC201 - Computer Programming I (Java) Time: 2hrs: 30mins -

-

- Answer Question 1 and any other THREE Questions -

-
- -
- -
    -
  1. -

    Question 1 (Compulsory)

    -
      -
    1. - State two reasons why we learn both structured programming and - object-oriented programming.{" "} - (4 marks) -
    2. -
    3. - State two major application areas of Java programming language.{" "} - (2 marks) -
    4. -
    5. - State two major features of Unified Modeling Language (UML).{" "} - (4 marks) -
    6. -
    7. - The entity television can be regarded as a class with attributes - and behaviours. Derive the attributes and behaviour of the class - television using UML class diagram.{" "} - (5 marks) -
    8. -
    9. - Highlight the four vital components of a for loop.{" "} - (4 marks) -
    10. -
    11. - A person invests N100,000:00 in a savings account yielding 5% - interest. Assuming that all the interest is left on deposit, - calculate and display the amount of money in the account at the - end of each year for 10 years. Write a Java application to - determine the amounts using the formula: -
      - - A = P(1+R)n - -
      - where -
      - P is the original amount invested (i.e., the principal) -
      - R is the annual interest rate -
      - n is the number of years -
      A is the amount on deposit at the end of the nth year.{" "} - (6 marks) -
    12. -
    -
  2. + +
  3. +

    Question 1 (Compulsory)

    +
      +
    1. + State two reasons why we learn both structured programming and + object-oriented programming.{" "} + (4 marks) + +
    2. +
    3. + State two major application areas of Java programming language.{" "} + (2 marks) + +
    4. +
    5. + State two major features of Unified Modeling Language (UML).{" "} + (4 marks) + +
    6. +
    7. + The entity television can be regarded as a class with attributes and + behaviours. Derive the attributes and behaviour of the class + television using UML class diagram.{" "} + (5 marks) + +
    8. +
    9. + Highlight the four vital components of a for loop.{" "} + (4 marks) + +
    10. +
    11. + A person invests N100,000:00 in a savings account yielding 5% + interest. Assuming that all the interest is left on deposit, + calculate and display the amount of money in the account at the end + of each year for 10 years. Write a Java application to determine the + amounts using the formula: +
      + + A = P(1+R)n + +
      + where +
      + P is the original amount invested (i.e., the principal) +
      + R is the annual interest rate +
      + n is the number of years +
      A is the amount on deposit at the end of the nth year.{" "} + (6 marks) + +
    12. +
    +
  4. -
  5. -

    Question 2

    -
      -
    1. - Define the following terms as used in Java development - environment. -
        -
      1. Javac.exe
      2. -
      3. Java.exe
      4. -
      - (4 marks) -
    2. -
    3. - Why is the Java programming language platform independent? - Illustrate with a diagram.{" "} - (4 marks) -
    4. -
    5. - What is a loop?{" "} - (2 marks) -
    6. -
    7. - Write a Java application that computes the sum of square of odd - numbers between 1 and 50.{" "} - (5 marks) -
    8. -
    -
  6. +
  7. +

    Question 2

    +
      +
    1. + Define the following terms as used in Java development environment. +
        +
      1. Javac.exe
      2. +
      3. Java.exe
      4. +
      + (4 marks) + +
    2. +
    3. + Why is the Java programming language platform independent? + Illustrate with a diagram.{" "} + (4 marks) + +
    4. +
    5. + What is a loop?{" "} + (2 marks) + +
    6. +
    7. + Write a Java application that computes the sum of square of odd + numbers between 1 and 50.{" "} + (5 marks) + +
    8. +
    +
  8. -
  9. -

    Question 3

    -
      -
    1. - Define the following: -
        -
      1. Local variable
      2. -
      3. Instance variable.
      4. -
      - (4 marks) -
    2. -
    3. - What is a static method? Illustrate with one example.{" "} - (4 marks) -
    4. -
    5. - Write a method that converts a Fahrenheit temperature to Celsius - temperature using the expression:{" "} - Celsius = (Fahrenheit - 32) * 0.55{" "} - (4 marks) -
    6. -
    7. - Give the syntax of the while loop.{" "} - (3 marks) -
    8. -
    -
  10. +
  11. +

    Question 3

    +
      +
    1. + Define the following: +
        +
      1. Local variable
      2. +
      3. Instance variable.
      4. +
      + (4 marks) + +
    2. +
    3. + What is a static method? Illustrate with one example.{" "} + (4 marks) + +
    4. +
    5. + Write a method that converts a Fahrenheit temperature to Celsius + temperature using the expression:{" "} + Celsius = (Fahrenheit - 32) * 0.55{" "} + (4 marks) + +
    6. +
    7. + Give the syntax of the while loop.{" "} + (3 marks) + +
    8. +
    +
  12. -
  13. -

    Question 4

    -
      -
    1. - Define enhanced for loop.{" "} - (2 marks) -
    2. -
    3. - State one situation in which the use of the enhanced is valuable. - (2 marks) -
    4. -
    5. - Highlight three methods available in the predefined class called - Arrays. (3 marks) -
    6. -
    7. - Write a java program segment to illustrate each of the methods - stated in (4c){" "} - (6 marks) -
    8. -
    9. - State two differences between primitive type and reference type. - (2 marks) -
    10. -
    -
  14. +
  15. +

    Question 4

    +
      +
    1. + Define enhanced for loop.{" "} + (2 marks) + +
    2. +
    3. + State one situation in which the use of the enhanced is valuable. + (2 marks) + +
    4. +
    5. + Highlight three methods available in the predefined class called + Arrays. (3 marks) + +
    6. +
    7. + Write a java program segment to illustrate each of the methods + stated in (4c){" "} + (6 marks) + +
    8. +
    9. + State two differences between primitive type and reference type. + (2 marks) + +
    10. +
    +
  16. -
  17. -

    Question 5

    -
      -
    1. - Define the following as used in Java file processing: -
        -
      1. Stream
      2. -
      3. Text file
      4. -
      5. Sequential file.
      6. -
      - (6 marks) -
    2. -
    3. - Write a Java program that requests from the user matriculation - number, total points earned, total units registered and compute - the GPA using the expression:{" "} - GPA = Total Points Earned / Total Units Registered. - Write your output consisting of Matriculation No., Total Units, - Total Points, and GPA to a file named honour.txt.{" "} - (7 marks) -
    4. -
    5. - Highlight two differences between a primitive variable and - non-primitive variable.{" "} - (2 marks) -
    6. -
    -
  18. +
  19. +

    Question 5

    +
      +
    1. + Define the following as used in Java file processing: +
        +
      1. Stream
      2. +
      3. Text file
      4. +
      5. Sequential file.
      6. +
      + (6 marks) + +
    2. +
    3. + Write a Java program that requests from the user matriculation + number, total points earned, total units registered and compute the + GPA using the expression:{" "} + GPA = Total Points Earned / Total Units Registered. + Write your output consisting of Matriculation No., Total Units, + Total Points, and GPA to a file named honour.txt.{" "} + (7 marks) + +
    4. +
    5. + Highlight two differences between a primitive variable and + non-primitive variable.{" "} + (2 marks) + +
    6. +
    +
  20. -
  21. -

    Question 6

    -
      -
    1. - Define an ArrayList. llustrate with an example.{" "} - (4 marks) -
    2. -
    3. - Define a static method. Illustrate with one example.{" "} - (4 marks) -
    4. -
    5. - The scores of ten students in a given examination are represented - in a one-dimensional array as follows: 57 40 34 61 66 56 48 56 52 - 51. Write a Java program that initializes the scores, computes the - total and average scores.{" "} - (7 marks) -
    6. -
    -
  22. -
-
+
  • +

    Question 6

    +
      +
    1. + Define an ArrayList. llustrate with an example.{" "} + (4 marks) + +
    2. +
    3. + Define a static method. Illustrate with one example.{" "} + (4 marks) + +
    4. +
    5. + The scores of ten students in a given examination are represented in + a one-dimensional array as follows: 57 40 34 61 66 56 48 56 52 51. + Write a Java program that initializes the scores, computes the total + and average scores.{" "} + (7 marks) + +
    6. +
    +
  • + ); } diff --git a/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc203/page.jsx b/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc203/page.jsx index ac1afa4..e9d6e82 100644 --- a/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc203/page.jsx +++ b/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc203/page.jsx @@ -1,313 +1,331 @@ +import Questions from "@/components/Questions"; +import Answers from "@/components/Answers"; + export default function CSC203() { return ( -
    -
    -

    - DEPARTMENT OF COMPUTER SCIENCE -
    - COLLEGE OF COMPUTING -
    - WESTERN DELTA UNIVERSITY -

    -

    - First Semester Examination 2022/2023 SESSION -

    -

    - CSC203 - Computer Programming II Time: 2hrs: 30mins -

    -

    Answer Four (4) Questions

    -
    - -
    - -
      -
    1. -

      Question 1

      -
        -
      1. - Briefly define the following: -
          -
        1. Whitespace
        2. -
        3. Constants
        4. -
        5. Header files
        6. -
        - (4.5 marks) -
      2. + +
      3. +

        Question 1

        +
          +
        1. + Briefly define the following: +
            +
          1. Whitespace
          2. +
          3. Constants
          4. +
          5. Header files
          6. +
          + (4.5 marks) + +
        2. -
        3. - Write a C++ program that displays "Welcome to C++ - Programming!" and "Computer Programming is an - interesting endeavor!"{" "} - (3 marks) -
        4. +
        5. + Write a C++ program that displays "Welcome to C++ + Programming!" and "Computer Programming is an interesting + endeavor!"{" "} + (3 marks) + +
        6. -
        7. - Consider the following C++ program and use it to answer the - question that follows:
          -
          -                
          -                  // This program computes the area of a circle
          -                  
          - #include <iosteam> -
          - #include <cmath> -
          - using namespace std; -
          - int main () { -
          - {" "}const float pi = 3.142; -
          - {" "}double radius, result, area, exponent = 2.0; -
          - {" "}cout >> "Enter the value of radius" - >> endl; -
          - {" "}cin << radius; -
          - {" "}result = power (radius, exponent); -
          - {" "}cout >> "The area of the circie is " - >> area; -
          - {" "}return 0; -
          - } -
          -
          - Identify and correct at least ten (10) syntax errors in the given - program (10 marks) -
        8. -
        -
      4. +
      5. + Consider the following C++ program and use it to answer the question + that follows:
        +
        +              
        +                // This program computes the area of a circle
        +                
        + #include <iosteam> +
        + #include <cmath> +
        + using namespace std; +
        + int main () { +
        + {" "}const float pi = 3.142; +
        + {" "}double radius, result, area, exponent = 2.0; +
        + {" "}cout >> "Enter the value of radius" + >> endl; +
        + {" "}cin << radius; +
        + {" "}result = power (radius, exponent); +
        + {" "}cout >> "The area of the circie is " + >> area; +
        + {" "}return 0; +
        + } +
        +
        + Identify and correct at least ten (10) syntax errors in the given + program (10 marks) + +
      6. +
      +
    2. -
    3. -

      Question 2

      -
        -
      1. - Define the following programming terms: -
          -
        1. comment
        2. -
        3. setw manipulator
        4. -
        5. variable name
        6. -
        7. increment operator
        8. -
        9. decrement operator
        10. -
        - (7.5 marks) -
      2. +
      3. +

        Question 2

        +
          +
        1. + Define the following programming terms: +
            +
          1. comment
          2. +
          3. setw manipulator
          4. +
          5. variable name
          6. +
          7. increment operator
          8. +
          9. decrement operator
          10. +
          + (7.5 marks) + +
        2. -
        3. - Give six (6) examples of valid variable names in C++ programming.{" "} - (3 marks) -
        4. +
        5. + Give six (6) examples of valid variable names in C++ programming.{" "} + (3 marks) + +
        6. -
        7. - Suppose total_tax = avg_ _Tax * ++count; explain how - a C++ compiler will execute the given statement{" "} - (3 marks) -
        8. +
        9. + Suppose total_tax = avg_ _Tax * ++count; explain how a + C++ compiler will execute the given statement{" "} + (3 marks) + +
        10. -
        11. - Write a C++ program that generates the table below - - - - - - - - - - - - - - - - - - - - - -
          YearProfit
          1950500000000
          1951600000000
          1952700000000
          - (4 marks) -
        12. -
        -
      4. +
      5. + Write a C++ program that generates the table below + + + + + + + + + + + + + + + + + + + + + +
        YearProfit
        1950500000000
        1951600000000
        1952700000000
        + (4 marks) + +
      6. +
      +
    4. -
    5. -

      Question 3

      -
        -
      1. - Using suitable examples, write explanatory notes on the following: -
          -
        1. Relational operators
        2. -
        3. Loops
        4. -
        - (4 marks) -
      2. +
      3. +

        Question 3

        +
          +
        1. + Using suitable examples, write explanatory notes on the following: +
            +
          1. Relational operators
          2. +
          3. Loops
          4. +
          + (4 marks) + +
        2. -
        3. - Give the syntax of while and do loops{" "} - (4.5 marks) -
        4. +
        5. + Give the syntax of while and do loops{" "} + (4.5 marks) + +
        6. -
        7. - Name four (4) arithmetic operators and describe them using - examples (4 marks) -
        8. +
        9. + Name four (4) arithmetic operators and describe them using examples{" "} + (4 marks) + +
        10. -
        11. - Write a C++ program using a while loop that asks a user to enter - numbers, calculates their sum, prints the result until the number - entered is negative{" "} - (5 marks) -
        12. -
        -
      4. +
      5. + Write a C++ program using a while loop that asks a user to enter + numbers, calculates their sum, prints the result until the number + entered is negative{" "} + (5 marks) + +
      6. +
      +
    6. -
    7. -

      Question 4

      -
        -
      1. - Highlight the role of decisions in computer programming{" "} - (2 marks) -
      2. +
      3. +

        Question 4

        +
          +
        1. + Highlight the role of decisions in computer programming{" "} + (2 marks) + +
        2. -
        3. -
            -
          1. - With the aid of a flowchart only, describe the working - principle of if...else statement{" "} - (3.5 marks) -
          2. -
          3. - Write down the syntax of a switch statement{" "} - (4 marks) -
          4. -
          -
        4. +
        5. +
            +
          1. + With the aid of a flowchart only, describe the working principle + of if...else statement{" "} + (3.5 marks) + +
          2. +
          3. + Write down the syntax of a switch statement{" "} + (4 marks) + +
          4. +
          +
        6. -
        7. - State three (3) examples of logical operators and show how they - are represented in C++{" "} - (3 marks) -
        8. +
        9. + State three (3) examples of logical operators and show how they are + represented in C++{" "} + (3 marks) + +
        10. -
        11. - Write a C++ program that obtains test score and assigns grade - according to the following information. The test score is an - integer from 1 to 100 - - - - - - - - - - - - - - - - - - - - - - - - - -
          Test ScoreGrade
          70 and aboveDistinction
          60 - 69Merit
          50-59Pass
          Below 50Fail
          - (5 marks) -
        12. -
        -
      4. +
      5. + Write a C++ program that obtains test score and assigns grade + according to the following information. The test score is an integer + from 1 to 100 + + + + + + + + + + + + + + + + + + + + + + + + + +
        Test ScoreGrade
        70 and aboveDistinction
        60 - 69Merit
        50-59Pass
        Below 50Fail
        + (5 marks) + +
      6. +
      +
    8. -
    9. -

      Question 5

      -
        -
      1. - What is a Structure?{" "} - (2.5 marks) -
      2. +
      3. +

        Question 5

        +
          +
        1. + What is a Structure?{" "} + (2.5 marks) + +
        2. -
        3. -
            -
          1. - State the syntax of a structure{" "} - (3 marks) -
          2. -
          3. - Write a program that defines a structure, assigns values to - the structure and displays the values{" "} - (5 marks) -
          4. -
          -
        4. +
        5. +
            +
          1. + State the syntax of a structure{" "} + (3 marks) + +
          2. +
          3. + Write a program that defines a structure, assigns values to the + structure and displays the values{" "} + (5 marks) + +
          4. +
          +
        6. -
        7. -
            -
          1. - Define Enumeration{" "} - (2 marks) -
          2. -
          3. - Give the syntax of enumeration and write an enumeration whose - values are months the year{" "} - (5 marks) -
          4. -
          -
        8. -
        -
      4. +
      5. +
          +
        1. + Define Enumeration{" "} + (2 marks) + +
        2. +
        3. + Give the syntax of enumeration and write an enumeration whose + values are months the year{" "} + (5 marks) + +
        4. +
        +
      6. +
      +
    10. -
    11. -

      Question 6

      -
        -
      1. - Define a function{" "} - (2 marks) -
      2. -
      3. -
          -
        1. - Give two (2) reasons for using functions in computer - programming{" "} - (2 marks) -
        2. -
        3. - What is an argument?{" "} - (2 marks) -
        4. -
        5. - Name three (3) parameter passing techniques to functions{" "} - (3 marks) -
        6. -
        -
      4. -
      5. -
          -
        1. - Define recursion{" "} - (2 marks) -
        2. -
        3. - Write a recursive function that adds a range of numbers by - adding two numbers each time it is called by a main program{" "} - (6 marks) -
        4. -
        -
      6. -
      -
    12. -
    -
    +
  • +

    Question 6

    +
      +
    1. + Define a function{" "} + (2 marks) + +
    2. +
    3. +
        +
      1. + Give two (2) reasons for using functions in computer programming{" "} + (2 marks) + +
      2. +
      3. + What is an argument?{" "} + (2 marks) + +
      4. +
      5. + Name three (3) parameter passing techniques to functions{" "} + (3 marks) + +
      6. +
      +
    4. +
    5. +
        +
      1. + Define recursion{" "} + (2 marks) + +
      2. +
      3. + Write a recursive function that adds a range of numbers by + adding two numbers each time it is called by a main program{" "} + (6 marks) + +
      4. +
      +
    6. +
    +
  • + ); } diff --git a/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc209/page.jsx b/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc209/page.jsx index 08826c2..a8b3a4d 100644 --- a/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc209/page.jsx +++ b/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/csc209/page.jsx @@ -1,246 +1,263 @@ +import Questions from "@/components/Questions"; +import Answers from "@/components/Answers"; + export default function CSC209() { return ( -
    -
    -

    - DEPARTMENT OF COMPUTER SCIENCE -
    - COLLEGE OF COMPUTING -
    - WESTERN DELTA UNIVERSITY -

    -

    - First Semester Examination 2022/2023 SESSION -

    -

    - CSC209 - Foundations Of Sequential Programming Time: 2hrs: 30mins -

    -

    Answer Four (4) Questions

    -
    - -
    - -
      -
    1. -

      Question 1

      -
        -
      1. -
          -
        1. - Define Computer Programming{" "} - (1.5 marks) -
        2. -
        3. - Differentiate between interpreters and compilers{" "} - (2 marks) -
        4. -
        -
      2. -
      3. - What is program design? List and explain five (5) aims of program - design (6 marks) -
      4. -
      5. - With the aid of a suitable diagram, discuss the program - development life cycle{" "} - (8 marks) -
      6. -
      -
    2. + +
    3. +

      Question 1

      +
        +
      1. +
          +
        1. + Define Computer Programming{" "} + (1.5 marks) + +
        2. +
        3. + Differentiate between interpreters and compilers{" "} + (2 marks) + +
        4. +
        +
      2. +
      3. + What is program design? List and explain five (5) aims of program + design (6 marks) + +
      4. +
      5. + With the aid of a suitable diagram, discuss the program development + life cycle (8 marks) + +
      6. +
      +
    4. -
    5. -

      Question 2

      -
        -
      1. - Using a sample pseudo-code in each case describe the following - control structures: -
          -
        1. - Sequence{" "} - (2.5 marks) -
        2. -
        3. - Selection{" "} - (2.5 marks) -
        4. -
        5. - Repitition{" "} - (2.5 marks) -
        6. -
        -
      2. -
      3. - Write a pseudo-code that computes and displays the product, sum, - average of three (3) integer numbers on the screen{" "} - (5 marks) -
      4. -
      5. - Draw the corresponding flowchart{" "} - (5 marks) -
      6. -
      -
    6. +
    7. +

      Question 2

      +
        +
      1. + Using a sample pseudo-code in each case describe the following + control structures: +
          +
        1. + Sequence{" "} + (2.5 marks) +
        2. +
        3. + Selection{" "} + (2.5 marks) +
        4. +
        5. + Repitition{" "} + (2.5 marks) +
        6. + +
        +
      2. +
      3. + Write a pseudo-code that computes and displays the product, sum, + average of three (3) integer numbers on the screen{" "} + (5 marks) + +
      4. +
      5. + Draw the corresponding flowchart{" "} + (5 marks) + +
      6. +
      +
    8. -
    9. -

      Question 3

      -
        -
      1. - Define the following Assembly language terms: -
          -
        1. Mnemonics
        2. -
        3. Labels
        4. -
        5. Operands
        6. -
        - (4.5 marks) -
      2. +
      3. +

        Question 3

        +
          +
        1. + Define the following Assembly language terms: +
            +
          1. Mnemonics
          2. +
          3. Labels
          4. +
          5. Operands
          6. +
          + (4.5 marks) + +
        2. -
        3. - Consider the Assembly language code below and use it to answer the - question that follows: - - READ X
          - ADD Y
          - STORE Z
          - PRINT Z
          - HALT -
          - Explain the code, one line after the other{" "} - (3 marks) -
        4. +
        5. + Consider the Assembly language code below and use it to answer the + question that follows: + + READ X
          + ADD Y
          + STORE Z
          + PRINT Z
          + HALT +
          + Explain the code, one line after the other{" "} + (3 marks) + +
        6. -
        7. - Write explanatory notes on the following: -
            -
          1. High level languages
          2. -
          3. Fifth generation languages
          4. -
          - (4 marks) -
        8. +
        9. + Write explanatory notes on the following: +
            +
          1. High level languages
          2. +
          3. Fifth generation languages
          4. +
          + (4 marks) + +
        10. -
        11. -
            -
          1. - Convert 10010 to Base 8{" "} - (2 marks) -
          2. -
          3. - Convert 75110 to Base 16{" "} - (2 marks) -
          4. -
          5. - Convert 2210 to Base 2{" "} - (2 marks) -
          6. -
          -
        12. -
        -
      4. +
      5. +
          +
        1. + Convert 10010 to Base 8{" "} + (2 marks) + +
        2. +
        3. + Convert 75110 to Base 16{" "} + (2 marks) + +
        4. +
        5. + Convert 2210 to Base 2{" "} + (2 marks) + +
        6. +
        +
      6. +
      +
    10. -
    11. -

      Question 4

      -
        -
      1. - Explain the following data storage representations: -
          -
        1. Binary Coded Decimal
        2. -
        3. Unsigned representation
        4. -
        5. Signed representation
        6. -
        - (6 marks) -
      2. -
      3. -
          -
        1. - Store 200 and 260 in a 16-bit memory location using unsigned - representation{" "} - (4 marks) -
        2. -
        3. - Store 30 and -40 in an 8-bit memory location using signed - representation - (4 marks) -
        4. -
        5. - Give the BCD equivalent of 1827 and 124{" "} - (3.5 marks) -
        6. -
        -
      4. -
      -
    12. +
    13. +

      Question 4

      +
        +
      1. + Explain the following data storage representations: +
          +
        1. Binary Coded Decimal
        2. +
        3. Unsigned representation
        4. +
        5. Signed representation
        6. +
        + (6 marks) + +
      2. +
      3. +
          +
        1. + Store 200 and 260 in a 16-bit memory location using unsigned + representation{" "} + (4 marks) + +
        2. +
        3. + Store 30 and -40 in an 8-bit memory location using signed + representation + (4 marks) + +
        4. +
        5. + Give the BCD equivalent of 1827 and 124{" "} + (3.5 marks) + +
        6. +
        +
      4. +
      +
    14. -
    15. -

      Question 5

      -
        -
      1. - Compute the following: -
          -
        1. NOT 101101010111
        2. -
        3. 101001011 OR 010110100
        4. -
        5. 10000 AND 01011
        6. -
        7. 101110111011 XOR 001111011101
        8. -
        - (6 marks) -
      2. -
      3. - Explain the following shift operations: -
          -
        1. logical shift left
        2. -
        3. logical shift right
        4. -
        5. circular left shift
        6. -
        7. circular right shift
        8. -
        - (6 marks) -
      4. -
      5. -
          -
        1. - Perform shift left operation on 1000110{" "} - (2 marks) -
        2. -
        3. - Carry out shift arithmetic right on 01100110{" "} - (2 marks) -
        4. -
        5. - Calculate logical shift right on 11001100{" "} - (1.5 marks) -
        6. -
        -
      6. -
      -
    16. +
    17. +

      Question 5

      +
        +
      1. + Compute the following: +
          +
        1. NOT 101101010111
        2. +
        3. 101001011 OR 010110100
        4. +
        5. 10000 AND 01011
        6. +
        7. 101110111011 XOR 001111011101
        8. +
        + (6 marks) + +
      2. +
      3. + Explain the following shift operations: +
          +
        1. logical shift left
        2. +
        3. logical shift right
        4. +
        5. circular left shift
        6. +
        7. circular right shift
        8. +
        + (6 marks) + +
      4. +
      5. +
          +
        1. + Perform shift left operation on 1000110{" "} + (2 marks) + +
        2. +
        3. + Carry out shift arithmetic right on 01100110{" "} + (2 marks) + +
        4. +
        5. + Calculate logical shift right on 11001100{" "} + (1.5 marks) + +
        6. +
        +
      6. +
      +
    18. -
    19. -

      Question 6

      -
        -
      1. - Define machine architecture{" "} - (2 marks) -
      2. -
      3. - Draw the Von Neuman model of a conventional digital computer and - briefly explain its Principle of operation{" "} - (5 marks) -
      4. -
      5. - Write explanatory notes on the following: -
          -
        1. Central Processing Unit
        2. -
        3. Data registers
        4. -
        5. Instruction registers
        6. -
        7. Program counter
        8. -
        9. Memory system
        10. -
        - (7.5 marks) -
      6. -
      7. - State three (3) functions of the processor{" "} - (3 marks) -
      8. -
      -
    20. -
    -
    +
  • +

    Question 6

    +
      +
    1. + Define machine architecture{" "} + (2 marks) + +
    2. +
    3. + Draw the Von Neuman model of a conventional digital computer and + briefly explain its Principle of operation{" "} + (5 marks) + +
    4. +
    5. + Write explanatory notes on the following: +
        +
      1. Central Processing Unit
      2. +
      3. Data registers
      4. +
      5. Instruction registers
      6. +
      7. Program counter
      8. +
      9. Memory system
      10. +
      + (7.5 marks) + +
    6. +
    7. + State three (3) functions of the processor{" "} + (3 marks) + +
    8. +
    +
  • + ); } diff --git a/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/mth211/page.jsx b/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/mth211/page.jsx index 722edfa..2e5c5cb 100644 --- a/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/mth211/page.jsx +++ b/src/app/past-questions-archive/wdu/compsci/2022_2023/200-level/1st-semester/mth211/page.jsx @@ -1,268 +1,275 @@ +import Questions from "@/components/Questions"; +import Answers from "@/components/Answers"; + export default function MTH211() { return ( -
    -
    -

    - DEPARTMENT OF COMPUTER SCIENCE -
    - COLLEGE OF COMPUTING -
    - WESTERN DELTA UNIVERSITY -

    -

    - First Semester Examination 2022/2023 SESSION -

    -

    MTH211 - Statistics Time: 1hr: 30mins

    -

    Answer Four (4) Questions

    -
    - -
    - -
      -
    1. -

      Question 1

      -
        -
      1. - Computer the spearman rank correlation coefficient between these - course MTH211 and CSC 201 for the given scores sand predict the - relationship between them{" "} -
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        - MTH211 - 35403365576128144768
        - CSC201 - 7867975942
        -
        - (10 marks) -
      2. -
      3. - State the axium of probability{" "} - (3 marks) -
      4. -
      5. - Prove that Q2(x) = E(x2) - E(x)2{" "} - (4.5 marks) -
      6. -
      -
    2. + +
    3. +

      Question 1

      +
        +
      1. + Computer the spearman rank correlation coefficient between these + course MTH211 and CSC 201 for the given scores sand predict the + relationship between them{" "} +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + MTH211 + 35403365576128144768
        + CSC201 + 7867975942
        +
        + (10 marks) + +
      2. +
      3. + State the axium of probability{" "} + (3 marks) + +
      4. +
      5. + Prove that Q2(x) = E(x2) - E(x)2{" "} + (4.5 marks) + +
      6. +
      +
    4. -
    5. -

      Question 2

      -
        -
      1. - A random variable X is defined as the sum of - faces when a pair of dice is thrown find -
          -
        1. - The Sample points{" "} - (5 marks) -
        2. -
        3. - Expected value of X{" "} - (5 marks) -
        4. -
        5. - Var (x){" "} - (7.5 marks) -
        6. -
        -
      2. -
      -
    6. +
    7. +

      Question 2

      +
        +
      1. + A random variable X is defined as the sum of faces + when a pair of dice is thrown find +
          +
        1. + The Sample points{" "} + (5 marks) + +
        2. +
        3. + Expected value of X{" "} + (5 marks) + +
        4. +
        5. + Var (x){" "} + (7.5 marks) + +
        6. +
        +
      2. +
      +
    8. -
    9. -

      Question 3

      -
        -
      1. - The data below rotates the weekly maintenance cost (N) to the age - (in month) of ten machines of formula types in a manufacturing - company -
        - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
        - Age (X) - 5101520303030505060
        - Cost (Y) - 19242530313330303539
        -
        -
          -
        1. - Find the least square regression line of maintenance cost (y) - on age (X) -
        2. -
        3. - Predict the maintenance cost for the machine of this type - which is 40 months old -
        4. -
        - (17.5 marks) -
      2. -
      -
    10. +
    11. +

      Question 3

      +
        +
      1. + The data below rotates the weekly maintenance cost (N) to the age + (in month) of ten machines of formula types in a manufacturing + company +
        + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
        + Age (X) + 5101520303030505060
        + Cost (Y) + 19242530313330303539
        +
        +
          +
        1. + Find the least square regression line of maintenance cost (y) on + age (X) +
        2. +
        3. + Predict the maintenance cost for the machine of this type which + is 40 months old +
        4. +
        + (17.5 marks) + +
      2. +
      +
    12. -
    13. -

      Question 4

      -
        -
      1. - Suppose A and B are two events, not necessarily independent or - mutually exclusive and suppose they are related. Use on diagram to - show that{" "} - - P(AUB) = P(A) * P(B) - P(AnB) - {" "} - (7 marks) -
      2. -
      3. - Three out of every batch of 30 manufactures articles pf a company - are known to be defective if 5 of the articles are picked at - random. What is the probability that: -
          -
        1. Exactly 2
        2. -
        3. Not more than 2
        4. -
        5. at most 4 are defective
        6. -
        - (10.5 marks) -
      4. -
      -
    14. +
    15. +

      Question 4

      +
        +
      1. + Suppose A and B are two events, not necessarily independent or + mutually exclusive and suppose they are related. Use on diagram to + show that{" "} + + P(AUB) = P(A) * P(B) - P(AnB) + {" "} + (7 marks) + +
      2. +
      3. + Three out of every batch of 30 manufactures articles pf a company + are known to be defective if 5 of the articles are picked at random. + What is the probability that: +
          +
        1. Exactly 2
        2. +
        3. Not more than 2
        4. +
        5. at most 4 are defective
        6. +
        + (10.5 marks) + +
      4. +
      +
    16. -
    17. -

      Question 5

      -
        -
      1. - The continuous random variable X has probability density function - (pdf) given by: -
        -
        F(X) =
        -
        - { - - 1/b-a, a < x < b - - O, otherwise -
        +
      2. +

        Question 5

        +
          +
        1. + The continuous random variable X has probability density function + (pdf) given by: +
          +
          F(X) =
          +
          + { + + 1/b-a, a < x < b + + O, otherwise
          - Find the E(X) items{" "} - (9.5 marks) -
        2. -
        3. - A manufacturer produces items such that 20 percent are defective - and 80 percent are non defective. If a defective items is - produced, the manufacturer loses N5 while a non defective items - fetches a profit of N20. Find the expected value of the profit per - items (8 marks) -
        4. -
        -
      3. +
    + Find the E(X) items{" "} + (9.5 marks) + + +
  • + A manufacturer produces items such that 20 percent are defective and + 80 percent are non defective. If a defective items is produced, the + manufacturer loses N5 while a non defective items fetches a profit + of N20. Find the expected value of the profit per items{" "} + (8 marks) + +
  • + + -
  • -

    Question 6

    -
      -
    1. - State the properties of variance{" "} - (7 marks) -
    2. +
    3. +

      Question 6

      +
        +
      1. + State the properties of variance{" "} + (7 marks) + +
      2. -
      3. - The discrete random variable X has a probability non function - given in the table below. Find var (X) -
        - - - - - - - - - - - - - - - - - -
        - X = X - 0123
        - P(X) = P(X-X) - - 1/8 - - {" "} - 3/8 - - {" "} - 3/8 - - {" "} - 1/8 -
        -
        - (10.5 marks) -
      4. -
      -
    4. -
    - +
  • + The discrete random variable X has a probability non function given + in the table below. Find var (X) +
    + + + + + + + + + + + + + + + + + +
    + X = X + 0123
    + P(X) = P(X-X) + + 1/8 + + {" "} + 3/8 + + {" "} + 3/8 + + {" "} + 1/8 +
    +
    + (10.5 marks) + +
  • + + + ); } diff --git a/src/app/privacy-policy/page.jsx b/src/app/privacy-policy/page.jsx index ef931f6..e40172f 100644 --- a/src/app/privacy-policy/page.jsx +++ b/src/app/privacy-policy/page.jsx @@ -94,7 +94,7 @@ export default function PrivacyPolicy() { href="https://examshare.vercel.app" rel="external nofollow noopener" target="_blank" - className="text-sky-500 hover:text-slate-400 hover:underline hover:decoration-sky-500 hover:underline-offset-4" + className="font-bold text-sky-500 hover:text-slate-400 hover:underline hover:decoration-sky-500 hover:underline-offset-4" > https://examshare.vercel.app @@ -181,7 +181,7 @@ export default function PrivacyPolicy() { Free Privacy Policy website {" "} @@ -500,7 +500,7 @@ export default function PrivacyPolicy() { By email:{" "} examshare.vercel.app@gmail.com @@ -511,7 +511,7 @@ export default function PrivacyPolicy() { href="https://examshare.vercel.app/contact-us" rel="external nofollow noopener" target="_blank" - className="text-sky-500 hover:text-slate-400 hover:underline hover:decoration-sky-500 hover:underline-offset-4" + className="font-bold text-sky-500 hover:text-slate-400 hover:underline hover:decoration-sky-500 hover:underline-offset-4" > https://examshare.vercel.app/contact-us diff --git a/src/app/signin/page.jsx b/src/app/signin/page.jsx index a40c537..c1c1188 100644 --- a/src/app/signin/page.jsx +++ b/src/app/signin/page.jsx @@ -18,7 +18,7 @@ export default function SignIn() { advocating{" "} open-source education {" "} diff --git a/src/app/signup/page.jsx b/src/app/signup/page.jsx index 7065d2d..e1817b8 100644 --- a/src/app/signup/page.jsx +++ b/src/app/signup/page.jsx @@ -17,7 +17,7 @@ export default function SignUp() { Thank you for joining the{" "} open-source education {" "} diff --git a/src/app/submit-past-question/page.jsx b/src/app/submit-past-question/page.jsx index 3e902d2..3f1e098 100644 --- a/src/app/submit-past-question/page.jsx +++ b/src/app/submit-past-question/page.jsx @@ -17,7 +17,7 @@ export default function SubmitPastQuestion() { You are just one step away from becoming one of the early pioneers of{" "} open-source education {" "} diff --git a/src/app/terms-and-conditions/page.jsx b/src/app/terms-and-conditions/page.jsx index 5af6e0a..5209867 100644 --- a/src/app/terms-and-conditions/page.jsx +++ b/src/app/terms-and-conditions/page.jsx @@ -99,7 +99,7 @@ export default function TermsAndConditions() { href="https://examshare.vercel.app" rel="external nofollow noopener" target="_blank" - className="text-sky-500 hover:text-slate-400 hover:underline hover:decoration-sky-500 hover:underline-offset-4" + className="font-bold text-sky-500 hover:text-slate-400 hover:underline hover:decoration-sky-500 hover:underline-offset-4" > https://examshare.vercel.app @@ -183,7 +183,7 @@ export default function TermsAndConditions() { please address your request to:{" "} examshare.vercel.app@gmail.com @@ -888,7 +888,7 @@ export default function TermsAndConditions() { By email:{" "} examshare.vercel.app@gmail.com @@ -901,7 +901,7 @@ export default function TermsAndConditions() { href="https://examshare.vercel.app/contact-us" rel="external nofollow noopener" target="_blank" - className="text-sky-500 hover:text-slate-400 hover:underline hover:decoration-sky-500 hover:underline-offset-4" + className="font-bold text-sky-500 hover:text-slate-400 hover:underline hover:decoration-sky-500 hover:underline-offset-4" > https://examshare.vercel.app/contact-us diff --git a/src/components/Answer.jsx b/src/components/Answer.jsx new file mode 100644 index 0000000..3bee34f --- /dev/null +++ b/src/components/Answer.jsx @@ -0,0 +1,268 @@ +"use client"; + +import { useState } from "react"; +import { useSWRConfig } from "swr"; +import { TbArrowBigUp, TbArrowBigDown } from "react-icons/tb"; +import RichTextEditor from "./RichTextEditor"; +import { useSession } from "next-auth/react"; +import HTMLReactParser from "html-react-parser"; +import Image from "next/image"; +import InlineSVG from "react-inlinesvg"; + +export default function Answer({ questionId, answer }) { + const { data: session } = useSession(); + const { mutate } = useSWRConfig(); + const [htmlContent, setHtmlContent] = useState(""); + const [isUpvoting, setIsUpvoting] = useState(false); + const [isDownvoting, setIsDownvoting] = useState(false); + const [isEditing, setIsEditing] = useState(false); + const [isSaving, setIsSaving] = useState(false); + const [isDeleting, setIsDeleting] = useState(false); + + const handleEditAnswer = async (content) => { + try { + setIsSaving(true); + + const response = await fetch(`/api/answers/${questionId}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + questionUserId: answer.userId, + userId: session.user.id, + htmlContent: content, + }), + }); + + if (!response.ok) { + setIsSaving(false); + setIsEditing(false); + return; + } + + setIsSaving(false); + setIsEditing(false); + await mutate(`/api/answers/${questionId}`); + } catch (e) { + console.log(e); + } + }; + + const handleDeleteAnswer = async () => { + try { + if ( + !confirm( + "Are you certain that you wish to delete your answer? Please note that this action is irreversible.", + ) + ) { + return; + } + + setIsDeleting(true); + + const response = await fetch(`/api/answers/${questionId}`, { + method: "DELETE", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ userId: session.user.id }), + }); + + if (!response.ok) { + setIsDeleting(false); + return; + } + + setIsDeleting(false); + await mutate(`/api/answers/${questionId}`); + } catch (e) { + console.error(e); + } + }; + + const handleUpvoteAnswer = async (answer) => { + try { + setIsUpvoting(true); + + const response = await fetch(`/api/answers/${questionId}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + questionUserId: answer.userId, + userId: session.user.id, + upvotes: answer.upvotes, + upvotesDirection: "up", + }), + }); + + if (!response.ok) { + setIsUpvoting(false); + return; + } + + setIsUpvoting(false); + await mutate(`/api/answers/${questionId}`); + } catch (e) { + console.error(e); + } + }; + + const handleDownvoteAnswer = async (answer) => { + try { + setIsDownvoting(true); + + const response = await fetch(`/api/answers/${questionId}`, { + method: "PATCH", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + questionUserId: answer.userId, + userId: session.user.id, + upvotes: answer.upvotes, + upvotesDirection: "down", + }), + }); + + if (!response.ok) { + setIsDownvoting(false); + return; + } + + setIsDownvoting(false); + await mutate(`/api/answers/${questionId}`); + } catch (e) { + console.error(e); + } + }; + + return ( + <> +
    +
    +
    + {answer.userImage.startsWith(" + ) : ( + {answer.userName} + )} +
    + +

    {answer.userName}

    + + + {answer.edited && ( + + edited + + )} +
    +
    + {isEditing ? ( + handleEditAnswer(htmlContent)} + /> + ) : ( +
    + {HTMLReactParser(answer.answer)} +
    + )} +
    +
    + + + {answer.upvotes > 0 && "+"} + {answer.upvotes} + + +
    + + {answer.userId === session.user.id && ( +
    + {!isEditing && ( + + )} + +
    + )} +
    +
    +
    + + ); +} diff --git a/src/components/Answers.jsx b/src/components/Answers.jsx new file mode 100644 index 0000000..5c3ed2d --- /dev/null +++ b/src/components/Answers.jsx @@ -0,0 +1,166 @@ +"use client"; + +import { useQuestionsId } from "@/providers/QuestionsIdProvider"; +import { useState } from "react"; +import useSWR, { useSWRConfig } from "swr"; +import Answer from "./Answer"; +import RichTextEditor from "./RichTextEditor"; +import { signIn, useSession } from "next-auth/react"; +import { usePathname } from "next/navigation"; + +export default function Answers({ questionNumber }) { + const { data: session } = useSession(); + const paths = usePathname(); + const [showAnswerBtn, setShowAnswerBtn] = useState(true); + const [showAnswers, setShowAnswers] = useState(false); + const [addAnswer, setAddAnswer] = useState(false); + const [htmlContent, setHtmlContent] = useState(""); + const [isSubmitting, setIsSubmitting] = useState(false); + + const questionsId = useQuestionsId(); + const questionId = `${questionsId}_${questionNumber}`; + + const { mutate } = useSWRConfig(); + + const compareFn = (item1, item2) => { + return item2.upvotes - item1.upvotes; + }; + + const fetcher = async (key) => { + try { + const response = await fetch(key, { + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + }); + + if (!response.ok) { + throw new Error(`HTTP error! status: ${response.status}`); + } + + const result = await response.json(); + return result.answers.sort(compareFn); + } catch (e) { + console.error("An error occured while fetching the data: ", e); + } + }; + + const { + data: answers, + error, + isLoading, + } = useSWR(`/api/answers/${questionId}`, fetcher); + + const handleShowButton = async () => { + if (!session) { + signIn({ callbackUrl: paths }); + } + + setShowAnswerBtn(false); + setShowAnswers(true); + }; + + const handleHideButton = () => { + setShowAnswerBtn(true); + setShowAnswers(false); + setAddAnswer(false); + }; + + const handleAddAnswer = async (content, setIsSubmitting) => { + try { + setIsSubmitting(true); + + const response = await fetch(`/api/answers/${questionId}`, { + method: "POST", + headers: { + "Content-Type": "application/json", + Accept: "application/json", + }, + body: JSON.stringify({ + userId: session.user.id, + userName: session.user.userName, + userImage: session.user.image, + htmlContent: content, + }), + }); + + if (!response.ok) { + setIsSubmitting(false); + setHtmlContent(""); + setAddAnswer(false); + return; + } + + setIsSubmitting(false); + setHtmlContent(""); + setAddAnswer(false); + await mutate(`/api/answers/${questionId}`); + } catch (e) { + console.error(e); + } + }; + + return ( +
    + {showAnswerBtn && ( + + )} + + {showAnswers && ( +
    + {error &&
    failed to load
    } + {isLoading &&
    loading...
    } +
    + {answers.length > 0 && ( +
      + {answers.map((answer) => ( +
    1. + +
    2. + ))} +
    + )} + + {addAnswer && ( +
    + + handleAddAnswer(htmlContent, setIsSubmitting) + } + /> +
    + )} +
    + +
    + {!addAnswer && ( + + )} + +
    +
    + )} +
    + ); +} diff --git a/src/components/Footer.jsx b/src/components/Footer.jsx index 2508021..4778a30 100644 --- a/src/components/Footer.jsx +++ b/src/components/Footer.jsx @@ -19,21 +19,25 @@ export default function Footer() {
  • + ExamShare on GitHub
  • + ExamShare on Twitter
  • + ExamShare on Facebook
  • + ExamShare on Instagram
  • diff --git a/src/components/ForgotPasswordForm.jsx b/src/components/ForgotPasswordForm.jsx index 3d99d82..a61dd3f 100644 --- a/src/components/ForgotPasswordForm.jsx +++ b/src/components/ForgotPasswordForm.jsx @@ -125,7 +125,7 @@ export default function ForgotPasswordForm() {
    Back to Login diff --git a/src/components/Header.jsx b/src/components/Header.jsx index a8da75c..67cc22a 100644 --- a/src/components/Header.jsx +++ b/src/components/Header.jsx @@ -36,12 +36,13 @@ export default function Header() {

    EXAMSHARE

    -
    +
    @@ -150,6 +151,7 @@ export default function Header() { ref={showThemeNavRef} type="button" onClick={() => setShowThemeNav((t) => !t)} + aria-label="Theme" > {theme === "os" && } {theme === "light" && } diff --git a/src/components/ProfileMenu.jsx b/src/components/ProfileMenu.jsx index 8ae3126..c7279aa 100644 --- a/src/components/ProfileMenu.jsx +++ b/src/components/ProfileMenu.jsx @@ -4,8 +4,8 @@ import { useSession, signOut } from "next-auth/react"; import { useState, useRef } from "react"; import Image from "next/image"; import useClickOutside from "@/hooks/useClickOutside"; -import { FaUserCircle } from "react-icons/fa"; import { FiLogOut } from "react-icons/fi"; +import InlineSVG from "react-inlinesvg"; export default function ProfileMenu() { const [showProfileMenu, setShowProfileMenu] = useState(false); @@ -25,50 +25,42 @@ export default function ProfileMenu() { return (
    - {session?.user?.image ? ( - - ) : ( - - )} + )} + {showProfileMenu && (
    • - {session?.user?.image ? ( -
      +
      + {session.user.image.startsWith(" + ) : ( {session.user.name} -
      - ) : ( -
      - -
      - )} + )} +
      -

      {session.user.name}

      +

      {session.user.userName}

      {session.user.email}

    • diff --git a/src/components/Questions.jsx b/src/components/Questions.jsx new file mode 100644 index 0000000..6a97e6c --- /dev/null +++ b/src/components/Questions.jsx @@ -0,0 +1,48 @@ +"use client"; + +import { usePathname } from "next/navigation"; +import QuestionsIdProvider from "@/providers/QuestionsIdProvider"; + +export default function Questions({ + school, + college, + department, + session, + semester, + courseCode, + courseTitle, + allowedTime, + instruction, + children, +}) { + const paths = usePathname(); + const pathNames = paths.split("/").filter((path) => path); + const id = pathNames.slice(1).join("_"); + + return ( + +
      +
      +

      + department of {department} +
      + college of {college} +
      + {school} +

      +

      + {semester} examination {session} session +

      +

      + {courseCode} - {courseTitle} time: {allowedTime} +

      +

      {instruction}

      +
      + +
      + +
        {children}
      +
      +
      + ); +} diff --git a/src/components/RichTextEditor.jsx b/src/components/RichTextEditor.jsx new file mode 100644 index 0000000..d50c667 --- /dev/null +++ b/src/components/RichTextEditor.jsx @@ -0,0 +1,361 @@ +"use client"; + +import { useCallback, useState } from "react"; +import { CgUndo, CgRedo } from "react-icons/cg"; +import { + MdCode, + MdFormatBold, + MdFormatItalic, + MdFormatQuote, + MdFormatUnderlined, + MdStrikethroughS, + MdSubscript, + MdSuperscript, +} from "react-icons/md"; +import { LuHeading } from "react-icons/lu"; +import { VscListOrdered, VscListUnordered } from "react-icons/vsc"; +import { GoLink } from "react-icons/go"; +import { AiFillCode } from "react-icons/ai"; +import { EditorContent, useEditor } from "@tiptap/react"; +import Placeholder from "@tiptap/extension-placeholder"; +import Underline from "@tiptap/extension-underline"; +import Superscript from "@tiptap/extension-superscript"; +import Subscript from "@tiptap/extension-subscript"; +import TextStyle from "@tiptap/extension-text-style"; +import ListItem from "@tiptap/extension-list-item"; +import Link from "@tiptap/extension-link"; +import StarterKit from "@tiptap/starter-kit"; +import HTMLReactParser from "html-react-parser"; + +export const EditorHeader = ({ editor }) => { + const setLink = useCallback(() => { + const previousUrl = editor.getAttributes("link").href; + const url = window.prompt("URL", previousUrl); + + // cancelled + if (url === url) { + return; + } + // empty + if (url === "") { + editor.chain().focus().extendMarkRange("link").unsetLink().run(); + return; + } + // update link + editor.chain().focus().extendMarkRange("link").setLink({ href: url }).run(); + }, [editor]); + + if (!editor) { + return null; + } + + return ( +
      + + + + + + + + + + + + + + + +
      + ); +}; + +export const EditorFooter = ({ + editor, + content, + writeMode, + setWriteMode, + mode, + submitting, + onAddOrSaveClick, +}) => { + if (!editor) { + return null; + } + + return ( +
      +
      + + +
      + {submitting ? ( + + ) : ( + + )} +
      + ); +}; + +export default function RichTextEditor({ + content, + setContent, + mode, + isSubmitting, + setIsSubmmitting, + onAddOrSaveClick, +}) { + const [writeMode, setWriteMode] = useState(true); + const editor = useEditor({ + extensions: [ + Placeholder.configure({ + placeholder: "Write your answer or reply here...", + }), + Underline, + Superscript, + Subscript, + TextStyle.configure({ types: [ListItem.name] }), + Link.configure({ + protocols: [ + "ftp", + "mailto", + { + scheme: "tel", + optionalSlashes: true, + }, + ], + validate: (href) => /^https?:\/\//.test(href), + }), + StarterKit.configure({ + bulletList: { + keepMarks: true, + keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help + }, + orderedList: { + keepMarks: true, + keepAttributes: false, // TODO : Making this as `false` becase marks are not preserved when I try to preserve attrs, awaiting a bit of help + }, + }), + ], + editorProps: { + attributes: { + class: + "prose prose-sm prose-slate m-5 max-h-[20rem] min-h-[20rem] overflow-y-auto dark:prose-invert sm:prose-base focus:outline-none", + }, + }, + content: content, + onUpdate: ({ editor }) => { + setContent(editor.getHTML()); + }, + }); + + return ( +
      + {writeMode ? ( + <> + + + + ) : ( + <> +
      +
      + {HTMLReactParser(content)} +
      + + )} + +
      + ); +} diff --git a/src/components/SignInForm.jsx b/src/components/SignInForm.jsx index 988da64..3479247 100644 --- a/src/components/SignInForm.jsx +++ b/src/components/SignInForm.jsx @@ -161,7 +161,7 @@ export default function SignInForm() { /> Forgot password? diff --git a/src/components/SignUpForm.jsx b/src/components/SignUpForm.jsx index 6001d88..649b63d 100644 --- a/src/components/SignUpForm.jsx +++ b/src/components/SignUpForm.jsx @@ -222,14 +222,14 @@ export default function SignUpForm() { By creating an account, you accept our{" "} Terms & Conditions {" "} and{" "} Privacy Policy diff --git a/src/providers/QuestionsIdProvider.jsx b/src/providers/QuestionsIdProvider.jsx new file mode 100644 index 0000000..3828ff1 --- /dev/null +++ b/src/providers/QuestionsIdProvider.jsx @@ -0,0 +1,17 @@ +"use client"; + +import { createContext, useContext } from "react"; + +const QuestionsIdContext = createContext(); + +export function useQuestionsId() { + return useContext(QuestionsIdContext); +} + +export default function QuestionsIdProvider({ id, children }) { + return ( + + {children} + + ); +} diff --git a/src/utils/generateAvatar.js b/src/utils/generateAvatar.js new file mode 100644 index 0000000..2bbff5a --- /dev/null +++ b/src/utils/generateAvatar.js @@ -0,0 +1,28 @@ +import generateColor from "./generateColor"; + +export default function generateAvatar(fullName) { + const initials = fullName + .split(" ") + .map((name) => name.charAt(0)) + .join(""); + + const hexColor = generateColor(fullName).hex; + + return ` + + + ${initials} + + `; +} diff --git a/src/utils/generateColor.js b/src/utils/generateColor.js new file mode 100644 index 0000000..e1c8714 --- /dev/null +++ b/src/utils/generateColor.js @@ -0,0 +1,27 @@ +export default function generateColor(str) { + let hash = 0; + + for (let i = 0; i < str.length; i++) { + hash = str.charCodeAt(i) + ((hash << 5) - hash); + } + + // Introduce a random factor to the hash + const randomFactor = Math.random() * 0.5 + 0.5; + hash *= randomFactor; + + // Generate RGB values based on the hash + const r = (hash & 0xff0000) >> 16; + const g = (hash & 0x00ff00) >> 8; + const b = hash & 0x0000ff; + + // Convert RGB to HEX + const hexColor = `#${((1 << 24) | (r << 16) | (g << 8) | b) + .toString(16) + .slice(1)}`; + + // Return RGB and HEX values + return { + rgb: `rgb(${r},${g},${b})`, + hex: hexColor, + }; +} diff --git a/tailwind.config.js b/tailwind.config.js index 267b416..dcffdcc 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -26,5 +26,5 @@ module.exports = { }, }, darkMode: "class", - plugins: [], + plugins: [require("@tailwindcss/typography")], };