mirror of
https://github.com/TurTaskProject/TurTaskWeb.git
synced 2025-12-19 05:54:07 +01:00
Merge pull request #22 from TurTaskProject/feature/user-profile-settings
Add profile update page and modify login page
This commit is contained in:
commit
eb22d30a24
@ -10,6 +10,7 @@ For the full list of settings and their values, see
|
|||||||
https://docs.djangoproject.com/en/4.2/ref/settings/
|
https://docs.djangoproject.com/en/4.2/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
from datetime import timedelta
|
||||||
import os
|
import os
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from decouple import config, Csv
|
from decouple import config, Csv
|
||||||
@ -80,6 +81,11 @@ REST_FRAMEWORK = {
|
|||||||
|
|
||||||
REST_USE_JWT = True
|
REST_USE_JWT = True
|
||||||
|
|
||||||
|
SIMPLE_JWT = {
|
||||||
|
'ACCESS_TOKEN_LIFETIME': timedelta(days=3),
|
||||||
|
'REFRESH_TOKEN_LIFETIME': timedelta(days=30),
|
||||||
|
}
|
||||||
|
|
||||||
GOOGLE_CLIENT_ID = config('GOOGLE_CLIENT_ID', default='fake-client-id')
|
GOOGLE_CLIENT_ID = config('GOOGLE_CLIENT_ID', default='fake-client-id')
|
||||||
GOOGLE_CLIENT_SECRET = config('GOOGLE_CLIENT_SECRET', default='fake-client-secret')
|
GOOGLE_CLIENT_SECRET = config('GOOGLE_CLIENT_SECRET', default='fake-client-secret')
|
||||||
|
|
||||||
@ -226,4 +232,27 @@ LOGOUT_REDIRECT_URL = '/'
|
|||||||
|
|
||||||
AUTH_USER_MODEL = "users.CustomUser"
|
AUTH_USER_MODEL = "users.CustomUser"
|
||||||
|
|
||||||
ACCOUNT_EMAIL_REQUIRED = True
|
ACCOUNT_EMAIL_REQUIRED = True
|
||||||
|
|
||||||
|
# Storages
|
||||||
|
|
||||||
|
AWS_ACCESS_KEY_ID = config('AMAZON_S3_ACCESS_KEY', default='fake-access-key')
|
||||||
|
AWS_SECRET_ACCESS_KEY = config('AMAZON_S3_SECRET_ACCESS_KEY', default='fake-secret-access-key')
|
||||||
|
AWS_STORAGE_BUCKET_NAME = config('BUCKET_NAME', default='fake-bucket-name')
|
||||||
|
AWS_DEFAULT_ACL = 'public-read'
|
||||||
|
AWS_S3_CUSTOM_DOMAIN = f'{AWS_STORAGE_BUCKET_NAME}.s3.amazonaws.com'
|
||||||
|
AWS_S3_OBJECT_PARAMETERS = {'CacheControl': 'max-age=86400'}
|
||||||
|
|
||||||
|
MEDIA_URL = '/mediafiles/'
|
||||||
|
MEDIA_ROOT = os.path.join(BASE_DIR, 'mediafiles')
|
||||||
|
|
||||||
|
STORAGES = {
|
||||||
|
"default": {
|
||||||
|
"BACKEND": "storages.backends.s3.S3Storage",
|
||||||
|
"OPTIONS": {
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"staticfiles": {
|
||||||
|
"BACKEND": "django.contrib.staticfiles.storage.StaticFilesStorage",
|
||||||
|
},
|
||||||
|
}
|
||||||
18
backend/users/migrations/0003_customuser_profile_pic.py
Normal file
18
backend/users/migrations/0003_customuser_profile_pic.py
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
# Generated by Django 4.2.6 on 2023-11-04 19:15
|
||||||
|
|
||||||
|
from django.db import migrations, models
|
||||||
|
|
||||||
|
|
||||||
|
class Migration(migrations.Migration):
|
||||||
|
|
||||||
|
dependencies = [
|
||||||
|
('users', '0002_customuser_refresh_token'),
|
||||||
|
]
|
||||||
|
|
||||||
|
operations = [
|
||||||
|
migrations.AddField(
|
||||||
|
model_name='customuser',
|
||||||
|
name='profile_pic',
|
||||||
|
field=models.ImageField(blank=True, default='profile_pics/default.png', null=True, upload_to='profile_pics'),
|
||||||
|
),
|
||||||
|
]
|
||||||
@ -13,6 +13,7 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
|
|||||||
first_name = models.CharField(max_length=150, blank=True)
|
first_name = models.CharField(max_length=150, blank=True)
|
||||||
start_date = models.DateTimeField(default=timezone.now)
|
start_date = models.DateTimeField(default=timezone.now)
|
||||||
about = models.TextField(_('about'), max_length=500, blank=True)
|
about = models.TextField(_('about'), max_length=500, blank=True)
|
||||||
|
profile_pic = models.ImageField(upload_to='profile_pics', null=True, blank=True, default='profile_pics/default.png')
|
||||||
is_staff = models.BooleanField(default=False)
|
is_staff = models.BooleanField(default=False)
|
||||||
is_active = models.BooleanField(default=True)
|
is_active = models.BooleanField(default=True)
|
||||||
|
|
||||||
|
|||||||
@ -7,12 +7,12 @@ class CustomUserSerializer(serializers.ModelSerializer):
|
|||||||
Serializer for CustomUser model.
|
Serializer for CustomUser model.
|
||||||
"""
|
"""
|
||||||
email = serializers.EmailField(required=True)
|
email = serializers.EmailField(required=True)
|
||||||
username = serializers.CharField(required=True)
|
username = serializers.CharField()
|
||||||
password = serializers.CharField(min_length=8, write_only=True)
|
password = serializers.CharField(min_length=8, write_only=True)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = CustomUser
|
model = CustomUser
|
||||||
fields = ('email', 'password')
|
fields = ('email', 'username', 'password')
|
||||||
extra_kwargs = {'password': {'write_only': True}}
|
extra_kwargs = {'password': {'write_only': True}}
|
||||||
|
|
||||||
def create(self, validated_data):
|
def create(self, validated_data):
|
||||||
@ -25,3 +25,25 @@ class CustomUserSerializer(serializers.ModelSerializer):
|
|||||||
instance.set_password(password)
|
instance.set_password(password)
|
||||||
instance.save()
|
instance.save()
|
||||||
return instance
|
return instance
|
||||||
|
|
||||||
|
|
||||||
|
class UpdateProfileSerializer(serializers.ModelSerializer):
|
||||||
|
"""
|
||||||
|
Serializer for updating user profile.
|
||||||
|
"""
|
||||||
|
profile_pic = serializers.ImageField(required=False)
|
||||||
|
first_name = serializers.CharField(max_length=255, required=False)
|
||||||
|
about = serializers.CharField(required=False)
|
||||||
|
|
||||||
|
class Meta:
|
||||||
|
model = CustomUser
|
||||||
|
fields = ('profile_pic', 'first_name', 'about')
|
||||||
|
|
||||||
|
def update(self, instance, validated_data):
|
||||||
|
"""
|
||||||
|
Update an existing user's profile.
|
||||||
|
"""
|
||||||
|
for attr, value in validated_data.items():
|
||||||
|
setattr(instance, attr, value)
|
||||||
|
instance.save()
|
||||||
|
return instance
|
||||||
@ -1,6 +1,7 @@
|
|||||||
from django.urls import path
|
from django.urls import path
|
||||||
from users.views import CustomUserCreate
|
from users.views import CustomUserCreate, CustomUserProfileUpdate
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
path('user/create/', CustomUserCreate.as_view(), name="create_user"),
|
path('user/create/', CustomUserCreate.as_view(), name="create_user"),
|
||||||
|
path('user/update/', CustomUserProfileUpdate.as_view(), name='update_user')
|
||||||
]
|
]
|
||||||
@ -1,12 +1,14 @@
|
|||||||
"""This module defines API views for user creation"""
|
"""This module defines API views for user creation"""
|
||||||
|
|
||||||
from rest_framework import status
|
from rest_framework import status
|
||||||
from rest_framework.permissions import AllowAny
|
from rest_framework.permissions import AllowAny, IsAuthenticated
|
||||||
|
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework.views import APIView
|
from rest_framework.views import APIView
|
||||||
|
from rest_framework.parsers import MultiPartParser
|
||||||
|
|
||||||
from .serializers import CustomUserSerializer
|
from users.serializers import CustomUserSerializer, UpdateProfileSerializer
|
||||||
|
from users.models import CustomUser
|
||||||
|
|
||||||
class CustomUserCreate(APIView):
|
class CustomUserCreate(APIView):
|
||||||
"""
|
"""
|
||||||
@ -25,3 +27,34 @@ class CustomUserCreate(APIView):
|
|||||||
if user:
|
if user:
|
||||||
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
return Response(serializer.data, status=status.HTTP_201_CREATED)
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
|
|
||||||
|
class CustomUserProfileUpdate(APIView):
|
||||||
|
"""
|
||||||
|
Custom User Profile Update View.
|
||||||
|
"""
|
||||||
|
parser_classes = (MultiPartParser,)
|
||||||
|
permission_classes = (IsAuthenticated,)
|
||||||
|
|
||||||
|
def get(self, request):
|
||||||
|
user = request.user
|
||||||
|
image_url = user.profile_pic.url
|
||||||
|
username = user.username
|
||||||
|
|
||||||
|
data = {
|
||||||
|
'image_url': image_url,
|
||||||
|
'username': username
|
||||||
|
}
|
||||||
|
|
||||||
|
return Response(data)
|
||||||
|
|
||||||
|
def post(self, request):
|
||||||
|
if not CustomUser.objects.filter(email=request.user.email).exists():
|
||||||
|
return Response ({
|
||||||
|
'error': 'User does not exist'
|
||||||
|
}, status=status.HTTP_404_NOT_FOUND)
|
||||||
|
serializer = UpdateProfileSerializer(request.user, data=request.data)
|
||||||
|
if serializer.is_valid():
|
||||||
|
serializer.save()
|
||||||
|
return Response(serializer.data)
|
||||||
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
@ -12,8 +12,6 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@emotion/react": "^11.11.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/styled": "^11.11.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@material-ui/core": "^4.12.4",
|
|
||||||
"@material-ui/icons": "^4.11.3",
|
|
||||||
"@mui/icons-material": "^5.14.15",
|
"@mui/icons-material": "^5.14.15",
|
||||||
"@mui/material": "^5.14.15",
|
"@mui/material": "^5.14.15",
|
||||||
"@mui/system": "^5.14.15",
|
"@mui/system": "^5.14.15",
|
||||||
@ -27,15 +25,16 @@
|
|||||||
"react": "^18.2.0",
|
"react": "^18.2.0",
|
||||||
"react-bootstrap": "^2.9.1",
|
"react-bootstrap": "^2.9.1",
|
||||||
"react-dom": "^18.2.0",
|
"react-dom": "^18.2.0",
|
||||||
"react-google-login": "^5.2.2",
|
|
||||||
"react-icons": "^4.11.0",
|
"react-icons": "^4.11.0",
|
||||||
"react-router-dom": "^6.17.0"
|
"react-router-dom": "^6.17.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
|
"@tailwindcss/typography": "^0.5.10",
|
||||||
"@types/react": "^18.2.15",
|
"@types/react": "^18.2.15",
|
||||||
"@types/react-dom": "^18.2.7",
|
"@types/react-dom": "^18.2.7",
|
||||||
"@vitejs/plugin-react": "^4.0.3",
|
"@vitejs/plugin-react": "^4.0.3",
|
||||||
"autoprefixer": "^10.4.16",
|
"autoprefixer": "^10.4.16",
|
||||||
|
"daisyui": "^3.9.4",
|
||||||
"eslint": "^8.45.0",
|
"eslint": "^8.45.0",
|
||||||
"eslint-plugin-react": "^7.32.2",
|
"eslint-plugin-react": "^7.32.2",
|
||||||
"eslint-plugin-react-hooks": "^4.6.0",
|
"eslint-plugin-react-hooks": "^4.6.0",
|
||||||
|
|||||||
@ -11,12 +11,6 @@ dependencies:
|
|||||||
'@emotion/styled':
|
'@emotion/styled':
|
||||||
specifier: ^11.11.0
|
specifier: ^11.11.0
|
||||||
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.33)(react@18.2.0)
|
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.33)(react@18.2.0)
|
||||||
'@material-ui/core':
|
|
||||||
specifier: ^4.12.4
|
|
||||||
version: 4.12.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@material-ui/icons':
|
|
||||||
specifier: ^4.11.3
|
|
||||||
version: 4.11.3(@material-ui/core@4.12.4)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@mui/icons-material':
|
'@mui/icons-material':
|
||||||
specifier: ^5.14.15
|
specifier: ^5.14.15
|
||||||
version: 5.14.15(@mui/material@5.14.15)(@types/react@18.2.33)(react@18.2.0)
|
version: 5.14.15(@mui/material@5.14.15)(@types/react@18.2.33)(react@18.2.0)
|
||||||
@ -56,9 +50,6 @@ dependencies:
|
|||||||
react-dom:
|
react-dom:
|
||||||
specifier: ^18.2.0
|
specifier: ^18.2.0
|
||||||
version: 18.2.0(react@18.2.0)
|
version: 18.2.0(react@18.2.0)
|
||||||
react-google-login:
|
|
||||||
specifier: ^5.2.2
|
|
||||||
version: 5.2.2(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
react-icons:
|
react-icons:
|
||||||
specifier: ^4.11.0
|
specifier: ^4.11.0
|
||||||
version: 4.11.0(react@18.2.0)
|
version: 4.11.0(react@18.2.0)
|
||||||
@ -67,6 +58,9 @@ dependencies:
|
|||||||
version: 6.17.0(react-dom@18.2.0)(react@18.2.0)
|
version: 6.17.0(react-dom@18.2.0)(react@18.2.0)
|
||||||
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
|
'@tailwindcss/typography':
|
||||||
|
specifier: ^0.5.10
|
||||||
|
version: 0.5.10(tailwindcss@3.3.5)
|
||||||
'@types/react':
|
'@types/react':
|
||||||
specifier: ^18.2.15
|
specifier: ^18.2.15
|
||||||
version: 18.2.33
|
version: 18.2.33
|
||||||
@ -79,6 +73,9 @@ devDependencies:
|
|||||||
autoprefixer:
|
autoprefixer:
|
||||||
specifier: ^10.4.16
|
specifier: ^10.4.16
|
||||||
version: 10.4.16(postcss@8.4.31)
|
version: 10.4.16(postcss@8.4.31)
|
||||||
|
daisyui:
|
||||||
|
specifier: ^3.9.4
|
||||||
|
version: 3.9.4
|
||||||
eslint:
|
eslint:
|
||||||
specifier: ^8.45.0
|
specifier: ^8.45.0
|
||||||
version: 8.52.0
|
version: 8.52.0
|
||||||
@ -364,10 +361,6 @@ packages:
|
|||||||
stylis: 4.2.0
|
stylis: 4.2.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/@emotion/hash@0.8.0:
|
|
||||||
resolution: {integrity: sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@emotion/hash@0.9.1:
|
/@emotion/hash@0.9.1:
|
||||||
resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==}
|
resolution: {integrity: sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==}
|
||||||
dev: false
|
dev: false
|
||||||
@ -785,132 +778,6 @@ packages:
|
|||||||
'@jridgewell/sourcemap-codec': 1.4.15
|
'@jridgewell/sourcemap-codec': 1.4.15
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/@material-ui/core@4.12.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ==}
|
|
||||||
engines: {node: '>=8.0.0'}
|
|
||||||
deprecated: Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.
|
|
||||||
peerDependencies:
|
|
||||||
'@types/react': ^16.8.6 || ^17.0.0
|
|
||||||
react: ^16.8.0 || ^17.0.0
|
|
||||||
react-dom: ^16.8.0 || ^17.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
'@material-ui/styles': 4.11.5(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@material-ui/system': 4.12.2(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@material-ui/types': 5.1.0(@types/react@18.2.33)
|
|
||||||
'@material-ui/utils': 4.11.3(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@types/react': 18.2.33
|
|
||||||
'@types/react-transition-group': 4.4.8
|
|
||||||
clsx: 1.2.1
|
|
||||||
hoist-non-react-statics: 3.3.2
|
|
||||||
popper.js: 1.16.1-lts
|
|
||||||
prop-types: 15.8.1
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
react-is: 16.13.1
|
|
||||||
react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@material-ui/icons@4.11.3(@material-ui/core@4.12.4)(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-IKHlyx6LDh8n19vzwH5RtHIOHl9Tu90aAAxcbWME6kp4dmvODM3UvOHJeMIDzUbd4muuJKHmlNoBN+mDY4XkBA==}
|
|
||||||
engines: {node: '>=8.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
'@material-ui/core': ^4.0.0
|
|
||||||
'@types/react': ^16.8.6 || ^17.0.0
|
|
||||||
react: ^16.8.0 || ^17.0.0
|
|
||||||
react-dom: ^16.8.0 || ^17.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
'@material-ui/core': 4.12.4(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@types/react': 18.2.33
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@material-ui/styles@4.11.5(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==}
|
|
||||||
engines: {node: '>=8.0.0'}
|
|
||||||
deprecated: Material UI v4 doesn't receive active development since September 2021. See the guide https://mui.com/material-ui/migration/migration-v4/ to upgrade to v5.
|
|
||||||
peerDependencies:
|
|
||||||
'@types/react': ^16.8.6 || ^17.0.0
|
|
||||||
react: ^16.8.0 || ^17.0.0
|
|
||||||
react-dom: ^16.8.0 || ^17.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
'@emotion/hash': 0.8.0
|
|
||||||
'@material-ui/types': 5.1.0(@types/react@18.2.33)
|
|
||||||
'@material-ui/utils': 4.11.3(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@types/react': 18.2.33
|
|
||||||
clsx: 1.2.1
|
|
||||||
csstype: 2.6.21
|
|
||||||
hoist-non-react-statics: 3.3.2
|
|
||||||
jss: 10.10.0
|
|
||||||
jss-plugin-camel-case: 10.10.0
|
|
||||||
jss-plugin-default-unit: 10.10.0
|
|
||||||
jss-plugin-global: 10.10.0
|
|
||||||
jss-plugin-nested: 10.10.0
|
|
||||||
jss-plugin-props-sort: 10.10.0
|
|
||||||
jss-plugin-rule-value-function: 10.10.0
|
|
||||||
jss-plugin-vendor-prefixer: 10.10.0
|
|
||||||
prop-types: 15.8.1
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@material-ui/system@4.12.2(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==}
|
|
||||||
engines: {node: '>=8.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
'@types/react': ^16.8.6 || ^17.0.0
|
|
||||||
react: ^16.8.0 || ^17.0.0
|
|
||||||
react-dom: ^16.8.0 || ^17.0.0
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
'@material-ui/utils': 4.11.3(react-dom@18.2.0)(react@18.2.0)
|
|
||||||
'@types/react': 18.2.33
|
|
||||||
csstype: 2.6.21
|
|
||||||
prop-types: 15.8.1
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@material-ui/types@5.1.0(@types/react@18.2.33):
|
|
||||||
resolution: {integrity: sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==}
|
|
||||||
peerDependencies:
|
|
||||||
'@types/react': '*'
|
|
||||||
peerDependenciesMeta:
|
|
||||||
'@types/react':
|
|
||||||
optional: true
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 18.2.33
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@material-ui/utils@4.11.3(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==}
|
|
||||||
engines: {node: '>=8.0.0'}
|
|
||||||
peerDependencies:
|
|
||||||
react: ^16.8.0 || ^17.0.0
|
|
||||||
react-dom: ^16.8.0 || ^17.0.0
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
prop-types: 15.8.1
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
react-is: 16.13.1
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/@mui/base@5.0.0-beta.21(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
/@mui/base@5.0.0-beta.21(@types/react@18.2.33)(react-dom@18.2.0)(react@18.2.0):
|
||||||
resolution: {integrity: sha512-eTKWx3WV/nwmRUK4z4K1MzlMyWCsi3WJ3RtV4DiXZeRh4qd4JCyp1Zzzi8Wv9xM4dEBmqQntFoei716PzwmFfA==}
|
resolution: {integrity: sha512-eTKWx3WV/nwmRUK4z4K1MzlMyWCsi3WJ3RtV4DiXZeRh4qd4JCyp1Zzzi8Wv9xM4dEBmqQntFoei716PzwmFfA==}
|
||||||
engines: {node: '>=12.0.0'}
|
engines: {node: '>=12.0.0'}
|
||||||
@ -1173,6 +1040,18 @@ packages:
|
|||||||
tslib: 2.6.2
|
tslib: 2.6.2
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
|
/@tailwindcss/typography@0.5.10(tailwindcss@3.3.5):
|
||||||
|
resolution: {integrity: sha512-Pe8BuPJQJd3FfRnm6H0ulKIGoMEQS+Vq01R6M5aCrFB/ccR/shT+0kXLjouGC1gFLm9hopTFN+DMP0pfwRWzPw==}
|
||||||
|
peerDependencies:
|
||||||
|
tailwindcss: '>=3.0.0 || insiders'
|
||||||
|
dependencies:
|
||||||
|
lodash.castarray: 4.4.0
|
||||||
|
lodash.isplainobject: 4.0.6
|
||||||
|
lodash.merge: 4.6.2
|
||||||
|
postcss-selector-parser: 6.0.10
|
||||||
|
tailwindcss: 3.3.5
|
||||||
|
dev: true
|
||||||
|
|
||||||
/@types/babel__core@7.20.3:
|
/@types/babel__core@7.20.3:
|
||||||
resolution: {integrity: sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==}
|
resolution: {integrity: sha512-54fjTSeSHwfan8AyHWrKbfBWiEUrNTZsUwPTDSNaaP1QDQIZbeNUg3a59E9D+375MzUw/x1vx2/0F5LBz+AeYA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -1525,11 +1404,6 @@ packages:
|
|||||||
resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==}
|
resolution: {integrity: sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw==}
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/clsx@1.2.1:
|
|
||||||
resolution: {integrity: sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg==}
|
|
||||||
engines: {node: '>=6'}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/clsx@2.0.0:
|
/clsx@2.0.0:
|
||||||
resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==}
|
resolution: {integrity: sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==}
|
||||||
engines: {node: '>=6'}
|
engines: {node: '>=6'}
|
||||||
@ -1554,6 +1428,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/colord@2.9.3:
|
||||||
|
resolution: {integrity: sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/combined-stream@1.0.8:
|
/combined-stream@1.0.8:
|
||||||
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
resolution: {integrity: sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==}
|
||||||
engines: {node: '>= 0.8'}
|
engines: {node: '>= 0.8'}
|
||||||
@ -1598,12 +1476,12 @@ packages:
|
|||||||
which: 2.0.2
|
which: 2.0.2
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/css-vendor@2.0.8:
|
/css-selector-tokenizer@0.8.0:
|
||||||
resolution: {integrity: sha512-x9Aq0XTInxrkuFeHKbYC7zWY8ai7qJ04Kxd9MnvbC1uO5DagxoHQjm4JvG+vCdXOoFtCjbL2XSZfxmoYa9uQVQ==}
|
resolution: {integrity: sha512-Jd6Ig3/pe62/qe5SBPTN8h8LeUg/pT4lLgtavPf7updwwHpvFzxvOQBHYj2LZDMjUnBzgvIUSjRcf6oT5HzHFg==}
|
||||||
dependencies:
|
dependencies:
|
||||||
'@babel/runtime': 7.23.2
|
cssesc: 3.0.0
|
||||||
is-in-browser: 1.1.3
|
fastparse: 1.1.2
|
||||||
dev: false
|
dev: true
|
||||||
|
|
||||||
/cssesc@3.0.0:
|
/cssesc@3.0.0:
|
||||||
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
resolution: {integrity: sha512-/Tb/JcjK111nNScGob5MNtsntNM1aCNUDipB/TkwZFhyDrrE47SOx/18wF2bbjgc3ZzCSKW1T5nt5EbFoAz/Vg==}
|
||||||
@ -1611,13 +1489,22 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/csstype@2.6.21:
|
|
||||||
resolution: {integrity: sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/csstype@3.1.2:
|
/csstype@3.1.2:
|
||||||
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
|
resolution: {integrity: sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==}
|
||||||
|
|
||||||
|
/daisyui@3.9.4:
|
||||||
|
resolution: {integrity: sha512-fvi2RGH4YV617/6DntOVGcOugOPym9jTGWW2XySb5ZpvdWO4L7bEG77VHirrnbRUEWvIEVXkBpxUz2KFj0rVnA==}
|
||||||
|
engines: {node: '>=16.9.0'}
|
||||||
|
dependencies:
|
||||||
|
colord: 2.9.3
|
||||||
|
css-selector-tokenizer: 0.8.0
|
||||||
|
postcss: 8.4.31
|
||||||
|
postcss-js: 4.0.1(postcss@8.4.31)
|
||||||
|
tailwindcss: 3.3.5
|
||||||
|
transitivePeerDependencies:
|
||||||
|
- ts-node
|
||||||
|
dev: true
|
||||||
|
|
||||||
/debug@4.3.4:
|
/debug@4.3.4:
|
||||||
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
resolution: {integrity: sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==}
|
||||||
engines: {node: '>=6.0'}
|
engines: {node: '>=6.0'}
|
||||||
@ -1995,6 +1882,10 @@ packages:
|
|||||||
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/fastparse@1.1.2:
|
||||||
|
resolution: {integrity: sha512-483XLLxTVIwWK3QTrMGRqUfUpoOs/0hbQrl2oz4J0pAcm3A3bu84wxTFqGqkJzewCLdME38xJLJAxBABfQT8sQ==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/fastq@1.15.0:
|
/fastq@1.15.0:
|
||||||
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
|
resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@ -2255,10 +2146,6 @@ packages:
|
|||||||
react-is: 16.13.1
|
react-is: 16.13.1
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/hyphenate-style-name@1.0.4:
|
|
||||||
resolution: {integrity: sha512-ygGZLjmXfPHj+ZWh6LwbC37l43MhfztxetbFCoYTM2VjkIUpeHgSNn7QIyVFj7YQ1Wl9Cbw5sholVJPzWvC2MQ==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/ignore@5.2.4:
|
/ignore@5.2.4:
|
||||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||||
engines: {node: '>= 4'}
|
engines: {node: '>= 4'}
|
||||||
@ -2384,10 +2271,6 @@ packages:
|
|||||||
is-extglob: 2.1.1
|
is-extglob: 2.1.1
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/is-in-browser@1.1.3:
|
|
||||||
resolution: {integrity: sha512-FeXIBgG/CPGd/WUxuEyvgGTEfwiG9Z4EKGxjNMRqviiIIfsmgrpnHLffEDdwUHqNva1VEW91o3xBT/m8Elgl9g==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/is-map@2.0.2:
|
/is-map@2.0.2:
|
||||||
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
|
resolution: {integrity: sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -2531,68 +2414,6 @@ packages:
|
|||||||
hasBin: true
|
hasBin: true
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/jss-plugin-camel-case@10.10.0:
|
|
||||||
resolution: {integrity: sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
hyphenate-style-name: 1.0.4
|
|
||||||
jss: 10.10.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jss-plugin-default-unit@10.10.0:
|
|
||||||
resolution: {integrity: sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
jss: 10.10.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jss-plugin-global@10.10.0:
|
|
||||||
resolution: {integrity: sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
jss: 10.10.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jss-plugin-nested@10.10.0:
|
|
||||||
resolution: {integrity: sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
jss: 10.10.0
|
|
||||||
tiny-warning: 1.0.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jss-plugin-props-sort@10.10.0:
|
|
||||||
resolution: {integrity: sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
jss: 10.10.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jss-plugin-rule-value-function@10.10.0:
|
|
||||||
resolution: {integrity: sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
jss: 10.10.0
|
|
||||||
tiny-warning: 1.0.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jss-plugin-vendor-prefixer@10.10.0:
|
|
||||||
resolution: {integrity: sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
css-vendor: 2.0.8
|
|
||||||
jss: 10.10.0
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jss@10.10.0:
|
|
||||||
resolution: {integrity: sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw==}
|
|
||||||
dependencies:
|
|
||||||
'@babel/runtime': 7.23.2
|
|
||||||
csstype: 3.1.2
|
|
||||||
is-in-browser: 1.1.3
|
|
||||||
tiny-warning: 1.0.3
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/jsx-ast-utils@3.3.5:
|
/jsx-ast-utils@3.3.5:
|
||||||
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
|
resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==}
|
||||||
engines: {node: '>=4.0'}
|
engines: {node: '>=4.0'}
|
||||||
@ -2637,6 +2458,14 @@ packages:
|
|||||||
p-locate: 5.0.0
|
p-locate: 5.0.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/lodash.castarray@4.4.0:
|
||||||
|
resolution: {integrity: sha512-aVx8ztPv7/2ULbArGJ2Y42bG1mEQ5mGjpdvrbJcJFU3TbYybe+QlLS4pst9zV52ymy2in1KpFPiZnAOATxD4+Q==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
|
/lodash.isplainobject@4.0.6:
|
||||||
|
resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==}
|
||||||
|
dev: true
|
||||||
|
|
||||||
/lodash.merge@4.6.2:
|
/lodash.merge@4.6.2:
|
||||||
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
resolution: {integrity: sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==}
|
||||||
dev: true
|
dev: true
|
||||||
@ -2872,10 +2701,6 @@ packages:
|
|||||||
engines: {node: '>= 6'}
|
engines: {node: '>= 6'}
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/popper.js@1.16.1-lts:
|
|
||||||
resolution: {integrity: sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/postcss-import@15.1.0(postcss@8.4.31):
|
/postcss-import@15.1.0(postcss@8.4.31):
|
||||||
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
|
resolution: {integrity: sha512-hpr+J05B2FVYUAXHeK1YyI267J/dDDhMU6B6civm8hSY1jYJnBXxzKDKDswzJmtLHryrjhnDjqqp/49t8FALew==}
|
||||||
engines: {node: '>=14.0.0'}
|
engines: {node: '>=14.0.0'}
|
||||||
@ -2925,6 +2750,14 @@ packages:
|
|||||||
postcss-selector-parser: 6.0.13
|
postcss-selector-parser: 6.0.13
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
|
/postcss-selector-parser@6.0.10:
|
||||||
|
resolution: {integrity: sha512-IQ7TZdoaqbT+LCpShg46jnZVlhWD2w6iQYAcYXfHARZ7X1t/UGhhceQDs5X0cGqKvYlHNOuv7Oa1xmb0oQuA3w==}
|
||||||
|
engines: {node: '>=4'}
|
||||||
|
dependencies:
|
||||||
|
cssesc: 3.0.0
|
||||||
|
util-deprecate: 1.0.2
|
||||||
|
dev: true
|
||||||
|
|
||||||
/postcss-selector-parser@6.0.13:
|
/postcss-selector-parser@6.0.13:
|
||||||
resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
|
resolution: {integrity: sha512-EaV1Gl4mUEV4ddhDnv/xtj7sxwrwxdetHdWUGnT4VJQf+4d05v6lHYZr8N573k5Z0BViss7BDhfWtKS3+sfAqQ==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
@ -3018,19 +2851,6 @@ packages:
|
|||||||
scheduler: 0.23.0
|
scheduler: 0.23.0
|
||||||
dev: false
|
dev: false
|
||||||
|
|
||||||
/react-google-login@5.2.2(react-dom@18.2.0)(react@18.2.0):
|
|
||||||
resolution: {integrity: sha512-JUngfvaSMcOuV0lFff7+SzJ2qviuNMQdqlsDJkUM145xkGPVIfqWXq9Ui+2Dr6jdJWH5KYdynz9+4CzKjI5u6g==}
|
|
||||||
deprecated: Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.
|
|
||||||
peerDependencies:
|
|
||||||
react: ^16 || ^17
|
|
||||||
react-dom: ^16 || ^17
|
|
||||||
dependencies:
|
|
||||||
'@types/react': 18.2.33
|
|
||||||
prop-types: 15.8.1
|
|
||||||
react: 18.2.0
|
|
||||||
react-dom: 18.2.0(react@18.2.0)
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/react-icons@4.11.0(react@18.2.0):
|
/react-icons@4.11.0(react@18.2.0):
|
||||||
resolution: {integrity: sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==}
|
resolution: {integrity: sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@ -3396,10 +3216,6 @@ packages:
|
|||||||
any-promise: 1.3.0
|
any-promise: 1.3.0
|
||||||
dev: true
|
dev: true
|
||||||
|
|
||||||
/tiny-warning@1.0.3:
|
|
||||||
resolution: {integrity: sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==}
|
|
||||||
dev: false
|
|
||||||
|
|
||||||
/to-fast-properties@2.0.0:
|
/to-fast-properties@2.0.0:
|
||||||
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
|
resolution: {integrity: sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==}
|
||||||
engines: {node: '>=4'}
|
engines: {node: '>=4'}
|
||||||
|
|||||||
@ -2,12 +2,11 @@ import './App.css';
|
|||||||
import { BrowserRouter, Route, Routes, Link } from 'react-router-dom';
|
import { BrowserRouter, Route, Routes, Link } from 'react-router-dom';
|
||||||
|
|
||||||
import TestAuth from './components/testAuth';
|
import TestAuth from './components/testAuth';
|
||||||
import IconSideNav from './components/IconSideNav';
|
import LoginPage from './components/authentication/LoginPage';
|
||||||
import AuthenticantionPage from './components/authentication/AuthenticationPage';
|
|
||||||
import SignUpPage from './components/authentication/SignUpPage';
|
import SignUpPage from './components/authentication/SignUpPage';
|
||||||
import NavBar from './components/Nav/Navbar';
|
import NavBar from './components/Nav/Navbar';
|
||||||
import Home from './components/Home';
|
import Home from './components/Home';
|
||||||
|
import ProfileUpdate from './components/ProfileUpdatePage'
|
||||||
|
|
||||||
const App = () => {
|
const App = () => {
|
||||||
return (
|
return (
|
||||||
@ -16,14 +15,12 @@ const App = () => {
|
|||||||
<NavBar/>
|
<NavBar/>
|
||||||
<Routes>
|
<Routes>
|
||||||
<Route path="/" element={<Home/>}/>
|
<Route path="/" element={<Home/>}/>
|
||||||
<Route path="/login" element={<AuthenticantionPage/>}/>
|
<Route path="/login" element={<LoginPage/>}/>
|
||||||
<Route path="/signup" element={<SignUpPage/>}/>
|
<Route path="/signup" element={<SignUpPage/>}/>
|
||||||
<Route path="/testAuth" element={<TestAuth/>}/>
|
<Route path="/testAuth" element={<TestAuth/>}/>
|
||||||
|
<Route path="/update_profile" element={<ProfileUpdate/>}/>
|
||||||
</Routes>
|
</Routes>
|
||||||
</div>
|
</div>
|
||||||
{/* <div>
|
|
||||||
<IconSideNav />
|
|
||||||
</div> */}
|
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
21
frontend/src/api/UserProfileApi.jsx
Normal file
21
frontend/src/api/UserProfileApi.jsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
const ApiUpdateUserProfile = async (formData) => {
|
||||||
|
try {
|
||||||
|
const response = await axios.post('http://127.0.1:8000/api/user/update/', formData, {
|
||||||
|
headers: {
|
||||||
|
'Authorization': "Bearer " + localStorage.getItem('access_token'),
|
||||||
|
'Content-Type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log(response.data);
|
||||||
|
|
||||||
|
return response.data;
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error updating user profile:', error);
|
||||||
|
throw error;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export { ApiUpdateUserProfile };
|
||||||
107
frontend/src/components/ProfileUpdatePage.jsx
Normal file
107
frontend/src/components/ProfileUpdatePage.jsx
Normal file
@ -0,0 +1,107 @@
|
|||||||
|
import React, { useState, useRef } from 'react';
|
||||||
|
import { ApiUpdateUserProfile } from '../api/UserProfileApi';
|
||||||
|
|
||||||
|
function ProfileUpdate() {
|
||||||
|
const [file, setFile] = useState(null);
|
||||||
|
const [username, setUsername] = useState('');
|
||||||
|
const [fullName, setFullName] = useState('');
|
||||||
|
const [about, setAbout] = useState('');
|
||||||
|
const defaultImage = 'https://i1.sndcdn.com/artworks-cTz48e4f1lxn5Ozp-L3hopw-t500x500.jpg';
|
||||||
|
const fileInputRef = useRef(null);
|
||||||
|
|
||||||
|
const handleImageUpload = () => {
|
||||||
|
if (fileInputRef.current) {
|
||||||
|
fileInputRef.current.click();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleFileChange = (e) => {
|
||||||
|
const selectedFile = e.target.files[0];
|
||||||
|
if (selectedFile) {
|
||||||
|
setFile(selectedFile);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSave = () => {
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('profile_pic', file);
|
||||||
|
formData.append('first_name', username);
|
||||||
|
formData.append('about', about);
|
||||||
|
|
||||||
|
ApiUpdateUserProfile(formData);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="flex flex-col items-center mt-12 space-y-4">
|
||||||
|
{/* Profile Image */}
|
||||||
|
<div className="w-32 h-32 relative">
|
||||||
|
<label htmlFor="profileImage" className="absolute inset-0">
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
id="profileImage"
|
||||||
|
accept="image/*"
|
||||||
|
className="hidden"
|
||||||
|
onChange={handleFileChange}
|
||||||
|
ref={fileInputRef}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
<div
|
||||||
|
className="avatar w-32 h-32 cursor-pointer hover:blur"
|
||||||
|
onClick={handleImageUpload}
|
||||||
|
>
|
||||||
|
{file ? (
|
||||||
|
<img src={URL.createObjectURL(file)} alt="Profile" className="rounded-full" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<img src={defaultImage} alt="Default" className="rounded-full" />
|
||||||
|
<i className="fas fa-camera text-white text-2xl absolute bottom-0 right-0 mr-2 mb-2"></i>
|
||||||
|
<i className="fas fa-arrow-up text-white text-2xl absolute top-0 right-0 mr-2 mt-2"></i>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Username Field */}
|
||||||
|
<div className="w-96">
|
||||||
|
<label className="block mb-2 text-gray-600">Username</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter your username"
|
||||||
|
className="input w-full"
|
||||||
|
value={username}
|
||||||
|
onChange={(e) => setUsername(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Full Name Field */}
|
||||||
|
<div className="w-96">
|
||||||
|
<label className="block mb-2 text-gray-600">Full Name</label>
|
||||||
|
<input
|
||||||
|
type="text"
|
||||||
|
placeholder="Enter your full name"
|
||||||
|
className="input w-full"
|
||||||
|
value={fullName}
|
||||||
|
onChange={(e) => setFullName(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* About Field */}
|
||||||
|
<div className="w-96">
|
||||||
|
<label className="block mb-2 text-gray-600">About Me</label>
|
||||||
|
<textarea
|
||||||
|
placeholder="Tell us about yourself"
|
||||||
|
className="textarea w-full h-32"
|
||||||
|
value={about}
|
||||||
|
onChange={(e) => setAbout(e.target.value)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Save Button */}
|
||||||
|
<button className="btn btn-primary w-96" onClick={handleSave}>
|
||||||
|
Save
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ProfileUpdate;
|
||||||
@ -1,206 +0,0 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
|
||||||
import { useNavigate } from 'react-router-dom';
|
|
||||||
import { useGoogleLogin } from '@react-oauth/google';
|
|
||||||
import Avatar from '@mui/material/Avatar';
|
|
||||||
import Button from '@mui/material/Button';
|
|
||||||
import CssBaseline from '@mui/material/CssBaseline';
|
|
||||||
import TextField from '@mui/material/TextField';
|
|
||||||
import FormControlLabel from '@mui/material/FormControlLabel';
|
|
||||||
import Checkbox from '@mui/material/Checkbox';
|
|
||||||
import Link from '@mui/material/Link';
|
|
||||||
import Divider from '@mui/material/Divider';
|
|
||||||
import Paper from '@mui/material/Paper';
|
|
||||||
import Box from '@mui/material/Box';
|
|
||||||
import Grid from '@mui/material/Grid';
|
|
||||||
import LockOutlinedIcon from '@mui/icons-material/LockOutlined';
|
|
||||||
import Typography from '@mui/material/Typography';
|
|
||||||
import { createTheme, ThemeProvider } from '@mui/material/styles';
|
|
||||||
|
|
||||||
import refreshAccessToken from './refreshAcesstoken';
|
|
||||||
import axiosapi from '../../api/axiosapi';
|
|
||||||
|
|
||||||
|
|
||||||
function Copyright(props) {
|
|
||||||
return (
|
|
||||||
<Typography variant="body2" color="text.secondary" align="center" {...props}>
|
|
||||||
{'Copyright © '}
|
|
||||||
<Link color="inherit" href="https://github.com/TurTaskProject/TurTaskWeb">
|
|
||||||
TurTask
|
|
||||||
</Link>{' '}
|
|
||||||
{new Date().getFullYear()}
|
|
||||||
{'.'}
|
|
||||||
</Typography>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
const defaultTheme = createTheme();
|
|
||||||
|
|
||||||
export default function SignInSide() {
|
|
||||||
|
|
||||||
const Navigate = useNavigate();
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (!refreshAccessToken()) {
|
|
||||||
Navigate("/");
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const [email, setEmail] = useState("");
|
|
||||||
const [username, setUsername] = useState("");
|
|
||||||
const [password, setPassword] = useState("");
|
|
||||||
|
|
||||||
const handleUsernameChange = (event) => {
|
|
||||||
setUsername(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleEmailChange = (event) => {
|
|
||||||
setEmail(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handlePasswordChange = (event) => {
|
|
||||||
setPassword(event.target.value);
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = (event) => {
|
|
||||||
event.preventDefault();
|
|
||||||
|
|
||||||
// Send a POST request to the authentication API
|
|
||||||
axiosapi.apiUserLogin({
|
|
||||||
email: email,
|
|
||||||
username: username,
|
|
||||||
password: password
|
|
||||||
}).then(res => {
|
|
||||||
// On successful login, store tokens and set the authorization header
|
|
||||||
localStorage.setItem('access_token', res.data.access);
|
|
||||||
localStorage.setItem('refresh_token', res.data.refresh);
|
|
||||||
axiosapi.axiosInstance.defaults.headers['Authorization'] = "Bearer " + res.data.access;
|
|
||||||
Navigate('/');
|
|
||||||
}).catch(err => {
|
|
||||||
console.log('Login failed'); // Handle login failure
|
|
||||||
console.log(err)
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const googleLoginImplicit = useGoogleLogin({
|
|
||||||
flow: 'auth-code',
|
|
||||||
redirect_uri: 'postmessage',
|
|
||||||
onSuccess: async (response) => {
|
|
||||||
try {
|
|
||||||
const loginResponse = await axiosapi.googleLogin(response.code);
|
|
||||||
if (loginResponse && loginResponse.data) {
|
|
||||||
const { access_token, refresh_token } = loginResponse.data;
|
|
||||||
|
|
||||||
// Save the tokens in localStorage
|
|
||||||
localStorage.setItem('access_token', access_token);
|
|
||||||
localStorage.setItem('refresh_token', refresh_token);
|
|
||||||
Navigate('/');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.error('Error with the POST request:', error);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onError: errorResponse => console.log(errorResponse),
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
return (
|
|
||||||
<ThemeProvider theme={defaultTheme}>
|
|
||||||
<Grid container component="main" sx={{ height: '100vh' }}>
|
|
||||||
<CssBaseline />
|
|
||||||
<Grid
|
|
||||||
item
|
|
||||||
xs={false}
|
|
||||||
sm={4}
|
|
||||||
md={7}
|
|
||||||
sx={{
|
|
||||||
backgroundImage: 'url(https://source.unsplash.com/random?wallpapers)',
|
|
||||||
backgroundRepeat: 'no-repeat',
|
|
||||||
backgroundColor: (t) =>
|
|
||||||
t.palette.mode === 'light' ? t.palette.grey[50] : t.palette.grey[900],
|
|
||||||
backgroundSize: 'cover',
|
|
||||||
backgroundPosition: 'center',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
<Grid item xs={12} sm={8} md={5} component={Paper} elevation={6} square>
|
|
||||||
<Box
|
|
||||||
sx={{
|
|
||||||
my: 8,
|
|
||||||
mx: 4,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
alignItems: 'center',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Avatar sx={{ m: 1, bgcolor: 'secondary.main' }}>
|
|
||||||
<LockOutlinedIcon />
|
|
||||||
</Avatar>
|
|
||||||
<Typography component="h1" variant="h5">
|
|
||||||
Sign in
|
|
||||||
</Typography>
|
|
||||||
<Box component="form" noValidate onSubmit={handleSubmit} sx={{ mt: 1 }}>
|
|
||||||
<TextField
|
|
||||||
margin="normal"
|
|
||||||
required
|
|
||||||
fullWidth
|
|
||||||
id="email"
|
|
||||||
label="Email Address"
|
|
||||||
name="email"
|
|
||||||
autoComplete="email"
|
|
||||||
autoFocus
|
|
||||||
onChange={handleEmailChange}
|
|
||||||
/>
|
|
||||||
<TextField
|
|
||||||
margin="normal"
|
|
||||||
required
|
|
||||||
fullWidth
|
|
||||||
name="password"
|
|
||||||
label="Password"
|
|
||||||
type="password"
|
|
||||||
id="password"
|
|
||||||
autoComplete="current-password"
|
|
||||||
onChange={handlePasswordChange}
|
|
||||||
/>
|
|
||||||
<FormControlLabel
|
|
||||||
control={<Checkbox value="remember" color="primary" />}
|
|
||||||
label="Remember me"
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
type="submit"
|
|
||||||
fullWidth
|
|
||||||
variant="contained"
|
|
||||||
sx={{ mt: 3, mb: 2 }}
|
|
||||||
>
|
|
||||||
Sign In
|
|
||||||
</Button>
|
|
||||||
<Divider>OR</Divider>
|
|
||||||
<Box py={2}>
|
|
||||||
<Button
|
|
||||||
fullWidth
|
|
||||||
variant="outlined"
|
|
||||||
color="secondary"
|
|
||||||
onClick={() => googleLoginImplicit()}
|
|
||||||
>
|
|
||||||
Sign in with Google 🚀
|
|
||||||
</Button>
|
|
||||||
</Box>
|
|
||||||
<Grid container>
|
|
||||||
<Grid item xs>
|
|
||||||
<Link href="#" variant="body2">
|
|
||||||
Forgot password?
|
|
||||||
</Link>
|
|
||||||
</Grid>
|
|
||||||
<Grid item>
|
|
||||||
<Link href="#" variant="body2">
|
|
||||||
{"Don't have an account? Sign Up"}
|
|
||||||
</Link>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
<Copyright sx={{ mt: 5 }} />
|
|
||||||
</Box>
|
|
||||||
</Box>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
|
||||||
</ThemeProvider>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
143
frontend/src/components/authentication/LoginPage.jsx
Normal file
143
frontend/src/components/authentication/LoginPage.jsx
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
import React, { useEffect, useState } from "react";
|
||||||
|
import { useNavigate } from "react-router-dom";
|
||||||
|
import { useGoogleLogin } from "@react-oauth/google"
|
||||||
|
|
||||||
|
import refreshAccessToken from './refreshAcesstoken';
|
||||||
|
import axiosapi from '../../api/axiosapi';
|
||||||
|
|
||||||
|
function LoginPage() {
|
||||||
|
const Navigate = useNavigate();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!refreshAccessToken()) {
|
||||||
|
Navigate("/");
|
||||||
|
}
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const [email, setEmail] = useState("");
|
||||||
|
const [password, setPassword] = useState("");
|
||||||
|
|
||||||
|
const handleEmailChange = event => {
|
||||||
|
setEmail(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handlePasswordChange = event => {
|
||||||
|
setPassword(event.target.value);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSubmit = event => {
|
||||||
|
event.preventDefault();
|
||||||
|
|
||||||
|
// Send a POST request to the authentication API
|
||||||
|
axiosapi
|
||||||
|
.apiUserLogin({
|
||||||
|
email: email,
|
||||||
|
password: password,
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
// On successful login, store tokens and set the authorization header
|
||||||
|
localStorage.setItem("access_token", res.data.access);
|
||||||
|
localStorage.setItem("refresh_token", res.data.refresh);
|
||||||
|
axiosapi.axiosInstance.defaults.headers["Authorization"] = "Bearer " + res.data.access;
|
||||||
|
Navigate("/");
|
||||||
|
})
|
||||||
|
.catch(err => {
|
||||||
|
console.log("Login failed");
|
||||||
|
console.log(err);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const googleLoginImplicit = useGoogleLogin({
|
||||||
|
flow: "auth-code",
|
||||||
|
redirect_uri: "postmessage",
|
||||||
|
onSuccess: async response => {
|
||||||
|
try {
|
||||||
|
const loginResponse = await axiosapi.googleLogin(response.code);
|
||||||
|
if (loginResponse && loginResponse.data) {
|
||||||
|
const { access_token, refresh_token } = loginResponse.data;
|
||||||
|
|
||||||
|
localStorage.setItem("access_token", access_token);
|
||||||
|
localStorage.setItem("refresh_token", refresh_token);
|
||||||
|
Navigate("/");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error with the POST request:", error);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onError: errorResponse => console.log(errorResponse),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<html data-theme="night">
|
||||||
|
<div className="min-h-screen flex">
|
||||||
|
{/* Left Section (Login Box) */}
|
||||||
|
<div className="w-1/2 flex items-center justify-center">
|
||||||
|
<div className="w-96 bg-neutral rounded-lg p-8 shadow-md space-y-4">
|
||||||
|
<h2 className="text-2xl font-semibold text-left">Log in to your account</h2>
|
||||||
|
{/* Email Input */}
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label" htmlFor="email">
|
||||||
|
<p className="text-bold">
|
||||||
|
Email<span className="text-red-500 text-bold">*</span>
|
||||||
|
</p>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
type="email"
|
||||||
|
id="email"
|
||||||
|
placeholder="Enter your email"
|
||||||
|
onChange={handleEmailChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Password Input */}
|
||||||
|
<div className="form-control">
|
||||||
|
<label className="label" htmlFor="password">
|
||||||
|
<p className="text-bold">
|
||||||
|
Password<span className="text-red-500 text-bold">*</span>
|
||||||
|
</p>
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
className="input"
|
||||||
|
type="password"
|
||||||
|
id="password"
|
||||||
|
placeholder="Enter your password"
|
||||||
|
onChange={handlePasswordChange}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
{/* Login Button */}
|
||||||
|
<button className="btn btn-primary w-full" onClick={handleSubmit}>
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
<div className="divider">OR</div>
|
||||||
|
{/* Login with Google Button */}
|
||||||
|
<button className="btn btn-outline btn-secondary w-full" onClick={() => googleLoginImplicit()}>
|
||||||
|
Login with Google
|
||||||
|
</button>
|
||||||
|
{/* Forgot Password Link */}
|
||||||
|
<div className="justify-left">
|
||||||
|
<a href="#" className="text-blue-500 text-sm text-left">
|
||||||
|
Forgot your password?
|
||||||
|
</a>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
{/* Right Section (Blurred Image Background) */}
|
||||||
|
<div className="w-1/2 relative">
|
||||||
|
<div
|
||||||
|
className="w-full h-full bg-cover bg-center"
|
||||||
|
style={{
|
||||||
|
backgroundImage: 'url("https://th.bing.com/th/id/OIG.9byG0pWUCcbGL7Kly9tA?pid=ImgGn&w=1024&h=1024&rs=1")',
|
||||||
|
filter: "blur(2px) brightness(.5)",
|
||||||
|
}}></div>
|
||||||
|
|
||||||
|
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-white text-2xl font-semibold">
|
||||||
|
Text Overlay
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default LoginPage;
|
||||||
@ -1,6 +1,6 @@
|
|||||||
import React, { useState, useEffect } from 'react';
|
import React, { useState, useEffect } from 'react';
|
||||||
import axiosapi from '../api/axiosapi';
|
import axiosapi from '../api/axiosapi';
|
||||||
import Button from '@material-ui/core/Button';
|
import { Button } from '@mui/material';
|
||||||
import { useNavigate } from 'react-router-dom';
|
import { useNavigate } from 'react-router-dom';
|
||||||
|
|
||||||
function TestAuth() {
|
function TestAuth() {
|
||||||
|
|||||||
@ -1,30 +1,21 @@
|
|||||||
/** @type {import('tailwindcss').Config} */
|
/** @type {import('tailwindcss').Config} */
|
||||||
|
|
||||||
|
const defaultTheme = require('tailwindcss/defaultTheme')
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
content: ["./src/**/*.{js,jsx}"],
|
content: ["./src/**/*.{js,jsx}"],
|
||||||
theme: {
|
theme: {
|
||||||
colors: {
|
|
||||||
'blue': '#1fb6ff',
|
|
||||||
'purple': '#7e5bef',
|
|
||||||
'pink': '#ff49db',
|
|
||||||
'orange': '#ff7849',
|
|
||||||
'green': '#13ce66',
|
|
||||||
'yellow': '#ffc82c',
|
|
||||||
'gray-dark': '#273444',
|
|
||||||
'gray': '#8492a6',
|
|
||||||
'gray-light': '#d3dce6',
|
|
||||||
},
|
|
||||||
fontFamily: {
|
|
||||||
sans: ['Graphik', 'sans-serif'],
|
|
||||||
serif: ['Merriweather', 'serif'],
|
|
||||||
},
|
|
||||||
extend: {
|
extend: {
|
||||||
spacing: {
|
fontFamily: {
|
||||||
'8xl': '96rem',
|
'sans': ['"Proxima Nova"', ...defaultTheme.fontFamily.sans],
|
||||||
'9xl': '128rem',
|
|
||||||
},
|
},
|
||||||
borderRadius: {
|
},
|
||||||
'4xl': '2rem',
|
},
|
||||||
}
|
plugins: [require("daisyui"),
|
||||||
}
|
require("@tailwindcss/typography"),
|
||||||
|
require("daisyui")
|
||||||
|
],
|
||||||
|
daisyui: {
|
||||||
|
themes: ["light", "night"],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@ -11,4 +11,6 @@ djangorestframework-simplejwt>=5.3
|
|||||||
django-cors-headers>=4.3
|
django-cors-headers>=4.3
|
||||||
google_api_python_client>=2.1
|
google_api_python_client>=2.1
|
||||||
google_auth_oauthlib>=1.1
|
google_auth_oauthlib>=1.1
|
||||||
google-auth-httplib2>=0.1
|
google-auth-httplib2>=0.1
|
||||||
|
django-storages[s3]>=1.14
|
||||||
|
Pillow>=10.1
|
||||||
Loading…
Reference in New Issue
Block a user