From 1be3ee54b0c845ce04e0d6330e9aea7603e7062f Mon Sep 17 00:00:00 2001 From: Barunes Padhy Date: Wed, 22 May 2024 22:06:39 +0300 Subject: [PATCH] Created backend structure, made editable homepage --- backend/.gitignore | 174 +++++++++++++++ backend/apimanager/MediaHandler.py | 8 + backend/apimanager/__init__.py | 0 backend/apimanager/admin.py | 10 + backend/apimanager/apps.py | 6 + backend/apimanager/initialize_data.py | 8 + backend/apimanager/migrations/0001_initial.py | 49 +++++ ..._contentbody_blog_content_body_and_more.py | 53 +++++ ...ename_resource_id_blog_blog_id_and_more.py | 23 ++ ...rk_theme_userdata_defaut_theme_and_more.py | 28 +++ ...0005_alter_userdata_dark_theme_and_more.py | 28 +++ ...ame_defaut_theme_userdata_default_theme.py | 18 ++ .../0007_alter_category_featured_id.py | 18 ++ ...0008_alter_userdata_dark_theme_and_more.py | 38 ++++ ...nt_body_alter_blog_description_and_more.py | 48 +++++ ...10_rename_category_blog_parent_category.py | 18 ++ backend/apimanager/migrations/__init__.py | 0 backend/apimanager/models.py | 28 +++ backend/apimanager/serializers.py | 66 ++++++ backend/apimanager/tests.py | 3 + backend/apimanager/views.py | 199 ++++++++++++++++++ backend/backend/__init__.py | 0 backend/backend/asgi.py | 16 ++ backend/backend/settings.py | 165 +++++++++++++++ backend/backend/urls.py | 52 +++++ backend/backend/views.py | 4 + backend/backend/wsgi.py | 16 ++ backend/manage.py | 22 ++ .../blog-data.json | 9 + .../blog-data.json | 9 + .../blog-metadata.json | 18 ++ .../category-data.json | 8 + .../blog-metadata.json | 3 + .../category-data.json | 7 + .../json/data/category/category-metadata.json | 16 ++ .../static/json/data/shared/theme-config.json | 38 ++++ .../static/json/data/shared/user-data.json | 13 ++ backend/templates/main.html | 10 + frontend/package.json | 1 + .../blog-data.json | 2 +- .../media/blog1.png | Bin .../blog-data.json | 2 +- .../media/blog2.png | Bin .../category-data.json | 4 +- .../media/technology.png | Bin .../category-data.json | 0 .../media/game.png | Bin .../data/category/category-metadata.json | 0 .../public/data/homepage/media/profile.png | Bin frontend/public/data/shared/theme-config.json | 10 +- frontend/public/data/shared/user-data.json | 0 frontend/public/vite.svg | 0 frontend/src/App.css | 0 frontend/src/App.jsx | 2 +- frontend/src/AppEditable.jsx | 59 ++++-- frontend/src/assets/react.svg | 0 .../src/components/editable/blog-list.jsx | 33 +-- frontend/src/components/editable/blog.jsx | 135 ++++-------- .../src/components/editable/category-list.jsx | 10 +- frontend/src/components/editable/home.jsx | 34 ++- .../editable/shared/card-list-viewer.jsx | 110 ++++++---- .../editable/shared/category-bar.jsx | 3 +- .../src/components/editable/shared/footer.jsx | 62 +++++- .../src/components/editable/shared/navbar.jsx | 61 +++++- .../editable/shared/notification.jsx | 2 +- .../src/components/editable/shared/tiptap.jsx | 76 +++++-- .../src/components/viewable/blog-list.jsx | 7 +- frontend/src/components/viewable/blog.jsx | 2 +- .../src/components/viewable/category-list.jsx | 3 +- frontend/src/components/viewable/home.jsx | 0 .../viewable/shared/card-list-viewer.jsx | 3 +- .../viewable/shared/category-bar.jsx | 3 +- .../src/components/viewable/shared/footer.jsx | 2 +- .../src/components/viewable/shared/navbar.jsx | 0 .../viewable/shared/notification.jsx | 0 frontend/src/config.json | 5 - frontend/src/index.css | 0 frontend/src/main.jsx | 1 - frontend/src/services/data-service.jsx | 5 +- .../src/services/editable-data-service.jsx | 21 ++ frontend/src/services/media-service.jsx | 0 frontend/vite.config.js | 9 + 82 files changed, 1666 insertions(+), 230 deletions(-) create mode 100644 backend/.gitignore create mode 100644 backend/apimanager/MediaHandler.py create mode 100644 backend/apimanager/__init__.py create mode 100644 backend/apimanager/admin.py create mode 100644 backend/apimanager/apps.py create mode 100644 backend/apimanager/initialize_data.py create mode 100644 backend/apimanager/migrations/0001_initial.py create mode 100644 backend/apimanager/migrations/0002_rename_contentbody_blog_content_body_and_more.py create mode 100644 backend/apimanager/migrations/0003_rename_resource_id_blog_blog_id_and_more.py create mode 100644 backend/apimanager/migrations/0004_userdata_dark_theme_userdata_defaut_theme_and_more.py create mode 100644 backend/apimanager/migrations/0005_alter_userdata_dark_theme_and_more.py create mode 100644 backend/apimanager/migrations/0006_rename_defaut_theme_userdata_default_theme.py create mode 100644 backend/apimanager/migrations/0007_alter_category_featured_id.py create mode 100644 backend/apimanager/migrations/0008_alter_userdata_dark_theme_and_more.py create mode 100644 backend/apimanager/migrations/0009_alter_blog_content_body_alter_blog_description_and_more.py create mode 100644 backend/apimanager/migrations/0010_rename_category_blog_parent_category.py create mode 100644 backend/apimanager/migrations/__init__.py create mode 100644 backend/apimanager/models.py create mode 100644 backend/apimanager/serializers.py create mode 100644 backend/apimanager/tests.py create mode 100644 backend/apimanager/views.py create mode 100644 backend/backend/__init__.py create mode 100644 backend/backend/asgi.py create mode 100644 backend/backend/settings.py create mode 100644 backend/backend/urls.py create mode 100644 backend/backend/views.py create mode 100644 backend/backend/wsgi.py create mode 100755 backend/manage.py create mode 100644 backend/static/json/data/blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json create mode 100644 backend/static/json/data/blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json create mode 100644 backend/static/json/data/category/520b7982-069e-4a48-9ef3-64507d86a579/blog-metadata.json create mode 100644 backend/static/json/data/category/520b7982-069e-4a48-9ef3-64507d86a579/category-data.json create mode 100644 backend/static/json/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/blog-metadata.json create mode 100644 backend/static/json/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/category-data.json create mode 100644 backend/static/json/data/category/category-metadata.json create mode 100644 backend/static/json/data/shared/theme-config.json create mode 100644 backend/static/json/data/shared/user-data.json create mode 100644 backend/templates/main.html rename frontend/public/data/{blogs => blog}/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json (97%) mode change 100644 => 100755 rename frontend/public/data/{blogs => blog}/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png (100%) mode change 100644 => 100755 rename frontend/public/data/{blogs => blog}/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json (97%) mode change 100644 => 100755 rename frontend/public/data/{blogs => blog}/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png (100%) mode change 100644 => 100755 mode change 100644 => 100755 frontend/public/data/category/520b7982-069e-4a48-9ef3-64507d86a579/category-data.json mode change 100644 => 100755 frontend/public/data/category/520b7982-069e-4a48-9ef3-64507d86a579/media/technology.png mode change 100644 => 100755 frontend/public/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/category-data.json mode change 100644 => 100755 frontend/public/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/media/game.png mode change 100644 => 100755 frontend/public/data/category/category-metadata.json mode change 100644 => 100755 frontend/public/data/homepage/media/profile.png mode change 100644 => 100755 frontend/public/data/shared/theme-config.json mode change 100644 => 100755 frontend/public/data/shared/user-data.json mode change 100644 => 100755 frontend/public/vite.svg mode change 100644 => 100755 frontend/src/App.css mode change 100644 => 100755 frontend/src/App.jsx mode change 100644 => 100755 frontend/src/AppEditable.jsx mode change 100644 => 100755 frontend/src/assets/react.svg mode change 100644 => 100755 frontend/src/components/editable/blog-list.jsx mode change 100644 => 100755 frontend/src/components/editable/blog.jsx mode change 100644 => 100755 frontend/src/components/editable/category-list.jsx mode change 100644 => 100755 frontend/src/components/editable/home.jsx mode change 100644 => 100755 frontend/src/components/editable/shared/card-list-viewer.jsx mode change 100644 => 100755 frontend/src/components/editable/shared/category-bar.jsx mode change 100644 => 100755 frontend/src/components/editable/shared/footer.jsx mode change 100644 => 100755 frontend/src/components/editable/shared/navbar.jsx mode change 100644 => 100755 frontend/src/components/editable/shared/notification.jsx mode change 100644 => 100755 frontend/src/components/editable/shared/tiptap.jsx mode change 100644 => 100755 frontend/src/components/viewable/blog-list.jsx mode change 100644 => 100755 frontend/src/components/viewable/blog.jsx mode change 100644 => 100755 frontend/src/components/viewable/category-list.jsx mode change 100644 => 100755 frontend/src/components/viewable/home.jsx mode change 100644 => 100755 frontend/src/components/viewable/shared/card-list-viewer.jsx mode change 100644 => 100755 frontend/src/components/viewable/shared/category-bar.jsx mode change 100644 => 100755 frontend/src/components/viewable/shared/footer.jsx mode change 100644 => 100755 frontend/src/components/viewable/shared/navbar.jsx mode change 100644 => 100755 frontend/src/components/viewable/shared/notification.jsx delete mode 100644 frontend/src/config.json mode change 100644 => 100755 frontend/src/index.css mode change 100644 => 100755 frontend/src/main.jsx mode change 100644 => 100755 frontend/src/services/data-service.jsx create mode 100644 frontend/src/services/editable-data-service.jsx mode change 100644 => 100755 frontend/src/services/media-service.jsx diff --git a/backend/.gitignore b/backend/.gitignore new file mode 100644 index 0000000..c1f993a --- /dev/null +++ b/backend/.gitignore @@ -0,0 +1,174 @@ +# Created by https://www.toptal.com/developers/gitignore/api/django +# Edit at https://www.toptal.com/developers/gitignore?templates=django + +### Django ### +*.log +*.pot +*.pyc +__pycache__/ +local_settings.py +db.sqlite3 +db.sqlite3-journal +media + +# If your build process includes running collectstatic, then you probably don't need or want to include staticfiles/ +# in your Git repository. Update and uncomment the following line accordingly. +# /staticfiles/ + +### Django.Python Stack ### +# Byte-compiled / optimized / DLL files +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo + +# Django stuff: + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/#use-with-ide +.pdm.toml + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + +# End of https://www.toptal.com/developers/gitignore/api/django diff --git a/backend/apimanager/MediaHandler.py b/backend/apimanager/MediaHandler.py new file mode 100644 index 0000000..78de39d --- /dev/null +++ b/backend/apimanager/MediaHandler.py @@ -0,0 +1,8 @@ +import os + +class MediaHandler: + def __init__(self, f, resource_type, resource_id): + pass + + def handleUploadedFile(self): + pass \ No newline at end of file diff --git a/backend/apimanager/__init__.py b/backend/apimanager/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/apimanager/admin.py b/backend/apimanager/admin.py new file mode 100644 index 0000000..af3c9ec --- /dev/null +++ b/backend/apimanager/admin.py @@ -0,0 +1,10 @@ +from django.contrib import admin +from .models import ( + UserData, + Category, + Blog +) + +admin.site.register(UserData) +admin.site.register(Category) +admin.site.register(Blog) \ No newline at end of file diff --git a/backend/apimanager/apps.py b/backend/apimanager/apps.py new file mode 100644 index 0000000..5092f92 --- /dev/null +++ b/backend/apimanager/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class ApimanagerConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'apimanager' diff --git a/backend/apimanager/initialize_data.py b/backend/apimanager/initialize_data.py new file mode 100644 index 0000000..c6395e4 --- /dev/null +++ b/backend/apimanager/initialize_data.py @@ -0,0 +1,8 @@ +userdata = { + "name": "Jhon Doe", + "intro_content": "

