diff --git a/backend/uploads/blog/1773814232254.png b/backend/uploads/blog/1773814232254.png
new file mode 100644
index 0000000..e0bbc79
Binary files /dev/null and b/backend/uploads/blog/1773814232254.png differ
diff --git a/backend/uploads/blog/1773814239753.png b/backend/uploads/blog/1773814239753.png
new file mode 100644
index 0000000..5d333f1
Binary files /dev/null and b/backend/uploads/blog/1773814239753.png differ
diff --git a/backend/uploads/blog/1773814266558.png b/backend/uploads/blog/1773814266558.png
new file mode 100644
index 0000000..5d333f1
Binary files /dev/null and b/backend/uploads/blog/1773814266558.png differ
diff --git a/backend/uploads/blog/1773814356620.png b/backend/uploads/blog/1773814356620.png
new file mode 100644
index 0000000..06e56ca
Binary files /dev/null and b/backend/uploads/blog/1773814356620.png differ
diff --git a/backend/uploads/blog/1773814805822.png b/backend/uploads/blog/1773814805822.png
new file mode 100644
index 0000000..5d333f1
Binary files /dev/null and b/backend/uploads/blog/1773814805822.png differ
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 62dc828..f1a50aa 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -8,6 +8,15 @@
"name": "frontend",
"version": "0.0.0",
"dependencies": {
+ "@editorjs/code": "^2.9.4",
+ "@editorjs/delimiter": "^1.4.2",
+ "@editorjs/editorjs": "^2.31.5",
+ "@editorjs/embed": "^2.8.0",
+ "@editorjs/header": "^2.8.8",
+ "@editorjs/image": "^2.10.3",
+ "@editorjs/list": "^2.0.9",
+ "@editorjs/quote": "^2.7.6",
+ "@editorjs/table": "^2.4.5",
"@fontsource-variable/geist": "^5.2.8",
"@tailwindcss/postcss": "^4.2.1",
"axios": "^1.13.6",
@@ -27,6 +36,8 @@
},
"devDependencies": {
"@eslint/js": "^9.39.1",
+ "@types/estree": "^1.0.8",
+ "@types/json-schema": "^7.0.15",
"@types/node": "^24.12.0",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
@@ -512,6 +523,12 @@
"node": ">=6.9.0"
}
},
+ "node_modules/@codexteam/icons": {
+ "version": "0.3.3",
+ "resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.3.3.tgz",
+ "integrity": "sha512-cp7mkZPgmBuSxigTm3Vb+DtVHYeX7qXfQd7o05vcLD8Ag5WvRlol2QSn5P10k0CDAJwmkH9nQGQLBycErS9lsQ==",
+ "license": "MIT"
+ },
"node_modules/@dotenvx/dotenvx": {
"version": "1.54.1",
"resolved": "https://registry.npmjs.org/@dotenvx/dotenvx/-/dotenvx-1.54.1.tgz",
@@ -680,6 +697,145 @@
"@noble/ciphers": "^1.0.0"
}
},
+ "node_modules/@editorjs/caret": {
+ "version": "1.0.3",
+ "resolved": "https://registry.npmjs.org/@editorjs/caret/-/caret-1.0.3.tgz",
+ "integrity": "sha512-VmgwQJZgL/LQjk049JunzRV1YCa0vDi+BNEpbDmr5cp3lGZllq9QQFO1eI71ZPzvFVn3vvhb+eOif4sAEyGgbw==",
+ "license": "MIT",
+ "dependencies": {
+ "@editorjs/dom": "^1.0.1"
+ }
+ },
+ "node_modules/@editorjs/code": {
+ "version": "2.9.4",
+ "resolved": "https://registry.npmjs.org/@editorjs/code/-/code-2.9.4.tgz",
+ "integrity": "sha512-c0zyWodNqjL/0WI67sZvACIOFU9IAHG0UeeIpjss8pZGGNBum+UWkh7nKULK0SYvaOrdPdlWWqjuFU1TFA5jUA==",
+ "license": "MIT",
+ "dependencies": {
+ "@codexteam/icons": "^0.3.2"
+ }
+ },
+ "node_modules/@editorjs/delimiter": {
+ "version": "1.4.2",
+ "resolved": "https://registry.npmjs.org/@editorjs/delimiter/-/delimiter-1.4.2.tgz",
+ "integrity": "sha512-S8q2LpeYdYkVShLp7K8c4HLthDHBevLw+sT+iO0+SH0oMvFmld9SUon3DFzMQ2gG07EOdZGRZ958+sVxyvFjZw==",
+ "license": "MIT",
+ "dependencies": {
+ "@codexteam/icons": "^0.3.2"
+ }
+ },
+ "node_modules/@editorjs/dom": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@editorjs/dom/-/dom-1.0.1.tgz",
+ "integrity": "sha512-yLO+86MYOIUr1Jl7SQw23SYT84ggv6aJW0EIRsI3NTHYgnQzmK7Bt2n5ZFupQlB0GJqmKqA5tCue3NKQb+o7Pw==",
+ "license": "MIT",
+ "dependencies": {
+ "@editorjs/helpers": "^1.0.1"
+ }
+ },
+ "node_modules/@editorjs/editorjs": {
+ "version": "2.31.5",
+ "resolved": "https://registry.npmjs.org/@editorjs/editorjs/-/editorjs-2.31.5.tgz",
+ "integrity": "sha512-pEwYE4HzE63DlSSCErV2foTak7Wp9fd7SGkG+WcwiYD0cPmuCowhEsqL+9MF4/ZIjc/KJzDEvhB3NC1B8gQkpQ==",
+ "license": "Apache-2.0",
+ "dependencies": {
+ "@editorjs/caret": "^1.0.1",
+ "codex-notifier": "^1.1.2",
+ "codex-tooltip": "^1.0.5"
+ }
+ },
+ "node_modules/@editorjs/embed": {
+ "version": "2.8.0",
+ "resolved": "https://registry.npmjs.org/@editorjs/embed/-/embed-2.8.0.tgz",
+ "integrity": "sha512-GkgL07M1GmRXq+vtYPkP9RLoij19mIMeyr5GrNo/0Km2XHmvDz2h6KHsDbiHXbq/5hZ5UgWi86kr+/aK165OBg==",
+ "license": "MIT",
+ "dependencies": {
+ "@editorjs/editorjs": "^2.31.0"
+ },
+ "engines": {
+ "node": ">=24.0.0"
+ }
+ },
+ "node_modules/@editorjs/header": {
+ "version": "2.8.8",
+ "resolved": "https://registry.npmjs.org/@editorjs/header/-/header-2.8.8.tgz",
+ "integrity": "sha512-bsMSs34u2hoi0UBuRoc5EGWXIFzJiwYgkFUYQGVm63y5FU+s8zPBmVx5Ip2sw1xgs0fqfDROqmteMvvmbCy62w==",
+ "license": "MIT",
+ "dependencies": {
+ "@codexteam/icons": "^0.0.5",
+ "@editorjs/editorjs": "^2.29.1"
+ }
+ },
+ "node_modules/@editorjs/header/node_modules/@codexteam/icons": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.5.tgz",
+ "integrity": "sha512-s6H2KXhLz2rgbMZSkRm8dsMJvyUNZsEjxobBEg9ztdrb1B2H3pEzY6iTwI4XUPJWJ3c3qRKwV4TrO3J5jUdoQA==",
+ "license": "MIT"
+ },
+ "node_modules/@editorjs/helpers": {
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/@editorjs/helpers/-/helpers-1.0.1.tgz",
+ "integrity": "sha512-Lmr8ImoQvoROXtzhsIJsA1ZtXzH46DmE6O8hMjn9/AvQq62UfjREjn+Ewi6KxjIZMay2PsgDEbLlsVyNJGEaxw==",
+ "license": "MIT"
+ },
+ "node_modules/@editorjs/image": {
+ "version": "2.10.3",
+ "resolved": "https://registry.npmjs.org/@editorjs/image/-/image-2.10.3.tgz",
+ "integrity": "sha512-ekCsGICZOIdghF/U2T34H7CItqaWAoJDXbkRD+x8l/LIo/7Ozf7KovYm21qz+CluArgV4RurVFHqwlz+O0vfJA==",
+ "license": "MIT",
+ "dependencies": {
+ "@codexteam/icons": "^0.3.0"
+ }
+ },
+ "node_modules/@editorjs/list": {
+ "version": "2.0.9",
+ "resolved": "https://registry.npmjs.org/@editorjs/list/-/list-2.0.9.tgz",
+ "integrity": "sha512-rUTgDSt5wygD3Dp24bNyp6vvye/Xf4UWju0ZuvWeP13Z4cu2z1Jb5JFSTEhCou72XUGuf4xVhtsd8cm/bwUS1g==",
+ "license": "MIT",
+ "dependencies": {
+ "@codexteam/icons": "^0.3.2"
+ }
+ },
+ "node_modules/@editorjs/quote": {
+ "version": "2.7.6",
+ "resolved": "https://registry.npmjs.org/@editorjs/quote/-/quote-2.7.6.tgz",
+ "integrity": "sha512-D01KUMSDj2r+6Z+xjDkQqI+y6URpeHCvj0+P4pah+GtkG040lWjFb2H4pgHFXuol2cbfyAoraYSw85fuPheCvw==",
+ "license": "MIT",
+ "dependencies": {
+ "@codexteam/icons": "^0.3.2",
+ "@editorjs/dom": "^0.0.5"
+ }
+ },
+ "node_modules/@editorjs/quote/node_modules/@editorjs/dom": {
+ "version": "0.0.5",
+ "resolved": "https://registry.npmjs.org/@editorjs/dom/-/dom-0.0.5.tgz",
+ "integrity": "sha512-SZ78Gwpkp3EUhjBIp0lSojeQ35V9acF8SubJsMeOH/vlOUE40GOnvvwWZnF05lO7bIB0dOHhhJy4N7IIAWxP2w==",
+ "license": "MIT",
+ "dependencies": {
+ "@editorjs/helpers": "^0.0.4"
+ }
+ },
+ "node_modules/@editorjs/quote/node_modules/@editorjs/helpers": {
+ "version": "0.0.4",
+ "resolved": "https://registry.npmjs.org/@editorjs/helpers/-/helpers-0.0.4.tgz",
+ "integrity": "sha512-ieg3dzo2m1/ELze/RMNADiAiC5amXxIlVXoJ5vvXITOu/p/dPsrF+Oi3h5gBYvtGk9vg5LJUSG5YWU0tBUO1tw==",
+ "license": "MIT"
+ },
+ "node_modules/@editorjs/table": {
+ "version": "2.4.5",
+ "resolved": "https://registry.npmjs.org/@editorjs/table/-/table-2.4.5.tgz",
+ "integrity": "sha512-pF48R2wc5m0c+N+RjtCLXBGZd23Rl7EjfSFpmcSViwNsu5RwMgYGrEiQ8mzVh98mbvYQwXm/NYBi9DEUUs970A==",
+ "license": "MIT",
+ "dependencies": {
+ "@codexteam/icons": "^0.0.6"
+ }
+ },
+ "node_modules/@editorjs/table/node_modules/@codexteam/icons": {
+ "version": "0.0.6",
+ "resolved": "https://registry.npmjs.org/@codexteam/icons/-/icons-0.0.6.tgz",
+ "integrity": "sha512-L7Q5PET8PjKcBT5wp7VR+FCjwCi5PUp7rd/XjsgQ0CI5FJz0DphyHGRILMuDUdCW2MQT9NHbVr4QP31vwAkS/A==",
+ "license": "MIT"
+ },
"node_modules/@esbuild/aix-ppc64": {
"version": "0.27.3",
"resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.3.tgz",
@@ -4826,6 +4982,18 @@
"integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==",
"license": "MIT"
},
+ "node_modules/codex-notifier": {
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/codex-notifier/-/codex-notifier-1.1.2.tgz",
+ "integrity": "sha512-DCp6xe/LGueJ1N5sXEwcBc3r3PyVkEEDNWCVigfvywAkeXcZMk9K41a31tkEFBW0Ptlwji6/JlAb49E3Yrxbtg==",
+ "license": "MIT"
+ },
+ "node_modules/codex-tooltip": {
+ "version": "1.0.5",
+ "resolved": "https://registry.npmjs.org/codex-tooltip/-/codex-tooltip-1.0.5.tgz",
+ "integrity": "sha512-IuA8LeyLU5p1B+HyhOsqR6oxyFQ11k3i9e9aXw40CrHFTRO2Y1npNBVU3W1SvhKAbUU7R/YikUBdcYFP0RcJag==",
+ "license": "MIT"
+ },
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
diff --git a/frontend/package.json b/frontend/package.json
index 5230bf4..3c87900 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -10,6 +10,15 @@
"preview": "vite preview"
},
"dependencies": {
+ "@editorjs/code": "^2.9.4",
+ "@editorjs/delimiter": "^1.4.2",
+ "@editorjs/editorjs": "^2.31.5",
+ "@editorjs/embed": "^2.8.0",
+ "@editorjs/header": "^2.8.8",
+ "@editorjs/image": "^2.10.3",
+ "@editorjs/list": "^2.0.9",
+ "@editorjs/quote": "^2.7.6",
+ "@editorjs/table": "^2.4.5",
"@fontsource-variable/geist": "^5.2.8",
"@tailwindcss/postcss": "^4.2.1",
"axios": "^1.13.6",
@@ -29,6 +38,8 @@
},
"devDependencies": {
"@eslint/js": "^9.39.1",
+ "@types/estree": "^1.0.8",
+ "@types/json-schema": "^7.0.15",
"@types/node": "^24.12.0",
"@types/react": "^19.2.7",
"@types/react-dom": "^19.2.3",
diff --git a/frontend/src/App.css b/frontend/src/App.css
index b9d355d..df674c0 100644
--- a/frontend/src/App.css
+++ b/frontend/src/App.css
@@ -1,42 +1,42 @@
#root {
- max-width: 1280px;
- margin: 0 auto;
- padding: 2rem;
- text-align: center;
+ max-width: 1280px;
+ margin: 0 auto;
+ padding: 2rem;
+ text-align: center;
}
.logo {
- height: 6em;
- padding: 1.5em;
- will-change: filter;
- transition: filter 300ms;
+ height: 6em;
+ padding: 1.5em;
+ will-change: filter;
+ transition: filter 300ms;
}
.logo:hover {
- filter: drop-shadow(0 0 2em #646cffaa);
+ filter: drop-shadow(0 0 2em #646cffaa);
}
.logo.react:hover {
- filter: drop-shadow(0 0 2em #61dafbaa);
+ filter: drop-shadow(0 0 2em #61dafbaa);
}
@keyframes logo-spin {
- from {
- transform: rotate(0deg);
- }
- to {
- transform: rotate(360deg);
- }
+ from {
+ transform: rotate(0deg);
+ }
+ to {
+ transform: rotate(360deg);
+ }
}
@media (prefers-reduced-motion: no-preference) {
- a:nth-of-type(2) .logo {
- animation: logo-spin infinite 20s linear;
- }
+ a:nth-of-type(2) .logo {
+ animation: logo-spin infinite 20s linear;
+ }
}
.card {
- padding: 2em;
+ padding: 2em;
}
.read-the-docs {
- color: #888;
+ color: #888;
}
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index de0ab13..dbdd97b 100644
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -12,6 +12,7 @@ import {AuthProvider} from "./context/AuthContext";
import Department from "./pages/Department";
import Doctor from "./pages/Doctor";
import Blog from "./pages/Blog";
+import BlogEditorPage from "./pages/BlogEditor";
export default function App() {
return (
@@ -27,6 +28,8 @@ export default function App() {