From 99720dd46d5727ba033d520334f89352a1e457b6 Mon Sep 17 00:00:00 2001 From: Barunes Padhy Date: Fri, 10 May 2024 21:35:44 +0300 Subject: [PATCH] Restructured frontend, data design, and created the "frontend" for the backend --- frontend/.env.editableview | 1 + frontend/.env.view | 1 + frontend/package-lock.json | 859 +++++++++++++++++- frontend/package.json | 20 +- frontend/public/data/shared/user-data.json | 11 +- frontend/src/App.jsx | 16 +- frontend/src/AppEditable.jsx | 71 ++ .../src/components/editable/blog-list.jsx | 96 ++ frontend/src/components/editable/blog.jsx | 165 ++++ .../src/components/editable/category-list.jsx | 81 ++ frontend/src/components/editable/home.jsx | 34 + .../editable/shared/card-list-viewer.jsx | 62 ++ .../{ => editable}/shared/category-bar.jsx | 2 +- .../{ => editable}/shared/footer.jsx | 6 +- .../{ => editable}/shared/navbar.jsx | 57 +- .../{ => editable}/shared/notification.jsx | 2 +- .../src/components/editable/shared/tiptap.jsx | 303 ++++++ frontend/src/components/home.jsx | 28 - .../components/{ => viewable}/blog-list.jsx | 4 +- .../src/components/{ => viewable}/blog.jsx | 4 +- .../{ => viewable}/category-list.jsx | 2 +- frontend/src/components/viewable/home.jsx | 44 + .../shared/card-list-viewer.jsx | 2 +- .../viewable/shared/category-bar.jsx | 55 ++ .../src/components/viewable/shared/footer.jsx | 35 + .../src/components/viewable/shared/navbar.jsx | 92 ++ .../viewable/shared/notification.jsx | 18 + frontend/src/index.css | 37 +- frontend/src/main.jsx | 12 +- 29 files changed, 2007 insertions(+), 113 deletions(-) create mode 100644 frontend/.env.editableview create mode 100644 frontend/.env.view create mode 100644 frontend/src/AppEditable.jsx create mode 100644 frontend/src/components/editable/blog-list.jsx create mode 100644 frontend/src/components/editable/blog.jsx create mode 100644 frontend/src/components/editable/category-list.jsx create mode 100644 frontend/src/components/editable/home.jsx create mode 100644 frontend/src/components/editable/shared/card-list-viewer.jsx rename frontend/src/components/{ => editable}/shared/category-bar.jsx (95%) rename frontend/src/components/{ => editable}/shared/footer.jsx (52%) rename frontend/src/components/{ => editable}/shared/navbar.jsx (56%) rename frontend/src/components/{ => editable}/shared/notification.jsx (80%) create mode 100644 frontend/src/components/editable/shared/tiptap.jsx delete mode 100644 frontend/src/components/home.jsx rename frontend/src/components/{ => viewable}/blog-list.jsx (96%) rename frontend/src/components/{ => viewable}/blog.jsx (97%) rename frontend/src/components/{ => viewable}/category-list.jsx (94%) create mode 100644 frontend/src/components/viewable/home.jsx rename frontend/src/components/{ => viewable}/shared/card-list-viewer.jsx (93%) create mode 100644 frontend/src/components/viewable/shared/category-bar.jsx create mode 100644 frontend/src/components/viewable/shared/footer.jsx create mode 100644 frontend/src/components/viewable/shared/navbar.jsx create mode 100644 frontend/src/components/viewable/shared/notification.jsx diff --git a/frontend/.env.editableview b/frontend/.env.editableview new file mode 100644 index 0000000..bb6041f --- /dev/null +++ b/frontend/.env.editableview @@ -0,0 +1 @@ +VITE_APP_VIEW_TYPE=editableview \ No newline at end of file diff --git a/frontend/.env.view b/frontend/.env.view new file mode 100644 index 0000000..a4b2440 --- /dev/null +++ b/frontend/.env.view @@ -0,0 +1 @@ +VITE_APP_VIEW_TYPE=view \ No newline at end of file diff --git a/frontend/package-lock.json b/frontend/package-lock.json index be4615a..1961e0c 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -11,6 +11,17 @@ "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.0", + "@tiptap/extension-blockquote": "^2.3.2", + "@tiptap/extension-color": "^2.3.2", + "@tiptap/extension-highlight": "^2.3.2", + "@tiptap/extension-image": "^2.3.2", + "@tiptap/extension-link": "^2.3.2", + "@tiptap/extension-list-item": "^2.3.2", + "@tiptap/extension-text-align": "^2.3.2", + "@tiptap/extension-text-style": "^2.3.2", + "@tiptap/extension-underline": "^2.3.2", + "@tiptap/react": "^2.3.2", + "@tiptap/starter-kit": "^2.3.2", "axios": "^1.6.8", "bootstrap": "^5.3.3", "html-react-parser": "^5.1.10", @@ -18,7 +29,8 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.22.3", "reactstrap": "^9.2.2", - "sass": "^1.75.0" + "sass": "^1.75.0", + "tiptap": "^1.32.2" }, "devDependencies": { "@types/react": "^18.2.66", @@ -294,7 +306,6 @@ "version": "7.24.4", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.24.4.tgz", "integrity": "sha512-zTvEBcghmeBma9QIGunWevvBAp4/Qu9Bdq+2k0Ot4fVMD6v3dsC9WOcRSKk7tRRyBM/53yKMJko9xOatGQAwSg==", - "dev": true, "bin": { "parser": "bin/babel-parser.js" }, @@ -1001,6 +1012,12 @@ "url": "https://opencollective.com/popperjs" } }, + "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==", + "peer": true + }, "node_modules/@remix-run/router": { "version": "1.15.3", "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", @@ -1217,6 +1234,439 @@ "win32" ] }, + "node_modules/@tiptap/core": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/core/-/core-2.3.2.tgz", + "integrity": "sha512-4sMpzYuxiG+fYMwPRXy+mLRVU315KEqzQUcBc2FEgSsmw9Kionykmkq3DvEco7rH8r0NdV/l9R49wVEtX54VqQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-blockquote": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-blockquote/-/extension-blockquote-2.3.2.tgz", + "integrity": "sha512-dyXx1hHAW/0BSxCUNWcxc8UN+s0wRTdtH46u6IEf91z+IOWjJwmSxT00+UMYh6hdOYDDsJYxPe9gcuSWYCIkCg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-bold": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bold/-/extension-bold-2.3.2.tgz", + "integrity": "sha512-Mdc0qOPeJxxt5kSYKpNs7TzbQHeVpbpxwafUrxrvfD2iOnJlwlNxVWsVulc1t5EA8NpbTqYJTPmAtv2h/qmsfw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-bubble-menu": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bubble-menu/-/extension-bubble-menu-2.3.2.tgz", + "integrity": "sha512-hg+ncQmoNngdeoUWBQs2AWzDO8YIrlAIgLmIponC+OSCZoVrri7LZ4N1uSp5B/U0lz5fSGUvsUNUs0le+MMr/Q==", + "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.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-bullet-list/-/extension-bullet-list-2.3.2.tgz", + "integrity": "sha512-nzvXSGxJuuZdQ6NE0gJ2GC+0gjXZTgU2+Z8TEKi7TYLUAjAoiU1Iniz1XA97cuFwVrNKp031IF1LivK085NqQA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-code": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code/-/extension-code-2.3.2.tgz", + "integrity": "sha512-LyIRBFJCxbgi96ejoeewESvfUf5igfngamZJK+uegfTcznimP0AjSWs3whJwZ9QXUsQrB9tIrWIG4GBtatp6qw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-code-block": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-code-block/-/extension-code-block-2.3.2.tgz", + "integrity": "sha512-Ng5dh8+FMD3pxaqZEDSRxTjgjPCNdEEVUTJnuljZXQ9ZxI9wVsKsGs53Hunpita4Qgk0DYhlfAvGUKCM0nCH4A==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-color": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-color/-/extension-color-2.3.2.tgz", + "integrity": "sha512-xPJyqDFkDI/jPW0SKPhARuSgvIiIUdcMS/i+nc4wSlShvUugcGNbd00LgDHNCUW0VpKQA/MMMtWzj9ZOPcjquQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/extension-text-style": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-document": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-document/-/extension-document-2.3.2.tgz", + "integrity": "sha512-EQcfkvA7lkZPKllhGo2jiEYLJyXhBFK7++oRatgbfgHEJ2uLBGv6ys7WLCeRA/ntcaWTH3rlS+HR/Y8/nnyQYg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-dropcursor": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-dropcursor/-/extension-dropcursor-2.3.2.tgz", + "integrity": "sha512-r7JJn9dEnIRDdbnTCAUFCWX4OPsR48+4OEm5eGlysEaD2h4z0G1AaK5XXwOoQhP3WP2LHHjL4LahlYZvltzFzw==", + "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.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-floating-menu/-/extension-floating-menu-2.3.2.tgz", + "integrity": "sha512-7MerFtr+7y0lThKEcNeM0B5LMWqP3RqmMZYJEOCIL20mIINYz5JzSIMQQujmeU5tcqI12O1u7jbRoxRmZrsXxw==", + "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.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-gapcursor/-/extension-gapcursor-2.3.2.tgz", + "integrity": "sha512-PSry4JHUIOhXytvYUQGtYgfIKCIhnmbKksZ8/CfCaKgGJpjOpnzqRG5FnYXZB7NiqouABreM7+IgkH0mOLq6HQ==", + "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.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-hard-break/-/extension-hard-break-2.3.2.tgz", + "integrity": "sha512-Oy/Dj75kw/tyNyrcFf97r872NZggISfvabTptH8j1gFPg/XzT5ERcT2fvgpbsBx0WWlXOaFkC1owta6kS6MZpg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-heading": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-heading/-/extension-heading-2.3.2.tgz", + "integrity": "sha512-KBew4QCnYASBPEJlZ4vKQnm4R9B206H8kE5+hq8OOyF3FVkR6FgF/AbY/E/4/+2blx82PGp+9gvPUVpEv36ifQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-highlight": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-highlight/-/extension-highlight-2.3.2.tgz", + "integrity": "sha512-OycPrcLTwRI+vi1p63J+d4ta3TESRoEBJP7qG1oxATLkCNvekNl+1LMgkdohJMBHMF3smCA9BAYUqtQqUhYD3w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-history": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-history/-/extension-history-2.3.2.tgz", + "integrity": "sha512-LTon7ys+C6wLmN/nXYkr1pDxIiIv0Czn4US7I/1b8Ws2N6PU+nMm4r7Uj8hKrDYL8yPQUaS4gIs1hhOwJ8UjtA==", + "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.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-2.3.2.tgz", + "integrity": "sha512-nz4GcYvZmJOX20GAjR5ymZgzQCbhnK/rmcunQf4zkl4LA5sXm70P70I9bDtrT/mgmz5dnBUTkVAkLTtKbovdDQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0", + "@tiptap/pm": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-image": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-image/-/extension-image-2.3.2.tgz", + "integrity": "sha512-otkhqToHnjjpWOIswuotfK/PTPEOhhKRFPf1NuXvqHpMNulz+J1uIuA9R/B1m+bXkxZzCMKkWQi50vjqH9idVg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-italic": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-italic/-/extension-italic-2.3.2.tgz", + "integrity": "sha512-6RJmexu/E+JP2+lhzJLV+5KZJiTrJE+p/hnDk13CBK2VgiwcJYmcZSVk+Yk6Suwrb1qTAosu8paKIwVJa/VMUg==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-link": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-link/-/extension-link-2.3.2.tgz", + "integrity": "sha512-Bs3PbYmXj5bzUzPdFkcuflxZkdI2nCIJY2YO5TykANos68FrRtxyRKCxSxyZABzKjctT/UUVSap7JUVQ+i/bSw==", + "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.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-list-item/-/extension-list-item-2.3.2.tgz", + "integrity": "sha512-vgT7tkSZd99xAEph9quPlVdRkgPU4GJp9K7bNS8Y7GnSLU0KkDHbtDpb0pyz76HVpeOnt/QGmtqF14Il9T2IPQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-ordered-list": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-ordered-list/-/extension-ordered-list-2.3.2.tgz", + "integrity": "sha512-eMnQDgWpaQ3sdlFg1M85oziFYl2h/GRBjUt4JhF5kyWpHOYDj1/bX1fndZOBQ5xaoNlbcaeEkIc53xVX4ZV9tw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-paragraph": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-paragraph/-/extension-paragraph-2.3.2.tgz", + "integrity": "sha512-bKzL4NXp0pDM/Q5ZCpjLxjQU4DwoWc6CDww1M4B4dp1sfiXiE2P7EOCMM2TfJOqNPUFpp5RcFKKcxC2Suj8W4w==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-strike": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-strike/-/extension-strike-2.3.2.tgz", + "integrity": "sha512-gi16YtLnXKPubxafvcGSAELac4i8S6Eb9Av0AaH6QH9H9zzSHN7qOrX930Tp2Pod5a/a82kk7kN7IB6htAeaYA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-text": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text/-/extension-text-2.3.2.tgz", + "integrity": "sha512-a3whwDyyOsrmOQbfeY+Fm5XypSRgT3IGqWgz0r4U7oko57/X6Env08F1Ie2e2UkQw9B1MoW9cm3dC6jvrdzzYA==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-text-align": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-align/-/extension-text-align-2.3.2.tgz", + "integrity": "sha512-PyTI0S/ASafxB8iq0tO/LKV245OC/r2mmLWre6Utp1Y7WXDAvJKITGCpuIBfV/Hrip37WjsEz0NSKnuUt4btdQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-text-style": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-text-style/-/extension-text-style-2.3.2.tgz", + "integrity": "sha512-y0ye1BqDSVqewLTcW9Rg4hXykZ8eTOEhb5KCLbcYYsX4LeKQv/gNQjj/Oy9+w177ts32gvbEuypOEKJJo/oBBw==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/extension-underline": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/extension-underline/-/extension-underline-2.3.2.tgz", + "integrity": "sha512-ZmhWG8gMXk62AhpIMuOofe8GWbkXBW1uYHG55Q9r7MmglESLJm13S5k8JVfOmOMKGzfE23A6yQkojnksAiSGoQ==", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + }, + "peerDependencies": { + "@tiptap/core": "^2.0.0" + } + }, + "node_modules/@tiptap/pm": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/pm/-/pm-2.3.2.tgz", + "integrity": "sha512-39Bmg7XqWWJg/G5YvWc3QVEPmFNpuMa05gw0Ap7KAKHnHiwl87hosOIDD8dtdcMrtgJL9NwBfUjEJ3ne53U9Ag==", + "peer": true, + "dependencies": { + "prosemirror-changeset": "^2.2.1", + "prosemirror-collab": "^1.3.1", + "prosemirror-commands": "^1.5.2", + "prosemirror-dropcursor": "^1.8.1", + "prosemirror-gapcursor": "^1.3.2", + "prosemirror-history": "^1.3.2", + "prosemirror-inputrules": "^1.3.0", + "prosemirror-keymap": "^1.2.2", + "prosemirror-markdown": "^1.12.0", + "prosemirror-menu": "^1.2.4", + "prosemirror-model": "^1.19.4", + "prosemirror-schema-basic": "^1.2.2", + "prosemirror-schema-list": "^1.3.0", + "prosemirror-state": "^1.4.3", + "prosemirror-tables": "^1.3.5", + "prosemirror-trailing-node": "^2.0.7", + "prosemirror-transform": "^1.8.0", + "prosemirror-view": "^1.32.7" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, + "node_modules/@tiptap/react": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/react/-/react-2.3.2.tgz", + "integrity": "sha512-NDvt3XfPn/6V3iAX3lqYGIuFPQgirUGKRyzfHl7ssIfpoY5VR5tRJkU4NigOr63NONrsgCgqJISG/nPY6YGw8w==", + "dependencies": { + "@tiptap/extension-bubble-menu": "^2.3.2", + "@tiptap/extension-floating-menu": "^2.3.2" + }, + "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.3.2", + "resolved": "https://registry.npmjs.org/@tiptap/starter-kit/-/starter-kit-2.3.2.tgz", + "integrity": "sha512-7KdOxnYcmg2x2XGOAYssoz7iHLGDznoS5cNHjiOzuca+mO+5YutQ3j5yr/6+ithkX9/HZZwHJFQ6KORIARoNQg==", + "dependencies": { + "@tiptap/core": "^2.3.2", + "@tiptap/extension-blockquote": "^2.3.2", + "@tiptap/extension-bold": "^2.3.2", + "@tiptap/extension-bullet-list": "^2.3.2", + "@tiptap/extension-code": "^2.3.2", + "@tiptap/extension-code-block": "^2.3.2", + "@tiptap/extension-document": "^2.3.2", + "@tiptap/extension-dropcursor": "^2.3.2", + "@tiptap/extension-gapcursor": "^2.3.2", + "@tiptap/extension-hard-break": "^2.3.2", + "@tiptap/extension-heading": "^2.3.2", + "@tiptap/extension-history": "^2.3.2", + "@tiptap/extension-horizontal-rule": "^2.3.2", + "@tiptap/extension-italic": "^2.3.2", + "@tiptap/extension-list-item": "^2.3.2", + "@tiptap/extension-ordered-list": "^2.3.2", + "@tiptap/extension-paragraph": "^2.3.2", + "@tiptap/extension-strike": "^2.3.2", + "@tiptap/extension-text": "^2.3.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/ueberdosis" + } + }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -1314,6 +1764,20 @@ "vite": "^4.2.0 || ^5.0.0" } }, + "node_modules/@vue/compiler-sfc": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/@vue/compiler-sfc/-/compiler-sfc-2.7.16.tgz", + "integrity": "sha512-KWhJ9k5nXuNtygPU7+t1rX6baZeqOYLEforUPjgNDBnLicfHCoi48H87Q8XyLZOrNNsmhuwKqtpDQWjEFe6Ekg==", + "peer": true, + "dependencies": { + "@babel/parser": "^7.23.5", + "postcss": "^8.4.14", + "source-map": "^0.6.1" + }, + "optionalDependencies": { + "prettier": "^1.18.2 || ^2.0.0" + } + }, "node_modules/acorn": { "version": "8.11.3", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", @@ -1387,8 +1851,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/array-buffer-byte-length": { "version": "1.0.1", @@ -1786,6 +2249,12 @@ "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, + "node_modules/crelt": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/crelt/-/crelt-1.0.6.tgz", + "integrity": "sha512-VQ2MBenTq1fWZUH9DJNGti7kKv6EeAuYr3cLwxUWhIu1baTaXh4Ib5W2CqHVqib4/MqbYGJqiL3Zb8GJZr3l4g==", + "peer": true + }, "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", @@ -1856,6 +2325,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/de-indent": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/de-indent/-/de-indent-1.0.2.tgz", + "integrity": "sha512-e/1zu3xH5MQryN2zdVaF0OrdNLUbvWxzMbi+iNA6Bky7l1RoP8a2fIbRocyHclXt/arDrrR6lL3TqFD9pMQTsg==", + "peer": true + }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2910,6 +3385,15 @@ "node": ">= 0.4" } }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "peer": true, + "bin": { + "he": "bin/he" + } + }, "node_modules/html-dom-parser": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/html-dom-parser/-/html-dom-parser-5.0.8.tgz", @@ -3506,6 +3990,20 @@ "node": ">= 0.8.0" } }, + "node_modules/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "peer": true, + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "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/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", @@ -3547,6 +4045,29 @@ "yallist": "^3.0.2" } }, + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "peer": true, + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "peer": true + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3588,7 +4109,6 @@ "version": "3.3.7", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.7.tgz", "integrity": "sha512-eSRppjcPIatRIMC1U6UngP8XFcz8MQWGQdt1MTBQ7NaAmvXDfvNxbvWV3x2y6CdEUciCSsDHDQZbhYaB8QEo2g==", - "dev": true, "funding": [ { "type": "github", @@ -3758,6 +4278,11 @@ "node": ">= 0.8.0" } }, + "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", @@ -3836,8 +4361,7 @@ "node_modules/picocolors": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==", - "dev": true + "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" }, "node_modules/picomatch": { "version": "2.3.1", @@ -3863,7 +4387,6 @@ "version": "8.4.38", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", - "dev": true, "funding": [ { "type": "opencollective", @@ -3896,6 +4419,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "optional": true, + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/prop-types": { "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", @@ -3906,6 +4445,201 @@ "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==", + "peer": true, + "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==", + "peer": true, + "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.4.0", + "resolved": "https://registry.npmjs.org/prosemirror-history/-/prosemirror-history-1.4.0.tgz", + "integrity": "sha512-UUiGzDVcqo1lovOPdi9YxxUps3oBFWAIYkXLu3Ot+JPv1qzVogRbcizxK3LhHmtaUxclohgiOVesRw5QSlMnbQ==", + "peer": true, + "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.4.0", + "resolved": "https://registry.npmjs.org/prosemirror-inputrules/-/prosemirror-inputrules-1.4.0.tgz", + "integrity": "sha512-6ygpPRuTJ2lcOXs9JkefieMst63wVJBgHZGl5QOytN7oSZs3Co/BYbc3Yx9zm9H37Bxw8kVzCnDsihsVsL4yEg==", + "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.12.0", + "resolved": "https://registry.npmjs.org/prosemirror-markdown/-/prosemirror-markdown-1.12.0.tgz", + "integrity": "sha512-6F5HS8Z0HDYiS2VQDZzfZP6A0s/I0gbkJy8NCzzDMtcsz3qrfqyroMMeoSjAmOhDITyon11NbXSzztfKi+frSQ==", + "peer": true, + "dependencies": { + "markdown-it": "^14.0.0", + "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==", + "peer": true, + "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.21.0", + "resolved": "https://registry.npmjs.org/prosemirror-model/-/prosemirror-model-1.21.0.tgz", + "integrity": "sha512-zLpS1mVCZLA7VTp82P+BfMiYVPcX1/z0Mf3gsjKZtzMWubwn2pN7CceMV0DycjlgE5JeXPR7UF4hJPbBV98oWA==", + "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==", + "peer": true, + "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.7", + "resolved": "https://registry.npmjs.org/prosemirror-tables/-/prosemirror-tables-1.3.7.tgz", + "integrity": "sha512-oEwX1wrziuxMtwFvdDWSFHVUWrFJWt929kVVfHvtTi8yvw+5ppxjXZkMG/fuTdFo+3DXyIPSKfid+Be1npKXDA==", + "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.8", + "resolved": "https://registry.npmjs.org/prosemirror-trailing-node/-/prosemirror-trailing-node-2.0.8.tgz", + "integrity": "sha512-ujRYhSuhQb1Jsarh1IHqb2KoSnRiD7wAMDGucP35DN7j5af6X7B18PfdPIrbwsPTqIAj0fyOvxbuPsWhNvylmA==", + "peer": true, + "dependencies": { + "@remirror/core-constants": "^2.0.2", + "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-trailing-node/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "peer": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prosemirror-transform": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/prosemirror-transform/-/prosemirror-transform-1.9.0.tgz", + "integrity": "sha512-5UXkr1LIRx3jmpXXNKDhv8OyAOeLTGuXNwdVfg8x27uASna/wQkr9p6fD3eupGOi4PLJfbezxTyi/7fSJypXHg==", + "dependencies": { + "prosemirror-model": "^1.21.0" + } + }, + "node_modules/prosemirror-view": { + "version": "1.33.6", + "resolved": "https://registry.npmjs.org/prosemirror-view/-/prosemirror-view-1.33.6.tgz", + "integrity": "sha512-zRLUNgLIQfd8IfGprsXxWTjdA8xEAFJe8cDNrOptj6Mop9sj+BMeVbJvceyAYCm5G2dOdT2prctH7K9dfnpIMw==", + "dependencies": { + "prosemirror-model": "^1.20.0", + "prosemirror-state": "^1.0.0", + "prosemirror-transform": "^1.1.0" + } + }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", @@ -3920,6 +4654,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "peer": true, + "engines": { + "node": ">=6" + } + }, "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", @@ -4204,6 +4947,12 @@ "fsevents": "~2.3.2" } }, + "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==", + "peer": true + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -4366,6 +5115,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/source-map-js": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", @@ -4519,6 +5277,59 @@ "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, + "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/tiptap": { + "version": "1.32.2", + "resolved": "https://registry.npmjs.org/tiptap/-/tiptap-1.32.2.tgz", + "integrity": "sha512-5IwVj8nGo8y5V3jbdtoEd7xNUsi8Q0N6WV2Nfs70olqz3fldXkiImBrDhZJ4Anx8vhyP6PIBttrg0prFVmwIvw==", + "dependencies": { + "prosemirror-commands": "^1.1.4", + "prosemirror-dropcursor": "^1.3.2", + "prosemirror-gapcursor": "^1.1.5", + "prosemirror-inputrules": "^1.1.3", + "prosemirror-keymap": "^1.1.4", + "prosemirror-model": "^1.13.1", + "prosemirror-state": "^1.3.3", + "prosemirror-view": "^1.16.5", + "tiptap-commands": "^1.17.1", + "tiptap-utils": "^1.13.1" + }, + "peerDependencies": { + "vue": "^2.5.17", + "vue-template-compiler": "^2.5.17" + } + }, + "node_modules/tiptap-commands": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/tiptap-commands/-/tiptap-commands-1.17.1.tgz", + "integrity": "sha512-CyGvMD/c6fNer5LThWGtrVMXHAqHn93ivGQpqJ58x3HNZFuoIiF9QTWXAiWbY/4QrG0ANYHKCSe9n5afickTqw==", + "dependencies": { + "prosemirror-commands": "^1.1.4", + "prosemirror-inputrules": "^1.1.2", + "prosemirror-model": "^1.13.1", + "prosemirror-schema-list": "^1.1.4", + "prosemirror-state": "^1.3.3", + "prosemirror-tables": "^1.1.1", + "tiptap-utils": "^1.13.1" + } + }, + "node_modules/tiptap-utils": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/tiptap-utils/-/tiptap-utils-1.13.1.tgz", + "integrity": "sha512-RoCvMfkdu7fp9u7nsRr1OgsYU8RFjoHKHEKpx075rJ9X0t+j5Vxah9n6QzTTr4yjvcavq22WO2flFacm36zYtA==", + "dependencies": { + "prosemirror-model": "^1.13.1", + "prosemirror-state": "^1.3.3", + "prosemirror-tables": "^1.1.1" + } + }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -4636,6 +5447,12 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "peer": true + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -4745,6 +5562,32 @@ } } }, + "node_modules/vue": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue/-/vue-2.7.16.tgz", + "integrity": "sha512-4gCtFXaAA3zYZdTp5s4Hl2sozuySsgz4jy1EnpBHNfpMa9dK1ZCG7viqBPCwXtmgc8nHqUsAu3G4gtmXkkY3Sw==", + "deprecated": "Vue 2 has reached EOL and is no longer actively maintained. See https://v2.vuejs.org/eol/ for more details.", + "peer": true, + "dependencies": { + "@vue/compiler-sfc": "2.7.16", + "csstype": "^3.1.0" + } + }, + "node_modules/vue-template-compiler": { + "version": "2.7.16", + "resolved": "https://registry.npmjs.org/vue-template-compiler/-/vue-template-compiler-2.7.16.tgz", + "integrity": "sha512-AYbUWAJHLGGQM7+cNTELw+KsOG9nl2CnSv467WobS5Cv9uk3wFcnr1Etsz2sEIHEZvw1U+o9mRlEO6QbZvUPGQ==", + "peer": true, + "dependencies": { + "de-indent": "^1.0.2", + "he": "^1.2.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/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", diff --git a/frontend/package.json b/frontend/package.json index cfff45e..7aa7761 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -4,8 +4,10 @@ "version": "0.0.0", "type": "module", "scripts": { - "dev": "vite --host 0.0.0.0 --port 3000", - "build": "vite build", + "dev:view": "REACT_APP_VIEW_TYPE=view vite --host 0.0.0.0 --port 3000 --mode view", + "dev:editableview": "REACT_APP_VIEW_TYPE=editableview vite --host 0.0.0.0 --port 3000 --mode editableview", + "build:view": "REACT_APP_VIEW_TYPE=view vite build", + "build:editableview": "REACT_APP_VIEW_TYPE=editableview vite build", "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", "preview": "vite preview" }, @@ -13,6 +15,17 @@ "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "^0.2.0", + "@tiptap/extension-blockquote": "^2.3.2", + "@tiptap/extension-color": "^2.3.2", + "@tiptap/extension-highlight": "^2.3.2", + "@tiptap/extension-image": "^2.3.2", + "@tiptap/extension-link": "^2.3.2", + "@tiptap/extension-list-item": "^2.3.2", + "@tiptap/extension-text-align": "^2.3.2", + "@tiptap/extension-text-style": "^2.3.2", + "@tiptap/extension-underline": "^2.3.2", + "@tiptap/react": "^2.3.2", + "@tiptap/starter-kit": "^2.3.2", "axios": "^1.6.8", "bootstrap": "^5.3.3", "html-react-parser": "^5.1.10", @@ -20,7 +33,8 @@ "react-dom": "^18.2.0", "react-router-dom": "^6.22.3", "reactstrap": "^9.2.2", - "sass": "^1.75.0" + "sass": "^1.75.0", + "tiptap": "^1.32.2" }, "devDependencies": { "@types/react": "^18.2.66", diff --git a/frontend/public/data/shared/user-data.json b/frontend/public/data/shared/user-data.json index 9e840a6..74c2204 100644 --- a/frontend/public/data/shared/user-data.json +++ b/frontend/public/data/shared/user-data.json @@ -1,13 +1,6 @@ { "name": "John Doe", - "greetingLine": "Hi! My name is", - "tagLine": "Me like tech. Checkout my blog where I have nice stuff!", + "introContent": "Write something about yourself here!", "profilePhoto": "homepage/media/profile.png", - "links": { - "instagram": "" - }, - "contact":{ - "email":"", - "phone": "" - } + "builtWith": true } diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx index 47cb752..4a20064 100644 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -4,16 +4,16 @@ import {useEffect, useState} from 'react'; import 'bootstrap/dist/css/bootstrap.min.css'; //Import Views -import Home from './components/home'; -import CategoryList from './components/category-list'; -import BlogList from './components/blog-list'; -import Blog from './components/blog'; +import Home from './components/viewable/home'; +import CategoryList from './components/viewable/category-list'; +import BlogList from './components/viewable/blog-list'; +import Blog from './components/viewable/blog'; //Import Shared Views -import Header from './components/shared/navbar'; -import Footer from './components/shared/footer'; -import Notification from './components/shared/notification'; +import Header from './components/viewable/shared/navbar'; +import Footer from './components/viewable/shared/footer'; +import Notification from './components/viewable/shared/notification'; //Import Services import DataService from './services/data-service' @@ -53,7 +53,7 @@ function App() {
- +
} /> diff --git a/frontend/src/AppEditable.jsx b/frontend/src/AppEditable.jsx new file mode 100644 index 0000000..a4b3da5 --- /dev/null +++ b/frontend/src/AppEditable.jsx @@ -0,0 +1,71 @@ +import './App.css'; +import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'; +import {useEffect, useState} from 'react'; +import 'bootstrap/dist/css/bootstrap.min.css'; + +//Import Views +import Home from './components/editable/home'; +import CategoryList from './components/editable/category-list'; +import BlogList from './components/editable/blog-list'; +import Blog from './components/editable/blog'; + + +//Import Shared Views +import Header from './components/editable/shared/navbar'; +import Footer from './components/editable/shared/footer'; +import Notification from './components/editable/shared/notification'; + +//Import Services +import DataService from './services/data-service' + +function AppEditable() { + const [userData, setUserData] = useState(null); + const [themeConfig, setThemeConfig] = useState(null); + const [globalTheme, setGlobalTheme] = useState("lightTheme"); + const [isOpen, setIsOpen] = useState(false); + const [notificationMessage, setNotificationMessage] = useState("") + + const notificationToggler = (message) => { + setIsOpen(true) + setNotificationMessage(message) + setTimeout(() => { + setIsOpen(false) + }, 3500) + } + + useEffect(() => { + DataService.getData('shared/user-data').then( response => + setUserData(response.data) + ) + DataService.getData('shared/theme-config').then( response =>{ + setThemeConfig(response.data) + setGlobalTheme(response.data.defaultTheme) + } + ) + },[]) + + const themeSwitcher = (theme) => { + setGlobalTheme(theme); + } + + if (themeConfig) + return ( +
+ +
+ +
+ + } /> + } /> + } /> + } /> + +
+
+ +
+ ); +} + +export default AppEditable; \ No newline at end of file diff --git a/frontend/src/components/editable/blog-list.jsx b/frontend/src/components/editable/blog-list.jsx new file mode 100644 index 0000000..3ee680d --- /dev/null +++ b/frontend/src/components/editable/blog-list.jsx @@ -0,0 +1,96 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; +import { useEffect, useState } from 'react'; +import DataService from '../../services/data-service'; +import MediaService from '../../services/media-service'; +import CardListViewer from './shared/card-list-viewer'; +import CategoryBar from './shared/category-bar'; +import { + Spinner, + Container, + Card, + Row, + Col, + CardImg, + CardTitle, + CardText, + CardBody, + Button, + ButtonGroup +} from 'reactstrap'; +import { Link, useParams } from 'react-router-dom'; + +function BlogList(props) { + + const { categoryID } = useParams(); + + const GlobalTheme = props.GlobalTheme; + const ThemeConfig = props.ThemeConfig; + + const [categoryData, setCategoryData] = useState('loading'); + const [featuredBlogData, setFeaturedBlogData] = useState('loading'); + const [currentPage, setCurrentPage] = useState('loading'); + + useEffect(() => { + DataService.getData(`category/${categoryID}/category-data`).then(response =>{ + setCategoryData(response.data); + if (response.data.featuredBlog){ + DataService.getData(`blogs/${response.data.featuredBlog}/blog-data`).then(response => + setFeaturedBlogData(response.data) + ); + } + else + setFeaturedBlogData("nodata") + } + ); + }, [categoryID]); + + if (GlobalTheme && ThemeConfig) { + return ( + + + + +
+ + + + {`Blogs in ${categoryData.name}`} + + + +
+
+

+ {`All blogs`} +

+ { + categoryData === 'loading' ? : + categoryData.blogMetadata.map((item, index) => ( + + )) + } + + + + +
+ +
+
+ ); + } else { + return null; + } + +} + +export default BlogList \ No newline at end of file diff --git a/frontend/src/components/editable/blog.jsx b/frontend/src/components/editable/blog.jsx new file mode 100644 index 0000000..ddbc490 --- /dev/null +++ b/frontend/src/components/editable/blog.jsx @@ -0,0 +1,165 @@ +import { useEffect, useState } from 'react'; +import parse from 'html-react-parser'; + +import DataService from '../../services/data-service'; +import MediaService from '../../services/media-service' +import CategoryBar from './shared/category-bar'; +import EditorComponent from './shared/tiptap'; + +import { + Container,Row, Col,Spinner, UncontrolledCollapse, Button, ButtonGroup, Card, CardBody +} from 'reactstrap'; +import { Link, useParams } from 'react-router-dom'; + +function Blog(props) { + + const { blogID } = useParams(); + + const GlobalTheme = props.GlobalTheme; + const ThemeConfig = props.ThemeConfig; + + const [blogData, setBlogData] = useState([]); + const [blogContent, setBlogContent] = useState() + + const replace = (node) => { + if (node.type === 'tag') { + if (node.name === 'a') { + const newClasses = `${ThemeConfig[GlobalTheme].linkBackground} ${ThemeConfig[GlobalTheme].linkTextColor}`; + const existingClasses = node.attribs.class ? `${node.attribs.class} ` : ''; + node.attribs.class = `${existingClasses}${newClasses}`; + node.attribs.rel = 'noopener noreferrer'; + node.attribs.target = '_blank'; + } + if (node.name === 'img') { + const newClasses = `img-fluid mt-2 mb-2 rounded`; + const existingClasses = node.attribs.class ? `${node.attribs.class} ` : ''; + node.attribs.class = `${existingClasses}${newClasses}`; + } + } + }; + + useEffect(() => { + DataService.getData(`blogs/${blogID}/blog-data`).then(response =>{ + setBlogData(response.data) + const parsedContent = parse(response.data.contentBody, { replace }); + setBlogContent(parsedContent); + } + ); + }, []); + + useEffect(() => { + if (blogData.contentBody){ + const parsedContent = parse(blogData.contentBody, { replace }); + setBlogContent(parsedContent); + } + }, [GlobalTheme]) + + if (GlobalTheme && ThemeConfig && blogData) { + return ( + + + + + Banner + + + + + = 765 ? '6':''}`}> +

{blogData.name}

+

{blogData.description}

+
+ + + + + + + + + + + + + +
+ + +
+ + + +
+ +
+ + + + + + + + + + + + + + +
+ ); + } else { + return () + } +} + +export default Blog \ No newline at end of file diff --git a/frontend/src/components/editable/category-list.jsx b/frontend/src/components/editable/category-list.jsx new file mode 100644 index 0000000..52d20e2 --- /dev/null +++ b/frontend/src/components/editable/category-list.jsx @@ -0,0 +1,81 @@ +import { useEffect, useState } from 'react'; + +//import services +import DataService from '../../services/data-service'; + +//import views +import CardListViewer from './shared/card-list-viewer'; + +import { + Spinner, + Row, + Col, + Container, + Card, + CardImg, + CardTitle, + CardText, + CardBody, + Button, + ButtonGroup +} from 'reactstrap'; +import { Link } from 'react-router-dom'; + +function Blogs(props) { + const GlobalTheme = props.GlobalTheme; + const ThemeConfig = props.ThemeConfig; + + const [categoryMetadata, setCategoryMetadata] = useState([]); + + useEffect(() => { + DataService.getData('category/category-metadata').then(response => + setCategoryMetadata(response.data) + ); + }, []); + + if (GlobalTheme && ThemeConfig) { + return ( + + + + +
+ + + + {"Categories"} + + + +
+ +
+ + {categoryMetadata.length > 0 ? + categoryMetadata.map((item, index) => ( + + )) : } + + + + +
+ + +
+
+ ); + } else { + return null; + } +} + +export default Blogs; diff --git a/frontend/src/components/editable/home.jsx b/frontend/src/components/editable/home.jsx new file mode 100644 index 0000000..d4aa4a6 --- /dev/null +++ b/frontend/src/components/editable/home.jsx @@ -0,0 +1,34 @@ +import { Container, Spinner, Input, InputGroup, InputGroupText, Button, ButtonGroup } from 'reactstrap'; +import EditorComponent from './shared/tiptap'; +import MediaService from '../../services/media-service' + +function HomePage(props) { + const UserData = props.UserData ? props.UserData : Loading... + const GlobalTheme = props.GlobalTheme; + const ThemeConfig = props.ThemeConfig; + if (GlobalTheme && ThemeConfig) + return ( + +
+ {UserData.profilePhoto !== "" ? : ""} +
+ <> + + + Name + + + + + + + + + +
+
+
+ ); +} + +export default HomePage; \ No newline at end of file diff --git a/frontend/src/components/editable/shared/card-list-viewer.jsx b/frontend/src/components/editable/shared/card-list-viewer.jsx new file mode 100644 index 0000000..0ad234c --- /dev/null +++ b/frontend/src/components/editable/shared/card-list-viewer.jsx @@ -0,0 +1,62 @@ +import { useEffect, useState } from 'react'; +import MediaService from '../../../services/media-service' +import { + Spinner, + Card, + CardImg, + CardTitle, + CardText, + CardBody, + Input, InputGroup, InputGroupText +} from 'reactstrap'; +import { Link } from 'react-router-dom'; + +function CardListViewer(props) { + + + const itemObject = props.itemObject + + if (props.totalItems > 0 && itemObject && Object.keys(itemObject).length !== 0) + return ( + + {itemObject.coverImage !== "" ? : ""} + + + + + Name + + + + + + + + Description + + + + + + + + + Tagline + + + + + + + + Open this resource + + + + + ) +else + return(

No items found in this section

) +} + +export default CardListViewer \ No newline at end of file diff --git a/frontend/src/components/shared/category-bar.jsx b/frontend/src/components/editable/shared/category-bar.jsx similarity index 95% rename from frontend/src/components/shared/category-bar.jsx rename to frontend/src/components/editable/shared/category-bar.jsx index ba7e223..5a66fca 100644 --- a/frontend/src/components/shared/category-bar.jsx +++ b/frontend/src/components/editable/shared/category-bar.jsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import DataService from '../../services/data-service'; +import DataService from '../../../services/data-service'; import { Link } from 'react-router-dom'; import { Container, Row, Col, Button, Spinner, ListGroup, ListGroupItem, ButtonGroup } from 'reactstrap'; diff --git a/frontend/src/components/shared/footer.jsx b/frontend/src/components/editable/shared/footer.jsx similarity index 52% rename from frontend/src/components/shared/footer.jsx rename to frontend/src/components/editable/shared/footer.jsx index c2bbe13..2ff8dea 100644 --- a/frontend/src/components/shared/footer.jsx +++ b/frontend/src/components/editable/shared/footer.jsx @@ -19,8 +19,10 @@ const Footer = (props) => { -
- {new Date().getFullYear()}, { UserData ? UserData.name : Loading... } +
+ {new Date().getFullYear()}, { UserData ? UserData.name : Loading... } +
+ Built with Rangolio
diff --git a/frontend/src/components/shared/navbar.jsx b/frontend/src/components/editable/shared/navbar.jsx similarity index 56% rename from frontend/src/components/shared/navbar.jsx rename to frontend/src/components/editable/shared/navbar.jsx index 2bbc4d2..2920fc2 100644 --- a/frontend/src/components/shared/navbar.jsx +++ b/frontend/src/components/editable/shared/navbar.jsx @@ -13,7 +13,7 @@ import { Button, ButtonGroup, Label, Input } from 'reactstrap'; import { useState, useEffect } from 'react'; -import MediaService from '../../services/media-service' +import MediaService from '../../../services/media-service' import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faSun, faMoon, faPen } from '@fortawesome/free-solid-svg-icons'; import { Link } from 'react-router-dom'; @@ -51,41 +51,38 @@ function Header(props) { src={MediaService.getMedia(UserData.profilePhoto)} /> : "" } - -
diff --git a/frontend/src/components/shared/notification.jsx b/frontend/src/components/editable/shared/notification.jsx similarity index 80% rename from frontend/src/components/shared/notification.jsx rename to frontend/src/components/editable/shared/notification.jsx index d03f9c4..ffafe5c 100644 --- a/frontend/src/components/shared/notification.jsx +++ b/frontend/src/components/editable/shared/notification.jsx @@ -7,7 +7,7 @@ function Notification(props) { - {props.notificationMessage} + {props.message} diff --git a/frontend/src/components/editable/shared/tiptap.jsx b/frontend/src/components/editable/shared/tiptap.jsx new file mode 100644 index 0000000..82e9391 --- /dev/null +++ b/frontend/src/components/editable/shared/tiptap.jsx @@ -0,0 +1,303 @@ +import React, { useCallback } from 'react' +import { + Button, ButtonGroup, Label, Input } from 'reactstrap'; +import { Color } from '@tiptap/extension-color' +import ListItem from '@tiptap/extension-list-item' +import TextStyle from '@tiptap/extension-text-style' +import Highlight from '@tiptap/extension-highlight' +import TextAlign from '@tiptap/extension-text-align' +import Underline from '@tiptap/extension-underline' +import Blockquote from '@tiptap/extension-blockquote' +import Link from '@tiptap/extension-link' +import { EditorProvider, useCurrentEditor } from '@tiptap/react' +import StarterKit from '@tiptap/starter-kit' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faBold, faItalic, + faUnderline, faAlignLeft, + faAlignCenter, faAlignRight, + faAlignJustify, faHighlighter, + faStrikethrough, faCode, + faParagraph, faListUl, + faListOl, faQuoteLeft, + faQuoteRight, faRulerHorizontal, + faRotateLeft, faRotateRight, + faBars, faLink } from '@fortawesome/free-solid-svg-icons'; + +const MenuBar = (props) => { + const { editor } = useCurrentEditor() + + const setLink = useCallback(() => { + const previousUrl = editor.getAttributes('link').href + const url = window.prompt('URL', previousUrl) + + // cancelled + if (url === null) { + 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 ( + <> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ) +} + +const extensions = [ + Color.configure({ types: [TextStyle.name, ListItem.name] }), + TextStyle.configure({ types: [ListItem.name] }), + StarterKit.configure({ + bulletList: { + keepMarks: true, + keepAttributes: false, + }, + orderedList: { + keepMarks: true, + keepAttributes: false, + }, + }), + Underline, + Blockquote, + TextAlign.configure({ + types: ['heading', 'paragraph'], + }), + Highlight, + Link.configure({ + openOnClick: false, + autolink: true, + }), +] + +export default (props) => { + if (props.content) + return ( + } extensions={extensions} content={props.content}> + ) +} \ No newline at end of file diff --git a/frontend/src/components/home.jsx b/frontend/src/components/home.jsx deleted file mode 100644 index 1176d2b..0000000 --- a/frontend/src/components/home.jsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Container, Spinner } from 'reactstrap'; -import MediaService from '../services/media-service' - -function HomePage(props) { - const UserData = props.UserData ? props.UserData : Loading... - const GlobalTheme = props.GlobalTheme; - const ThemeConfig = props.ThemeConfig; - if (GlobalTheme && ThemeConfig) - return ( - -
- {UserData.profilePhoto !== "" ? : ""} -

-
- {`${UserData.greetingLine} ${UserData.name}`} -
-

-
- <> -
${UserData.tagLine}` }} /> - -
-
-
- ); -} - -export default HomePage; \ No newline at end of file diff --git a/frontend/src/components/blog-list.jsx b/frontend/src/components/viewable/blog-list.jsx similarity index 96% rename from frontend/src/components/blog-list.jsx rename to frontend/src/components/viewable/blog-list.jsx index eea49af..83c9c05 100644 --- a/frontend/src/components/blog-list.jsx +++ b/frontend/src/components/viewable/blog-list.jsx @@ -1,8 +1,8 @@ import React from 'react'; import ReactDOM from 'react-dom'; import { useEffect, useState } from 'react'; -import DataService from '../services/data-service'; -import MediaService from '../services/media-service'; +import DataService from '../../services/data-service'; +import MediaService from '../../services/media-service'; import CardListViewer from './shared/card-list-viewer'; import CategoryBar from './shared/category-bar'; import { diff --git a/frontend/src/components/blog.jsx b/frontend/src/components/viewable/blog.jsx similarity index 97% rename from frontend/src/components/blog.jsx rename to frontend/src/components/viewable/blog.jsx index 62f4490..0c69cdb 100644 --- a/frontend/src/components/blog.jsx +++ b/frontend/src/components/viewable/blog.jsx @@ -1,8 +1,8 @@ import { useEffect, useState } from 'react'; import parse from 'html-react-parser'; -import DataService from '../services/data-service'; -import MediaService from '../services/media-service' +import DataService from '../../services/data-service'; +import MediaService from '../../services/media-service' import CategoryBar from './shared/category-bar'; import { diff --git a/frontend/src/components/category-list.jsx b/frontend/src/components/viewable/category-list.jsx similarity index 94% rename from frontend/src/components/category-list.jsx rename to frontend/src/components/viewable/category-list.jsx index 6d36939..e908da0 100644 --- a/frontend/src/components/category-list.jsx +++ b/frontend/src/components/viewable/category-list.jsx @@ -1,7 +1,7 @@ import { useEffect, useState } from 'react'; //import services -import DataService from '../services/data-service'; +import DataService from '../../services/data-service'; //import views import CardListViewer from './shared/card-list-viewer'; diff --git a/frontend/src/components/viewable/home.jsx b/frontend/src/components/viewable/home.jsx new file mode 100644 index 0000000..f68ac33 --- /dev/null +++ b/frontend/src/components/viewable/home.jsx @@ -0,0 +1,44 @@ +import { Container, Spinner } from 'reactstrap'; +import parse from 'html-react-parser'; +import MediaService from '../../services/media-service' + +function HomePage(props) { + + const replace = (node) => { + if (node.type === 'tag') { + if (node.name === 'a') { + const newClasses = `${ThemeConfig[GlobalTheme].linkBackground} ${ThemeConfig[GlobalTheme].linkTextColor}`; + const existingClasses = node.attribs.class ? `${node.attribs.class} ` : ''; + node.attribs.class = `${existingClasses}${newClasses}`; + node.attribs.rel = 'noopener noreferrer'; + node.attribs.target = '_blank'; + } + if (node.name === 'img') { + const newClasses = `img-fluid mt-2 mb-2 rounded`; + const existingClasses = node.attribs.class ? `${node.attribs.class} ` : ''; + node.attribs.class = `${existingClasses}${newClasses}`; + } + } + }; + + const UserData = props.UserData ? props.UserData : Loading... + const GlobalTheme = props.GlobalTheme; + const ThemeConfig = props.ThemeConfig; + const introContent = props.UserData ? parse(props.UserData.introContent, { replace }) : "" + + if (GlobalTheme && ThemeConfig) + return ( + +
+ {UserData.profilePhoto !== "" ? : ""} +
+ <> +
{introContent}
+ +
+
+
+ ); +} + +export default HomePage; \ No newline at end of file diff --git a/frontend/src/components/shared/card-list-viewer.jsx b/frontend/src/components/viewable/shared/card-list-viewer.jsx similarity index 93% rename from frontend/src/components/shared/card-list-viewer.jsx rename to frontend/src/components/viewable/shared/card-list-viewer.jsx index e469c35..226d550 100644 --- a/frontend/src/components/shared/card-list-viewer.jsx +++ b/frontend/src/components/viewable/shared/card-list-viewer.jsx @@ -1,5 +1,5 @@ import { useEffect, useState } from 'react'; -import MediaService from '../../services/media-service' +import MediaService from '../../../services/media-service' import { Spinner, Card, diff --git a/frontend/src/components/viewable/shared/category-bar.jsx b/frontend/src/components/viewable/shared/category-bar.jsx new file mode 100644 index 0000000..5a66fca --- /dev/null +++ b/frontend/src/components/viewable/shared/category-bar.jsx @@ -0,0 +1,55 @@ +import { useEffect, useState } from 'react'; +import DataService from '../../../services/data-service'; +import { Link } from 'react-router-dom'; +import { Container, Row, Col, Button, Spinner, ListGroup, ListGroupItem, ButtonGroup } from 'reactstrap'; + +function CategoryBar(props) { + + const [categoryMetadata, setCategoryMetadata] = useState([]); + + useEffect(() => { + DataService.getData('category/category-metadata').then(response => + setCategoryMetadata(response.data) + ); + }, []); + + const rowStyle = { + height: 'auto', + width: 'auto', + overflowX: 'auto', + display: 'grid', + }; + + const GlobalTheme = props.GlobalTheme; + const ThemeConfig = props.ThemeConfig; + if (GlobalTheme && ThemeConfig) + return ( + + +
+ + + {categoryMetadata.length > 0 ? + categoryMetadata.map((item, index) => ( + + )) : + } + + +
+
+
+ ); +}; + +export default CategoryBar; \ No newline at end of file diff --git a/frontend/src/components/viewable/shared/footer.jsx b/frontend/src/components/viewable/shared/footer.jsx new file mode 100644 index 0000000..95f146f --- /dev/null +++ b/frontend/src/components/viewable/shared/footer.jsx @@ -0,0 +1,35 @@ +import React from 'react'; +// Import necessary components from Argon Design System +import { + Container, + Row, + Col, + Nav, + NavLink, + Spinner +} from 'reactstrap'; + +const Footer = (props) => { + const GlobalTheme = props.GlobalTheme; + const ThemeConfig = props.ThemeConfig; + const UserData = props.UserData; + + if (UserData) + return ( + + ); +}; + +export default Footer; \ No newline at end of file diff --git a/frontend/src/components/viewable/shared/navbar.jsx b/frontend/src/components/viewable/shared/navbar.jsx new file mode 100644 index 0000000..2920fc2 --- /dev/null +++ b/frontend/src/components/viewable/shared/navbar.jsx @@ -0,0 +1,92 @@ +// Update import paths based on your Argon source location +import { + Navbar, + NavbarBrand, + UncontrolledCollapse, + Row, + Col, + Nav, + NavItem, + NavLink, + Container, + Spinner, + Button, ButtonGroup, Label, Input +} from 'reactstrap'; +import { useState, useEffect } from 'react'; +import MediaService from '../../../services/media-service' +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { faSun, faMoon, faPen } from '@fortawesome/free-solid-svg-icons'; +import { Link } from 'react-router-dom'; + +function Header(props) { + + const GlobalTheme = props.GlobalTheme; + const ThemeConfig = props.ThemeConfig; + const UserData = props.UserData; + + const [collapseClasses, setCollapseClasses] = useState(''); + const [themeSelected, setThemeSelected] = useState('lightTheme'); + + useEffect(() => { + props.ThemeSwitcher(themeSelected) + }, [themeSelected]) + + useEffect(() => { + setThemeSelected(props.ThemeConfig.defaultTheme) + }, []) + + if (GlobalTheme && ThemeConfig && UserData) + return ( + + ); +} + +export default Header; \ No newline at end of file diff --git a/frontend/src/components/viewable/shared/notification.jsx b/frontend/src/components/viewable/shared/notification.jsx new file mode 100644 index 0000000..ffafe5c --- /dev/null +++ b/frontend/src/components/viewable/shared/notification.jsx @@ -0,0 +1,18 @@ +import React, { useState } from 'react'; +import { Collapse, Button, CardBody, Card, Alert } from 'reactstrap'; + +function Notification(props) { + return ( + + + + + {props.message} + + + + + ); +} + +export default Notification; \ No newline at end of file diff --git a/frontend/src/index.css b/frontend/src/index.css index 56d4f13..c121572 100644 --- a/frontend/src/index.css +++ b/frontend/src/index.css @@ -1,7 +1,7 @@ a { - text-decoration: none; /* Removes underline */ - color: inherit; /* Inherits color from parent */ - border: none; /* Removes any borders */ + text-decoration: none !important; /* Removes underline */ + color: inherit !important; /* Inherits color from parent */ + border: none !important; /* Removes any borders */ } .app-container { @@ -11,20 +11,31 @@ a { } .blogContent a{ - border-radius: 5px; - padding-left: 5px; - padding-right: 5px; - transition-duration: 0.1s; - + border-radius: 5px; + padding-left: 5px; + padding-right: 5px; + transition-duration: 0.1s; } .blogContent a:hover{ - border-radius: 5px; - padding-left: 15px; - padding-right: 15px; - transition-duration: 0.1s; + border-radius: 5px; + padding-left: 15px; + padding-right: 15px; + transition-duration: 0.1s; } .blogContent{ - font-size: 20px + font-size: 20px +} + +.tiptap blockquote { + padding-left: 1rem; + border-left: 2px solid grey; +} + +.tiptap.ProseMirror { + margin-top: 20px; + border: solid grey; + border-radius: 10px; + padding: 1em; } \ No newline at end of file diff --git a/frontend/src/main.jsx b/frontend/src/main.jsx index 055327c..262bed0 100644 --- a/frontend/src/main.jsx +++ b/frontend/src/main.jsx @@ -1,11 +1,15 @@ -import React from 'react' +import React, { Suspense, lazy } from 'react'; import ReactDOM from 'react-dom/client' -import App from './App.jsx' import './index.css' - +const ViewComponent = lazy(() => + import.meta.env.VITE_APP_VIEW_TYPE === 'editableview' + ? import('./AppEditable.jsx') + : import('./App.jsx') +); +console.log(import.meta.env.VITE_APP_VIEW_TYPE) ReactDOM.createRoot(document.getElementById('root')).render( - + , ) \ No newline at end of file