mirror of
https://github.com/TurTaskProject/TurTaskWeb.git
synced 2025-12-19 05:54:07 +01:00
Merge branch 'main' into feature/tasks-api
This commit is contained in:
commit
b7354845fc
0
backend/boards/__init__.py
Normal file
0
backend/boards/__init__.py
Normal file
11
backend/boards/admin.py
Normal file
11
backend/boards/admin.py
Normal file
@ -0,0 +1,11 @@
|
||||
from django.contrib import admin
|
||||
from .models import Board, ListBoard
|
||||
|
||||
@admin.register(Board)
|
||||
class BoardAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'user']
|
||||
|
||||
@admin.register(ListBoard)
|
||||
class ListBoardAdmin(admin.ModelAdmin):
|
||||
list_display = ['name', 'position', 'board']
|
||||
list_filter = ['board', 'position']
|
||||
9
backend/boards/apps.py
Normal file
9
backend/boards/apps.py
Normal file
@ -0,0 +1,9 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class BoardsConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'boards'
|
||||
|
||||
def ready(self):
|
||||
import boards.signals
|
||||
35
backend/boards/migrations/0001_initial.py
Normal file
35
backend/boards/migrations/0001_initial.py
Normal file
@ -0,0 +1,35 @@
|
||||
# Generated by Django 4.2.6 on 2023-11-19 19:19
|
||||
|
||||
from django.conf import settings
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = [
|
||||
migrations.swappable_dependency(settings.AUTH_USER_MODEL),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='Board',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('created_at', models.DateTimeField(auto_now_add=True)),
|
||||
('user', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='ListBoard',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('name', models.CharField(max_length=255)),
|
||||
('position', models.IntegerField()),
|
||||
('board', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='boards.board')),
|
||||
],
|
||||
),
|
||||
]
|
||||
0
backend/boards/migrations/__init__.py
Normal file
0
backend/boards/migrations/__init__.py
Normal file
34
backend/boards/models.py
Normal file
34
backend/boards/models.py
Normal file
@ -0,0 +1,34 @@
|
||||
from django.db import models
|
||||
|
||||
from users.models import CustomUser
|
||||
|
||||
class Board(models.Model):
|
||||
"""
|
||||
Kanban board model.
|
||||
|
||||
:param user: The user who owns the board.
|
||||
:param name: The name of the board.
|
||||
:param created_at: The date and time when the board was created.
|
||||
"""
|
||||
user = models.ForeignKey(CustomUser, on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=255)
|
||||
created_at = models.DateTimeField(auto_now_add=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name}"
|
||||
|
||||
|
||||
class ListBoard(models.Model):
|
||||
"""
|
||||
List inside a Kanban board.
|
||||
|
||||
:param board: The board that the list belongs to.
|
||||
:param name: The name of the list.
|
||||
:param position: The position of the list in Kanban.
|
||||
"""
|
||||
board = models.ForeignKey(Board, on_delete=models.CASCADE)
|
||||
name = models.CharField(max_length=255)
|
||||
position = models.IntegerField()
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.name}"
|
||||
14
backend/boards/signals.py
Normal file
14
backend/boards/signals.py
Normal file
@ -0,0 +1,14 @@
|
||||
from django.db.models.signals import post_save
|
||||
from django.dispatch import receiver
|
||||
|
||||
from boards.models import Board, ListBoard
|
||||
from users.models import CustomUser
|
||||
|
||||
@receiver(post_save, sender=CustomUser)
|
||||
def create_default_board(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
board = Board.objects.create(user=instance, name="My Default Board")
|
||||
|
||||
ListBoard.objects.create(board=board, name="Todo", position=1)
|
||||
ListBoard.objects.create(board=board, name="In Progress", position=2)
|
||||
ListBoard.objects.create(board=board, name="Done", position=3)
|
||||
3
backend/boards/tests.py
Normal file
3
backend/boards/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
5
backend/boards/urls.py
Normal file
5
backend/boards/urls.py
Normal file
@ -0,0 +1,5 @@
|
||||
from django.urls import path
|
||||
|
||||
urlpatterns = [
|
||||
|
||||
]
|
||||
3
backend/boards/views.py
Normal file
3
backend/boards/views.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.shortcuts import render
|
||||
|
||||
# Create your views here.
|
||||
@ -52,6 +52,8 @@ INSTALLED_APPS = [
|
||||
'tasks',
|
||||
'users',
|
||||
'authentications',
|
||||
'dashboard',
|
||||
'boards',
|
||||
|
||||
'corsheaders',
|
||||
'drf_spectacular',
|
||||
|
||||
@ -27,4 +27,6 @@ urlpatterns = [
|
||||
path('api/schema/', SpectacularAPIView.as_view(), name='schema'),
|
||||
path('api/schema/swagger-ui/', SpectacularSwaggerView.as_view(url_name='schema'), name='swagger-ui'),
|
||||
path('api/schema/redoc/', SpectacularRedocView.as_view(url_name='schema'), name='redoc'),
|
||||
path('api/', include('dashboard.urls')),
|
||||
path('api/', include('boards.urls')),
|
||||
]
|
||||
0
backend/dashboard/__init__.py
Normal file
0
backend/dashboard/__init__.py
Normal file
3
backend/dashboard/admin.py
Normal file
3
backend/dashboard/admin.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.contrib import admin
|
||||
|
||||
# Register your models here.
|
||||
6
backend/dashboard/apps.py
Normal file
6
backend/dashboard/apps.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.apps import AppConfig
|
||||
|
||||
|
||||
class DashboardConfig(AppConfig):
|
||||
default_auto_field = 'django.db.models.BigAutoField'
|
||||
name = 'dashboard'
|
||||
0
backend/dashboard/migrations/__init__.py
Normal file
0
backend/dashboard/migrations/__init__.py
Normal file
7
backend/dashboard/serializers.py
Normal file
7
backend/dashboard/serializers.py
Normal file
@ -0,0 +1,7 @@
|
||||
from rest_framework import serializers
|
||||
from .models import UserStats
|
||||
|
||||
class UserStatsSerializer(serializers.ModelSerializer):
|
||||
class Meta:
|
||||
model = UserStats
|
||||
fields = ['health', 'gold', 'experience', 'strength', 'intelligence', 'endurance', 'perception', 'luck', 'level']
|
||||
3
backend/dashboard/tests.py
Normal file
3
backend/dashboard/tests.py
Normal file
@ -0,0 +1,3 @@
|
||||
from django.test import TestCase
|
||||
|
||||
# Create your tests here.
|
||||
6
backend/dashboard/urls.py
Normal file
6
backend/dashboard/urls.py
Normal file
@ -0,0 +1,6 @@
|
||||
from django.urls import path
|
||||
from .views import DashboardStatsAPIView
|
||||
|
||||
urlpatterns = [
|
||||
path('dashboard/stats/', DashboardStatsAPIView.as_view(), name='dashboard-stats'),
|
||||
]
|
||||
58
backend/dashboard/views.py
Normal file
58
backend/dashboard/views.py
Normal file
@ -0,0 +1,58 @@
|
||||
from rest_framework.views import APIView
|
||||
from rest_framework.response import Response
|
||||
from rest_framework import status
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
from django.db.models import Count
|
||||
from django.utils import timezone
|
||||
|
||||
from tasks.models import Todo, RecurrenceTask
|
||||
|
||||
class DashboardStatsAPIView(APIView):
|
||||
permission_classes = [IsAuthenticated]
|
||||
|
||||
def get(self, request):
|
||||
user = request.user
|
||||
|
||||
# Calculate task usage statistics
|
||||
todo_count = Todo.objects.filter(user=user).count()
|
||||
recurrence_task_count = RecurrenceTask.objects.filter(user=user).count()
|
||||
|
||||
# Calculate how many tasks were completed in the last 7 days
|
||||
completed_todo_count_last_week = Todo.objects.filter(user=user, completed=True, last_update__gte=timezone.now() - timezone.timedelta(days=7)).count()
|
||||
completed_recurrence_task_count_last_week = RecurrenceTask.objects.filter(user=user, completed=True, last_update__gte=timezone.now() - timezone.timedelta(days=7)).count()
|
||||
|
||||
# Calculate subtask completion rate
|
||||
total_subtasks = Todo.objects.filter(user=user).aggregate(total=Count('subtask__id'))['total']
|
||||
completed_subtasks = Todo.objects.filter(user=user, subtask__completed=True).aggregate(total=Count('subtask__id'))['total']
|
||||
|
||||
# Calculate overall completion rate
|
||||
total_tasks = todo_count + recurrence_task_count
|
||||
completed_tasks = completed_todo_count_last_week + completed_recurrence_task_count_last_week
|
||||
overall_completion_rate = (completed_tasks / total_tasks) * 100 if total_tasks > 0 else 0
|
||||
|
||||
data = {
|
||||
'todo_count': todo_count,
|
||||
'recurrence_task_count': recurrence_task_count,
|
||||
'completed_todo_count_last_week': completed_todo_count_last_week,
|
||||
'completed_recurrence_task_count_last_week': completed_recurrence_task_count_last_week,
|
||||
'total_subtasks': total_subtasks,
|
||||
'completed_subtasks': completed_subtasks,
|
||||
'overall_completion_rate': overall_completion_rate,
|
||||
}
|
||||
|
||||
return Response(data, status=status.HTTP_200_OK)
|
||||
|
||||
def post(self, request):
|
||||
# Handle incoming data from the POST request
|
||||
# Update the necessary information based on the data
|
||||
|
||||
task_id = request.data.get('task_id')
|
||||
is_completed = request.data.get('is_completed')
|
||||
|
||||
try:
|
||||
task = Todo.objects.get(id=task_id, user=request.user)
|
||||
task.completed = is_completed
|
||||
task.save()
|
||||
return Response({'message': 'Task completion status updated successfully'}, status=status.HTTP_200_OK)
|
||||
except Todo.DoesNotExist:
|
||||
return Response({'error': 'Task not found'}, status=status.HTTP_404_NOT_FOUND)
|
||||
@ -7,4 +7,7 @@ DB_PASSWORD=your_DB_PASSWORD
|
||||
DB_HOST=your_DB_HOST
|
||||
DB_PORT=your_DB_PORT
|
||||
GOOGLE_CLIENT_ID=your_GOOGLE_CLIENT_ID
|
||||
GOOGLE_CLIENT_SECRET=your_GOOGLE_CLIENT_SECRET
|
||||
GOOGLE_CLIENT_SECRET=your_GOOGLE_CLIENT_SECRET
|
||||
BUCKET_NAME=your_BUCKET_NAME
|
||||
AMAZON_S3_ACCESS_KEY=YOUR_S3_ACCESS_KEY
|
||||
AMAZON_S3_SECRET_ACCESS_KEY=YOUR_S3_SECRET
|
||||
|
||||
@ -1,3 +1,29 @@
|
||||
from django.contrib import admin
|
||||
from .models import Tag, Todo, RecurrenceTask, RecurrencePattern, Habit, Subtask
|
||||
|
||||
# Register your models here.
|
||||
@admin.register(Tag)
|
||||
class TagAdmin(admin.ModelAdmin):
|
||||
list_display = ['name']
|
||||
|
||||
@admin.register(Todo)
|
||||
class TodoAdmin(admin.ModelAdmin):
|
||||
list_display = ['title', 'list_board', 'is_active', 'priority']
|
||||
list_filter = ['list_board', 'is_active', 'priority']
|
||||
|
||||
@admin.register(RecurrenceTask)
|
||||
class RecurrenceTaskAdmin(admin.ModelAdmin):
|
||||
list_display = ['title', 'list_board', 'rrule', 'is_active']
|
||||
list_filter = ['list_board', 'rrule', 'is_active']
|
||||
|
||||
@admin.register(RecurrencePattern)
|
||||
class RecurrencePatternAdmin(admin.ModelAdmin):
|
||||
list_display = ['recurrence_task', 'recurring_type', 'day_of_week', 'week_of_month', 'day_of_month', 'month_of_year']
|
||||
|
||||
@admin.register(Habit)
|
||||
class HabitAdmin(admin.ModelAdmin):
|
||||
list_display = ['title', 'streak', 'current_count']
|
||||
|
||||
@admin.register(Subtask)
|
||||
class SubtaskAdmin(admin.ModelAdmin):
|
||||
list_display = ['parent_task', 'description', 'completed']
|
||||
list_filter = ['parent_task', 'completed']
|
||||
|
||||
@ -6,9 +6,9 @@ from rest_framework import viewsets
|
||||
from rest_framework.response import Response
|
||||
from rest_framework.permissions import IsAuthenticated
|
||||
|
||||
from tasks.utils import get_service, generate_recurrence_rule
|
||||
from tasks.models import Todo, RecurrenceTask
|
||||
from tasks.serializers import TodoUpdateSerializer, RecurrenceTaskUpdateSerializer
|
||||
from tasks.utils import get_service
|
||||
from tasks.models import Todo
|
||||
from tasks.serializers import TodoUpdateSerializer
|
||||
|
||||
class GoogleCalendarEventViewset(viewsets.ViewSet):
|
||||
"""Viewset for list or save Google Calendar Events."""
|
||||
@ -50,7 +50,11 @@ class GoogleCalendarEventViewset(viewsets.ViewSet):
|
||||
return events
|
||||
|
||||
def _validate_serializer(self, serializer):
|
||||
"""Validate serializer and return response."""
|
||||
"""
|
||||
Validate serializer and return response.
|
||||
|
||||
:param serializer: The serializer to validate.
|
||||
"""
|
||||
if serializer.is_valid():
|
||||
serializer.save()
|
||||
return Response("Validate Successfully", status=200)
|
||||
@ -61,7 +65,6 @@ class GoogleCalendarEventViewset(viewsets.ViewSet):
|
||||
events = self._get_google_events(request)
|
||||
|
||||
responses = []
|
||||
recurrence_task_ids = []
|
||||
for event in events:
|
||||
start_datetime = event.get('start', {}).get('dateTime')
|
||||
end_datetime = event.get('end', {}).get('dateTime')
|
||||
@ -71,25 +74,6 @@ class GoogleCalendarEventViewset(viewsets.ViewSet):
|
||||
event.pop('start')
|
||||
event.pop('end')
|
||||
|
||||
if (event.get('recurringEventId') in recurrence_task_ids):
|
||||
continue
|
||||
|
||||
if (event.get('recurringEventId') is not None):
|
||||
originalStartTime = event.get('originalStartTime', {}).get('dateTime')
|
||||
rrule_text = generate_recurrence_rule(event['start_datetime'], event['end_datetime'], originalStartTime)
|
||||
event['recurrence'] = rrule_text
|
||||
event.pop('originalStartTime')
|
||||
recurrence_task_ids.append(event['recurringEventId'])
|
||||
|
||||
try:
|
||||
task = RecurrenceTask.objects.get(google_calendar_id=event['id'])
|
||||
serializer = RecurrenceTaskUpdateSerializer(instance=task, data=event)
|
||||
except RecurrenceTask.DoesNotExist:
|
||||
serializer = RecurrenceTaskUpdateSerializer(data=event, user=request.user)
|
||||
|
||||
responses.append(self._validate_serializer(serializer))
|
||||
continue
|
||||
|
||||
try:
|
||||
task = Todo.objects.get(google_calendar_id=event['id'])
|
||||
serializer = TodoUpdateSerializer(instance=task, data=event)
|
||||
|
||||
@ -0,0 +1,23 @@
|
||||
# Generated by Django 4.2.6 on 2023-11-17 16:40
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('tasks', '0013_alter_recurrencetask_recurrence_rule'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AddField(
|
||||
model_name='recurrencetask',
|
||||
name='completed',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='todo',
|
||||
name='completed',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,107 @@
|
||||
# Generated by Django 4.2.6 on 2023-11-19 20:15
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('boards', '0001_initial'),
|
||||
('tasks', '0014_recurrencetask_completed_todo_completed'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='RecurrencePattern',
|
||||
fields=[
|
||||
('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('recurring_type', models.IntegerField(choices=[(0, 'Daily'), (1, 'Weekly'), (2, 'Monthly'), (3, 'Yearly')])),
|
||||
('max_occurrences', models.IntegerField(default=0)),
|
||||
('day_of_week', models.IntegerField(choices=[(0, 'Monday'), (1, 'Tuesday'), (2, 'Wednesday'), (3, 'Thursday'), (4, 'Friday'), (5, 'Saturday'), (6, 'Sunday')])),
|
||||
('week_of_month', models.IntegerField(choices=[(1, 'First'), (2, 'Second'), (3, 'Third'), (4, 'Fourth'), (5, 'Last')])),
|
||||
('day_of_month', models.IntegerField(default=0)),
|
||||
('month_of_year', models.IntegerField(choices=[(1, 'January'), (2, 'February'), (3, 'March'), (4, 'April'), (5, 'May'), (6, 'June'), (7, 'July'), (8, 'August'), (9, 'September'), (10, 'October'), (11, 'November'), (12, 'December')])),
|
||||
],
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='transaction',
|
||||
name='user',
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='UserNotification',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='habit',
|
||||
name='end_event',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='habit',
|
||||
name='google_calendar_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='habit',
|
||||
name='start_event',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='recurrencetask',
|
||||
name='google_calendar_id',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='recurrencetask',
|
||||
name='recurrence_rule',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='habit',
|
||||
name='current_count',
|
||||
field=models.IntegerField(default=0),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recurrencetask',
|
||||
name='is_active',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recurrencetask',
|
||||
name='is_full_day_event',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recurrencetask',
|
||||
name='list_board',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='boards.listboard'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recurrencetask',
|
||||
name='parent_task',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='tasks.recurrencetask'),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recurrencetask',
|
||||
name='rrule',
|
||||
field=models.CharField(blank=True, max_length=255, null=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='todo',
|
||||
name='is_active',
|
||||
field=models.BooleanField(default=True),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='todo',
|
||||
name='is_full_day_event',
|
||||
field=models.BooleanField(default=False),
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='todo',
|
||||
name='list_board',
|
||||
field=models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, to='boards.listboard'),
|
||||
),
|
||||
migrations.DeleteModel(
|
||||
name='Transaction',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='recurrencepattern',
|
||||
name='recurrence_task',
|
||||
field=models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, to='tasks.recurrencetask'),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.2.6 on 2023-11-19 20:24
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('boards', '0001_initial'),
|
||||
('tasks', '0015_recurrencepattern_remove_transaction_user_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='recurrencetask',
|
||||
name='list_board',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='boards.listboard'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='todo',
|
||||
name='list_board',
|
||||
field=models.ForeignKey(default=None, null=True, on_delete=django.db.models.deletion.CASCADE, to='boards.listboard'),
|
||||
),
|
||||
]
|
||||
@ -0,0 +1,25 @@
|
||||
# Generated by Django 4.2.6 on 2023-11-19 20:27
|
||||
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('boards', '0001_initial'),
|
||||
('tasks', '0016_alter_recurrencetask_list_board_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterField(
|
||||
model_name='recurrencetask',
|
||||
name='list_board',
|
||||
field=models.ForeignKey(default=1, null=True, on_delete=django.db.models.deletion.CASCADE, to='boards.listboard'),
|
||||
),
|
||||
migrations.AlterField(
|
||||
model_name='todo',
|
||||
name='list_board',
|
||||
field=models.ForeignKey(default=1, null=True, on_delete=django.db.models.deletion.CASCADE, to='boards.listboard'),
|
||||
),
|
||||
]
|
||||
@ -1,6 +1,8 @@
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
|
||||
from boards.models import ListBoard, Board
|
||||
|
||||
class Tag(models.Model):
|
||||
"""
|
||||
Represents a tag that can be associated with tasks.
|
||||
@ -12,22 +14,17 @@ class Tag(models.Model):
|
||||
|
||||
class Task(models.Model):
|
||||
"""
|
||||
Represents a Abstract of task, such as Habit, Daily, Todo, or Reward.
|
||||
Represents a Abstract of task, such as Habit, Recurrence, Todo.
|
||||
|
||||
:param user: The user who owns the task.
|
||||
:param title: Title of the task.
|
||||
:param notes: Optional additional notes for the task.
|
||||
:param tags: Associated tags for the task.
|
||||
:param completed: A boolean field indicating whether the task is completed.
|
||||
:param importance: The importance of the task (range: 1 to 5)
|
||||
:param difficulty: The difficulty of the task (range: 1 to 5).
|
||||
:param challenge: Associated challenge (optional).
|
||||
:param fromSystem: A boolean field indicating if the task is from System.
|
||||
:param creation_date: Creation date of the task.
|
||||
:param last_update: Last updated date of the task.
|
||||
:param: google_calendar_id: Google Calendar Event ID of the task.
|
||||
:param start_event: Start event of the task.
|
||||
:param end_event: End event(Due Date) of the task.
|
||||
"""
|
||||
class Difficulty(models.IntegerChoices):
|
||||
EASY = 1, 'Easy'
|
||||
@ -46,36 +43,132 @@ class Task(models.Model):
|
||||
fromSystem = models.BooleanField(default=False)
|
||||
creation_date = models.DateTimeField(auto_now_add=True)
|
||||
last_update = models.DateTimeField(auto_now=True)
|
||||
google_calendar_id = models.CharField(max_length=255, null=True, blank=True)
|
||||
start_event = models.DateTimeField(null=True)
|
||||
end_event = models.DateTimeField(null=True)
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
|
||||
class Todo(Task):
|
||||
|
||||
"""
|
||||
Represent a Todo task.
|
||||
|
||||
:param list_board: The list board that the task belongs to.
|
||||
:param is_active: A boolean field indicating whether the task is active. (Archive or not)
|
||||
:param is_full_day_event: A boolean field indicating whether the task is a full day event.
|
||||
:param start_event: Start date and time of the task.
|
||||
:param end_event: End date and time of the task.
|
||||
:param google_calendar_id: The Google Calendar ID of the task.
|
||||
:param completed: A boolean field indicating whether the task is completed.
|
||||
:param priority: The priority of the task (range: 1 to 4).
|
||||
"""
|
||||
class EisenhowerMatrix(models.IntegerChoices):
|
||||
IMPORTANT_URGENT = 1, 'Important & Urgent'
|
||||
IMPORTANT_NOT_URGENT = 2, 'Important & Not Urgent'
|
||||
NOT_IMPORTANT_URGENT = 3, 'Not Important & Urgent'
|
||||
NOT_IMPORTANT_NOT_URGENT = 4, 'Not Important & Not Urgent'
|
||||
|
||||
list_board = models.ForeignKey(ListBoard, on_delete=models.CASCADE, null=True, default=1)
|
||||
is_active = models.BooleanField(default=True)
|
||||
is_full_day_event = models.BooleanField(default=False)
|
||||
start_event = models.DateTimeField(null=True)
|
||||
end_event = models.DateTimeField(null=True)
|
||||
google_calendar_id = models.CharField(max_length=255, null=True, blank=True)
|
||||
completed = models.BooleanField(default=False)
|
||||
priority = models.PositiveSmallIntegerField(choices=EisenhowerMatrix.choices, default=EisenhowerMatrix.NOT_IMPORTANT_NOT_URGENT)
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
class RecurrenceTask(Task):
|
||||
recurrence_rule = models.CharField()
|
||||
"""
|
||||
Represent a Recurrence task. (Occure every day, week, month, year)
|
||||
|
||||
:param list_board: The list board that the task belongs to.
|
||||
:param rrule: The recurrence rule of the task.
|
||||
:param is_active: A boolean field indicating whether the task is active. (Archive or not)
|
||||
:param is_full_day_event: A boolean field indicating whether the task is a full day event.
|
||||
:param start_event: Start date and time of the task.
|
||||
:param end_event: End date and time of the task.
|
||||
:param completed: A boolean field indicating whether the task is completed.
|
||||
:param parent_task: The parent task of the subtask.
|
||||
"""
|
||||
list_board = models.ForeignKey(ListBoard, on_delete=models.CASCADE, null=True, default=1)
|
||||
rrule = models.CharField(max_length=255, null=True, blank=True)
|
||||
is_active = models.BooleanField(default=True)
|
||||
is_full_day_event = models.BooleanField(default=False)
|
||||
start_event = models.DateTimeField(null=True)
|
||||
end_event = models.DateTimeField(null=True)
|
||||
completed = models.BooleanField(default=False)
|
||||
parent_task = models.ForeignKey("self", on_delete=models.CASCADE, null=True)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.title} ({self.recurrence_rule})"
|
||||
|
||||
|
||||
class RecurrencePattern(models.Model):
|
||||
"""
|
||||
:param recurrence_task: The recurrence task that the pattern belongs to.
|
||||
:param recurring_type: The type of recurrence.
|
||||
:param max_occurrences: The maximum number of occurrences.
|
||||
:param day_of_week: The day of the week that event will occure.
|
||||
:param week_of_month: The week of the month that event will occure.
|
||||
:param day_of_month: The day of the month that event will occure.
|
||||
:param month_of_year: The month of the year that event will occure.
|
||||
"""
|
||||
class RecurringType(models.IntegerChoices):
|
||||
DAILY = 0, 'Daily'
|
||||
WEEKLY = 1, 'Weekly'
|
||||
MONTHLY = 2, 'Monthly'
|
||||
YEARLY = 3, 'Yearly'
|
||||
|
||||
class DayOfWeek(models.IntegerChoices):
|
||||
MONDAY = 0, 'Monday'
|
||||
TUESDAY = 1, 'Tuesday'
|
||||
WEDNESDAY = 2, 'Wednesday'
|
||||
THURSDAY = 3, 'Thursday'
|
||||
FRIDAY = 4, 'Friday'
|
||||
SATURDAY = 5, 'Saturday'
|
||||
SUNDAY = 6, 'Sunday'
|
||||
|
||||
class WeekOfMonth(models.IntegerChoices):
|
||||
FIRST = 1, 'First'
|
||||
SECOND = 2, 'Second'
|
||||
THIRD = 3, 'Third'
|
||||
FOURTH = 4, 'Fourth'
|
||||
LAST = 5, 'Last'
|
||||
|
||||
class MonthOfYear(models.IntegerChoices):
|
||||
JANUARY = 1, 'January'
|
||||
FEBRUARY = 2, 'February'
|
||||
MARCH = 3, 'March'
|
||||
APRIL = 4, 'April'
|
||||
MAY = 5, 'May'
|
||||
JUNE = 6, 'June'
|
||||
JULY = 7, 'July'
|
||||
AUGUST = 8, 'August'
|
||||
SEPTEMBER = 9, 'September'
|
||||
OCTOBER = 10, 'October'
|
||||
NOVEMBER = 11, 'November'
|
||||
DECEMBER = 12, 'December'
|
||||
|
||||
recurrence_task = models.ForeignKey(RecurrenceTask, on_delete=models.CASCADE)
|
||||
recurring_type = models.IntegerField(choices=RecurringType.choices)
|
||||
max_occurrences = models.IntegerField(default=0)
|
||||
day_of_week = models.IntegerField(choices=DayOfWeek.choices)
|
||||
week_of_month = models.IntegerField(choices=WeekOfMonth.choices)
|
||||
day_of_month = models.IntegerField(default=0)
|
||||
month_of_year = models.IntegerField(choices=MonthOfYear.choices)
|
||||
|
||||
|
||||
class Habit(Task):
|
||||
"""
|
||||
Represent a Habit task with streaks.
|
||||
|
||||
:param streak: The streak of the habit.
|
||||
:param current_count: The current count of the habit.
|
||||
"""
|
||||
streak = models.IntegerField(default=0)
|
||||
current_count = models.IntegerField(default=0)
|
||||
|
||||
def __str__(self) -> str:
|
||||
return f"{self.title} ({self.streak})"
|
||||
@ -90,67 +183,4 @@ class Subtask(models.Model):
|
||||
"""
|
||||
parent_task = models.ForeignKey(Todo, on_delete=models.CASCADE)
|
||||
description = models.TextField()
|
||||
completed = models.BooleanField(default=False)
|
||||
|
||||
|
||||
class UserNotification(models.Model):
|
||||
"""
|
||||
Represents a user notification.
|
||||
|
||||
:param type: The type of the notification (e.g., 'NEW_CHAT_MESSAGE').
|
||||
:param data: JSON data associated with the notification.
|
||||
:param seen: A boolean field indicating whether the notification has been seen.
|
||||
"""
|
||||
NOTIFICATION_TYPES = (
|
||||
('LEVEL_UP', 'Level Up'),
|
||||
('DEATH', 'Death'),
|
||||
)
|
||||
|
||||
type = models.CharField(max_length=255, choices=[type for type in NOTIFICATION_TYPES])
|
||||
data = models.JSONField(default=dict)
|
||||
seen = models.BooleanField(default=False)
|
||||
|
||||
@staticmethod
|
||||
def clean_notification(notifications):
|
||||
"""
|
||||
Cleanup function for removing corrupt notification data:
|
||||
- Removes notifications with null or missing id or type.
|
||||
"""
|
||||
if not notifications:
|
||||
return notifications
|
||||
|
||||
filtered_notifications = []
|
||||
|
||||
for notification in notifications:
|
||||
if notification.id is None or notification.type is None:
|
||||
continue
|
||||
|
||||
return filtered_notifications
|
||||
|
||||
|
||||
class Transaction(models.Model):
|
||||
"""
|
||||
Represents a transaction involving currencies in the system.
|
||||
|
||||
:param currency: The type of currency used in the transaction
|
||||
:param transactionType: The type of the transaction
|
||||
:param description: Additional text.
|
||||
:param amount: The transaction amount.
|
||||
:param user: The user involved in the transaction.
|
||||
"""
|
||||
CURRENCIES = (('gold', 'Gold'),)
|
||||
TRANSACTION_TYPES = (
|
||||
('buy_gold', 'Buy Gold'),
|
||||
('spend', 'Spend'),
|
||||
('debug', 'Debug'),
|
||||
('force_update_gold', 'Force Update Gold'),
|
||||
)
|
||||
|
||||
currency = models.CharField(max_length=12, choices=CURRENCIES)
|
||||
transaction_type = models.CharField(max_length=24, choices=TRANSACTION_TYPES)
|
||||
description = models.TextField(blank=True)
|
||||
amount = models.FloatField(default=0)
|
||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, on_delete=models.CASCADE)
|
||||
|
||||
def __str__(self):
|
||||
return f"Transaction ({self.id})"
|
||||
completed = models.BooleanField(default=False)
|
||||
@ -1,5 +1,4 @@
|
||||
from rest_framework import serializers
|
||||
from django.utils.dateparse import parse_datetime
|
||||
from .models import Todo, RecurrenceTask
|
||||
|
||||
|
||||
|
||||
@ -1,7 +1,8 @@
|
||||
from django.db.models.signals import pre_save
|
||||
from django.db.models.signals import pre_save, post_save
|
||||
from django.dispatch import receiver
|
||||
from django.utils import timezone
|
||||
|
||||
from boards.models import ListBoard
|
||||
from tasks.models import Todo
|
||||
|
||||
|
||||
@ -22,4 +23,17 @@ def update_priority(sender, instance, **kwargs):
|
||||
elif time_until_due <= urgency_threshold and instance.importance < importance_threshold:
|
||||
instance.priority = Todo.EisenhowerMatrix.NOT_IMPORTANT_URGENT
|
||||
else:
|
||||
instance.priority = Todo.EisenhowerMatrix.NOT_IMPORTANT_NOT_URGENT
|
||||
instance.priority = Todo.EisenhowerMatrix.NOT_IMPORTANT_NOT_URGENT
|
||||
|
||||
|
||||
@receiver(post_save, sender=Todo)
|
||||
def assign_todo_to_listboard(sender, instance, created, **kwargs):
|
||||
if created:
|
||||
user_board = instance.user.board_set.first()
|
||||
|
||||
if user_board:
|
||||
first_list_board = user_board.listboard_set.order_by('position').first()
|
||||
|
||||
if first_list_board:
|
||||
instance.list_board = first_list_board
|
||||
instance.save()
|
||||
@ -0,0 +1,38 @@
|
||||
# Generated by Django 4.2.6 on 2023-11-19 20:15
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('users', '0005_alter_userstats_endurance_and_more'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.RemoveField(
|
||||
model_name='userstats',
|
||||
name='endurance',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='userstats',
|
||||
name='intelligence',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='userstats',
|
||||
name='luck',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='userstats',
|
||||
name='perception',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='userstats',
|
||||
name='strength',
|
||||
),
|
||||
migrations.AddField(
|
||||
model_name='customuser',
|
||||
name='last_name',
|
||||
field=models.CharField(blank=True, max_length=150),
|
||||
),
|
||||
]
|
||||
@ -5,16 +5,18 @@ 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
|
||||
from django.core.validators import MinValueValidator, MaxValueValidator
|
||||
|
||||
from .managers import CustomAccountManager
|
||||
|
||||
|
||||
class CustomUser(AbstractBaseUser, PermissionsMixin):
|
||||
# User fields
|
||||
"""
|
||||
User model where email is the unique identifier for authentication.
|
||||
"""
|
||||
email = models.EmailField(_('email address'), unique=True)
|
||||
username = models.CharField(max_length=150, unique=True)
|
||||
first_name = models.CharField(max_length=150, blank=True)
|
||||
last_name = models.CharField(max_length=150, blank=True)
|
||||
start_date = models.DateTimeField(default=timezone.now)
|
||||
about = models.TextField(_('about'), max_length=500, blank=True)
|
||||
profile_pic = models.ImageField(upload_to='profile_pics', null=True, blank=True, default='profile_pics/default.png')
|
||||
@ -35,7 +37,6 @@ class CustomUser(AbstractBaseUser, PermissionsMixin):
|
||||
# String representation of the user
|
||||
return self.username
|
||||
|
||||
|
||||
def random_luck():
|
||||
return random.randint(1, 50)
|
||||
|
||||
@ -51,17 +52,6 @@ class UserStats(models.Model):
|
||||
health = models.IntegerField(default=100)
|
||||
gold = models.FloatField(default=0.0)
|
||||
experience = models.FloatField(default=0)
|
||||
strength = models.IntegerField(default=1,
|
||||
validators=[MinValueValidator(1),
|
||||
MaxValueValidator(100)])
|
||||
intelligence = models.IntegerField(default=1, validators=[MinValueValidator(1),
|
||||
MaxValueValidator(100)])
|
||||
endurance = models.IntegerField(default=1, validators=[MinValueValidator(1),
|
||||
MaxValueValidator(100)])
|
||||
perception = models.IntegerField(default=1, validators=[MinValueValidator(1),
|
||||
MaxValueValidator(100)])
|
||||
luck = models.IntegerField(default=random_luck, validators=[MinValueValidator(1),
|
||||
MaxValueValidator(50)],)
|
||||
|
||||
@property
|
||||
def level(self):
|
||||
|
||||
@ -9,5 +9,6 @@
|
||||
<body>
|
||||
<div id="root"></div>
|
||||
<script type="module" src="/src/main.jsx"></script>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
|
||||
@ -15,6 +15,9 @@
|
||||
"@dnd-kit/utilities": "^3.2.2",
|
||||
"@emotion/react": "^11.11.1",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@fortawesome/fontawesome-svg-core": "^6.4.2",
|
||||
"@fortawesome/free-brands-svg-icons": "^6.4.2",
|
||||
"@fortawesome/react-fontawesome": "^0.2.0",
|
||||
"@fullcalendar/core": "^6.1.9",
|
||||
"@fullcalendar/daygrid": "^6.1.9",
|
||||
"@fullcalendar/interaction": "^6.1.9",
|
||||
@ -38,7 +41,9 @@
|
||||
"react-datetime-picker": "^5.5.3",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-icons": "^4.11.0",
|
||||
"react-router-dom": "^6.18.0"
|
||||
"react-router-dom": "^6.18.0",
|
||||
"react-tsparticles": "^2.12.2",
|
||||
"tsparticles": "^2.12.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@tailwindcss/typography": "^0.5.10",
|
||||
|
||||
@ -20,6 +20,15 @@ dependencies:
|
||||
'@emotion/styled':
|
||||
specifier: ^11.11.0
|
||||
version: 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.37)(react@18.2.0)
|
||||
'@fortawesome/fontawesome-svg-core':
|
||||
specifier: ^6.4.2
|
||||
version: 6.4.2
|
||||
'@fortawesome/free-brands-svg-icons':
|
||||
specifier: ^6.4.2
|
||||
version: 6.4.2
|
||||
'@fortawesome/react-fontawesome':
|
||||
specifier: ^0.2.0
|
||||
version: 0.2.0(@fortawesome/fontawesome-svg-core@6.4.2)(react@18.2.0)
|
||||
'@fullcalendar/core':
|
||||
specifier: ^6.1.9
|
||||
version: 6.1.9
|
||||
@ -37,13 +46,13 @@ dependencies:
|
||||
version: 6.1.9(@fullcalendar/core@6.1.9)
|
||||
'@mui/icons-material':
|
||||
specifier: ^5.14.16
|
||||
version: 5.14.16(@mui/material@5.14.17)(@types/react@18.2.37)(react@18.2.0)
|
||||
version: 5.14.18(@mui/material@5.14.18)(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/material':
|
||||
specifier: ^5.14.17
|
||||
version: 5.14.17(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 5.14.18(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@mui/system':
|
||||
specifier: ^5.14.17
|
||||
version: 5.14.17(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react@18.2.0)
|
||||
version: 5.14.18(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react@18.2.0)
|
||||
'@react-oauth/google':
|
||||
specifier: ^0.11.1
|
||||
version: 0.11.1(react-dom@18.2.0)(react@18.2.0)
|
||||
@ -55,7 +64,7 @@ dependencies:
|
||||
version: 23.1.36
|
||||
axios:
|
||||
specifier: ^1.6.1
|
||||
version: 1.6.1
|
||||
version: 1.6.2
|
||||
bootstrap:
|
||||
specifier: ^5.3.2
|
||||
version: 5.3.2(@popperjs/core@2.11.8)
|
||||
@ -64,7 +73,7 @@ dependencies:
|
||||
version: 16.3.1
|
||||
framer-motion:
|
||||
specifier: ^10.16.4
|
||||
version: 10.16.4(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 10.16.5(react-dom@18.2.0)(react@18.2.0)
|
||||
gapi-script:
|
||||
specifier: ^1.2.0
|
||||
version: 1.2.0
|
||||
@ -88,10 +97,16 @@ dependencies:
|
||||
version: 18.2.0(react@18.2.0)
|
||||
react-icons:
|
||||
specifier: ^4.11.0
|
||||
version: 4.11.0(react@18.2.0)
|
||||
version: 4.12.0(react@18.2.0)
|
||||
react-router-dom:
|
||||
specifier: ^6.18.0
|
||||
version: 6.18.0(react-dom@18.2.0)(react@18.2.0)
|
||||
version: 6.19.0(react-dom@18.2.0)(react@18.2.0)
|
||||
react-tsparticles:
|
||||
specifier: ^2.12.2
|
||||
version: 2.12.2(react@18.2.0)
|
||||
tsparticles:
|
||||
specifier: ^2.12.0
|
||||
version: 2.12.0
|
||||
|
||||
devDependencies:
|
||||
'@tailwindcss/typography':
|
||||
@ -105,7 +120,7 @@ devDependencies:
|
||||
version: 18.2.15
|
||||
'@vitejs/plugin-react':
|
||||
specifier: ^4.1.1
|
||||
version: 4.1.1(vite@4.5.0)
|
||||
version: 4.2.0(vite@4.5.0)
|
||||
autoprefixer:
|
||||
specifier: ^10.4.16
|
||||
version: 10.4.16(postcss@8.4.31)
|
||||
@ -114,16 +129,16 @@ devDependencies:
|
||||
version: 3.9.4
|
||||
eslint:
|
||||
specifier: ^8.53.0
|
||||
version: 8.53.0
|
||||
version: 8.54.0
|
||||
eslint-plugin-react:
|
||||
specifier: ^7.33.2
|
||||
version: 7.33.2(eslint@8.53.0)
|
||||
version: 7.33.2(eslint@8.54.0)
|
||||
eslint-plugin-react-hooks:
|
||||
specifier: ^4.6.0
|
||||
version: 4.6.0(eslint@8.53.0)
|
||||
version: 4.6.0(eslint@8.54.0)
|
||||
eslint-plugin-react-refresh:
|
||||
specifier: ^0.4.4
|
||||
version: 0.4.4(eslint@8.53.0)
|
||||
version: 0.4.4(eslint@8.54.0)
|
||||
postcss:
|
||||
specifier: ^8.4.31
|
||||
version: 8.4.31
|
||||
@ -742,13 +757,13 @@ packages:
|
||||
dev: true
|
||||
optional: true
|
||||
|
||||
/@eslint-community/eslint-utils@4.4.0(eslint@8.53.0):
|
||||
/@eslint-community/eslint-utils@4.4.0(eslint@8.54.0):
|
||||
resolution: {integrity: sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
eslint: ^6.0.0 || ^7.0.0 || >=8.0.0
|
||||
dependencies:
|
||||
eslint: 8.53.0
|
||||
eslint: 8.54.0
|
||||
eslint-visitor-keys: 3.4.3
|
||||
dev: true
|
||||
|
||||
@ -765,7 +780,7 @@ packages:
|
||||
debug: 4.3.4
|
||||
espree: 9.6.1
|
||||
globals: 13.23.0
|
||||
ignore: 5.2.4
|
||||
ignore: 5.3.0
|
||||
import-fresh: 3.3.0
|
||||
js-yaml: 4.1.0
|
||||
minimatch: 3.1.2
|
||||
@ -774,8 +789,8 @@ packages:
|
||||
- supports-color
|
||||
dev: true
|
||||
|
||||
/@eslint/js@8.53.0:
|
||||
resolution: {integrity: sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==}
|
||||
/@eslint/js@8.54.0:
|
||||
resolution: {integrity: sha512-ut5V+D+fOoWPgGGNj83GGjnntO39xDy6DWxO0wb7Jp3DcMX0TfIqdzHF85VTQkerdyGmuuMD9AKAo5KiNlf/AQ==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
@ -807,6 +822,39 @@ packages:
|
||||
resolution: {integrity: sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==}
|
||||
dev: false
|
||||
|
||||
/@fortawesome/fontawesome-common-types@6.4.2:
|
||||
resolution: {integrity: sha512-1DgP7f+XQIJbLFCTX1V2QnxVmpLdKdzzo2k8EmvDOePfchaIGQ9eCHj2up3/jNEbZuBqel5OxiaOJf37TWauRA==}
|
||||
engines: {node: '>=6'}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/@fortawesome/fontawesome-svg-core@6.4.2:
|
||||
resolution: {integrity: sha512-gjYDSKv3TrM2sLTOKBc5rH9ckje8Wrwgx1CxAPbN5N3Fm4prfi7NsJVWd1jklp7i5uSCVwhZS5qlhMXqLrpAIg==}
|
||||
engines: {node: '>=6'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@fortawesome/fontawesome-common-types': 6.4.2
|
||||
dev: false
|
||||
|
||||
/@fortawesome/free-brands-svg-icons@6.4.2:
|
||||
resolution: {integrity: sha512-LKOwJX0I7+mR/cvvf6qIiqcERbdnY+24zgpUSouySml+5w8B4BJOx8EhDR/FTKAu06W12fmUIcv6lzPSwYKGGg==}
|
||||
engines: {node: '>=6'}
|
||||
requiresBuild: true
|
||||
dependencies:
|
||||
'@fortawesome/fontawesome-common-types': 6.4.2
|
||||
dev: false
|
||||
|
||||
/@fortawesome/react-fontawesome@0.2.0(@fortawesome/fontawesome-svg-core@6.4.2)(react@18.2.0):
|
||||
resolution: {integrity: sha512-uHg75Rb/XORTtVt7OS9WoK8uM276Ufi7gCzshVWkUJbHhh3svsUUeqXerrM96Wm7fRiDzfKRwSoahhMIkGAYHw==}
|
||||
peerDependencies:
|
||||
'@fortawesome/fontawesome-svg-core': ~1 || ~6
|
||||
react: '>=16.3'
|
||||
dependencies:
|
||||
'@fortawesome/fontawesome-svg-core': 6.4.2
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@fullcalendar/core@6.1.9:
|
||||
resolution: {integrity: sha512-eeG+z9BWerdsU9Ac6j16rpYpPnE0wxtnEHiHrh/u/ADbGTR3hCOjCD9PxQOfhOTHbWOVs7JQunGcksSPu5WZBQ==}
|
||||
dependencies:
|
||||
@ -900,8 +948,8 @@ packages:
|
||||
'@jridgewell/sourcemap-codec': 1.4.15
|
||||
dev: true
|
||||
|
||||
/@mui/base@5.0.0-beta.23(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-9L8SQUGAWtd/Qi7Qem26+oSSgpY7f2iQTuvcz/rsGpyZjSomMMO6lwYeQSA0CpWM7+aN7eGoSY/WV6wxJiIxXw==}
|
||||
/@mui/base@5.0.0-beta.24(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-bKt2pUADHGQtqWDZ8nvL2Lvg2GNJyd/ZUgZAJoYzRgmnxBL9j36MSlS3+exEdYkikcnvVafcBtD904RypFKb0w==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0
|
||||
@ -913,8 +961,8 @@ packages:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.2
|
||||
'@floating-ui/react-dom': 2.0.4(react-dom@18.2.0)(react@18.2.0)
|
||||
'@mui/types': 7.2.8(@types/react@18.2.37)
|
||||
'@mui/utils': 5.14.17(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/types': 7.2.9(@types/react@18.2.37)
|
||||
'@mui/utils': 5.14.18(@types/react@18.2.37)(react@18.2.0)
|
||||
'@popperjs/core': 2.11.8
|
||||
'@types/react': 18.2.37
|
||||
clsx: 2.0.0
|
||||
@ -923,12 +971,12 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@mui/core-downloads-tracker@5.14.17:
|
||||
resolution: {integrity: sha512-eE0uxrpJAEL2ZXkeGLKg8HQDafsiXY+6eNpP4lcv3yIjFfGbU6Hj9/P7Adt8jpU+6JIhmxvILGj2r27pX+zdrQ==}
|
||||
/@mui/core-downloads-tracker@5.14.18:
|
||||
resolution: {integrity: sha512-yFpF35fEVDV81nVktu0BE9qn2dD/chs7PsQhlyaV3EnTeZi9RZBuvoEfRym1/jmhJ2tcfeWXiRuHG942mQXJJQ==}
|
||||
dev: false
|
||||
|
||||
/@mui/icons-material@5.14.16(@mui/material@5.14.17)(@types/react@18.2.37)(react@18.2.0):
|
||||
resolution: {integrity: sha512-wmOgslMEGvbHZjFLru8uH5E+pif/ciXAvKNw16q6joK6EWVWU5rDYWFknDaZhCvz8ZE/K8ZnJQ+lMG6GgHzXbg==}
|
||||
/@mui/icons-material@5.14.18(@mui/material@5.14.18)(@types/react@18.2.37)(react@18.2.0):
|
||||
resolution: {integrity: sha512-o2z49R1G4SdBaxZjbMmkn+2OdT1bKymLvAYaB6pH59obM1CYv/0vAVm6zO31IqhwtYwXv6A7sLIwCGYTaVkcdg==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@mui/material': ^5.0.0
|
||||
@ -939,13 +987,13 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.2
|
||||
'@mui/material': 5.14.17(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@mui/material': 5.14.18(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@types/react': 18.2.37
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@mui/material@5.14.17(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-+y0VeOLWfEA4Z98We/UH6KCo8+f2HLZDK45FY+sJf8kSojLy3VntadKtC/u0itqnXXb1Pr4wKB2tSIBW02zY4Q==}
|
||||
/@mui/material@5.14.18(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-y3UiR/JqrkF5xZR0sIKj6y7xwuEiweh9peiN3Zfjy1gXWXhz5wjlaLdoxFfKIEBUFfeQALxr/Y8avlHH+B9lpQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.5.0
|
||||
@ -964,11 +1012,11 @@ packages:
|
||||
'@babel/runtime': 7.23.2
|
||||
'@emotion/react': 11.11.1(@types/react@18.2.37)(react@18.2.0)
|
||||
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/base': 5.0.0-beta.23(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@mui/core-downloads-tracker': 5.14.17
|
||||
'@mui/system': 5.14.17(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/types': 7.2.8(@types/react@18.2.37)
|
||||
'@mui/utils': 5.14.17(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/base': 5.0.0-beta.24(@types/react@18.2.37)(react-dom@18.2.0)(react@18.2.0)
|
||||
'@mui/core-downloads-tracker': 5.14.18
|
||||
'@mui/system': 5.14.18(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/types': 7.2.9(@types/react@18.2.37)
|
||||
'@mui/utils': 5.14.18(@types/react@18.2.37)(react@18.2.0)
|
||||
'@types/react': 18.2.37
|
||||
'@types/react-transition-group': 4.4.9
|
||||
clsx: 2.0.0
|
||||
@ -980,8 +1028,8 @@ packages:
|
||||
react-transition-group: 4.4.5(react-dom@18.2.0)(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@mui/private-theming@5.14.17(@types/react@18.2.37)(react@18.2.0):
|
||||
resolution: {integrity: sha512-u4zxsCm9xmQrlhVPug+Ccrtsjv7o2+rehvrgHoh0siSguvVgVQq5O3Hh10+tp/KWQo2JR4/nCEwquSXgITS1+g==}
|
||||
/@mui/private-theming@5.14.18(@types/react@18.2.37)(react@18.2.0):
|
||||
resolution: {integrity: sha512-WSgjqRlzfHU+2Rou3HlR2Gqfr4rZRsvFgataYO3qQ0/m6gShJN+lhVEvwEiJ9QYyVzMDvNpXZAcqp8Y2Vl+PAw==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0
|
||||
@ -991,14 +1039,14 @@ packages:
|
||||
optional: true
|
||||
dependencies:
|
||||
'@babel/runtime': 7.23.2
|
||||
'@mui/utils': 5.14.17(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/utils': 5.14.18(@types/react@18.2.37)(react@18.2.0)
|
||||
'@types/react': 18.2.37
|
||||
prop-types: 15.8.1
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@mui/styled-engine@5.14.17(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-AqpVjBEA7wnBvKPW168bNlqB6EN7HxTjLOY7oi275AzD/b1C7V0wqELy6NWoJb2yya5sRf7ENf4iNi3+T5cOgw==}
|
||||
/@mui/styled-engine@5.14.18(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-pW8bpmF9uCB5FV2IPk6mfbQCjPI5vGI09NOLhtGXPeph/4xIfC3JdIX0TILU0WcTs3aFQqo6s2+1SFgIB9rCXA==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.4.1
|
||||
@ -1019,8 +1067,8 @@ packages:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@mui/system@5.14.17(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Ccz3XlbCqka6DnbHfpL3o3TfOeWQPR+ewvNAgm8gnS9M0yVMmzzmY6z0w/C1eebb+7ZP7IoLUj9vojg/GBaTPg==}
|
||||
/@mui/system@5.14.18(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(@types/react@18.2.37)(react@18.2.0):
|
||||
resolution: {integrity: sha512-hSQQdb3KF72X4EN2hMEiv8EYJZSflfdd1TRaGPoR7CIAG347OxCslpBUwWngYobaxgKvq6xTrlIl+diaactVww==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.5.0
|
||||
@ -1038,10 +1086,10 @@ packages:
|
||||
'@babel/runtime': 7.23.2
|
||||
'@emotion/react': 11.11.1(@types/react@18.2.37)(react@18.2.0)
|
||||
'@emotion/styled': 11.11.0(@emotion/react@11.11.1)(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/private-theming': 5.14.17(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/styled-engine': 5.14.17(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0)
|
||||
'@mui/types': 7.2.8(@types/react@18.2.37)
|
||||
'@mui/utils': 5.14.17(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/private-theming': 5.14.18(@types/react@18.2.37)(react@18.2.0)
|
||||
'@mui/styled-engine': 5.14.18(@emotion/react@11.11.1)(@emotion/styled@11.11.0)(react@18.2.0)
|
||||
'@mui/types': 7.2.9(@types/react@18.2.37)
|
||||
'@mui/utils': 5.14.18(@types/react@18.2.37)(react@18.2.0)
|
||||
'@types/react': 18.2.37
|
||||
clsx: 2.0.0
|
||||
csstype: 3.1.2
|
||||
@ -1049,8 +1097,8 @@ packages:
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
/@mui/types@7.2.8(@types/react@18.2.37):
|
||||
resolution: {integrity: sha512-9u0ji+xspl96WPqvrYJF/iO+1tQ1L5GTaDOeG3vCR893yy7VcWwRNiVMmPdPNpMDqx0WV1wtEW9OMwK9acWJzQ==}
|
||||
/@mui/types@7.2.9(@types/react@18.2.37):
|
||||
resolution: {integrity: sha512-k1lN/PolaRZfNsRdAqXtcR71sTnv3z/VCCGPxU8HfdftDkzi335MdJ6scZxvofMAd/K/9EbzCZTFBmlNpQVdCg==}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0
|
||||
peerDependenciesMeta:
|
||||
@ -1060,8 +1108,8 @@ packages:
|
||||
'@types/react': 18.2.37
|
||||
dev: false
|
||||
|
||||
/@mui/utils@5.14.17(@types/react@18.2.37)(react@18.2.0):
|
||||
resolution: {integrity: sha512-yxnWgSS4J6DMFPw2Dof85yBkG02VTbEiqsikymMsnZnXDurtVGTIhlNuV24GTmFTuJMzEyTTU9UF+O7zaL8LEQ==}
|
||||
/@mui/utils@5.14.18(@types/react@18.2.37)(react@18.2.0):
|
||||
resolution: {integrity: sha512-HZDRsJtEZ7WMSnrHV9uwScGze4wM/Y+u6pDVo+grUjt5yXzn+wI8QX/JwTHh9YSw/WpnUL80mJJjgCnWj2VrzQ==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0
|
||||
@ -1123,8 +1171,8 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/@remix-run/router@1.11.0:
|
||||
resolution: {integrity: sha512-BHdhcWgeiudl91HvVa2wxqZjSHbheSgIiDvxrF1VjFzBzpTtuDPkOdOi3Iqvc08kXtFkLjhbS+ML9aM8mJS+wQ==}
|
||||
/@remix-run/router@1.12.0:
|
||||
resolution: {integrity: sha512-2hXv036Bux90e1GXTWSMfNzfDDK8LA8JYEWfyHxzvwdp6GyoWEovKc9cotb3KCKmkdwsIBuFGX7ScTWyiHv7Eg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
dev: false
|
||||
|
||||
@ -1370,11 +1418,11 @@ packages:
|
||||
resolution: {integrity: sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==}
|
||||
dev: true
|
||||
|
||||
/@vitejs/plugin-react@4.1.1(vite@4.5.0):
|
||||
resolution: {integrity: sha512-Jie2HERK+uh27e+ORXXwEP5h0Y2lS9T2PRGbfebiHGlwzDO0dEnd2aNtOR/qjBlPb1YgxwAONeblL1xqLikLag==}
|
||||
/@vitejs/plugin-react@4.2.0(vite@4.5.0):
|
||||
resolution: {integrity: sha512-+MHTH/e6H12kRp5HUkzOGqPMksezRMmW+TNzlh/QXfI8rRf6l2Z2yH/v12no1UvTwhZgEDMuQ7g7rrfMseU6FQ==}
|
||||
engines: {node: ^14.18.0 || >=16.0.0}
|
||||
peerDependencies:
|
||||
vite: ^4.2.0
|
||||
vite: ^4.2.0 || ^5.0.0
|
||||
dependencies:
|
||||
'@babel/core': 7.23.3
|
||||
'@babel/plugin-transform-react-jsx-self': 7.23.3(@babel/core@7.23.3)
|
||||
@ -1530,7 +1578,7 @@ packages:
|
||||
postcss: ^8.1.0
|
||||
dependencies:
|
||||
browserslist: 4.22.1
|
||||
caniuse-lite: 1.0.30001561
|
||||
caniuse-lite: 1.0.30001563
|
||||
fraction.js: 4.3.7
|
||||
normalize-range: 0.1.2
|
||||
picocolors: 1.0.0
|
||||
@ -1543,8 +1591,8 @@ packages:
|
||||
engines: {node: '>= 0.4'}
|
||||
dev: true
|
||||
|
||||
/axios@1.6.1:
|
||||
resolution: {integrity: sha512-vfBmhDpKafglh0EldBEbVuoe7DyAavGSLWhuSm5ZSEKQnHhBf0xAAwybbNH1IkrJNGnS/VG4I5yxig1pCEXE4g==}
|
||||
/axios@1.6.2:
|
||||
resolution: {integrity: sha512-7i24Ri4pmDRfJTR7LDBhsOTtcm+9kjX5WiY1X3wIisx6G9So3pfMkEiU7emUBe46oceVImccTEM3k6C5dbVW8A==}
|
||||
dependencies:
|
||||
follow-redirects: 1.15.3
|
||||
form-data: 4.0.0
|
||||
@ -1598,8 +1646,8 @@ packages:
|
||||
engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
caniuse-lite: 1.0.30001561
|
||||
electron-to-chromium: 1.4.581
|
||||
caniuse-lite: 1.0.30001563
|
||||
electron-to-chromium: 1.4.588
|
||||
node-releases: 2.0.13
|
||||
update-browserslist-db: 1.0.13(browserslist@4.22.1)
|
||||
dev: true
|
||||
@ -1621,8 +1669,8 @@ packages:
|
||||
engines: {node: '>= 6'}
|
||||
dev: true
|
||||
|
||||
/caniuse-lite@1.0.30001561:
|
||||
resolution: {integrity: sha512-NTt0DNoKe958Q0BE0j0c1V9jbUzhBxHIEJy7asmGrpE0yG63KTV7PLHPnK2E1O9RsQrQ081I3NLuXGS6zht3cw==}
|
||||
/caniuse-lite@1.0.30001563:
|
||||
resolution: {integrity: sha512-na2WUmOxnwIZtwnFI2CZ/3er0wdNzU7hN+cPYz/z2ajHThnkWjNBOpEPP4n+4r2WPM847JaMotaJE3bnfzjyKw==}
|
||||
dev: true
|
||||
|
||||
/chalk@2.4.2:
|
||||
@ -1849,8 +1897,8 @@ packages:
|
||||
engines: {node: '>=12'}
|
||||
dev: false
|
||||
|
||||
/electron-to-chromium@1.4.581:
|
||||
resolution: {integrity: sha512-6uhqWBIapTJUxgPTCHH9sqdbxIMPt7oXl0VcAL1kOtlU6aECdcMncCrX5Z7sHQ/invtrC9jUQUef7+HhO8vVFw==}
|
||||
/electron-to-chromium@1.4.588:
|
||||
resolution: {integrity: sha512-soytjxwbgcCu7nh5Pf4S2/4wa6UIu+A3p03U2yVr53qGxi1/VTR3ENI+p50v+UxqqZAfl48j3z55ud7VHIOr9w==}
|
||||
dev: true
|
||||
|
||||
/error-ex@1.3.2:
|
||||
@ -1990,24 +2038,24 @@ packages:
|
||||
resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==}
|
||||
engines: {node: '>=10'}
|
||||
|
||||
/eslint-plugin-react-hooks@4.6.0(eslint@8.53.0):
|
||||
/eslint-plugin-react-hooks@4.6.0(eslint@8.54.0):
|
||||
resolution: {integrity: sha512-oFc7Itz9Qxh2x4gNHStv3BqJq54ExXmfC+a1NjAta66IAN87Wu0R/QArgIS9qKzX3dXKPI9H5crl9QchNMY9+g==}
|
||||
engines: {node: '>=10'}
|
||||
peerDependencies:
|
||||
eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0
|
||||
dependencies:
|
||||
eslint: 8.53.0
|
||||
eslint: 8.54.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-react-refresh@0.4.4(eslint@8.53.0):
|
||||
/eslint-plugin-react-refresh@0.4.4(eslint@8.54.0):
|
||||
resolution: {integrity: sha512-eD83+65e8YPVg6603Om2iCIwcQJf/y7++MWm4tACtEswFLYMwxwVWAfwN+e19f5Ad/FOyyNg9Dfi5lXhH3Y3rA==}
|
||||
peerDependencies:
|
||||
eslint: '>=7'
|
||||
dependencies:
|
||||
eslint: 8.53.0
|
||||
eslint: 8.54.0
|
||||
dev: true
|
||||
|
||||
/eslint-plugin-react@7.33.2(eslint@8.53.0):
|
||||
/eslint-plugin-react@7.33.2(eslint@8.54.0):
|
||||
resolution: {integrity: sha512-73QQMKALArI8/7xGLNI/3LylrEYrlKZSb5C9+q3OtOewTnMQi5cT+aE9E41sLCmli3I9PGGmD1yiZydyo4FEPw==}
|
||||
engines: {node: '>=4'}
|
||||
peerDependencies:
|
||||
@ -2018,7 +2066,7 @@ packages:
|
||||
array.prototype.tosorted: 1.1.2
|
||||
doctrine: 2.1.0
|
||||
es-iterator-helpers: 1.0.15
|
||||
eslint: 8.53.0
|
||||
eslint: 8.54.0
|
||||
estraverse: 5.3.0
|
||||
jsx-ast-utils: 3.3.5
|
||||
minimatch: 3.1.2
|
||||
@ -2045,15 +2093,15 @@ packages:
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
dev: true
|
||||
|
||||
/eslint@8.53.0:
|
||||
resolution: {integrity: sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==}
|
||||
/eslint@8.54.0:
|
||||
resolution: {integrity: sha512-NY0DfAkM8BIZDVl6PgSa1ttZbx3xHgJzSNJKYcQglem6CppHyMhRIQkBVSSMaSRnLhig3jsDbEzOjwCVt4AmmA==}
|
||||
engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0}
|
||||
hasBin: true
|
||||
dependencies:
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.53.0)
|
||||
'@eslint-community/eslint-utils': 4.4.0(eslint@8.54.0)
|
||||
'@eslint-community/regexpp': 4.10.0
|
||||
'@eslint/eslintrc': 2.1.3
|
||||
'@eslint/js': 8.53.0
|
||||
'@eslint/js': 8.54.0
|
||||
'@humanwhocodes/config-array': 0.11.13
|
||||
'@humanwhocodes/module-importer': 1.0.1
|
||||
'@nodelib/fs.walk': 1.2.8
|
||||
@ -2075,7 +2123,7 @@ packages:
|
||||
glob-parent: 6.0.2
|
||||
globals: 13.23.0
|
||||
graphemer: 1.4.0
|
||||
ignore: 5.2.4
|
||||
ignore: 5.3.0
|
||||
imurmurhash: 0.1.4
|
||||
is-glob: 4.0.3
|
||||
is-path-inside: 3.0.3
|
||||
@ -2162,7 +2210,7 @@ packages:
|
||||
resolution: {integrity: sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
dependencies:
|
||||
flat-cache: 3.1.1
|
||||
flat-cache: 3.2.0
|
||||
dev: true
|
||||
|
||||
/fill-range@7.0.1:
|
||||
@ -2184,9 +2232,9 @@ packages:
|
||||
path-exists: 4.0.0
|
||||
dev: true
|
||||
|
||||
/flat-cache@3.1.1:
|
||||
resolution: {integrity: sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==}
|
||||
engines: {node: '>=12.0.0'}
|
||||
/flat-cache@3.2.0:
|
||||
resolution: {integrity: sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==}
|
||||
engines: {node: ^10.12.0 || >=12.0.0}
|
||||
dependencies:
|
||||
flatted: 3.2.9
|
||||
keyv: 4.5.4
|
||||
@ -2226,8 +2274,8 @@ packages:
|
||||
resolution: {integrity: sha512-ZsDfxO51wGAXREY55a7la9LScWpwv9RxIrYABrlvOFBlH/ShPnrtsXeuUIfXKKOVicNxQ+o8JTbJvjS4M89yew==}
|
||||
dev: true
|
||||
|
||||
/framer-motion@10.16.4(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-p9V9nGomS3m6/CALXqv6nFGMuFOxbWsmaOrdmhyQimMIlLl3LC7h7l86wge/Js/8cRu5ktutS/zlzgR7eBOtFA==}
|
||||
/framer-motion@10.16.5(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-GEzVjOYP2MIpV9bT/GbhcsBNoImG3/2X3O/xVNWmktkv9MdJ7P/44zELm/7Fjb+O3v39SmKFnoDQB32giThzpg==}
|
||||
peerDependencies:
|
||||
react: ^18.0.0
|
||||
react-dom: ^18.0.0
|
||||
@ -2419,8 +2467,8 @@ packages:
|
||||
react-is: 16.13.1
|
||||
dev: false
|
||||
|
||||
/ignore@5.2.4:
|
||||
resolution: {integrity: sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==}
|
||||
/ignore@5.3.0:
|
||||
resolution: {integrity: sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==}
|
||||
engines: {node: '>= 4'}
|
||||
dev: true
|
||||
|
||||
@ -3278,6 +3326,8 @@ packages:
|
||||
|
||||
/react-icons@4.11.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-V+4khzYcE5EBk/BvcuYRq6V/osf11ODUM2J8hg2FDSswRrGvqiYUYPRy4OdrWaQOBj4NcpJfmHZLNaD+VH0TyA==}
|
||||
/react-icons@4.12.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-IBaDuHiShdZqmfc/TwHu6+d6k2ltNCf3AszxNmjJc1KUfXdEeRJOKyNvLmAHaarhzGmTSVygNdyu8/opXv2gaw==}
|
||||
peerDependencies:
|
||||
react: '*'
|
||||
dependencies:
|
||||
@ -3326,26 +3376,26 @@ packages:
|
||||
engines: {node: '>=0.10.0'}
|
||||
dev: true
|
||||
|
||||
/react-router-dom@6.18.0(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-Ubrue4+Ercc/BoDkFQfc6og5zRQ4A8YxSO3Knsne+eRbZ+IepAsK249XBH/XaFuOYOYr3L3r13CXTLvYt5JDjw==}
|
||||
/react-router-dom@6.19.0(react-dom@18.2.0)(react@18.2.0):
|
||||
resolution: {integrity: sha512-N6dWlcgL2w0U5HZUUqU2wlmOrSb3ighJmtQ438SWbhB1yuLTXQ8yyTBMK3BSvVjp7gBtKurT554nCtMOgxCZmQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
react-dom: '>=16.8'
|
||||
dependencies:
|
||||
'@remix-run/router': 1.11.0
|
||||
'@remix-run/router': 1.12.0
|
||||
react: 18.2.0
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
react-router: 6.18.0(react@18.2.0)
|
||||
react-router: 6.19.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react-router@6.18.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-vk2y7Dsy8wI02eRRaRmOs9g2o+aE72YCx5q9VasT1N9v+lrdB79tIqrjMfByHiY5+6aYkH2rUa5X839nwWGPDg==}
|
||||
/react-router@6.19.0(react@18.2.0):
|
||||
resolution: {integrity: sha512-0W63PKCZ7+OuQd7Tm+RbkI8kCLmn4GPjDbX61tWljPxWgqTKlEpeQUwPkT1DRjYhF8KSihK0hQpmhU4uxVMcdw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
react: '>=16.8'
|
||||
dependencies:
|
||||
'@remix-run/router': 1.11.0
|
||||
'@remix-run/router': 1.12.0
|
||||
react: 18.2.0
|
||||
dev: false
|
||||
|
||||
@ -3388,6 +3438,16 @@ packages:
|
||||
react-dom: 18.2.0(react@18.2.0)
|
||||
dev: false
|
||||
|
||||
/react-tsparticles@2.12.2(react@18.2.0):
|
||||
resolution: {integrity: sha512-/nrEbyL8UROXKIMXe+f+LZN2ckvkwV2Qa+GGe/H26oEIc+wq/ybSG9REDwQiSt2OaDQGu0MwmA4BKmkL6wAWcA==}
|
||||
requiresBuild: true
|
||||
peerDependencies:
|
||||
react: '>=16'
|
||||
dependencies:
|
||||
react: 18.2.0
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/react@18.2.0:
|
||||
resolution: {integrity: sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==}
|
||||
engines: {node: '>=0.10.0'}
|
||||
@ -3725,6 +3785,310 @@ packages:
|
||||
resolution: {integrity: sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==}
|
||||
dev: false
|
||||
|
||||
/tsparticles-basic@2.12.0:
|
||||
resolution: {integrity: sha512-pN6FBpL0UsIUXjYbiui5+IVsbIItbQGOlwyGV55g6IYJBgdTNXgFX0HRYZGE9ZZ9psEXqzqwLM37zvWnb5AG9g==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
tsparticles-move-base: 2.12.0
|
||||
tsparticles-shape-circle: 2.12.0
|
||||
tsparticles-updater-color: 2.12.0
|
||||
tsparticles-updater-opacity: 2.12.0
|
||||
tsparticles-updater-out-modes: 2.12.0
|
||||
tsparticles-updater-size: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-engine@2.12.0:
|
||||
resolution: {integrity: sha512-ZjDIYex6jBJ4iMc9+z0uPe7SgBnmb6l+EJm83MPIsOny9lPpetMsnw/8YJ3xdxn8hV+S3myTpTN1CkOVmFv0QQ==}
|
||||
requiresBuild: true
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-attract@2.12.0:
|
||||
resolution: {integrity: sha512-0roC6D1QkFqMVomcMlTaBrNVjVOpyNzxIUsjMfshk2wUZDAvTNTuWQdUpmsLS4EeSTDN3rzlGNnIuuUQqyBU5w==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-bounce@2.12.0:
|
||||
resolution: {integrity: sha512-MMcqKLnQMJ30hubORtdq+4QMldQ3+gJu0bBYsQr9BsThsh8/V0xHc1iokZobqHYVP5tV77mbFBD8Z7iSCf0TMQ==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-bubble@2.12.0:
|
||||
resolution: {integrity: sha512-5kImCSCZlLNccXOHPIi2Yn+rQWTX3sEa/xCHwXW19uHxtILVJlnAweayc8+Zgmb7mo0DscBtWVFXHPxrVPFDUA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-connect@2.12.0:
|
||||
resolution: {integrity: sha512-ymzmFPXz6AaA1LAOL5Ihuy7YSQEW8MzuSJzbd0ES13U8XjiU3HlFqlH6WGT1KvXNw6WYoqrZt0T3fKxBW3/C3A==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-grab@2.12.0:
|
||||
resolution: {integrity: sha512-iQF/A947hSfDNqAjr49PRjyQaeRkYgTYpfNmAf+EfME8RsbapeP/BSyF6mTy0UAFC0hK2A2Hwgw72eT78yhXeQ==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-pause@2.12.0:
|
||||
resolution: {integrity: sha512-4SUikNpsFROHnRqniL+uX2E388YTtfRWqqqZxRhY0BrijH4z04Aii3YqaGhJxfrwDKkTQlIoM2GbFT552QZWjw==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-push@2.12.0:
|
||||
resolution: {integrity: sha512-kqs3V0dgDKgMoeqbdg+cKH2F+DTrvfCMrPF1MCCUpBCqBiH+TRQpJNNC86EZYHfNUeeLuIM3ttWwIkk2hllR/Q==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-remove@2.12.0:
|
||||
resolution: {integrity: sha512-2eNIrv4m1WB2VfSVj46V2L/J9hNEZnMgFc+A+qmy66C8KzDN1G8aJUAf1inW8JVc0lmo5+WKhzex4X0ZSMghBg==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-repulse@2.12.0:
|
||||
resolution: {integrity: sha512-rSzdnmgljeBCj5FPp4AtGxOG9TmTsK3AjQW0vlyd1aG2O5kSqFjR+FuT7rfdSk9LEJGH5SjPFE6cwbuy51uEWA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-slow@2.12.0:
|
||||
resolution: {integrity: sha512-2IKdMC3om7DttqyroMtO//xNdF0NvJL/Lx7LDo08VpfTgJJozxU+JAUT8XVT7urxhaDzbxSSIROc79epESROtA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-external-trail@2.12.0:
|
||||
resolution: {integrity: sha512-LKSapU5sPTaZqYx+y5VJClj0prlV7bswplSFQaIW1raXkvsk45qir2AVcpP5JUhZSFSG+SwsHr+qCgXhNeN1KA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-particles-attract@2.12.0:
|
||||
resolution: {integrity: sha512-Hl8qwuwF9aLq3FOkAW+Zomu7Gb8IKs6Y3tFQUQScDmrrSCaeRt2EGklAiwgxwgntmqzL7hbMWNx06CHHcUQKdQ==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-particles-collisions@2.12.0:
|
||||
resolution: {integrity: sha512-Se9nPWlyPxdsnHgR6ap4YUImAu3W5MeGKJaQMiQpm1vW8lSMOUejI1n1ioIaQth9weKGKnD9rvcNn76sFlzGBA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-interaction-particles-links@2.12.0:
|
||||
resolution: {integrity: sha512-e7I8gRs4rmKfcsHONXMkJnymRWpxHmeaJIo4g2NaDRjIgeb2AcJSWKWZvrsoLnm7zvaf/cMQlbN6vQwCixYq3A==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-move-base@2.12.0:
|
||||
resolution: {integrity: sha512-oSogCDougIImq+iRtIFJD0YFArlorSi8IW3HD2gO3USkH+aNn3ZqZNTqp321uB08K34HpS263DTbhLHa/D6BWw==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-move-parallax@2.12.0:
|
||||
resolution: {integrity: sha512-58CYXaX8Ih5rNtYhpnH0YwU4Ks7gVZMREGUJtmjhuYN+OFr9FVdF3oDIJ9N6gY5a5AnAKz8f5j5qpucoPRcYrQ==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-particles.js@2.12.0:
|
||||
resolution: {integrity: sha512-LyOuvYdhbUScmA4iDgV3LxA0HzY1DnOwQUy3NrPYO393S2YwdDjdwMod6Btq7EBUjg9FVIh+sZRizgV5elV2dg==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-plugin-absorbers@2.12.0:
|
||||
resolution: {integrity: sha512-2CkPreaXHrE5VzFlxUKLeRB5t66ff+3jwLJoDFgQcp+R4HOEITo0bBZv2DagGP0QZdYN4grpnQzRBVdB4d1rWA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-plugin-easing-quad@2.12.0:
|
||||
resolution: {integrity: sha512-2mNqez5pydDewMIUWaUhY5cNQ80IUOYiujwG6qx9spTq1D6EEPLbRNAEL8/ecPdn2j1Um3iWSx6lo340rPkv4Q==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-plugin-emitters@2.12.0:
|
||||
resolution: {integrity: sha512-fbskYnaXWXivBh9KFReVCfqHdhbNQSK2T+fq2qcGEWpwtDdgujcaS1k2Q/xjZnWNMfVesik4IrqspcL51gNdSA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-shape-circle@2.12.0:
|
||||
resolution: {integrity: sha512-L6OngbAlbadG7b783x16ns3+SZ7i0SSB66M8xGa5/k+YcY7zm8zG0uPt1Hd+xQDR2aNA3RngVM10O23/Lwk65Q==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-shape-image@2.12.0:
|
||||
resolution: {integrity: sha512-iCkSdUVa40DxhkkYjYuYHr9MJGVw+QnQuN5UC+e/yBgJQY+1tQL8UH0+YU/h0GHTzh5Sm+y+g51gOFxHt1dj7Q==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-shape-line@2.12.0:
|
||||
resolution: {integrity: sha512-RcpKmmpKlk+R8mM5wA2v64Lv1jvXtU4SrBDv3vbdRodKbKaWGGzymzav1Q0hYyDyUZgplEK/a5ZwrfrOwmgYGA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-shape-polygon@2.12.0:
|
||||
resolution: {integrity: sha512-5YEy7HVMt1Obxd/jnlsjajchAlYMr9eRZWN+lSjcFSH6Ibra7h59YuJVnwxOxAobpijGxsNiBX0PuGQnB47pmA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-shape-square@2.12.0:
|
||||
resolution: {integrity: sha512-33vfajHqmlODKaUzyPI/aVhnAOT09V7nfEPNl8DD0cfiNikEuPkbFqgJezJuE55ebtVo7BZPDA9o7GYbWxQNuw==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-shape-star@2.12.0:
|
||||
resolution: {integrity: sha512-4sfG/BBqm2qBnPLASl2L5aBfCx86cmZLXeh49Un+TIR1F5Qh4XUFsahgVOG0vkZQa+rOsZPEH04xY5feWmj90g==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-shape-text@2.12.0:
|
||||
resolution: {integrity: sha512-v2/FCA+hyTbDqp2ymFOe97h/NFb2eezECMrdirHWew3E3qlvj9S/xBibjbpZva2gnXcasBwxn0+LxKbgGdP0rA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-slim@2.12.0:
|
||||
resolution: {integrity: sha512-27w9aGAAAPKHvP4LHzWFpyqu7wKyulayyaZ/L6Tuuejy4KP4BBEB4rY5GG91yvAPsLtr6rwWAn3yS+uxnBDpkA==}
|
||||
dependencies:
|
||||
tsparticles-basic: 2.12.0
|
||||
tsparticles-engine: 2.12.0
|
||||
tsparticles-interaction-external-attract: 2.12.0
|
||||
tsparticles-interaction-external-bounce: 2.12.0
|
||||
tsparticles-interaction-external-bubble: 2.12.0
|
||||
tsparticles-interaction-external-connect: 2.12.0
|
||||
tsparticles-interaction-external-grab: 2.12.0
|
||||
tsparticles-interaction-external-pause: 2.12.0
|
||||
tsparticles-interaction-external-push: 2.12.0
|
||||
tsparticles-interaction-external-remove: 2.12.0
|
||||
tsparticles-interaction-external-repulse: 2.12.0
|
||||
tsparticles-interaction-external-slow: 2.12.0
|
||||
tsparticles-interaction-particles-attract: 2.12.0
|
||||
tsparticles-interaction-particles-collisions: 2.12.0
|
||||
tsparticles-interaction-particles-links: 2.12.0
|
||||
tsparticles-move-base: 2.12.0
|
||||
tsparticles-move-parallax: 2.12.0
|
||||
tsparticles-particles.js: 2.12.0
|
||||
tsparticles-plugin-easing-quad: 2.12.0
|
||||
tsparticles-shape-circle: 2.12.0
|
||||
tsparticles-shape-image: 2.12.0
|
||||
tsparticles-shape-line: 2.12.0
|
||||
tsparticles-shape-polygon: 2.12.0
|
||||
tsparticles-shape-square: 2.12.0
|
||||
tsparticles-shape-star: 2.12.0
|
||||
tsparticles-shape-text: 2.12.0
|
||||
tsparticles-updater-color: 2.12.0
|
||||
tsparticles-updater-life: 2.12.0
|
||||
tsparticles-updater-opacity: 2.12.0
|
||||
tsparticles-updater-out-modes: 2.12.0
|
||||
tsparticles-updater-rotate: 2.12.0
|
||||
tsparticles-updater-size: 2.12.0
|
||||
tsparticles-updater-stroke-color: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-color@2.12.0:
|
||||
resolution: {integrity: sha512-KcG3a8zd0f8CTiOrylXGChBrjhKcchvDJjx9sp5qpwQK61JlNojNCU35xoaSk2eEHeOvFjh0o3CXWUmYPUcBTQ==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-destroy@2.12.0:
|
||||
resolution: {integrity: sha512-6NN3dJhxACvzbIGL4dADbYQSZJmdHfwjujj1uvnxdMbb2x8C/AZzGxiN33smo4jkrZ5VLEWZWCJPJ8aOKjQ2Sg==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-life@2.12.0:
|
||||
resolution: {integrity: sha512-J7RWGHAZkowBHpcLpmjKsxwnZZJ94oGEL2w+wvW1/+ZLmAiFFF6UgU0rHMC5CbHJT4IPx9cbkYMEHsBkcRJ0Bw==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-opacity@2.12.0:
|
||||
resolution: {integrity: sha512-YUjMsgHdaYi4HN89LLogboYcCi1o9VGo21upoqxq19yRy0hRCtx2NhH22iHF/i5WrX6jqshN0iuiiNefC53CsA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-out-modes@2.12.0:
|
||||
resolution: {integrity: sha512-owBp4Gk0JNlSrmp12XVEeBroDhLZU+Uq3szbWlHGSfcR88W4c/0bt0FiH5bHUqORIkw+m8O56hCjbqwj69kpOQ==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-roll@2.12.0:
|
||||
resolution: {integrity: sha512-dxoxY5jP4C9x15BxlUv5/Q8OjUPBiE09ToXRyBxea9aEJ7/iMw6odvi1HuT0H1vTIfV7o1MYawjeCbMycvODKQ==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-rotate@2.12.0:
|
||||
resolution: {integrity: sha512-waOFlGFmEZOzsQg4C4VSejNVXGf4dMf3fsnQrEROASGf1FCd8B6WcZau7JtXSTFw0OUGuk8UGz36ETWN72DkCw==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-size@2.12.0:
|
||||
resolution: {integrity: sha512-B0yRdEDd/qZXCGDL/ussHfx5YJ9UhTqNvmS5X2rR2hiZhBAE2fmsXLeWkdtF2QusjPeEqFDxrkGiLOsh6poqRA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-stroke-color@2.12.0:
|
||||
resolution: {integrity: sha512-MPou1ZDxsuVq6SN1fbX+aI5yrs6FyP2iPCqqttpNbWyL+R6fik1rL0ab/x02B57liDXqGKYomIbBQVP3zUTW1A==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-tilt@2.12.0:
|
||||
resolution: {integrity: sha512-HDEFLXazE+Zw+kkKKAiv0Fs9D9sRP61DoCR6jZ36ipea6OBgY7V1Tifz2TSR1zoQkk57ER9+EOQbkSQO+YIPGQ==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-twinkle@2.12.0:
|
||||
resolution: {integrity: sha512-JhK/DO4kTx7IFwMBP2EQY9hBaVVvFnGBvX21SQWcjkymmN1hZ+NdcgUtR9jr4jUiiSNdSl7INaBuGloVjWvOgA==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles-updater-wobble@2.12.0:
|
||||
resolution: {integrity: sha512-85FIRl95ipD3jfIsQdDzcUC5PRMWIrCYqBq69nIy9P8rsNzygn+JK2n+P1VQZowWsZvk0mYjqb9OVQB21Lhf6Q==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
dev: false
|
||||
|
||||
/tsparticles@2.12.0:
|
||||
resolution: {integrity: sha512-aw77llkaEhcKYUHuRlggA6SB1Dpa814/nrStp9USGiDo5QwE1Ckq30QAgdXU6GRvnblUFsiO750ZuLQs5Y0tVw==}
|
||||
dependencies:
|
||||
tsparticles-engine: 2.12.0
|
||||
tsparticles-interaction-external-trail: 2.12.0
|
||||
tsparticles-plugin-absorbers: 2.12.0
|
||||
tsparticles-plugin-emitters: 2.12.0
|
||||
tsparticles-slim: 2.12.0
|
||||
tsparticles-updater-destroy: 2.12.0
|
||||
tsparticles-updater-roll: 2.12.0
|
||||
tsparticles-updater-tilt: 2.12.0
|
||||
tsparticles-updater-twinkle: 2.12.0
|
||||
tsparticles-updater-wobble: 2.12.0
|
||||
dev: false
|
||||
|
||||
/type-check@0.4.0:
|
||||
resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==}
|
||||
engines: {node: '>= 0.8.0'}
|
||||
|
||||
@ -43,4 +43,4 @@
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,4 +8,4 @@ const PrivateRoute = () => {
|
||||
return auth ? <Outlet /> : <Navigate to="/login" />;
|
||||
};
|
||||
|
||||
export default PrivateRoute;
|
||||
export default PrivateRoute;
|
||||
@ -1,15 +1,17 @@
|
||||
import React, { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import { useGoogleLogin } from "@react-oauth/google";
|
||||
|
||||
import { useCallback } from "react";
|
||||
import Particles from "react-tsparticles";
|
||||
import { loadFull } from "tsparticles";
|
||||
import refreshAccessToken from "./refreshAcesstoken";
|
||||
import axiosapi from "../../api/AuthenticationApi";
|
||||
import { useAuth } from "../../hooks/authentication/IsAuthenticated";
|
||||
import { FcGoogle } from "react-icons/fc";
|
||||
|
||||
import { useAuth } from "../../hooks/authentication/IsAuthenticated";
|
||||
|
||||
function LoginPage() {
|
||||
const Navigate = useNavigate();
|
||||
|
||||
const { isAuthenticated, setIsAuthenticated } = useAuth();
|
||||
|
||||
useEffect(() => {
|
||||
@ -21,15 +23,14 @@ function LoginPage() {
|
||||
const [email, setEmail] = useState("");
|
||||
const [password, setPassword] = useState("");
|
||||
|
||||
const handleEmailChange = event => {
|
||||
const handleEmailChange = (event) => {
|
||||
setEmail(event.target.value);
|
||||
};
|
||||
|
||||
const handlePasswordChange = event => {
|
||||
const handlePasswordChange = (event) => {
|
||||
setPassword(event.target.value);
|
||||
};
|
||||
|
||||
const handleSubmit = event => {
|
||||
const handleSubmit = (event) => {
|
||||
event.preventDefault();
|
||||
|
||||
// Send a POST request to the authentication API
|
||||
@ -38,15 +39,16 @@ function LoginPage() {
|
||||
email: email,
|
||||
password: password,
|
||||
})
|
||||
.then(res => {
|
||||
.then((res) => {
|
||||
// On successful login, store tokens and set the authorization header
|
||||
localStorage.setItem("access_token", res.data.access);
|
||||
localStorage.setItem("refresh_token", res.data.refresh);
|
||||
axiosapi.axiosInstance.defaults.headers["Authorization"] = "Bearer " + res.data.access;
|
||||
axiosapi.axiosInstance.defaults.headers["Authorization"] =
|
||||
"Bearer " + res.data.access;
|
||||
setIsAuthenticated(true);
|
||||
Navigate("/");
|
||||
})
|
||||
.catch(err => {
|
||||
.catch((err) => {
|
||||
console.log("Login failed");
|
||||
console.log(err);
|
||||
setIsAuthenticated(false);
|
||||
@ -56,7 +58,7 @@ function LoginPage() {
|
||||
const googleLoginImplicit = useGoogleLogin({
|
||||
flow: "auth-code",
|
||||
redirect_uri: "postmessage",
|
||||
onSuccess: async response => {
|
||||
onSuccess: async (response) => {
|
||||
try {
|
||||
const loginResponse = await axiosapi.googleLogin(response.code);
|
||||
if (loginResponse && loginResponse.data) {
|
||||
@ -72,17 +74,104 @@ function LoginPage() {
|
||||
setIsAuthenticated(false);
|
||||
}
|
||||
},
|
||||
onError: errorResponse => console.log(errorResponse),
|
||||
onError: (errorResponse) => console.log(errorResponse),
|
||||
});
|
||||
{
|
||||
/* Particles Loader*/
|
||||
}
|
||||
const particlesInit = useCallback(async (engine) => {
|
||||
console.log(engine);
|
||||
await loadFull(engine);
|
||||
}, []);
|
||||
|
||||
const particlesLoaded = useCallback(async (container) => {
|
||||
console.log(container);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div data-theme="night" className="min-h-screen flex">
|
||||
{/* Left Section (Login Box) */}
|
||||
<div className="w-1/2 flex items-center justify-center">
|
||||
<div className="w-96 bg-neutral rounded-lg p-8 shadow-md space-y-4">
|
||||
<h2 className="text-2xl font-semibold text-left">Log in to your account</h2>
|
||||
<div
|
||||
data-theme="night"
|
||||
className="h-screen flex items-center justify-center"
|
||||
>
|
||||
{/* Particles Container */}
|
||||
<div style={{ width: "0%", height: "100vh" }}>
|
||||
<Particles
|
||||
id="particles"
|
||||
init={particlesInit}
|
||||
loaded={particlesLoaded}
|
||||
className="-z-10"
|
||||
options={{
|
||||
fpsLimit: 240,
|
||||
interactivity: {
|
||||
events: {
|
||||
onClick: {
|
||||
enable: true,
|
||||
mode: "push",
|
||||
},
|
||||
onHover: {
|
||||
enable: true,
|
||||
mode: "repulse",
|
||||
},
|
||||
resize: true,
|
||||
},
|
||||
modes: {
|
||||
push: {
|
||||
quantity: 4,
|
||||
},
|
||||
repulse: {
|
||||
distance: 200,
|
||||
duration: 0.4,
|
||||
},
|
||||
},
|
||||
},
|
||||
particles: {
|
||||
color: {
|
||||
value: "#008000",
|
||||
},
|
||||
links: {
|
||||
color: "#00ff00",
|
||||
distance: 150,
|
||||
enable: true,
|
||||
opacity: 0.1,
|
||||
width: 1,
|
||||
},
|
||||
move: {
|
||||
direction: "none",
|
||||
enable: true,
|
||||
outModes: {
|
||||
default: "bounce",
|
||||
},
|
||||
random: false,
|
||||
speed: 4,
|
||||
straight: false,
|
||||
},
|
||||
number: {
|
||||
density: {
|
||||
enable: true,
|
||||
area: 800,
|
||||
},
|
||||
value: 50,
|
||||
},
|
||||
opacity: {
|
||||
value: 0.5,
|
||||
},
|
||||
shape: {
|
||||
type: "circle",
|
||||
},
|
||||
size: {
|
||||
value: { min: 4, max: 5 },
|
||||
},
|
||||
},
|
||||
detectRetina: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{/* Login Box */}
|
||||
<div className="w-1/4 flex items-center justify-center">
|
||||
<div className="w-96 bg-neutral rounded-lg p-8 shadow-md space-y-4 z-10">
|
||||
<h2 className="text-3xl font-bold text-center">Login</h2>
|
||||
{/* Email Input */}
|
||||
<div className="form-control">
|
||||
<div className="form-control ">
|
||||
<label className="label" htmlFor="email">
|
||||
<p className="text-bold">
|
||||
Email<span className="text-red-500 text-bold">*</span>
|
||||
@ -112,13 +201,16 @@ function LoginPage() {
|
||||
/>
|
||||
</div>
|
||||
{/* Login Button */}
|
||||
<button className="btn btn-primary w-full" onClick={handleSubmit}>
|
||||
<button className="btn btn-success w-full " onClick={handleSubmit}>
|
||||
Login
|
||||
</button>
|
||||
<div className="divider">OR</div>
|
||||
{/* Login with Google Button */}
|
||||
<button className="btn btn-outline btn-secondary w-full" onClick={() => googleLoginImplicit()}>
|
||||
Login with Google
|
||||
<button
|
||||
className="btn btn-outline btn-secondary w-full "
|
||||
onClick={() => googleLoginImplicit()}
|
||||
>
|
||||
<FcGoogle className="rounded-full bg-white"/>Login with Google
|
||||
</button>
|
||||
{/* Forgot Password Link */}
|
||||
<div className="justify-left">
|
||||
@ -128,20 +220,6 @@ function LoginPage() {
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Right Section (Blurred Image Background) */}
|
||||
<div className="w-1/2 relative">
|
||||
<div
|
||||
className="w-full h-full bg-cover bg-center"
|
||||
style={{
|
||||
backgroundImage: 'url("https://th.bing.com/th/id/OIG.9byG0pWUCcbGL7Kly9tA?pid=ImgGn&w=1024&h=1024&rs=1")',
|
||||
filter: "blur(2px) brightness(.5)",
|
||||
}}></div>
|
||||
|
||||
<div className="absolute top-1/2 left-1/2 transform -translate-x-1/2 -translate-y-1/2 text-white text-2xl font-semibold">
|
||||
Text Overlay
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -1,36 +1,29 @@
|
||||
import React, { useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import axiosapi from "../../api/AuthenticationApi";
|
||||
import { useCallback } from "react";
|
||||
import Particles from "react-tsparticles";
|
||||
import { loadFull } from "tsparticles";
|
||||
import { FcGoogle } from "react-icons/fc";
|
||||
import { useGoogleLogin } from "@react-oauth/google";
|
||||
|
||||
import Avatar from "@mui/material/Avatar";
|
||||
import Button from "@mui/material/Button";
|
||||
import CssBaseline from "@mui/material/CssBaseline";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import FormControlLabel from "@mui/material/FormControlLabel";
|
||||
import Checkbox from "@mui/material/Checkbox";
|
||||
import Link from "@mui/material/Link";
|
||||
import Grid from "@mui/material/Grid";
|
||||
import Box from "@mui/material/Box";
|
||||
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import Container from "@mui/material/Container";
|
||||
import { createTheme, ThemeProvider } from "@mui/material/styles";
|
||||
|
||||
function Copyright(props) {
|
||||
return (
|
||||
<Typography variant="body2" color="text.secondary" align="center" {...props}>
|
||||
<div className="text-center text-sm text-gray-500" {...props}>
|
||||
{"Copyright © "}
|
||||
<Link color="inherit" href="https://github.com/TurTaskProject/TurTaskWeb">
|
||||
<a
|
||||
href="https://github.com/TurTaskProject/TurTaskWeb"
|
||||
className="text-blue-500 hover:underline"
|
||||
>
|
||||
TurTask
|
||||
</Link>{" "}
|
||||
</a>{" "}
|
||||
{new Date().getFullYear()}
|
||||
{"."}
|
||||
</Typography>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const defaultTheme = createTheme();
|
||||
|
||||
export default function SignUp() {
|
||||
const Navigate = useNavigate();
|
||||
|
||||
@ -42,7 +35,7 @@ export default function SignUp() {
|
||||
const [error, setError] = useState(null);
|
||||
const [isSubmitting, setIsSubmitting] = useState(false);
|
||||
|
||||
const handleSubmit = async e => {
|
||||
const handleSubmit = async (e) => {
|
||||
e.preventDefault();
|
||||
setIsSubmitting(true);
|
||||
setError(null);
|
||||
@ -58,86 +51,194 @@ export default function SignUp() {
|
||||
Navigate("/login");
|
||||
};
|
||||
|
||||
const handleChange = e => {
|
||||
const handleChange = (e) => {
|
||||
const { name, value } = e.target;
|
||||
setFormData({ ...formData, [name]: value });
|
||||
};
|
||||
{
|
||||
/* Particles Loader*/
|
||||
}
|
||||
const particlesInit = useCallback(async (engine) => {
|
||||
console.log(engine);
|
||||
await loadFull(engine);
|
||||
}, []);
|
||||
|
||||
const particlesLoaded = useCallback(async (container) => {
|
||||
console.log(container);
|
||||
}, []);
|
||||
|
||||
const googleLoginImplicit = useGoogleLogin({
|
||||
flow: "auth-code",
|
||||
redirect_uri: "postmessage",
|
||||
onSuccess: async (response) => {
|
||||
try {
|
||||
const loginResponse = await axiosapi.googleLogin(response.code);
|
||||
if (loginResponse && loginResponse.data) {
|
||||
const { access_token, refresh_token } = loginResponse.data;
|
||||
|
||||
localStorage.setItem("access_token", access_token);
|
||||
localStorage.setItem("refresh_token", refresh_token);
|
||||
setIsAuthenticated(true);
|
||||
Navigate("/");
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Error with the POST request:", error);
|
||||
setIsAuthenticated(false);
|
||||
}
|
||||
},
|
||||
onError: (errorResponse) => console.log(errorResponse),
|
||||
});
|
||||
|
||||
return (
|
||||
<ThemeProvider theme={defaultTheme}>
|
||||
<Container component="main" maxWidth="xs">
|
||||
<CssBaseline />
|
||||
<Box
|
||||
sx={{
|
||||
marginTop: 8,
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
}}>
|
||||
<Avatar sx={{ m: 1, bgcolor: "secondary.main" }}>
|
||||
<LockOutlinedIcon />
|
||||
</Avatar>
|
||||
<Typography component="h1" variant="h5">
|
||||
Sign up
|
||||
</Typography>
|
||||
<Box component="form" noValidate onSubmit={handleSubmit} sx={{ mt: 3 }}>
|
||||
<Grid container spacing={2}>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
required
|
||||
fullWidth
|
||||
id="email"
|
||||
label="Email Address"
|
||||
name="email"
|
||||
autoComplete="email"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
autoComplete="username"
|
||||
name="Username"
|
||||
required
|
||||
fullWidth
|
||||
id="Username"
|
||||
label="Username"
|
||||
autoFocus
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<TextField
|
||||
required
|
||||
fullWidth
|
||||
name="password"
|
||||
label="Password"
|
||||
type="password"
|
||||
id="password"
|
||||
autoComplete="new-password"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</Grid>
|
||||
<Grid item xs={12}>
|
||||
<FormControlLabel
|
||||
control={<Checkbox value="allowExtraEmails" color="primary" />}
|
||||
label="I want to receive inspiration, marketing promotions and updates via email."
|
||||
/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2 }}>
|
||||
Sign Up
|
||||
</Button>
|
||||
<Grid container justifyContent="flex-end">
|
||||
<Grid item>
|
||||
<Link href="#" variant="body2">
|
||||
Already have an account? Sign in
|
||||
</Link>
|
||||
</Grid>
|
||||
</Grid>
|
||||
</Box>
|
||||
</Box>
|
||||
<Copyright sx={{ mt: 5 }} />
|
||||
</Container>
|
||||
</ThemeProvider>
|
||||
<div
|
||||
data-theme="night"
|
||||
className="h-screen flex items-center justify-center"
|
||||
>
|
||||
{/* Particles Container */}
|
||||
<div style={{ width: "0%", height: "100vh" }}>
|
||||
<Particles
|
||||
id="particles"
|
||||
init={particlesInit}
|
||||
loaded={particlesLoaded}
|
||||
className="-z-10"
|
||||
options={{
|
||||
fpsLimit: 240,
|
||||
interactivity: {
|
||||
events: {
|
||||
onClick: {
|
||||
enable: true,
|
||||
mode: "push",
|
||||
},
|
||||
onHover: {
|
||||
enable: true,
|
||||
mode: "repulse",
|
||||
},
|
||||
resize: true,
|
||||
},
|
||||
modes: {
|
||||
push: {
|
||||
quantity: 4,
|
||||
},
|
||||
repulse: {
|
||||
distance: 200,
|
||||
duration: 0.4,
|
||||
},
|
||||
},
|
||||
},
|
||||
particles: {
|
||||
color: {
|
||||
value: "#023020",
|
||||
},
|
||||
links: {
|
||||
color: "#228B22",
|
||||
distance: 150,
|
||||
enable: true,
|
||||
opacity: 0.5,
|
||||
width: 1,
|
||||
},
|
||||
move: {
|
||||
direction: "none",
|
||||
enable: true,
|
||||
outModes: {
|
||||
default: "bounce",
|
||||
},
|
||||
random: false,
|
||||
speed: 4,
|
||||
straight: false,
|
||||
},
|
||||
number: {
|
||||
density: {
|
||||
enable: true,
|
||||
area: 800,
|
||||
},
|
||||
value: 50,
|
||||
},
|
||||
opacity: {
|
||||
value: 0.5,
|
||||
},
|
||||
shape: {
|
||||
type: "circle",
|
||||
},
|
||||
size: {
|
||||
value: { min: 6, max: 8 },
|
||||
},
|
||||
},
|
||||
detectRetina: true,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="w-1/4 h-1 flex items-center justify-center">
|
||||
<div className="w-96 bg-neutral rounded-lg p-8 shadow-md space-y-4 z-10">
|
||||
{/* Register Form */}
|
||||
<h2 className="text-3xl font-bold text-center">Signup</h2>
|
||||
{/* Email Input */}
|
||||
<div className="form-control ">
|
||||
<label className="label" htmlFor="email">
|
||||
<p className="text-bold">
|
||||
Email<span className="text-red-500 text-bold">*</span>
|
||||
</p>
|
||||
</label>
|
||||
<input
|
||||
className="input"
|
||||
type="email"
|
||||
id="email"
|
||||
placeholder="Enter your email"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
{/* Username Input */}
|
||||
<div className="form-control">
|
||||
<label className="label" htmlFor="Username">
|
||||
<p className="text-bold">
|
||||
Username<span className="text-red-500 text-bold">*</span>
|
||||
</p>
|
||||
</label>
|
||||
<input
|
||||
className="input"
|
||||
type="text"
|
||||
id="Username"
|
||||
placeholder="Enter your username"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
{/* Password Input */}
|
||||
<div className="form-control">
|
||||
<label className="label" htmlFor="password">
|
||||
<p className="text-bold">
|
||||
Password<span className="text-red-500 text-bold">*</span>
|
||||
</p>
|
||||
</label>
|
||||
<input
|
||||
className="input"
|
||||
type="password"
|
||||
id="password"
|
||||
placeholder="Enter your password"
|
||||
onChange={handleChange}
|
||||
/>
|
||||
</div>
|
||||
<br></br>
|
||||
|
||||
{/* Login Button */}
|
||||
<button className="btn btn-success w-full " onClick={handleSubmit}>
|
||||
Signup
|
||||
</button>
|
||||
<div className="divider">OR</div>
|
||||
{/* Login with Google Button */}
|
||||
<button
|
||||
className="btn btn-outline btn-secondary w-full "
|
||||
onClick={() => googleLoginImplicit()}
|
||||
>
|
||||
<FcGoogle className="rounded-full bg-white"/>Login with Google
|
||||
</button>
|
||||
{/* Already have an account? */}
|
||||
<div className="text-blue-500 flex justify-center text-sm">
|
||||
<a href="login">
|
||||
Already have an account?
|
||||
</a>
|
||||
</div>
|
||||
<Copyright />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@ -136,6 +136,7 @@ function KanbanBoard() {
|
||||
))}
|
||||
</SortableContext>
|
||||
</div>
|
||||
{/* create new column */}
|
||||
<button
|
||||
onClick={() => {
|
||||
createNewColumn();
|
||||
@ -203,7 +204,7 @@ function KanbanBoard() {
|
||||
if (task.id !== id) return task;
|
||||
return { ...task, content };
|
||||
});
|
||||
|
||||
if (content === "") return deleteTask(id);
|
||||
setTasks(newTasks);
|
||||
}
|
||||
|
||||
|
||||
@ -54,7 +54,7 @@ const NavItem = ({ icon, selected, id, setSelected, logo, path }) => {
|
||||
<AnimatePresence>
|
||||
{selected && (
|
||||
<motion.span
|
||||
className="absolute inset-0 rounded-md bg-indigo-600 z-0"
|
||||
className="absolute inset-0 rounded-md bg-emerald-600 z-0"
|
||||
initial={{ scale: 0 }}
|
||||
animate={{ scale: 1 }}
|
||||
exit={{ scale: 0 }}></motion.span>
|
||||
|
||||
@ -39,7 +39,7 @@ function NavBar() {
|
||||
</label>
|
||||
<ul
|
||||
tabIndex={0}
|
||||
className="mt-3 z-[1] p-2 shadow menu menu-sm dropdown-content bg-base-100 rounded-box w-52">
|
||||
className="mt-3 z-[10] p-2 shadow menu menu-sm dropdown-content bg-base-100 rounded-box w-52">
|
||||
<li>
|
||||
<a href={settings.Profile} className="justify-between">
|
||||
Profile
|
||||
|
||||
39
frontend/src/components/signup/Signup.jsx
Normal file
39
frontend/src/components/signup/Signup.jsx
Normal file
@ -0,0 +1,39 @@
|
||||
import React from 'react';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faGoogle, faGithub } from '@fortawesome/free-brands-svg-icons';
|
||||
|
||||
function Signup() {
|
||||
return (
|
||||
<div className="flex items-center justify-center h-screen">
|
||||
<div className="flex flex-col items-center bg-white p-10 rounded-lg shadow-md">
|
||||
<h1 className="text-4xl font-semibold mb-6">Create your account</h1>
|
||||
<p className="text-gray-700 mb-6 text-lg">
|
||||
Start spending more time on your own table.
|
||||
</p>
|
||||
<div className='font-bold'>
|
||||
<div className="mb-4">
|
||||
<button className="flex items-center justify-center bg-blue-500 text-white px-14 py-3 rounded-lg">
|
||||
<span className="mr-3"><FontAwesomeIcon icon={faGoogle} /></span>
|
||||
Sign Up with Google
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div className="mb-4">
|
||||
<button className="flex items-center justify-center bg-gray-800 text-white px-14 py-3 rounded-lg">
|
||||
<span className="mr-3"><FontAwesomeIcon icon={faGithub} /></span>
|
||||
Sign Up with Github
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<button className="bg-green-500 text-white px-14 py-3 rounded-lg">
|
||||
Sign Up with your email.
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default Signup;
|
||||
Loading…
Reference in New Issue
Block a user