├── backend ├── .gitignore ├── api │ ├── __init__.py │ ├── admin.py │ ├── apps.py │ ├── migrations │ │ ├── 0001_initial.py │ │ ├── 0002_category_gallery_service.py │ │ ├── 0003_auto_20230128_1150.py │ │ ├── 0004_auto_20230128_1920.py │ │ ├── 0005_orders.py │ │ ├── 0006_auto_20230130_0550.py │ │ ├── 0007_profile.py │ │ ├── 0008_auto_20230301_0444.py │ │ ├── 0009_todo.py │ │ ├── 0010_chatmessage.py │ │ └── __init__.py │ ├── models.py │ ├── serializer.py │ ├── tests.py │ ├── urls.py │ └── views.py ├── backend │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── manage.py ├── media │ ├── default.jpg │ └── user_images │ │ ├── 14323626_s84-pm-2698-ploy-mockup.jpg │ │ └── 218884489_1489407691458770_8916744002593220091_n.jpg └── requirements.txt ├── frontend ├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public │ ├── favicon.ico │ ├── index.html │ ├── logo192.png │ ├── logo512.png │ ├── manifest.json │ └── robots.txt └── src │ ├── App.js │ ├── context │ └── AuthContext.js │ ├── index.css │ ├── index.js │ ├── utils │ ├── PrivateRoute.js │ └── useAxios.js │ └── views │ ├── Dashboard.js │ ├── Homepage.js │ ├── Loginpage.js │ ├── Message.js │ ├── MessageDetail.js │ ├── Navbar.js │ ├── Registerpage.js │ ├── SearchUsers.js │ ├── Todo.js │ └── style │ └── Message.css └── templates ├── Authentication ├── dashboard.html ├── index.html ├── login.html └── register.html ├── Chat ├── index.html └── style.css └── To-do └── index.html /backend/.gitignore: -------------------------------------------------------------------------------- 1 | # Byte-compiled / optimized / DLL files 2 | __pycache__/ 3 | *.py[cod] 4 | *$py.class 5 | *$py.class 6 | 7 | # C extensions 8 | *.so 9 | 10 | # Distribution / packaging 11 | .Python 12 | build/ 13 | develop-eggs/ 14 | dist/ 15 | downloads/ 16 | eggs/ 17 | .eggs/ 18 | lib/ 19 | lib64/ 20 | parts/ 21 | sdist/ 22 | var/ 23 | wheels/ 24 | share/python-wheels/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | MANIFEST 29 | 30 | # PyInstaller 31 | # Usually these files are written by a python script from a template 32 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 33 | *.manifest 34 | *.spec 35 | 36 | # Installer logs 37 | pip-log.txt 38 | pip-delete-this-directory.txt 39 | 40 | # Unit test / coverage reports 41 | htmlcov/ 42 | .tox/ 43 | .nox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | *.py,cover 51 | .hypothesis/ 52 | .pytest_cache/ 53 | cover/ 54 | 55 | # Translations 56 | *.mo 57 | *.pot 58 | 59 | # Django stuff: 60 | *.log 61 | local_settings.py 62 | db.sqlite3 63 | db.sqlite3-journal 64 | 65 | # Flask stuff: 66 | instance/ 67 | .webassets-cache 68 | 69 | # Scrapy stuff: 70 | .scrapy 71 | 72 | # Sphinx documentation 73 | docs/_build/ 74 | 75 | # PyBuilder 76 | .pybuilder/ 77 | target/ 78 | 79 | # Jupyter Notebook 80 | .ipynb_checkpoints 81 | 82 | # IPython 83 | profile_default/ 84 | ipython_config.py 85 | 86 | # pyenv 87 | # For a library or package, you might want to ignore these files since the code is 88 | # intended to run in multiple environments; otherwise, check them in: 89 | # .python-version 90 | 91 | # pipenv 92 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. 93 | # However, in case of collaboration, if having platform-specific dependencies or dependencies 94 | # having no cross-platform support, pipenv may install dependencies that don't work, or not 95 | # install all needed dependencies. 96 | #Pipfile.lock 97 | 98 | # poetry 99 | # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. 100 | # This is especially recommended for binary packages to ensure reproducibility, and is more 101 | # commonly ignored for libraries. 102 | # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control 103 | #poetry.lock 104 | 105 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow 106 | __pypackages__/ 107 | 108 | # Celery stuff 109 | celerybeat-schedule 110 | celerybeat.pid 111 | 112 | # SageMath parsed files 113 | *.sage.py 114 | 115 | # Environments 116 | .env 117 | .venv 118 | env/ 119 | venv/ 120 | ENV/ 121 | env.bak/ 122 | venv.bak/ 123 | 124 | # Spyder project settings 125 | .spyderproject 126 | .spyproject 127 | 128 | # Rope project settings 129 | .ropeproject 130 | 131 | # mkdocs documentation 132 | /site 133 | 134 | # mypy 135 | .mypy_cache/ 136 | .dmypy.json 137 | dmypy.json 138 | 139 | # Pyre type checker 140 | .pyre/ 141 | 142 | # pytype static type analyzer 143 | .pytype/ 144 | 145 | # Cython debug symbols 146 | cython_debug/ 147 | 148 | # PyCharm 149 | # JetBrains specific template is maintainted in a separate JetBrains.gitignore that can 150 | # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore 151 | # and can be added to the global gitignore or merged into this file. For a more nuclear 152 | # option (not recommended) you can uncomment the following to ignore the entire idea folder. 153 | .idea/ -------------------------------------------------------------------------------- /backend/api/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/desphixs/Chatapp-using-Django-RestFramework-and-React/5f074730e6a60d934a39b8f49ff67981d9015033/backend/api/__init__.py -------------------------------------------------------------------------------- /backend/api/admin.py: -------------------------------------------------------------------------------- 1 | from django.contrib import admin 2 | from api.models import User,Profile, Todo, ChatMessage 3 | 4 | 5 | class UserAdmin(admin.ModelAdmin): 6 | list_display = ['username', 'email'] 7 | 8 | 9 | class ProfileAdmin(admin.ModelAdmin): 10 | list_editable = ['verified'] 11 | list_display = ['user', 'full_name' ,'verified'] 12 | 13 | class TodoAdmin(admin.ModelAdmin): 14 | list_editable = ['completed'] 15 | list_display = ['user', 'title' ,'completed', 'date'] 16 | 17 | 18 | class ChatMessageAdmin(admin.ModelAdmin): 19 | list_editable = ['is_read', 'message'] 20 | list_display = ['user','sender', 'reciever', 'is_read', 'message'] 21 | 22 | admin.site.register(User, UserAdmin) 23 | admin.site.register( Profile,ProfileAdmin) 24 | admin.site.register( Todo,TodoAdmin) 25 | admin.site.register( ChatMessage,ChatMessageAdmin) 26 | -------------------------------------------------------------------------------- /backend/api/apps.py: -------------------------------------------------------------------------------- 1 | from django.apps import AppConfig 2 | 3 | 4 | class ApiConfig(AppConfig): 5 | default_auto_field = 'django.db.models.BigAutoField' 6 | name = 'api' 7 | -------------------------------------------------------------------------------- /backend/api/migrations/0001_initial.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2023-01-28 09:31 2 | 3 | import django.contrib.auth.models 4 | from django.db import migrations, models 5 | import django.utils.timezone 6 | 7 | 8 | class Migration(migrations.Migration): 9 | 10 | initial = True 11 | 12 | dependencies = [ 13 | ('auth', '0012_alter_user_first_name_max_length'), 14 | ] 15 | 16 | operations = [ 17 | migrations.CreateModel( 18 | name='User', 19 | fields=[ 20 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 21 | ('password', models.CharField(max_length=128, verbose_name='password')), 22 | ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), 23 | ('first_name', models.CharField(blank=True, max_length=150, verbose_name='first name')), 24 | ('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')), 25 | ('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')), 26 | ('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')), 27 | ('username', models.CharField(max_length=100)), 28 | ('email', models.EmailField(max_length=254, unique=True)), 29 | ('is_staff', models.BooleanField(default=False)), 30 | ('is_superuser', models.BooleanField(default=False)), 31 | ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')), 32 | ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')), 33 | ], 34 | options={ 35 | 'verbose_name': 'user', 36 | 'verbose_name_plural': 'users', 37 | 'abstract': False, 38 | }, 39 | managers=[ 40 | ('objects', django.contrib.auth.models.UserManager()), 41 | ], 42 | ), 43 | ] 44 | -------------------------------------------------------------------------------- /backend/api/migrations/0002_category_gallery_service.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2023-01-28 10:29 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import django_ckeditor_5.fields 7 | import shortuuid.django_fields 8 | 9 | 10 | class Migration(migrations.Migration): 11 | 12 | dependencies = [ 13 | ('api', '0001_initial'), 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 | ('cid', shortuuid.django_fields.ShortUUIDField(alphabet='abcdefghijklmnopqrstuvxyz123', length=5, max_length=25, prefix='')), 22 | ('image', models.ImageField(blank=True, default='category.jpg', null=True, upload_to='category')), 23 | ('title', models.CharField(max_length=1000)), 24 | ('slug', models.SlugField(unique=True)), 25 | ('description', models.TextField(blank=True, null=True)), 26 | ('date', models.DateTimeField(auto_now_add=True)), 27 | ], 28 | options={ 29 | 'verbose_name_plural': 'Category', 30 | 'ordering': ['-date'], 31 | }, 32 | ), 33 | migrations.CreateModel( 34 | name='Service', 35 | fields=[ 36 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 37 | ('sid', shortuuid.django_fields.ShortUUIDField(alphabet='abcdefghijklmnopqrstuvxyz', length=10, max_length=25, prefix='')), 38 | ('image', models.ImageField(default='service.jpg', upload_to='service-images')), 39 | ('title', models.CharField(max_length=1000)), 40 | ('description', django_ckeditor_5.fields.CKEditor5Field()), 41 | ('price', models.DecimalField(decimal_places=2, default=1.99, max_digits=12)), 42 | ('tags', models.CharField(blank=True, max_length=10000, null=True)), 43 | ('features', models.CharField(blank=True, max_length=10000, null=True)), 44 | ('phone', models.CharField(max_length=1000)), 45 | ('address', models.CharField(max_length=1000)), 46 | ('country', models.CharField(blank=True, max_length=100, null=True)), 47 | ('state', models.CharField(blank=True, max_length=100, null=True)), 48 | ('zipcode', models.CharField(max_length=1000)), 49 | ('longitude', models.CharField(max_length=1000)), 50 | ('latitude', models.CharField(max_length=1000)), 51 | ('status', models.CharField(choices=[('live', 'Live'), ('in_review', 'In review'), ('pending', 'Pending'), ('cancelled', 'Cancelled'), ('finished', 'Finished')], default='in_review', max_length=100)), 52 | ('date', models.DateTimeField(auto_now_add=True)), 53 | ('category', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to='api.category')), 54 | ('liked', models.ManyToManyField(blank=True, related_name='likes', to=settings.AUTH_USER_MODEL)), 55 | ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, to=settings.AUTH_USER_MODEL)), 56 | ], 57 | options={ 58 | 'verbose_name_plural': '1. Service', 59 | 'ordering': ['-date'], 60 | }, 61 | ), 62 | migrations.CreateModel( 63 | name='Gallery', 64 | fields=[ 65 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 66 | ('image', models.ImageField(default='gallery.jpg', upload_to='userauths.user_directory_path')), 67 | ('title', models.CharField(blank=True, max_length=1000, null=True)), 68 | ('active', models.BooleanField(default=True)), 69 | ('date', models.DateTimeField(auto_now_add=True)), 70 | ('gid', shortuuid.django_fields.ShortUUIDField(alphabet='abcdefghijklmnopqrstuvxyz1231234567890', length=10, max_length=25, prefix='')), 71 | ('service', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='service_gallery', to='api.service')), 72 | ], 73 | options={ 74 | 'verbose_name_plural': 'Service Images', 75 | 'ordering': ['date'], 76 | }, 77 | ), 78 | ] 79 | -------------------------------------------------------------------------------- /backend/api/migrations/0003_auto_20230128_1150.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2023-01-28 10:50 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api', '0002_category_gallery_service'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AlterModelOptions( 14 | name='category', 15 | options={'ordering': ['-date'], 'verbose_name_plural': '2. Category'}, 16 | ), 17 | migrations.AlterModelOptions( 18 | name='service', 19 | options={'ordering': ['-date'], 'verbose_name_plural': '3. Service'}, 20 | ), 21 | migrations.AlterModelOptions( 22 | name='user', 23 | options={'verbose_name_plural': '1. Users'}, 24 | ), 25 | migrations.RemoveField( 26 | model_name='service', 27 | name='phone', 28 | ), 29 | ] 30 | -------------------------------------------------------------------------------- /backend/api/migrations/0004_auto_20230128_1920.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2023-01-28 18:20 2 | 3 | from django.db import migrations 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api', '0003_auto_20230128_1150'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='service', 15 | name='country', 16 | ), 17 | migrations.RemoveField( 18 | model_name='service', 19 | name='state', 20 | ), 21 | migrations.RemoveField( 22 | model_name='service', 23 | name='zipcode', 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /backend/api/migrations/0005_orders.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2023-01-30 02:37 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import shortuuid.django_fields 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('api', '0004_auto_20230128_1920'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Orders', 18 | fields=[ 19 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('description', models.CharField(blank=True, max_length=1000, null=True)), 21 | ('date', models.DateTimeField(auto_now_add=True)), 22 | ('booking_date', models.DateTimeField()), 23 | ('status', models.CharField(choices=[('processing', 'processing'), ('delivering', 'Delivering'), ('completed', 'Completed'), ('cancelled', 'Cancelled')], default='processing', max_length=100)), 24 | ('duration', models.CharField(choices=[('3_days', '3 Days'), ('1_week', '1 week'), ('less_a_month', 'Less than a month'), ('1_to_3_months', '1 to 3 Months')], default='3_days', max_length=100)), 25 | ('oid', shortuuid.django_fields.ShortUUIDField(alphabet='abcdefghijklmnopqrstuvxyz', length=10, max_length=25, prefix='')), 26 | ('buyer', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='buyer', to=settings.AUTH_USER_MODEL)), 27 | ('seller', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='seller', to=settings.AUTH_USER_MODEL)), 28 | ('service', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='service_orders', to='api.service')), 29 | ], 30 | options={ 31 | 'verbose_name_plural': '4. Orders', 32 | 'ordering': ['date'], 33 | }, 34 | ), 35 | ] 36 | -------------------------------------------------------------------------------- /backend/api/migrations/0006_auto_20230130_0550.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2023-01-30 04:50 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api', '0005_orders'), 10 | ] 11 | 12 | operations = [ 13 | migrations.AddField( 14 | model_name='orders', 15 | name='booking_time', 16 | field=models.TimeField(blank=True, null=True), 17 | ), 18 | migrations.AlterField( 19 | model_name='orders', 20 | name='booking_date', 21 | field=models.DateField(blank=True, null=True), 22 | ), 23 | ] 24 | -------------------------------------------------------------------------------- /backend/api/migrations/0007_profile.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2023-01-30 05:27 2 | 3 | from django.conf import settings 4 | from django.db import migrations, models 5 | import django.db.models.deletion 6 | import shortuuid.django_fields 7 | 8 | 9 | class Migration(migrations.Migration): 10 | 11 | dependencies = [ 12 | ('api', '0006_auto_20230130_0550'), 13 | ] 14 | 15 | operations = [ 16 | migrations.CreateModel( 17 | name='Profile', 18 | fields=[ 19 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 20 | ('pid', shortuuid.django_fields.ShortUUIDField(alphabet='1234567890', blank=True, length=20, max_length=25, null=True, prefix='')), 21 | ('full_name', models.CharField(max_length=1000)), 22 | ('bio', models.CharField(default='I am an investor', max_length=100)), 23 | ('country', models.CharField(default='My Country', max_length=100, null=True)), 24 | ('image', models.ImageField(default='default.jpg', upload_to='user_images')), 25 | ('address', models.CharField(max_length=1000)), 26 | ('phone', models.CharField(default='+123 (456) 789', max_length=100)), 27 | ('website', models.URLField(blank=True, default='https://stridearn.com/', null=True)), 28 | ('pin', shortuuid.django_fields.ShortUUIDField(alphabet='1234567890', length=4, max_length=20, prefix='', unique=True)), 29 | ('facebook', models.URLField(blank=True, default='https://facebook.com/', null=True)), 30 | ('instagram', models.URLField(blank=True, default='https://instagram.com/', null=True)), 31 | ('twitter', models.URLField(blank=True, default='https://twitter.com/', null=True)), 32 | ('whatsApp', models.CharField(blank=True, default='+123 (456) 789', max_length=100, null=True)), 33 | ('level_1', models.BooleanField(default=False)), 34 | ('level_2', models.BooleanField(default=False)), 35 | ('level_3', models.BooleanField(default=False)), 36 | ('level_4', models.BooleanField(default=False)), 37 | ('level_5', models.BooleanField(default=False)), 38 | ('verified', models.BooleanField(default=False)), 39 | ('wallet', models.DecimalField(decimal_places=2, default=0.0, max_digits=12)), 40 | ('code', shortuuid.django_fields.ShortUUIDField(alphabet='1234567890abcdefghij', length=10, max_length=20, prefix='REF', unique=True)), 41 | ('date', models.DateTimeField(auto_now_add=True)), 42 | ('recommended_by', models.ForeignKey(blank=True, null=True, on_delete=django.db.models.deletion.DO_NOTHING, related_name='ref_by', to=settings.AUTH_USER_MODEL)), 43 | ('user', models.OneToOneField(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 44 | ], 45 | options={ 46 | 'ordering': ['-date'], 47 | }, 48 | ), 49 | ] 50 | -------------------------------------------------------------------------------- /backend/api/migrations/0008_auto_20230301_0444.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.7 on 2023-03-01 03:44 2 | 3 | from django.db import migrations, models 4 | 5 | 6 | class Migration(migrations.Migration): 7 | 8 | dependencies = [ 9 | ('api', '0007_profile'), 10 | ] 11 | 12 | operations = [ 13 | migrations.RemoveField( 14 | model_name='gallery', 15 | name='service', 16 | ), 17 | migrations.RemoveField( 18 | model_name='orders', 19 | name='buyer', 20 | ), 21 | migrations.RemoveField( 22 | model_name='orders', 23 | name='seller', 24 | ), 25 | migrations.RemoveField( 26 | model_name='orders', 27 | name='service', 28 | ), 29 | migrations.RemoveField( 30 | model_name='service', 31 | name='category', 32 | ), 33 | migrations.RemoveField( 34 | model_name='service', 35 | name='liked', 36 | ), 37 | migrations.RemoveField( 38 | model_name='service', 39 | name='user', 40 | ), 41 | migrations.AlterModelOptions( 42 | name='profile', 43 | options={}, 44 | ), 45 | migrations.AlterModelOptions( 46 | name='user', 47 | options={'verbose_name': 'user', 'verbose_name_plural': 'users'}, 48 | ), 49 | migrations.RemoveField( 50 | model_name='profile', 51 | name='address', 52 | ), 53 | migrations.RemoveField( 54 | model_name='profile', 55 | name='code', 56 | ), 57 | migrations.RemoveField( 58 | model_name='profile', 59 | name='country', 60 | ), 61 | migrations.RemoveField( 62 | model_name='profile', 63 | name='date', 64 | ), 65 | migrations.RemoveField( 66 | model_name='profile', 67 | name='facebook', 68 | ), 69 | migrations.RemoveField( 70 | model_name='profile', 71 | name='instagram', 72 | ), 73 | migrations.RemoveField( 74 | model_name='profile', 75 | name='level_1', 76 | ), 77 | migrations.RemoveField( 78 | model_name='profile', 79 | name='level_2', 80 | ), 81 | migrations.RemoveField( 82 | model_name='profile', 83 | name='level_3', 84 | ), 85 | migrations.RemoveField( 86 | model_name='profile', 87 | name='level_4', 88 | ), 89 | migrations.RemoveField( 90 | model_name='profile', 91 | name='level_5', 92 | ), 93 | migrations.RemoveField( 94 | model_name='profile', 95 | name='phone', 96 | ), 97 | migrations.RemoveField( 98 | model_name='profile', 99 | name='pid', 100 | ), 101 | migrations.RemoveField( 102 | model_name='profile', 103 | name='pin', 104 | ), 105 | migrations.RemoveField( 106 | model_name='profile', 107 | name='recommended_by', 108 | ), 109 | migrations.RemoveField( 110 | model_name='profile', 111 | name='twitter', 112 | ), 113 | migrations.RemoveField( 114 | model_name='profile', 115 | name='wallet', 116 | ), 117 | migrations.RemoveField( 118 | model_name='profile', 119 | name='website', 120 | ), 121 | migrations.RemoveField( 122 | model_name='profile', 123 | name='whatsApp', 124 | ), 125 | migrations.AlterField( 126 | model_name='profile', 127 | name='bio', 128 | field=models.CharField(max_length=100), 129 | ), 130 | migrations.AlterField( 131 | model_name='user', 132 | name='is_staff', 133 | field=models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status'), 134 | ), 135 | migrations.AlterField( 136 | model_name='user', 137 | name='is_superuser', 138 | field=models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status'), 139 | ), 140 | migrations.DeleteModel( 141 | name='Category', 142 | ), 143 | migrations.DeleteModel( 144 | name='Gallery', 145 | ), 146 | migrations.DeleteModel( 147 | name='Orders', 148 | ), 149 | migrations.DeleteModel( 150 | name='Service', 151 | ), 152 | ] 153 | -------------------------------------------------------------------------------- /backend/api/migrations/0009_todo.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-06-26 13:01 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 | dependencies = [ 11 | ('api', '0008_auto_20230301_0444'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='Todo', 17 | fields=[ 18 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('title', models.CharField(max_length=1000)), 20 | ('completed', models.BooleanField(default=False)), 21 | ('date', models.DateTimeField(auto_now_add=True)), 22 | ('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)), 23 | ], 24 | ), 25 | ] 26 | -------------------------------------------------------------------------------- /backend/api/migrations/0010_chatmessage.py: -------------------------------------------------------------------------------- 1 | # Generated by Django 3.2.18 on 2023-08-11 15:39 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 | dependencies = [ 11 | ('api', '0009_todo'), 12 | ] 13 | 14 | operations = [ 15 | migrations.CreateModel( 16 | name='ChatMessage', 17 | fields=[ 18 | ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), 19 | ('message', models.CharField(max_length=10000000000)), 20 | ('is_read', models.BooleanField(default=False)), 21 | ('date', models.DateTimeField(auto_now_add=True)), 22 | ('reciever', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='reciever', to=settings.AUTH_USER_MODEL)), 23 | ('sender', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='sender', to=settings.AUTH_USER_MODEL)), 24 | ('user', models.ForeignKey(null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='user', to=settings.AUTH_USER_MODEL)), 25 | ], 26 | options={ 27 | 'verbose_name_plural': 'Message', 28 | 'ordering': ['date'], 29 | }, 30 | ), 31 | ] 32 | -------------------------------------------------------------------------------- /backend/api/migrations/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/desphixs/Chatapp-using-Django-RestFramework-and-React/5f074730e6a60d934a39b8f49ff67981d9015033/backend/api/migrations/__init__.py -------------------------------------------------------------------------------- /backend/api/models.py: -------------------------------------------------------------------------------- 1 | from django.db import models 2 | from django.db.models.signals import post_save 3 | from django.contrib.auth.models import AbstractUser 4 | 5 | 6 | class User(AbstractUser): 7 | username = models.CharField(max_length=100) 8 | email = models.EmailField(unique=True) 9 | 10 | USERNAME_FIELD = 'email' 11 | REQUIRED_FIELDS = ['username'] 12 | 13 | 14 | def profile(self): 15 | profile = Profile.objects.get(user=self) 16 | 17 | 18 | 19 | class Profile(models.Model): 20 | user = models.OneToOneField(User, on_delete=models.CASCADE) 21 | full_name = models.CharField(max_length=1000) 22 | bio = models.CharField(max_length=100) 23 | image = models.ImageField(upload_to="user_images", default="default.jpg") 24 | verified = models.BooleanField(default=False) 25 | 26 | def save(self, *args, **kwargs): 27 | if self.full_name == "" or self.full_name == None: 28 | self.full_name = self.user.username 29 | super(Profile, self).save(*args, **kwargs) 30 | 31 | 32 | def create_user_profile(sender, instance, created, **kwargs): 33 | if created: 34 | Profile.objects.create(user=instance) 35 | 36 | def save_user_profile(sender, instance, **kwargs): 37 | instance.profile.save() 38 | 39 | post_save.connect(create_user_profile, sender=User) 40 | post_save.connect(save_user_profile, sender=User) 41 | 42 | 43 | # Todo List 44 | class Todo(models.Model): 45 | user = models.ForeignKey(User, on_delete=models.CASCADE) 46 | title = models.CharField(max_length=1000) 47 | completed = models.BooleanField(default=False) 48 | date = models.DateTimeField(auto_now_add=True) 49 | 50 | def __str__(self): 51 | return self.title[:30] 52 | 53 | 54 | 55 | # Chat App Model 56 | class ChatMessage(models.Model): 57 | user = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name="user") 58 | sender = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name="sender") 59 | reciever = models.ForeignKey(User, on_delete=models.SET_NULL, null=True, related_name="reciever") 60 | 61 | message = models.CharField(max_length=10000000000) 62 | 63 | is_read = models.BooleanField(default=False) 64 | date = models.DateTimeField(auto_now_add=True) 65 | 66 | class Meta: 67 | ordering = ['date'] 68 | verbose_name_plural = "Message" 69 | 70 | def __str__(self): 71 | return f"{self.sender} - {self.reciever}" 72 | 73 | @property 74 | def sender_profile(self): 75 | sender_profile = Profile.objects.get(user=self.sender) 76 | return sender_profile 77 | @property 78 | def reciever_profile(self): 79 | reciever_profile = Profile.objects.get(user=self.reciever) 80 | return reciever_profile -------------------------------------------------------------------------------- /backend/api/serializer.py: -------------------------------------------------------------------------------- 1 | from api.models import User, Todo, ChatMessage, Profile 2 | from django.contrib.auth.password_validation import validate_password 3 | from rest_framework_simplejwt.serializers import TokenObtainPairSerializer 4 | from rest_framework import serializers 5 | from rest_framework.validators import UniqueValidator 6 | from rest_framework_simplejwt.serializers import TokenObtainPairSerializer 7 | 8 | class UserSerializer(serializers.ModelSerializer): 9 | 10 | class Meta: 11 | model = User 12 | fields = ('id', 'username', 'email') 13 | 14 | class MyTokenObtainPairSerializer(TokenObtainPairSerializer): 15 | @classmethod 16 | def get_token(cls, user): 17 | token = super().get_token(user) 18 | 19 | # These are claims, you can add custom claims 20 | token['full_name'] = user.profile.full_name 21 | token['username'] = user.username 22 | token['email'] = user.email 23 | token['bio'] = user.profile.bio 24 | token['image'] = str(user.profile.image) 25 | token['verified'] = user.profile.verified 26 | # ... 27 | return token 28 | 29 | 30 | class RegisterSerializer(serializers.ModelSerializer): 31 | password = serializers.CharField( 32 | write_only=True, required=True, validators=[validate_password]) 33 | password2 = serializers.CharField(write_only=True, required=True) 34 | 35 | class Meta: 36 | model = User 37 | fields = ('email', 'username', 'password', 'password2') 38 | 39 | def validate(self, attrs): 40 | if attrs['password'] != attrs['password2']: 41 | raise serializers.ValidationError( 42 | {"password": "Password fields didn't match."}) 43 | 44 | return attrs 45 | 46 | def create(self, validated_data): 47 | user = User.objects.create( 48 | username=validated_data['username'], 49 | email=validated_data['email'] 50 | 51 | ) 52 | 53 | user.set_password(validated_data['password']) 54 | user.save() 55 | 56 | return user 57 | 58 | 59 | class TodoSerializer(serializers.ModelSerializer): 60 | 61 | class Meta: 62 | model = Todo 63 | fields = ['id', 'user', 'title', 'completed'] 64 | 65 | 66 | 67 | class ProfileSerializer(serializers.ModelSerializer): 68 | 69 | class Meta: 70 | model = Profile 71 | fields = [ 'id', 'user', 'full_name', 'image' ] 72 | 73 | def __init__(self, *args, **kwargs): 74 | super(ProfileSerializer, self).__init__(*args, **kwargs) 75 | request = self.context.get('request') 76 | if request and request.method=='POST': 77 | self.Meta.depth = 0 78 | else: 79 | self.Meta.depth = 3 80 | 81 | 82 | class MessageSerializer(serializers.ModelSerializer): 83 | reciever_profile = ProfileSerializer(read_only=True) 84 | sender_profile = ProfileSerializer(read_only=True) 85 | 86 | class Meta: 87 | model = ChatMessage 88 | fields = ['id','sender', 'reciever', 'reciever_profile', 'sender_profile' ,'message', 'is_read', 'date'] 89 | 90 | def __init__(self, *args, **kwargs): 91 | super(MessageSerializer, self).__init__(*args, **kwargs) 92 | request = self.context.get('request') 93 | if request and request.method=='POST': 94 | self.Meta.depth = 0 95 | else: 96 | self.Meta.depth = 2 97 | 98 | 99 | 100 | -------------------------------------------------------------------------------- /backend/api/tests.py: -------------------------------------------------------------------------------- 1 | from django.test import TestCase 2 | 3 | # Create your tests here. 4 | -------------------------------------------------------------------------------- /backend/api/urls.py: -------------------------------------------------------------------------------- 1 | from django.urls import path 2 | from . import views 3 | 4 | from rest_framework_simplejwt.views import ( 5 | TokenRefreshView, 6 | ) 7 | 8 | urlpatterns = [ 9 | path('token/', views.MyTokenObtainPairView.as_view(), name='token_obtain_pair'), 10 | path('token/refresh/', TokenRefreshView.as_view(), name='token_refresh'), 11 | path('register/', views.RegisterView.as_view(), name='auth_register'), 12 | path('test/', views.testEndPoint, name='test'), 13 | path('', views.getRoutes), 14 | 15 | # Todo URLS 16 | path("todo//", views.TodoListView.as_view()), 17 | path("todo-detail///", views.TodoDetailView.as_view()), 18 | path("todo-mark-as-completed///", views.TodoMarkAsCompleted.as_view()), 19 | 20 | # Chat/Text Messaging Functionality 21 | path("my-messages//", views.MyInbox.as_view()), 22 | path("get-messages///", views.GetMessages.as_view()), 23 | path("send-messages/", views.SendMessages.as_view()), 24 | 25 | # Get profile 26 | path("profile//", views.ProfileDetail.as_view()), 27 | path("search//", views.SearchUser.as_view()), 28 | 29 | ] -------------------------------------------------------------------------------- /backend/api/views.py: -------------------------------------------------------------------------------- 1 | from django.shortcuts import render 2 | from django.http import JsonResponse 3 | from django.db.models import OuterRef, Subquery 4 | from django.db.models import Q 5 | 6 | from api.models import User, Todo, Profile, ChatMessage 7 | 8 | from api.serializer import MyTokenObtainPairSerializer, RegisterSerializer, TodoSerializer, MessageSerializer, ProfileSerializer,UserSerializer 9 | 10 | from rest_framework.decorators import api_view 11 | from rest_framework.response import Response 12 | from rest_framework_simplejwt.views import TokenObtainPairView 13 | from rest_framework import generics 14 | from rest_framework.permissions import AllowAny, IsAuthenticated 15 | from rest_framework import status 16 | from rest_framework.decorators import api_view, permission_classes 17 | 18 | 19 | class MyTokenObtainPairView(TokenObtainPairView): 20 | serializer_class = MyTokenObtainPairSerializer 21 | 22 | class RegisterView(generics.CreateAPIView): 23 | queryset = User.objects.all() 24 | permission_classes = (AllowAny,) 25 | serializer_class = RegisterSerializer 26 | 27 | 28 | # Get All Routes 29 | 30 | @api_view(['GET']) 31 | def getRoutes(request): 32 | routes = [ 33 | '/api/token/', 34 | '/api/register/', 35 | '/api/token/refresh/' 36 | ] 37 | return Response(routes) 38 | 39 | 40 | @api_view(['GET', 'POST']) 41 | @permission_classes([IsAuthenticated]) 42 | def testEndPoint(request): 43 | if request.method == 'GET': 44 | data = f"Congratulation {request.user}, your API just responded to GET request" 45 | return Response({'response': data}, status=status.HTTP_200_OK) 46 | elif request.method == 'POST': 47 | text = "Hello buddy" 48 | data = f'Congratulation your API just responded to POST request with text: {text}' 49 | return Response({'response': data}, status=status.HTTP_200_OK) 50 | return Response({}, status.HTTP_400_BAD_REQUEST) 51 | 52 | 53 | class TodoListView(generics.ListCreateAPIView): 54 | queryset = Todo.objects.all() 55 | serializer_class = TodoSerializer 56 | 57 | def get_queryset(self): 58 | user_id = self.kwargs['user_id'] 59 | user = User.objects.get(id=user_id) 60 | 61 | todo = Todo.objects.filter(user=user) 62 | return todo 63 | 64 | 65 | class TodoDetailView(generics.RetrieveUpdateDestroyAPIView): 66 | serializer_class = TodoSerializer 67 | 68 | def get_object(self): 69 | user_id = self.kwargs['user_id'] 70 | todo_id = self.kwargs['todo_id'] 71 | 72 | user = User.objects.get(id=user_id) 73 | todo = Todo.objects.get(id=todo_id, user=user) 74 | 75 | return todo 76 | 77 | 78 | class TodoMarkAsCompleted(generics.RetrieveUpdateDestroyAPIView): 79 | serializer_class = TodoSerializer 80 | 81 | def get_object(self): 82 | user_id = self.kwargs['user_id'] 83 | todo_id = self.kwargs['todo_id'] 84 | 85 | user = User.objects.get(id=user_id) 86 | todo = Todo.objects.get(id=todo_id, user=user) 87 | 88 | todo.completed = True 89 | todo.save() 90 | 91 | return todo 92 | 93 | 94 | # Chat APp 95 | class MyInbox(generics.ListAPIView): 96 | serializer_class = MessageSerializer 97 | 98 | def get_queryset(self): 99 | user_id = self.kwargs['user_id'] 100 | 101 | messages = ChatMessage.objects.filter( 102 | id__in = Subquery( 103 | User.objects.filter( 104 | Q(sender__reciever=user_id) | 105 | Q(reciever__sender=user_id) 106 | ).distinct().annotate( 107 | last_msg=Subquery( 108 | ChatMessage.objects.filter( 109 | Q(sender=OuterRef('id'),reciever=user_id) | 110 | Q(reciever=OuterRef('id'),sender=user_id) 111 | ).order_by('-id')[:1].values_list('id',flat=True) 112 | ) 113 | ).values_list('last_msg', flat=True).order_by("-id") 114 | ) 115 | ).order_by("-id") 116 | 117 | return messages 118 | 119 | class GetMessages(generics.ListAPIView): 120 | serializer_class = MessageSerializer 121 | 122 | def get_queryset(self): 123 | sender_id = self.kwargs['sender_id'] 124 | reciever_id = self.kwargs['reciever_id'] 125 | messages = ChatMessage.objects.filter(sender__in=[sender_id, reciever_id], reciever__in=[sender_id, reciever_id]) 126 | return messages 127 | 128 | class SendMessages(generics.CreateAPIView): 129 | serializer_class = MessageSerializer 130 | 131 | 132 | 133 | class ProfileDetail(generics.RetrieveUpdateAPIView): 134 | serializer_class = ProfileSerializer 135 | queryset = Profile.objects.all() 136 | permission_classes = [IsAuthenticated] 137 | 138 | 139 | class SearchUser(generics.ListAPIView): 140 | serializer_class = ProfileSerializer 141 | queryset = Profile.objects.all() 142 | permission_classes = [IsAuthenticated] 143 | 144 | def list(self, request, *args, **kwargs): 145 | username = self.kwargs['username'] 146 | logged_in_user = self.request.user 147 | users = Profile.objects.filter(Q(user__username__icontains=username) | Q(full_name__icontains=username) | Q(user__email__icontains=username) & 148 | ~Q(user=logged_in_user)) 149 | 150 | if not users.exists(): 151 | return Response( 152 | {"detail": "No users found."}, 153 | status=status.HTTP_404_NOT_FOUND 154 | ) 155 | 156 | serializer = self.get_serializer(users, many=True) 157 | return Response(serializer.data) -------------------------------------------------------------------------------- /backend/backend/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/desphixs/Chatapp-using-Django-RestFramework-and-React/5f074730e6a60d934a39b8f49ff67981d9015033/backend/backend/__init__.py -------------------------------------------------------------------------------- /backend/backend/asgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | ASGI config for backend 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.0/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', 'backend.settings') 15 | 16 | application = get_asgi_application() 17 | -------------------------------------------------------------------------------- /backend/backend/settings.py: -------------------------------------------------------------------------------- 1 | """ 2 | Django settings for backend project. 3 | 4 | Generated by 'django-admin startproject' using Django 4.0.1. 5 | 6 | For more information on this file, see 7 | https://docs.djangoproject.com/en/4.0/topics/settings/ 8 | 9 | For the full list of settings and their values, see 10 | https://docs.djangoproject.com/en/4.0/ref/settings/ 11 | """ 12 | 13 | from pathlib import Path 14 | from datetime import timedelta 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.0/howto/deployment/checklist/ 22 | 23 | # SECURITY WARNING: keep the secret key used in production secret! 24 | SECRET_KEY = 'django-insecure-xmyy56c+#c$!7u^#8#&(egh&2_+or##y4+t)xps)i#zbhlw(5o' 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 | 'jazzmin', 36 | 37 | 'django.contrib.admin', 38 | 'django.contrib.auth', 39 | 'django.contrib.contenttypes', 40 | 'django.contrib.sessions', 41 | 'django.contrib.messages', 42 | 'django.contrib.staticfiles', 43 | 44 | # Custom 45 | 'api', 46 | 47 | # Third Party 48 | 'import_export', 49 | 'crispy_forms', 50 | 'mathfilters', 51 | 'django.contrib.humanize', 52 | 'ckeditor', 53 | 'ckeditor_uploader', 54 | 'django_ckeditor_5', 55 | 'taggit', 56 | 'rest_framework', 57 | 'rest_framework_simplejwt.token_blacklist', 58 | 'corsheaders', 59 | ] 60 | 61 | MIDDLEWARE = [ 62 | 'django.middleware.security.SecurityMiddleware', 63 | 'corsheaders.middleware.CorsMiddleware', 64 | 'django.contrib.sessions.middleware.SessionMiddleware', 65 | 'django.middleware.common.CommonMiddleware', 66 | 'django.middleware.csrf.CsrfViewMiddleware', 67 | 'django.contrib.auth.middleware.AuthenticationMiddleware', 68 | 'django.contrib.messages.middleware.MessageMiddleware', 69 | 'django.middleware.clickjacking.XFrameOptionsMiddleware', 70 | ] 71 | 72 | ROOT_URLCONF = 'backend.urls' 73 | 74 | TEMPLATES = [ 75 | { 76 | 'BACKEND': 'django.template.backends.django.DjangoTemplates', 77 | 'DIRS': [BASE_DIR / 'templates'], 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 = 'backend.wsgi.application' 91 | 92 | 93 | # Database 94 | # https://docs.djangoproject.com/en/4.0/ref/settings/#databases 95 | 96 | DATABASES = { 97 | 'default': { 98 | 'ENGINE': 'django.db.backends.sqlite3', 99 | 'NAME': BASE_DIR / 'db.sqlite3', 100 | } 101 | } 102 | 103 | 104 | # Password validation 105 | # https://docs.djangoproject.com/en/4.0/ref/settings/#auth-password-validators 106 | 107 | AUTH_PASSWORD_VALIDATORS = [ 108 | { 109 | 'NAME': 'django.contrib.auth.password_validation.UserAttributeSimilarityValidator', 110 | }, 111 | { 112 | 'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator', 113 | }, 114 | { 115 | 'NAME': 'django.contrib.auth.password_validation.CommonPasswordValidator', 116 | }, 117 | { 118 | 'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator', 119 | }, 120 | ] 121 | 122 | AUTH_USER_MODEL = 'api.User' 123 | 124 | REST_FRAMEWORK = { 125 | 'DEFAULT_AUTHENTICATION_CLASSES': ( 126 | 'rest_framework_simplejwt.authentication.JWTAuthentication', 127 | ) 128 | } 129 | 130 | SIMPLE_JWT = { 131 | 'ACCESS_TOKEN_LIFETIME': timedelta(minutes=5), 132 | 'REFRESH_TOKEN_LIFETIME': timedelta(days=50), 133 | 'ROTATE_REFRESH_TOKENS': True, 134 | 'BLACKLIST_AFTER_ROTATION': True, 135 | 'UPDATE_LAST_LOGIN': False, 136 | 137 | 'ALGORITHM': 'HS256', 138 | 139 | 'VERIFYING_KEY': None, 140 | 'AUDIENCE': None, 141 | 'ISSUER': None, 142 | 'JWK_URL': None, 143 | 'LEEWAY': 0, 144 | 145 | 'AUTH_HEADER_TYPES': ('Bearer',), 146 | 'AUTH_HEADER_NAME': 'HTTP_AUTHORIZATION', 147 | 'USER_ID_FIELD': 'id', 148 | 'USER_ID_CLAIM': 'user_id', 149 | 'USER_AUTHENTICATION_RULE': 'rest_framework_simplejwt.authentication.default_user_authentication_rule', 150 | 151 | 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',), 152 | 'TOKEN_TYPE_CLAIM': 'token_type', 153 | 'TOKEN_USER_CLASS': 'rest_framework_simplejwt.models.TokenUser', 154 | 155 | 'JTI_CLAIM': 'jti', 156 | 157 | 'SLIDING_TOKEN_REFRESH_EXP_CLAIM': 'refresh_exp', 158 | 'SLIDING_TOKEN_LIFETIME': timedelta(minutes=5), 159 | 'SLIDING_TOKEN_REFRESH_LIFETIME': timedelta(days=1), 160 | } 161 | 162 | CORS_ALLOW_ALL_ORIGINS = True 163 | 164 | # Internationalization 165 | # https://docs.djangoproject.com/en/4.0/topics/i18n/ 166 | 167 | LANGUAGE_CODE = 'en-us' 168 | 169 | TIME_ZONE = 'UTC' 170 | 171 | USE_I18N = True 172 | 173 | USE_TZ = True 174 | 175 | 176 | # Static files (CSS, JavaScript, Images) 177 | # https://docs.djangoproject.com/en/4.0/howto/static-files/ 178 | 179 | STATIC_URL = 'static/' 180 | STATICFILES_DIRS = [BASE_DIR / 'static'] 181 | STATIC_ROOT = BASE_DIR / 'staticfiles' 182 | 183 | MEDIA_URL = 'media/' 184 | MEDIA_ROOT = BASE_DIR / 'media' 185 | 186 | # Default primary key field type 187 | # https://docs.djangoproject.com/en/4.0/ref/settings/#default-auto-field 188 | 189 | DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField' 190 | 191 | 192 | customColorPalette = [ 193 | {"color": "hsl(4, 90%, 58%)", "label": "Red"}, 194 | {"color": "hsl(340, 82%, 52%)", "label": "Pink"}, 195 | {"color": "hsl(291, 64%, 42%)", "label": "Purple"}, 196 | {"color": "hsl(262, 52%, 47%)", "label": "Deep Purple"}, 197 | {"color": "hsl(231, 48%, 48%)", "label": "Indigo"}, 198 | {"color": "hsl(207, 90%, 54%)", "label": "Blue"}, 199 | ] 200 | 201 | CKEDITOR_5_CONFIGS = { 202 | "default": { 203 | "toolbar": [ 204 | "heading", 205 | "|", 206 | "bold", 207 | "italic", 208 | "link", 209 | "bulletedList", 210 | "numberedList", 211 | "blockQuote", 212 | "imageUpload" 213 | ], 214 | }, 215 | "comment": { 216 | "language": {"ui": "en", "content": "en"}, 217 | "toolbar": [ 218 | "heading", 219 | "|", 220 | "bold", 221 | "italic", 222 | "link", 223 | "bulletedList", 224 | "numberedList", 225 | "blockQuote", 226 | ], 227 | }, 228 | "extends": { 229 | "language": "en", 230 | "blockToolbar": [ 231 | "paragraph", 232 | "heading1", 233 | "heading2", 234 | "heading3", 235 | "|", 236 | "bulletedList", 237 | "numberedList", 238 | "|", 239 | "blockQuote", 240 | ], 241 | "toolbar": [ 242 | # "heading", 243 | # "codeBlock", 244 | # "|", 245 | 246 | # "|", 247 | "bold", 248 | "italic", 249 | # "link", 250 | "underline", 251 | "strikethrough", 252 | # "code", 253 | # "subscript", 254 | # "superscript", 255 | # "highlight", 256 | "|", 257 | "bulletedList", 258 | # "numberedList", 259 | # "todoList", 260 | # "|", 261 | # "outdent", 262 | # "indent", 263 | # "|", 264 | # "blockQuote", 265 | # "insertImage", 266 | # "|", 267 | # "fontSize", 268 | # "fontFamily", 269 | # "fontColor", 270 | # "fontBackgroundColor", 271 | # "mediaEmbed", 272 | "removeFormat", 273 | # "insertTable", 274 | # "sourceEditing", 275 | ], 276 | "image": { 277 | "toolbar": [ 278 | "imageTextAlternative", 279 | "|", 280 | "imageStyle:alignLeft", 281 | "imageStyle:alignRight", 282 | "imageStyle:alignCenter", 283 | "imageStyle:side", 284 | "|", 285 | "toggleImageCaption", 286 | "|" 287 | ], 288 | "styles": [ 289 | "full", 290 | "side", 291 | "alignLeft", 292 | "alignRight", 293 | "alignCenter", 294 | ], 295 | }, 296 | "table": { 297 | "contentToolbar": [ 298 | "tableColumn", 299 | "tableRow", 300 | "mergeTableCells", 301 | "tableProperties", 302 | "tableCellProperties", 303 | ], 304 | "tableProperties": { 305 | "borderColors": customColorPalette, 306 | "backgroundColors": customColorPalette, 307 | }, 308 | "tableCellProperties": { 309 | "borderColors": customColorPalette, 310 | "backgroundColors": customColorPalette, 311 | }, 312 | }, 313 | "heading": { 314 | "options": [ 315 | { 316 | "model": "paragraph", 317 | "title": "Paragraph", 318 | "class": "ck-heading_paragraph", 319 | }, 320 | { 321 | "model": "heading1", 322 | "view": "h1", 323 | "title": "Heading 1", 324 | "class": "ck-heading_heading1", 325 | }, 326 | { 327 | "model": "heading2", 328 | "view": "h2", 329 | "title": "Heading 2", 330 | "class": "ck-heading_heading2", 331 | }, 332 | { 333 | "model": "heading3", 334 | "view": "h3", 335 | "title": "Heading 3", 336 | "class": "ck-heading_heading3", 337 | }, 338 | ] 339 | }, 340 | "list": { 341 | "properties": { 342 | "styles": True, 343 | "startIndex": True, 344 | "reversed": True, 345 | } 346 | }, 347 | "htmlSupport": { 348 | "allow": [ 349 | {"name": "/.*/", "attributes": True, "classes": True, "styles": True} 350 | ] 351 | }, 352 | }, 353 | } 354 | 355 | CKEDITOR_UPLOAD_PATH = 'uploads/' 356 | 357 | 358 | JAZZMIN_SETTINGS = { 359 | "site_title": "Django & React JWT Authentication ", 360 | "welcome_sign": "Hey there...welcome back", 361 | "site_header": "Django & React JWT Authentication ", 362 | "site_brand": "Think | Create | Inspire", 363 | "copyright": "www.desphixs.com", 364 | } 365 | 366 | 367 | JAZZMIN_UI_TWEAKS = { 368 | "navbar_small_text": False, 369 | "footer_small_text": False, 370 | "body_small_text": False, 371 | "brand_small_text": False, 372 | "brand_colour": "navbar-success", 373 | "accent": "accent-teal", 374 | "navbar": "navbar-dark", 375 | "no_navbar_border": False, 376 | "navbar_fixed": False, 377 | "layout_boxed": False, 378 | "footer_fixed": False, 379 | "sidebar_fixed": False, 380 | "sidebar": "sidebar-dark-info", 381 | "sidebar_nav_small_text": False, 382 | "sidebar_disable_expand": False, 383 | "sidebar_nav_child_indent": False, 384 | "sidebar_nav_compact_style": False, 385 | "sidebar_nav_legacy_style": False, 386 | "sidebar_nav_flat_style": False, 387 | "theme": "cyborg", 388 | "dark_mode_theme": None, 389 | "button_classes": { 390 | "primary": "btn-primary", 391 | "secondary": "btn-secondary", 392 | "info": "btn-info", 393 | "warning": "btn-warning", 394 | "danger": "btn-danger", 395 | "success": "btn-success", 396 | }, 397 | } -------------------------------------------------------------------------------- /backend/backend/urls.py: -------------------------------------------------------------------------------- 1 | # backend/urls.py 2 | 3 | from django.contrib import admin 4 | from django.urls import path 5 | from django.urls import path, include 6 | from django.conf import settings 7 | from django.conf.urls.static import static 8 | 9 | urlpatterns = [ 10 | path('admin/', admin.site.urls), 11 | path('api/', include("api.urls")), 12 | path('ckeditor/', include('ckeditor_uploader.urls')), 13 | path("ckeditor5/", include('django_ckeditor_5.urls')), 14 | 15 | ] 16 | 17 | 18 | urlpatterns +=static(settings.STATIC_URL, document_root=settings.STATIC_ROOT) 19 | urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT) 20 | 21 | -------------------------------------------------------------------------------- /backend/backend/wsgi.py: -------------------------------------------------------------------------------- 1 | """ 2 | WSGI config for backend 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.0/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', 'backend.settings') 15 | 16 | application = get_wsgi_application() 17 | -------------------------------------------------------------------------------- /backend/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', 'backend.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 | -------------------------------------------------------------------------------- /backend/media/default.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/desphixs/Chatapp-using-Django-RestFramework-and-React/5f074730e6a60d934a39b8f49ff67981d9015033/backend/media/default.jpg -------------------------------------------------------------------------------- /backend/media/user_images/14323626_s84-pm-2698-ploy-mockup.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/desphixs/Chatapp-using-Django-RestFramework-and-React/5f074730e6a60d934a39b8f49ff67981d9015033/backend/media/user_images/14323626_s84-pm-2698-ploy-mockup.jpg -------------------------------------------------------------------------------- /backend/media/user_images/218884489_1489407691458770_8916744002593220091_n.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/desphixs/Chatapp-using-Django-RestFramework-and-React/5f074730e6a60d934a39b8f49ff67981d9015033/backend/media/user_images/218884489_1489407691458770_8916744002593220091_n.jpg -------------------------------------------------------------------------------- /backend/requirements.txt: -------------------------------------------------------------------------------- 1 | asgiref==3.4.1 2 | autopep8==1.6.0 3 | Django==4.0.1 4 | django-cors-headers==3.11.0 5 | djangorestframework==3.13.1 6 | djangorestframework-simplejwt==5.0.0 7 | pycodestyle==2.8.0 8 | PyJWT==2.3.0 9 | pytz==2021.3 10 | sqlparse==0.4.2 11 | toml==0.10.2 12 | tzdata==2021.5 -------------------------------------------------------------------------------- /frontend/.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 | -------------------------------------------------------------------------------- /frontend/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 | -------------------------------------------------------------------------------- /frontend/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "frontend", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "axios": "^1.4.0", 10 | "dayjs": "^1.11.7", 11 | "jwt-decode": "^3.1.2", 12 | "moment": "^2.29.4", 13 | "react": "^18.2.0", 14 | "react-dom": "^18.2.0", 15 | "react-router-dom": "^5.2.0", 16 | "react-scripts": "5.0.1", 17 | "sweetalert2": "^11.7.5", 18 | "web-vitals": "^2.1.4" 19 | }, 20 | "scripts": { 21 | "start": "react-scripts start", 22 | "build": "react-scripts build", 23 | "test": "react-scripts test", 24 | "eject": "react-scripts eject" 25 | }, 26 | "eslintConfig": { 27 | "extends": [ 28 | "react-app", 29 | "react-app/jest" 30 | ] 31 | }, 32 | "browserslist": { 33 | "production": [ 34 | ">0.2%", 35 | "not dead", 36 | "not op_mini all" 37 | ], 38 | "development": [ 39 | "last 1 chrome version", 40 | "last 1 firefox version", 41 | "last 1 safari version" 42 | ] 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /frontend/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/desphixs/Chatapp-using-Django-RestFramework-and-React/5f074730e6a60d934a39b8f49ff67981d9015033/frontend/public/favicon.ico -------------------------------------------------------------------------------- /frontend/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | React App 12 | 13 | 14 | Jumbotron Template for Bootstrap 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 |
25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /frontend/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/desphixs/Chatapp-using-Django-RestFramework-and-React/5f074730e6a60d934a39b8f49ff67981d9015033/frontend/public/logo192.png -------------------------------------------------------------------------------- /frontend/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/desphixs/Chatapp-using-Django-RestFramework-and-React/5f074730e6a60d934a39b8f49ff67981d9015033/frontend/public/logo512.png -------------------------------------------------------------------------------- /frontend/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 | -------------------------------------------------------------------------------- /frontend/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /frontend/src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import {BrowserRouter as Router, Route, Switch} from "react-router-dom" 4 | import PrivateRoute from "./utils/PrivateRoute" 5 | import { AuthProvider } from './context/AuthContext' 6 | 7 | import Homepage from './views/Homepage' 8 | import Registerpage from './views/Registerpage' 9 | import Loginpage from './views/Loginpage' 10 | import Dashboard from './views/Dashboard' 11 | import Navbar from './views/Navbar' 12 | import Todo from './views/Todo' 13 | import Message from './views/Message' 14 | import MessageDetail from './views/MessageDetail' 15 | import SearchUsers from './views/SearchUsers' 16 | 17 | 18 | 19 | function App() { 20 | return ( 21 | 22 | 23 | < Navbar/> 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | ) 37 | } 38 | 39 | export default App -------------------------------------------------------------------------------- /frontend/src/context/AuthContext.js: -------------------------------------------------------------------------------- 1 | import {createContext, useState, useEffect} from "react"; 2 | import jwt_decode from "jwt-decode"; 3 | import {useHistory} from "react-router-dom"; 4 | const swal = require('sweetalert2') 5 | 6 | const AuthContext = createContext(); 7 | 8 | export default AuthContext 9 | 10 | export const AuthProvider = ({ children }) => { 11 | const [authTokens, setAuthTokens] = useState(() => 12 | localStorage.getItem("authTokens") 13 | ? JSON.parse(localStorage.getItem("authTokens")) 14 | : null 15 | ); 16 | 17 | 18 | const [user, setUser] = useState(() => 19 | localStorage.getItem("authTokens") 20 | ? jwt_decode(localStorage.getItem("authTokens")) 21 | : null 22 | ); 23 | 24 | 25 | const [loading, setLoading] = useState(true); 26 | 27 | const history = useHistory(); 28 | 29 | const loginUser = async (email, password) => { 30 | const response = await fetch("http://127.0.0.1:8000/api/token/", { 31 | method: "POST", 32 | headers:{ 33 | "Content-Type":"application/json" 34 | }, 35 | body: JSON.stringify({ 36 | email, password 37 | }) 38 | }) 39 | const data = await response.json() 40 | console.log(data); 41 | 42 | if(response.status === 200){ 43 | console.log("Logged In"); 44 | setAuthTokens(data) 45 | setUser(jwt_decode(data.access)) 46 | localStorage.setItem("authTokens", JSON.stringify(data)) 47 | history.push("/") 48 | swal.fire({ 49 | title: "Login Successful", 50 | icon: "success", 51 | toast: true, 52 | timer: 6000, 53 | position: 'top-right', 54 | timerProgressBar: true, 55 | showConfirmButton: false, 56 | showCancelButton: true, 57 | }) 58 | 59 | } else { 60 | console.log(response.status); 61 | console.log("there was a server issue"); 62 | swal.fire({ 63 | title: "Username or passowrd does not exists", 64 | icon: "error", 65 | toast: true, 66 | timer: 6000, 67 | position: 'top-right', 68 | timerProgressBar: true, 69 | showConfirmButton: false, 70 | showCancelButton: true, 71 | 72 | }) 73 | } 74 | } 75 | 76 | const registerUser = async (email, username, password, password2) => { 77 | const response = await fetch("http://127.0.0.1:8000/api/register/", { 78 | method: "POST", 79 | headers: { 80 | "Content-Type":"application/json" 81 | }, 82 | body: JSON.stringify({ 83 | email, username, password, password2 84 | }) 85 | }) 86 | if(response.status === 201){ 87 | history.push("/login") 88 | swal.fire({ 89 | title: "Registration Successful, Login Now", 90 | icon: "success", 91 | toast: true, 92 | timer: 6000, 93 | position: 'top-right', 94 | timerProgressBar: true, 95 | showConfirmButton: false, 96 | showCancelButton: true, 97 | 98 | }) 99 | } else { 100 | console.log(response.status); 101 | console.log("there was a server issue"); 102 | swal.fire({ 103 | title: "An Error Occured " + response.status, 104 | icon: "error", 105 | toast: true, 106 | timer: 6000, 107 | position: 'top-right', 108 | timerProgressBar: true, 109 | showConfirmButton: false, 110 | showCancelButton: true, 111 | 112 | }) 113 | } 114 | } 115 | 116 | const logoutUser = () => { 117 | setAuthTokens(null) 118 | setUser(null) 119 | localStorage.removeItem("authTokens") 120 | history.push("/login") 121 | swal.fire({ 122 | title: "YOu have been logged out...", 123 | icon: "success", 124 | toast: true, 125 | timer: 6000, 126 | position: 'top-right', 127 | timerProgressBar: true, 128 | showConfirmButton: false, 129 | showCancelButton: true, 130 | 131 | }) 132 | } 133 | 134 | const contextData = { 135 | user, 136 | setUser, 137 | authTokens, 138 | setAuthTokens, 139 | registerUser, 140 | loginUser, 141 | logoutUser, 142 | } 143 | 144 | useEffect(() => { 145 | if (authTokens) { 146 | setUser(jwt_decode(authTokens.access)) 147 | } 148 | setLoading(false) 149 | }, [authTokens, loading]) 150 | 151 | return ( 152 | 153 | {loading ? null : children} 154 | 155 | ) 156 | 157 | } 158 | -------------------------------------------------------------------------------- /frontend/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 | -------------------------------------------------------------------------------- /frontend/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 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render( 8 | 9 | ); 10 | -------------------------------------------------------------------------------- /frontend/src/utils/PrivateRoute.js: -------------------------------------------------------------------------------- 1 | import {Route, Redirect} from "react-router-dom" 2 | import {useContext} from "react" 3 | import AuthContext from "../context/AuthContext" 4 | 5 | 6 | const PrivateRoute = ({children, ...rest}) => { 7 | let {user} = useContext(AuthContext) 8 | return {!user ? : children} 9 | } 10 | 11 | export default PrivateRoute -------------------------------------------------------------------------------- /frontend/src/utils/useAxios.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | import jwt_decode from "jwt-decode"; 3 | import dayjs from "dayjs"; 4 | import { useContext } from "react"; 5 | import AuthContext from "../context/AuthContext"; 6 | 7 | const baseURL = "http://127.0.0.1:8000/api"; 8 | 9 | const useAxios = () => { 10 | const { authTokens, setUser, setAuthTokens } = useContext(AuthContext); 11 | 12 | const axiosInstance = axios.create({ 13 | baseURL, 14 | headers: { Authorization: `Bearer ${authTokens?.access}` } 15 | }); 16 | 17 | axiosInstance.interceptors.request.use(async req => { 18 | const user = jwt_decode(authTokens.access); 19 | const isExpired = dayjs.unix(user.exp).diff(dayjs()) < 1; 20 | 21 | if (!isExpired) return req; 22 | 23 | const response = await axios.post(`${baseURL}/token/refresh/`, { 24 | refresh: authTokens.refresh 25 | }); 26 | localStorage.setItem("authTokens", JSON.stringify(response.data)); 27 | localStorage.setItem("authTokens", JSON.stringify(response.data)); 28 | 29 | setAuthTokens(response.data); 30 | setUser(jwt_decode(response.data.access)); 31 | 32 | req.headers.Authorization = `Bearer ${response.data.access}`; 33 | return req; 34 | }); 35 | 36 | return axiosInstance; 37 | }; 38 | 39 | export default useAxios; -------------------------------------------------------------------------------- /frontend/src/views/Dashboard.js: -------------------------------------------------------------------------------- 1 | import {useState, useEffect} from 'react' 2 | import useAxios from "../utils/useAxios" 3 | import jwtDecode from 'jwt-decode' 4 | function Dashboard() { 5 | 6 | const [res, setRes] = useState("") 7 | const api = useAxios(); 8 | const token = localStorage.getItem("authTokens") 9 | 10 | if (token){ 11 | const decode = jwtDecode(token) 12 | var user_id = decode.user_id 13 | var username = decode.username 14 | var full_name = decode.full_name 15 | var image = decode.image 16 | 17 | } 18 | 19 | useEffect(() => { 20 | const fetchData = async () => { 21 | try{ 22 | const response = await api.get("/test/") 23 | setRes(response.data.response) 24 | } catch (error) { 25 | console.log(error); 26 | setRes("Something went wrong") 27 | } 28 | } 29 | fetchData() 30 | }, []) 31 | 32 | 33 | useEffect(() => { 34 | const fetchPostData = async () => { 35 | try{ 36 | const response = await api.post("/test/") 37 | setRes(response.data.response) 38 | } catch (error) { 39 | console.log(error); 40 | setRes("Something went wrong") 41 | } 42 | } 43 | fetchPostData() 44 | }, []) 45 | 46 | 47 | return ( 48 |
49 | <> 50 |
51 |
52 | 126 |
127 |
128 |

My Dashboard

129 | Hello {username}! 130 |
131 |
132 | 135 | 138 |
139 | 143 |
144 |
145 |
146 | {res} 147 |
148 |

Section title

149 |
150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 | 269 | 270 | 271 | 272 | 273 | 274 |
#HeaderHeaderHeaderHeader
1,001Loremipsumdolorsit
1,002ametconsecteturadipiscingelit
1,003IntegernecodioPraesent
1,003liberoSedcursusante
1,004dapibusdiamSednisi
1,005Nullaquissemat
1,006nibhelementumimperdietDuis
1,007sagittisipsumPraesentmauris
1,008Fuscenectellussed
1,009auguesemperportaMauris
1,010massaVestibulumlaciniaarcu
1,011egetnullaClassaptent
1,012tacitisociosquadlitora
1,013torquentperconubianostra
1,014perinceptoshimenaeosCurabitur
1,015sodalesligulainlibero
275 |
276 |
277 |
278 |
279 | {/* Bootstrap core JavaScript 280 | ================================================== */} 281 | {/* Placed at the end of the document so the pages load faster */} 282 | {/* Icons */} 283 | {/* Graphs */} 284 | 285 | 286 |
287 | ) 288 | } 289 | 290 | export default Dashboard -------------------------------------------------------------------------------- /frontend/src/views/Homepage.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | function Homepage() { 4 | return ( 5 |
6 | <> 7 |
8 | {/* Main jumbotron for a primary marketing message or call to action */} 9 |
10 |
11 |

Hello, world!

12 |

13 | This is a template for a simple marketing or informational website. It 14 | includes a large callout called a jumbotron and three supporting 15 | pieces of content. Use it as a starting point to create something more 16 | unique. 17 |

18 |

19 | 20 | Learn more » 21 | 22 |

23 |
24 |
25 |
26 | {/* Example row of columns */} 27 |
28 |
29 |

Heading

30 |

31 | Donec id elit non mi porta gravida at eget metus. Fusce dapibus, 32 | tellus ac cursus commodo, tortor mauris condimentum nibh, ut 33 | fermentum massa justo sit amet risus. Etiam porta sem malesuada 34 | magna mollis euismod. Donec sed odio dui.{" "} 35 |

36 |

37 | 38 | View details » 39 | 40 |

41 |
42 |
43 |

Heading

44 |

45 | Donec id elit non mi porta gravida at eget metus. Fusce dapibus, 46 | tellus ac cursus commodo, tortor mauris condimentum nibh, ut 47 | fermentum massa justo sit amet risus. Etiam porta sem malesuada 48 | magna mollis euismod. Donec sed odio dui.{" "} 49 |

50 |

51 | 52 | View details » 53 | 54 |

55 |
56 |
57 |

Heading

58 |

59 | Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, 60 | egestas eget quam. Vestibulum id ligula porta felis euismod semper. 61 | Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum 62 | nibh, ut fermentum massa justo sit amet risus. 63 |

64 |

65 | 66 | View details » 67 | 68 |

69 |
70 |
71 |
72 |
{" "} 73 | {/* /container */} 74 |
75 |
76 |

© Company 2017-2018

77 |
78 | 79 | 80 |
81 | ) 82 | } 83 | 84 | export default Homepage -------------------------------------------------------------------------------- /frontend/src/views/Loginpage.js: -------------------------------------------------------------------------------- 1 | import React, { useContext } from 'react' 2 | import { Link } from 'react-router-dom' 3 | import AuthContext from '../context/AuthContext' 4 | 5 | 6 | 7 | function Loginpage() { 8 | 9 | const {loginUser} = useContext(AuthContext) 10 | const handleSubmit = e => { 11 | e.preventDefault() 12 | const email = e.target.email.value 13 | const password = e.target.password.value 14 | 15 | email.length > 0 && loginUser(email, password) 16 | 17 | console.log(email) 18 | console.log(password) 19 | 20 | } 21 | 22 | return ( 23 |
24 | <> 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 | login form 38 |
39 |
40 |
41 |
42 |
43 | 47 |
48 | 52 | Welcome back 👋 53 |
54 |
55 |
59 | Sign into your account 60 |
61 |
62 | 68 | 71 |
72 |
73 | 79 | 82 |
83 |
84 | 90 |
91 | 92 | Forgot password? 93 | 94 |

95 | Don't have an account?{" "} 96 | 97 | Register Now 98 | 99 |

100 | 101 | Terms of use. 102 | 103 | 104 | Privacy policy 105 | 106 | 107 |
108 |
109 |
110 |
111 |
112 |
113 |
114 |
115 | 126 | 127 | 128 |
129 | ) 130 | } 131 | 132 | export default Loginpage -------------------------------------------------------------------------------- /frontend/src/views/Message.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './style/Message.css' 3 | import { useState, useEffect } from 'react' 4 | import useAxios from '../utils/useAxios' 5 | import jwtDecode from 'jwt-decode' 6 | import { Link, useHistory } from 'react-router-dom/' 7 | import moment from 'moment'; 8 | 9 | function Message() { 10 | 11 | const baseURL = 'http://127.0.0.1:8000/api' 12 | // Create New State 13 | const [messages, setMessages] = useState([]) 14 | let [newSearch, setnewSearch] = useState({search: "",}); 15 | 16 | // Initialize the useAxios Function to post and get data from protected routes 17 | const axios = useAxios() 18 | 19 | // Get and Decode Token 20 | const token = localStorage.getItem('authTokens'); 21 | const decoded = jwtDecode(token) 22 | // Get Userdata from decoded token 23 | const user_id = decoded.user_id 24 | const username = decoded.username 25 | const history = useHistory() 26 | 27 | useEffect(() => { 28 | try { 29 | // Send a get request to the api endpoint to get the message of the logged in user 30 | axios.get(baseURL + '/my-messages/' + user_id + '/').then((res) => { 31 | // Set the data that was gotten back from the database via the api to the setMessage state 32 | setMessages(res.data) 33 | // Console Log the data that was gotten from the db 34 | console.log(res.data); 35 | }) 36 | } catch (error) { 37 | console.log(error); 38 | } 39 | }, []) 40 | 41 | const handleSearchChange = (event) => { 42 | setnewSearch({ 43 | ...newSearch, 44 | [event.target.name]: event.target.value, 45 | }); 46 | 47 | }; 48 | 49 | const SearchUser = () => { 50 | axios.get(baseURL + '/search/' + newSearch.username + '/') 51 | .then((res) => { 52 | if (res.status === 404) { 53 | console.log(res.data.detail); 54 | alert("User does not exist"); 55 | } else { 56 | history.push('/search/'+newSearch.username+'/'); 57 | } 58 | }) 59 | .catch((error) => { 60 | alert("User Does Not Exist") 61 | }); 62 | }; 63 | return ( 64 |
65 |
66 |
67 |

Messages

68 |
69 |
70 |
71 |
72 |
73 |
74 | 82 | 83 |
84 |
85 |
86 | {messages.map((message) => 87 | 92 |
{moment.utc(message.date).local().startOf('seconds').fromNow()}
93 |
94 | {message.sender.id !== user_id && 95 | 1 96 | } 97 | {message.sender.id === user_id && 98 | 2 99 | } 100 |
101 | {message.sender.id === user_id && 102 | (message.reciever_profile.full_name !== null ? message.reciever_profile.full_name : message.reciever.username) 103 | } 104 | 105 | {message.sender.id !== user_id && 106 | (message.sender.username) 107 | } 108 |
109 | {message.message} 110 |
111 |
112 |
113 | 114 | )} 115 | 116 |
117 |
118 |
119 |
120 |
121 |
122 | Sharon Lessman 129 |
130 |
131 | Sharon Lessman 132 |
133 | Online 134 |
135 |
136 |
137 | 153 | 170 | 188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 | Chris Wood 202 |
203 | 2:33 am 204 |
205 |
206 |
207 |
You
208 | Lorem ipsum dolor sit amet, vis erat denique in, dicunt 209 | prodesset te vix. 210 |
211 |
212 |
213 |
214 | Sharon Lessman 221 |
222 | 2:34 am 223 |
224 |
225 |
226 |
Sharon Lessman
227 | Sit meis deleniti eu, pri vidit meliore docendi ut, an eum 228 | erat animal commodo. 229 |
230 |
231 |
232 |
233 | Chris Wood 240 |
241 | 2:35 am 242 |
243 |
244 |
245 |
You
246 | Cum ea graeci tractatos. 247 |
248 |
249 |
250 |
251 | Sharon Lessman 258 |
259 | 2:36 am 260 |
261 |
262 |
263 |
Sharon Lessman
264 | Sed pulvinar, massa vitae interdum pulvinar, risus lectus 265 | porttitor magna, vitae commodo lectus mauris et velit. Proin 266 | ultricies placerat imperdiet. Morbi varius quam ac venenatis 267 | tempus. 268 |
269 |
270 |
271 |
272 | Sharon Lessman 279 |
280 | 2:37 am 281 |
282 |
283 |
284 |
Sharon Lessman
285 | Cras pulvinar, sapien id vehicula aliquet, diam velit 286 | elementum orci. 287 |
288 |
289 |
290 |
291 | Chris Wood 298 |
299 | 2:38 am 300 |
301 |
302 |
303 |
You
304 | Lorem ipsum dolor sit amet, vis erat denique in, dicunt 305 | prodesset te vix. 306 |
307 |
308 |
309 |
310 | Sharon Lessman 317 |
318 | 2:39 am 319 |
320 |
321 |
322 |
Sharon Lessman
323 | Sit meis deleniti eu, pri vidit meliore docendi ut, an eum 324 | erat animal commodo. 325 |
326 |
327 |
328 |
329 | Chris Wood 336 |
337 | 2:40 am 338 |
339 |
340 |
341 |
You
342 | Cum ea graeci tractatos. 343 |
344 |
345 |
346 |
347 | Chris Wood 354 |
355 | 2:41 am 356 |
357 |
358 |
359 |
You
360 | Morbi finibus, lorem id placerat ullamcorper, nunc enim 361 | ultrices massa, id dignissim metus urna eget purus. 362 |
363 |
364 |
365 |
366 | Sharon Lessman 373 |
374 | 2:42 am 375 |
376 |
377 |
378 |
Sharon Lessman
379 | Sed pulvinar, massa vitae interdum pulvinar, risus lectus 380 | porttitor magna, vitae commodo lectus mauris et velit. Proin 381 | ultricies placerat imperdiet. Morbi varius quam ac venenatis 382 | tempus. 383 |
384 |
385 |
386 |
387 | Chris Wood 394 |
395 | 2:43 am 396 |
397 |
398 |
399 |
You
400 | Lorem ipsum dolor sit amet, vis erat denique in, dicunt 401 | prodesset te vix. 402 |
403 |
404 |
405 |
406 | Sharon Lessman 413 |
414 | 2:44 am 415 |
416 |
417 |
418 |
Sharon Lessman
419 | Sit meis deleniti eu, pri vidit meliore docendi ut, an eum 420 | erat animal commodo. 421 |
422 |
423 |
424 |
425 |
426 |
427 | 432 | 433 |
434 |
435 |
436 |
437 |
438 |
439 |
440 |
441 | ) 442 | } 443 | 444 | export default Message -------------------------------------------------------------------------------- /frontend/src/views/MessageDetail.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './style/Message.css' 3 | import { useState, useEffect } from 'react' 4 | import useAxios from '../utils/useAxios' 5 | import jwtDecode from 'jwt-decode' 6 | import { Link, useParams, useHistory } from 'react-router-dom/' 7 | import moment from 'moment'; 8 | 9 | function MessageDetail() { 10 | 11 | const baseURL = 'http://127.0.0.1:8000/api' 12 | const [messages, setMessages] = useState([]) 13 | const [message, setMessage] = useState([]) 14 | const [user, setUser] = useState([]) 15 | const [profile, setProfile] = useState([]) 16 | let [newMessage, setnewMessage] = useState({message: "",}); 17 | let [newSearch, setnewSearch] = useState({search: "",}); 18 | 19 | 20 | const axios = useAxios() 21 | const id = useParams() 22 | const token = localStorage.getItem('authTokens'); 23 | const decoded = jwtDecode(token) 24 | const user_id = decoded.user_id 25 | const username = decoded.username 26 | const history = useHistory() 27 | 28 | useEffect(() => { 29 | try { 30 | axios.get(baseURL + '/my-messages/' + user_id + '/').then((res) => { 31 | setMessages(res.data) 32 | }) 33 | } catch (error) { 34 | console.log(error); 35 | } 36 | }, []) 37 | 38 | // Get all messages for a conversation 39 | useEffect(() => { 40 | let interval = setInterval(() => { 41 | try { 42 | axios.get(baseURL + '/get-messages/' + user_id + '/' + id.id + '/').then((res) => { 43 | setMessage(res.data) 44 | console.log(res.data); 45 | }) 46 | } catch (error) { 47 | console.log(error); 48 | } 49 | }, 1000); 50 | return () => { 51 | clearInterval(interval); 52 | }; 53 | }, []); 54 | 55 | useEffect(() => { 56 | const fetchProfile = async () => { 57 | try { 58 | await axios.get(baseURL + '/profile/' + id.id + '/').then((res) => { 59 | setProfile(res.data) 60 | setUser(res.data.user) 61 | }) 62 | 63 | }catch (error) { 64 | console.log(error); 65 | }} 66 | fetchProfile() 67 | }, []) 68 | 69 | // capture changes made by the user in those fields and update the component's state accordingly. 70 | const handleChange = (event) => { 71 | setnewMessage({ 72 | ...newMessage, 73 | [event.target.name]: event.target.value, 74 | }); 75 | }; 76 | 77 | // Send Message 78 | const SendMessage = () => { 79 | const formdata = new FormData() 80 | formdata.append("user", user_id) 81 | formdata.append("sender", user_id) 82 | formdata.append("reciever", id.id) 83 | formdata.append("message", newMessage.message) 84 | formdata.append("is_read", false) 85 | 86 | try { 87 | axios.post(baseURL + '/send-messages/', formdata).then((res) => { 88 | document.getElementById("text-input").value = ""; 89 | setnewMessage(newMessage = "") 90 | }) 91 | } catch (error) { 92 | console.log("error ===", error); 93 | } 94 | 95 | } 96 | 97 | const handleSearchChange = (event) => { 98 | setnewSearch({ 99 | ...newSearch, 100 | [event.target.name]: event.target.value, 101 | }); 102 | 103 | }; 104 | 105 | console.log(newSearch.username); 106 | 107 | const SearchUser = () => { 108 | axios.get(baseURL + '/search/' + newSearch.username + '/') 109 | .then((res) => { 110 | if (res.status === 404) { 111 | console.log(res.data.detail); 112 | alert("User does not exist"); 113 | } else { 114 | history.push('/search/'+newSearch.username+'/'); 115 | } 116 | }) 117 | .catch((error) => { 118 | alert("User Does Not Exist") 119 | }); 120 | }; 121 | 122 | 123 | return ( 124 |
125 |
126 |
127 |

Messages

128 |
129 |
130 |
131 |
132 |
133 |
134 | 142 | 143 |
144 |
145 |
146 | {messages.map((message) => 147 | 152 |
{moment.utc(message.date).local().startOf('seconds').fromNow()}
153 |
154 | {message.sender.id !== user_id && 155 | 1 156 | } 157 | {message.sender.id === user_id && 158 | 2 159 | } 160 |
161 | {message.sender.id === user_id && 162 | (message.reciever_profile.full_name !== null ? message.reciever_profile.full_name : message.reciever.username) 163 | } 164 | 165 | {message.sender.id !== user_id && 166 | (message.sender.username) 167 | } 168 |
169 | {message.message} 170 |
171 |
172 |
173 | 174 | )} 175 | 176 |
177 |
178 |
179 |
180 |
181 |
182 | Sharon Lessman 189 |
190 |
191 | {profile.full_name} 192 |
193 | @{user.username} 194 |
195 |
196 |
197 | 213 | 230 | 248 |
249 |
250 |
251 |
252 |
253 | {message.map((message, index) => 254 | <> 255 | {message.sender.id !== user_id && 256 |
257 |
258 | Chris Wood 259 |
260 |
261 |
262 |
263 |
You
264 | {message.message} 265 |
266 | {moment.utc(message.date).local().startOf('seconds').fromNow()} 267 |
268 |
269 | } 270 | {message.sender.id === user_id && 271 |
272 |
273 | {message.reciever_profile.full_name} 274 |
275 |
{moment.utc(message.date).local().startOf('seconds').fromNow()}
276 |
277 |
278 |
{message.reciever_profile.full_name}
279 | {message.message} 280 |
281 |
282 | } 283 | 284 | 285 | )} 286 | 287 |
288 |
289 |
290 |
291 | 300 | 301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 | ) 310 | } 311 | 312 | export default MessageDetail -------------------------------------------------------------------------------- /frontend/src/views/Navbar.js: -------------------------------------------------------------------------------- 1 | import {useContext} from 'react' 2 | import jwt_decode from "jwt-decode" 3 | import AuthContext from '../context/AuthContext' 4 | import { Link } from 'react-router-dom' 5 | 6 | function Navbar() { 7 | 8 | const {user, logoutUser} = useContext(AuthContext) 9 | const token = localStorage.getItem("authTokens") 10 | 11 | if (token){ 12 | const decoded = jwt_decode(token) 13 | var user_id = decoded.user_id 14 | } 15 | 16 | return ( 17 |
18 | 64 |
65 | ) 66 | } 67 | 68 | export default Navbar -------------------------------------------------------------------------------- /frontend/src/views/Registerpage.js: -------------------------------------------------------------------------------- 1 | import {useState, useContext} from 'react' 2 | import { Link } from 'react-router-dom' 3 | import AuthContext from '../context/AuthContext' 4 | 5 | 6 | function Registerpage() { 7 | 8 | const [email, setEmail] = useState("") 9 | const [username, setUsername] = useState("") 10 | const [password, setPassword] = useState("") 11 | const [password2, setPassword2] = useState("") 12 | 13 | const {registerUser} = useContext(AuthContext) 14 | 15 | console.log(email); 16 | console.log(username); 17 | console.log(password); 18 | console.log(password2); 19 | 20 | 21 | const handleSubmit = async e => { 22 | e.preventDefault() 23 | registerUser(email, username, password, password2) 24 | } 25 | 26 | 27 | return ( 28 |
29 | <> 30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | login form 43 |
44 |
45 |
46 |
47 |
48 | 52 | 53 | Welcome to Desphixs👋 54 | 55 |
56 |
60 | Sign Up 61 |
62 |
63 | setEmail(e.target.value)} 69 | /> 70 |
71 |
72 | setUsername(e.target.value)} 78 | 79 | /> 80 |
81 |
82 | setPassword(e.target.value)} 88 | 89 | /> 90 |
91 |
92 | setPassword2(e.target.value)} 98 | 99 | /> 100 |
101 |
102 | 108 |
109 | 110 | Forgot password? 111 | 112 |