Write something about yourself

", + "profile_photo": "", + "default_theme": "darkTheme", + "dark_theme": "{\"darkTheme\": { \"theme\": \"Dark Mode\",\"background\": \"bg-dark\",\"textColor\": \"text-white\",\"linkBackground\": \"bg-light\",\"linkTextColor\": \"text-black\",\"captionColor\": \"#8a8a8a\",\"fontAwesomeIcon\": \"faSun\",\"borderColor\": \"secondary\",\"buttonColor\": \"light\",\"navBar\": {\"navBarTheme\": \"navbar-dark\",\"background\": \"bg-secondary\",\"buttonColor\": \"light\"},\"footer\": {\"background\": \"bg-secondary\",\"text\": \"bg-white\"}}}", + "light_theme": "{\"lightTheme\":{\"theme\": \"Light Mode\",\"background\": \"bg-light\",\"textColor\": \"text-black\",\"linkBackground\": \"bg-dark\",\"linkTextColor\": \"text-white\",\"captionColor\": \"#605f5f\",\"fontAwesomeIcon\": \"faMoon\",\"borderColor\": \"secondary\",\"buttonColor\": \"dark\",\"navBar\": {\"navBarTheme\": \"navbar-light\",\"background\": \"bg-secondary\",\"buttonColor\": \"light\"},\"footer\": {\"background\": \"bg-secondary\",\"text\": \"bg-white\"}}}" +} \ No newline at end of file diff --git a/backend/apimanager/migrations/0001_initial.py b/backend/apimanager/migrations/0001_initial.py new file mode 100644 index 0000000..3578563 --- /dev/null +++ b/backend/apimanager/migrations/0001_initial.py @@ -0,0 +1,49 @@ +# Generated by Django 5.0.4 on 2024-05-18 14:55 + +import django.db.models.deletion +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ] + + operations = [ + migrations.CreateModel( + name='Category', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('resourceid', models.SlugField()), + ('featuredid', models.SlugField()), + ('name', models.CharField(blank=True, max_length=200, null=True)), + ('description', models.CharField(blank=True, max_length=200, null=True)), + ('tagline', models.CharField(blank=True, max_length=200, null=True)), + ('coverimage', models.CharField(blank=True, max_length=500, null=True)), + ], + ), + migrations.CreateModel( + name='UserData', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('name', models.CharField(max_length=200)), + ('introContent', models.CharField(blank=True, max_length=100000, null=True)), + ('profilePhoto', models.CharField(blank=True, max_length=500, null=True)), + ], + ), + migrations.CreateModel( + name='Blog', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('resourceid', models.SlugField()), + ('name', models.CharField(blank=True, max_length=200, null=True)), + ('description', models.CharField(blank=True, max_length=200, null=True)), + ('tagline', models.CharField(blank=True, max_length=200, null=True)), + ('coverimage', models.CharField(blank=True, max_length=500, null=True)), + ('contentbody', models.CharField(blank=True, max_length=100000, null=True)), + ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='blogs', to='apimanager.category')), + ], + ), + ] diff --git a/backend/apimanager/migrations/0002_rename_contentbody_blog_content_body_and_more.py b/backend/apimanager/migrations/0002_rename_contentbody_blog_content_body_and_more.py new file mode 100644 index 0000000..636a7dd --- /dev/null +++ b/backend/apimanager/migrations/0002_rename_contentbody_blog_content_body_and_more.py @@ -0,0 +1,53 @@ +# Generated by Django 5.0.4 on 2024-05-18 15:43 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('apimanager', '0001_initial'), + ] + + operations = [ + migrations.RenameField( + model_name='blog', + old_name='contentbody', + new_name='content_body', + ), + migrations.RenameField( + model_name='blog', + old_name='coverimage', + new_name='cover_image', + ), + migrations.RenameField( + model_name='blog', + old_name='resourceid', + new_name='resource_id', + ), + migrations.RenameField( + model_name='category', + old_name='coverimage', + new_name='cover_image', + ), + migrations.RenameField( + model_name='category', + old_name='featuredid', + new_name='featured_id', + ), + migrations.RenameField( + model_name='category', + old_name='resourceid', + new_name='resource_id', + ), + migrations.RenameField( + model_name='userdata', + old_name='introContent', + new_name='intro_content', + ), + migrations.RenameField( + model_name='userdata', + old_name='profilePhoto', + new_name='profile_photo', + ), + ] diff --git a/backend/apimanager/migrations/0003_rename_resource_id_blog_blog_id_and_more.py b/backend/apimanager/migrations/0003_rename_resource_id_blog_blog_id_and_more.py new file mode 100644 index 0000000..1ac8756 --- /dev/null +++ b/backend/apimanager/migrations/0003_rename_resource_id_blog_blog_id_and_more.py @@ -0,0 +1,23 @@ +# Generated by Django 5.0.4 on 2024-05-18 16:13 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('apimanager', '0002_rename_contentbody_blog_content_body_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='blog', + old_name='resource_id', + new_name='blog_id', + ), + migrations.RenameField( + model_name='category', + old_name='resource_id', + new_name='category_id', + ), + ] diff --git a/backend/apimanager/migrations/0004_userdata_dark_theme_userdata_defaut_theme_and_more.py b/backend/apimanager/migrations/0004_userdata_dark_theme_userdata_defaut_theme_and_more.py new file mode 100644 index 0000000..5c5691a --- /dev/null +++ b/backend/apimanager/migrations/0004_userdata_dark_theme_userdata_defaut_theme_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.0.4 on 2024-05-19 07:02 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('apimanager', '0003_rename_resource_id_blog_blog_id_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='userdata', + name='dark_theme', + field=models.CharField(default='m', max_length=1500), + ), + migrations.AddField( + model_name='userdata', + name='defaut_theme', + field=models.CharField(default='m', max_length=200), + ), + migrations.AddField( + model_name='userdata', + name='light_theme', + field=models.CharField(default='m', max_length=1500), + ), + ] diff --git a/backend/apimanager/migrations/0005_alter_userdata_dark_theme_and_more.py b/backend/apimanager/migrations/0005_alter_userdata_dark_theme_and_more.py new file mode 100644 index 0000000..e164179 --- /dev/null +++ b/backend/apimanager/migrations/0005_alter_userdata_dark_theme_and_more.py @@ -0,0 +1,28 @@ +# Generated by Django 5.0.4 on 2024-05-19 07:11 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('apimanager', '0004_userdata_dark_theme_userdata_defaut_theme_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='userdata', + name='dark_theme', + field=models.CharField(max_length=1500), + ), + migrations.AlterField( + model_name='userdata', + name='defaut_theme', + field=models.CharField(max_length=200), + ), + migrations.AlterField( + model_name='userdata', + name='light_theme', + field=models.CharField(max_length=1500), + ), + ] diff --git a/backend/apimanager/migrations/0006_rename_defaut_theme_userdata_default_theme.py b/backend/apimanager/migrations/0006_rename_defaut_theme_userdata_default_theme.py new file mode 100644 index 0000000..8c48302 --- /dev/null +++ b/backend/apimanager/migrations/0006_rename_defaut_theme_userdata_default_theme.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-05-19 07:19 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('apimanager', '0005_alter_userdata_dark_theme_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='userdata', + old_name='defaut_theme', + new_name='default_theme', + ), + ] diff --git a/backend/apimanager/migrations/0007_alter_category_featured_id.py b/backend/apimanager/migrations/0007_alter_category_featured_id.py new file mode 100644 index 0000000..718d73d --- /dev/null +++ b/backend/apimanager/migrations/0007_alter_category_featured_id.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-05-19 07:27 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('apimanager', '0006_rename_defaut_theme_userdata_default_theme'), + ] + + operations = [ + migrations.AlterField( + model_name='category', + name='featured_id', + field=models.CharField(blank=True, max_length=500, null=True), + ), + ] diff --git a/backend/apimanager/migrations/0008_alter_userdata_dark_theme_and_more.py b/backend/apimanager/migrations/0008_alter_userdata_dark_theme_and_more.py new file mode 100644 index 0000000..89be917 --- /dev/null +++ b/backend/apimanager/migrations/0008_alter_userdata_dark_theme_and_more.py @@ -0,0 +1,38 @@ +# Generated by Django 5.0.4 on 2024-05-19 08:18 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('apimanager', '0007_alter_category_featured_id'), + ] + + operations = [ + migrations.AlterField( + model_name='userdata', + name='dark_theme', + field=models.CharField(default='{"darkTheme": { "theme": "Dark Mode","background": "bg-dark","textColor": "text-white","linkBackground": "bg-light","linkTextColor": "text-black","captionColor": "#8a8a8a","fontAwesomeIcon": "faSun","borderColor": "secondary","buttonColor": "light","navBar": {"navBarTheme": "navbar-dark","background": "bg-secondary","buttonColor": "light"},"footer": {"background": "bg-secondary","text": "bg-white"}}}', max_length=1500), + ), + migrations.AlterField( + model_name='userdata', + name='default_theme', + field=models.CharField(default='darkTheme', max_length=200), + ), + migrations.AlterField( + model_name='userdata', + name='intro_content', + field=models.CharField(default='

