diff --git a/.gitignore b/.gitignore index 99f8f6d69..c3d269eff 100644 --- a/.gitignore +++ b/.gitignore @@ -11,6 +11,7 @@ node_modules pocs-capstone/backend/.env pocs-capstone/backend/tutorial-env/ +pocs-capstone/backend/henry_env/ pocs-capstone/backend/db/__pycache__/ pocs-capstone/backend/db/migrations/ pocs-capstone/backend/backend/__pycache__/ @@ -121,3 +122,4 @@ pocs-capstone/frontend/src/components/Tutorial/Tutorial-screenshots/original scr pocs-capstone/frontend/src/components/Tutorial/Tutorial-screenshots/original screenshots/calendar.png pocs-capstone/frontend/src/components/Tutorial/Tutorial-screenshots/original screenshots/completed-tasks.png pocs-capstone/frontend/src/components/Tutorial/Tutorial-screenshots/original screenshots/inventory.png +pocs-capstone/backend/venv/* \ No newline at end of file diff --git a/README.md b/README.md index ec2c08156..57a88afb7 100644 --- a/README.md +++ b/README.md @@ -115,7 +115,7 @@ cd /sb/frontend ```bash npm install npm run build -nmp start +npm start ``` 8. In your browser, navigate in the search bar to http://localhost:3000. diff --git a/pocs-capstone/backend/backend/db/__init__.py b/pocs-capstone/backend/backend/db/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pocs-capstone/backend/backend/db/admin.py b/pocs-capstone/backend/backend/db/admin.py new file mode 100644 index 000000000..529812433 --- /dev/null +++ b/pocs-capstone/backend/backend/db/admin.py @@ -0,0 +1,10 @@ +from django.contrib import admin + +# Register your models here. +""" +from .models import Question +from .models import Note + +admin.site.register(Question) +admin.site.register(Note) +""" \ No newline at end of file diff --git a/pocs-capstone/backend/backend/db/apps.py b/pocs-capstone/backend/backend/db/apps.py new file mode 100644 index 000000000..f6db5ee72 --- /dev/null +++ b/pocs-capstone/backend/backend/db/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class DbConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'db' diff --git a/pocs-capstone/backend/backend/db/authenticate.py b/pocs-capstone/backend/backend/db/authenticate.py new file mode 100644 index 000000000..e4c4d1a6d --- /dev/null +++ b/pocs-capstone/backend/backend/db/authenticate.py @@ -0,0 +1,33 @@ +from rest_framework_simplejwt.authentication import JWTAuthentication +from django.conf import settings + +from rest_framework.authentication import CSRFCheck +from rest_framework import exceptions + +def enforce_csrf(request): + """ + Enforce CSRF validation. + """ + check = CSRFCheck() + # populates request.META['CSRF_COOKIE'], which is used in process_view() + check.process_request(request) + reason = check.process_view(request, None, (), {}) + if reason: + # CSRF failed, bail with explicit error message + raise exceptions.PermissionDenied('CSRF Failed: %s' % reason) + +class CustomAuthentication(JWTAuthentication): + + def authenticate(self, request): + header = self.get_header(request) + + if header is None: + raw_token = request.COOKIES.get(settings.SIMPLE_JWT['AUTH_COOKIE']) or None + else: + raw_token = self.get_raw_token(header) + if raw_token is None: + return None + + validated_token = self.get_validated_token(raw_token) + enforce_csrf(request) + return self.get_user(validated_token), validated_token diff --git a/pocs-capstone/backend/backend/db/canvasrequests.py b/pocs-capstone/backend/backend/db/canvasrequests.py new file mode 100644 index 000000000..171bf4a25 --- /dev/null +++ b/pocs-capstone/backend/backend/db/canvasrequests.py @@ -0,0 +1,232 @@ + +import requests +import bs4 as bs +import pprint +# import re +# from dateutil import parser +pp = pprint.PrettyPrinter(indent=2) + +# Static settings +BASE_URL = 'https://templeu.instructure.com/api/v1' + + +'''Returns the response from a GET request to the Canvas API''' + + +def canvas_request(url, headers, params): + try: + response = requests.get(url=url, headers=headers, params=params) + status = response.status_code + except Exception as e: + return e, 500 # this is an internal service error due to failed request to external resource + return response.json(), status # convert to json + + +'''Return a list of all Canvas courses' IDs''' + + +def get_courses(canvas_token): # later we'll add userId as a parameter + auth_header = {'Authorization': 'Bearer ' + canvas_token} + courses_params = { + "per_page": 100, + "enrollment_state": "active", + "workflow_state": "available", + "enrollment_type": "student" + } + + courses_data, status = canvas_request( + BASE_URL + '/courses', auth_header, courses_params) + + if status == 200: + course_id_list = [] # a list of all the user's courses (their ids) + + for course_entry in courses_data: + course = (course_entry['id'], course_entry['name']) + course_id_list.append(course) + + return course_id_list, status + return None, status + + +'''Given a course ID, return a list of all assignments' IDs''' + + +def get_assignments(canvas_token, course_id): + auth_header = {'Authorization': 'Bearer ' + canvas_token} + assignment_params = { + "per_page": 5000, + "include": "submission" + } + + assignments_data, status = canvas_request( + BASE_URL + '/courses/' + str(course_id) + '/assignments', auth_header, assignment_params) + + assignment_id_list = [] # a list of all the user's courses (their ids) + for assignment_entry in assignments_data: + try: + assignment_id_list.append(assignment_entry['id']) + except Exception as e: + print(e) + + return assignment_id_list, assignments_data + + +'''Given a course ID, return a json object of course information for that particular course''' + + +def get_course_info(canvas_token, course_id): + auth_header = {'Authorization': 'Bearer ' + canvas_token} + course_url = BASE_URL + '/courses/' + str(course_id) + return canvas_request(url=course_url, headers=auth_header, params={}) + + +'''Given a course ID and assignment ID, return a dict of assignment information for that particular assignment''' + + +def get_assignment_info(canvas_token, course_id, assignment_id): + auth_header = {'Authorization': 'Bearer ' + canvas_token} + + # TODO - This needs to move further back in the procedure + user_url = BASE_URL + '/users/self' + user_id = None + + try: + b, bstatus = canvas_request( + url=user_url, headers=auth_header, params={}) + if bstatus == 200: + user_id = b['id'] + except Exception as e: + print(e) + return None + + assignment_url = BASE_URL + '/courses/' + \ + str(course_id) + '/assignments/' + str(assignment_id) + a, status = canvas_request(url=assignment_url, headers=auth_header, params={ + "include[]": ['submission']}) + submission_details = {} + + if status == 200: + due = a['due_at'] + submission_details = a['submission'] + submitted = submission_details['submitted_at'] + else: + due = None + + if due != None: + due = due[0:10] # hack into a string UwU + if submitted != None: + submitted = submitted[0:10] # hack into a string UwU + + try: + description = bs.BeautifulSoup(a['description'], 'lxml').get_text() + except Exception as e: + description = "" + + return {'title': a['name'] or "No title.", + 'due_date': due, + + 'task_type': 'S', + # 'task_level': 1, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'completed_date': submitted, + 'description': description, + 'course_id': a['course_id'], + 'assignment_id': a['id'] + } + + +'''Given a course ID, return a list of all assignments' IDs''' + + +def get_assignments(canvas_token, course_info): + course_id = course_info[0] + auth_header = {'Authorization': 'Bearer ' + canvas_token} + assignment_params = { + "per_page": 5000, + "include": "submission" + } + + assignments_data, status = canvas_request( + BASE_URL + '/courses/' + str(course_id) + '/assignments', auth_header, assignment_params) + + assignment_id_list = [] # a list of all the user's courses (their ids) + for assignment_entry in assignments_data: + try: + assignment_id_list.append(assignment_entry['id']) + except Exception as e: + print(e) + + return assignment_id_list, assignments_data + + +'''Given a course ID, return a json object of course information for that particular course''' + + +def get_course_info(canvas_token, course_id): + auth_header = {'Authorization': 'Bearer ' + canvas_token} + course_url = BASE_URL + '/courses/' + str(course_id) + return canvas_request(url=course_url, headers=auth_header, params={}) + + +'''Given a course ID and assignment ID, return a dict of assignment information for that particular assignment''' + + +def parse_assignments(assignments, course_title): + + tasks = [] + + for a in assignments: + submission_details = {} + + due = a['due_at'] + submission_details = a['submission'] + + submitted = submission_details['submitted_at'] + + if due != None: + due = due[0:10] # hack into a string UwU + if submitted != None: + submitted = submitted[0:10] # hack into a string UwU + + try: + description = bs.BeautifulSoup(a['description'], 'lxml').get_text() + except Exception as e: + description = "" + + tasks.append({'title': a['name'] or "No title.", + 'due_date': due, + + 'task_type': 'S', + # 'task_level': 1, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': course_title, + 'completed_date': submitted, + 'description': description, + 'course_id': a['course_id'], + 'assignment_id': a['id'], + }) + return tasks + + +'''Given a list of assignment IDs, return a list where each entry is a dict of assignment information corresponding to those IDs''' + + +def get_all_assignments(canvas_token): + all_assignments = [] # list of all assignment IDs for all courses + + course_info, status = get_courses(canvas_token) # all course IDs + if status != 200: + return None, status + for course in course_info: + # all assignment IDs for one specific course + assignment_ids, assignments = get_assignments(canvas_token, course) + + # for assignment_id in assignment_ids: + # assignment_info = get_assignment_info(canvas_token, course_id, assignment_id) + # all_assignments.append(assignment_info) # add each assignment dict from this course to list + _assignments = parse_assignments(assignments, course[1]) + all_assignments = all_assignments+_assignments + + return all_assignments, status diff --git a/pocs-capstone/backend/backend/db/models.py b/pocs-capstone/backend/backend/db/models.py new file mode 100644 index 000000000..ec291d5a4 --- /dev/null +++ b/pocs-capstone/backend/backend/db/models.py @@ -0,0 +1,238 @@ +from django.db import models +from django.utils import timezone +from django.utils.translation import gettext_lazy as _ +from django.contrib.auth.models import AbstractBaseUser, PermissionsMixin, BaseUserManager +from django.conf import settings + + +# from django.contrib.auth.models import User +import datetime +from django.utils import timezone +today = timezone.now + + +# add related name = user on my end +class CustomAccountManager(BaseUserManager): + """Class responsible for the creation of superusers and standard users. + Overrides built-in user class and constructor. + Extends BaseUserManager. + """ + + def create_superuser(self, email, user_name, first_name, password, **other_fields): + """Returns a superuser User object upon successful creation. + + :param email: user's email, unique + :type email: str + :param username: user's username, unique + :type username: str + :param first_name: user's first name + :type first_name: str + :param password: user's password, hashed then stored + :type password: str + :param other_fields: additional keyword arguments: is_staff, is_superuser, is_active + :type other_fields: dict + """ + other_fields.setdefault('is_staff', True) + other_fields.setdefault('is_superuser', True) + other_fields.setdefault('is_active', True) + + if other_fields.get('is_staff') is not True: + raise ValueError( + "Superuser must be staff! 'is_staff' must be True" + ) + if other_fields.get('is_superuser') is not True: + raise ValueError( + "Superuser must be superuser! 'is_superuser' must be True" + ) + return self.create_user(email, user_name, first_name, password, **other_fields) + + def create_user(self, email, user_name, first_name, password, **other_fields): + """Returns a standard User object upon successful creation. + + :param email: user's email, unique + :type email: str + :param username: user's username, unique + :type username: str + :param first_name: user's first name + :type first_name: str + :param password: user's password, hashed then stored + :type password: str + :param other_fields: additional keyword arguments: is_staff, is_superuser, is_active + :type other_fields: dict + """ + if not email: + raise ValueError(_( + 'You must provide an Email Address to register!' + )) + + email = self.normalize_email(email) + user = self.model(email=email, user_name=user_name, + first_name=first_name, **other_fields) + user.set_password(password) + user.save() + return user + + +class NewUser(AbstractBaseUser, PermissionsMixin): # TODO rename to something less weird! + """Class responsible for creating and storing account information for a user + Extends AbstractBaseUser and PermissionsMixin. + """ + email = models.EmailField(_('email address'), unique=True) + username = models.CharField(max_length=128, unique=True) + first_name = models.CharField( + max_length=128, unique=False, blank=True, null=True, default="") + join_date = models.DateTimeField(default=timezone.now) + birthday = models.DateField(null=True, blank=True, default=None) + bio = models.TextField(_('about'), max_length=512, blank=True) + is_staff = models.BooleanField(default=False) + # TODO if we want email verification to activate user we change this to false + is_active = models.BooleanField(default=True) + + # Add blank=True field - allow canvas_token to remain "" when patches are made + canvas_token = models.CharField(max_length=512, default="", blank=True) + tutorial = models.BooleanField(default=True) + demo_canvas = models.BooleanField(default=False) + tags = models.JSONField(default=list, null=True, blank=True) + canvas_tags = models.JSONField(default=list, null=True, blank=True) + + + + objects = CustomAccountManager() + + USERNAME_FIELD = 'email' # to log in and authenticate ! + REQUIRED_FIELDS = ['user_name', 'first_name'] + + def __str__(self): + """NewUser toString method + """ + return self.username + + def get_canvas_token(self): + return self.canvas_token + +# Many to One relationship between user and petprofile +# If user is deleted so is their pet profile + + +class Avatar(models.Model): + + # inner class to specify enumerations + class AvatarType(models.TextChoices): + + CAT = 'CT' + DOG = "DG" + CRAB = 'CR' + ROCK = "RK" + avatar_id = models.AutoField(primary_key=True) + user = models.ForeignKey(settings.AUTH_USER_MODEL, + on_delete=models.CASCADE) + avatar_type = models.CharField( + max_length=2, + choices=AvatarType.choices, + default=AvatarType.CAT + ) + total_xp = models.PositiveIntegerField(default=1) + last_interaction = models.DateField(default=None, null=True, blank=True) + last_feed = models.DateField(default=None, null=True, blank=True) + pet_name = models.CharField(max_length=32, default='') + flavour_text = models.TextField( + max_length=256, default='Welcome to Study Buddy!', blank=True, null=True) + palette = models.IntegerField(default=0) + + def __str__(self): + """Avatar toString method + """ + return f'{self.pet_name}, {self.avatar_type}' + +# Inventory for candies earned, currently no accessories + + +class Inventory(models.Model): + """Class defining the model for the Inventory + Extends models.Model. + """ + + class BaseType(models.TextChoices): + """Class defining the model for candy size + Extends models.TextChoices. + """ + SMALL = 'S', "SMALL" + MEDIUM = 'M', "MEDIUM" + LARGE = 'L', "LARGE" + CAKE = 'C', "CAKE" + + # TODO RETURN AND UPDATE WITH DERIVED VALUES + class CandyLevel(models.IntegerChoices): + """Class defining the model for candy levels + Extends models.IntegerChoices. + """ + BEGINNER = 1, "Beginner" + NOVICE = 2, "Novice" + INTERMEDIATE = 3, "Intermediate" + ADVANCED = 4, "Advanced" + EXPERT = 5, "Expert" + + inventory_id = models.AutoField(primary_key=True) + user = models.ForeignKey(settings.AUTH_USER_MODEL, + on_delete=models.CASCADE) + candy_base_type = models.CharField(max_length=1, choices=BaseType.choices) + candy_level = models.PositiveIntegerField(choices=CandyLevel.choices) + quantity = models.PositiveIntegerField(default=0) + + def __str__(self): + """Candy toString method + """ + return f'{self.candy_base_type}, {self.candy_level}' + + +class Task(models.Model): + """Class definiing the model for a Task + Extends models.Model. + """ + class BaseType(models.TextChoices): + SMALL = 'S', "SMALL" + MEDIUM = 'M', "MEDIUM" + LARGE = 'L', "LARGE" + CAKE = 'C', "CAKE" + + class TaskLevel(models.IntegerChoices): + """Class defining the model for candy levels + Extends models.IntegerChoices. + """ + BEGINNER = 1, "Beginner" + NOVICE = 2, "Novice" + INTERMEDIATE = 3, "Intermediate" + ADVANCED = 4, "Advanced" + EXPERT = 5, "Expert" + task_id = models.AutoField(primary_key=True) + user = models.ForeignKey(settings.AUTH_USER_MODEL, + on_delete=models.CASCADE) + title = models.CharField(max_length=128, default="A new task!") + due_date = models.DateField(null=True, blank=True) + created_date = models.DateTimeField(default=timezone.now) + completed_date = models.DateField(default=None, null=True, blank=True) + completed = models.BooleanField(default=False) + active = models.BooleanField(default=True) + task_type = models.CharField( + max_length=1, choices=BaseType.choices, default='S') + task_level = models.PositiveSmallIntegerField( + choices=TaskLevel.choices, default=1) + recurring = models.BooleanField(default=False) + recurring_time_delta = models.PositiveIntegerField(default=0) + description = models.TextField( + default="A new task!", blank=True, null=True) + course_id = models.PositiveBigIntegerField(default=0) + assignment_id = models.PositiveIntegerField(default=0) + received = models.BooleanField(default=False) + unique_canvas_tag = models.CharField( + max_length=256, default=None, null=True, blank=True) + course_title = models.CharField( + max_length=256, default=None, null=True, blank=True) + tags = models.JSONField(default=list, null=False, blank=True) + + + + def __str__(self): + """Task toString method + """ + return f'{self.title}, {self.task_type}, {self.task_level}, {self.description}' diff --git a/pocs-capstone/backend/backend/db/serializers.py b/pocs-capstone/backend/backend/db/serializers.py new file mode 100644 index 000000000..039b78196 --- /dev/null +++ b/pocs-capstone/backend/backend/db/serializers.py @@ -0,0 +1,125 @@ +from rest_framework import serializers +from db.models import NewUser, Avatar, Task, Inventory +from django.contrib.auth.password_validation import validate_password + + +class RegisterUserSerializer(serializers.ModelSerializer): + class Meta: + model = NewUser + fields = ('email', 'username', 'password', + 'bio', 'birthday', 'first_name') + extra_kwargs = {'password': {'write_only': True}} + + def create(self, validated_data): + password = validated_data.pop('password', None) + instance = self.Meta.model(**validated_data) + if password is not None: + instance.set_password(password) + instance.save() + return instance + + +class UserDataSerializer(serializers.ModelSerializer): + class Meta: + model = NewUser + fields = ['id', + 'canvas_token', + 'tutorial', + 'email', + 'username', + 'first_name', + 'join_date', + 'birthday', + 'bio', + 'tags', + 'canvas_tags'] + + +""" +Note: not sure if I'll need to only use subsets of fields later so +explicitly including all here. +""" + + +class AvatarSerializer(serializers.ModelSerializer): + class Meta: + model = Avatar + fields = [ # 'avatar_owner', + 'avatar_id', + 'avatar_type', + 'total_xp', + 'last_interaction', + 'last_feed', 'pet_name', + 'flavour_text', 'palette'] + + +class TaskSerializer(serializers.ModelSerializer): + class Meta: + model = Task + fields = [ + 'task_id', + 'title', + 'due_date', + 'created_date', + 'completed_date', + 'completed', + 'active', + 'task_type', + 'task_level', + 'recurring', + 'recurring_time_delta', + 'description', + 'course_id', + 'assignment_id', + 'received', + 'tags', + 'course_title', + ] + + +class InventorySerializer(serializers.ModelSerializer): + class Meta: + model = Inventory + fields = [ + # 'inventory_owner', + 'inventory_id', + 'candy_base_type', + 'candy_level', + 'quantity' + ] + + +class CanvasSerializer(serializers.ModelSerializer): + class Meta: + model = Task + fields = [ + 'user_id', + 'task_id', + 'title', + 'due_date', + 'created_date', + 'completed_date', + 'completed', + 'active', + 'task_type', + 'task_level', + 'recurring', + 'recurring_time_delta', + 'description', + 'course_id', + 'assignment_id', + 'unique_canvas_tag', + 'received', + 'tags', + 'course_title', + ] + # TODO - consider overriding create rather than doing validation in the view + """ + def create(self,validated_data): + obj, created = Task.objects.update_or_create( + tag = validated_data.get('unique_canvas_tag'), + defaults=validated_data + ) + print(created) + return obj + """ diff --git a/pocs-capstone/backend/backend/db/studybuddyemail.py b/pocs-capstone/backend/backend/db/studybuddyemail.py new file mode 100644 index 000000000..3d506692f --- /dev/null +++ b/pocs-capstone/backend/backend/db/studybuddyemail.py @@ -0,0 +1,221 @@ +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +import ssl +import smtplib +import os +from dotenv import load_dotenv, find_dotenv + +# Provide this function with an address to send the email to. Email comes from gmail account +# Need gmail password in .env +# Email will most likely be sent to the recievers trash +# Button currently just links to google.com +# Email will send if address is not real. Certain symbols are not accepted but numbers appear fine after @ ex. jay@gmasd12321as.com +# Should wrap function in try except +def send_email(email_reciever): + + load_dotenv(find_dotenv()) + + email_sender = 'productivitypet101@gmail.com' + email_password = os.getenv('GMAIL_PASSWORD') + email_reciever = email_reciever + + subject = 'Thank you for registering with Study Buddy!' + + html = ''' + + + + + + + + + + + + + + + + + + + + + + ''' + + # Construct MIME type email + email_message = MIMEMultipart() + email_message['From'] = email_sender + email_message['To'] = email_reciever + email_message['Subject'] = subject + + # Attach html as MIMEText html content type to the MIME message + email_message.attach(MIMEText(html, "html")) + + email_string = email_message.as_string() + + # ssl for layer of security + context = ssl.create_default_context() + + with smtplib.SMTP_SSL('smtp.gmail.com', 465, context=context) as smtp: + # Login and send email + smtp.login(email_sender, email_password) + smtp.sendmail(email_sender, email_reciever, email_string) \ No newline at end of file diff --git a/pocs-capstone/backend/backend/db/urls.py b/pocs-capstone/backend/backend/db/urls.py new file mode 100644 index 000000000..d655f3ccc --- /dev/null +++ b/pocs-capstone/backend/backend/db/urls.py @@ -0,0 +1,25 @@ +from django.urls import include, path +from rest_framework.routers import DefaultRouter +from .views import * + + +router = DefaultRouter() +router.register(r'tasks',TaskViewSet,basename='tasks') +router.register(r'inventory',InventoryViewSet,basename='inventory') +router.register(r'avatar',AvatarViewSet,basename='avatar') +router.register(r'user-data',NewUserViewSet,basename="user-data") +#router.register(r'canvas-integration',CanvasTaskViewSet,basename="oof") +#router.register(r'users',CustomUserCreate.as_view(),basename="user") +# Wire up our API using automatic URL routing. +# Additionally, we include login URLs for the browsable API. + +app_name = 'db' +#urlpatterns = router.urls + +urlpatterns = [ + path('api/register/',CustomUserCreate.as_view(),name="create_user"), + path('api/delete/',DeleteUserView.as_view(),name="delete_user"), + path('api/logout/blacklist',BlacklistTokenView.as_view(),name="blacklist"), + path('api/canvas/',CanvasView.as_view(),name="pose-canvas-tasks"), + path('api/', include(router.urls)), +] \ No newline at end of file diff --git a/pocs-capstone/backend/backend/db/views.py b/pocs-capstone/backend/backend/db/views.py new file mode 100644 index 000000000..29522d8b0 --- /dev/null +++ b/pocs-capstone/backend/backend/db/views.py @@ -0,0 +1,433 @@ +from django.shortcuts import render +from django.http import HttpResponse +# Create your views here. + +from rest_framework import viewsets +from rest_framework.permissions import AllowAny +from .serializers import * +from .models import * +from .models import NewUser +from rest_framework.response import Response +from rest_framework import status +from rest_framework.views import APIView +from rest_framework.permissions import IsAuthenticated +from rest_framework_simplejwt.tokens import RefreshToken +from db.studybuddyemail import send_email +import db.canvasrequests as canvas +import pprint + +pp = pprint.PrettyPrinter(indent=2) + + +def lololol(userId): + + u = NewUser.objects.get(id=userId) + # u.demo_canvas=False + # u.save() + + canvas_token = u.get_canvas_token() + print(canvas_token) + if canvas_token == "canvastoken": # Here we check for canvas token + return mock_canvas_data(u) + + if canvas_token == "mock1": + return mock_profile_data() + + if canvas_token == "mock2": + return mock_completed_data() + + return canvas.get_all_assignments(canvas_token) + # assignments, status = canvas.get_all_assignments(canvas_token) + + # return assignments, status + + +def mock_canvas_data(user): + + _tasks = [] + + for i in range(1, 3): + _tasks.append({'title': "Homework", + 'due_date': "2023-06-01", + + 'task_type': 'S', + 'task_level': 3, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': None, + 'description': 'Very instructive assignment.', + 'course_id': '000'+str(i), + 'assignment_id': str(i)+'000' + + }) + + for i in range(3, 5): + _tasks.append({'title': "Quiz", + 'due_date': "2023-06-01", + + 'task_type': 'M', + 'task_level': 3, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': None, + 'description': 'Very instructive assignment.', + 'course_id': '000'+str(i), + 'assignment_id': str(i)+'000' + + }) + + _tasks.append({'title': "Project", + 'due_date': "2023-06-01", + + 'task_type': 'C', + 'task_level': 3, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': None, + 'description': 'Very instructive assignment.', + 'course_id': '0005', + 'assignment_id': '5000' + + }) + + if user.demo_canvas == True: + _tasks[0]['completed_date'] = "2023-06-01" + user.demo_canvas = True + user.save() + print(user.demo_canvas) + pp.pprint(_tasks) + return _tasks, 200 + user.demo_canvas = True + user.save() + return _tasks, 200 + + +def mock_profile_data(): + print("HERE") + _tasks = [] + try: + for i in range(1, 50): + + if i%6 == 0: + continue + print("HERE:"+str(i%6 )) + + _tasks.append({'title': "A legitimate task", + 'due_date': "2023-06-01", + + 'task_type': 'S', + 'task_level': i%6, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': None, + 'description': 'Very instructive assignment.', + 'course_id': '010'+str(i), + 'assignment_id': '010'+str(i) + + }) + _tasks.append({'title': "A legitimate task", + 'due_date': "2023-06-01", + + 'task_type': 'M', + 'task_level': i%6, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': None, + 'description': 'Very instructive assignment.', + 'course_id': '020'+str(i), + 'assignment_id': '020'+str(i) + + + }) + _tasks.append({'title': "A legitimate task", + 'due_date': "2023-06-01", + + 'task_type': 'L', + 'task_level': i%6, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': None, + 'description': 'Very instructive assignment.', + + 'course_id': '030'+str(i), + 'assignment_id': '030'+str(i) + }) + _tasks.append({'title': "A legitimate task", + 'due_date': "2023-06-01", + + 'task_type': 'C', + 'task_level': i%6, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': None, + 'description': 'Very instructive assignment.', + 'course_id': '040'+str(i), + 'assignment_id': '040'+str(i) + + }) + except Exception as e: + print(e) + + pp.pprint(_tasks) + return _tasks, 200 + + +def mock_completed_data(): + + _tasks = [] + + for i in range(1, 50): + if i%6 == 0: + continue + print("HERE:"+str(i%6)) + _tasks.append({'title': "A legitimate task", + 'due_date': "2023-06-01", + + 'task_type': 'S', + 'task_level': i%6, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': "2023-06-01", + 'description': 'Very instructive assignment.', + 'course_id': '010'+str(i), + 'assignment_id': '010'+str(i) + + }) + + _tasks.append({'title': "A legitimate task", + 'due_date': "2023-06-01", + + 'task_type': 'M', + 'task_level': i%6, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': "2023-06-01", + 'description': 'Very instructive assignment.', + 'course_id': '020'+str(i), + 'assignment_id': '020'+str(i) + + + }) + _tasks.append({'title': "A legitimate task", + 'due_date': "2023-06-01", + + 'task_type': 'L', + 'task_level': i%6, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': "2023-06-01", + 'description': 'Very instructive assignment.', + + 'course_id': '030'+str(i), + 'assignment_id': '030'+str(i) + }) + _tasks.append({'title': "A legitimate task", + 'due_date': "2023-06-01", + + 'task_type': 'C', + 'task_level': i%6, # TODO - this should be set here! + # 'recurring': 'false', + # 'recurring_time_delta': 0, + 'course_title': 'Capstone', + 'completed_date': "2023-06-01", + 'description': 'Very instructive assignment.', + 'course_id': '040'+str(i), + 'assignment_id': '040'+str(i) + + }) + + pp.pprint(_tasks) + return _tasks, 200 + + +class CustomUserCreate(APIView): + permission_classes = [AllowAny] + + def post(self, request): + registration_serializer = RegisterUserSerializer(data=request.data) + if registration_serializer.is_valid(): + newuser = registration_serializer.save() + if newuser: + try: + send_email(request.data['email']) + except: + print("Oops! Registration email failed to send") + return Response(status=status.HTTP_201_CREATED) + + print(registration_serializer.errors) + errors = registration_serializer.errors + if ('email' in errors.keys()): + return Response("Email is taken", status=status.HTTP_409_CONFLICT) + if ('username' in errors.keys()): + return Response("Username is taken", status=status.HTTP_409_CONFLICT) + return Response(registration_serializer.errors, status=status.HTTP_400_BAD_REQUEST) + + +class BlacklistTokenView(APIView): + permission_classes = [AllowAny] + + def post(self, request): + try: + refresh_token = request.data["refresh_token"] + token = RefreshToken(refresh_token) + token.blacklist() + return Response(status.HTTP_202_ACCEPTED) + except Exception as e: + return Response(e, status=status.HTTP_400_BAD_REQUEST) + + +class CanvasView(APIView): + permission_classes = [IsAuthenticated,] + + def __enter_inventory_item(self, _user, old_date, new_task): + + if ((old_date != None) and (new_task.completed_date != None)): + new_task.completed = True + new_task.save() + return + + if old_date == None: + if new_task.completed_date != None: # first time completed + + new_task.completed = True # The task has officially been completed! + new_task.save() + + try: + obj = Inventory.objects.get( + user=_user, candy_base_type=new_task.task_type, candy_level=new_task.task_level) + updated_quantity = obj.quantity + 1 + obj.quantity = updated_quantity + obj.save() # inventory is updated + + except Inventory.DoesNotExist: + obj = Inventory( + user=_user, candy_base_type=new_task.task_type, candy_level=new_task.task_level, quantity=1) + obj.save() # new inventory item now created + pass + + def get(self, request): + + pp = pprint.PrettyPrinter(indent=4) + test_task = Task.objects.filter(unique_canvas_tag="knownbadtag") + + __user = self.request.user + _user = self.request.user.id + course_data, _status = lololol(_user) + + if _status != 200: + return Response(None, _status) + if course_data == None: # this fixes a rare bug + return Response(None, _status) + + try: + + for x in course_data: + try: + tag = str(_user)+str(x['course_id']) + \ + str(x['assignment_id']) + x['unique_canvas_tag'] = tag + x['user_id'] = _user + + old_task = Task.objects.filter(unique_canvas_tag=tag) + old_date = None + + if (old_task.count()): + old_date = old_task[0].completed_date + else: + pass + + serializer = CanvasSerializer(x) + temp = serializer.data # task data BEFORE update + + obj, created = Task.objects.update_or_create( + unique_canvas_tag=tag, defaults=serializer.data) # TODO validate the dat + + if created: + if obj.completed_date != None: + obj.completed = True + obj.save() + print("CREATED") + else: # operate on object here + old_task = old_task[0] + self.__enter_inventory_item(__user, old_date, obj) + + except Exception as e: + print(e) + + except Exception as e: + print(e) + _status = 500 + + return Response(course_data, _status) + + +class NewUserViewSet(viewsets.ModelViewSet): + permission_classes = [IsAuthenticated,] + serializer_class = UserDataSerializer + + # query tasks by user. + def get_queryset(self): + _user = self.request.user.id + return NewUser.objects.filter(id=_user) + +class DeleteUserView(APIView): + permission_classes = [IsAuthenticated,] + + def delete(self, request): + + try: + user = request.user + user.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + except Exception as e: + print(e) + return Response(status=status.HTTP_400_BAD_REQUEST) + + +class TaskViewSet(viewsets.ModelViewSet): + + serializer_class = TaskSerializer + + def perform_create(self, serializer): + serializer.save(user=self.request.user) + # query tasks by user. + + def get_queryset(self): + _user = self.request.user + return Task.objects.filter(user=_user) + + +class AvatarViewSet(viewsets.ModelViewSet): + + serializer_class = AvatarSerializer + + def perform_create(self, serializer): + serializer.save(user=self.request.user) + # query tasks by user. + + def get_queryset(self): + _user = self.request.user + return Avatar.objects.filter(user=_user) + + +class InventoryViewSet(viewsets.ModelViewSet): + + serializer_class = InventorySerializer + + def perform_create(self, serializer): + serializer.save(user=self.request.user) + # query tasks by user. + + def get_queryset(self): + _user = self.request.user + return Inventory.objects.filter(user=_user) diff --git a/pocs-capstone/backend/db/assignment_reminder_email.py b/pocs-capstone/backend/db/assignment_reminder_email.py new file mode 100644 index 000000000..d744ec985 --- /dev/null +++ b/pocs-capstone/backend/db/assignment_reminder_email.py @@ -0,0 +1,292 @@ +from email.mime.multipart import MIMEMultipart +from email.mime.text import MIMEText +import ssl +import smtplib +import os +from dotenv import load_dotenv, find_dotenv +from datetime import datetime, timedelta, timezone +import requests + + + +def _load_env(): + """Load environment variables from .env.""" + load_dotenv(find_dotenv()) + + +def _get_canvas_headers(): + """Return headers for Canvas API requests.""" + token = os.getenv("CANVAS_API_TOKEN") + if not token: + raise RuntimeError("CANVAS_API_TOKEN not set in .env") + return { + "Authorization": f"Bearer {token}" + } + + +def get_upcoming_canvas_assignments(days_ahead: int = 7): + """ + Fetch assignments due in the next `days_ahead` days from Canvas. + + Returns a list of dicts: + [ + { + "course_name": str, + "course_id": int, + "name": str, + "due_at": datetime, + "html_url": str or None, + "points_possible": float or None, + }, + ... + ] + """ + _load_env() + base_url = os.getenv("CANVAS_BASE_URL") + if not base_url: + raise RuntimeError("CANVAS_BASE_URL not set in .env") + + base_url = base_url.rstrip("/") + + headers = _get_canvas_headers() + + now = datetime.now(timezone.utc) + cutoff = now + timedelta(days=days_ahead) + + assignments = [] + + try: + # Get active courses for the user associated with the token + courses_resp = requests.get( + f"{base_url}/api/v1/courses", + headers=headers, + params={"enrollment_state": "active"} + ) + courses_resp.raise_for_status() + courses = courses_resp.json() + except Exception as e: + print(f"[Canvas] Failed to fetch courses: {e}") + return [] + + for course in courses: + course_id = course.get("id") + course_name = course.get("name", "Unknown Course") + + if not course_id: + continue + + try: + # 'upcoming' bucket focuses on future assignments + assignments_resp = requests.get( + f"{base_url}/api/v1/courses/{course_id}/assignments", + headers=headers, + params={"bucket": "upcoming"} + ) + assignments_resp.raise_for_status() + course_assignments = assignments_resp.json() + except Exception as e: + print(f"[Canvas] Failed to fetch assignments for course {course_id}: {e}") + continue + + for a in course_assignments: + due_at_str = a.get("due_at") + if not due_at_str: + # No due date, skip for reminders + continue + + try: + due_at = datetime.fromisoformat(due_at_str.replace("Z", "+00:00")) + except ValueError: + # If parsing fails, skip this assignment + continue + + if now <= due_at <= cutoff: + assignments.append({ + "course_name": course_name, + "course_id": course_id, + "name": a.get("name", "Untitled Assignment"), + "due_at": due_at, + "html_url": a.get("html_url"), + "points_possible": a.get("points_possible"), + }) + + # Sort by due date + assignments.sort(key=lambda x: x["due_at"]) + return assignments + + +def build_assignments_email_html(assignments, days_ahead: int): + """ + Build an HTML email body listing upcoming assignments. + """ + if not assignments: + return f""" + + +

Upcoming Assignments (next {days_ahead} days)

+

You have no assignments due in the next {days_ahead} days 🎉

+ + + """ + + # Build list items for assignments + items_html = "" + for a in assignments: + due_local = a["due_at"].astimezone().strftime("%b %d, %Y %I:%M %p") + name = a["name"] + course_name = a["course_name"] + points = a["points_possible"] + url = a["html_url"] + + points_text = f"{points} pts" if points is not None else "Points: N/A" + + if url: + name_html = f'{name}' + else: + name_html = name + + items_html += f""" + + + {name_html}
+ Course: {course_name}
+ Due: {due_local}
+ {points_text} + + + """ + + html = f""" + + + + + Upcoming Assignments + + + + + + +
+ + + + + + + +
+

Study Buddy – Assignment Reminders

+
+

+ Here are your assignments due in the next {days_ahead} days: +

+ + {items_html} +
+

+ Tip: Try to knock out the earliest due items first to stay ahead 😎 +

+
+
+ + + """ + return html + + +def send_assignment_reminder_email(email_receiver: str, days_ahead: int = 7): + """ + Fetch upcoming assignments from Canvas and send an email reminder. + + :param email_receiver: Email address to send reminders to. + :param days_ahead: Look-ahead window (in days) for upcoming assignments. + """ + _load_env() + + email_sender = 'productivitypet101@gmail.com' + email_password = os.getenv('GMAIL_PASSWORD') + + if not email_password: + raise RuntimeError("GMAIL_PASSWORD not set in .env") + + try: + assignments = get_upcoming_canvas_assignments(days_ahead=days_ahead) + except Exception as e: + print(f"[Assignments] Failed to fetch assignments: {e}") + return + + subject = f"Study Buddy – Assignments Due in the Next {days_ahead} Days" + html_body = build_assignments_email_html(assignments, days_ahead) + + # Construct MIME type email + email_message = MIMEMultipart() + email_message['From'] = email_sender + email_message['To'] = email_receiver + email_message['Subject'] = subject + email_message.attach(MIMEText(html_body, "html")) + + email_string = email_message.as_string() + + context = ssl.create_default_context() + + try: + with smtplib.SMTP_SSL('smtp.gmail.com', 465, context=context) as smtp: + smtp.login(email_sender, email_password) + smtp.sendmail(email_sender, email_receiver, email_string) + print(f"[Email] Assignment reminder sent to {email_receiver}") + except Exception as e: + print(f"[Email] Failed to send assignment reminder: {e}") + + +##Tests +if __name__ == "__main__": + test_email = "oswaynesmith34@gmail.com" + send_assignment_reminder_email(test_email, days_ahead=7) + +def test_email_with_fake_assignments(): + from datetime import datetime, timezone, timedelta + + fake_assignments = [ + { + "course_name": "Intro to Cybersecurity", + "course_id": 12345, + "name": "Lab 5 – Wireshark Analysis", + "due_at": datetime.now(timezone.utc) + timedelta(days=1), + "html_url": "https://canvas.fake/courses/12345/assignments/1", + "points_possible": 50, + }, + { + "course_name": "Data Structures", + "course_id": 67890, + "name": "Project 2 – Linked Lists", + "due_at": datetime.now(timezone.utc) + timedelta(days=3), + "html_url": "https://canvas.fake/courses/67890/assignments/2", + "points_possible": 100, + }, + ] + + html_body = build_assignments_email_html(fake_assignments, days_ahead=7) + + _load_env() + email_sender = 'productivitypet101@gmail.com' + email_password = os.getenv('GMAIL_PASSWORD') + + email_receiver = "oswaynesmith34@gmail.com" + subject = "Study Buddy – Fake Assignments Test" + + email_message = MIMEMultipart() + email_message['From'] = email_sender + email_message['To'] = email_receiver + email_message['Subject'] = subject + email_message.attach(MIMEText(html_body, "html")) + + email_string = email_message.as_string() + + context = ssl.create_default_context() + with smtplib.SMTP_SSL('smtp.gmail.com', 465, context=context) as smtp: + smtp.login(email_sender, email_password) + smtp.sendmail(email_sender, email_receiver, email_string) + + print("[Email] Fake assignments email sent!") \ No newline at end of file diff --git a/pocs-capstone/backend/db/canvasrequests.py b/pocs-capstone/backend/db/canvasrequests.py index 171bf4a25..e4872e1cb 100644 --- a/pocs-capstone/backend/db/canvasrequests.py +++ b/pocs-capstone/backend/db/canvasrequests.py @@ -2,6 +2,7 @@ import requests import bs4 as bs import pprint +from datetime import datetime, timezone # import re # from dateutil import parser pp = pprint.PrettyPrinter(indent=2) @@ -39,12 +40,41 @@ def get_courses(canvas_token): # later we'll add userId as a parameter if status == 200: course_id_list = [] # a list of all the user's courses (their ids) - + current_date = datetime.now().date() + closest_end_date = None + closest_start_date = None + for course_entry in courses_data: - course = (course_entry['id'], course_entry['name']) - course_id_list.append(course) - - return course_id_list, status + course_end_date = None + if course_entry['end_at'] is not None: + course_end_date = datetime.fromisoformat(course_entry['end_at'][:-1]).date() + if course_end_date >= current_date: + if closest_end_date is None or course_end_date < closest_end_date: + closest_end_date = course_end_date + + if closest_end_date is not None: + for course_entry in courses_data: + course_start_date = None + if course_entry['start_at'] is not None: + course_start_date = datetime.fromisoformat(course_entry['start_at'][:-1]).date() + if course_start_date <= closest_end_date: + if closest_start_date is None or course_start_date > closest_start_date: + closest_start_date = course_start_date + + if closest_start_date is None: + closest_start_date = current_date + + for course_entry in courses_data: + created_date = datetime.fromisoformat(course_entry['created_at'][:-1]).date() + difference = abs((created_date - closest_start_date).days) + difference_to_today = abs((created_date - current_date).days) + if difference < 35 and difference_to_today < 200: + course = (course_entry['id'], course_entry['name']) + course_id_list.append(course) + print(course_id_list) + if len(course_id_list) > 0: + return course_id_list, status + return None, status return None, status @@ -168,36 +198,90 @@ def get_course_info(canvas_token, course_id): course_url = BASE_URL + '/courses/' + str(course_id) return canvas_request(url=course_url, headers=auth_header, params={}) +'''Given an assignment, Detects the type based on Canvas data. Returns a single char code''' + +def detect_task_type(assignment): + + name = (assignment.get('name') or "").lower() + description = (assignment.get('description') or "").lower() + submission_types = assignment.get('submission_types', []) + + # 1. Canvas Quizzes + if assignment.get('quiz_id') is not None or "online_quiz" in submission_types: + return 'Q' # Quiz + + # 2. Keyword matching in title + if any(word in name for word in ['final exam', 'midterm', 'final test', 'exam', 'test', 'mid-term', 'final']): + return 'C' + if 'quiz' in name or 'vsq' in name: + return 'L' + if any(word in name for word in ['homework', 'hw ', 'assignment', 'problem set', 'pset', 'discussion', 'forum']): + return 'M' + + # 3. Check description + if description: + if any(word in description for word in ['final exam', 'midterm', 'final test', 'exam', 'test', 'mid-term', 'final']): + return 'C' + if 'quiz' in description or 'vsq' in description: + return 'L' + + # 4. Default + return 'S' # Most things are homework/assignments + '''Given a course ID and assignment ID, return a dict of assignment information for that particular assignment''' def parse_assignments(assignments, course_title): - tasks = [] + format_string = "%Y-%m-%dT%H:%M:%SZ" + current_UTC = datetime.now(timezone.utc) for a in assignments: submission_details = {} due = a['due_at'] submission_details = a['submission'] + graded = None + + if submission_details.get('grade','Done') is not None: + if submission_details.get('grade','Done') != 'Done': + graded = True + else: + if a['submission_types'] == ['none'] and due == None: + graded = True + else: + graded = False + else: + if a['submission_types'] == ['none'] and due == None: + graded = True + else: + graded = False submitted = submission_details['submitted_at'] if due != None: due = due[0:10] # hack into a string UwU + if submitted != None: submitted = submitted[0:10] # hack into a string UwU + elif submitted == None and graded == True: + if submission_details.get('graded_at', None) is not None: + submitted = submission_details['graded_at'][0:10] + else: + submitted = current_UTC.strftime(format_string)[0:10] + try: description = bs.BeautifulSoup(a['description'], 'lxml').get_text() except Exception as e: description = "" + task_type = detect_task_type(a) + tasks.append({'title': a['name'] or "No title.", 'due_date': due, - - 'task_type': 'S', + 'task_type': task_type, # 'task_level': 1, # TODO - this should be set here! # 'recurring': 'false', # 'recurring_time_delta': 0, @@ -228,5 +312,4 @@ def get_all_assignments(canvas_token): # all_assignments.append(assignment_info) # add each assignment dict from this course to list _assignments = parse_assignments(assignments, course[1]) all_assignments = all_assignments+_assignments - return all_assignments, status diff --git a/pocs-capstone/backend/db/models.py b/pocs-capstone/backend/db/models.py index fa0ac1c2c..e1d31d900 100644 --- a/pocs-capstone/backend/db/models.py +++ b/pocs-capstone/backend/db/models.py @@ -136,7 +136,7 @@ class AvatarType(models.TextChoices): last_feed = models.DateField(default=None) pet_name = models.CharField(max_length=32, default='') flavour_text = models.TextField( - max_length=256, default='Welcome to Study Buddy!', blank=True, null=True) + max_length=256, default='Welcome to Task Pet!', blank=True, null=True) palette = models.IntegerField(default=0) def __str__(self): diff --git a/pocs-capstone/backend/db/studybuddyemail.py b/pocs-capstone/backend/db/studybuddyemail.py index 8d3885884..d9c4ec1df 100644 --- a/pocs-capstone/backend/db/studybuddyemail.py +++ b/pocs-capstone/backend/db/studybuddyemail.py @@ -15,8 +15,8 @@ def send_email(email_reciever): load_dotenv(find_dotenv()) - email_sender = 'studybuddyverify@gmail.com' - email_password = os.getenv('GMAIL_PASSWORD') + email_sender = 'productivitypet101@gmail.com' + email_password = os.getenv('EMAIL_PASSWORD') email_reciever = email_reciever subject = 'Thank you for registering with Study Buddy!' @@ -26,176 +26,176 @@ def send_email(email_reciever): - - - - + + + + - - - - - - - + + + + + + + @@ -215,7 +215,13 @@ def send_email(email_reciever): # ssl for layer of security context = ssl.create_default_context() - with smtplib.SMTP_SSL('smtp.gmail.com', 465, context=context) as smtp: - # Login and send email - smtp.login(email_sender, email_password) - smtp.sendmail(email_sender, email_reciever, email_string) \ No newline at end of file + try: + with smtplib.SMTP_SSL('smtp.gmail.com', 465) as smtp: + # Login and send email + smtp.login(email_sender, email_password) + smtp.sendmail(email_sender, email_reciever, email_string) + except smtplib.SMTPException as e: + print(f"Error sending email: {e}") + except ssl.SSLError as e: + print(f"SSL error: {e}") + \ No newline at end of file diff --git a/pocs-capstone/backend/db/urls.py b/pocs-capstone/backend/db/urls.py index 1eef9049f..d655f3ccc 100644 --- a/pocs-capstone/backend/db/urls.py +++ b/pocs-capstone/backend/db/urls.py @@ -18,6 +18,7 @@ urlpatterns = [ path('api/register/',CustomUserCreate.as_view(),name="create_user"), + path('api/delete/',DeleteUserView.as_view(),name="delete_user"), path('api/logout/blacklist',BlacklistTokenView.as_view(),name="blacklist"), path('api/canvas/',CanvasView.as_view(),name="pose-canvas-tasks"), path('api/', include(router.urls)), diff --git a/pocs-capstone/backend/db/views.py b/pocs-capstone/backend/db/views.py index 11450e89f..8b13aee04 100644 --- a/pocs-capstone/backend/db/views.py +++ b/pocs-capstone/backend/db/views.py @@ -15,6 +15,10 @@ from db.studybuddyemail import send_email import db.canvasrequests as canvas import pprint +from db.studybuddyemail import send_email +from db.assignment_reminder_email import send_assignment_reminder_email +import db.canvasrequests as canvas +import pprint pp = pprint.PrettyPrinter(indent=2) @@ -258,10 +262,20 @@ def post(self, request): if registration_serializer.is_valid(): newuser = registration_serializer.save() if newuser: - try: - send_email(request.data['email']) - except: - print("Oops! Registration email failed to send") + user_email = request.data.get('email') + + if user_email: + try: + send_email(user_email) + except Exception as e: + print(f"Oops! Registration email failed to send: {e}") + + # Canvas assignments reminder email + try: + send_assignment_reminder_email(user_email, days_ahead=7) + except Exception as e: + print(f"Oops! Assignment reminder email failed to send: {e}") + return Response(status=status.HTTP_201_CREATED) print(registration_serializer.errors) @@ -393,6 +407,19 @@ def get_queryset(self): _user = self.request.user return Task.objects.filter(user=_user) +class DeleteUserView(APIView): + permission_classes = [IsAuthenticated,] + + def delete(self, request): + + try: + user = request.user + print("Deleteing user:", user) + user.delete() + return Response(status=status.HTTP_204_NO_CONTENT) + except Exception as e: + print(e) + return Response(status=status.HTTP_400_BAD_REQUEST) class AvatarViewSet(viewsets.ModelViewSet): diff --git a/pocs-capstone/backend/requirements.txt b/pocs-capstone/backend/requirements.txt index d3ffa118b..a22b8bd7a 100644 --- a/pocs-capstone/backend/requirements.txt +++ b/pocs-capstone/backend/requirements.txt @@ -11,7 +11,7 @@ bottle==0.12.23 bottle-websocket==0.2.9 cachelib==0.1.1 cachetools==5.2.1 -certifi==2022.12.7 +certifi==2025.11.12 cffi==1.15.1 chardet==3.0.4 charset-normalizer==3.1.0 @@ -30,8 +30,8 @@ Eel==0.14.0 et-xmlfile==1.1.0 exceptiongroup==1.0.3 future==0.18.3 -gevent==22.10.2 -gevent-websocket==0.10.1 +# gevent==22.10.2 +# gevent-websocket==0.10.1 google-api-core==2.11.0 google-api-python-client==2.73.0 google-auth==2.16.0 @@ -54,7 +54,7 @@ Jinja2==3.1.2 jsonschema==4.17.3 lazy-object-proxy==1.4.3 loguru==0.5.3 -lxml==4.9.2 +lxml==6.0.2 macholib==1.16.2 MarkupSafe==2.1.2 mccabe==0.6.1 @@ -77,9 +77,9 @@ PyJWT==2.6.0 pylint==2.6.0 pyparsing==3.0.9 pyperclip==1.8.1 -PyQt5==5.15.7 -PyQt5-Qt5==5.15.2 -PyQt5-sip==12.11.0 +# PyQt5==5.15.7 +# PyQt5-Qt5==5.15.2 +# PyQt5-sip==12.11.0 pyrsistent==0.19.3 pytest==7.2.0 python-dateutil==2.8.2 diff --git a/pocs-capstone/frontend/package-lock.json b/pocs-capstone/frontend/package-lock.json index 81321c8eb..8a9d18060 100644 --- a/pocs-capstone/frontend/package-lock.json +++ b/pocs-capstone/frontend/package-lock.json @@ -14,8 +14,6 @@ "@fortawesome/fontawesome-svg-core": "^6.3.0", "@fortawesome/free-solid-svg-icons": "^6.3.0", "@fortawesome/react-fontawesome": "^0.2.0", - "@material-ui/core": "^4.12.4", - "@material-ui/icons": "^4.11.3", "@mui/icons-material": "^5.11.16", "@mui/material": "^5.11.10", "@react-hook/window-size": "^3.1.1", @@ -3445,189 +3443,6 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, - "node_modules/@material-ui/core": { - "version": "4.12.4", - "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.4.tgz", - "integrity": "sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ==", - "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.", - "dependencies": { - "@babel/runtime": "^7.4.4", - "@material-ui/styles": "^4.11.5", - "@material-ui/system": "^4.12.2", - "@material-ui/types": "5.1.0", - "@material-ui/utils": "^4.11.3", - "@types/react-transition-group": "^4.2.0", - "clsx": "^1.0.4", - "hoist-non-react-statics": "^3.3.2", - "popper.js": "1.16.1-lts", - "prop-types": "^15.7.2", - "react-is": "^16.8.0 || ^17.0.0", - "react-transition-group": "^4.4.0" - }, - "engines": { - "node": ">=8.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/material-ui" - }, - "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 - } - } - }, - "node_modules/@material-ui/core/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, - "node_modules/@material-ui/icons": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.3.tgz", - "integrity": "sha512-IKHlyx6LDh8n19vzwH5RtHIOHl9Tu90aAAxcbWME6kp4dmvODM3UvOHJeMIDzUbd4muuJKHmlNoBN+mDY4XkBA==", - "dependencies": { - "@babel/runtime": "^7.4.4" - }, - "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 - } - } - }, - "node_modules/@material-ui/styles": { - "version": "4.11.5", - "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", - "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", - "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.", - "dependencies": { - "@babel/runtime": "^7.4.4", - "@emotion/hash": "^0.8.0", - "@material-ui/types": "5.1.0", - "@material-ui/utils": "^4.11.3", - "clsx": "^1.0.4", - "csstype": "^2.5.2", - "hoist-non-react-statics": "^3.3.2", - "jss": "^10.5.1", - "jss-plugin-camel-case": "^10.5.1", - "jss-plugin-default-unit": "^10.5.1", - "jss-plugin-global": "^10.5.1", - "jss-plugin-nested": "^10.5.1", - "jss-plugin-props-sort": "^10.5.1", - "jss-plugin-rule-value-function": "^10.5.1", - "jss-plugin-vendor-prefixer": "^10.5.1", - "prop-types": "^15.7.2" - }, - "engines": { - "node": ">=8.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/material-ui" - }, - "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 - } - } - }, - "node_modules/@material-ui/styles/node_modules/@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" - }, - "node_modules/@material-ui/styles/node_modules/csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" - }, - "node_modules/@material-ui/system": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", - "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", - "dependencies": { - "@babel/runtime": "^7.4.4", - "@material-ui/utils": "^4.11.3", - "csstype": "^2.5.2", - "prop-types": "^15.7.2" - }, - "engines": { - "node": ">=8.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/material-ui" - }, - "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 - } - } - }, - "node_modules/@material-ui/system/node_modules/csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" - }, - "node_modules/@material-ui/types": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", - "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==", - "peerDependencies": { - "@types/react": "*" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, - "node_modules/@material-ui/utils": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", - "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", - "dependencies": { - "@babel/runtime": "^7.4.4", - "prop-types": "^15.7.2", - "react-is": "^16.8.0 || ^17.0.0" - }, - "engines": { - "node": ">=8.0.0" - }, - "peerDependencies": { - "react": "^16.8.0 || ^17.0.0", - "react-dom": "^16.8.0 || ^17.0.0" - } - }, - "node_modules/@material-ui/utils/node_modules/react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - }, "node_modules/@mui/base": { "version": "5.0.0-alpha.119", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.119.tgz", @@ -4458,6 +4273,26 @@ "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.5.0.tgz", "integrity": "sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg==" }, + "node_modules/@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + }, + "engines": { + "node": ">=18" + } + }, "node_modules/@testing-library/jest-dom": { "version": "5.16.5", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", @@ -5564,11 +5399,12 @@ } }, "node_modules/aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", + "license": "Apache-2.0", "dependencies": { - "deep-equal": "^2.0.5" + "dequal": "^2.0.3" } }, "node_modules/array-flatten": { @@ -13667,9 +13503,10 @@ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "node_modules/picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "license": "ISC" }, "node_modules/picomatch": { "version": "2.3.1", @@ -13824,11 +13661,6 @@ "node": ">=4" } }, - "node_modules/popper.js": { - "version": "1.16.1-lts", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", - "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" - }, "node_modules/postcss": { "version": "8.4.21", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", @@ -17585,6 +17417,20 @@ "is-typedarray": "^1.0.0" } }, + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "license": "Apache-2.0", + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -20173,12 +20019,14 @@ "@csstools/postcss-unset-value": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@csstools/postcss-unset-value/-/postcss-unset-value-1.0.2.tgz", - "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==" + "integrity": "sha512-c8J4roPBILnelAsdLr4XOAR/GsTm0GJi4XpcfvoWk3U6KiTCqiFYc63KhRMQQX35jYMp4Ao8Ij9+IZRgMfJp1g==", + "requires": {} }, "@csstools/selector-specificity": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/@csstools/selector-specificity/-/selector-specificity-2.1.1.tgz", - "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==" + "integrity": "sha512-jwx+WCqszn53YHOfvFMJJRd/B2GqkCBt+1MJSG6o5/s8+ytHMvDZXsJgUEWLk12UnLd7HYKac4BYU5i/Ron1Cw==", + "requires": {} }, "@emotion/babel-plugin": { "version": "11.10.6", @@ -20281,7 +20129,8 @@ "@emotion/use-insertion-effect-with-fallbacks": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.0.tgz", - "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==" + "integrity": "sha512-1eEgUGmkaljiBnRMTdksDV1W4kUnmwgp7X9G8B++9GYwl1lUdqSndSriIrTJ0N7LQaoauY9JJ2yhiOYK5+NI4A==", + "requires": {} }, "@emotion/utils": { "version": "1.2.0", @@ -21166,115 +21015,6 @@ "resolved": "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz", "integrity": "sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==" }, - "@material-ui/core": { - "version": "4.12.4", - "resolved": "https://registry.npmjs.org/@material-ui/core/-/core-4.12.4.tgz", - "integrity": "sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ==", - "requires": { - "@babel/runtime": "^7.4.4", - "@material-ui/styles": "^4.11.5", - "@material-ui/system": "^4.12.2", - "@material-ui/types": "5.1.0", - "@material-ui/utils": "^4.11.3", - "@types/react-transition-group": "^4.2.0", - "clsx": "^1.0.4", - "hoist-non-react-statics": "^3.3.2", - "popper.js": "1.16.1-lts", - "prop-types": "^15.7.2", - "react-is": "^16.8.0 || ^17.0.0", - "react-transition-group": "^4.4.0" - }, - "dependencies": { - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - } - } - }, - "@material-ui/icons": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.3.tgz", - "integrity": "sha512-IKHlyx6LDh8n19vzwH5RtHIOHl9Tu90aAAxcbWME6kp4dmvODM3UvOHJeMIDzUbd4muuJKHmlNoBN+mDY4XkBA==", - "requires": { - "@babel/runtime": "^7.4.4" - } - }, - "@material-ui/styles": { - "version": "4.11.5", - "resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz", - "integrity": "sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA==", - "requires": { - "@babel/runtime": "^7.4.4", - "@emotion/hash": "^0.8.0", - "@material-ui/types": "5.1.0", - "@material-ui/utils": "^4.11.3", - "clsx": "^1.0.4", - "csstype": "^2.5.2", - "hoist-non-react-statics": "^3.3.2", - "jss": "^10.5.1", - "jss-plugin-camel-case": "^10.5.1", - "jss-plugin-default-unit": "^10.5.1", - "jss-plugin-global": "^10.5.1", - "jss-plugin-nested": "^10.5.1", - "jss-plugin-props-sort": "^10.5.1", - "jss-plugin-rule-value-function": "^10.5.1", - "jss-plugin-vendor-prefixer": "^10.5.1", - "prop-types": "^15.7.2" - }, - "dependencies": { - "@emotion/hash": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz", - "integrity": "sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow==" - }, - "csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" - } - } - }, - "@material-ui/system": { - "version": "4.12.2", - "resolved": "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz", - "integrity": "sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw==", - "requires": { - "@babel/runtime": "^7.4.4", - "@material-ui/utils": "^4.11.3", - "csstype": "^2.5.2", - "prop-types": "^15.7.2" - }, - "dependencies": { - "csstype": { - "version": "2.6.21", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz", - "integrity": "sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w==" - } - } - }, - "@material-ui/types": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz", - "integrity": "sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A==" - }, - "@material-ui/utils": { - "version": "4.11.3", - "resolved": "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz", - "integrity": "sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg==", - "requires": { - "@babel/runtime": "^7.4.4", - "prop-types": "^15.7.2", - "react-is": "^16.8.0 || ^17.0.0" - }, - "dependencies": { - "react-is": { - "version": "17.0.2", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", - "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" - } - } - }, "@mui/base": { "version": "5.0.0-alpha.119", "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.119.tgz", @@ -21361,7 +21101,8 @@ "@mui/types": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.3.tgz", - "integrity": "sha512-tZ+CQggbe9Ol7e/Fs5RcKwg/woU+o8DCtOnccX6KmbBc7YrfqMYEYuaIcXHuhpT880QwNkZZ3wQwvtlDFA2yOw==" + "integrity": "sha512-tZ+CQggbe9Ol7e/Fs5RcKwg/woU+o8DCtOnccX6KmbBc7YrfqMYEYuaIcXHuhpT880QwNkZZ3wQwvtlDFA2yOw==", + "requires": {} }, "@mui/utils": { "version": "5.11.12", @@ -21484,12 +21225,14 @@ "@react-hook/event": { "version": "1.2.6", "resolved": "https://registry.npmjs.org/@react-hook/event/-/event-1.2.6.tgz", - "integrity": "sha512-JUL5IluaOdn5w5Afpe/puPa1rj8X6udMlQ9dt4hvMuKmTrBS1Ya6sb4sVgvfe2eU4yDuOfAhik8xhbcCekbg9Q==" + "integrity": "sha512-JUL5IluaOdn5w5Afpe/puPa1rj8X6udMlQ9dt4hvMuKmTrBS1Ya6sb4sVgvfe2eU4yDuOfAhik8xhbcCekbg9Q==", + "requires": {} }, "@react-hook/latest": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@react-hook/latest/-/latest-1.0.3.tgz", - "integrity": "sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==" + "integrity": "sha512-dy6duzl+JnAZcDbNTfmaP3xHiKtbXYOaz3G51MGVljh548Y8MWzTr+PHLOfvpypEVW9zwvl+VyKjbWKEVbV1Rg==", + "requires": {} }, "@react-hook/throttle": { "version": "2.2.0", @@ -21761,6 +21504,22 @@ } } }, + "@testing-library/dom": { + "version": "10.4.1", + "resolved": "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz", + "integrity": "sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==", + "peer": true, + "requires": { + "@babel/code-frame": "^7.10.4", + "@babel/runtime": "^7.12.5", + "@types/aria-query": "^5.0.1", + "aria-query": "5.3.0", + "dom-accessibility-api": "^0.5.9", + "lz-string": "^1.5.0", + "picocolors": "1.1.1", + "pretty-format": "^27.0.2" + } + }, "@testing-library/jest-dom": { "version": "5.16.5", "resolved": "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz", @@ -22509,12 +22268,14 @@ "acorn-import-assertions": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/acorn-import-assertions/-/acorn-import-assertions-1.8.0.tgz", - "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==" + "integrity": "sha512-m7VZ3jwz4eK6A4Vtt8Ew1/mNbP24u0FhdyfA7fSvnJR6LMdfOYnmuIrrJAgrYfYJ10F/otaHTtrtrtmHdMNzEw==", + "requires": {} }, "acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", - "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==" + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", + "requires": {} }, "acorn-node": { "version": "1.8.2", @@ -22600,7 +22361,8 @@ "ajv-keywords": { "version": "3.5.2", "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-3.5.2.tgz", - "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==" + "integrity": "sha512-5p6WTN0DdTGVQk6VjcEju19IgaHudalcfabD7yhDGeA6bcQnmL+CpveLJq/3hvfwd1aof6L386Ougkx6RfyMIQ==", + "requires": {} }, "ansi-escapes": { "version": "4.3.2", @@ -22651,11 +22413,11 @@ } }, "aria-query": { - "version": "5.1.3", - "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz", - "integrity": "sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz", + "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "requires": { - "deep-equal": "^2.0.5" + "dequal": "^2.0.3" } }, "array-flatten": { @@ -22867,7 +22629,8 @@ "babel-plugin-named-asset-import": { "version": "0.3.8", "resolved": "https://registry.npmjs.org/babel-plugin-named-asset-import/-/babel-plugin-named-asset-import-0.3.8.tgz", - "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==" + "integrity": "sha512-WXiAc++qo7XcJ1ZnTYGtLxmBCVbddAml3CEXgWaBzNzLNoxtQ8AiGEFDMOhot9XjTCQbvP5E77Fj9Gk924f00Q==", + "requires": {} }, "babel-plugin-polyfill-corejs2": { "version": "0.3.3", @@ -23062,7 +22825,8 @@ "bootstrap": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/bootstrap/-/bootstrap-5.2.3.tgz", - "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==" + "integrity": "sha512-cEKPM+fwb3cT8NzQZYEu4HilJ3anCrWqh3CHAok1p9jXqMPsPTBhU25fBckEJHJ/p+tTxTFTsFQGM+gaHpi3QQ==", + "requires": {} }, "brace-expansion": { "version": "1.1.11", @@ -23543,7 +23307,8 @@ "css-declaration-sorter": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/css-declaration-sorter/-/css-declaration-sorter-6.3.1.tgz", - "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==" + "integrity": "sha512-fBffmak0bPAnyqc/HO8C3n2sHrp9wcqQz6ES9koRF2/mLOVAx9zIQ3Y7R29sYCteTPqMCwns4WYQoCX91Xl3+w==", + "requires": {} }, "css-has-pseudo": { "version": "3.0.4", @@ -23636,7 +23401,8 @@ "css-prefers-color-scheme": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/css-prefers-color-scheme/-/css-prefers-color-scheme-6.0.3.tgz", - "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==" + "integrity": "sha512-4BqMbZksRkJQx2zAjrokiGMd07RqOa2IxIrrN10lyBe9xhn9DEvjUK79J6jkeiv9D9hQFXKb6g1jwU62jziJZA==", + "requires": {} }, "css-select": { "version": "4.3.0", @@ -23749,7 +23515,8 @@ "cssnano-utils": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/cssnano-utils/-/cssnano-utils-3.1.0.tgz", - "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==" + "integrity": "sha512-JQNR19/YZhz4psLX/rQ9M83e3z2Wf/HdJbryzte4a3NSuafyp9w/I4U+hx5C2S9g41qlstH7DEWnZaaj83OuEA==", + "requires": {} }, "csso": { "version": "4.2.0", @@ -24626,7 +24393,8 @@ "eslint-plugin-react-hooks": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/eslint-plugin-react-hooks/-/eslint-plugin-react-hooks-4.6.0.tgz", - "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==" + "integrity": "sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==", + "requires": {} }, "eslint-plugin-testing-library": { "version": "5.10.2", @@ -25660,7 +25428,8 @@ "icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", - "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==" + "integrity": "sha512-soFhflCVWLfRNOPU3iv5Z9VUdT44xFRbzjLsEzSr5AQmgqPMTHdU3PMT1Cf1ssx8fLNJDA1juftYl+PUcv3MqA==", + "requires": {} }, "idb": { "version": "7.1.1", @@ -26944,7 +26713,8 @@ "jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==" + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", + "requires": {} }, "jest-regex-util": { "version": "27.5.1", @@ -28770,9 +28540,9 @@ "integrity": "sha512-7EAHlyLHI56VEIdK57uwHdHKIaAGbnXPiw0yWbarQZOKaKpvUIgW0jWRVLiatnM+XXlSwsanIBH/hzGMJulMow==" }, "picocolors": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz", - "integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==" + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==" }, "picomatch": { "version": "2.3.1", @@ -28880,11 +28650,6 @@ } } }, - "popper.js": { - "version": "1.16.1-lts", - "resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz", - "integrity": "sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA==" - }, "postcss": { "version": "8.4.21", "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz", @@ -28906,7 +28671,8 @@ "postcss-browser-comments": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-browser-comments/-/postcss-browser-comments-4.0.0.tgz", - "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==" + "integrity": "sha512-X9X9/WN3KIvY9+hNERUqX9gncsgBA25XaeR+jshHz2j8+sYyHktHw1JdKuMjeLpGktXidqDhA7b/qm1mrBDmgg==", + "requires": {} }, "postcss-calc": { "version": "8.2.4", @@ -29004,22 +28770,26 @@ "postcss-discard-comments": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/postcss-discard-comments/-/postcss-discard-comments-5.1.2.tgz", - "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==" + "integrity": "sha512-+L8208OVbHVF2UQf1iDmRcbdjJkuBF6IS29yBDSiWUIzpYaAhtNl6JYnYm12FnkeCwQqF5LeklOu6rAqgfBZqQ==", + "requires": {} }, "postcss-discard-duplicates": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-duplicates/-/postcss-discard-duplicates-5.1.0.tgz", - "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==" + "integrity": "sha512-zmX3IoSI2aoenxHV6C7plngHWWhUOV3sP1T8y2ifzxzbtnuhk1EdPwm0S1bIUNaJ2eNbWeGLEwzw8huPD67aQw==", + "requires": {} }, "postcss-discard-empty": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/postcss-discard-empty/-/postcss-discard-empty-5.1.1.tgz", - "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==" + "integrity": "sha512-zPz4WljiSuLWsI0ir4Mcnr4qQQ5e1Ukc3i7UfE2XcrwKK2LIPIqE5jxMRxO6GbI3cv//ztXDsXwEWT3BHOGh3A==", + "requires": {} }, "postcss-discard-overridden": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-discard-overridden/-/postcss-discard-overridden-5.1.0.tgz", - "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==" + "integrity": "sha512-21nOL7RqWR1kasIVdKs8HNqQJhFxLsyRfAnUDm4Fe4t4mCWL9OJiHvlHPjcd8zc5Myu89b/7wZDnOSjFgeWRtw==", + "requires": {} }, "postcss-double-position-gradients": { "version": "3.1.2", @@ -29041,7 +28811,8 @@ "postcss-flexbugs-fixes": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/postcss-flexbugs-fixes/-/postcss-flexbugs-fixes-5.0.2.tgz", - "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==" + "integrity": "sha512-18f9voByak7bTktR2QgDveglpn9DTbBWPUzSOe9g0N4WR/2eSt6Vrcbf0hmspvMI6YWGywz6B9f7jzpFNJJgnQ==", + "requires": {} }, "postcss-focus-visible": { "version": "6.0.4", @@ -29062,12 +28833,14 @@ "postcss-font-variant": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-font-variant/-/postcss-font-variant-5.0.0.tgz", - "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==" + "integrity": "sha512-1fmkBaCALD72CK2a9i468mA/+tr9/1cBxRRMXOUaZqO43oWPR5imcyPjXwuv7PXbCid4ndlP5zWhidQVVa3hmA==", + "requires": {} }, "postcss-gap-properties": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/postcss-gap-properties/-/postcss-gap-properties-3.0.5.tgz", - "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==" + "integrity": "sha512-IuE6gKSdoUNcvkGIqdtjtcMtZIFyXZhmFd5RUlg97iVEvp1BZKV5ngsAjCjrVy+14uhGBQl9tzmi1Qwq4kqVOg==", + "requires": {} }, "postcss-image-set-function": { "version": "4.0.7", @@ -29090,7 +28863,8 @@ "postcss-initial": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/postcss-initial/-/postcss-initial-4.0.1.tgz", - "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==" + "integrity": "sha512-0ueD7rPqX8Pn1xJIjay0AZeIuDoF+V+VvMt/uOnn+4ezUKhZM/NokDeP6DwMNyIoYByuN/94IQnt5FEkaN59xQ==", + "requires": {} }, "postcss-js": { "version": "4.0.1", @@ -29131,12 +28905,14 @@ "postcss-logical": { "version": "5.0.4", "resolved": "https://registry.npmjs.org/postcss-logical/-/postcss-logical-5.0.4.tgz", - "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==" + "integrity": "sha512-RHXxplCeLh9VjinvMrZONq7im4wjWGlRJAqmAVLXyZaXwfDWP73/oq4NdIp+OZwhQUMj0zjqDfM5Fj7qby+B4g==", + "requires": {} }, "postcss-media-minmax": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/postcss-media-minmax/-/postcss-media-minmax-5.0.0.tgz", - "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==" + "integrity": "sha512-yDUvFf9QdFZTuCUg0g0uNSHVlJ5X1lSzDZjPSFaiCWvjgsvu8vEVxtahPrLMinIDEEGnx6cBe6iqdx5YWz08wQ==", + "requires": {} }, "postcss-merge-longhand": { "version": "5.1.7", @@ -29197,7 +28973,8 @@ "postcss-modules-extract-imports": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==" + "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "requires": {} }, "postcss-modules-local-by-default": { "version": "4.0.0", @@ -29255,7 +29032,8 @@ "postcss-normalize-charset": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/postcss-normalize-charset/-/postcss-normalize-charset-5.1.0.tgz", - "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==" + "integrity": "sha512-mSgUJ+pd/ldRGVx26p2wz9dNZ7ji6Pn8VWBajMXFf8jk7vUoSrZ2lt/wZR7DtlZYKesmZI680qjr2CeFF2fbUg==", + "requires": {} }, "postcss-normalize-display-values": { "version": "5.1.0", @@ -29326,7 +29104,8 @@ "postcss-opacity-percentage": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/postcss-opacity-percentage/-/postcss-opacity-percentage-1.1.3.tgz", - "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==" + "integrity": "sha512-An6Ba4pHBiDtyVpSLymUUERMo2cU7s+Obz6BTrS+gxkbnSBNKSuD0AVUc+CpBMrpVPKKfoVz0WQCX+Tnst0i4A==", + "requires": {} }, "postcss-ordered-values": { "version": "5.1.3", @@ -29348,7 +29127,8 @@ "postcss-page-break": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/postcss-page-break/-/postcss-page-break-3.0.4.tgz", - "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==" + "integrity": "sha512-1JGu8oCjVXLa9q9rFTo4MbeeA5FMe00/9C7lN4va606Rdb+HkxXtXsmEDrIraQ11fGz/WvKWa8gMuCKkrXpTsQ==", + "requires": {} }, "postcss-place": { "version": "7.0.5", @@ -29442,7 +29222,8 @@ "postcss-replace-overflow-wrap": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-replace-overflow-wrap/-/postcss-replace-overflow-wrap-4.0.0.tgz", - "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==" + "integrity": "sha512-KmF7SBPphT4gPPcKZc7aDkweHiKEEO8cla/GjcBK+ckKxiZslIu3C4GCRW3DNfL0o7yW7kMQu9xlZ1kXRXLXtw==", + "requires": {} }, "postcss-selector-not": { "version": "6.0.1", @@ -29927,7 +29708,8 @@ "react-onesignal": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/react-onesignal/-/react-onesignal-2.0.4.tgz", - "integrity": "sha512-llZ4PV1+EsWWZDt0BRils6gAxaxMoYP0Z3rlNivQy4xqUaiO1/PcRaIJ6CuzCci/koDsw5IzLOXEHlV4Yn+d9Q==" + "integrity": "sha512-llZ4PV1+EsWWZDt0BRils6gAxaxMoYP0Z3rlNivQy4xqUaiO1/PcRaIJ6CuzCci/koDsw5IzLOXEHlV4Yn+d9Q==", + "requires": {} }, "react-popper": { "version": "2.3.0", @@ -30992,7 +30774,8 @@ "style-loader": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-3.3.1.tgz", - "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==" + "integrity": "sha512-GPcQ+LDJbrcxHORTRes6Jy2sfvK2kS6hpSfI/fXhPt+spVzxF6LJ1dHLN9zIGmVaaP044YKaIatFaufENRiDoQ==", + "requires": {} }, "stylehacks": { "version": "5.1.1", @@ -31460,6 +31243,12 @@ "is-typedarray": "^1.0.0" } }, + "typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "peer": true + }, "unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", @@ -31852,7 +31641,8 @@ "ws": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/ws/-/ws-8.12.1.tgz", - "integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==" + "integrity": "sha512-1qo+M9Ba+xNhPB+YTWUlK6M17brTut5EXbcBaMRN5pH5dFrXz7lzz1ChFSUq3bOUl8yEvSenhHmYUNJxFzdJew==", + "requires": {} } } }, @@ -32286,7 +32076,8 @@ "ws": { "version": "7.5.9", "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", - "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==" + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "requires": {} }, "xml-name-validator": { "version": "3.0.0", diff --git a/pocs-capstone/frontend/package.json b/pocs-capstone/frontend/package.json index c6269c82f..14f5b726e 100644 --- a/pocs-capstone/frontend/package.json +++ b/pocs-capstone/frontend/package.json @@ -9,8 +9,6 @@ "@fortawesome/fontawesome-svg-core": "^6.3.0", "@fortawesome/free-solid-svg-icons": "^6.3.0", "@fortawesome/react-fontawesome": "^0.2.0", - "@material-ui/core": "^4.12.4", - "@material-ui/icons": "^4.11.3", "@mui/icons-material": "^5.11.16", "@mui/material": "^5.11.10", "@react-hook/window-size": "^3.1.1", diff --git a/pocs-capstone/frontend/public/android-chrome-dog-192x192.png b/pocs-capstone/frontend/public/android-chrome-dog-192x192.png new file mode 100644 index 000000000..7475b561a Binary files /dev/null and b/pocs-capstone/frontend/public/android-chrome-dog-192x192.png differ diff --git a/pocs-capstone/frontend/public/android-chrome-dog-512x512.png b/pocs-capstone/frontend/public/android-chrome-dog-512x512.png new file mode 100644 index 000000000..d499d80a7 Binary files /dev/null and b/pocs-capstone/frontend/public/android-chrome-dog-512x512.png differ diff --git a/pocs-capstone/frontend/public/apple-touch-icon-dog.png b/pocs-capstone/frontend/public/apple-touch-icon-dog.png new file mode 100644 index 000000000..5a062007a Binary files /dev/null and b/pocs-capstone/frontend/public/apple-touch-icon-dog.png differ diff --git a/pocs-capstone/frontend/public/brown_dog_logo_8x.png b/pocs-capstone/frontend/public/brown_dog_logo_8x.png new file mode 100644 index 000000000..ffcda4830 Binary files /dev/null and b/pocs-capstone/frontend/public/brown_dog_logo_8x.png differ diff --git a/pocs-capstone/frontend/public/favicon-dog-16x16.png b/pocs-capstone/frontend/public/favicon-dog-16x16.png new file mode 100644 index 000000000..19af036b6 Binary files /dev/null and b/pocs-capstone/frontend/public/favicon-dog-16x16.png differ diff --git a/pocs-capstone/frontend/public/favicon-dog-32x32.png b/pocs-capstone/frontend/public/favicon-dog-32x32.png new file mode 100644 index 000000000..3f907c02b Binary files /dev/null and b/pocs-capstone/frontend/public/favicon-dog-32x32.png differ diff --git a/pocs-capstone/frontend/public/favicon_dog.ico b/pocs-capstone/frontend/public/favicon_dog.ico new file mode 100644 index 000000000..3420378b4 Binary files /dev/null and b/pocs-capstone/frontend/public/favicon_dog.ico differ diff --git a/pocs-capstone/frontend/public/index.html b/pocs-capstone/frontend/public/index.html index bde92a407..e4aea7e9b 100644 --- a/pocs-capstone/frontend/public/index.html +++ b/pocs-capstone/frontend/public/index.html @@ -8,11 +8,11 @@ - + - - - + + + @@ -33,7 +33,7 @@ work correctly both with client-side routing and a non-root public URL. Learn how to configure a non-root public URL by running `npm run build`. --> - Study Buddy + Task Pet diff --git a/pocs-capstone/frontend/src/App.js b/pocs-capstone/frontend/src/App.js index 032e5d5cb..528c445ce 100644 --- a/pocs-capstone/frontend/src/App.js +++ b/pocs-capstone/frontend/src/App.js @@ -24,6 +24,8 @@ import CanvasIntegrationPage from './components/CanvasIntegrationPage.js' import AccountPage from './components/AccountPage' import PetProfPage from './components/PageDisplay/PetProfPage.js' import Tutorial from './components/Tutorial/Tutorial.js' +import DeleteAccount from './components/DeleteAccount.js' +import { Delete } from '@mui/icons-material' function App(){ @@ -40,15 +42,16 @@ function App(){ }> }> }> - }/> - }/> - }/> - }/> - }/> - {/* }/> */} + }/> + }/> + }/> + }/> + }/> + }/> + {/* }/> */} + - diff --git a/pocs-capstone/frontend/src/components/AccountPage.css b/pocs-capstone/frontend/src/components/AccountPage.css index ae6b23811..e0e9c2583 100644 --- a/pocs-capstone/frontend/src/components/AccountPage.css +++ b/pocs-capstone/frontend/src/components/AccountPage.css @@ -21,6 +21,7 @@ display: flex; justify-content: center; align-items: center; + color: rgb(172, 65, 100); } .form-content { diff --git a/pocs-capstone/frontend/src/components/AnimateChoice.css b/pocs-capstone/frontend/src/components/AnimateChoice.css index 18d66a02e..35c03a204 100644 --- a/pocs-capstone/frontend/src/components/AnimateChoice.css +++ b/pocs-capstone/frontend/src/components/AnimateChoice.css @@ -8,9 +8,9 @@ src: url("../fonts/Minecraft.ttf"); } .pet-choice{ - background-color: #ffffff; + background-color: rgb(251, 216, 216); font-family: pixelFont; - background-color:bisque; + background-color: rgb(251, 216, 216); } .AnimateChoice{ display: grid; @@ -53,7 +53,7 @@ .button { cursor: pointer; - border: 1px solid #1a202c; + border: 2px solid rgb(172, 65, 100); padding: 8px; /* width: 5%;*/ margin: 5px; @@ -83,12 +83,12 @@ } .button:hover { - background: #1a202c; + background: rgb(172, 65, 100); color: #ffffff; } .input { - border: 1px solid #1a202c; + border: 2px solid rgb(172, 65, 100); padding: 8px; /* width: 25%; */ border-radius: 3px; @@ -102,7 +102,7 @@ .input:hover{ background-color: rgba(255, 255, 255, 0.45); - box-shadow: 0px 4px 20px 0px rgba(0, 0, 0, 0.05); + box-shadow: 0px 4px 20px 0px rgb(172, 65, 100); } .petsprite-body { @@ -119,10 +119,20 @@ } .pet-selection-carousel .carousel-inner { - width:400px; + width:450px; } .pet-selection-carousel .carousel-inner img { margin: auto; - margin-bottom: 50px; + margin-bottom: 65px; + } + + .choose-card { + border: 6px rgb(172, 65, 100) solid; + background-color: rgb(251, 216, 216); + border-radius: 30px; + display: flex; + justify-content: center; + margin: center; + align-items: center; } \ No newline at end of file diff --git a/pocs-capstone/frontend/src/components/AnimateChoice.js b/pocs-capstone/frontend/src/components/AnimateChoice.js index a383b655c..8aca0ce2d 100644 --- a/pocs-capstone/frontend/src/components/AnimateChoice.js +++ b/pocs-capstone/frontend/src/components/AnimateChoice.js @@ -10,6 +10,8 @@ import { useNavigate } from "react-router-dom"; import { useWindowWidth } from "@react-hook/window-size"; import { faColonSign } from "@fortawesome/free-solid-svg-icons"; import Carousel from "react-bootstrap/Carousel"; +import brown_dog from "../images/brown_dog_scaled_8x.png"; +import white_dog from "../images/white_dog_scaled_8x.png"; import gray_cat from "../images/gray_neutral_scaled_5x_pngcrushed.png"; import orange_cat from "../images/orange_neutral_scaled_5x_pngcrushed.png"; import white_cat from "../images/whitecat_scaled_5x_pngcrushed.png"; @@ -114,7 +116,7 @@ const AnimateChoice = () => { //contains sprite sheets return (
- +

CHOOSE YOUR PET

@@ -131,13 +133,31 @@ const AnimateChoice = () => { interval={null} className="pet-selection-carousel" > + + browndog + + + + whitedog + + orange {/*

*/} {/* */} @@ -151,8 +171,8 @@ const AnimateChoice = () => { src={gray_cat} alt="gray" className="sprite-container" - index={1} - ref={spriteRefs[1]} + index={3} + ref={spriteRefs[3]} > {/* */} {/*
handleSubmit(event, 1)}> @@ -165,8 +185,8 @@ const AnimateChoice = () => { src={white_cat} alt="white" className="sprite-container" - index={2} - ref={spriteRefs[2]} + index={4} + ref={spriteRefs[4]} > @@ -174,8 +194,8 @@ const AnimateChoice = () => { src={tux_cat} alt="tux" className="sprite-container" - index={3} - ref={spriteRefs[3]} + index={5} + ref={spriteRefs[5]} > @@ -183,8 +203,8 @@ const AnimateChoice = () => { src = {pet_rock} alt = "rock" className="sprite-container" - index={4} - ref={spriteRefs[4]}> + index={6} + ref={spriteRefs[6]}> diff --git a/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarPageDeskop.js b/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarPageDeskop.js index 1b6aa57d5..248778b9b 100644 --- a/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarPageDeskop.js +++ b/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarPageDeskop.js @@ -1,8 +1,9 @@ -import {useEffect, useState, useContext} from 'react'; +import { useEffect, useState, useContext } from 'react'; import "./CalendarPageDesktop.css"; -import Calendar from 'react-calendar'; +import Calendar from 'react-calendar'; import useAxiosPrivate from "../../hooks/useAxiosPrivate"; import CalendarTaskList from './CalendarTaskList'; +import GlobalContext from '../../context/GlobalContext'; const CalendarPageDesktop = () => { @@ -10,31 +11,66 @@ const CalendarPageDesktop = () => { const [show, setShow] = useState(false); const [showCreateTask, setShowCreateTask] = useState(false); - const handleClose = () => { - setShowCreateTask(false); + const handlers = useContext(GlobalContext); + const taskList = handlers?.taskList || []; + + console.log("taskList in CalendarPageDesktop: ", taskList); + + const handleClose = () => { + setShowCreateTask(false); setShow(false); } const handleShow = () => setShowCreateTask(true); const myf = (v) => { - + setShow(true); handleShow(); - + setDate(v); } - return( + // Helper function to format a Date object into YYYY-MM-DD string + const getDueDateString = (d) => { + const date = ('0' + (d.getDate())).slice(-2); + const month = ('0' + (d.getMonth() + 1)).slice(-2); + const year = d.getFullYear(); + return year + "-" + month + "-" + date; + } + + // Function to check if a specific day has any tasks + const hasTasksOnDay = (date) => { + return taskList.some(task => { + if (!task.due_date) return false; + const taskDate = new Date(task.due_date.replace(/-/g, '/')); + return ( + taskDate.getFullYear() === date.getFullYear() && + taskDate.getMonth() === date.getMonth() && + taskDate.getDate() === date.getDate() && + !task.completed + ); + }); + }; + + // The tileContent function renders custom content for each tile + const tileContent = ({ date, view }) => { + if (view === 'month' && hasTasksOnDay(date)) { + return
; + } + return null; + }; + + return ( //
- myf(value)} /> + myf(value)} tileContent={tileContent} /> + + {show === true ? : ""} - {show === true ? : ""} -
- + ) } diff --git a/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarPageDesktop.css b/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarPageDesktop.css index c8502bd00..2d1bd86d9 100644 --- a/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarPageDesktop.css +++ b/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarPageDesktop.css @@ -127,4 +127,13 @@ } .react-calendar--selectRange .react-calendar__tile--hover { background-color: #e6e6e6; +} + +.event-dot { + width: 6px; + height: 6px; + background-color: rgb(179, 40, 244); + border-radius: 50%; + margin: 2px auto 0 auto; + display: block; } \ No newline at end of file diff --git a/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarTaskItem.js b/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarTaskItem.js index 3a90e3544..15959c8d0 100644 --- a/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarTaskItem.js +++ b/pocs-capstone/frontend/src/components/CalendarDeskop/CalendarTaskItem.js @@ -37,7 +37,7 @@ const CalendarTaskItem = ({ task, updateTask, deleteTask }) => {
-
Course {task.course_id}
+
Course: {task.course_title}
diff --git a/pocs-capstone/frontend/src/components/CalendarMobile/CalendarPageMobile.css b/pocs-capstone/frontend/src/components/CalendarMobile/CalendarPageMobile.css index f48b15c49..6e3b5ae26 100644 --- a/pocs-capstone/frontend/src/components/CalendarMobile/CalendarPageMobile.css +++ b/pocs-capstone/frontend/src/components/CalendarMobile/CalendarPageMobile.css @@ -62,7 +62,7 @@ .react-calendar__navigation button:enabled:hover, .react-calendar__navigation button:enabled:focus { /* background-color: #e6e6e6; */ - background-color: grey; + background-color: rgb(250, 217, 217); } .react-calendar__navigation button[disabled] { /* background-color: #f0f0f0; */ @@ -113,7 +113,7 @@ .react-calendar__tile:enabled:hover, .react-calendar__tile:enabled:focus { /* background-color: #e6e6e6; */ - background-color: grey; + background-color: rgb(250, 217, 217); } .react-calendar__tile--now { /* background: #ffff76; */ @@ -121,22 +121,22 @@ } .react-calendar__tile--now:enabled:hover, .react-calendar__tile--now:enabled:focus { - background: #ffffa9; + background: rgb(249, 76, 134); } .react-calendar__tile--hasActive { - background: #76baff; + background: palevioletred; } .react-calendar__tile--hasActive:enabled:hover, .react-calendar__tile--hasActive:enabled:focus { - background: #a9d4ff; + background: rgb(249, 76, 134); } .react-calendar__tile--active { - background: #6f48eb; + background: rgb(249, 76, 134); color: white; } .react-calendar__tile--active:enabled:hover, .react-calendar__tile--active:enabled:focus { - background: #1087ff; + background: rgb(249, 76, 134); } .react-calendar--selectRange .react-calendar__tile--hover { background-color: #e6e6e6; diff --git a/pocs-capstone/frontend/src/components/CalendarMobile/CalendarTaskListMobile.js b/pocs-capstone/frontend/src/components/CalendarMobile/CalendarTaskListMobile.js index 15031771c..45e286c80 100644 --- a/pocs-capstone/frontend/src/components/CalendarMobile/CalendarTaskListMobile.js +++ b/pocs-capstone/frontend/src/components/CalendarMobile/CalendarTaskListMobile.js @@ -62,7 +62,6 @@ function CalendarTaskListMobile (props) { "Large": large, "Medium": medium, "Small": small - } return ( diff --git a/pocs-capstone/frontend/src/components/CanvasIntegrationPage.css b/pocs-capstone/frontend/src/components/CanvasIntegrationPage.css index 3e8484c85..224ccee59 100644 --- a/pocs-capstone/frontend/src/components/CanvasIntegrationPage.css +++ b/pocs-capstone/frontend/src/components/CanvasIntegrationPage.css @@ -58,6 +58,9 @@ align-items: stretch; justify-content: flex-start; flex-direction: column; + border: 8px rgb(172, 65, 100) solid; + border-radius: 35px; + padding: 10px; } @media (max-width: 1130px){ diff --git a/pocs-capstone/frontend/src/components/CanvasIntegrationPage.js b/pocs-capstone/frontend/src/components/CanvasIntegrationPage.js index bd302ffcf..0398478c7 100644 --- a/pocs-capstone/frontend/src/components/CanvasIntegrationPage.js +++ b/pocs-capstone/frontend/src/components/CanvasIntegrationPage.js @@ -149,13 +149,13 @@ const CanvasIntegrationPage = () => { {/* */} {/* */}
-

ADD YOUR CANVAS ACCOUNT!

+

ADD YOUR CANVAS ACCOUNT!

{/* {" "} */} {/* */}
- Follow these steps to link your Canvas account with Study Buddy! {" "} + Follow these steps to link your Canvas account with Task Pet! {" "}

@@ -170,7 +170,7 @@ const CanvasIntegrationPage = () => { bottom!

Select "New access token".

+ New access token -

4. Enter a purpose and expiration date (ex. study buddy, and +

4. Enter a purpose and expiration date (ex. task pet, and the end of your semester date).

Select "Generate token", and copy and paste it here!
@@ -190,6 +190,7 @@ const CanvasIntegrationPage = () => { { + + +

Return

+

Decided not to delete your account?

+ +
+
+ ); +} + +export default DeleteAccount; \ No newline at end of file diff --git a/pocs-capstone/frontend/src/components/Footer.css b/pocs-capstone/frontend/src/components/Footer.css index b4388a2a0..10df1a09c 100644 --- a/pocs-capstone/frontend/src/components/Footer.css +++ b/pocs-capstone/frontend/src/components/Footer.css @@ -11,7 +11,7 @@ align-items: center; padding: 1em; - background-color: rgb(22, 22, 22); + background-color: rgb(172, 65, 100); color: #fff; } diff --git a/pocs-capstone/frontend/src/components/Header.css b/pocs-capstone/frontend/src/components/Header.css index 40a0a2f70..87cd72bd8 100644 --- a/pocs-capstone/frontend/src/components/Header.css +++ b/pocs-capstone/frontend/src/components/Header.css @@ -23,7 +23,7 @@ } .header-whole { - background-color: rgb(33, 33, 33); + background-color: rgb(172, 65, 100); } @@ -42,7 +42,7 @@ display: flex; justify-content: center; align-items: center; - background-color: rgb(33, 33, 33); + background-color: rgb(172, 65, 100); } .header-text { @@ -63,7 +63,7 @@ .App-link:hover, .App-link { - color: #6e7172; + color: #ffffff; font-weight: bold; } diff --git a/pocs-capstone/frontend/src/components/Header.js b/pocs-capstone/frontend/src/components/Header.js index 537ff6815..11a02291a 100644 --- a/pocs-capstone/frontend/src/components/Header.js +++ b/pocs-capstone/frontend/src/components/Header.js @@ -1,5 +1,6 @@ import "./Header.css"; -import logo from "../images/orangecat.png"; +//import logo from "../images/orangecat.png"; +import logo from "../images/brown_dog_centered_8x.png"; import usericon from "../images/user_icon.png"; import canvas_bug from "../images/canvas_bug.png"; import { useState, useEffect, useContext } from "react"; @@ -136,10 +137,10 @@ const Header = ({ }) => {
- study buddy logo + productivity pet logo - My Study Buddy + My Pet
@@ -170,6 +171,7 @@ const Header = ({ }) => { Logout + Delete Account
diff --git a/pocs-capstone/frontend/src/components/Inventory/Inventory.css b/pocs-capstone/frontend/src/components/Inventory/Inventory.css index 315eae2e3..c9c6a4815 100644 --- a/pocs-capstone/frontend/src/components/Inventory/Inventory.css +++ b/pocs-capstone/frontend/src/components/Inventory/Inventory.css @@ -47,7 +47,8 @@ img { .inventory-header { font-weight: bold; - font-size: calc(2vw+.4vw); + font-size: calc(0.75vw + 1.1vh); + color: rgb(172, 65, 100); } .inventory-box { diff --git a/pocs-capstone/frontend/src/components/LandingPage.css b/pocs-capstone/frontend/src/components/LandingPage.css index ad78be0fc..795c5758e 100644 --- a/pocs-capstone/frontend/src/components/LandingPage.css +++ b/pocs-capstone/frontend/src/components/LandingPage.css @@ -22,8 +22,10 @@ } -.card { +.landing-card { background-color: rgb(255, 214, 214); + border: 6px rgb(172, 65, 100) solid; + border-radius: 30px; flex: 1 1 auto; width: 0; height: auto; @@ -98,7 +100,7 @@ background: rgb(172, 65, 100); color: #ffffff; border-radius: 10px; - font-size: 20px; + font-size: 30px; transition: all 0.1s ease-in; position: relative; } @@ -130,4 +132,12 @@ .landingbutton:hover { background: #3a0422; color: #ffffff; +} +.titlebox { + border: 8px rgb(172, 65, 100) solid; + padding: 20px; + display: inline-block; + border-radius: 35px; + font-size: 2.5rem; + background-color: rgb(255, 214, 214); } \ No newline at end of file diff --git a/pocs-capstone/frontend/src/components/LandingPage.js b/pocs-capstone/frontend/src/components/LandingPage.js index 2ada6c604..486030cce 100644 --- a/pocs-capstone/frontend/src/components/LandingPage.js +++ b/pocs-capstone/frontend/src/components/LandingPage.js @@ -4,6 +4,7 @@ import { useNavigate } from 'react-router-dom'; import { useState } from 'react'; import { Card } from 'react-bootstrap'; import "./LandingPage.css" +import browndog from '../images/brown_dog_centered_8x.png'; import kittycat from '../images/orangecat.png'; import { useWindowWidth } from "@react-hook/window-size"; @@ -43,15 +44,15 @@ const LandingPage = () => { } return (
-

WELCOME TO STUDY BUDDY!

- cat -
- About +

WELCOME TO TASK PET!

+ dog +
+ About - Study Buddy allows you to take care of a virtual pet to achieve your academic goals! Choose from a variety of pets and colors - of pets to find the buddy who suits you best. By completing tasks, you can receive candies of various sizes to feed your pet. Watch it grow and level up! - Integrate your Canvas account so your pet can help you keep track of your school assignments. With Study Buddy, you get a cute - way to make keeping up with your schoolwork and personal study goals fun! Click on the buttons below to register or log in to see your buddy. + Task Pet allows you to take care of a virtual pet to achieve your academic goals! Choose from a variety of pets + to find the one who suits you best. By completing tasks, you can receive candies of various sizes to feed your pet. Watch it grow and level up! + Integrate your Canvas account so your pet can help you keep track of your school assignments. With Task Pet, you get a cute + way to make keeping up with your schoolwork and personal study goals fun! Click on the buttons below to register or log in to see your pet.
diff --git a/pocs-capstone/frontend/src/components/LoginLogout/Login.js b/pocs-capstone/frontend/src/components/LoginLogout/Login.js index 7e657e489..96cddedd8 100644 --- a/pocs-capstone/frontend/src/components/LoginLogout/Login.js +++ b/pocs-capstone/frontend/src/components/LoginLogout/Login.js @@ -85,7 +85,7 @@ const Login = () => {

{errMsg}

-

Sign-in to visit your buddy!

+

Sign-in to visit your pet!

{ {height < 600 ? ( "" ) : ( -

Welcome to Study Buddy!

+

Welcome to Task Pet!

)} {/*

Welcome to Study Buddy!

*/} @@ -261,15 +261,8 @@ const Register = () => { 8 to 24 characters.
- Must include uppercase and lowercase letters, a number and a - special character. + Must include at least one letter and one number.
- Allowed special characters:{" "} - !{" "} - @{" "} - #{" "} - ${" "} - %

*/} - {/* */} -
-
{task.title}
-
Course: {task.course_title}
- {/*
{task.tags}
*/} -
{task.description}
- {task.due_date ? -
Due {calculateDueDate(task.due_date)}
: - <> - } +
+ + {/* LEFT SIDE – Title, Course, Description, Due Date */} +
+
{task.title}
+
Course: {task.course_title}
+
+ {task.description} +
+ {task.due_date ? ( +
+ Due {calculateDueDate(task.due_date)} +
+ ) : null} +
+ + {/* RIGHT SIDE – Task Type Badge */} +
+ {task.task_type === 'C' && + TEST} + {task.task_type === 'L' && + QUIZ} + {task.task_type === 'M' && + HW} + {(!task.task_type || !['C','L','M'].includes(task.task_type)) && + TASK} +
{ diff --git a/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskList.js b/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskList.js index daaa8b090..9579d99be 100644 --- a/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskList.js +++ b/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskList.js @@ -14,7 +14,35 @@ const TaskList = ({ showAll, filterTags, filterTaskType }) => { } return true } - const showTasks = handlers?.taskList.filter(task => task.completed !== showAll).filter(task => filterTags.every(fT => task.tags?.includes(fT))).filter(taskFilterCondition) + let showTasks = handlers?.taskList.filter(task => { + const now = new Date(); + const due = task.due_date ? new Date(task.due_date) : null; + + if (showAll === 'all') { + if (task.completed) return false; + if (due && due < now) return false; + } + if (showAll === 'completed' && !task.completed) { + return false; + } + if (showAll === 'pastdue') { + if (task.completed) return false; + if (!due) return false; + if (due >= now) return false; + } + + if (!taskFilterCondition(task)) return false; + + if (filterTags.length > 0 && !filterTags.some(fT => task.tags?.includes(fT))) return false; + + return true; + }); + + showTasks = showTasks.sort((a, b) => { + const da = a.due_date ? new Date(a.due_date) : Infinity; + const db = b.due_date ? new Date(b.due_date) : Infinity; + return da - db; + }); const taskListHandlers = { updateTask: handlers.updateTask, @@ -22,14 +50,14 @@ const TaskList = ({ showAll, filterTags, filterTaskType }) => { } // console.log(`TASKS: ${showTasks}`) - + return ( <> { showTasks.length === 0 ? - + No tasks! diff --git a/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskPage.css b/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskPage.css index 69877ba0e..35013eb46 100644 --- a/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskPage.css +++ b/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskPage.css @@ -2,7 +2,7 @@ .to-tabs, .to-tabs .nav-link { background-color: rgba(243, 193, 193, 0); - color: rgb(93, 93, 93); + color: rgb(172, 65, 100); font-size: calc(0.5vw + 1vh); margin: 0px; letter-spacing: 0.5px; @@ -12,14 +12,14 @@ .to-tabs .nav-link.active { background-color: rgba(255, 255, 255, 0); - color: rgb(43, 99, 158); - border-color: rgb(43, 99, 158); + color: rgb(249, 76, 134); + border-color: rgb(249, 76, 134); border-width: 0px 0px 3px 0px; } .to-tabs .nav-link:hover { - color: rgb(43, 99, 158); + color: rgb(249, 76, 134); } /* margin for ACTIVE / COMPLETE Tabs */ @@ -103,7 +103,7 @@ /* the list items in task page */ .task-item.list-group-item { /* border-bottom-width: 3px; */ - border-color: rgb(226, 226, 226); + border-color: rgb(237, 169, 192); border-radius: 0px; border-width: 0px; border-top-width: 0px !important; @@ -139,7 +139,7 @@ } .due-date { - color: rgb(43, 99, 158); + color: rgb(249, 76, 134); } .course-id { @@ -178,7 +178,7 @@ .completed-checkbox input[type="checkbox"]:checked { - background-color: rgba(180, 180, 180, 0.783); + background-color: rgb(249, 76, 134); border-color: rgba(179, 41, 75, 0); } @@ -222,7 +222,7 @@ .noncompleted-checkbox input[type="checkbox"]:hover { background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20'%3e%3cpath fill='none' stroke='%23fff' stroke-linecap='round' stroke-linejoin='round' stroke-width='3' d='m6 10 3 3 6-6'/%3e%3c/svg%3e"); - background-color: rgba(242, 160, 160, 0.827); + background-color: rgb(249, 76, 134); border-color: rgba(179, 41, 75, 0); } diff --git a/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskPage.js b/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskPage.js index 8ed065a47..5582fad7e 100644 --- a/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskPage.js +++ b/pocs-capstone/frontend/src/components/PageDisplay/Task/TaskPage.js @@ -15,7 +15,7 @@ const TaskPage = () => { const [selectedTags, setSelectedTags] = useState([]) const [taskTypes, setTaskTypes] = useState([]) const [showCreateTask, setShowCreateTask] = useState(false); - const [filterTodo, setFilterTodo] = useState(true) + const [filterTodo, setFilterTodo] = useState("all") const handleShow = () => setShowCreateTask(true); @@ -50,15 +50,12 @@ const TaskPage = () => { return selectedTags.find(t => t === tagItem) ? true : false } - const getCheckedType = (tagItem) => { - // if tagItem exists in the selectedTags list, it has been checked off. Return true. - return taskTypes.find(t => t === tagItem) ? true : false - } + const getCheckedType = (tagItem) => {return taskTypes.includes(tagItem)}; const handleTaskCheck = (e, typeItem) => { - if (e.target.checked && !taskTypes.find(type => type === typeItem)) { - setTaskTypes(taskTypes.concat(typeItem)) + if (e.target.checked && !taskTypes.includes(typeItem)) { + setTaskTypes([...taskTypes, typeItem]) } else { setTaskTypes(taskTypes.filter(type => type !== typeItem)) @@ -88,17 +85,17 @@ const TaskPage = () => {
{ - setFilterTodo(f === 'all' ? true : false) + setFilterTodo(f) }} - className="mb-3 to-tabs"> + +
diff --git a/pocs-capstone/frontend/src/components/PetDisplay/PetDisplay.css b/pocs-capstone/frontend/src/components/PetDisplay/PetDisplay.css index 0c9c8ccc4..791d81509 100644 --- a/pocs-capstone/frontend/src/components/PetDisplay/PetDisplay.css +++ b/pocs-capstone/frontend/src/components/PetDisplay/PetDisplay.css @@ -41,6 +41,7 @@ flex: 1 1 auto; width: 0; height: auto; + border: 6px rgb(172, 65, 100) solid; /* top: 0; left: 0; */ @@ -105,7 +106,7 @@ .pbar-exp { display: flex; align-items: center; - background-color: rgb(255, 255, 255); + background-color: rgb(237, 169, 192); } .pet-desc { @@ -115,11 +116,12 @@ } .pet-desc-text { - font-size: calc(0.7vw + 1.4vh); + font-size: calc(0.5vw + 1.4vh); padding: 5px; margin: 5px; text-align: left; height: 15vh; + } /* EXP RATIO */ @@ -146,7 +148,7 @@ } .pd-bg { - background-color: rgb(237, 237, 237); + background-color: rgb(250, 217, 217); } .pet-display .card { @@ -154,6 +156,7 @@ /* border-color: black; */ border-radius: 0px; border-width: 1px; + background-color: rgb(250, 217, 217); /* background-color: black; */ } diff --git a/pocs-capstone/frontend/src/components/PetDisplay/PetDisplay.js b/pocs-capstone/frontend/src/components/PetDisplay/PetDisplay.js index 8d928f29a..bd99eb046 100644 --- a/pocs-capstone/frontend/src/components/PetDisplay/PetDisplay.js +++ b/pocs-capstone/frontend/src/components/PetDisplay/PetDisplay.js @@ -18,11 +18,20 @@ import gray_N_prop from "../../images/propeller_hat.gif"; import gray_H_prop from "../../images/prop_happy.gif"; import gray_S_prop from "../../images/prop_sad.gif"; import dingSound from "../../audio/dingsound.mp3"; +import brown_dog from "../../images/brown_dog_scaled_8x.png"; +import browndog_H_gif from "../../images/brown_dog_happy_scaled_8x.gif"; +import browndog_S_gif from "../../images/brown_dog_sad_scaled_8x.gif"; +import white_dog from "../../images/white_dog_scaled_8x.png"; +import whitedog_H_gif from "../../images/white_dog_happy_scaled_8x.gif"; +import whitedog_S_gif from "../../images/white_dog_sad_scaled_8x.gif"; + import { useContext, useEffect, useRef, useState } from "react"; -import bgimage from "../../images/bg.gif"; +import bgimage from "../../images/background.png"; import ProgressBar from "react-bootstrap/ProgressBar"; +import browndog_click from "../../images/brown_dog_hi_b_scaled_8x.png"; +import whitedog_click from "../../images/white_dog_hi_b_scaled_8x.png"; import orange_click from "../../images/orange_cat_hi_scaled_5x_pngcrushed.png"; import gray_click from "../../images/gray_cat_hi_scaled_5x_pngcrushed.png"; import white_click from "../../images/white_cat_hi_scaled_5x_pngcrushed.png"; @@ -121,15 +130,21 @@ const PetDisplay = () => { // console.log(pet.palette); switch (contextHandler?.avatarInfo.palette) { case 0: - setAvatarImage(orange_click); + setAvatarImage(browndog_click); break; case 1: - setAvatarImage(gray_click); + setAvatarImage(whitedog_click); break; case 2: - setAvatarImage(white_click); + setAvatarImage(orange_click); break; case 3: + setAvatarImage(gray_click); + break; + case 4: + setAvatarImage(white_click); + break; + case 5: setAvatarImage(tux_click); break; } @@ -251,14 +266,14 @@ const PetDisplay = () => { switch (pet.palette) { case 0: if (mood === 'N') { - setAvatarImage(orange_cat); + setAvatarImage(brown_dog); //} else { // setAvatarImage(`orange_${mood}_gif`) // console.log(`orange_${mood}_gif`) } else if (mood === 'H') { - setAvatarImage(orange_H_gif); + setAvatarImage(browndog_H_gif); } else { - setAvatarImage(orange_S_gif); + setAvatarImage(browndog_S_gif); } return case 1: @@ -275,6 +290,28 @@ const PetDisplay = () => { return } if(mood==='N'){ + setAvatarImage(white_dog); + } else if (mood === 'H') { + setAvatarImage(whitedog_H_gif); + // setAvatarImage(`gray_${mood}_gif`) + // console.log(`gray_${mood}_gif`) + } else { + setAvatarImage(whitedog_S_gif); + } + return + case 2: + if (mood === 'N') { + setAvatarImage(orange_cat); + } else if (mood === 'H') { + setAvatarImage(orange_H_gif); + // setAvatarImage(`gray_${mood}_gif`) + // console.log(`gray_${mood}_gif`) + } else { + setAvatarImage(orange_S_gif); + } + return + case 3: + if (mood === 'N') { setAvatarImage(gray_cat); } else if (mood === 'H') { setAvatarImage(gray_H_gif); @@ -284,7 +321,7 @@ const PetDisplay = () => { setAvatarImage(gray_S_gif); } return - case 2: + case 4: if (mood === 'N') { setAvatarImage(white_cat); } else if (mood === 'H') { @@ -295,7 +332,7 @@ const PetDisplay = () => { setAvatarImage(white_S_gif); } return - case 3: + case 5: if (mood === 'N') { setAvatarImage(tux_cat); } else if (mood === 'H') { @@ -306,9 +343,9 @@ const PetDisplay = () => { setAvatarImage(tux_S_gif); } return - case 4: + case 6: setAvatarImage(pet_rock); - return; + return // case 2: // if(mood==='N'){ // setAvatarImage(orange_cat); @@ -407,7 +444,7 @@ const PetDisplay = () => { return (
- + {
{" "}
-
How To Use Study Buddy!
+
How To Use Task Pet!
active tasks
@@ -258,14 +259,14 @@ const Tutorial = () => {
-

-
WELCOME TO STUDY BUDDY!
+

+
WELCOME TO TASK PET!

- cat + browndog
- Thank you! + Thank you! {TUT_TEXT[10]}
diff --git a/pocs-capstone/frontend/src/images/background.png b/pocs-capstone/frontend/src/images/background.png new file mode 100644 index 000000000..d2653e637 Binary files /dev/null and b/pocs-capstone/frontend/src/images/background.png differ diff --git a/pocs-capstone/frontend/src/images/background2.png b/pocs-capstone/frontend/src/images/background2.png new file mode 100644 index 000000000..7f76e5cbc Binary files /dev/null and b/pocs-capstone/frontend/src/images/background2.png differ diff --git a/pocs-capstone/frontend/src/images/background3.png b/pocs-capstone/frontend/src/images/background3.png new file mode 100644 index 000000000..a570e7104 Binary files /dev/null and b/pocs-capstone/frontend/src/images/background3.png differ diff --git a/pocs-capstone/frontend/src/images/brown_dog_centered_8x.png b/pocs-capstone/frontend/src/images/brown_dog_centered_8x.png new file mode 100644 index 000000000..603db5c87 Binary files /dev/null and b/pocs-capstone/frontend/src/images/brown_dog_centered_8x.png differ diff --git a/pocs-capstone/frontend/src/images/brown_dog_happy_scaled_8x.gif b/pocs-capstone/frontend/src/images/brown_dog_happy_scaled_8x.gif new file mode 100644 index 000000000..b44766507 Binary files /dev/null and b/pocs-capstone/frontend/src/images/brown_dog_happy_scaled_8x.gif differ diff --git a/pocs-capstone/frontend/src/images/brown_dog_hi_b_scaled_8x.png b/pocs-capstone/frontend/src/images/brown_dog_hi_b_scaled_8x.png new file mode 100644 index 000000000..f840cd23e Binary files /dev/null and b/pocs-capstone/frontend/src/images/brown_dog_hi_b_scaled_8x.png differ diff --git a/pocs-capstone/frontend/src/images/brown_dog_hi_scaled_8x.png b/pocs-capstone/frontend/src/images/brown_dog_hi_scaled_8x.png new file mode 100644 index 000000000..0a1477597 Binary files /dev/null and b/pocs-capstone/frontend/src/images/brown_dog_hi_scaled_8x.png differ diff --git a/pocs-capstone/frontend/src/images/brown_dog_sad_scaled_8x.gif b/pocs-capstone/frontend/src/images/brown_dog_sad_scaled_8x.gif new file mode 100644 index 000000000..16d07b567 Binary files /dev/null and b/pocs-capstone/frontend/src/images/brown_dog_sad_scaled_8x.gif differ diff --git a/pocs-capstone/frontend/src/images/brown_dog_sad_scaled_8x.png b/pocs-capstone/frontend/src/images/brown_dog_sad_scaled_8x.png new file mode 100644 index 000000000..efc019676 Binary files /dev/null and b/pocs-capstone/frontend/src/images/brown_dog_sad_scaled_8x.png differ diff --git a/pocs-capstone/frontend/src/images/brown_dog_scaled_2x.png b/pocs-capstone/frontend/src/images/brown_dog_scaled_2x.png new file mode 100644 index 000000000..5af0c56ab Binary files /dev/null and b/pocs-capstone/frontend/src/images/brown_dog_scaled_2x.png differ diff --git a/pocs-capstone/frontend/src/images/brown_dog_scaled_4x.png b/pocs-capstone/frontend/src/images/brown_dog_scaled_4x.png new file mode 100644 index 000000000..7f8765d96 Binary files /dev/null and b/pocs-capstone/frontend/src/images/brown_dog_scaled_4x.png differ diff --git a/pocs-capstone/frontend/src/images/brown_dog_scaled_8x.png b/pocs-capstone/frontend/src/images/brown_dog_scaled_8x.png new file mode 100644 index 000000000..b166df699 Binary files /dev/null and b/pocs-capstone/frontend/src/images/brown_dog_scaled_8x.png differ diff --git a/pocs-capstone/frontend/src/images/white_dog_happy_scaled_8x.gif b/pocs-capstone/frontend/src/images/white_dog_happy_scaled_8x.gif new file mode 100644 index 000000000..e292d1795 Binary files /dev/null and b/pocs-capstone/frontend/src/images/white_dog_happy_scaled_8x.gif differ diff --git a/pocs-capstone/frontend/src/images/white_dog_hi_b_scaled_8x.png b/pocs-capstone/frontend/src/images/white_dog_hi_b_scaled_8x.png new file mode 100644 index 000000000..f2c2fe119 Binary files /dev/null and b/pocs-capstone/frontend/src/images/white_dog_hi_b_scaled_8x.png differ diff --git a/pocs-capstone/frontend/src/images/white_dog_hi_scaled_8x.png b/pocs-capstone/frontend/src/images/white_dog_hi_scaled_8x.png new file mode 100644 index 000000000..269cdde89 Binary files /dev/null and b/pocs-capstone/frontend/src/images/white_dog_hi_scaled_8x.png differ diff --git a/pocs-capstone/frontend/src/images/white_dog_sad_scaled_8x.gif b/pocs-capstone/frontend/src/images/white_dog_sad_scaled_8x.gif new file mode 100644 index 000000000..f22ba57e8 Binary files /dev/null and b/pocs-capstone/frontend/src/images/white_dog_sad_scaled_8x.gif differ diff --git a/pocs-capstone/frontend/src/images/white_dog_sad_scaled_8x.png b/pocs-capstone/frontend/src/images/white_dog_sad_scaled_8x.png new file mode 100644 index 000000000..64831ad83 Binary files /dev/null and b/pocs-capstone/frontend/src/images/white_dog_sad_scaled_8x.png differ diff --git a/pocs-capstone/frontend/src/images/white_dog_scaled_8x.png b/pocs-capstone/frontend/src/images/white_dog_scaled_8x.png new file mode 100644 index 000000000..4c778fa57 Binary files /dev/null and b/pocs-capstone/frontend/src/images/white_dog_scaled_8x.png differ diff --git a/pocs-capstone/frontend/yarn.lock b/pocs-capstone/frontend/yarn.lock index 1593c31be..394feca06 100644 --- a/pocs-capstone/frontend/yarn.lock +++ b/pocs-capstone/frontend/yarn.lock @@ -36,7 +36,7 @@ resolved "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.21.0.tgz" integrity sha512-gMuZsmsgxk/ENC3O/fRw5QY8A9/uxQbbCEypnLIiYYc/qVJtEV7ouxC3EllIIwNzMqAQee5tanFabWsUOutS7g== -"@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.3", "@babel/core@^7.16.0", "@babel/core@^7.7.2", "@babel/core@^7.8.0": +"@babel/core@^7.0.0", "@babel/core@^7.0.0-0", "@babel/core@^7.1.0", "@babel/core@^7.11.1", "@babel/core@^7.11.6", "@babel/core@^7.12.0", "@babel/core@^7.12.3", "@babel/core@^7.13.0", "@babel/core@^7.16.0", "@babel/core@^7.4.0-0", "@babel/core@^7.7.2", "@babel/core@^7.8.0", "@babel/core@>=7.11.0": version "7.21.0" resolved "https://registry.npmjs.org/@babel/core/-/core-7.21.0.tgz" integrity sha512-PuxUbxcW6ZYe656yL3EAhpy7qXKq0DmYsrJLpbB8XrsCP9Nm+XCg9XFMb5vIDliPD7+U/+M+QJlH17XOcB7eXA== @@ -499,7 +499,7 @@ dependencies: "@babel/helper-plugin-utils" "^7.8.3" -"@babel/plugin-syntax-flow@^7.18.6": +"@babel/plugin-syntax-flow@^7.14.5", "@babel/plugin-syntax-flow@^7.18.6": version "7.18.6" resolved "https://registry.npmjs.org/@babel/plugin-syntax-flow/-/plugin-syntax-flow-7.18.6.tgz" integrity sha512-LUbR+KNTBWCUAqRG9ex5Gnzu2IOkt8jRJbHHXFT9q+L9zm7M/QQbEqXyw1n1pohYvOyWC8CjeyjrSaIwiYjK7A== @@ -811,7 +811,7 @@ dependencies: "@babel/plugin-transform-react-jsx" "^7.18.6" -"@babel/plugin-transform-react-jsx@^7.18.6": +"@babel/plugin-transform-react-jsx@^7.14.9", "@babel/plugin-transform-react-jsx@^7.18.6": version "7.21.0" resolved "https://registry.npmjs.org/@babel/plugin-transform-react-jsx/-/plugin-transform-react-jsx-7.21.0.tgz" integrity sha512-6OAWljMvQrZjR2DaNhVfRz6dkCAVV+ymcLUmaf8bccGOHn2v5rHJK3tTpij0BuhdYWP4LLaqj5lwcdlpAAPuvg== @@ -1035,7 +1035,7 @@ resolved "https://registry.npmjs.org/@babel/regjsgen/-/regjsgen-0.8.0.tgz" integrity sha512-x/rqGMdzj+fWZvCOYForTghzbtqPDZ5gPwaoNGHdgDfF2QA/XZbCBp4Moo5scrkAMPhB7z26XM/AaHuIJdgauA== -"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.4.4", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.11.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.3", "@babel/runtime@^7.20.7", "@babel/runtime@^7.21.0", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.3", "@babel/runtime@^7.8.3", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": version "7.21.0" resolved "https://registry.npmjs.org/@babel/runtime/-/runtime-7.21.0.tgz" integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== @@ -1220,11 +1220,6 @@ "@emotion/weak-memoize" "^0.3.0" stylis "4.1.3" -"@emotion/hash@^0.8.0": - version "0.8.0" - resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.8.0.tgz" - integrity sha512-kBJtf7PH6aWwZ6fka3zQ0p6SBYzx4fl1LoZXE2RrnYST9Xljm7WfKJrU4g/Xr3Beg72MLrp1AWNUmuYJTL7Cow== - "@emotion/hash@^0.9.0": version "0.9.0" resolved "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.0.tgz" @@ -1254,7 +1249,7 @@ resolved "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.7.1.tgz" integrity sha512-Qv4LTqO11jepd5Qmlp3M1YEjBumoTHcHFdgPTQ+sFlIL5myi/7xu/POwP7IRu6odBdmLXdtIs1D6TuW6kbwbbg== -"@emotion/react@^11.10.6": +"@emotion/react@^11.0.0-rc.0", "@emotion/react@^11.10.6", "@emotion/react@^11.4.1", "@emotion/react@^11.5.0": version "11.10.6" resolved "https://registry.npmjs.org/@emotion/react/-/react-11.10.6.tgz" integrity sha512-6HT8jBmcSkfzO7mc+N1L9uwvOnlcGoix8Zn7srt+9ga0MjREo6lRpuVX0kzo6Jp6oTqDhREOFsygN6Ew4fEQbw== @@ -1284,7 +1279,7 @@ resolved "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.1.tgz" integrity sha512-zxRBwl93sHMsOj4zs+OslQKg/uhF38MB+OMKoCrVuS0nyTkqnau+BM3WGEoOptg9Oz45T/aIGs1qbVAsEFo3nA== -"@emotion/styled@^11.10.6": +"@emotion/styled@^11.10.6", "@emotion/styled@^11.3.0": version "11.10.6" resolved "https://registry.npmjs.org/@emotion/styled/-/styled-11.10.6.tgz" integrity sha512-OXtBzOmDSJo5Q0AFemHCfl+bUueT8BIcPSxu0EGTpGk6DmI5dnhSzQANm1e1ze0YZL7TDyAyy6s/b/zmGOS3Og== @@ -1346,7 +1341,7 @@ resolved "https://registry.npmjs.org/@fortawesome/fontawesome-common-types/-/fontawesome-common-types-6.3.0.tgz" integrity sha512-4BC1NMoacEBzSXRwKjZ/X/gmnbp/HU5Qqat7E8xqorUtBFZS+bwfGH5/wqOC2K6GV0rgEobp3OjGRMa5fK9pFg== -"@fortawesome/fontawesome-svg-core@^6.3.0": +"@fortawesome/fontawesome-svg-core@^6.3.0", "@fortawesome/fontawesome-svg-core@~1 || ~6": version "6.3.0" resolved "https://registry.npmjs.org/@fortawesome/fontawesome-svg-core/-/fontawesome-svg-core-6.3.0.tgz" integrity sha512-uz9YifyKlixV6AcKlOX8WNdtF7l6nakGyLYxYaCa823bEBqyj/U2ssqtctO38itNEwXb8/lMzjdoJ+aaJuOdrw== @@ -1721,77 +1716,6 @@ resolved "https://registry.npmjs.org/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@material-ui/core@^4.12.4": - version "4.12.4" - resolved "https://registry.npmjs.org/@material-ui/core/-/core-4.12.4.tgz" - integrity sha512-tr7xekNlM9LjA6pagJmL8QCgZXaubWUwkJnoYcMKd4gw/t4XiyvnTkjdGrUVicyB2BsdaAv1tvow45bPM4sSwQ== - dependencies: - "@babel/runtime" "^7.4.4" - "@material-ui/styles" "^4.11.5" - "@material-ui/system" "^4.12.2" - "@material-ui/types" "5.1.0" - "@material-ui/utils" "^4.11.3" - "@types/react-transition-group" "^4.2.0" - clsx "^1.0.4" - hoist-non-react-statics "^3.3.2" - popper.js "1.16.1-lts" - prop-types "^15.7.2" - react-is "^16.8.0 || ^17.0.0" - react-transition-group "^4.4.0" - -"@material-ui/icons@^4.11.3": - version "4.11.3" - resolved "https://registry.npmjs.org/@material-ui/icons/-/icons-4.11.3.tgz" - integrity sha512-IKHlyx6LDh8n19vzwH5RtHIOHl9Tu90aAAxcbWME6kp4dmvODM3UvOHJeMIDzUbd4muuJKHmlNoBN+mDY4XkBA== - dependencies: - "@babel/runtime" "^7.4.4" - -"@material-ui/styles@^4.11.5": - version "4.11.5" - resolved "https://registry.npmjs.org/@material-ui/styles/-/styles-4.11.5.tgz" - integrity sha512-o/41ot5JJiUsIETME9wVLAJrmIWL3j0R0Bj2kCOLbSfqEkKf0fmaPt+5vtblUh5eXr2S+J/8J3DaCb10+CzPGA== - dependencies: - "@babel/runtime" "^7.4.4" - "@emotion/hash" "^0.8.0" - "@material-ui/types" "5.1.0" - "@material-ui/utils" "^4.11.3" - clsx "^1.0.4" - csstype "^2.5.2" - hoist-non-react-statics "^3.3.2" - jss "^10.5.1" - jss-plugin-camel-case "^10.5.1" - jss-plugin-default-unit "^10.5.1" - jss-plugin-global "^10.5.1" - jss-plugin-nested "^10.5.1" - jss-plugin-props-sort "^10.5.1" - jss-plugin-rule-value-function "^10.5.1" - jss-plugin-vendor-prefixer "^10.5.1" - prop-types "^15.7.2" - -"@material-ui/system@^4.12.2": - version "4.12.2" - resolved "https://registry.npmjs.org/@material-ui/system/-/system-4.12.2.tgz" - integrity sha512-6CSKu2MtmiJgcCGf6nBQpM8fLkuB9F55EKfbdTC80NND5wpTmKzwdhLYLH3zL4cLlK0gVaaltW7/wMuyTnN0Lw== - dependencies: - "@babel/runtime" "^7.4.4" - "@material-ui/utils" "^4.11.3" - csstype "^2.5.2" - prop-types "^15.7.2" - -"@material-ui/types@5.1.0": - version "5.1.0" - resolved "https://registry.npmjs.org/@material-ui/types/-/types-5.1.0.tgz" - integrity sha512-7cqRjrY50b8QzRSYyhSpx4WRw2YuO0KKIGQEVk5J8uoz2BanawykgZGoWEqKm7pVIbzFDN0SpPcVV4IhOFkl8A== - -"@material-ui/utils@^4.11.3": - version "4.11.3" - resolved "https://registry.npmjs.org/@material-ui/utils/-/utils-4.11.3.tgz" - integrity sha512-ZuQPV4rBK/V1j2dIkSSEcH5uT6AaHuKWFfotADHsC0wVL1NLd2WkFCm4ZZbX33iO4ydl6V0GPngKm8HZQ2oujg== - dependencies: - "@babel/runtime" "^7.4.4" - prop-types "^15.7.2" - react-is "^16.8.0 || ^17.0.0" - "@mui/base@5.0.0-alpha.119": version "5.0.0-alpha.119" resolved "https://registry.npmjs.org/@mui/base/-/base-5.0.0-alpha.119.tgz" @@ -1818,7 +1742,7 @@ dependencies: "@babel/runtime" "^7.21.0" -"@mui/material@^5.11.10": +"@mui/material@^5.0.0", "@mui/material@^5.11.10": version "5.11.12" resolved "https://registry.npmjs.org/@mui/material/-/material-5.11.12.tgz" integrity sha512-M6BiIeJjySeEzWeiFJQ9pIjJy6mx5mHPWeMT99wjQdAmA2GxCQhE9A0fh6jQP4jMmYzxhOIhjsGcp0vSdpseXg== @@ -1928,7 +1852,7 @@ schema-utils "^3.0.0" source-map "^0.7.3" -"@popperjs/core@^2.11.6", "@popperjs/core@^2.6.0": +"@popperjs/core@^2.0.0", "@popperjs/core@^2.11.6", "@popperjs/core@^2.6.0": version "2.11.6" resolved "https://registry.npmjs.org/@popperjs/core/-/core-2.11.6.tgz" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== @@ -2215,6 +2139,20 @@ lz-string "^1.4.4" pretty-format "^27.0.2" +"@testing-library/dom@>=7.21.4": + version "10.4.1" + resolved "https://registry.npmjs.org/@testing-library/dom/-/dom-10.4.1.tgz" + integrity sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg== + dependencies: + "@babel/code-frame" "^7.10.4" + "@babel/runtime" "^7.12.5" + "@types/aria-query" "^5.0.1" + aria-query "5.3.0" + dom-accessibility-api "^0.5.9" + lz-string "^1.5.0" + picocolors "1.1.1" + pretty-format "^27.0.2" + "@testing-library/jest-dom@^5.16.5": version "5.16.5" resolved "https://registry.npmjs.org/@testing-library/jest-dom/-/jest-dom-5.16.5.tgz" @@ -2261,7 +2199,7 @@ resolved "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz" integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q== -"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14": +"@types/babel__core@^7.0.0", "@types/babel__core@^7.1.14", "@types/babel__core@^7.1.9": version "7.20.0" resolved "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.0.tgz" integrity sha512-+n8dL/9GWblDO0iU6eZAwEIJVr5DWigtle+Q6HLOrh/pdbXOhOtqzq8VPPE2zvNJzSKY4vH/z3iT3tn0A3ypiQ== @@ -2447,7 +2385,7 @@ resolved "https://registry.npmjs.org/@types/mime/-/mime-3.0.1.tgz" integrity sha512-Y4XFY5VJAuw0FgAqPNd6NNoV44jbq9Bz2L7Rh/J6jLTiHBSBJa9fxqQIvkIld4GsoDOcCbvzOUAbLPsSKKg+uA== -"@types/node@*": +"@types/node@*", "@types/node@>= 12": version "18.15.0" resolved "https://registry.npmjs.org/@types/node/-/node-18.15.0.tgz" integrity sha512-z6nr0TTEOBGkzLGmbypWOGnpSpSIBorEhC4L+4HeQ2iezKCi4f77kyslRwvHeNitymGQ+oFyIWGP96l/DPSV9w== @@ -2496,14 +2434,14 @@ dependencies: "@types/react" "*" -"@types/react-transition-group@^4.2.0", "@types/react-transition-group@^4.4.4", "@types/react-transition-group@^4.4.5": +"@types/react-transition-group@^4.4.4", "@types/react-transition-group@^4.4.5": version "4.4.5" resolved "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.5.tgz" integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA== dependencies: "@types/react" "*" -"@types/react@*", "@types/react@>=16.9.11": +"@types/react@*", "@types/react@^17.0.0 || ^18.0.0", "@types/react@>= 16", "@types/react@>=16.14.8", "@types/react@>=16.9.11": version "18.0.28" resolved "https://registry.npmjs.org/@types/react/-/react-18.0.28.tgz" integrity sha512-RD0ivG1kEztNBdoAK7lekI9M+azSnitIn85h4iOiaLjaTrMjzslhaqCGaI4IyCJ1RljWiLCEu4jyrLLgqxBTew== @@ -2604,7 +2542,7 @@ dependencies: "@types/yargs-parser" "*" -"@typescript-eslint/eslint-plugin@^5.5.0": +"@typescript-eslint/eslint-plugin@^4.0.0 || ^5.0.0", "@typescript-eslint/eslint-plugin@^5.5.0": version "5.54.1" resolved "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.54.1.tgz" integrity sha512-a2RQAkosH3d3ZIV08s3DcL/mcGc2M/UC528VkPULFxR9VnVPT8pBu0IyBAJJmVsCmhVfwQX1v6q+QGnmSe1bew== @@ -2627,7 +2565,7 @@ dependencies: "@typescript-eslint/utils" "5.54.1" -"@typescript-eslint/parser@^5.5.0": +"@typescript-eslint/parser@^5.0.0", "@typescript-eslint/parser@^5.5.0": version "5.54.1" resolved "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.54.1.tgz" integrity sha512-8zaIXJp/nG9Ff9vQNh7TI+C3nA6q6iIsGJ4B4L6MhZ7mHnTMR4YP5vp2xydmFXIy8rpyIVbNAG44871LMt6ujg== @@ -2876,6 +2814,11 @@ acorn-walk@^7.0.0, acorn-walk@^7.1.1: resolved "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz" integrity sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA== +"acorn@^6.0.0 || ^7.0.0 || ^8.0.0", acorn@^8, acorn@^8.2.4, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: + version "8.8.2" + resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz" + integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== + acorn@^7.0.0: version "7.4.1" resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" @@ -2886,11 +2829,6 @@ acorn@^7.1.1: resolved "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz" integrity sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A== -acorn@^8.2.4, acorn@^8.5.0, acorn@^8.7.1, acorn@^8.8.0: - version "8.8.2" - resolved "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz" - integrity sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw== - address@^1.0.1, address@^1.1.2: version "1.2.2" resolved "https://registry.npmjs.org/address/-/address-1.2.2.tgz" @@ -2930,7 +2868,7 @@ ajv-keywords@^5.0.0: dependencies: fast-deep-equal "^3.1.3" -ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5: +ajv@^6.10.0, ajv@^6.12.2, ajv@^6.12.4, ajv@^6.12.5, ajv@^6.9.1: version "6.12.6" resolved "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz" integrity sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g== @@ -2950,7 +2888,7 @@ ajv@^8.0.0: require-from-string "^2.0.2" uri-js "^4.2.2" -ajv@^8.6.0: +ajv@^8.6.0, ajv@>=8: version "8.12.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -2960,7 +2898,7 @@ ajv@^8.6.0: require-from-string "^2.0.2" uri-js "^4.2.2" -ajv@^8.8.0: +ajv@^8.8.0, ajv@^8.8.2: version "8.12.0" resolved "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz" integrity sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA== @@ -3036,12 +2974,12 @@ argparse@^2.0.1: resolved "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz" integrity sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q== -aria-query@^5.0.0, aria-query@^5.1.3: - version "5.1.3" - resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.1.3.tgz" - integrity sha512-R5iJ5lkuHybztUfuOAznmboyjWq8O6sqNqtK7CLOqdydi54VNbORp49mb14KbWgG1QD3JFO9hJdZ+y4KutfdOQ== +aria-query@^5.0.0, aria-query@^5.1.3, aria-query@5.3.0: + version "5.3.0" + resolved "https://registry.npmjs.org/aria-query/-/aria-query-5.3.0.tgz" + integrity sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A== dependencies: - deep-equal "^2.0.5" + dequal "^2.0.3" array-flatten@^2.1.2: version "2.1.2" @@ -3463,7 +3401,7 @@ browser-process-hrtime@^1.0.0: resolved "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz" integrity sha512-9o5UecI3GhkpM6DrXr69PblIuWxPKk9Y0jHBRhdocZ2y7YECBFCsHm79Pr3OyR2AvjhDkabFJaDJMYRazHgsow== -browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5: +browserslist@^4.0.0, browserslist@^4.14.5, browserslist@^4.18.1, browserslist@^4.21.3, browserslist@^4.21.4, browserslist@^4.21.5, "browserslist@>= 4", "browserslist@>= 4.21.0", browserslist@>=4: version "4.21.5" resolved "https://registry.npmjs.org/browserslist/-/browserslist-4.21.5.tgz" integrity sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w== @@ -3656,7 +3594,7 @@ cliui@^7.0.2: strip-ansi "^6.0.0" wrap-ansi "^7.0.0" -clsx@^1.0.4, clsx@^1.2.1: +clsx@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/clsx/-/clsx-1.2.1.tgz" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== @@ -4091,11 +4029,6 @@ cssstyle@^2.3.0: dependencies: cssom "~0.3.6" -csstype@^2.5.2: - version "2.6.21" - resolved "https://registry.npmjs.org/csstype/-/csstype-2.6.21.tgz" - integrity sha512-Z1PhmomIfypOpoMjRQB70jfvy/wxT50qW08YXO5lMIJkrdq4yOTR+AW7FqutScmB9NkLwxo+jU+kZLbofZZq/w== - csstype@^3.0.2, csstype@^3.1.1: version "3.1.1" resolved "https://registry.npmjs.org/csstype/-/csstype-3.1.1.tgz" @@ -4815,7 +4748,7 @@ eslint-webpack-plugin@^3.1.1: normalize-path "^3.0.0" schema-utils "^4.0.0" -eslint@^8.3.0: +eslint@*, "eslint@^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8", "eslint@^3 || ^4 || ^5 || ^6 || ^7 || ^8", "eslint@^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0", "eslint@^6.0.0 || ^7.0.0 || ^8.0.0", "eslint@^7.0.0 || ^8.0.0", "eslint@^7.5.0 || ^8.0.0", eslint@^8.0.0, eslint@^8.1.0, eslint@^8.3.0, "eslint@>= 6", eslint@>=5: version "8.35.0" resolved "https://registry.npmjs.org/eslint/-/eslint-8.35.0.tgz" integrity sha512-BxAf1fVL7w+JLRQhWl2pzGeSiGqbWumV4WNvc9Rhp6tiCtm4oHnyPBSEtMGZwrQgudFQ+otqzWoPB7x+hxoWsw== @@ -6430,7 +6363,7 @@ jest-resolve-dependencies@^27.5.1: jest-regex-util "^27.5.1" jest-snapshot "^27.5.1" -jest-resolve@^27.4.2, jest-resolve@^27.5.1: +jest-resolve@*, jest-resolve@^27.4.2, jest-resolve@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest-resolve/-/jest-resolve-27.5.1.tgz" integrity sha512-FFDy8/9E6CV83IMbDpcjOhumAQPDyETnU2KZ1O98DwTnz8AOBsW/Xv3GySr1mOZdItLR+zDZ7I/UdTFbgSOVCw== @@ -6662,7 +6595,7 @@ jest-worker@^29.5.0: merge-stream "^2.0.0" supports-color "^8.0.0" -jest@^27.4.3, jest@^27.5.1: +"jest@^27.0.0 || ^28.0.0", jest@^27.4.3, jest@^27.5.1: version "27.5.1" resolved "https://registry.npmjs.org/jest/-/jest-27.5.1.tgz" integrity sha512-Yn0mADZB89zTtjkPJEXwrac3LHudkQMR+Paqa8uxJHCBr9agxztUifWCyiYrjhMPBoUVBjyny0I7XH6ozDr7QQ== @@ -6800,7 +6733,7 @@ jsonpointer@^5.0.0: resolved "https://registry.npmjs.org/jsonpointer/-/jsonpointer-5.0.1.tgz" integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== -jss-plugin-camel-case@^10.5.1, jss-plugin-camel-case@10.10.0: +jss-plugin-camel-case@10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss-plugin-camel-case/-/jss-plugin-camel-case-10.10.0.tgz" integrity sha512-z+HETfj5IYgFxh1wJnUAU8jByI48ED+v0fuTuhKrPR+pRBYS2EDwbusU8aFOpCdYhtRc9zhN+PJ7iNE8pAWyPw== @@ -6818,7 +6751,7 @@ jss-plugin-compose@10.10.0: jss "10.10.0" tiny-warning "^1.0.2" -jss-plugin-default-unit@^10.5.1, jss-plugin-default-unit@10.10.0: +jss-plugin-default-unit@10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss-plugin-default-unit/-/jss-plugin-default-unit-10.10.0.tgz" integrity sha512-SvpajxIECi4JDUbGLefvNckmI+c2VWmP43qnEy/0eiwzRUsafg5DVSIWSzZe4d2vFX1u9nRDP46WCFV/PXVBGQ== @@ -6843,7 +6776,7 @@ jss-plugin-extend@10.10.0: jss "10.10.0" tiny-warning "^1.0.2" -jss-plugin-global@^10.5.1, jss-plugin-global@10.10.0: +jss-plugin-global@10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss-plugin-global/-/jss-plugin-global-10.10.0.tgz" integrity sha512-icXEYbMufiNuWfuazLeN+BNJO16Ge88OcXU5ZDC2vLqElmMybA31Wi7lZ3lf+vgufRocvPj8443irhYRgWxP+A== @@ -6851,7 +6784,7 @@ jss-plugin-global@^10.5.1, jss-plugin-global@10.10.0: "@babel/runtime" "^7.3.1" jss "10.10.0" -jss-plugin-nested@^10.5.1, jss-plugin-nested@10.10.0: +jss-plugin-nested@10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss-plugin-nested/-/jss-plugin-nested-10.10.0.tgz" integrity sha512-9R4JHxxGgiZhurDo3q7LdIiDEgtA1bTGzAbhSPyIOWb7ZubrjQe8acwhEQ6OEKydzpl8XHMtTnEwHXCARLYqYA== @@ -6860,7 +6793,7 @@ jss-plugin-nested@^10.5.1, jss-plugin-nested@10.10.0: jss "10.10.0" tiny-warning "^1.0.2" -jss-plugin-props-sort@^10.5.1, jss-plugin-props-sort@10.10.0: +jss-plugin-props-sort@10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss-plugin-props-sort/-/jss-plugin-props-sort-10.10.0.tgz" integrity sha512-5VNJvQJbnq/vRfje6uZLe/FyaOpzP/IH1LP+0fr88QamVrGJa0hpRRyAa0ea4U/3LcorJfBFVyC4yN2QC73lJg== @@ -6868,7 +6801,7 @@ jss-plugin-props-sort@^10.5.1, jss-plugin-props-sort@10.10.0: "@babel/runtime" "^7.3.1" jss "10.10.0" -jss-plugin-rule-value-function@^10.5.1, jss-plugin-rule-value-function@10.10.0: +jss-plugin-rule-value-function@10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.10.0.tgz" integrity sha512-uEFJFgaCtkXeIPgki8ICw3Y7VMkL9GEan6SqmT9tqpwM+/t+hxfMUdU4wQ0MtOiMNWhwnckBV0IebrKcZM9C0g== @@ -6895,7 +6828,7 @@ jss-plugin-template@10.10.0: jss "10.10.0" tiny-warning "^1.0.2" -jss-plugin-vendor-prefixer@^10.5.1, jss-plugin-vendor-prefixer@10.10.0: +jss-plugin-vendor-prefixer@10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.10.0.tgz" integrity sha512-UY/41WumgjW8r1qMCO8l1ARg7NHnfRVWRhZ2E2m0DMYsr2DD91qIXLyNhiX83hHswR7Wm4D+oDYNC1zWCJWtqg== @@ -6924,7 +6857,7 @@ jss-preset-default@^10.10.0, jss-preset-default@10.10.0: jss-plugin-template "10.10.0" jss-plugin-vendor-prefixer "10.10.0" -jss@^10.10.0, jss@^10.5.1, jss@10.10.0: +jss@^10.10.0, jss@10.10.0: version "10.10.0" resolved "https://registry.npmjs.org/jss/-/jss-10.10.0.tgz" integrity sha512-cqsOTS7jqPsPMjtKYDUpdFC0AbhYFLTcuGRqymgmdJIeQ8cH7+AgX7YSgQy79wXloZq2VvATYxUOUQEvS1V/Zw== @@ -7104,7 +7037,7 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" -lz-string@^1.4.4: +lz-string@^1.4.4, lz-string@^1.5.0: version "1.5.0" resolved "https://registry.npmjs.org/lz-string/-/lz-string-1.5.0.tgz" integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== @@ -7655,10 +7588,10 @@ picocolors@^0.2.1: resolved "https://registry.npmjs.org/picocolors/-/picocolors-0.2.1.tgz" integrity sha512-cMlDqaLEqfSaW8Z7N5Jw+lyIW869EzT73/F5lhtY9cLGoVxSXznfgfXMO0Z5K0o0Q2TkTXq+0KFsdnSe3jDViA== -picocolors@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.0.0.tgz" - integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ== +picocolors@^1.0.0, picocolors@1.1.1: + version "1.1.1" + resolved "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz" + integrity sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA== picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.2.3, picomatch@^2.3.1: version "2.3.1" @@ -7689,11 +7622,6 @@ pkg-up@^3.1.0: dependencies: find-up "^3.0.0" -popper.js@1.16.1-lts: - version "1.16.1-lts" - resolved "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1-lts.tgz" - integrity sha512-Kjw8nKRl1m+VrSFCoVGPph93W/qrSO7ZkqPpTf7F4bk/sqcfWK019dWBUpE/fBOsOQY1dks/Bmcbfn1heM/IsA== - postcss-attribute-case-insensitive@^5.0.2: version "5.0.2" resolved "https://registry.npmjs.org/postcss-attribute-case-insensitive/-/postcss-attribute-case-insensitive-5.0.2.tgz" @@ -8229,15 +8157,7 @@ postcss-value-parser@^4.0.0, postcss-value-parser@^4.1.0, postcss-value-parser@^ resolved "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz" integrity sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ== -postcss@^7.0.35: - version "7.0.39" - resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" - integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== - dependencies: - picocolors "^0.2.1" - source-map "^0.6.1" - -postcss@^8.0.9, postcss@^8.3.5, postcss@^8.4.19, postcss@^8.4.4: +"postcss@^7.0.0 || ^8.0.1", postcss@^8, postcss@^8.0.0, postcss@^8.0.3, postcss@^8.0.9, postcss@^8.1.0, postcss@^8.1.4, postcss@^8.2, postcss@^8.2.14, postcss@^8.2.15, postcss@^8.2.2, postcss@^8.3, postcss@^8.3.5, postcss@^8.4, postcss@^8.4.19, postcss@^8.4.21, postcss@^8.4.4, postcss@^8.4.6, "postcss@>= 8", postcss@>=8, postcss@>=8.0.9: version "8.4.21" resolved "https://registry.npmjs.org/postcss/-/postcss-8.4.21.tgz" integrity sha512-tP7u/Sn/dVxK2NnruI4H9BG+x+Wxz6oeZ1cJ8P6G/PZY0IKk4k/63TDsQf2kQq3+qoJeLm2kIBUNlZe3zgb4Zg== @@ -8246,6 +8166,14 @@ postcss@^8.0.9, postcss@^8.3.5, postcss@^8.4.19, postcss@^8.4.4: picocolors "^1.0.0" source-map-js "^1.0.2" +postcss@^7.0.35: + version "7.0.39" + resolved "https://registry.npmjs.org/postcss/-/postcss-7.0.39.tgz" + integrity sha512-yioayjNbHn6z1/Bywyb2Y4s3yvDAeXGOyxqD+LnVOinq6Mdmd++SW2wUNVzavyyHxd6+DxzWGIuosg6P1Rj8uA== + dependencies: + picocolors "^0.2.1" + source-map "^0.6.1" + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz" @@ -8334,7 +8262,7 @@ prop-types-extra@^1.1.0: react-is "^16.3.2" warning "^4.0.0" -prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.7.2, prop-types@^15.8.1: +prop-types@^15.5.8, prop-types@^15.6.0, prop-types@^15.6.2, prop-types@^15.8.1: version "15.8.1" resolved "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz" integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== @@ -8536,7 +8464,7 @@ react-dnd@^16.0.1: fast-deep-equal "^3.1.3" hoist-non-react-statics "^3.3.2" -react-dom@^18.2.0: +"react-dom@^16.8.0 || ^17 || ^18", "react-dom@^16.8.0 || ^17.0.0 || ^18.0.0", "react-dom@^17.0.0 || ^18.0.0", react-dom@^18.0.0, react-dom@^18.2.0, react-dom@^18.x, react-dom@>=16.14.0, react-dom@>=16.6.0, react-dom@>=16.8, react-dom@>=16.8.0: version "18.2.0" resolved "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz" integrity sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g== @@ -8579,11 +8507,6 @@ react-is@^16.7.0: resolved "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz" integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== -"react-is@^16.8.0 || ^17.0.0": - version "17.0.2" - resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" - integrity sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w== - react-is@^17.0.1: version "17.0.2" resolved "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz" @@ -8624,7 +8547,7 @@ react-popper@^2.2.4: react-fast-compare "^3.0.1" warning "^4.0.2" -react-refresh@^0.11.0: +react-refresh@^0.11.0, "react-refresh@>=0.10.0 <1.0.0": version "0.11.0" resolved "https://registry.npmjs.org/react-refresh/-/react-refresh-0.11.0.tgz" integrity sha512-F27qZr8uUqwhWZboondsPx8tnC3Ct3SxZA3V5WyEvujRyyNv0VYPhoBg1gZ8/MV5tubQp76Trw8lTv9hzRBa+A== @@ -8721,7 +8644,7 @@ react-test-renderer@^18.2.0: react-shallow-renderer "^16.15.0" scheduler "^0.23.0" -react-transition-group@^4.4.0, react-transition-group@^4.4.2, react-transition-group@^4.4.5: +react-transition-group@^4.4.2, react-transition-group@^4.4.5: version "4.4.5" resolved "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz" integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== @@ -8731,7 +8654,7 @@ react-transition-group@^4.4.0, react-transition-group@^4.4.2, react-transition-g loose-envify "^1.4.0" prop-types "^15.6.2" -react@^18.2.0: +"react@^16.0.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17 || ^18", "react@^16.8.0 || ^17.0.0 || ^18.0.0", "react@^16.8.0 || ^17.0.0-rc.1 || ^18.0.0", "react@^17.0.0 || ^18.0.0", react@^18.0.0, react@^18.2.0, react@^18.x, "react@>= 16", "react@>= 16.14", "react@>= 16.8", react@>=0.14.0, react@>=15.0.0, react@>=15.4, react@>=16.14.0, react@>=16.3, react@>=16.6.0, react@>=16.8, react@>=16.8.0, react@>=16.8.6: version "18.2.0" resolved "https://registry.npmjs.org/react/-/react-18.2.0.tgz" integrity sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ== @@ -8979,7 +8902,7 @@ rollup-plugin-terser@^7.0.0: serialize-javascript "^4.0.0" terser "^5.0.0" -rollup@^2.43.1: +"rollup@^1.20.0 || ^2.0.0", rollup@^1.20.0||^2.0.0, rollup@^2.0.0, rollup@^2.43.1: version "2.79.1" resolved "https://registry.npmjs.org/rollup/-/rollup-2.79.1.tgz" integrity sha512-uKxbd0IhMZOhjAiD5oAFp7BqvkA4Dv47qpOCtaNvng4HBwdbWtdOh8f5nZNuk2rp51PMGk3bzfWu5oayNEuYnw== @@ -9863,7 +9786,7 @@ type-fest@^0.20.2: resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz" integrity sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ== -type-fest@^0.21.3: +type-fest@^0.21.3, "type-fest@>=0.17.0 <4.0.0": version "0.21.3" resolved "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz" integrity sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w== @@ -9897,6 +9820,11 @@ typedarray-to-buffer@^3.1.5: dependencies: is-typedarray "^1.0.0" +"typescript@^3.2.1 || ^4", "typescript@>= 2.7", "typescript@>=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta": + version "4.9.5" + resolved "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz" + integrity sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g== + unbox-primitive@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz" @@ -10118,7 +10046,7 @@ webpack-dev-middleware@^5.3.1: range-parser "^1.2.1" schema-utils "^4.0.0" -webpack-dev-server@^4.6.0: +webpack-dev-server@^4.6.0, "webpack-dev-server@3.x || 4.x": version "4.11.1" resolved "https://registry.npmjs.org/webpack-dev-server/-/webpack-dev-server-4.11.1.tgz" integrity sha512-lILVz9tAUy1zGFwieuaQtYiadImb5M3d+H+L1zDYalYoDl0cksAB1UNyuE5MMWJrG6zR1tXkCP2fitl7yoUJiw== @@ -10182,7 +10110,7 @@ webpack-sources@^3.2.3: resolved "https://registry.npmjs.org/webpack-sources/-/webpack-sources-3.2.3.tgz" integrity sha512-/DyMEOrDgLKKIG0fmvtz+4dUX/3Ghozwgm6iPp8KRhvn+eQf9+Q7GWxVNMk3+uCPWfdXYC4ExGBckIXdFEfH1w== -webpack@^5.64.4: +"webpack@^4.0.0 || ^5.0.0", "webpack@^4.37.0 || ^5.0.0", "webpack@^4.4.0 || ^5.9.0", "webpack@^4.44.2 || ^5.47.0", webpack@^5.0.0, webpack@^5.1.0, webpack@^5.20.0, webpack@^5.64.4, "webpack@>= 4", webpack@>=2, "webpack@>=4.43.0 <6.0.0": version "5.76.0" resolved "https://registry.npmjs.org/webpack/-/webpack-5.76.0.tgz" integrity sha512-l5sOdYBDunyf72HW8dF23rFtWq/7Zgvt/9ftMof71E/yUb1YLOBmTgA2K4vQthB3kotMrSj609txVE0dnr2fjA==