113 | Already have an account?{" "} 114 | 115 | Login Now 116 | 117 |

118 | 119 | Terms of use. 120 | 121 | 122 | Privacy policy 123 | 124 | 125 |
126 |
127 |
128 |
129 |
130 |
131 |
132 |
133 |
134 | {/* Copyright */} 135 |
139 | © 2019 - till date Copyright: 140 | 141 | desphixs.com 142 | 143 |
144 | {/* Copyright */} 145 |
146 | 147 | 148 |
149 | ) 150 | } 151 | 152 | export default Registerpage -------------------------------------------------------------------------------- /frontend/src/views/SearchUsers.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import './style/Message.css' 3 | import { useState, useEffect } from 'react' 4 | import useAxios from '../utils/useAxios' 5 | import jwtDecode from 'jwt-decode' 6 | import { Link, useParams, useHistory } from 'react-router-dom/' 7 | import moment from 'moment'; 8 | const swal = require('sweetalert2') 9 | 10 | function SearchUsers() { 11 | 12 | const baseURL = 'http://127.0.0.1:8000/api' 13 | const [users, setUser] = useState([]) 14 | const [profiles, setProfile] = useState([]) 15 | let [newSearch, setnewSearch] = useState({search: "",}); 16 | const [loading, setLoading] = useState(true); 17 | 18 | const username = useParams() 19 | const history = useHistory() 20 | const axios = useAxios() 21 | 22 | useEffect(() => { 23 | axios.get(baseURL + '/search/' + username.username + '/') 24 | .then((res) => { 25 | setUser(res.data) 26 | }) 27 | .catch((error) => { 28 | swal.fire({ 29 | title: "User Does Not Exist", 30 | icon: "error", 31 | toast: true, 32 | timer: 2000, 33 | position: 'middle', 34 | timerProgressBar: true, 35 | showConfirmButton: false, 36 | showCancelButton: true, 37 | }) 38 | }); 39 | }, []) 40 | console.log(users); 41 | 42 | const handleSearchChange = (event) => { 43 | setnewSearch({ 44 | ...newSearch, 45 | [event.target.name]: event.target.value, 46 | }); 47 | 48 | }; 49 | 50 | console.log(newSearch.username); 51 | 52 | 53 | const SearchUser = () => { 54 | axios.get(baseURL + '/search/' + newSearch.username + '/') 55 | .then((res) => { 56 | history.push('/search/'+newSearch.username+'/'); 57 | setUser(res.data) 58 | 59 | }) 60 | .catch((error) => { 61 | swal.fire({ 62 | title: "User Does Not Exist", 63 | icon: "error", 64 | toast: true, 65 | timer: 2000, 66 | position: 'middle', 67 | timerProgressBar: true, 68 | showConfirmButton: false, 69 | showCancelButton: true, 70 | }) 71 | }); 72 | }; 73 | 74 | 75 | console.log(users); 76 | console.log(profiles); 77 | return ( 78 |
79 |
80 |
81 |
82 |

Messages

83 |
84 |
85 |
86 |
87 |
88 |
89 | 97 | 98 |
99 |
100 |
101 | 102 | {users.map((user, index) => 103 | 107 | 108 |
109 |
110 | 1 111 | 112 |
113 | {user.full_name} 114 | 115 |
116 | Send Message 117 |
118 |
119 |
120 | 121 | )} 122 | 123 |
124 |
125 | 126 |
127 |
128 |
129 |
130 |
131 |
132 | ) 133 | } 134 | 135 | export default SearchUsers -------------------------------------------------------------------------------- /frontend/src/views/Todo.js: -------------------------------------------------------------------------------- 1 | import {useState, useEffect} from 'react' 2 | import useAxios from '../utils/useAxios' 3 | import jwtDecode from 'jwt-decode' 4 | import Swal from 'sweetalert2' 5 | 6 | 7 | function Todo() { 8 | const baseUrl = "http://127.0.0.1:8000/api" 9 | const api = useAxios() 10 | 11 | const token = localStorage.getItem("authTokens") // 233704237huhweioyop;yrwriweyrwe 12 | const decoded = jwtDecode(token) 13 | const user_id = decoded.user_id 14 | 15 | const [todo, setTodo] = useState([]) 16 | useEffect(() => { 17 | fetchTodos() 18 | }, []) 19 | 20 | const fetchTodos = async () => { 21 | await api.get(baseUrl + '/todo/' + user_id + '/').then((res) => { 22 | console.log(res.data); 23 | setTodo(res.data) 24 | }) 25 | } 26 | 27 | 28 | const [createTodo, setCreateTodo] = useState({title: "", completed: ""}) 29 | const handleNewTodoTitle = (event) => { 30 | setCreateTodo({ 31 | ...createTodo, 32 | [event.target.name]: event.target.value 33 | }) 34 | } 35 | 36 | console.log(createTodo.title); 37 | 38 | const formSubmit = () => { 39 | const formdata = new FormData() 40 | 41 | formdata.append("user", user_id) 42 | formdata.append("title", createTodo.title) 43 | formdata.append("completed", false) 44 | 45 | try{ 46 | api.post(baseUrl + '/todo/' + user_id + '/', formdata).then((res) => { 47 | console.log(res.data); 48 | Swal.fire({ 49 | title: "Todo Added", 50 | icon:"success", 51 | toast: true, 52 | timer: 2000, 53 | position: "top-right", 54 | timerProgressBar: true, 55 | }) 56 | fetchTodos() 57 | createTodo.title = "" 58 | }) 59 | } catch (error){ 60 | console.log(error); 61 | } 62 | } 63 | 64 | const deleteTodo = async (todo_id) => { 65 | await api.delete(baseUrl + '/todo-detail/' + user_id + '/' + todo_id + '/') 66 | Swal.fire({ 67 | title: "Todo Deleted", 68 | icon:"success", 69 | toast: true, 70 | timer: 2000, 71 | position: "top-right", 72 | timerProgressBar: true, 73 | }) 74 | fetchTodos() 75 | } 76 | 77 | const markTodoAsComplete = async (todo_id) => { 78 | await api.patch(baseUrl + '/todo-mark-as-completed/' + user_id + '/' + todo_id + '/') 79 | Swal.fire({ 80 | title: "Todo Completed", 81 | icon:"success", 82 | toast: true, 83 | timer: 2000, 84 | position: "top-right", 85 | timerProgressBar: true, 86 | }) 87 | fetchTodos() 88 | } 89 | 90 | return ( 91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |

Desphixs Todo App

99 |
100 |
101 |
102 |
103 | 104 |
105 | 106 |
107 |
108 | {todo.map((todo) => 109 | 110 |
111 |
112 | {todo.completed.toString() === "true" && 113 |

{todo.title}

114 | } 115 | {todo.completed.toString() === "false" && 116 |

{todo.title}

117 | } 118 |
119 | 120 | 121 |
122 |
123 |
124 | )} 125 | 126 |
127 |
128 |
129 |
130 |
131 |
132 | ) 133 | } 134 | 135 | export default Todo -------------------------------------------------------------------------------- /frontend/src/views/style/Message.css: -------------------------------------------------------------------------------- 1 | body{margin-top:20px;} 2 | 3 | .chat-online { 4 | color: #34ce57 5 | } 6 | 7 | .chat-offline { 8 | color: #e4606d 9 | } 10 | 11 | .chat-messages { 12 | display: flex; 13 | flex-direction: column; 14 | max-height: 800px; 15 | overflow-y: scroll 16 | } 17 | 18 | .chat-message-left, 19 | .chat-message-right { 20 | display: flex; 21 | flex-shrink: 0 22 | } 23 | 24 | .chat-message-left { 25 | margin-right: auto 26 | } 27 | 28 | .chat-message-right { 29 | flex-direction: row-reverse; 30 | margin-left: auto 31 | } 32 | .py-3 { 33 | padding-top: 1rem!important; 34 | padding-bottom: 1rem!important; 35 | } 36 | .px-4 { 37 | padding-right: 1.5rem!important; 38 | padding-left: 1.5rem!important; 39 | } 40 | .flex-grow-0 { 41 | flex-grow: 0!important; 42 | } 43 | .border-top { 44 | border-top: 1px solid #dee2e6!important; 45 | } -------------------------------------------------------------------------------- /templates/Authentication/dashboard.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Dashboard Template for Bootstrap 12 | 13 | 14 | 15 | 16 | 17 | 18 | 45 | 46 |
47 |
48 | 123 | 124 |
125 |
126 |

My Dashboard

127 | Hello Destiny! 128 |
129 |
130 | 131 | 132 |
133 | 137 |
138 |
139 | 140 | 141 | 142 |

Section title

143 |
144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 | 157 | 158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 | 169 | 170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 | 192 | 193 | 194 | 195 | 196 | 197 | 198 | 199 | 200 | 201 | 202 | 203 | 204 | 205 | 206 | 207 | 208 | 209 | 210 | 211 | 212 | 213 | 214 | 215 | 216 | 217 | 218 | 219 | 220 | 221 | 222 | 223 | 224 | 225 | 226 | 227 | 228 | 229 | 230 | 231 | 232 | 233 | 234 | 235 | 236 | 237 | 238 | 239 | 240 | 241 | 242 | 243 | 244 | 245 | 246 | 247 | 248 | 249 | 250 | 251 | 252 | 253 | 254 | 255 | 256 | 257 | 258 | 259 | 260 | 261 | 262 | 263 | 264 | 265 | 266 | 267 | 268 |
#HeaderHeaderHeaderHeader
1,001Loremipsumdolorsit
1,002ametconsecteturadipiscingelit
1,003IntegernecodioPraesent
1,003liberoSedcursusante
1,004dapibusdiamSednisi
1,005Nullaquissemat
1,006nibhelementumimperdietDuis
1,007sagittisipsumPraesentmauris
1,008Fuscenectellussed
1,009auguesemperportaMauris
1,010massaVestibulumlaciniaarcu
1,011egetnullaClassaptent
1,012tacitisociosquadlitora
1,013torquentperconubianostra
1,014perinceptoshimenaeosCurabitur
1,015sodalesligulainlibero
269 |
270 |
271 |
272 |
273 | 274 | 276 | 277 | 278 | 279 | 280 | 281 | 282 | 283 | 286 | 287 | 288 | 289 | 318 | 319 | 320 | 321 | 322 | 323 | 324 | 325 | 326 | -------------------------------------------------------------------------------- /templates/Authentication/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | Jumbotron Template for Bootstrap 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 52 | 53 |
54 | 55 | 56 |
57 |
58 |

Hello, world!

59 |

This is a template for a simple marketing or informational website. It includes a large callout called a jumbotron and three supporting pieces of content. Use it as a starting point to create something more unique.

60 |

Learn more »

61 |
62 |
63 | 64 |
65 | 66 |
67 |
68 |

Heading

69 |

Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

70 |

View details »

71 |
72 |
73 |

Heading

74 |

Donec id elit non mi porta gravida at eget metus. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus. Etiam porta sem malesuada magna mollis euismod. Donec sed odio dui.

75 |

View details »

76 |
77 |
78 |

Heading

79 |

Donec sed odio dui. Cras justo odio, dapibus ac facilisis in, egestas eget quam. Vestibulum id ligula porta felis euismod semper. Fusce dapibus, tellus ac cursus commodo, tortor mauris condimentum nibh, ut fermentum massa justo sit amet risus.

80 |

View details »

81 |
82 |
83 | 84 |
85 | 86 |
87 | 88 |
89 | 90 |
91 |

© Company 2017-2018

92 |
93 | 94 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /templates/Authentication/login.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | login form 48 |
49 |
50 |
51 | 52 |
53 | 54 |
55 | 56 |
57 | 58 | Welcome back 👋 59 |
60 |
61 | 62 |
Sign into your account
63 | 64 |
65 | 66 | 67 |
68 | 69 |
70 | 71 | 72 |
73 | 74 |
75 | 76 |
77 | 78 | Forgot password? 79 |

Don't have an account? Register here

81 | Terms of use. 82 | Privacy policy 83 |
84 | 85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 | 94 |
95 | 96 |
97 | © 2019 - till date Copyright: 98 | desphixs.com 99 |
100 | 101 |
102 | 103 | 104 | 105 | 106 | -------------------------------------------------------------------------------- /templates/Authentication/register.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 | 11 | 12 | 39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 | login form 48 |
49 |
50 |
51 | 52 |
53 | 54 |
55 | 56 | Welcome to Desphixs👋 57 |
58 | 59 |
Sign Up
60 | 61 |
62 | 63 |
64 | 65 |
66 | 67 |
68 | 69 |
70 | 71 |
72 | 73 |
74 | 75 |
76 | 77 |
78 | 79 |
80 | 81 | Forgot password? 82 |

Don't have an account? Register here

84 | Terms of use. 85 | Privacy policy 86 |
87 | 88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 | 97 |
98 | 99 |
100 | © 2019 - till date Copyright: 101 | desphixs.com 102 |
103 | 104 |
105 | 106 | 107 | 108 | 109 | -------------------------------------------------------------------------------- /templates/Chat/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |

Messages

4 |
5 |
6 | 182 |
183 |
184 |
185 |
186 | Sharon Lessman 193 |
194 |
195 | Sharon Lessman 196 |
197 | Online 198 |
199 |
200 |
201 | 217 | 234 | 252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 | Chris Wood 266 |
267 | 2:33 am 268 |
269 |
270 |
271 |
You
272 | Lorem ipsum dolor sit amet, vis erat denique in, dicunt 273 | prodesset te vix. 274 |
275 |
276 |
277 |
278 | Sharon Lessman 285 |
286 | 2:34 am 287 |
288 |
289 |
290 |
Sharon Lessman
291 | Sit meis deleniti eu, pri vidit meliore docendi ut, an eum 292 | erat animal commodo. 293 |
294 |
295 |
296 |
297 | Chris Wood 304 |
305 | 2:35 am 306 |
307 |
308 |
309 |
You
310 | Cum ea graeci tractatos. 311 |
312 |
313 |
314 |
315 | Sharon Lessman 322 |
323 | 2:36 am 324 |
325 |
326 |
327 |
Sharon Lessman
328 | Sed pulvinar, massa vitae interdum pulvinar, risus lectus 329 | porttitor magna, vitae commodo lectus mauris et velit. Proin 330 | ultricies placerat imperdiet. Morbi varius quam ac venenatis 331 | tempus. 332 |
333 |
334 |
335 |
336 | Sharon Lessman 343 |
344 | 2:37 am 345 |
346 |
347 |
348 |
Sharon Lessman
349 | Cras pulvinar, sapien id vehicula aliquet, diam velit 350 | elementum orci. 351 |
352 |
353 |
354 |
355 | Chris Wood 362 |
363 | 2:38 am 364 |
365 |
366 |
367 |
You
368 | Lorem ipsum dolor sit amet, vis erat denique in, dicunt 369 | prodesset te vix. 370 |
371 |
372 |
373 |
374 | Sharon Lessman 381 |
382 | 2:39 am 383 |
384 |
385 |
386 |
Sharon Lessman
387 | Sit meis deleniti eu, pri vidit meliore docendi ut, an eum 388 | erat animal commodo. 389 |
390 |
391 |
392 |
393 | Chris Wood 400 |
401 | 2:40 am 402 |
403 |
404 |
405 |
You
406 | Cum ea graeci tractatos. 407 |
408 |
409 |
410 |
411 | Chris Wood 418 |
419 | 2:41 am 420 |
421 |
422 |
423 |
You
424 | Morbi finibus, lorem id placerat ullamcorper, nunc enim 425 | ultrices massa, id dignissim metus urna eget purus. 426 |
427 |
428 |
429 |
430 | Sharon Lessman 437 |
438 | 2:42 am 439 |
440 |
441 |
442 |
Sharon Lessman
443 | Sed pulvinar, massa vitae interdum pulvinar, risus lectus 444 | porttitor magna, vitae commodo lectus mauris et velit. Proin 445 | ultricies placerat imperdiet. Morbi varius quam ac venenatis 446 | tempus. 447 |
448 |
449 |
450 |
451 | Chris Wood 458 |
459 | 2:43 am 460 |
461 |
462 |
463 |
You
464 | Lorem ipsum dolor sit amet, vis erat denique in, dicunt 465 | prodesset te vix. 466 |
467 |
468 |
469 |
470 | Sharon Lessman 477 |
478 | 2:44 am 479 |
480 |
481 |
482 |
Sharon Lessman
483 | Sit meis deleniti eu, pri vidit meliore docendi ut, an eum 484 | erat animal commodo. 485 |
486 |
487 |
488 |
489 |
490 |
491 | 496 | 497 |
498 |
499 |
500 |
501 |
502 |
503 |
-------------------------------------------------------------------------------- /templates/Chat/style.css: -------------------------------------------------------------------------------- 1 | body{margin-top:20px;} 2 | 3 | .chat-online { 4 | color: #34ce57 5 | } 6 | 7 | .chat-offline { 8 | color: #e4606d 9 | } 10 | 11 | .chat-messages { 12 | display: flex; 13 | flex-direction: column; 14 | max-height: 800px; 15 | overflow-y: scroll 16 | } 17 | 18 | .chat-message-left, 19 | .chat-message-right { 20 | display: flex; 21 | flex-shrink: 0 22 | } 23 | 24 | .chat-message-left { 25 | margin-right: auto 26 | } 27 | 28 | .chat-message-right { 29 | flex-direction: row-reverse; 30 | margin-left: auto 31 | } 32 | .py-3 { 33 | padding-top: 1rem!important; 34 | padding-bottom: 1rem!important; 35 | } 36 | .px-4 { 37 | padding-right: 1.5rem!important; 38 | padding-left: 1.5rem!important; 39 | } 40 | .flex-grow-0 { 41 | flex-grow: 0!important; 42 | } 43 | .border-top { 44 | border-top: 1px solid #dee2e6!important; 45 | } -------------------------------------------------------------------------------- /templates/To-do/index.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |

Desphixs Todo App

9 |
10 |
11 |
12 |
13 | 14 |
15 | 16 |
17 |
18 |
19 |
20 | 21 |

Buy Milk

22 |
23 | 24 | 25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
--------------------------------------------------------------------------------