Write something about yourself

', max_length=100000), + ), + migrations.AlterField( + model_name='userdata', + name='light_theme', + field=models.CharField(default='{"lightTheme":{"theme": "Light Mode","background": "bg-light","textColor": "text-black","linkBackground": "bg-dark","linkTextColor": "text-white","captionColor": "#605f5f","fontAwesomeIcon": "faMoon","borderColor": "secondary","buttonColor": "dark","navBar": {"navBarTheme": "navbar-light","background": "bg-secondary","buttonColor": "light"},"footer": {"background": "bg-secondary","text": "bg-white"}}}', max_length=1500), + ), + migrations.AlterField( + model_name='userdata', + name='name', + field=models.CharField(default='Jhon Doe', max_length=200), + ), + ] diff --git a/backend/apimanager/migrations/0009_alter_blog_content_body_alter_blog_description_and_more.py b/backend/apimanager/migrations/0009_alter_blog_content_body_alter_blog_description_and_more.py new file mode 100644 index 0000000..36d2336 --- /dev/null +++ b/backend/apimanager/migrations/0009_alter_blog_content_body_alter_blog_description_and_more.py @@ -0,0 +1,48 @@ +# Generated by Django 5.0.4 on 2024-05-19 08:22 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('apimanager', '0008_alter_userdata_dark_theme_and_more'), + ] + + operations = [ + migrations.AlterField( + model_name='blog', + name='content_body', + field=models.CharField(default='

', max_length=100000), + ), + migrations.AlterField( + model_name='blog', + name='description', + field=models.CharField(max_length=200), + ), + migrations.AlterField( + model_name='blog', + name='name', + field=models.CharField(max_length=200), + ), + migrations.AlterField( + model_name='blog', + name='tagline', + field=models.CharField(max_length=200), + ), + migrations.AlterField( + model_name='category', + name='description', + field=models.CharField(max_length=200), + ), + migrations.AlterField( + model_name='category', + name='name', + field=models.CharField(max_length=200), + ), + migrations.AlterField( + model_name='category', + name='tagline', + field=models.CharField(max_length=200), + ), + ] diff --git a/backend/apimanager/migrations/0010_rename_category_blog_parent_category.py b/backend/apimanager/migrations/0010_rename_category_blog_parent_category.py new file mode 100644 index 0000000..aa22a4f --- /dev/null +++ b/backend/apimanager/migrations/0010_rename_category_blog_parent_category.py @@ -0,0 +1,18 @@ +# Generated by Django 5.0.4 on 2024-05-19 15:55 + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('apimanager', '0009_alter_blog_content_body_alter_blog_description_and_more'), + ] + + operations = [ + migrations.RenameField( + model_name='blog', + old_name='category', + new_name='parent_category', + ), + ] diff --git a/backend/apimanager/migrations/__init__.py b/backend/apimanager/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/apimanager/models.py b/backend/apimanager/models.py new file mode 100644 index 0000000..ba4ea3b --- /dev/null +++ b/backend/apimanager/models.py @@ -0,0 +1,28 @@ +from django.db import models +from .initialize_data import userdata + +class UserData(models.Model): + name = models.CharField(default=userdata["name"], null=False, blank=False, max_length=200) + intro_content = models.CharField(default=userdata["intro_content"], null=False, blank=False, max_length=100000) + profile_photo = models.CharField(null=True, blank=True, max_length=500) + default_theme = models.CharField(default=userdata["default_theme"], null=False, blank=False, max_length=200) + dark_theme = models.CharField(default=userdata["dark_theme"], null=False, blank=False, max_length=1500) + light_theme = models.CharField(default=userdata["light_theme"], null=False, blank=False, max_length=1500) + +class Category(models.Model): + category_id = models.SlugField() + featured_id = models.CharField(null=True, blank=True, max_length=500) + name = models.CharField(null=False, blank=False, max_length=200) + description = models.CharField(null=False, blank=False, max_length=200) + tagline = models.CharField(null=False, blank=False, max_length=200) + cover_image = models.CharField(null=True, blank=True, max_length=500) + + +class Blog(models.Model): + blog_id = models.SlugField() + name = models.CharField(null=False, blank=False, max_length=200) + description = models.CharField(null=False, blank=False, max_length=200) + tagline = models.CharField(null=False, blank=False, max_length=200) + cover_image = models.CharField(null=True, blank=True, max_length=500) + content_body = models.CharField(default='

