├── .gitignore ├── blog_platform ├── blog │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-310.pyc │ │ ├── admin.cpython-310.pyc │ │ ├── apps.cpython-310.pyc │ │ ├── models.cpython-310.pyc │ │ ├── serializers.cpython-310.pyc │ │ ├── urls.cpython-310.pyc │ │ └── views.cpython-310.pyc │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_rename_comments_comment_rename_tags_tag.py │ │ ├── 0003_post_image.py │ │ ├── __init__.py │ │ └── __pycache__ │ │ │ ├── 0001_initial.cpython-310.pyc │ │ │ ├── 0002_rename_comments_comment_rename_tags_tag.cpython-310.pyc │ │ │ ├── 0003_post_image.cpython-310.pyc │ │ │ └── __init__.cpython-310.pyc │ ├── models.py │ ├── serializers.py │ ├── templates │ │ └── email_templates │ │ │ └── password_reset_email.html │ ├── tests.py │ ├── urls.py │ └── views.py ├── blog_platform │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-310.pyc │ │ ├── settings.cpython-310.pyc │ │ ├── urls.cpython-310.pyc │ │ └── wsgi.cpython-310.pyc │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ ├── view.py │ └── wsgi.py ├── db.sqlite3 ├── email.log ├── manage.py └── requirements.txt └── blog_platform_front ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.css ├── App.js ├── App.test.js ├── components ├── Auth │ ├── LogOut.jsx │ ├── Login.jsx │ ├── LoginButton.jsx │ ├── PasswordResetConfirmation.jsx │ ├── PasswordRest.jsx │ ├── PrivateRoutes.jsx │ ├── Register.jsx │ └── Token.jsx ├── BlogPosts │ ├── BlogPostDetail.jsx │ ├── BlogPostForm.jsx │ ├── BlogPostFull.jsx │ ├── BlogPostList.jsx │ ├── DashboardButton.jsx │ └── MyBlogPosts.jsx ├── Categories │ └── CategoryList.jsx ├── Comments │ ├── CommentForm.jsx │ └── CommentList.jsx └── Tags │ └── TagList.jsx ├── config.js ├── index.css ├── index.js ├── logo.svg ├── pages ├── DashBoard.jsx └── HomePage.jsx ├── reportWebVitals.js └── setupTests.js /.gitignore: -------------------------------------------------------------------------------- 1 | .vscode/ 2 | myenv/ 3 | /blog_platform/blog/__pycache__/* 4 | /blog_platform/blog/migrations/__pycache__/* 5 | /blog_platform/blog_platform/__pycache__/* 6 | /blog_platform/django.log 7 | /blog_platform/env 8 | pass.txt 9 | /blog_platform/db.sqlite3 10 | /sample_images 11 | -------------------------------------------------------------------------------- /blog_platform/blog/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/__init__.py -------------------------------------------------------------------------------- /blog_platform/blog/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/__pycache__/admin.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/__pycache__/admin.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/__pycache__/apps.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/__pycache__/apps.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/__pycache__/models.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/__pycache__/models.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/__pycache__/serializers.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/__pycache__/serializers.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/__pycache__/urls.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/__pycache__/urls.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/__pycache__/views.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/__pycache__/views.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from .models import Post, Category, Tag, Comment 3 | 4 | admin.site.register(Post) 5 | admin.site.register(Category) 6 | admin.site.register(Tag) 7 | admin.site.register(Comment) 8 | -------------------------------------------------------------------------------- /blog_platform/blog/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class BlogConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'blog' 7 | -------------------------------------------------------------------------------- /blog_platform/blog/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.4 on 2023-08-18 15:36 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='Category', 19 | fields=[ 20 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('name', models.CharField(max_length=100)), 22 | ], 23 | ), 24 | migrations.CreateModel( 25 | name='Tags', 26 | fields=[ 27 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 28 | ('name', models.CharField(max_length=100)), 29 | ], 30 | ), 31 | migrations.CreateModel( 32 | name='Post', 33 | fields=[ 34 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 35 | ('title', models.CharField(max_length=200)), 36 | ('content', models.TextField()), 37 | ('created_at', models.DateTimeField(auto_now_add=True)), 38 | ('updated_at', models.DateTimeField(auto_now=True)), 39 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 40 | ('category', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.category')), 41 | ('tags', models.ManyToManyField(to='blog.tags')), 42 | ], 43 | ), 44 | migrations.CreateModel( 45 | name='Comments', 46 | fields=[ 47 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 48 | ('content', models.TextField()), 49 | ('created_at', models.DateTimeField(auto_now_add=True)), 50 | ('author', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 51 | ('post', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='blog.post')), 52 | ], 53 | ), 54 | ] 55 | -------------------------------------------------------------------------------- /blog_platform/blog/migrations/0002_rename_comments_comment_rename_tags_tag.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.4 on 2023-08-18 15:47 2 | 3 | from django.conf import settings 4 | from django.db import migrations 5 | 6 | 7 | class Migration(migrations.Migration): 8 | 9 | dependencies = [ 10 | migrations.swappable_dependency(settings.AUTH_USER_MODEL), 11 | ('blog', '0001_initial'), 12 | ] 13 | 14 | operations = [ 15 | migrations.RenameModel( 16 | old_name='Comments', 17 | new_name='Comment', 18 | ), 19 | migrations.RenameModel( 20 | old_name='Tags', 21 | new_name='Tag', 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /blog_platform/blog/migrations/0003_post_image.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 4.2.4 on 2023-10-23 16:00 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('blog', '0002_rename_comments_comment_rename_tags_tag'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='post', 15 | name='image', 16 | field=models.ImageField(blank=True, null=True, upload_to='blog_images/'), 17 | ), 18 | ] 19 | -------------------------------------------------------------------------------- /blog_platform/blog/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/migrations/__init__.py -------------------------------------------------------------------------------- /blog_platform/blog/migrations/__pycache__/0001_initial.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/migrations/__pycache__/0001_initial.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/migrations/__pycache__/0002_rename_comments_comment_rename_tags_tag.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/migrations/__pycache__/0002_rename_comments_comment_rename_tags_tag.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/migrations/__pycache__/0003_post_image.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/migrations/__pycache__/0003_post_image.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/migrations/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog/migrations/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.contrib.auth.models import User 3 | 4 | 5 | class Category(models.Model): 6 | name = models.CharField(max_length=100) 7 | 8 | def __str__(self): 9 | return self.name 10 | 11 | 12 | class Tag(models.Model): 13 | name = models.CharField(max_length=100) 14 | 15 | def __str__(self): 16 | return self.name 17 | 18 | 19 | class Post(models.Model): 20 | title = models.CharField(max_length=200) 21 | content = models.TextField() 22 | image = models.ImageField(upload_to='blog_images/', null=True, blank=True) 23 | author = models.ForeignKey(User, on_delete=models.CASCADE) 24 | category = models.ForeignKey(Category, on_delete=models.CASCADE) 25 | tags = models.ManyToManyField(Tag) 26 | created_at = models.DateTimeField(auto_now_add=True) 27 | updated_at = models.DateTimeField(auto_now=True) 28 | 29 | def __str__(self): 30 | return self.title 31 | 32 | 33 | class Comment(models.Model): 34 | post = models.ForeignKey(Post, on_delete=models.CASCADE) 35 | author = models.ForeignKey(User, on_delete=models.CASCADE) 36 | content = models.TextField() 37 | created_at = models.DateTimeField(auto_now_add=True) 38 | 39 | def __str__(self): 40 | return f"Commnet by {self.author.username} on {self.post.title}" 41 | -------------------------------------------------------------------------------- /blog_platform/blog/serializers.py: -------------------------------------------------------------------------------- 1 | from rest_framework import serializers 2 | from .models import Post, Category, Tag, User, Comment 3 | 4 | 5 | class CategorySerializer(serializers.ModelSerializer): 6 | class Meta: 7 | model = Category 8 | fields = '__all__' 9 | 10 | 11 | class TagSerializer(serializers.ModelSerializer): 12 | class Meta: 13 | model = Tag 14 | fields = '__all__' 15 | 16 | 17 | class UserSerializer(serializers.ModelSerializer): 18 | class Meta: 19 | model = User 20 | fields = ('username', 'email', 'password', 'first_name', 'last_name') 21 | extra_kwargs = {'password': {'write_only': True}} 22 | 23 | # validation for exisiting usernames and email addresses. 24 | def validate(self, data): 25 | username = data.get('username') 26 | email = data.get('email') 27 | 28 | if User.objects.filter(username=username).exists(): 29 | raise serializers.ValidationError( 30 | {'username': 'Username already exists'}) 31 | if User.objects.filter(email=email).exists(): 32 | raise serializers.ValidationError( 33 | {'email': 'Email Address already exists'}) 34 | 35 | return data 36 | 37 | 38 | class PasswordResetSerializer(serializers.Serializer): 39 | email = serializers.EmailField() 40 | 41 | def validate_email(self, value): 42 | # Check if the email exists in your database 43 | try: 44 | user = User.objects.get(email=value) 45 | print(user) 46 | 47 | except User.DoesNotExist: 48 | raise serializers.ValidationError( 49 | "Email address not found in our database.") 50 | 51 | return value 52 | 53 | 54 | class PostSerializer(serializers.ModelSerializer): 55 | 56 | author = UserSerializer(read_only=True) 57 | 58 | class Meta: 59 | model = Post 60 | fields = '__all__' 61 | 62 | 63 | class CommentSerializer(serializers.ModelSerializer): 64 | post = PostSerializer() 65 | author = UserSerializer() 66 | 67 | class Meta: 68 | model = Comment 69 | fields = '__all__' 70 | -------------------------------------------------------------------------------- /blog_platform/blog/templates/email_templates/password_reset_email.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Password Reset 5 | 6 | 7 |

Hello {{ user.username }},

8 |

Click the link below to reset your password:

9 |

Reset Password

10 |

If you did not request a password reset, please ignore this email.

11 | 12 | 13 | -------------------------------------------------------------------------------- /blog_platform/blog/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /blog_platform/blog/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path, include 2 | from rest_framework.routers import DefaultRouter 3 | from . import views 4 | from django.contrib.auth import views as auth_views 5 | 6 | 7 | router = DefaultRouter() 8 | router.register(r'posts', views.PostModelViewSet) 9 | router.register(r'my_posts', views.PostModelViewSet) 10 | router.register(r'categories', views.CategoryViewSet) 11 | router.register(r'tags', views.TagViewSet) 12 | router.register(r'comments', views.CommentViewSet) 13 | router.register(r'users', views.UserViewSet) 14 | 15 | urlpatterns = [ 16 | path('', include(router.urls)), 17 | path('postlist', views.PostListView.as_view(), name='postlist'), 18 | path('postlist//', 19 | views.PostDetailView.as_view(), name='post_detail'), 20 | 21 | # Start - User Authenticatio Related Urls 22 | path('token', views.CustomAuthToken.as_view(), name='token_obtain'), 23 | path('register', views.UserRegistrationView.as_view(), name='register'), 24 | path('logout', views.LogOutView.as_view(), name='logout'), 25 | path('password_reset', views.password_reset, name='password_reset'), 26 | path('password_reset/confirm///', 27 | views.password_reset_confirm, name='password_reset_confirm'), 28 | 29 | 30 | path('password-reset/done/', auth_views.PasswordResetDoneView.as_view(), 31 | name='password_reset_done'), 32 | 33 | path('password-reset-complete/', auth_views.PasswordResetCompleteView.as_view(), 34 | name='password_reset_complete'), 35 | # END - User Authenticatio Related Urls 36 | ] 37 | -------------------------------------------------------------------------------- /blog_platform/blog/views.py: -------------------------------------------------------------------------------- 1 | from django.http import JsonResponse 2 | 3 | # For Define API Views 4 | from rest_framework import generics, viewsets, status 5 | from rest_framework.response import Response 6 | from .models import Post, Category, Tag, Comment, User 7 | from .serializers import PostSerializer, CategorySerializer, TagSerializer, CommentSerializer, UserSerializer, PasswordResetSerializer 8 | 9 | # For User Authentication 10 | from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly 11 | from rest_framework.authtoken.views import ObtainAuthToken, APIView 12 | from rest_framework.authtoken.models import Token 13 | from django.contrib.auth.hashers import make_password 14 | from django.contrib.auth.forms import UserCreationForm, SetPasswordForm 15 | 16 | # For PasswordRest 17 | from rest_framework.decorators import api_view, permission_classes 18 | from rest_framework.permissions import AllowAny 19 | from django.core import mail 20 | from django.template.loader import render_to_string 21 | from django.utils.http import urlsafe_base64_encode, urlsafe_base64_decode 22 | from django.utils.encoding import force_bytes 23 | from django.contrib.auth.tokens import default_token_generator 24 | from django.contrib.auth import get_user_model 25 | 26 | 27 | # List all Post Public 28 | class PostListView(generics.ListCreateAPIView): 29 | queryset = Post.objects.all() 30 | serializer_class = PostSerializer 31 | permission_classes = [IsAuthenticatedOrReadOnly] 32 | 33 | 34 | # View Post Details Public 35 | class PostDetailView(generics.RetrieveAPIView): 36 | serializer_class = PostSerializer 37 | permission_classes = [IsAuthenticatedOrReadOnly] 38 | lookup_field = 'id' 39 | 40 | def get_queryset(self): 41 | query = Post.objects.filter(id=self.kwargs.get('id')) 42 | return query 43 | 44 | 45 | # List Posts Private 46 | class PostModelViewSet(viewsets.ModelViewSet): 47 | queryset = Post.objects.all() 48 | serializer_class = PostSerializer 49 | permission_classes = [IsAuthenticated] 50 | 51 | def get_queryset(self): 52 | return Post.objects.filter(author=self.request.user) 53 | 54 | def perform_create(self, serializer): 55 | serializer.save(author=self.request.user) 56 | 57 | 58 | class CategoryListCreateView(generics.ListCreateAPIView): 59 | queryset = Category.objects.all() 60 | serializer_class = CategorySerializer 61 | permission_classes = [IsAuthenticatedOrReadOnly] 62 | 63 | 64 | class CategoryViewSet(viewsets.ModelViewSet): 65 | queryset = Category.objects.all() 66 | serializer_class = CategorySerializer 67 | permission_classes = [IsAuthenticated] 68 | 69 | 70 | class TagListCrateView(generics.ListCreateAPIView): 71 | queryset = Tag.objects.all() 72 | serializer_class = TagSerializer 73 | permission_classes = [IsAuthenticatedOrReadOnly] 74 | 75 | 76 | class TagViewSet(viewsets.ModelViewSet): 77 | queryset = Tag.objects.all() 78 | serializer_class = TagSerializer 79 | permission_classes = [IsAuthenticated] 80 | 81 | 82 | class CommentListCreateView(generics.ListCreateAPIView): 83 | queryset = Comment.objects.all() 84 | serializer_class = CommentSerializer 85 | permission_classes = [IsAuthenticatedOrReadOnly] 86 | 87 | 88 | class CommentViewSet(viewsets.ModelViewSet): 89 | queryset = Comment.objects.all() 90 | serializer_class = CommentSerializer 91 | permission_classes = [IsAuthenticated] 92 | 93 | 94 | class UserViewSet(viewsets.ModelViewSet): 95 | queryset = User.objects.all() 96 | serializer_class = UserSerializer 97 | permission_classes = [IsAuthenticated] 98 | 99 | 100 | def home(request): 101 | return JsonResponse({'message': 'Testing App'}) 102 | 103 | 104 | # For User Authentication 105 | class CustomUserCreateForm(UserCreationForm): 106 | class Meta: 107 | model = User 108 | fields = ['email', 'first_name', 'last_name'] 109 | 110 | 111 | class CustomAuthToken(ObtainAuthToken): 112 | def post(self, request, *args, **kwargs): 113 | serializer = self.serializer_class( 114 | data=request.data, context={'request': request}) 115 | serializer.is_valid(raise_exception=True) 116 | user = serializer.validated_data['user'] 117 | token, created = Token.objects.get_or_create(user=user) 118 | 119 | return Response({'token': token.key, 'user_id': user.id, 'user_name': user.username}) 120 | 121 | 122 | class UserRegistrationView(generics.CreateAPIView): 123 | queryset = User.objects.all() 124 | serializer_class = UserSerializer 125 | 126 | def perform_create(self, serializer): 127 | hashed_password = make_password(serializer.validated_data['password']) 128 | serializer.validated_data['password'] = hashed_password 129 | user = serializer.save() 130 | Token.objects.create(user=user) 131 | 132 | 133 | class LogOutView(APIView): 134 | permission_class = [IsAuthenticated] 135 | 136 | def post(self, request): 137 | user = request.user 138 | token = request.auth 139 | 140 | if user is not None and token is not None: 141 | # Logout the user by deleting their token. 142 | token.delete() 143 | return Response({'detail': 'Logout successful'}, status=status.HTTP_200_OK) 144 | else: 145 | return Response({'detail': 'Authentication required'}, status=status.HTTP_401_UNAUTHORIZED) 146 | 147 | 148 | @api_view(['POST']) 149 | @permission_classes([AllowAny]) 150 | def password_reset(request): 151 | serializer = PasswordResetSerializer(data=request.data) 152 | 153 | if serializer.is_valid(): 154 | email = serializer.validated_data['email'] 155 | try: 156 | user = User.objects.get(email=email) 157 | except User.DoesNotExist: 158 | return Response({'detail': 'No user with this email address.'}, status=status.HTTP_400_BAD_REQUEST) 159 | 160 | # Generate a password reset token and send an email with a link to reset the password. 161 | token = default_token_generator.make_token(user) 162 | uid = urlsafe_base64_encode(force_bytes(user.id)) 163 | frontend_url = "http://localhost:3000" 164 | context = { 165 | 'user': user, 166 | 'domain': frontend_url, 167 | 'uid': urlsafe_base64_encode(force_bytes(user.id)), 168 | 'token': token, 169 | } 170 | 171 | subject = 'Password Reset' 172 | message = render_to_string( 173 | 'email_templates/password_reset_email.html', context) 174 | from_email = 'noreply@example.com' 175 | recipient_list = [email] 176 | mail.send_mail(subject, message, from_email, recipient_list) 177 | 178 | return Response({'detail': 'Password reset email sent.'}, status=status.HTTP_200_OK) 179 | 180 | return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST) 181 | 182 | 183 | @api_view(['POST']) 184 | @permission_classes([AllowAny]) 185 | def password_reset_confirm(request, uidb64, token): 186 | User = get_user_model() 187 | try: 188 | # Decode the uidb64 to get the user's ID 189 | user_id = urlsafe_base64_decode(uidb64).decode() 190 | user = User.objects.get(id=user_id) 191 | 192 | # Check if the token is valid 193 | if default_token_generator.check_token(user, token): 194 | if request.method == 'POST': 195 | new_password = request.data.get('new_password1') 196 | form = SetPasswordForm(user, request.data) 197 | if form.is_valid(): 198 | # Set the new password 199 | user.set_password(new_password) 200 | user.save() 201 | return JsonResponse({'detail': 'Password reset successful'}, status=status.HTTP_200_OK) 202 | else: 203 | print(form.errors) 204 | return JsonResponse({'errors': form.errors}, status=status.HTTP_400_BAD_REQUEST) 205 | else: 206 | return JsonResponse({'detail': 'Invalid token'}, status=status.HTTP_400_BAD_REQUEST) 207 | except User.DoesNotExist: 208 | return JsonResponse({'detail': 'User not found'}, status=status.HTTP_400_BAD_REQUEST) 209 | -------------------------------------------------------------------------------- /blog_platform/blog_platform/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog_platform/__init__.py -------------------------------------------------------------------------------- /blog_platform/blog_platform/__pycache__/__init__.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog_platform/__pycache__/__init__.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog_platform/__pycache__/settings.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog_platform/__pycache__/settings.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog_platform/__pycache__/urls.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog_platform/__pycache__/urls.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog_platform/__pycache__/wsgi.cpython-310.pyc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/blog_platform/__pycache__/wsgi.cpython-310.pyc -------------------------------------------------------------------------------- /blog_platform/blog_platform/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for blog_platform project. 3 | 4 | It exposes the ASGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/asgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.asgi import get_asgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blog_platform.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /blog_platform/blog_platform/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for blog_platform project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.2.4. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.2/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | import os 15 | 16 | # Build paths inside the project like this: BASE_DIR / 'subdir'. 17 | BASE_DIR = Path(__file__).resolve().parent.parent 18 | 19 | 20 | # Quick-start development settings - unsuitable for production 21 | # See https://docs.djangoproject.com/en/4.2/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = 'django-insecure-u4*$!fqaeb*^1fyalki4mnk7ir#tj(=hmvn!)bri2x&oh$hrle' 25 | 26 | # SECURITY WARNING: don't run with debug turned on in production! 27 | DEBUG = True 28 | 29 | ALLOWED_HOSTS = [] 30 | 31 | 32 | # Application definition 33 | 34 | INSTALLED_APPS = [ 35 | 'django.contrib.admin', 36 | 'django.contrib.auth', 37 | 'django.contrib.contenttypes', 38 | 'django.contrib.sessions', 39 | 'django.contrib.messages', 40 | 'django.contrib.staticfiles', 41 | 'corsheaders', 42 | 'rest_framework', 43 | 'rest_framework.authtoken', 44 | 'blog', 45 | ] 46 | 47 | REST_FRAMEWORK = { 48 | 'DEFAULT_AUTHENTICATION_CLASSES': [ 49 | 'rest_framework.authentication.TokenAuthentication', 50 | ], 51 | } 52 | 53 | MIDDLEWARE = [ 54 | 'corsheaders.middleware.CorsMiddleware', 55 | 'django.middleware.security.SecurityMiddleware', 56 | 'django.contrib.sessions.middleware.SessionMiddleware', 57 | 'django.middleware.csrf.CsrfViewMiddleware', 58 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 59 | 'django.contrib.messages.middleware.MessageMiddleware', 60 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 61 | 'django.middleware.common.CommonMiddleware', 62 | ] 63 | 64 | AUTHENTICATION_BACKENDS = [ 65 | 'django.contrib.auth.backends.ModelBackend', 66 | ] 67 | 68 | LOGIN_URL = '/login/' 69 | 70 | LOGIN_REDIRECT_URL = '/home/' 71 | 72 | ROOT_URLCONF = 'blog_platform.urls' 73 | 74 | TEMPLATES = [ 75 | { 76 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 77 | 'DIRS': [], 78 | 'APP_DIRS': True, 79 | 'OPTIONS': { 80 | 'context_processors': [ 81 | 'django.template.context_processors.debug', 82 | 'django.template.context_processors.request', 83 | 'django.contrib.auth.context_processors.auth', 84 | 'django.contrib.messages.context_processors.messages', 85 | ], 86 | }, 87 | }, 88 | ] 89 | 90 | WSGI_APPLICATION = 'blog_platform.wsgi.application' 91 | 92 | 93 | # Database 94 | # https://docs.djangoproject.com/en/4.2/ref/settings/#databases 95 | 96 | DATABASES = { 97 | 'default': { 98 | 'ENGINE': 'django.db.backends.postgresql', 99 | 'NAME': 'blogdb', 100 | 'USER': 'postgres', 101 | 'PASSWORD': 'postgres', 102 | 'HOST': 'localhost', 103 | 'PORT': '5432', 104 | } 105 | } 106 | 107 | 108 | # Password validation 109 | # https://docs.djangoproject.com/en/4.2/ref/settings/#auth-password-validators 110 | 111 | AUTH_PASSWORD_VALIDATORS = [ 112 | { 113 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 114 | }, 115 | { 116 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 117 | }, 118 | { 119 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 120 | }, 121 | { 122 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 123 | }, 124 | ] 125 | 126 | 127 | # Internationalization 128 | # https://docs.djangoproject.com/en/4.2/topics/i18n/ 129 | 130 | LANGUAGE_CODE = 'en-us' 131 | 132 | TIME_ZONE = 'UTC' 133 | 134 | USE_I18N = True 135 | 136 | USE_TZ = True 137 | 138 | 139 | # Static files (CSS, JavaScript, Images) 140 | # https://docs.djangoproject.com/en/4.2/howto/static-files/ 141 | 142 | STATIC_URL = 'static/' 143 | 144 | # Default primary key field type 145 | # https://docs.djangoproject.com/en/4.2/ref/settings/#default-auto-field 146 | 147 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 148 | 149 | CORS_ALLOW_ALL_ORIGINS = False 150 | CORS_ALLOW_CREDENTIALS = True 151 | # CORS_ALLOW_HEADERS = ['X-CSRFTOKEN', 'content-type'] 152 | 153 | CORS_ALLOWED_ORIGINS = [ 154 | 'http://localhost:3000', 155 | ] 156 | 157 | CORS_ALLOW_METHODS = [ 158 | "GET", 159 | "POST", 160 | "PUT", 161 | "DELETE", 162 | "OPTIONS", 163 | ] 164 | 165 | EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend' 166 | EMAIL_HOST = 'smtp.gmail.com' 167 | EMAIL_HOST_USER = 'ridmi.developer@gmail.com' 168 | # App Password Generated From Google 169 | EMAIL_HOST_PASSWORD = 'xefv qfrc awxt fuya' 170 | EMAIL_PORT = 587 171 | EMAIL_USE_TLS = True 172 | EMAIL_USE_SSL = False 173 | 174 | LOGGING = { 175 | 'version': 1, 176 | 'disable_existing_loggers': False, 177 | 'handlers': { 178 | 'file': { 179 | 'level': 'DEBUG', 180 | 'class': 'logging.FileHandler', 181 | 'filename': 'django.log', # Change this to the desired log file path 182 | }, 183 | }, 184 | 'root': { 185 | 'handlers': ['file'], 186 | 'level': 'INFO', # Change this to 'DEBUG' for more detailed logs 187 | }, 188 | } 189 | 190 | 191 | # Blog Post Images save to: 192 | MEDIA_URL = '/media/' 193 | MEDIA_ROOT = os.path.join(BASE_DIR, 'media') 194 | -------------------------------------------------------------------------------- /blog_platform/blog_platform/urls.py: -------------------------------------------------------------------------------- 1 | """ 2 | URL configuration for blog_platform project. 3 | 4 | The `urlpatterns` list routes URLs to views. For more information please see: 5 | https://docs.djangoproject.com/en/4.2/topics/http/urls/ 6 | Examples: 7 | Function views 8 | 1. Add an import: from my_app import views 9 | 2. Add a URL to urlpatterns: path('', views.home, name='home') 10 | Class-based views 11 | 1. Add an import: from other_app.views import Home 12 | 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') 13 | Including another URLconf 14 | 1. Import the include() function: from django.urls import include, path 15 | 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) 16 | """ 17 | from django.contrib import admin 18 | from django.urls import include, path 19 | 20 | # Image Sevrve and Debuging 21 | from django.views.static import serve 22 | from django.conf import settings 23 | from django.conf.urls.static import static 24 | urlpatterns = [ 25 | path('admin/', admin.site.urls), 26 | path('media/', serve, {'document_root': settings.MEDIA_ROOT}), 27 | path('api/', include('blog.urls')), 28 | ] 29 | # Image Debuging 30 | if settings.DEBUG: 31 | urlpatterns += static(settings.MEDIA_URL, 32 | document_root=settings.MEDIA_ROOT) 33 | -------------------------------------------------------------------------------- /blog_platform/blog_platform/view.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.http import JsonResponse 3 | 4 | 5 | def home(request): 6 | return JsonResponse({'message': 'Testing Page'}) 7 | -------------------------------------------------------------------------------- /blog_platform/blog_platform/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for blog_platform project. 3 | 4 | It exposes the WSGI callable as a module-level variable named ``application``. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.2/howto/deployment/wsgi/ 8 | """ 9 | 10 | import os 11 | 12 | from django.core.wsgi import get_wsgi_application 13 | 14 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blog_platform.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /blog_platform/db.sqlite3: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/db.sqlite3 -------------------------------------------------------------------------------- /blog_platform/email.log: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform/email.log -------------------------------------------------------------------------------- /blog_platform/manage.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | """Django's command-line utility for administrative tasks.""" 3 | import os 4 | import sys 5 | 6 | 7 | def main(): 8 | """Run administrative tasks.""" 9 | os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'blog_platform.settings') 10 | try: 11 | from django.core.management import execute_from_command_line 12 | except ImportError as exc: 13 | raise ImportError( 14 | "Couldn't import Django. Are you sure it's installed and " 15 | "available on your PYTHONPATH environment variable? Did you " 16 | "forget to activate a virtual environment?" 17 | ) from exc 18 | execute_from_command_line(sys.argv) 19 | 20 | 21 | if __name__ == '__main__': 22 | main() 23 | -------------------------------------------------------------------------------- /blog_platform/requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.7.2 2 | Django==5.0 3 | django-cors-headers==4.3.1 4 | djangorestframework==3.14.0 5 | Pillow==10.1.0 6 | psycopg2==2.9.9 7 | pytz==2023.3.post1 8 | sqlparse==0.4.4 9 | typing_extensions==4.9.0 10 | tzdata==2023.3 11 | -------------------------------------------------------------------------------- /blog_platform_front/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /blog_platform_front/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in your browser. 13 | 14 | The page will reload when you make changes.\ 15 | You may also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can't go back!** 35 | 36 | If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. 39 | 40 | You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /blog_platform_front/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "blog_platform_front", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.17.0", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^1.5.1", 10 | "dompurify": "^3.0.6", 11 | "react": "^18.2.0", 12 | "react-dom": "^18.2.0", 13 | "react-quill": "^2.0.0", 14 | "react-router-dom": "^6.16.0", 15 | "react-scripts": "5.0.1", 16 | "styled-components": "^6.0.8", 17 | "web-vitals": "^2.1.4" 18 | }, 19 | "scripts": { 20 | "start": "react-scripts start", 21 | "build": "react-scripts build", 22 | "test": "react-scripts test", 23 | "eject": "react-scripts eject" 24 | }, 25 | "eslintConfig": { 26 | "extends": [ 27 | "react-app", 28 | "react-app/jest" 29 | ] 30 | }, 31 | "browserslist": { 32 | "production": [ 33 | ">0.2%", 34 | "not dead", 35 | "not op_mini all" 36 | ], 37 | "development": [ 38 | "last 1 chrome version", 39 | "last 1 firefox version", 40 | "last 1 safari version" 41 | ] 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /blog_platform_front/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform_front/public/favicon.ico -------------------------------------------------------------------------------- /blog_platform_front/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /blog_platform_front/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform_front/public/logo192.png -------------------------------------------------------------------------------- /blog_platform_front/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform_front/public/logo512.png -------------------------------------------------------------------------------- /blog_platform_front/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | }, 10 | { 11 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /blog_platform_front/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /blog_platform_front/src/App.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /blog_platform_front/src/App.js: -------------------------------------------------------------------------------- 1 | import './App.css'; 2 | import { BrowserRouter as Router, Routes, Route, Navigate} from 'react-router-dom'; 3 | import HomePage from './pages/HomePage'; 4 | import BlogPostFull from './components/BlogPosts/BlogPostFull'; 5 | import LogIn from './components/Auth/Login' 6 | import Register from './components/Auth/Register'; 7 | import PasswordReset from './components/Auth/PasswordRest'; 8 | import PasswordResetConfirmation from './components/Auth/PasswordResetConfirmation'; 9 | import DashBoard from './pages/DashBoard'; 10 | import PrivateRoutes from './components/Auth/PrivateRoutes'; 11 | import {token} from './components/Auth/Token'; 12 | 13 | function App() { 14 | const isAuthorized = !!token 15 | 16 | return ( 17 | 18 | 19 | } /> 20 | } />} /> 21 | } /> 22 | } /> 23 | } /> 24 | } /> 25 | ) : ()} /> 26 | 27 | 28 | ); 29 | } 30 | 31 | export default App; 32 | -------------------------------------------------------------------------------- /blog_platform_front/src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /blog_platform_front/src/components/Auth/LogOut.jsx: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import axios from "axios"; 3 | import API_BASE_URL from '../../config'; 4 | import { token, userData} from "./Token"; 5 | 6 | const LogOut = () => { 7 | const handleLogout = async () => { 8 | 9 | try{ 10 | if(token){ 11 | await axios.post(`${API_BASE_URL}/api/logout`, null, {headers: {Authorization: `Token ${token}`, 'Content-Type': 'application/json',}}); 12 | localStorage.removeItem('token'); 13 | localStorage.removeItem(userData) 14 | window.location.href = '/login'; 15 | } 16 | 17 | }catch (error) { 18 | console.error('LogOut Fail: ', error); 19 | } 20 | } 21 | 22 | return ( 23 | 24 | ) 25 | 26 | } 27 | 28 | export default LogOut -------------------------------------------------------------------------------- /blog_platform_front/src/components/Auth/Login.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import { Link } from 'react-router-dom'; 3 | import axios from "axios"; 4 | import API_BASE_URL from '../../config'; 5 | 6 | const LogIn = () => { 7 | const [username, setUsername] = useState(''); 8 | const [password, setPassword] = useState(''); 9 | const [error, setError] = useState(''); 10 | 11 | const handleLogin = async (e) =>{ 12 | e.preventDefault(); 13 | try{ 14 | const response = await axios.post(`${API_BASE_URL}/api/token`, {username, password}); 15 | const token = response.data.token; // Assuming the response contains the token upon successful login. 16 | localStorage.setItem('token', token); // Store the token securely (e.g., in local storage). 17 | 18 | const userData = { 19 | id: response.data.user_id, 20 | username: response.data.user_name, 21 | } 22 | localStorage.setItem('userData',JSON.stringify(userData)) 23 | window.location.href = '/'; // Redirect to the home page or any desired page. 24 | }catch (error) { 25 | setError('Invalid Login Credentials') 26 | } 27 | }; 28 | 29 | return( 30 |
31 |

Login

32 | {error &&
{error}
} 33 |
34 | setUsername(e.target.value)} /> 35 | setPassword(e.target.value)} /> 36 | 37 |
38 |
Forgot Your Password?
39 |
New to Our Platform? Please Register
40 |
41 | ) 42 | 43 | } 44 | 45 | export default LogIn -------------------------------------------------------------------------------- /blog_platform_front/src/components/Auth/LoginButton.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | const LogInButton = () => { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | } 11 | 12 | export default LogInButton -------------------------------------------------------------------------------- /blog_platform_front/src/components/Auth/PasswordResetConfirmation.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { useParams, Link } from 'react-router-dom'; 3 | import axios from 'axios'; 4 | import API_BASE_URL from "../../config"; 5 | 6 | const PasswordResetConfirmation = () => { 7 | const { uid, token } = useParams() 8 | const [password, setPassword] = useState(''); 9 | const [confirmPassword, setConfirmPassword] = useState(''); 10 | const [success, setSuccess] = useState(''); 11 | const [error, setError] = useState('') 12 | 13 | const handlePasswordResetConfirmation = async (e) => { 14 | e.preventDefault(); 15 | try { 16 | const response = await axios.post(`${API_BASE_URL}/api/password_reset/confirm/${uid}/${token}/`, { 17 | new_password1: password, 18 | new_password2: confirmPassword, 19 | }); 20 | const responseData = response.data; 21 | if (response.status === 200) { 22 | // Password reset was successful 23 | alert(responseData.detail) 24 | setSuccess(responseData.detail); 25 | } else { 26 | // Handle other status codes or error responses if needed 27 | console.log(responseData.detail); 28 | } 29 | setSuccess(true); 30 | } catch (error) { 31 | if(error.response){ 32 | const errorData = error.response.data; 33 | console.error(errorData) 34 | if(errorData.errors){ 35 | setError('Your password should contain at least 8 characters, including at least one letter (a-z or A-Z) and at least one number (0-9).') 36 | }else{ 37 | setError(`${errorData.detail}`) 38 | } 39 | }else{ 40 | setError(error.message) 41 | console.error(error.message); 42 | } 43 | 44 | } 45 | 46 | }; 47 | return ( 48 |
49 | {success ? ( 50 |
{success} Please navigate to the Login Page to access your blog account.
51 | ) : ( 52 | <> 53 | {error &&
{error}
} 54 |
55 | setPassword(e.target.value)} 61 | /> 62 | setConfirmPassword(e.target.value)} 68 | /> 69 | 70 |
71 | 72 | )} 73 |
74 | ); 75 | }; 76 | 77 | export default PasswordResetConfirmation; 78 | -------------------------------------------------------------------------------- /blog_platform_front/src/components/Auth/PasswordRest.jsx: -------------------------------------------------------------------------------- 1 | import React, {useState} from "react"; 2 | import axios from "axios"; 3 | import API_BASE_URL from "../../config"; 4 | 5 | const PasswordReset = () => { 6 | const [email, setEmail] = useState(''); 7 | const [success, setSuccess] = useState(false); 8 | const [error, setError] = useState(''); 9 | 10 | const handleSubmit = async (e) => { 11 | e.preventDefault(); 12 | try{ 13 | await axios.post(`${API_BASE_URL}/api/password_reset`, {email}); 14 | setSuccess(true) 15 | }catch(error){ 16 | if(error.response.status === 400){ 17 | const errorData = error.response.data; 18 | console.log(errorData) 19 | if(errorData.email){ 20 | setError(`${errorData.email[0]}`) 21 | } 22 | 23 | }else{ 24 | setError(error) 25 | } 26 | 27 | } 28 | 29 | } 30 | 31 | return( 32 |
33 | {error &&
{error}
} 34 | {success ? (
Password reset email sent. Check your inbox.
) 35 | : ( 36 |
37 |
38 | setEmail(e.target.value)} /> 39 | 40 |
41 |
42 | ) 43 | 44 | } 45 |
46 | ) 47 | } 48 | 49 | export default PasswordReset; -------------------------------------------------------------------------------- /blog_platform_front/src/components/Auth/PrivateRoutes.jsx: -------------------------------------------------------------------------------- 1 | import { useEffect } from "react"; 2 | import { useNavigate } from "react-router-dom"; 3 | 4 | const PrivateRoutes = ({authenticated, children}) => { 5 | const navigate = useNavigate() 6 | 7 | 8 | useEffect(() => { 9 | if(!authenticated){ 10 | navigate('/login') // Redirect to the login page if not authenticated 11 | } 12 | }, [authenticated, navigate]) 13 | 14 | 15 | 16 | 17 | return ( 18 | authenticated ? children : null 19 | ) 20 | } 21 | 22 | export default PrivateRoutes; -------------------------------------------------------------------------------- /blog_platform_front/src/components/Auth/Register.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState} from "react"; 2 | import { Link } from 'react-router-dom' 3 | import axios from "axios"; 4 | import API_BASE_URL from "../../config"; 5 | 6 | const Register = () => { 7 | const [formData, setFormData] = useState({ 8 | 'first_name': '', 9 | 'last_name': '', 10 | 'email': '', 11 | 'username': '', 12 | 'password': '', 13 | 'confirm_password': '', 14 | 15 | 16 | }) 17 | const [error, setError] = useState(''); 18 | 19 | const handleChange = (e) => { 20 | const {name, value} = e.target; 21 | setFormData({ 22 | ...formData, 23 | [name]: value, 24 | }); 25 | } 26 | 27 | const formSubmission = async (e) => { 28 | e.preventDefault(); 29 | if(formData.password !== formData.confirm_password){ 30 | setError('Passwords does not match.!') 31 | return; 32 | } 33 | const hasNullOrEmpytValue = Object.values(formData).some(value => value === null || value === undefined || value === ''); 34 | if(hasNullOrEmpytValue){ 35 | setError('Please fill in all required fields.') 36 | return; 37 | } 38 | try{ 39 | await axios.post(`${API_BASE_URL}/api/register`, formData); 40 | window.location.href = '/login'; 41 | }catch(error){ 42 | if(error.response.status === 400){ 43 | const errorData = error.response.data; 44 | console.log(errorData) 45 | if(errorData.username){ 46 | setError(`Registration Failed: ${errorData.username[0]}`) 47 | } 48 | if(errorData.email){ 49 | setError(`Registration Failed: ${errorData.email[0]}`) 50 | } 51 | 52 | }else{ 53 | setError(`Registration Failed: ${error}`) 54 | } 55 | } 56 | 57 | } 58 | 59 | return( 60 | <> 61 | {error &&
{error}
} 62 |
63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 |
71 |
Already a member? Login here.
72 | 73 | ) 74 | } 75 | 76 | export default Register 77 | -------------------------------------------------------------------------------- /blog_platform_front/src/components/Auth/Token.jsx: -------------------------------------------------------------------------------- 1 | const token = localStorage.getItem('token'); 2 | const userDataJSON = localStorage.getItem('userData') 3 | const userData = userDataJSON ? JSON.parse(userDataJSON) : null 4 | 5 | export {token, userData}; -------------------------------------------------------------------------------- /blog_platform_front/src/components/BlogPosts/BlogPostDetail.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform_front/src/components/BlogPosts/BlogPostDetail.jsx -------------------------------------------------------------------------------- /blog_platform_front/src/components/BlogPosts/BlogPostForm.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import ReactQuill from 'react-quill'; 3 | import 'react-quill/dist/quill.snow.css'; 4 | import axios from 'axios'; 5 | import API_BASE_URL from '../../config'; 6 | import { useNavigate, useParams } from 'react-router-dom'; 7 | import { token, userData } from '../Auth/Token'; 8 | 9 | const BlogPostForm = () => { 10 | const navigate = useNavigate() 11 | const {id} = useParams() 12 | const isEditing = !!id 13 | const [formData, setFormData] = useState({ 14 | title: '', 15 | content: '', 16 | image: null, 17 | category: '', 18 | tags: [], 19 | author: userData.id, 20 | }) 21 | const [categories, setCategories]= useState([]) 22 | const [tags, setTags]= useState([]) 23 | const [error, setError] = useState('') 24 | const [authorDetails,setAuthorDetails] = useState({}) 25 | 26 | useEffect(() => { 27 | if(isEditing){ 28 | axios.get(`${API_BASE_URL}/api/posts/${id}`, {headers: {Authorization: `Token ${token}`, 'Content-Type': 'application/json',}}) 29 | .then(response => { 30 | setFormData(response.data) 31 | }) 32 | .catch(error => { 33 | setError(`Error fetching blog post: ${error}`) 34 | console.error(`Error fetching blog post: ${error}`); 35 | }) 36 | } 37 | 38 | //Fetch Category List 39 | axios.get(`${API_BASE_URL}/api/categories`, {headers: {Authorization: `Token ${token}`, 'Content-Type': 'application/json',}}) 40 | .then(response => { 41 | setCategories(response.data) 42 | }) 43 | .catch(error => { 44 | setError(`Error fetching categories: ${error}`) 45 | console.error(`Error fetching categories: ${error}`); 46 | }) 47 | 48 | //Fetch Tags 49 | axios.get(`${API_BASE_URL}/api/tags`, {headers: {Authorization: `Token ${token}`, 'Content-Type': 'application/json',}}) 50 | .then(response => { 51 | setTags(response.data) 52 | }) 53 | .catch(error => { 54 | setError(`Error fetching tags: ${error}`) 55 | console.error(`Error fetching tags: ${error}`); 56 | }) 57 | 58 | //Fetch Author Details 59 | axios.get(`${API_BASE_URL}/api/users/${formData.author}`, {headers: {Authorization: `Token ${token}`, 'Content-Type': 'application/json',}}) 60 | .then(response => { 61 | const fetchedAuthorDetails = response.data; 62 | setAuthorDetails(fetchedAuthorDetails) 63 | }) 64 | .catch(error => { 65 | console.error(`Error fetching author data: ${error}`); 66 | }); 67 | 68 | }, [id, isEditing]); 69 | 70 | const handleChange = (e) => { 71 | const { name, value, type } = e.target 72 | if(type === 'checkbox'){ 73 | const selectedTags = formData.tags.includes(value) ? formData.tags.filter(tag => tag !== value) 74 | : [...formData.tags, value] 75 | setFormData({ 76 | ...formData, 77 | [name]: selectedTags, 78 | }) 79 | }else{ 80 | setFormData({ 81 | ...formData, 82 | [name]: value, 83 | }) 84 | } 85 | } 86 | 87 | const handleImageChange = (e) => { 88 | const image = e.target.files[0] 89 | setFormData({ 90 | ...formData, 91 | image: image, 92 | }) 93 | } 94 | 95 | const handleContentChange = (newContent) => { 96 | setFormData({ 97 | ...formData, 98 | content: newContent, 99 | }) 100 | } 101 | 102 | const handleSubmission = (e) => { 103 | e.preventDefault(); 104 | const postData = new FormData(); 105 | postData.append('title', formData.title) 106 | postData.append('content', formData.content) 107 | postData.append('category', formData.category) 108 | postData.append('image', formData.image) 109 | postData.append('author', formData.author) 110 | formData.tags.forEach((tagId) => { 111 | postData.append('tags', tagId); 112 | }) 113 | 114 | if(id){ 115 | //Update blog post 116 | axios.put(`${API_BASE_URL}/api/posts/${id}/`, postData, {headers: {Authorization: `Token ${token}`, 'Content-Type': 'multipart/form-data',}}) 117 | .then(response => { 118 | navigate('/'); 119 | }) 120 | .catch(error => { 121 | setError(`Error updating blog post: ${error}`) 122 | console.error(`Error updating blog post: ${error}`); 123 | }) 124 | }else{ 125 | axios.post(`${API_BASE_URL}/api/posts/`, postData, {headers: {Authorization: `Token ${token}`, 'Content-Type': 'multipart/form-data',}}) 126 | .then(response => { 127 | navigate('/') 128 | }) 129 | .catch(error => { 130 | setError(`Error creating blog post: ${error}`) 131 | console.error(`Error creating blog post: ${error}`); 132 | console.error('Error response:', error.response) 133 | }) 134 | } 135 | } 136 | 137 | return( 138 |
139 |

{isEditing ? 'Edit Blog Post' : 'Create New Blog Post'}

140 | {error &&
{error}
} 141 |
Author: {authorDetails.username}
142 |
143 |
144 | 145 | 146 |
147 |
148 | 149 | 154 |
155 |
156 | 157 | {tags.map(tag => ( 158 | 162 | ))} 163 |
164 |
165 | 166 | 167 |
168 |
169 | 170 | 171 |
172 | 173 |
174 |
175 | ) 176 | } 177 | 178 | export default BlogPostForm -------------------------------------------------------------------------------- /blog_platform_front/src/components/BlogPosts/BlogPostFull.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from "react"; 2 | import axios from "axios"; 3 | import API_BASE_URL from '../../config'; 4 | import { useParams, useNavigate } from "react-router-dom"; 5 | import {token} from "../Auth/Token"; 6 | 7 | const BlogPostFull = () =>{ 8 | const { id } = useParams(); 9 | const [post, setPost] = useState(null); 10 | const navigate = useNavigate(); 11 | 12 | useEffect(() => { 13 | axios.get(`${API_BASE_URL}/api/postlist/${id}/`) 14 | .then((response) => { 15 | setPost(response.data); 16 | }) 17 | .catch((error) => { 18 | alert(`Error fetching blog post: 19 | ${error}`); 20 | }) 21 | },[]); 22 | 23 | const handleEditClick = (postId) =>{ 24 | navigate(`/dashboard/?id=${postId}`) 25 | } 26 | 27 | return( 28 | 29 |
30 |

{post && post.title}

31 |

{post && post.author.username}

32 | {token && } 33 | {post && post.image && 34 |
35 | {post && {post.title} } 36 |
37 | } 38 |
39 | {post &&

} 40 |

41 | Back To Article List 42 |
43 | ) 44 | 45 | 46 | } 47 | 48 | export default BlogPostFull; -------------------------------------------------------------------------------- /blog_platform_front/src/components/BlogPosts/BlogPostList.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect} from "react"; 2 | import axios from "axios"; 3 | import DOMPurify from 'dompurify'; 4 | import API_BASE_URL from '../../config'; 5 | import LoginButton from "../Auth/LoginButton" 6 | import LogOut from "../Auth/LogOut"; 7 | import {token} from "../Auth/Token"; 8 | import DashboadButton from "./DashboardButton"; 9 | 10 | const BlogPostList = () => { 11 | 12 | const [posts, setPosts] = useState([]); 13 | 14 | useEffect(() => { 15 | axios.get(`${API_BASE_URL}/api/postlist`) 16 | .then(response => { 17 | setPosts(response.data); 18 | }) 19 | .catch(error => { 20 | alert(`Error fetching blog posts: 21 | ${error}`); 22 | }) 23 | }, []) 24 | 25 | const getPostReadMore = (content) => { 26 | const words = content.split(' '); 27 | if (words.length > 50){ 28 | return words.slice(0, 50).join(' ') + '...'; 29 | }else{ 30 | return words.join(' '); 31 | } 32 | } 33 | 34 | return( 35 |
36 |

Articles

37 |
{token 38 | ? <> 39 | : }
40 |
    41 | {posts.map(post => ( 42 |
  • 43 |

    {post.title}

    44 |

    {post.author.username}

    45 |

    46 | Read More 47 |

  • 48 | ))} 49 |
50 |
51 | ) 52 | } 53 | 54 | export default BlogPostList; -------------------------------------------------------------------------------- /blog_platform_front/src/components/BlogPosts/DashboardButton.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | 4 | const DashboadButton = () => { 5 | return ( 6 | 7 | 8 | 9 | ); 10 | } 11 | 12 | export default DashboadButton -------------------------------------------------------------------------------- /blog_platform_front/src/components/BlogPosts/MyBlogPosts.jsx: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from "react" 2 | import axios from "axios" 3 | import API_BASE_URL from "../../config" 4 | import { token } from "../Auth/Token" 5 | import DOMPurify from 'dompurify'; 6 | 7 | const MyBlogPost = () => { 8 | const [blogposts, setBlogPost] = useState([]); 9 | 10 | useEffect(() => { 11 | if(token){ 12 | axios.get(`${API_BASE_URL}/api/my_posts`, { 13 | headers: { 14 | Authorization: `Token ${token}` 15 | } 16 | }) 17 | .then(response => { 18 | console.log(response.data) 19 | setBlogPost(response.data) 20 | }) 21 | .catch(error => { 22 | console.log(`Error fetching user-specific blog posts: ${error}`) 23 | alert(`Error fetching user-specific blog posts: ${error}`) 24 | }) 25 | } 26 | },[]); 27 | 28 | const getPostReadMore = (content) => { 29 | const words = content.split(' '); 30 | if (words.length > 50){ 31 | return words.slice(0, 50).join(' ') + '...'; 32 | }else{ 33 | return words.join(' '); 34 | } 35 | } 36 | 37 | return( 38 |
39 |

My Blog Posts

40 |
    41 | {blogposts.map(post => ( 42 |
  • 43 |

    {post.title}

    44 |

    {post.author.username}

    45 |

    46 | Read More 47 |

  • 48 | ))} 49 |
50 |
51 | ); 52 | } 53 | 54 | export default MyBlogPost -------------------------------------------------------------------------------- /blog_platform_front/src/components/Categories/CategoryList.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform_front/src/components/Categories/CategoryList.jsx -------------------------------------------------------------------------------- /blog_platform_front/src/components/Comments/CommentForm.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform_front/src/components/Comments/CommentForm.jsx -------------------------------------------------------------------------------- /blog_platform_front/src/components/Comments/CommentList.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform_front/src/components/Comments/CommentList.jsx -------------------------------------------------------------------------------- /blog_platform_front/src/components/Tags/TagList.jsx: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/koheimizuno/Blog-Django-React-PostgreSQL/71981fcd65e39386567797722268cc5987cbc129/blog_platform_front/src/components/Tags/TagList.jsx -------------------------------------------------------------------------------- /blog_platform_front/src/config.js: -------------------------------------------------------------------------------- 1 | const API_BASE_URL = 'http://127.0.0.1:8000'; // Replace with your actual backend URL 2 | 3 | export default API_BASE_URL; 4 | -------------------------------------------------------------------------------- /blog_platform_front/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 4 | 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | } 9 | 10 | code { 11 | font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', 12 | monospace; 13 | } 14 | -------------------------------------------------------------------------------- /blog_platform_front/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | const root = ReactDOM.createRoot(document.getElementById('root')); 8 | root.render( 9 | 10 | 11 | 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /blog_platform_front/src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /blog_platform_front/src/pages/DashBoard.jsx: -------------------------------------------------------------------------------- 1 | import React, { useState } from "react"; 2 | import {token} from "../components/Auth/Token"; 3 | import LogOut from "../components/Auth/LogOut"; 4 | import BlogPostForm from "../components/BlogPosts/BlogPostForm"; 5 | import MyBlogPost from "../components/BlogPosts/MyBlogPosts"; 6 | 7 | const DashBoard = () => { 8 | const [displayComponent, setDisplayComponent] = useState('create_post') 9 | return( 10 | <> 11 | {token && } 12 |
13 |
14 |
    15 |
  • 16 |
  • 17 |
  • 18 |
  • 19 |
20 |
21 |
22 | {displayComponent === 'create_post' && } 23 | {displayComponent ==='my_posts' && } 24 |
25 | 26 |
27 | 28 | 29 | ) 30 | } 31 | 32 | export default DashBoard; -------------------------------------------------------------------------------- /blog_platform_front/src/pages/HomePage.jsx: -------------------------------------------------------------------------------- 1 | import BlogPostList from '../components/BlogPosts/BlogPostList'; 2 | 3 | const HomePage = () => { 4 | return( 5 | <> 6 | 7 | 8 | ) 9 | } 10 | 11 | 12 | export default HomePage; -------------------------------------------------------------------------------- /blog_platform_front/src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /blog_platform_front/src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | --------------------------------------------------------------------------------