', null=False, blank=False, max_length=100000) + parent_category = models.ForeignKey(Category, on_delete=models.CASCADE, related_name="blogs") \ No newline at end of file diff --git a/backend/apimanager/serializers.py b/backend/apimanager/serializers.py new file mode 100644 index 0000000..8944abf --- /dev/null +++ b/backend/apimanager/serializers.py @@ -0,0 +1,66 @@ +from django.contrib.auth import get_user_model, authenticate, login, logout +from django.contrib.auth.models import User +from django.db.models import Q +from django.urls import reverse +from django.utils import timezone +from rest_framework import serializers +from .models import ( + UserData, + Category, + Blog +) + +class UserDataSerializer(serializers.ModelSerializer): + + class Meta: + model = UserData + fields = [ + 'name', + 'intro_content', + 'profile_photo', + ] + +class ThemeDataSerializer(serializers.ModelSerializer): + + class Meta: + model = UserData + fields = [ + 'default_theme', + 'dark_theme', + 'light_theme' + ] + +class CategorySerializer(serializers.ModelSerializer): + + class Meta: + model = Category + fields = [ + 'category_id', + 'featured_id', + 'name', + 'description', + 'tagline', + 'cover_image' + ] + +class BlogSerializer(serializers.ModelSerializer): + + class Meta: + model = Blog + fields = [ + 'blog_id', + 'name', + 'description', + 'tagline', + 'cover_image', + 'content_body', + 'parent_category' + ] + + +class MediaSerializer(serializers.Serializer): + media = serializers.ListField( + child=serializers.FileField(max_length=100000, allow_empty_file=False, use_url=False) + ) + resource_type = serializers.CharField(allow_blank=False) + resource_id = serializers.CharField(allow_blank=False) \ No newline at end of file diff --git a/backend/apimanager/tests.py b/backend/apimanager/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/backend/apimanager/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/backend/apimanager/views.py b/backend/apimanager/views.py new file mode 100644 index 0000000..275955e --- /dev/null +++ b/backend/apimanager/views.py @@ -0,0 +1,199 @@ +#######################Django related imports#################### +import os +import subprocess +import ast +import shutil +from django.shortcuts import render +from django.contrib.auth.models import User +from django.http import HttpResponseRedirect +from rest_framework.views import APIView +from rest_framework.response import Response +from rest_framework.decorators import api_view +from rest_framework.generics import GenericAPIView +from rest_framework.parsers import MultiPartParser, FormParser +from rest_framework import generics, permissions, views, serializers, status +################################################################# +#API related imports +from .models import ( + UserData, + Category, + Blog +) +from .serializers import ( + UserDataSerializer, + ThemeDataSerializer, + CategorySerializer, + BlogSerializer, + MediaSerializer +) +################################################################ +#Custom Imports +from .MediaHandler import ( + MediaHandler, +) + +#UserData related views############################################# +class UserDataUpdateAPIView(generics.RetrieveUpdateAPIView): + serializer_class = UserDataSerializer + + def get_object(self): + obj, created = UserData.objects.get_or_create(pk=1) + return obj + +class UserDataListAPIView(generics.ListAPIView): + queryset = UserData.objects.all() + serializer_class = UserDataSerializer +##################################################################### + +#ThemeData related views############################################# +class ThemeDataUpdateAPIView(generics.RetrieveUpdateAPIView): + serializer_class = ThemeDataSerializer + + def get_object(self): + obj, created = UserData.objects.get_or_create(pk=1) + return obj + +class ThemeDataListAPIView(generics.ListAPIView): + queryset = UserData.objects.all() + serializer_class = ThemeDataSerializer +##################################################################### + +#Category related views############################################# +class CategoryCreateAPIView(generics.CreateAPIView): + queryset = Category.objects.all() + serializer_class = CategorySerializer + lookup_field = 'category_id' + +class CategoryUpdateAPIView(generics.RetrieveUpdateAPIView): + queryset = Category.objects.all() + serializer_class = CategorySerializer + lookup_field = 'category_id' + +class CategoryListAPIView(generics.ListAPIView): + queryset = Category.objects.all() + serializer_class = CategorySerializer + +class CategoryDeleteAPIView(generics.DestroyAPIView): + queryset = Category.objects.all() + serializer_class = CategorySerializer + lookup_field = 'category_id' +################################################################ + +#Blog related views################################################## +class BlogCreateAPIView(generics.CreateAPIView): + queryset = Blog.objects.all() + serializer_class = BlogSerializer + lookup_field = 'blog_id' + +class BlogUpdateAPIView(generics.RetrieveUpdateAPIView): + queryset = Blog.objects.all() + serializer_class = BlogSerializer + lookup_field = 'blog_id' + +class BlogRetrieveAPIView(generics.RetrieveAPIView): + queryset = Blog.objects.all() + serializer_class = BlogSerializer + lookup_field = 'blog_id' + +class BlogsByCategoryAPIView(APIView): + def get(self, request, category_id): + try: + category = Category.objects.get(category_id=category_id) + except Category.DoesNotExist: + return Response({'message': 'Category not found'}, status=404) + + blogs = category.blogs.all() + serializer = BlogSerializer(blogs, many=True) + return Response(serializer.data) + +class BlogDeleteAPIView(generics.DestroyAPIView): + queryset = Blog.objects.all() + serializer_class = BlogSerializer + lookup_field = 'blog_id' +#################################################################### + +''' +class MediaView(APIView): + parser_classes = (MultiPartParser, FormParser) + + def post(self, request, *args, **kwargs): + file_serializer = FileSerializer(data=request.data) + if file_serializer.is_valid(): + files = dict((f, f) for f in request.FILES.getlist('file')) + nodeName = file_serializer.validated_data['nodeName'] + preferredFormat = file_serializer.validated_data['preferredFormat'] + for f in files.values(): + fileHandlerObject = FileHandler(f, preferredFormat, nodeName) + fileProcessed = fileHandlerObject.handleUploadedFile() + if not fileProcessed[0]: + return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST) + else: + return Response(file_serializer.data, status=status.HTTP_201_CREATED) + + + return Response(file_serializer.data, status=status.HTTP_201_CREATED) + else: + return Response(file_serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + + +class ETLFunctions(GenericAPIView): + + serializer_class = ETLData + + def post(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + data = serializer.data + if data['operation'] == "create-folder": + os.mkdir('../Analysis/'+data['postData']) + with open('../Analysis/'+data['postData']+'/'+data['postData']+'-run.log', 'w') as fp: + pass + fp.close() + + if data['operation'] == "rename-folder": + os.rename('../Analysis/'+data['oldTitle'], '../Analysis/'+data['postData']) + + if data['operation'] == "create-partition-file": + print(os.getcwd()) + print(os.listdir(os.getcwd())) + partInfo = (data['postData'].split('|')) + with open(f'"{partInfo[0]}.part"', "w") as fpp: + pass + fpp.close() + partitionFile = open(f'"{partInfo[0]}.part"', "w+") + partitionFile.write(partInfo[1]) + partitionFile.close() + + if data['operation'] == "move-file": + pass + + return Response("Success", status=status.HTTP_200_OK) + +class BioTools(APIView): + def get(self, request): + params = request.GET.get('function', '') + params = params.split(";") + output = subprocess.check_output(f'seqmagick extract-ids ../Analysis/"{params[1]}"/"{(params[2])[:-1]}"', shell=True) + outgroups = (output.decode("utf-8")).split('\n') + outgroups = outgroups[:len(outgroups)-1] + return Response({'outgroups': outgroups}) + +class CommandRunner(GenericAPIView): + + serializer_class = InterimData + + def post(self, request, *args, **kwargs): + serializer = self.get_serializer(data=request.data) + serializer.is_valid(raise_exception=True) + data = serializer.data + runLogFile = f'../Analysis/{data["nodeName"]}/{data["nodeName"]}-run.log' + + try: + commands = ast.literal_eval(data['finalParameter']) + for key, value in commands.items(): + process = subprocess.Popen(value+f" > {runLogFile}", shell=True) + except: + process = subprocess.Popen(data['finalParameter']+f" > {runLogFile}", shell=True) + return Response("Command successfully sent for execution", status=status.HTTP_200_OK) +''' \ No newline at end of file diff --git a/backend/backend/__init__.py b/backend/backend/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/backend/backend/asgi.py b/backend/backend/asgi.py new file mode 100644 index 0000000..cb37c7d --- /dev/null +++ b/backend/backend/asgi.py @@ -0,0 +1,16 @@ +""" +ASGI config for backend project. + +It exposes the ASGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/asgi/ +""" + +import os + +from django.core.asgi import get_asgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') + +application = get_asgi_application() diff --git a/backend/backend/settings.py b/backend/backend/settings.py new file mode 100644 index 0000000..aea85db --- /dev/null +++ b/backend/backend/settings.py @@ -0,0 +1,165 @@ +""" +Django settings for backend project. + +Generated by 'django-admin startproject' using Django 5.0.4. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/topics/settings/ + +For the full list of settings and their values, see +https://docs.djangoproject.com/en/5.0/ref/settings/ +""" + +from pathlib import Path +import os + +# Build paths inside the project like this: BASE_DIR / 'subdir'. +BASE_DIR = Path(__file__).resolve().parent.parent + + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/5.0/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = 'django-insecure-2qks!5e#imys-r@tp2t#%cc3!*apkfu9f-(a7t)bn%sm@@3aq+' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.admin', + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'apimanager' +] + +TINYMCE_DEFAULT_CONFIG = { + 'height': 360, + 'width': 1120, + 'cleanup_on_startup': True, + 'custom_undo_redo_levels': 20, + 'selector': 'textarea', + 'theme': 'silver', + 'plugins': ''' + textcolor save link image media preview codesample contextmenu + table code lists fullscreen insertdatetime nonbreaking + contextmenu directionality searchreplace wordcount visualblocks + visualchars code fullscreen autolink lists charmap print hr + anchor pagebreak spellchecker + ''', + 'toolbar1': ''' + fullscreen preview bold italic underline | fontselect, + fontsizeselect | forecolor backcolor | alignleft alignright | + aligncenter alignjustify | indent outdent | bullist numlist table | + | link image media | codesample | + ''', + 'toolbar2': ''' + visualblocks visualchars | + charmap hr pagebreak nonbreaking anchor | code | + ''', + 'contextmenu': 'formats | link image', + 'menubar': True, + 'statusbar': True, + } + +REST_FRAMEWORK = { + 'DEFAULT_PERMISSION_CLASSES': [ + 'rest_framework.permissions.AllowAny', + ] +} + +MIDDLEWARE = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'backend.urls' + +TEMPLATES = [ + { + 'BACKEND': 'django.template.backends.django.DjangoTemplates', + 'DIRS': [os.path.join(BASE_DIR, 'templates')], + 'APP_DIRS': True, + 'OPTIONS': { + 'context_processors': [ + 'django.template.context_processors.debug', + 'django.template.context_processors.request', + 'django.contrib.auth.context_processors.auth', + 'django.contrib.messages.context_processors.messages', + ], + }, + }, +] + +WSGI_APPLICATION = 'backend.wsgi.application' + + +# Database +# https://docs.djangoproject.com/en/5.0/ref/settings/#databases + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': BASE_DIR / 'db.sqlite3', + } +} + + +# Password validation +# https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators + +AUTH_PASSWORD_VALIDATORS = [ + { + 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', + }, + { + 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', + }, +] + + +# Internationalization +# https://docs.djangoproject.com/en/5.0/topics/i18n/ + +LANGUAGE_CODE = 'en-us' + +TIME_ZONE = 'UTC' + +USE_I18N = True + +USE_TZ = True + + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/5.0/howto/static-files/ + +STATIC_URL = 'static/' + +# Default primary key field type +# https://docs.djangoproject.com/en/5.0/ref/settings/#default-auto-field + +DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' + + +CSRF_TRUSTED_ORIGINS = [ + 'http://localhost:3000', +] \ No newline at end of file diff --git a/backend/backend/urls.py b/backend/backend/urls.py new file mode 100644 index 0000000..b39bd42 --- /dev/null +++ b/backend/backend/urls.py @@ -0,0 +1,52 @@ +""" +URL configuration for backend project. + +The `urlpatterns` list routes URLs to views. For more information please see: + https://docs.djangoproject.com/en/5.0/topics/http/urls/ +Examples: +Function views + 1. Add an import: from my_app import views + 2. Add a URL to urlpatterns: path('', views.home, name='home') +Class-based views + 1. Add an import: from other_app.views import Home + 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') +Including another URLconf + 1. Import the include() function: from django.urls import include, path + 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) +""" +from django.contrib import admin +from django.urls import path, re_path +from django.conf.urls import include +from .views import my_form +from apimanager.views import ( + UserDataUpdateAPIView, + UserDataListAPIView, + ThemeDataUpdateAPIView, + ThemeDataListAPIView, + CategoryCreateAPIView, + CategoryUpdateAPIView, + CategoryDeleteAPIView, + CategoryListAPIView, + BlogCreateAPIView, + BlogUpdateAPIView, + BlogRetrieveAPIView, + BlogDeleteAPIView, + BlogsByCategoryAPIView +) + +urlpatterns = [ + path('admin/', admin.site.urls), + path('data/shared/user-data/', UserDataListAPIView.as_view(), name='user-data-list-view'), + path('data/shared/update/user-data/', UserDataUpdateAPIView.as_view(), name='user-data-update-view'), + path('data/shared/theme-config/', ThemeDataListAPIView.as_view(), name='theme-data-list-view'), + path('data/shared/update/theme-config/', ThemeDataUpdateAPIView.as_view(), name='theme-data-update-view'), + path('data/category/', CategoryListAPIView.as_view(), name='category-list-view'), + path('data/category//', BlogsByCategoryAPIView.as_view(), name='blogs-by-category-view'), + path('data/category/create//', CategoryCreateAPIView.as_view(), name='category-create-view'), + path('data/category/update//', CategoryUpdateAPIView.as_view(), name='category-update-view'), + path('data/category/delete//', CategoryDeleteAPIView.as_view(), name='category-delete-view'), + path('data/blog//', BlogRetrieveAPIView.as_view(), name='blog-retrieve-view'), + path('data/blog/create//', BlogCreateAPIView.as_view(), name='blog-create-view'), + path('data/blog/update//', BlogUpdateAPIView.as_view(), name='blog-update-view'), + path('data/blog/delete//', BlogDeleteAPIView.as_view(), name='blog-delete-view'), +] \ No newline at end of file diff --git a/backend/backend/views.py b/backend/backend/views.py new file mode 100644 index 0000000..ef025f3 --- /dev/null +++ b/backend/backend/views.py @@ -0,0 +1,4 @@ +from django.shortcuts import render + +def my_form(request): + return render(request, 'main') diff --git a/backend/backend/wsgi.py b/backend/backend/wsgi.py new file mode 100644 index 0000000..83e5322 --- /dev/null +++ b/backend/backend/wsgi.py @@ -0,0 +1,16 @@ +""" +WSGI config for backend project. + +It exposes the WSGI callable as a module-level variable named ``application``. + +For more information on this file, see +https://docs.djangoproject.com/en/5.0/howto/deployment/wsgi/ +""" + +import os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') + +application = get_wsgi_application() diff --git a/backend/manage.py b/backend/manage.py new file mode 100755 index 0000000..eb6431e --- /dev/null +++ b/backend/manage.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +"""Django's command-line utility for administrative tasks.""" +import os +import sys + + +def main(): + """Run administrative tasks.""" + os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'backend.settings') + try: + from django.core.management import execute_from_command_line + except ImportError as exc: + raise ImportError( + "Couldn't import Django. Are you sure it's installed and " + "available on your PYTHONPATH environment variable? Did you " + "forget to activate a virtual environment?" + ) from exc + execute_from_command_line(sys.argv) + + +if __name__ == '__main__': + main() diff --git a/backend/static/json/data/blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json b/backend/static/json/data/blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json new file mode 100644 index 0000000..c26b260 --- /dev/null +++ b/backend/static/json/data/blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json @@ -0,0 +1,9 @@ +{ + "id": "72e4d550-a19b-4b62-bf5a-13f98813d31a", + "name": "Blog 1", + "description": "A subtitle for Blog 1", + "tagLine": "Read blog", + "coverImage": "blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png", + "parentCategory": "520b7982-069e-4a48-9ef3-64507d86a579", + "contentBody": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec non ipsum in nunc pretium gravida id ut lectus. Duis ligula nisl, egestas a tortor nec, scelerisque hendrerit urna. Nullam gravida, ante id aliquet ultrices, justo metus aliquet augue, vestibulum posuere massa lacus at est. Cras vitae dolor euismod, volutpat quam eu, cursus enim. Maecenas in magna ut augue ultrices laoreet. Maecenas sapien sem, mollis sed ipsum nec, viverra vehicula quam. Sed et pulvinar justo. Quisque et vestibulum dui. Aliquam laoreet tempus neque, et eleifend nulla vestibulum eget. Cras tempus justo at nunc facilisis auctor. Duis facilisis tortor eu risus aliquam dapibus nec sit amet est. Maecenas ante lectus, sagittis eu facilisis sit amet, convallis eu ex. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean imperdiet vulputate ipsum sed scelerisque. Donec sit amet rutrum est. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam gravida augue sem, quis aliquet justo varius non. Nam tortor nulla, bibendum sit amet finibus sed, ultricies non tellus. Aliquam condimentum risus ut felis porta iaculis. Nullam gravida mauris lacinia finibus hendrerit. Sed nec consectetur erat, sed tincidunt velit. Donec sit amet nulla at sem blandit imperdiet ut eu nisl. Sed condimentum, lectus quis sodales commodo, arcu turpis sodales ex, ac placerat mi turpis sit amet mi. Nullam lorem velit, porta eu quam eu, hendrerit egestas nibh. Sed non pellentesque arcu. Nam felis lectus, scelerisque in semper a, faucibus sit amet nibh. Cras est ligula, pretium id maximus nec, hendrerit eu ante. Maecenas tincidunt est ante, sed vestibulum mauris dignissim nec. Phasellus varius varius leo in pharetra. Aenean vestibulum id dui cursus lobortis. Mauris vel orci massa. Nullam vitae lorem mattis, lobortis dui in, pharetra velit. Aenean non urna ac felis volutpat consequat sit amet sed nisi. Quisque rutrum nisi ac erat ultricies tempor. Etiam nec pellentesque metus. Nulla tempus mi a ex rutrum, ut luctus tellus porta. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus et quam sit amet arcu mattis commodo quis non risus. Sed luctus non dui ullamcorper consectetur. Duis eros magna, tempus ut aliquam sit amet, tempor vel nibh. Vestibulum hendrerit odio convallis elit pretium dictum. Proin ligula dolor, finibus eget lacus sed, facilisis fringilla lorem. Quisque quis lacus sit amet massa blandit fringilla vel vitae tortor. Vivamus dictum nibh vel justo ullamcorper faucibus. Nullam vitae augue pretium erat semper rhoncus. Etiam tempus, arcu feugiat hendrerit pretium, nisi justo sollicitudin velit, sagittis elementum ante metus sed ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam quis ligula euismod, venenatis arcu vitae, condimentum arcu. Donec vitae nisl aliquam, rutrum arcu vel, sollicitudin justo. Vestibulum nec sagittis massa. Etiam maximus, erat vitae dapibus vulputate, velit lectus imperdiet est, sed lobortis ante erat lobortis arcu. Maecenas mollis nunc ut nisi tristique tempus. Nulla tempor est non dui scelerisque, eget semper augue consequat." +} \ No newline at end of file diff --git a/backend/static/json/data/blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json b/backend/static/json/data/blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json new file mode 100644 index 0000000..2944507 --- /dev/null +++ b/backend/static/json/data/blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json @@ -0,0 +1,9 @@ +{ + "id": "b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e", + "name": "Blog 2", + "description": "A subtitle for Blog 2", + "tagLine": "Read blog", + "coverImage": "blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png", + "parentCategory": "520b7982-069e-4a48-9ef3-64507d86a579", + "contentBody": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec non ipsum in nunc pretium gravida id ut lectus. Duis ligula nisl, egestas a tortor nec, scelerisque hendrerit urna. Nullam gravida, ante id aliquet ultrices, justo metus aliquet augue, vestibulum posuere massa lacus at est. Cras vitae dolor euismod, volutpat quam eu, cursus enim. Maecenas in magna ut augue ultrices laoreet. Maecenas sapien sem, mollis sed ipsum nec, viverra vehicula quam. Sed et pulvinar justo. Quisque et vestibulum dui. Aliquam laoreet tempus neque, et eleifend nulla vestibulum eget. Cras tempus justo at nunc facilisis auctor. Duis facilisis tortor eu risus aliquam dapibus nec sit amet est. Maecenas ante lectus, sagittis eu facilisis sit amet, convallis eu ex. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean imperdiet vulputate ipsum sed scelerisque. Donec sit amet rutrum est. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam gravida augue sem, quis aliquet justo varius non. Nam tortor nulla, bibendum sit amet finibus sed, ultricies non tellus. Aliquam condimentum risus ut felis porta iaculis. Nullam gravida mauris lacinia finibus hendrerit. Sed nec consectetur erat, sed tincidunt velit. Donec sit amet nulla at sem blandit imperdiet ut eu nisl. Sed condimentum, lectus quis sodales commodo, arcu turpis sodales ex, ac placerat mi turpis sit amet mi. Nullam lorem velit, porta eu quam eu, hendrerit egestas nibh. Sed non pellentesque arcu. Nam felis lectus, scelerisque in semper a, faucibus sit amet nibh. Cras est ligula, pretium id maximus nec, hendrerit eu ante. Maecenas tincidunt est ante, sed vestibulum mauris dignissim nec. Phasellus varius varius leo in pharetra. Aenean vestibulum id dui cursus lobortis. Mauris vel orci massa. Nullam vitae lorem mattis, lobortis dui in, pharetra velit. Aenean non urna ac felis volutpat consequat sit amet sed nisi. Quisque rutrum nisi ac erat ultricies tempor. Etiam nec pellentesque metus. Nulla tempus mi a ex rutrum, ut luctus tellus porta. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus et quam sit amet arcu mattis commodo quis non risus. Sed luctus non dui ullamcorper consectetur. Duis eros magna, tempus ut aliquam sit amet, tempor vel nibh. Vestibulum hendrerit odio convallis elit pretium dictum. Proin ligula dolor, finibus eget lacus sed, facilisis fringilla lorem. Quisque quis lacus sit amet massa blandit fringilla vel vitae tortor. Vivamus dictum nibh vel justo ullamcorper faucibus. Nullam vitae augue pretium erat semper rhoncus. Etiam tempus, arcu feugiat hendrerit pretium, nisi justo sollicitudin velit, sagittis elementum ante metus sed ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam quis ligula euismod, venenatis arcu vitae, condimentum arcu. Donec vitae nisl aliquam, rutrum arcu vel, sollicitudin justo. Vestibulum nec sagittis massa. Etiam maximus, erat vitae dapibus vulputate, velit lectus imperdiet est, sed lobortis ante erat lobortis arcu. Maecenas mollis nunc ut nisi tristique tempus. Nulla tempor est non dui scelerisque, eget semper augue consequat." +} \ No newline at end of file diff --git a/backend/static/json/data/category/520b7982-069e-4a48-9ef3-64507d86a579/blog-metadata.json b/backend/static/json/data/category/520b7982-069e-4a48-9ef3-64507d86a579/blog-metadata.json new file mode 100644 index 0000000..f3f1037 --- /dev/null +++ b/backend/static/json/data/category/520b7982-069e-4a48-9ef3-64507d86a579/blog-metadata.json @@ -0,0 +1,18 @@ +[ + { + "id": "72e4d550-a19b-4b62-bf5a-13f98813d31a", + "name": "Blog 1", + "description": "A subtitle for Blog 1", + "coverImage": "blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png", + "tagLine": "Read more", + "parentCategory": "520b7982-069e-4a48-9ef3-64507d86a579" + }, + { + "id": "b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e", + "name": "Blog 2", + "description": "A subtitle for Blog 2", + "coverImage": "blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png", + "tagLine": "Read more", + "parentCategory": "520b7982-069e-4a48-9ef3-64507d86a579" + } +] \ No newline at end of file diff --git a/backend/static/json/data/category/520b7982-069e-4a48-9ef3-64507d86a579/category-data.json b/backend/static/json/data/category/520b7982-069e-4a48-9ef3-64507d86a579/category-data.json new file mode 100644 index 0000000..476aad9 --- /dev/null +++ b/backend/static/json/data/category/520b7982-069e-4a48-9ef3-64507d86a579/category-data.json @@ -0,0 +1,8 @@ +{ + "id": "520b7982-069e-4a48-9ef3-64507d86a579", + "name": "Technology", + "coverImage": "category/520b7982-069e-4a48-9ef3-64507d86a579/media/technology.png", + "tagLine": "Read articles about tech", + "description": "I have been working in tech for long, and here are my thoughts of random stuff", + "featuredBlog": "b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e" +} \ No newline at end of file diff --git a/backend/static/json/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/blog-metadata.json b/backend/static/json/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/blog-metadata.json new file mode 100644 index 0000000..367e8fa --- /dev/null +++ b/backend/static/json/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/blog-metadata.json @@ -0,0 +1,3 @@ +[{ + +}] \ No newline at end of file diff --git a/backend/static/json/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/category-data.json b/backend/static/json/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/category-data.json new file mode 100644 index 0000000..8f1143e --- /dev/null +++ b/backend/static/json/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/category-data.json @@ -0,0 +1,7 @@ +{ + "id": "b9e0d686-351d-49af-8e3d-b62023f44dbe", + "name": "Gaming", + "coverImage": "category/b9e0d686-351d-49af-8e3d-b62023f44dbe/media/game.png", + "tagLine": "Read articles about games", + "description": "I like to game, and here are my thoughts on games" +} diff --git a/backend/static/json/data/category/category-metadata.json b/backend/static/json/data/category/category-metadata.json new file mode 100644 index 0000000..c795a13 --- /dev/null +++ b/backend/static/json/data/category/category-metadata.json @@ -0,0 +1,16 @@ +[ + { + "id": "520b7982-069e-4a48-9ef3-64507d86a579", + "name": "Technology", + "coverImage": "category/520b7982-069e-4a48-9ef3-64507d86a579/media/technology.png", + "tagLine": "Read articles about tech", + "description": "I have been working in tech for long, and here are my thoughts of random stuff" + }, + { + "id": "b9e0d686-351d-49af-8e3d-b62023f44dbe", + "name": "Gaming", + "coverImage": "category/b9e0d686-351d-49af-8e3d-b62023f44dbe/media/game.png", + "tagLine": "Read articles about games", + "description": "I like to game, and here are my thoughts on games" + } +] diff --git a/backend/static/json/data/shared/theme-config.json b/backend/static/json/data/shared/theme-config.json new file mode 100644 index 0000000..5005f9e --- /dev/null +++ b/backend/static/json/data/shared/theme-config.json @@ -0,0 +1,38 @@ +{ + "darkTheme": { + "theme": "Dark Mode", + "background": "bg-dark", + "textColor": "text-white", + "captionColor": "#8a8a8a", + "fontAwesomeIcon": "faSun", + "borderColor": "white", + "categoryNavigator": "light", + "navBar": { + "navBarTheme": "navbar-dark", + "background": "bg-secondary", + "buttonColor": "light" + }, + "footer": { + "background": "bg-secondary", + "text": "bg-white" + } + }, + "lightTheme":{ + "theme": "Light Mode", + "background": "bg-light", + "textColor": "text-black", + "captionColor": "#605f5f", + "fontAwesomeIcon": "faMoon", + "borderColor": "black", + "categoryNavigator": "dark", + "navBar": { + "navBarTheme": "navbar-light", + "background": "bg-secondary", + "buttonColor": "light" + }, + "footer": { + "background": "bg-secondary", + "text": "bg-white" + } + } +} \ No newline at end of file diff --git a/backend/static/json/data/shared/user-data.json b/backend/static/json/data/shared/user-data.json new file mode 100644 index 0000000..2644d70 --- /dev/null +++ b/backend/static/json/data/shared/user-data.json @@ -0,0 +1,13 @@ +{ + "name": "John Doe", + "greetingLine": "Hi! My name is", + "tagLine": "Me like tech. Checkout my blog where I have nice stuff!", + "profilePhoto": "homepage/media/profile.png", + "links": { + "instagram": "" + }, + "contact":{ + "email":"", + "phone": "" + } +} diff --git a/backend/templates/main.html b/backend/templates/main.html new file mode 100644 index 0000000..dde5e4d --- /dev/null +++ b/backend/templates/main.html @@ -0,0 +1,10 @@ + + + + + + + +

Hi

+ + diff --git a/frontend/package.json b/frontend/package.json index 7aa7761..7dd7046 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -3,6 +3,7 @@ "private": true, "version": "0.0.0", "type": "module", + "proxy": "http://127.0.0.1:8000", "scripts": { "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", diff --git a/frontend/public/data/blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json b/frontend/public/data/blog/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json old mode 100644 new mode 100755 similarity index 97% rename from frontend/public/data/blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json rename to frontend/public/data/blog/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json index e96a9f0..8dc720c --- a/frontend/public/data/blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json +++ b/frontend/public/data/blog/72e4d550-a19b-4b62-bf5a-13f98813d31a/blog-data.json @@ -3,7 +3,7 @@ "name": "Blog 1", "description": "A subtitle for Blog 1", "tagLine": "Read blog", - "coverImage": "blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png", + "coverImage": "blog/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png", "parentCategory": "520b7982-069e-4a48-9ef3-64507d86a579", "contentBody": "

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec non ipsum in nunc pretium gravida id ut lectus. Duis ligula nisl, egestas a tortor nec, scelerisque hendrerit urna. Nullam gravida, ante id aliquet ultrices, justo metus aliquet augue, vestibulum posuere massa lacus at est. Cras vitae dolor euismod, volutpat quam eu, cursus enim. Maecenas in magna ut augue ultrices laoreet. Maecenas sapien sem, mollis sed ipsum nec, viverra vehicula quam. Sed et pulvinar justo. Quisque et vestibulum dui. Aliquam laoreet tempus neque, et eleifend nulla vestibulum eget. Cras tempus justo at nunc facilisis auctor. Duis facilisis tortor eu risus aliquam dapibus nec sit amet est. Maecenas ante lectus, sagittis eu facilisis sit amet, convallis eu ex. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean imperdiet vulputate ipsum sed scelerisque. Donec sit amet rutrum est. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam gravida augue sem, quis aliquet justo varius non. Nam tortor nulla, bibendum sit amet finibus sed, ultricies non tellus. Aliquam condimentum risus ut felis porta iaculis. Nullam gravida mauris lacinia finibus hendrerit. Sed nec consectetur erat, sed tincidunt velit. Donec sit amet nulla at sem blandit imperdiet ut eu nisl. Sed condimentum, lectus quis sodales commodo, arcu turpis sodales ex, ac placerat mi turpis sit amet mi. Nullam lorem velit, porta eu quam eu, hendrerit egestas nibh. Sed non pellentesque arcu. Nam felis lectus, scelerisque in semper a, faucibus sit amet nibh. Cras est ligula, pretium id maximus nec, hendrerit eu ante. Maecenas tincidunt est ante, sed vestibulum mauris dignissim nec. Phasellus varius varius leo in pharetra. Aenean vestibulum id dui cursus lobortis. Mauris vel orci massa. Nullam vitae lorem mattis, lobortis dui in, pharetra velit. Aenean non urna ac felis volutpat consequat sit amet sed nisi. Quisque rutrum nisi ac erat ultricies tempor. Etiam nec pellentesque metus. Nulla tempus mi a ex rutrum, ut luctus tellus porta. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus et quam sit amet arcu mattis commodo quis non risus. Sed luctus non dui ullamcorper consectetur. Duis eros magna, tempus ut aliquam sit amet, tempor vel nibh. Vestibulum hendrerit odio convallis elit pretium dictum. Proin ligula dolor, finibus eget lacus sed, facilisis fringilla lorem. Quisque quis lacus sit amet massa blandit fringilla vel vitae tortor. Vivamus dictum nibh vel justo ullamcorper faucibus. Nullam vitae augue pretium erat semper rhoncus. Etiam tempus, arcu feugiat hendrerit pretium, nisi justo sollicitudin velit, sagittis elementum ante metus sed ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam quis ligula euismod, venenatis arcu vitae, condimentum arcu. Donec vitae nisl aliquam, rutrum arcu vel, sollicitudin justo. Vestibulum nec sagittis massa. Etiam maximus, erat vitae dapibus vulputate, velit lectus imperdiet est, sed lobortis ante erat lobortis arcu. Maecenas mollis nunc ut nisi tristique tempus. Nulla tempor est non dui scelerisque, eget semper augue consequat.

" } \ No newline at end of file diff --git a/frontend/public/data/blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png b/frontend/public/data/blog/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png old mode 100644 new mode 100755 similarity index 100% rename from frontend/public/data/blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png rename to frontend/public/data/blog/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png diff --git a/frontend/public/data/blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json b/frontend/public/data/blog/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json old mode 100644 new mode 100755 similarity index 97% rename from frontend/public/data/blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json rename to frontend/public/data/blog/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json index 96afc12..e8d6bf2 --- a/frontend/public/data/blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json +++ b/frontend/public/data/blog/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/blog-data.json @@ -3,7 +3,7 @@ "name": "Blog 2", "description": "A subtitle for Blog 2", "tagLine": "Read blog", - "coverImage": "blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png", + "coverImage": "blog/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png", "parentCategory": "520b7982-069e-4a48-9ef3-64507d86a579", "contentBody": "

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Donec non ipsum in nunc pretium gravida id ut lectus. Duis ligula nisl, egestas a tortor nec, scelerisque hendrerit urna. Nullam gravida, ante id aliquet ultrices, justo metus aliquet augue, vestibulum posuere massa lacus at est. Cras vitae dolor euismod, volutpat quam eu, cursus enim. Maecenas in magna ut augue ultrices laoreet. Maecenas sapien sem, mollis sed ipsum nec, viverra vehicula quam. Sed et pulvinar justo. Quisque et vestibulum dui. Aliquam laoreet tempus neque, et eleifend nulla vestibulum eget. Cras tempus justo at nunc facilisis auctor. Duis facilisis tortor eu risus aliquam dapibus nec sit amet est. Maecenas ante lectus, sagittis eu facilisis sit amet, convallis eu ex. Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean imperdiet vulputate ipsum sed scelerisque. Donec sit amet rutrum est. Orci varius natoque penatibus et magnis dis parturient montes, nascetur ridiculus mus. Etiam gravida augue sem, quis aliquet justo varius non. Nam tortor nulla, bibendum sit amet finibus sed, ultricies non tellus. Aliquam condimentum risus ut felis porta iaculis. Nullam gravida mauris lacinia finibus hendrerit. Sed nec consectetur erat, sed tincidunt velit. Donec sit amet nulla at sem blandit imperdiet ut eu nisl. Sed condimentum, lectus quis sodales commodo, arcu turpis sodales ex, ac placerat mi turpis sit amet mi. Nullam lorem velit, porta eu quam eu, hendrerit egestas nibh. Sed non pellentesque arcu. Nam felis lectus, scelerisque in semper a, faucibus sit amet nibh. Cras est ligula, pretium id maximus nec, hendrerit eu ante. Maecenas tincidunt est ante, sed vestibulum mauris dignissim nec. Phasellus varius varius leo in pharetra. Aenean vestibulum id dui cursus lobortis. Mauris vel orci massa. Nullam vitae lorem mattis, lobortis dui in, pharetra velit. Aenean non urna ac felis volutpat consequat sit amet sed nisi. Quisque rutrum nisi ac erat ultricies tempor. Etiam nec pellentesque metus. Nulla tempus mi a ex rutrum, ut luctus tellus porta. Interdum et malesuada fames ac ante ipsum primis in faucibus. Phasellus et quam sit amet arcu mattis commodo quis non risus. Sed luctus non dui ullamcorper consectetur. Duis eros magna, tempus ut aliquam sit amet, tempor vel nibh. Vestibulum hendrerit odio convallis elit pretium dictum. Proin ligula dolor, finibus eget lacus sed, facilisis fringilla lorem. Quisque quis lacus sit amet massa blandit fringilla vel vitae tortor. Vivamus dictum nibh vel justo ullamcorper faucibus. Nullam vitae augue pretium erat semper rhoncus. Etiam tempus, arcu feugiat hendrerit pretium, nisi justo sollicitudin velit, sagittis elementum ante metus sed ante. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Etiam quis ligula euismod, venenatis arcu vitae, condimentum arcu. Donec vitae nisl aliquam, rutrum arcu vel, sollicitudin justo. Vestibulum nec sagittis massa. Etiam maximus, erat vitae dapibus vulputate, velit lectus imperdiet est, sed lobortis ante erat lobortis arcu. Maecenas mollis nunc ut nisi tristique tempus. Nulla tempor est non dui scelerisque, eget semper augue consequat.

" } \ No newline at end of file diff --git a/frontend/public/data/blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png b/frontend/public/data/blog/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png old mode 100644 new mode 100755 similarity index 100% rename from frontend/public/data/blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png rename to frontend/public/data/blog/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png diff --git a/frontend/public/data/category/520b7982-069e-4a48-9ef3-64507d86a579/category-data.json b/frontend/public/data/category/520b7982-069e-4a48-9ef3-64507d86a579/category-data.json old mode 100644 new mode 100755 index 9fce824..a83f0c8 --- a/frontend/public/data/category/520b7982-069e-4a48-9ef3-64507d86a579/category-data.json +++ b/frontend/public/data/category/520b7982-069e-4a48-9ef3-64507d86a579/category-data.json @@ -9,7 +9,7 @@ "id": "72e4d550-a19b-4b62-bf5a-13f98813d31a", "name": "Blog 1", "description": "A subtitle for Blog 1", - "coverImage": "blogs/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png", + "coverImage": "blog/72e4d550-a19b-4b62-bf5a-13f98813d31a/media/blog1.png", "tagLine": "Read more", "parentCategory": "520b7982-069e-4a48-9ef3-64507d86a579" }, @@ -17,7 +17,7 @@ "id": "b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e", "name": "Blog 2", "description": "A subtitle for Blog 2", - "coverImage": "blogs/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png", + "coverImage": "blog/b4d9e1a0-4a77-48eb-a04b-06ec23e2b73e/media/blog2.png", "tagLine": "Read more", "parentCategory": "520b7982-069e-4a48-9ef3-64507d86a579" }] diff --git a/frontend/public/data/category/520b7982-069e-4a48-9ef3-64507d86a579/media/technology.png b/frontend/public/data/category/520b7982-069e-4a48-9ef3-64507d86a579/media/technology.png old mode 100644 new mode 100755 diff --git a/frontend/public/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/category-data.json b/frontend/public/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/category-data.json old mode 100644 new mode 100755 diff --git a/frontend/public/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/media/game.png b/frontend/public/data/category/b9e0d686-351d-49af-8e3d-b62023f44dbe/media/game.png old mode 100644 new mode 100755 diff --git a/frontend/public/data/category/category-metadata.json b/frontend/public/data/category/category-metadata.json old mode 100644 new mode 100755 diff --git a/frontend/public/data/homepage/media/profile.png b/frontend/public/data/homepage/media/profile.png old mode 100644 new mode 100755 diff --git a/frontend/public/data/shared/theme-config.json b/frontend/public/data/shared/theme-config.json old mode 100644 new mode 100755 index d8c1ea3..e471983 --- a/frontend/public/data/shared/theme-config.json +++ b/frontend/public/data/shared/theme-config.json @@ -8,8 +8,8 @@ "linkTextColor": "text-black", "captionColor": "#8a8a8a", "fontAwesomeIcon": "faSun", - "borderColor": "white", - "categoryNavigator": "light", + "borderColor": "secondary", + "buttonColor": "light", "navBar": { "navBarTheme": "navbar-dark", "background": "bg-secondary", @@ -17,7 +17,7 @@ }, "footer": { "background": "bg-secondary", - "text": "bg-white" + "text": "text-white" } }, "lightTheme":{ @@ -28,8 +28,8 @@ "linkTextColor": "text-white", "captionColor": "#605f5f", "fontAwesomeIcon": "faMoon", - "borderColor": "black", - "categoryNavigator": "dark", + "borderColor": "secondary", + "buttonColor": "dark", "navBar": { "navBarTheme": "navbar-light", "background": "bg-secondary", diff --git a/frontend/public/data/shared/user-data.json b/frontend/public/data/shared/user-data.json old mode 100644 new mode 100755 diff --git a/frontend/public/vite.svg b/frontend/public/vite.svg old mode 100644 new mode 100755 diff --git a/frontend/src/App.css b/frontend/src/App.css old mode 100644 new mode 100755 diff --git a/frontend/src/App.jsx b/frontend/src/App.jsx old mode 100644 new mode 100755 index 4a20064..bb271a0 --- a/frontend/src/App.jsx +++ b/frontend/src/App.jsx @@ -53,8 +53,8 @@ function App() {
-
+ } /> } /> diff --git a/frontend/src/AppEditable.jsx b/frontend/src/AppEditable.jsx old mode 100644 new mode 100755 index a4b3da5..b17ab99 --- a/frontend/src/AppEditable.jsx +++ b/frontend/src/AppEditable.jsx @@ -16,7 +16,7 @@ import Footer from './components/editable/shared/footer'; import Notification from './components/editable/shared/notification'; //Import Services -import DataService from './services/data-service' +import EditableDataService from './services/editable-data-service' function AppEditable() { const [userData, setUserData] = useState(null); @@ -25,44 +25,69 @@ function AppEditable() { const [isOpen, setIsOpen] = useState(false); const [notificationMessage, setNotificationMessage] = useState("") - const notificationToggler = (message) => { + const notificationToggler = (message, color) => { setIsOpen(true) - setNotificationMessage(message) + setNotificationMessage({message: message, color: color}) setTimeout(() => { setIsOpen(false) - }, 3500) + }, 1500) + } + + const setInfo = async (path, data) => { + try { + const response = await EditableDataService.updateData(path, data); + setConfigData(); + return response.status; + } catch (error) { + return error.response ? error.response.status : 500; + } + } + + const setConfigData = () => { + EditableDataService.getData('/data/shared/user-data/').then( response => { + let responseData = response.data[0] + setUserData({ + "name": responseData["name"], + "introContent": responseData["intro_content"], + "profilePhoto": responseData["profile_photo"] + }) + } + ) + EditableDataService.getData('/data/shared/theme-config/').then( response =>{ + let responseData = response.data[0] + setThemeConfig({ + "defaultTheme": responseData["default_theme"], + "darkTheme": JSON.parse(responseData["dark_theme"]), + "lightTheme": JSON.parse(responseData["light_theme"]) + }) + setGlobalTheme(responseData["default_theme"]) + } + ) } 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) - } - ) + setConfigData() },[]) const themeSwitcher = (theme) => { setGlobalTheme(theme); } - if (themeConfig) + if (themeConfig && userData && globalTheme) return (
-
- +
- } /> + } /> } /> } /> } />
-
+
+
); diff --git a/frontend/src/assets/react.svg b/frontend/src/assets/react.svg old mode 100644 new mode 100755 diff --git a/frontend/src/components/editable/blog-list.jsx b/frontend/src/components/editable/blog-list.jsx old mode 100644 new mode 100755 index 3ee680d..2a25c34 --- a/frontend/src/components/editable/blog-list.jsx +++ b/frontend/src/components/editable/blog-list.jsx @@ -14,9 +14,7 @@ import { CardImg, CardTitle, CardText, - CardBody, - Button, - ButtonGroup + CardBody } from 'reactstrap'; import { Link, useParams } from 'react-router-dom'; @@ -34,8 +32,9 @@ function BlogList(props) { useEffect(() => { DataService.getData(`category/${categoryID}/category-data`).then(response =>{ setCategoryData(response.data); + console.log(response.data) if (response.data.featuredBlog){ - DataService.getData(`blogs/${response.data.featuredBlog}/blog-data`).then(response => + DataService.getData(`blog/${response.data.featuredBlog}/blog-data`).then(response => setFeaturedBlogData(response.data) ); } @@ -52,36 +51,46 @@ function BlogList(props) {
- + - {`Blogs in ${categoryData.name}`} + {`Blogs in ${categoryData.name}`}

- {`All blogs`} + {`Featured`}

+ { + featuredBlogData === 'loading' ? : + + } { categoryData === 'loading' ? : categoryData.blogMetadata.map((item, index) => ( )) } - - - -
diff --git a/frontend/src/components/editable/blog.jsx b/frontend/src/components/editable/blog.jsx old mode 100644 new mode 100755 index ddbc490..06276d9 --- a/frontend/src/components/editable/blog.jsx +++ b/frontend/src/components/editable/blog.jsx @@ -1,5 +1,4 @@ -import { useEffect, useState } from 'react'; -import parse from 'html-react-parser'; +import { useEffect, useState, useRef } from 'react'; import DataService from '../../services/data-service'; import MediaService from '../../services/media-service' @@ -7,53 +6,42 @@ import CategoryBar from './shared/category-bar'; import EditorComponent from './shared/tiptap'; import { - Container,Row, Col,Spinner, UncontrolledCollapse, Button, ButtonGroup, Card, CardBody + Container,Row, Col,Spinner, UncontrolledCollapse, Button, ButtonGroup, Card, CardBody, Input, InputGroup, InputGroupText } from 'reactstrap'; import { Link, useParams } from 'react-router-dom'; function Blog(props) { + const nameField = useRef(null); + const descriptionField = useRef(null); + const tagLineField = useRef(null); + const { blogID } = useParams(); const GlobalTheme = props.GlobalTheme; const ThemeConfig = props.ThemeConfig; const [blogData, setBlogData] = useState([]); - const [blogContent, setBlogContent] = 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}`; - } - } - }; + const setInfo = (event) => { + let localEditedBlogData = {...blogData}; + localEditedBlogData["name"] = nameField.current.value; + localEditedBlogData["description"] = descriptionField.current.value; + localEditedBlogData["tagLine"] = tagLineField.current.value; + localEditedBlogData["contentBody"] = blogContent + setBlogData(localEditedBlogData); + props.notificationToggler('Data saved!'); + } useEffect(() => { - DataService.getData(`blogs/${blogID}/blog-data`).then(response =>{ + DataService.getData(`blog/${blogID}/blog-data`).then(response =>{ setBlogData(response.data) - const parsedContent = parse(response.data.contentBody, { replace }); - setBlogContent(parsedContent); + setBlogContent(response.data.contentBody) } ); }, []); - useEffect(() => { - if (blogData.contentBody){ - const parsedContent = parse(blogData.contentBody, { replace }); - setBlogContent(parsedContent); - } - }, [GlobalTheme]) - if (GlobalTheme && ThemeConfig && blogData) { return ( @@ -70,68 +58,24 @@ function Blog(props) { = 765 ? '6':''}`}> -

{blogData.name}

-

{blogData.description}

-
- - - - - - - - - - - - - -
+ + + Name + + + + + + Description + + + + + + Tagline + + +
@@ -141,15 +85,14 @@ function Blog(props) {
- - + - - + + diff --git a/frontend/src/components/editable/category-list.jsx b/frontend/src/components/editable/category-list.jsx old mode 100644 new mode 100755 index 52d20e2..9596e6f --- a/frontend/src/components/editable/category-list.jsx +++ b/frontend/src/components/editable/category-list.jsx @@ -37,13 +37,12 @@ function Blogs(props) { return ( -
- + - {"Categories"} + {"Categories"} @@ -60,12 +59,13 @@ function Blogs(props) { resourceType={"categories"} textColor={ThemeConfig[GlobalTheme].textColor} bgColor={ThemeConfig[GlobalTheme].background} + borderColor={ThemeConfig[GlobalTheme].borderColor} itemObject={item} /> )) : } - - + +
diff --git a/frontend/src/components/editable/home.jsx b/frontend/src/components/editable/home.jsx old mode 100644 new mode 100755 index d4aa4a6..9b41330 --- a/frontend/src/components/editable/home.jsx +++ b/frontend/src/components/editable/home.jsx @@ -1,12 +1,34 @@ import { Container, Spinner, Input, InputGroup, InputGroupText, Button, ButtonGroup } from 'reactstrap'; +import {useEffect, useState, useRef} from 'react'; import EditorComponent from './shared/tiptap'; import MediaService from '../../services/media-service' function HomePage(props) { + const [introContent, setIntroContent] = useState("") + const [saveKeyReady, setSaveKeyReady] = useState(true) + const nameField = useRef(null) const UserData = props.UserData ? props.UserData : Loading... const GlobalTheme = props.GlobalTheme; const ThemeConfig = props.ThemeConfig; - if (GlobalTheme && ThemeConfig) + + const setInfo = async () => { + let response = await props.setInfo('/data/shared/update/user-data/', { + "name": nameField.current.value, + "intro_content": introContent, + "profile_photo": "" + }) + console.log(response) + if (response === 200) + props.notificationToggler("Data saved successfully!") + if ([500, 404, 403].includes(response)) + props.notificationToggler("Something failed!", "danger") + } + + useEffect(() => { + setIntroContent(UserData.introContent) + }, [UserData]) + + if (GlobalTheme && ThemeConfig) return (
@@ -17,13 +39,13 @@ function HomePage(props) { Name - + - + - - - + + +
diff --git a/frontend/src/components/editable/shared/card-list-viewer.jsx b/frontend/src/components/editable/shared/card-list-viewer.jsx old mode 100644 new mode 100755 index 0ad234c..145b22d --- a/frontend/src/components/editable/shared/card-list-viewer.jsx +++ b/frontend/src/components/editable/shared/card-list-viewer.jsx @@ -12,51 +12,73 @@ import { 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

) -} + if (props.totalItems > 0 && itemObject && Object.keys(itemObject).length !== 0){ + if (props.resourceType === 'categories') + return ( + + {itemObject.coverImage !== "" ? : ""} + + + + + Name + + + + + + + + Description + + + + + + + + + Tagline + + + + + + + + Open this resource + + + + + ) + else + return ( + + {itemObject.coverImage !== "" ? : ""} + + + + {itemObject.name} + + + {itemObject.description} + + + + {itemObject.tagLine} + + + + + + ) + } + else + return(

No items found in this section

) + } export default CardListViewer \ No newline at end of file diff --git a/frontend/src/components/editable/shared/category-bar.jsx b/frontend/src/components/editable/shared/category-bar.jsx old mode 100644 new mode 100755 index 5a66fca..0ab732d --- a/frontend/src/components/editable/shared/category-bar.jsx +++ b/frontend/src/components/editable/shared/category-bar.jsx @@ -34,10 +34,9 @@ function CategoryBar(props) { + + + +
{new Date().getFullYear()}, { UserData ? UserData.name : Loading... } diff --git a/frontend/src/components/editable/shared/navbar.jsx b/frontend/src/components/editable/shared/navbar.jsx old mode 100644 new mode 100755 index 2920fc2..42b40c1 --- a/frontend/src/components/editable/shared/navbar.jsx +++ b/frontend/src/components/editable/shared/navbar.jsx @@ -15,7 +15,7 @@ import { 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 { faSun, faMoon, faPen, faBrush } from '@fortawesome/free-solid-svg-icons'; import { Link } from 'react-router-dom'; function Header(props) { @@ -27,6 +27,20 @@ function Header(props) { const [collapseClasses, setCollapseClasses] = useState(''); const [themeSelected, setThemeSelected] = useState('lightTheme'); + const setInfo = async (color, colorArea) => { + let localThemeConfig = {...ThemeConfig} + localThemeConfig[GlobalTheme].navBar[colorArea] = `${color}` + let response = await props.setInfo('/data/shared/update/theme-config/', GlobalTheme === "darkTheme" ? { + "dark_theme": JSON.stringify(localThemeConfig[GlobalTheme]), + }:{ + "light_theme": JSON.stringify(localThemeConfig[GlobalTheme]), + }) + if (response === 200) + props.notificationToggler(`Color set for ${ThemeConfig[GlobalTheme].theme} successfully!`) + if ([500, 404, 403].includes(response)) + props.notificationToggler("Something failed!", "danger") + } + useEffect(() => { props.ThemeSwitcher(themeSelected) }, [themeSelected]) @@ -58,7 +72,7 @@ function Header(props) { diff --git a/frontend/src/components/editable/shared/notification.jsx b/frontend/src/components/editable/shared/notification.jsx old mode 100644 new mode 100755 index ffafe5c..7e73890 --- a/frontend/src/components/editable/shared/notification.jsx +++ b/frontend/src/components/editable/shared/notification.jsx @@ -7,7 +7,7 @@ function Notification(props) { - {props.message} + {props.message.message} diff --git a/frontend/src/components/editable/shared/tiptap.jsx b/frontend/src/components/editable/shared/tiptap.jsx old mode 100644 new mode 100755 index 82e9391..c8e47d8 --- a/frontend/src/components/editable/shared/tiptap.jsx +++ b/frontend/src/components/editable/shared/tiptap.jsx @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react' +import React, { useCallback, useEffect } from 'react' import { Button, ButtonGroup, Label, Input } from 'reactstrap'; import { Color } from '@tiptap/extension-color' @@ -17,19 +17,34 @@ import { faBold, faItalic, faAlignCenter, faAlignRight, faAlignJustify, faHighlighter, faStrikethrough, faCode, - faParagraph, faListUl, + faListUl, faLink, faListOl, faQuoteLeft, faQuoteRight, faRulerHorizontal, - faRotateLeft, faRotateRight, - faBars, faLink } from '@fortawesome/free-solid-svg-icons'; + faRotateLeft, faRotateRight } from '@fortawesome/free-solid-svg-icons'; const MenuBar = (props) => { const { editor } = useCurrentEditor() + useEffect(() => { + if (editor){ + const handleChange = () => { + props.setContent(editor.getHTML()) + } + editor.on('update', handleChange) + return () => { + editor.on('update', handleChange) + } + } + },[editor]) + + const GlobalTheme = props.GlobalTheme; + const ThemeConfig = props.ThemeConfig; + + const setLink = useCallback(() => { const previousUrl = editor.getAttributes('link').href const url = window.prompt('URL', previousUrl) - + // cancelled if (url === null) { return @@ -54,8 +69,9 @@ const MenuBar = (props) => { return ( <> - - - - + + - + - - + - +