From b65bf276cb97e5f190061cce8c184c0e99ec2208 Mon Sep 17 00:00:00 2001 From: Vijay Singh Date: Mon, 9 Sep 2024 12:49:53 +0530 Subject: [PATCH 1/3] feat: added custom user model to add email as authentication --- task-calendar-backend/backend/settings.py | 2 + .../events/migrations/0001_initial.py | 2 +- task-calendar-backend/events/models.py | 4 +- task-calendar-backend/events/serializer.py | 3 +- .../notifications/migrations/0001_initial.py | 2 +- task-calendar-backend/notifications/models.py | 4 +- task-calendar-backend/notifications/views.py | 4 +- task-calendar-backend/users/__init__.py | 0 task-calendar-backend/users/admin.py | 23 ++++++++++ task-calendar-backend/users/apps.py | 6 +++ .../users/migrations/0001_initial.py | 36 +++++++++++++++ .../users/migrations/__init__.py | 0 task-calendar-backend/users/models.py | 46 +++++++++++++++++++ task-calendar-backend/users/tests.py | 3 ++ task-calendar-backend/users/views.py | 3 ++ 15 files changed, 131 insertions(+), 7 deletions(-) create mode 100644 task-calendar-backend/users/__init__.py create mode 100644 task-calendar-backend/users/admin.py create mode 100644 task-calendar-backend/users/apps.py create mode 100644 task-calendar-backend/users/migrations/0001_initial.py create mode 100644 task-calendar-backend/users/migrations/__init__.py create mode 100644 task-calendar-backend/users/models.py create mode 100644 task-calendar-backend/users/tests.py create mode 100644 task-calendar-backend/users/views.py diff --git a/task-calendar-backend/backend/settings.py b/task-calendar-backend/backend/settings.py index 39fd906..227fc1e 100644 --- a/task-calendar-backend/backend/settings.py +++ b/task-calendar-backend/backend/settings.py @@ -42,6 +42,7 @@ 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', + 'users', 'events', 'notifications', 'rest_framework', @@ -96,6 +97,7 @@ } } +AUTH_USER_MODEL = 'users.CustomUser' # Password validation # https://docs.djangoproject.com/en/5.0/ref/settings/#auth-password-validators diff --git a/task-calendar-backend/events/migrations/0001_initial.py b/task-calendar-backend/events/migrations/0001_initial.py index e260c2e..ce50c06 100644 --- a/task-calendar-backend/events/migrations/0001_initial.py +++ b/task-calendar-backend/events/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.0.7 on 2024-09-04 19:22 +# Generated by Django 5.0.7 on 2024-09-08 19:08 import django.db.models.deletion from django.conf import settings diff --git a/task-calendar-backend/events/models.py b/task-calendar-backend/events/models.py index bb4f5c0..b68928b 100644 --- a/task-calendar-backend/events/models.py +++ b/task-calendar-backend/events/models.py @@ -1,7 +1,9 @@ from django.db import models -from django.contrib.auth.models import User +from django.contrib.auth import get_user_model # Create your models here. +User = get_user_model() + class Event(models.Model): title = models.CharField(max_length=200) color = models.CharField(max_length=10) diff --git a/task-calendar-backend/events/serializer.py b/task-calendar-backend/events/serializer.py index 23d4d06..fd08d37 100644 --- a/task-calendar-backend/events/serializer.py +++ b/task-calendar-backend/events/serializer.py @@ -1,7 +1,8 @@ from rest_framework import serializers -from django.contrib.auth.models import User +from django.contrib.auth import get_user_model from .models import Event +User = get_user_model() class UserSerializer(serializers.ModelSerializer): class Meta: model = User diff --git a/task-calendar-backend/notifications/migrations/0001_initial.py b/task-calendar-backend/notifications/migrations/0001_initial.py index fc94d2a..a6eee91 100644 --- a/task-calendar-backend/notifications/migrations/0001_initial.py +++ b/task-calendar-backend/notifications/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.0.7 on 2024-09-07 18:31 +# Generated by Django 5.0.7 on 2024-09-08 19:08 import django.db.models.deletion from django.conf import settings diff --git a/task-calendar-backend/notifications/models.py b/task-calendar-backend/notifications/models.py index 16329e8..4fb229d 100644 --- a/task-calendar-backend/notifications/models.py +++ b/task-calendar-backend/notifications/models.py @@ -1,9 +1,11 @@ from django.db import models -from django.contrib.auth.models import User +from django.contrib.auth import get_user_model from django.contrib.contenttypes.fields import GenericForeignKey from django.contrib.contenttypes.models import ContentType # Create your models here. +User = get_user_model() + class Notification(models.Model): recipient = models.ForeignKey(User, on_delete=models.CASCADE,related_name='notifications') message = models.TextField() diff --git a/task-calendar-backend/notifications/views.py b/task-calendar-backend/notifications/views.py index c6d535a..1b88c34 100644 --- a/task-calendar-backend/notifications/views.py +++ b/task-calendar-backend/notifications/views.py @@ -11,14 +11,14 @@ class NotificationListView(viewsets.ReadOnlyModelViewSet): def get_queryset(self): return Notification.objects.filter(recipient=self.request.user) - @action(detail=True,methods=['POST']) + @action(detail=True, methods=['POST']) def mark_as_read(self,request,pk=None): notification = self.get_object() notification.is_read = True notification.save() return Response({'status': 'notification marked as read'}) - @action(detail=True,methods=['POST']) + @action(detail=True, methods=['POST']) def mark_all_as_read(self,request): self.get_queryset().update(is_read=True) return Response({'status': 'all notifications marked as read'}) diff --git a/task-calendar-backend/users/__init__.py b/task-calendar-backend/users/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/task-calendar-backend/users/admin.py b/task-calendar-backend/users/admin.py new file mode 100644 index 0000000..df9b10c --- /dev/null +++ b/task-calendar-backend/users/admin.py @@ -0,0 +1,23 @@ +from django.contrib import admin +from django.contrib.auth.admin import UserAdmin +from .models import CustomUser + +# Register your models here. +class CustomUserAdmin(UserAdmin): + model = CustomUser + list_display = ('email', 'is_staff', 'is_active') + list_filter = ('email', 'is_staff', 'is_active') + fieldsets = ( + (None, {'fields': ('email', 'password')}), + ('Permissions', {'fields': ('is_staff', 'is_active')}) + ) + add_fieldsets = ( + (None, { + 'classes': ('wide',), + 'fields': ('email', 'password1', 'password2', 'is_staff', 'is_active') + }), + ) + search_fields = ('email',) + ordering = ('email',) + +admin.site.register(CustomUser, CustomUserAdmin) \ No newline at end of file diff --git a/task-calendar-backend/users/apps.py b/task-calendar-backend/users/apps.py new file mode 100644 index 0000000..72b1401 --- /dev/null +++ b/task-calendar-backend/users/apps.py @@ -0,0 +1,6 @@ +from django.apps import AppConfig + + +class UsersConfig(AppConfig): + default_auto_field = 'django.db.models.BigAutoField' + name = 'users' diff --git a/task-calendar-backend/users/migrations/0001_initial.py b/task-calendar-backend/users/migrations/0001_initial.py new file mode 100644 index 0000000..67dc743 --- /dev/null +++ b/task-calendar-backend/users/migrations/0001_initial.py @@ -0,0 +1,36 @@ +# Generated by Django 5.0.7 on 2024-09-08 19:05 + +import django.utils.timezone +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + ('auth', '0012_alter_user_first_name_max_length'), + ] + + operations = [ + migrations.CreateModel( + name='CustomUser', + fields=[ + ('id', models.BigAutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), + ('password', models.CharField(max_length=128, verbose_name='password')), + ('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')), + ('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')), + ('email', models.EmailField(max_length=254, unique=True)), + ('first_name', models.CharField(blank=True, max_length=30)), + ('last_name', models.CharField(blank=True, max_length=30)), + ('is_active', models.BooleanField(default=True)), + ('is_staff', models.BooleanField(default=False)), + ('date_joined', models.DateTimeField(default=django.utils.timezone.now)), + ('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.group', verbose_name='groups')), + ('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.permission', verbose_name='user permissions')), + ], + options={ + 'abstract': False, + }, + ), + ] diff --git a/task-calendar-backend/users/migrations/__init__.py b/task-calendar-backend/users/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/task-calendar-backend/users/models.py b/task-calendar-backend/users/models.py new file mode 100644 index 0000000..6110e4f --- /dev/null +++ b/task-calendar-backend/users/models.py @@ -0,0 +1,46 @@ +from typing import Any +from django.contrib.auth.models import AbstractBaseUser, BaseUserManager, PermissionsMixin +from django.db import models +from django.utils import timezone + +# Create your models here. +class CustomUserManager(BaseUserManager): + def create_user(self, email, password=None, **extra_fields): + if not email: + raise ValueError('The Email field must be set') + email = self.normalize_email(email) + user = self.model(email=email, **extra_fields) + user.set_password(password) + user.save(using=self._db) + return user + + def create_superuser(self, email, password=None, **extra_fields): + extra_fields.setdefault('is_staff', True) + extra_fields.setdefault('is_superuser', True) + if extra_fields.get('is_staff') is not True: + raise ValueError('Superuser must have is_staff=True.') + if extra_fields.get('is_superuser') is not True: + raise ValueError('Superuser must have is_superuser=True.') + return self.create_user(email, password, **extra_fields) + +class CustomUser(AbstractBaseUser, PermissionsMixin): + email = models.EmailField(unique=True) + first_name = models.CharField(max_length=30, blank=True) + last_name = models.CharField(max_length=30, blank=True) + is_active = models.BooleanField(default=True) + is_staff = models.BooleanField(default=False) + date_joined = models.DateTimeField(default=timezone.now) + + objects = CustomUserManager() + + USERNAME_FIELD = 'email' + REQUIRED_FIELDS = [] + + def __str__(self) -> str: + return self.email + + def get_full_name(self): + return f"{self.first_name} {self.last_name}".strip() + + def get_short_name(self): + return self.first_name \ No newline at end of file diff --git a/task-calendar-backend/users/tests.py b/task-calendar-backend/users/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/task-calendar-backend/users/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/task-calendar-backend/users/views.py b/task-calendar-backend/users/views.py new file mode 100644 index 0000000..91ea44a --- /dev/null +++ b/task-calendar-backend/users/views.py @@ -0,0 +1,3 @@ +from django.shortcuts import render + +# Create your views here. From 696fcef2e38b875b18bbc00010967fc08902313e Mon Sep 17 00:00:00 2001 From: Vijay Singh Date: Mon, 7 Oct 2024 08:11:37 +0530 Subject: [PATCH 2/3] feat: added event creation page or popup --- task-calendar-backend/events/serializer.py | 2 +- task-calendar-frontend/package.json | 6 + task-calendar-frontend/pnpm-lock.yaml | 621 +++++++++++++++++- task-calendar-frontend/src/App.css | 3 +- task-calendar-frontend/src/App.tsx | 50 +- .../src/components/Calendars/Calendar.tsx | 26 +- .../components/Calendars/CalendarCells.tsx | 37 +- .../components/Calendars/CalendarHeader.tsx | 2 +- .../components/Calendars/CalendarWeekBar.tsx | 23 +- .../src/components/TopAppBar.tsx | 113 ++++ task-calendar-frontend/src/index.css | 2 +- .../src/styles/top-app-bar.module.scss | 14 + task-calendar-frontend/src/utils/common.ts | 3 + 13 files changed, 835 insertions(+), 67 deletions(-) create mode 100644 task-calendar-frontend/src/components/TopAppBar.tsx create mode 100644 task-calendar-frontend/src/styles/top-app-bar.module.scss create mode 100644 task-calendar-frontend/src/utils/common.ts diff --git a/task-calendar-backend/events/serializer.py b/task-calendar-backend/events/serializer.py index fd08d37..f79a4a7 100644 --- a/task-calendar-backend/events/serializer.py +++ b/task-calendar-backend/events/serializer.py @@ -6,7 +6,7 @@ class UserSerializer(serializers.ModelSerializer): class Meta: model = User - fields = ["id", "username", "email"] + fields = ["id", "email"] class EventSerializer(serializers.ModelSerializer): author = UserSerializer(read_only=True) diff --git a/task-calendar-frontend/package.json b/task-calendar-frontend/package.json index 9c1e40e..4210fa2 100644 --- a/task-calendar-frontend/package.json +++ b/task-calendar-frontend/package.json @@ -10,7 +10,11 @@ "preview": "vite preview" }, "dependencies": { + "@mui/material": "^6.0.1", + "@mui/icons-material": "^6.0.1", + "@mui/x-date-pickers": "^7.15.0", "axios": "^1.7.2", + "dayjs": "^1.11.13", "dotenv": "^16.4.5", "react": "^18.3.1", "react-dom": "^18.3.1" @@ -29,6 +33,8 @@ "eslint-plugin-react-hooks": "^4.6.2", "eslint-plugin-react-refresh": "^0.4.7", "postcss": "^8.4.39", + "prettier": "^3.3.3", + "sass": "^1.77.8", "tailwindcss": "^3.4.6", "typescript": "^5.2.2", "vite": "^5.3.4" diff --git a/task-calendar-frontend/pnpm-lock.yaml b/task-calendar-frontend/pnpm-lock.yaml index 8aae041..158ccbf 100644 --- a/task-calendar-frontend/pnpm-lock.yaml +++ b/task-calendar-frontend/pnpm-lock.yaml @@ -8,9 +8,21 @@ importers: .: dependencies: + '@mui/icons-material': + specifier: ^6.0.1 + version: 6.0.1(@mui/material@6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + '@mui/material': + specifier: ^6.0.1 + version: 6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/x-date-pickers': + specifier: ^7.15.0 + version: 7.15.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) axios: specifier: ^1.7.2 version: 1.7.2 + dayjs: + specifier: ^1.11.13 + version: 1.11.13 dotenv: specifier: ^16.4.5 version: 16.4.5 @@ -38,7 +50,7 @@ importers: version: 7.16.1(eslint@8.57.0)(typescript@5.5.3) '@vitejs/plugin-react': specifier: ^4.3.1 - version: 4.3.1(vite@5.3.4(@types/node@20.14.11)) + version: 4.3.1(vite@5.3.4(@types/node@20.14.11)(sass@1.77.8)) autoprefixer: specifier: ^10.4.19 version: 10.4.19(postcss@8.4.39) @@ -60,6 +72,12 @@ importers: postcss: specifier: ^8.4.39 version: 8.4.39 + prettier: + specifier: ^3.3.3 + version: 3.3.3 + sass: + specifier: ^1.77.8 + version: 1.77.8 tailwindcss: specifier: ^3.4.6 version: 3.4.6 @@ -68,7 +86,7 @@ importers: version: 5.5.3 vite: specifier: ^5.3.4 - version: 5.3.4(@types/node@20.14.11) + version: 5.3.4(@types/node@20.14.11)(sass@1.77.8) packages: @@ -171,6 +189,10 @@ packages: peerDependencies: '@babel/core': ^7.0.0-0 + '@babel/runtime@7.25.4': + resolution: {integrity: sha512-DSgLeL/FNcpXuzav5wfYvHCGvynXkJbn3Zvc3823AEe9nPwW9IK4UoCSS5yGymmQzN0pCPvivtgS6/8U2kkm1w==} + engines: {node: '>=6.9.0'} + '@babel/template@7.24.7': resolution: {integrity: sha512-jYqfPrU9JTF0PmPy1tLYHW4Mp4KlgxJD9l2nP9fD6yT/ICi554DmrWBAEYpIelzjHf1msDP3PxJIRt/nFNfBig==} engines: {node: '>=6.9.0'} @@ -183,6 +205,60 @@ packages: resolution: {integrity: sha512-xm8XrMKz0IlUdocVbYJe0Z9xEgidU7msskG8BbhnTPK/HZ2z/7FP7ykqPgrUH+C+r414mNfNWam1f2vqOjqjYQ==} engines: {node: '>=6.9.0'} + '@emotion/babel-plugin@11.12.0': + resolution: {integrity: sha512-y2WQb+oP8Jqvvclh8Q55gLUyb7UFvgv7eJfsj7td5TToBrIUtPay2kMrZi4xjq9qw2vD0ZR5fSho0yqoFgX7Rw==} + + '@emotion/cache@11.13.1': + resolution: {integrity: sha512-iqouYkuEblRcXmylXIwwOodiEK5Ifl7JcX7o6V4jI3iW4mLXX3dmt5xwBtIkJiQEXFAI+pC8X0i67yiPkH9Ucw==} + + '@emotion/hash@0.9.2': + resolution: {integrity: sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==} + + '@emotion/is-prop-valid@1.3.0': + resolution: {integrity: sha512-SHetuSLvJDzuNbOdtPVbq6yMMMlLoW5Q94uDqJZqy50gcmAjxFkVqmzqSGEFq9gT2iMuIeKV1PXVWmvUhuZLlQ==} + + '@emotion/memoize@0.9.0': + resolution: {integrity: sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==} + + '@emotion/react@11.13.3': + resolution: {integrity: sha512-lIsdU6JNrmYfJ5EbUCf4xW1ovy5wKQ2CkPRM4xogziOxH1nXxBSjpC9YqbFAP7circxMfYp+6x676BqWcEiixg==} + peerDependencies: + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/serialize@1.3.1': + resolution: {integrity: sha512-dEPNKzBPU+vFPGa+z3axPRn8XVDetYORmDC0wAiej+TNcOZE70ZMJa0X7JdeoM6q/nWTMZeLpN/fTnD9o8MQBA==} + + '@emotion/sheet@1.4.0': + resolution: {integrity: sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==} + + '@emotion/styled@11.13.0': + resolution: {integrity: sha512-tkzkY7nQhW/zC4hztlwucpT8QEZ6eUzpXDRhww/Eej4tFfO0FxQYWRyg/c5CCXa4d/f174kqeXYjuQRnhzf6dA==} + peerDependencies: + '@emotion/react': ^11.0.0-rc.0 + '@types/react': '*' + react: '>=16.8.0' + peerDependenciesMeta: + '@types/react': + optional: true + + '@emotion/unitless@0.10.0': + resolution: {integrity: sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==} + + '@emotion/use-insertion-effect-with-fallbacks@1.1.0': + resolution: {integrity: sha512-+wBOcIV5snwGgI2ya3u99D7/FJquOIniQT1IKyDsBmEgwvpxMNeS65Oib7OnE2d2aY+3BU4OiH+0Wchf8yk3Hw==} + peerDependencies: + react: '>=16.8.0' + + '@emotion/utils@1.4.0': + resolution: {integrity: sha512-spEnrA1b6hDR/C68lC2M7m6ALPUHZC0lIY7jAS/B/9DuuO1ZP04eov8SMv/6fwRd8pzmsn2AuJEznRREWlQrlQ==} + + '@emotion/weak-memoize@0.4.0': + resolution: {integrity: sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==} + '@esbuild/aix-ppc64@0.21.5': resolution: {integrity: sha512-1SDgH6ZSPTlggy1yI6+Dbkiz8xzpHJEVAlF/AM1tHPLsf5STom9rwtjE4hKAF20FfXXNTFqEYXyJNWh1GiZedQ==} engines: {node: '>=12'} @@ -374,6 +450,144 @@ packages: '@jridgewell/trace-mapping@0.3.25': resolution: {integrity: sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==} + '@mui/core-downloads-tracker@6.0.1': + resolution: {integrity: sha512-TmKkCTwgtwvlFTF1tZzG4lYbi7v6NGweEJwFBZoIWZrkF1OLa0xu4umifmIyd+bVIScsEj//E2AD6bOJbPMOOQ==} + + '@mui/icons-material@6.0.1': + resolution: {integrity: sha512-CsgaF65jA3H1YzpDg6H2nFH/UHueVlmpEtPim7xF9VbjYnmnblG3aX0GflBahH96Pg0schrFWyRySlgbVAh5Kw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^6.0.1 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/material@6.0.1': + resolution: {integrity: sha512-gOJS0RKYs9lRACaTluXPNopxFpIBhWVmhf09lHpqpPlR6bujXhuiTE2Q8puensdz3Qm2JGzl1VjccYHieV1g8A==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@mui/material-pigment-css': ^6.0.1 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@mui/material-pigment-css': + optional: true + '@types/react': + optional: true + + '@mui/private-theming@6.0.1': + resolution: {integrity: sha512-jQCJml1OwIrhqN5tTk5Lpqx2RZKQnShE8lMlvAkuO7Ft+xaHkP8J3iHpEk3/Pzue34DfBQtK00jcaplgM47mBA==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/styled-engine@6.0.1': + resolution: {integrity: sha512-7ZOnUhIak2vosDgMlBE/oLrsvvF3O8QKmTFpP6bhZkHjPu4dv0DbF1vC7gzgkOqiMaT0/NgRQCFW9zh38pIvsg==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.4.1 + '@emotion/styled': ^11.3.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + + '@mui/system@6.0.1': + resolution: {integrity: sha512-RdWyCMi+GkAekOnpMKhy51lyzid4F6Vj96vekp3AExkFY21JWg2+KVBqcAgJOROJ3RiaeDJf98n0yrixlCvuEw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.5.0 + '@emotion/styled': ^11.3.0 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + '@types/react': + optional: true + + '@mui/types@7.2.16': + resolution: {integrity: sha512-qI8TV3M7ShITEEc8Ih15A2vLzZGLhD+/UPNwck/hcls2gwg7dyRjNGXcQYHKLB5Q7PuTRfrTkAoPa2VV1s67Ag==} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@5.16.6': + resolution: {integrity: sha512-tWiQqlhxAt3KENNiSRL+DIn9H5xNVK6Jjf70x3PnfQPz1MPBdh7yyIcAyVBT9xiw7hP3SomRhPR7hzBMBCjqEA==} + engines: {node: '>=12.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 + react: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/utils@6.0.1': + resolution: {integrity: sha512-YmQYb2tY5nJactHltTrKA15TZfbd1R003a2xYHxUuycTv9n83rsIwHkypOxM4x7+c+Pc8xfCuE9EfLT3B3n40Q==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + + '@mui/x-date-pickers@7.15.0': + resolution: {integrity: sha512-YQEQICNxUEFYp/I/yP58cqihA8yhXaXSNZ1/N0JANu2IlCwoJ4Jzi+S0s4RN7RghpiDyoSMFijROBC5HfpTjiw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@emotion/react': ^11.9.0 + '@emotion/styled': ^11.8.1 + '@mui/material': ^5.15.14 || ^6.0.0 + '@mui/system': ^5.15.14 || ^6.0.0 + date-fns: ^2.25.0 || ^3.2.0 + date-fns-jalali: ^2.13.0-0 || ^3.2.0-0 + dayjs: ^1.10.7 + luxon: ^3.0.2 + moment: ^2.29.4 + moment-hijri: ^2.1.2 + moment-jalaali: ^0.7.4 || ^0.8.0 || ^0.9.0 || ^0.10.0 + react: ^17.0.0 || ^18.0.0 + react-dom: ^17.0.0 || ^18.0.0 + peerDependenciesMeta: + '@emotion/react': + optional: true + '@emotion/styled': + optional: true + date-fns: + optional: true + date-fns-jalali: + optional: true + dayjs: + optional: true + luxon: + optional: true + moment: + optional: true + moment-hijri: + optional: true + moment-jalaali: + optional: true + '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} @@ -394,6 +608,9 @@ packages: resolution: {integrity: sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} + '@popperjs/core@2.11.8': + resolution: {integrity: sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==} + '@rollup/rollup-android-arm-eabi@4.18.1': resolution: {integrity: sha512-lncuC4aHicncmbORnx+dUaAgzee9cm/PbIqgWz1PpXuwc+sa1Ct83tnqUDy/GFKleLiN7ZIeytM6KJ4cAn1SxA==} cpu: [arm] @@ -492,12 +709,18 @@ packages: '@types/node@20.14.11': resolution: {integrity: sha512-kprQpL8MMeszbz6ojB5/tU8PLN4kesnN8Gjzw349rDlNgsSzg90lAVj3llK99Dh7JON+t9AuscPPFW6mPbTnSA==} + '@types/parse-json@4.0.2': + resolution: {integrity: sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==} + '@types/prop-types@15.7.12': resolution: {integrity: sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q==} '@types/react-dom@18.3.0': resolution: {integrity: sha512-EhwApuTmMBmXuFOikhQLIBUn6uFg81SwLMOAUgodJF14SOBOCMdU04gDoYi0WOJJHD144TL32z4yDqCW3dnkQg==} + '@types/react-transition-group@4.4.11': + resolution: {integrity: sha512-RM05tAniPZ5DZPzzNFP+DmrcOdD0efDUxMy3145oljWSl3x9ZV5vhme98gTxFrj2lhXvmGNnUiuDyJgY9IKkNA==} + '@types/react@18.3.3': resolution: {integrity: sha512-hti/R0pS0q1/xx+TsI73XIqk26eBsISZ2R0wUijXIngRK9R/e7Xw/cXVxQK7R5JjW+SV4zGcn5hXjudkN/pLIw==} @@ -631,6 +854,10 @@ packages: axios@1.7.2: resolution: {integrity: sha512-2A8QhOMrbomlDuiLeK9XibIBzuHeRcqqNOHp0Cyp5EoJ1IFDh+XZH3A6BkXtv0K4gFGCI0Y4BM7B1wOEi0Rmgw==} + babel-plugin-macros@3.1.0: + resolution: {integrity: sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==} + engines: {node: '>=10', npm: '>=6'} + balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} @@ -676,6 +903,10 @@ packages: resolution: {integrity: sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==} engines: {node: '>= 8.10.0'} + clsx@2.1.1: + resolution: {integrity: sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==} + engines: {node: '>=6'} + color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} @@ -700,9 +931,16 @@ packages: concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} + convert-source-map@1.9.0: + resolution: {integrity: sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==} + convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cosmiconfig@7.1.0: + resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} + engines: {node: '>=10'} + cross-spawn@7.0.3: resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==} engines: {node: '>= 8'} @@ -715,6 +953,9 @@ packages: csstype@3.1.3: resolution: {integrity: sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw==} + dayjs@1.11.13: + resolution: {integrity: sha512-oaMBel6gjolK862uaPQOVTA7q3TZhuSvuMQAAglQDOWYO9A91IrAOUJEyKVlqJlHE0vq5p5UXxzdPfMH/x6xNg==} + debug@4.3.5: resolution: {integrity: sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==} engines: {node: '>=6.0'} @@ -745,6 +986,9 @@ packages: resolution: {integrity: sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==} engines: {node: '>=6.0.0'} + dom-helpers@5.2.1: + resolution: {integrity: sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==} + dotenv@16.4.5: resolution: {integrity: sha512-ZmdL2rui+eB2YwhsWzjInR8LldtZHGDoQ1ugH85ppHKwpUHL7j7rN0Ti9NCnGiQbhaZ11FpR+7ao1dNsmduNUg==} engines: {node: '>=12'} @@ -761,6 +1005,9 @@ packages: emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} + error-ex@1.3.2: + resolution: {integrity: sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==} + esbuild@0.21.5: resolution: {integrity: sha512-mg3OPMV4hXywwpoDxu3Qda5xCKQi+vCTZq8S9J/EpkhB2HzKXq4SNFZE3+NK93JYxc8VMSep+lOUSC/RVKaBqw==} engines: {node: '>=12'} @@ -869,6 +1116,9 @@ packages: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} + find-root@1.1.0: + resolution: {integrity: sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==} + find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} @@ -958,10 +1208,16 @@ packages: resolution: {integrity: sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==} engines: {node: '>= 0.4'} + hoist-non-react-statics@3.3.2: + resolution: {integrity: sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==} + ignore@5.3.1: resolution: {integrity: sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==} engines: {node: '>= 4'} + immutable@4.3.7: + resolution: {integrity: sha512-1hqclzwYwjRDFLjcFxOM5AYkkG0rpFPpr1RLPMEuGczoS7YA8gLhy8SWXYRAA/XwfEHpfo3cw5JGioS32fnMRw==} + import-fresh@3.3.0: resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==} engines: {node: '>=6'} @@ -977,6 +1233,9 @@ packages: inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} + is-arrayish@0.2.1: + resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} + is-binary-path@2.1.0: resolution: {integrity: sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==} engines: {node: '>=8'} @@ -1030,6 +1289,9 @@ packages: json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} + json-parse-even-better-errors@2.3.1: + resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} + json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} @@ -1158,6 +1420,10 @@ packages: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} + parse-json@5.2.0: + resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} + engines: {node: '>=8'} + path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} @@ -1250,6 +1516,9 @@ packages: engines: {node: '>=14'} hasBin: true + prop-types@15.8.1: + resolution: {integrity: sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==} + proxy-from-env@1.1.0: resolution: {integrity: sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==} @@ -1265,10 +1534,22 @@ packages: peerDependencies: react: ^18.3.1 + react-is@16.13.1: + resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==} + + react-is@18.3.1: + resolution: {integrity: sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==} + react-refresh@0.14.2: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-transition-group@4.4.5: + resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} + peerDependencies: + react: '>=16.6.0' + react-dom: '>=16.6.0' + react@18.3.1: resolution: {integrity: sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==} engines: {node: '>=0.10.0'} @@ -1280,6 +1561,9 @@ packages: resolution: {integrity: sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==} engines: {node: '>=8.10.0'} + regenerator-runtime@0.14.1: + resolution: {integrity: sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==} + resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} @@ -1305,6 +1589,11 @@ packages: run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} + sass@1.77.8: + resolution: {integrity: sha512-4UHg6prsrycW20fqLGPShtEvo/WyHRVRHwOP4DzkUrObWoWI05QBSfzU71TVB7PFaL104TwNaHpjlWXAZbQiNQ==} + engines: {node: '>=14.0.0'} + hasBin: true + scheduler@0.23.2: resolution: {integrity: sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==} @@ -1337,6 +1626,10 @@ packages: resolution: {integrity: sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==} engines: {node: '>=0.10.0'} + source-map@0.5.7: + resolution: {integrity: sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==} + engines: {node: '>=0.10.0'} + string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} @@ -1357,6 +1650,9 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + stylis@4.2.0: + resolution: {integrity: sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==} + sucrase@3.35.0: resolution: {integrity: sha512-8EbVDiu9iN/nESwxeSxDKe0dunta1GOlHufmSSXxMD2z2/tMZpDMpvXQGsc+ajGo8y2uYUmixaSRUc/QPoQ0GA==} engines: {node: '>=16 || 14 >=14.17'} @@ -1492,6 +1788,10 @@ packages: yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} + yaml@1.10.2: + resolution: {integrity: sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==} + engines: {node: '>= 6'} + yaml@2.4.5: resolution: {integrity: sha512-aBx2bnqDzVOyNKfsysjA2ms5ZlnjSAW2eG3/L5G/CSujfjLJTJsEw1bGw8kCf04KodQWk1pxlGnZ56CRxiawmg==} engines: {node: '>= 14'} @@ -1628,6 +1928,10 @@ snapshots: '@babel/core': 7.24.9 '@babel/helper-plugin-utils': 7.24.8 + '@babel/runtime@7.25.4': + dependencies: + regenerator-runtime: 0.14.1 + '@babel/template@7.24.7': dependencies: '@babel/code-frame': 7.24.7 @@ -1655,6 +1959,97 @@ snapshots: '@babel/helper-validator-identifier': 7.24.7 to-fast-properties: 2.0.0 + '@emotion/babel-plugin@11.12.0': + dependencies: + '@babel/helper-module-imports': 7.24.7 + '@babel/runtime': 7.25.4 + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/serialize': 1.3.1 + babel-plugin-macros: 3.1.0 + convert-source-map: 1.9.0 + escape-string-regexp: 4.0.0 + find-root: 1.1.0 + source-map: 0.5.7 + stylis: 4.2.0 + transitivePeerDependencies: + - supports-color + optional: true + + '@emotion/cache@11.13.1': + dependencies: + '@emotion/memoize': 0.9.0 + '@emotion/sheet': 1.4.0 + '@emotion/utils': 1.4.0 + '@emotion/weak-memoize': 0.4.0 + stylis: 4.2.0 + + '@emotion/hash@0.9.2': + optional: true + + '@emotion/is-prop-valid@1.3.0': + dependencies: + '@emotion/memoize': 0.9.0 + optional: true + + '@emotion/memoize@0.9.0': {} + + '@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.4 + '@emotion/babel-plugin': 11.12.0 + '@emotion/cache': 11.13.1 + '@emotion/serialize': 1.3.1 + '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) + '@emotion/utils': 1.4.0 + '@emotion/weak-memoize': 0.4.0 + hoist-non-react-statics: 3.3.2 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + transitivePeerDependencies: + - supports-color + optional: true + + '@emotion/serialize@1.3.1': + dependencies: + '@emotion/hash': 0.9.2 + '@emotion/memoize': 0.9.0 + '@emotion/unitless': 0.10.0 + '@emotion/utils': 1.4.0 + csstype: 3.1.3 + optional: true + + '@emotion/sheet@1.4.0': {} + + '@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.4 + '@emotion/babel-plugin': 11.12.0 + '@emotion/is-prop-valid': 1.3.0 + '@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1) + '@emotion/serialize': 1.3.1 + '@emotion/use-insertion-effect-with-fallbacks': 1.1.0(react@18.3.1) + '@emotion/utils': 1.4.0 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + transitivePeerDependencies: + - supports-color + optional: true + + '@emotion/unitless@0.10.0': + optional: true + + '@emotion/use-insertion-effect-with-fallbacks@1.1.0(react@18.3.1)': + dependencies: + react: 18.3.1 + optional: true + + '@emotion/utils@1.4.0': {} + + '@emotion/weak-memoize@0.4.0': {} + '@esbuild/aix-ppc64@0.21.5': optional: true @@ -1785,6 +2180,120 @@ snapshots: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.0 + '@mui/core-downloads-tracker@6.0.1': {} + + '@mui/icons-material@6.0.1(@mui/material@6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.4 + '@mui/material': 6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@mui/material@6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.4 + '@mui/core-downloads-tracker': 6.0.1 + '@mui/system': 6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + '@mui/types': 7.2.16(@types/react@18.3.3) + '@mui/utils': 6.0.1(@types/react@18.3.3)(react@18.3.1) + '@popperjs/core': 2.11.8 + '@types/react-transition-group': 4.4.11 + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-is: 18.3.1 + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + '@types/react': 18.3.3 + + '@mui/private-theming@6.0.1(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.4 + '@mui/utils': 6.0.1(@types/react@18.3.3)(react@18.3.1) + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@mui/styled-engine@6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.4 + '@emotion/cache': 11.13.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + + '@mui/system@6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.4 + '@mui/private-theming': 6.0.1(@types/react@18.3.3)(react@18.3.1) + '@mui/styled-engine': 6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(react@18.3.1) + '@mui/types': 7.2.16(@types/react@18.3.3) + '@mui/utils': 6.0.1(@types/react@18.3.3)(react@18.3.1) + clsx: 2.1.1 + csstype: 3.1.3 + prop-types: 15.8.1 + react: 18.3.1 + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + '@types/react': 18.3.3 + + '@mui/types@7.2.16(@types/react@18.3.3)': + optionalDependencies: + '@types/react': 18.3.3 + + '@mui/utils@5.16.6(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.4 + '@mui/types': 7.2.16(@types/react@18.3.3) + '@types/prop-types': 15.7.12 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@mui/utils@6.0.1(@types/react@18.3.3)(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.4 + '@mui/types': 7.2.16(@types/react@18.3.3) + '@types/prop-types': 15.7.12 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-is: 18.3.1 + optionalDependencies: + '@types/react': 18.3.3 + + '@mui/x-date-pickers@7.15.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@mui/material@6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1))(@mui/system@6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(dayjs@1.11.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)': + dependencies: + '@babel/runtime': 7.25.4 + '@mui/material': 6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + '@mui/system': 6.0.1(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@emotion/styled@11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + '@mui/utils': 5.16.6(@types/react@18.3.3)(react@18.3.1) + '@types/react-transition-group': 4.4.11 + clsx: 2.1.1 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-transition-group: 4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + optionalDependencies: + '@emotion/react': 11.13.3(@types/react@18.3.3)(react@18.3.1) + '@emotion/styled': 11.13.0(@emotion/react@11.13.3(@types/react@18.3.3)(react@18.3.1))(@types/react@18.3.3)(react@18.3.1) + dayjs: 1.11.13 + transitivePeerDependencies: + - '@types/react' + '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 @@ -1802,6 +2311,8 @@ snapshots: '@pkgr/core@0.1.1': {} + '@popperjs/core@2.11.8': {} + '@rollup/rollup-android-arm-eabi@4.18.1': optional: true @@ -1877,12 +2388,19 @@ snapshots: dependencies: undici-types: 5.26.5 + '@types/parse-json@4.0.2': + optional: true + '@types/prop-types@15.7.12': {} '@types/react-dom@18.3.0': dependencies: '@types/react': 18.3.3 + '@types/react-transition-group@4.4.11': + dependencies: + '@types/react': 18.3.3 + '@types/react@18.3.3': dependencies: '@types/prop-types': 15.7.12 @@ -1971,14 +2489,14 @@ snapshots: '@ungap/structured-clone@1.2.0': {} - '@vitejs/plugin-react@4.3.1(vite@5.3.4(@types/node@20.14.11))': + '@vitejs/plugin-react@4.3.1(vite@5.3.4(@types/node@20.14.11)(sass@1.77.8))': dependencies: '@babel/core': 7.24.9 '@babel/plugin-transform-react-jsx-self': 7.24.7(@babel/core@7.24.9) '@babel/plugin-transform-react-jsx-source': 7.24.7(@babel/core@7.24.9) '@types/babel__core': 7.20.5 react-refresh: 0.14.2 - vite: 5.3.4(@types/node@20.14.11) + vite: 5.3.4(@types/node@20.14.11)(sass@1.77.8) transitivePeerDependencies: - supports-color @@ -2042,6 +2560,13 @@ snapshots: transitivePeerDependencies: - debug + babel-plugin-macros@3.1.0: + dependencies: + '@babel/runtime': 7.25.4 + cosmiconfig: 7.1.0 + resolve: 1.22.8 + optional: true + balanced-match@1.0.2: {} binary-extensions@2.3.0: {} @@ -2095,6 +2620,8 @@ snapshots: optionalDependencies: fsevents: 2.3.3 + clsx@2.1.1: {} + color-convert@1.9.3: dependencies: color-name: 1.1.3 @@ -2115,8 +2642,20 @@ snapshots: concat-map@0.0.1: {} + convert-source-map@1.9.0: + optional: true + convert-source-map@2.0.0: {} + cosmiconfig@7.1.0: + dependencies: + '@types/parse-json': 4.0.2 + import-fresh: 3.3.0 + parse-json: 5.2.0 + path-type: 4.0.0 + yaml: 1.10.2 + optional: true + cross-spawn@7.0.3: dependencies: path-key: 3.1.1 @@ -2127,6 +2666,8 @@ snapshots: csstype@3.1.3: {} + dayjs@1.11.13: {} + debug@4.3.5: dependencies: ms: 2.1.2 @@ -2147,6 +2688,11 @@ snapshots: dependencies: esutils: 2.0.3 + dom-helpers@5.2.1: + dependencies: + '@babel/runtime': 7.25.4 + csstype: 3.1.3 + dotenv@16.4.5: {} eastasianwidth@0.2.0: {} @@ -2157,6 +2703,11 @@ snapshots: emoji-regex@9.2.2: {} + error-ex@1.3.2: + dependencies: + is-arrayish: 0.2.1 + optional: true + esbuild@0.21.5: optionalDependencies: '@esbuild/aix-ppc64': 0.21.5 @@ -2306,6 +2857,9 @@ snapshots: dependencies: to-regex-range: 5.0.1 + find-root@1.1.0: + optional: true + find-up@5.0.0: dependencies: locate-path: 6.0.0 @@ -2394,8 +2948,15 @@ snapshots: dependencies: function-bind: 1.1.2 + hoist-non-react-statics@3.3.2: + dependencies: + react-is: 16.13.1 + optional: true + ignore@5.3.1: {} + immutable@4.3.7: {} + import-fresh@3.3.0: dependencies: parent-module: 1.0.1 @@ -2410,6 +2971,9 @@ snapshots: inherits@2.0.4: {} + is-arrayish@0.2.1: + optional: true + is-binary-path@2.1.0: dependencies: binary-extensions: 2.3.0 @@ -2450,6 +3014,9 @@ snapshots: json-buffer@3.0.1: {} + json-parse-even-better-errors@2.3.1: + optional: true + json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} @@ -2559,6 +3126,14 @@ snapshots: dependencies: callsites: 3.1.0 + parse-json@5.2.0: + dependencies: + '@babel/code-frame': 7.24.7 + error-ex: 1.3.2 + json-parse-even-better-errors: 2.3.1 + lines-and-columns: 1.2.4 + optional: true + path-exists@4.0.0: {} path-is-absolute@1.0.1: {} @@ -2627,6 +3202,12 @@ snapshots: prettier@3.3.3: {} + prop-types@15.8.1: + dependencies: + loose-envify: 1.4.0 + object-assign: 4.1.1 + react-is: 16.13.1 + proxy-from-env@1.1.0: {} punycode@2.3.1: {} @@ -2639,8 +3220,21 @@ snapshots: react: 18.3.1 scheduler: 0.23.2 + react-is@16.13.1: {} + + react-is@18.3.1: {} + react-refresh@0.14.2: {} + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@babel/runtime': 7.25.4 + dom-helpers: 5.2.1 + loose-envify: 1.4.0 + prop-types: 15.8.1 + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react@18.3.1: dependencies: loose-envify: 1.4.0 @@ -2653,6 +3247,8 @@ snapshots: dependencies: picomatch: 2.3.1 + regenerator-runtime@0.14.1: {} + resolve-from@4.0.0: {} resolve@1.22.8: @@ -2693,6 +3289,12 @@ snapshots: dependencies: queue-microtask: 1.2.3 + sass@1.77.8: + dependencies: + chokidar: 3.6.0 + immutable: 4.3.7 + source-map-js: 1.2.0 + scheduler@0.23.2: dependencies: loose-envify: 1.4.0 @@ -2713,6 +3315,9 @@ snapshots: source-map-js@1.2.0: {} + source-map@0.5.7: + optional: true + string-width@4.2.3: dependencies: emoji-regex: 8.0.0 @@ -2735,6 +3340,8 @@ snapshots: strip-json-comments@3.1.1: {} + stylis@4.2.0: {} + sucrase@3.35.0: dependencies: '@jridgewell/gen-mapping': 0.3.5 @@ -2833,7 +3440,7 @@ snapshots: util-deprecate@1.0.2: {} - vite@5.3.4(@types/node@20.14.11): + vite@5.3.4(@types/node@20.14.11)(sass@1.77.8): dependencies: esbuild: 0.21.5 postcss: 8.4.39 @@ -2841,6 +3448,7 @@ snapshots: optionalDependencies: '@types/node': 20.14.11 fsevents: 2.3.3 + sass: 1.77.8 which@2.0.2: dependencies: @@ -2864,6 +3472,9 @@ snapshots: yallist@3.1.1: {} + yaml@1.10.2: + optional: true + yaml@2.4.5: {} yocto-queue@0.1.0: {} diff --git a/task-calendar-frontend/src/App.css b/task-calendar-frontend/src/App.css index 1449855..3328fb2 100644 --- a/task-calendar-frontend/src/App.css +++ b/task-calendar-frontend/src/App.css @@ -2,7 +2,8 @@ margin: 0 auto; padding: 2rem; text-align: center; - background-color: #242424; + background-color: #fff; + color: #191919; width: 100%; max-width: 100%; height: 100%; diff --git a/task-calendar-frontend/src/App.tsx b/task-calendar-frontend/src/App.tsx index 55574e1..8a92eaf 100644 --- a/task-calendar-frontend/src/App.tsx +++ b/task-calendar-frontend/src/App.tsx @@ -1,10 +1,11 @@ // import { useState } from 'react' import { useEffect, useState } from "react"; import "./App.css"; -import TaskCalendar from './components/Calendars/Calendar'; -import axios from 'axios' +import TaskCalendar from "./components/Calendars/Calendar"; +import axios from "axios"; +import TopAppBar from "./components/TopAppBar"; -export type TaskProps = { +export type EventProps = { title: string; description: string; startDate: Date; @@ -12,12 +13,13 @@ export type TaskProps = { color: string; }; -const API_ENDPOINT = import.meta.env.VITE_TASK_ENDPOINT +const PREFIX = "/api/v1"; +export const API_ENDPOINT = import.meta.env.VITE_TASK_ENDPOINT + PREFIX; function App() { - const TODAY = new Date("12/16/1997"); + const TODAY = new Date(); - const TASKS: Array = [ + const EVENTS: Array = [ { title: "Task 1", color: "#7B9CD7", @@ -125,30 +127,36 @@ function App() { }, ]; - const [_tasks, setTasks] = useState - ([]) + const [_events, setEvents] = useState([]); useEffect(() => { - axios.get(API_ENDPOINT + '/tasks').then(response => { - setTasks(response.data.map((datum: TaskProps) => {return { title: datum.title, - color: datum.color, - description: datum.description, - startDate: new Date(datum.startDate), - endDate: new Date(datum.endDate) - }})) - }).catch(error => { - console.log(error) - setTasks(TASKS) + axios + .get(API_ENDPOINT + "/events") + .then((response) => { + setEvents( + response.data.map((datum: EventProps) => { + return { + title: datum.title, + color: datum.color, + description: datum.description, + startDate: new Date(datum.startDate), + endDate: new Date(datum.endDate), + }; + }), + ); + }) + .catch((error) => { + console.log(error); + setEvents(EVENTS); }); }, []); - return (
-

Task Calendar

+
- +
); diff --git a/task-calendar-frontend/src/components/Calendars/Calendar.tsx b/task-calendar-frontend/src/components/Calendars/Calendar.tsx index 3d2b9a1..d05317e 100644 --- a/task-calendar-frontend/src/components/Calendars/Calendar.tsx +++ b/task-calendar-frontend/src/components/Calendars/Calendar.tsx @@ -1,36 +1,36 @@ import { FC } from "react"; import CalendarHeader from "./CalendarHeader"; import CalendarCells from "./CalendarCells"; -import { TaskProps } from "../../App"; +import { EventProps } from "../../App"; import CalendarWeekBar from "./CalendarWeekBar"; const TaskCalendar: FC<{ date: Date; type: "weekly" | "daily" | "monthly"; - tasks: Array; -}> = ({ date, tasks }) => { + events: Array; +}> = ({ date, events }) => { const WEEKDAYS = ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"]; - const DAY_TASKS: { [key: string]: Array } = {}; - const LONG_TASKS: Array = []; + const DAY_EVENTS: { [key: string]: Array } = {}; + const LONG_EVENTS: Array = []; - tasks.forEach((task) => { - const START_DATE = task.startDate.toDateString(); - const END_DATE = task.endDate.toDateString(); + events.forEach((event) => { + const START_DATE = event.startDate.toDateString(); + const END_DATE = event.endDate.toDateString(); if (START_DATE === END_DATE) { - if (!DAY_TASKS[START_DATE]) DAY_TASKS[START_DATE] = []; + if (!DAY_EVENTS[START_DATE]) DAY_EVENTS[START_DATE] = []; - DAY_TASKS[START_DATE].push(task); + DAY_EVENTS[START_DATE].push(event); } else { - LONG_TASKS.push(task); + LONG_EVENTS.push(event); } }); return (
- - + +
); }; diff --git a/task-calendar-frontend/src/components/Calendars/CalendarCells.tsx b/task-calendar-frontend/src/components/Calendars/CalendarCells.tsx index f1acf9c..b6ab412 100644 --- a/task-calendar-frontend/src/components/Calendars/CalendarCells.tsx +++ b/task-calendar-frontend/src/components/Calendars/CalendarCells.tsx @@ -1,11 +1,11 @@ import { FC } from "react"; -import { TaskProps } from "../../App"; +import { EventProps } from "../../App"; const CalendarCells: FC<{ date: Date; - tasks: { [key: string]: Array }; + events: { [key: string]: Array }; weekdays: Array; -}> = ({ date, tasks, weekdays }) => { +}> = ({ date, events, weekdays }) => { // TODO: Refactor this code const DAYS_COLUMN = []; // TODO: Refactor this code @@ -18,7 +18,10 @@ const CalendarCells: FC<{ for (let min15Count = 0; min15Count < 24; min15Count++) { MIN_15_UNITS.push( -
+
@@ -27,28 +30,33 @@ const CalendarCells: FC<{ ); } DAYS_COLUMN.push( -
+
{MIN_15_UNITS}
- {tasks[CURRENT_DATE.toDateString()]?.map((task, index) => { + {events[CURRENT_DATE.toDateString()]?.map((event, index) => { const START_TIME_INDEX = - task.startDate.getHours() * 4 + - task.startDate.getMinutes() / 15 + + event.startDate.getHours() * 4 + + event.startDate.getMinutes() / 15 + 1; const END_TIME_INDEX = - task.endDate.getHours() * 4 + task.endDate.getMinutes() / 15 + 1; + event.endDate.getHours() * 4 + + event.endDate.getMinutes() / 15 + + 1; return (
- {task.title} + {event.title}
); })} @@ -64,7 +72,10 @@ const CalendarCells: FC<{ const ITEMS = []; for (let hourCount = 0; hourCount < 24; hourCount++) { ITEMS.push( -
+
{hourCount != 0 && hourCount + (hourCount > 11 ? " AM" : " PM")} diff --git a/task-calendar-frontend/src/components/Calendars/CalendarHeader.tsx b/task-calendar-frontend/src/components/Calendars/CalendarHeader.tsx index 0f774ec..3c9a972 100644 --- a/task-calendar-frontend/src/components/Calendars/CalendarHeader.tsx +++ b/task-calendar-frontend/src/components/Calendars/CalendarHeader.tsx @@ -10,7 +10,7 @@ const CalendarHeader: FC<{ date: Date; weekdays: Array }> = ({ const DAY_HEADERS = weekdays.map((day, index) => (
; - tasks: Array; -}> = ({ date, tasks }) => { + events: Array; +}> = ({ date, events }) => { return (
- {tasks.map((task, index) => { + {events.map((event, index) => { const IS_DATE_BEFORE = !isDateInRange( date, - task.startDate, + event.startDate, "week", "before", ); const IS_DATE_AFTER = !isDateInRange( date, - task.endDate, + event.endDate, "week", "after", ); - const START_DAY = IS_DATE_BEFORE ? 1 : task.startDate.getDay() + 1; - const END_DAY = IS_DATE_AFTER ? 8 : task.endDate.getDay() + 1; + const START_DAY = IS_DATE_BEFORE ? 1 : event.startDate.getDay() + 1; + const END_DAY = IS_DATE_AFTER ? 8 : event.endDate.getDay() + 1; // ! ******************************* // ! Fix col start and col end issue @@ -37,19 +37,20 @@ const CalendarWeekBar: FC<{ // ! ******************************* return ( -
- {task.title} + {event.title}
); })} diff --git a/task-calendar-frontend/src/components/TopAppBar.tsx b/task-calendar-frontend/src/components/TopAppBar.tsx new file mode 100644 index 0000000..ce69bd0 --- /dev/null +++ b/task-calendar-frontend/src/components/TopAppBar.tsx @@ -0,0 +1,113 @@ +import { FC, useState } from "react"; +import styles from "../styles/top-app-bar.module.scss"; +import { AdapterDayjs } from "@mui/x-date-pickers/AdapterDayjs"; +import { LocalizationProvider } from "@mui/x-date-pickers/LocalizationProvider"; +import dayjs from "dayjs"; +import Button from "@mui/material/Button"; +import AddIcon from "@mui/icons-material/Add"; +import { Drawer, FormControl, Stack, TextField } from "@mui/material"; +import { DateTimePicker } from "@mui/x-date-pickers"; +import { API_ENDPOINT } from "../App"; +import axios from "axios"; +import { getRandomHexColor } from "../utils/common"; + +interface Props { + date: Date; +} + +const TopAppBar: FC = ({ date }) => { + const [startDate, setStartDate] = useState(date); + const [endDate, setEndDate] = useState(date); + const [isDrawerOpen, setDrawerState] = useState(false); + const [title, setTitle] = useState(""); + const [desc, setDesc] = useState(""); + + function openNewTaskDrawer() { + setDrawerState(!isDrawerOpen); + } + + function createEvent() { + axios + .post(API_ENDPOINT + "/events/", { + title: title, + description: desc, + startDate: startDate, + endDate: endDate, + color: getRandomHexColor(), + }) + .then((response) => { + console.log(response); + setDrawerState(false); + }) + .catch((error) => { + console.log(error); + }); + } + + return ( +
+
Task Calendar
+
+ + setDrawerState(false)} + PaperProps={{ + sx: { + p: 2, + }, + }} + > + + setTitle(event.target.value)} + /> + setDesc(event.target.value)} + /> + + + Start Date: + + setStartDate(newValue ? newValue.toDate() : startDate) + } + /> + + + + + End Date: + + setEndDate(newValue ? newValue.toDate() : endDate) + } + /> + + + + + +
+
+ ); +}; + +export default TopAppBar; diff --git a/task-calendar-frontend/src/index.css b/task-calendar-frontend/src/index.css index 2952d80..f9c1666 100644 --- a/task-calendar-frontend/src/index.css +++ b/task-calendar-frontend/src/index.css @@ -9,7 +9,7 @@ color-scheme: light dark; color: rgba(255, 255, 255, 0.87); - background-color: #242424; + background-color: #fff; font-synthesis: none; text-rendering: optimizeLegibility; diff --git a/task-calendar-frontend/src/styles/top-app-bar.module.scss b/task-calendar-frontend/src/styles/top-app-bar.module.scss new file mode 100644 index 0000000..f6dd5b6 --- /dev/null +++ b/task-calendar-frontend/src/styles/top-app-bar.module.scss @@ -0,0 +1,14 @@ +.top_app_bar { + height: 65px; + color: #191919; + display: flex; + justify-content: space-between; + font-size: 20px; + line-height: 30px; + .left { + margin-right: 20px; + } + .drawer { + padding: 20px; + } +} \ No newline at end of file diff --git a/task-calendar-frontend/src/utils/common.ts b/task-calendar-frontend/src/utils/common.ts new file mode 100644 index 0000000..d0fd9b2 --- /dev/null +++ b/task-calendar-frontend/src/utils/common.ts @@ -0,0 +1,3 @@ +export function getRandomHexColor() { + return "#" + Math.floor(Math.random() * 16777215).toString(16); +} From 681752c94e6e748ad86799054e04a424e2775403 Mon Sep 17 00:00:00 2001 From: Vijay Singh Date: Sun, 1 Dec 2024 13:10:19 +0530 Subject: [PATCH 3/3] style: chnaged layout of calendar and styles --- task-calendar-frontend/package.json | 5 +- task-calendar-frontend/pnpm-lock.yaml | 57 ++++++ task-calendar-frontend/src/App.css | 5 +- task-calendar-frontend/src/App.tsx | 163 +----------------- .../src/components/Calendars/Calendar.tsx | 4 +- .../components/Calendars/CalendarCells.tsx | 17 +- .../components/Calendars/CalendarHeader.tsx | 34 +++- .../components/Calendars/CalendarWeekBar.tsx | 6 +- .../src/components/Main.tsx | 61 +++++++ .../src/components/Navbar.tsx | 25 +++ .../src/components/SideBar.tsx | 13 ++ .../src/components/TopAppBar.tsx | 2 +- .../src/styles/_variables.scss | 16 ++ task-calendar-frontend/src/styles/main.scss | 118 +++++++++++++ .../src/styles/sidebar.scss | 59 +++++++ .../src/utils/format-date.ts | 11 -- task-calendar-frontend/src/utils/format.ts | 27 +++ 17 files changed, 430 insertions(+), 193 deletions(-) create mode 100644 task-calendar-frontend/src/components/Main.tsx create mode 100644 task-calendar-frontend/src/components/Navbar.tsx create mode 100644 task-calendar-frontend/src/components/SideBar.tsx create mode 100644 task-calendar-frontend/src/styles/_variables.scss create mode 100644 task-calendar-frontend/src/styles/main.scss create mode 100644 task-calendar-frontend/src/styles/sidebar.scss delete mode 100644 task-calendar-frontend/src/utils/format-date.ts create mode 100644 task-calendar-frontend/src/utils/format.ts diff --git a/task-calendar-frontend/package.json b/task-calendar-frontend/package.json index 4210fa2..0a146f1 100644 --- a/task-calendar-frontend/package.json +++ b/task-calendar-frontend/package.json @@ -10,14 +10,15 @@ "preview": "vite preview" }, "dependencies": { - "@mui/material": "^6.0.1", "@mui/icons-material": "^6.0.1", + "@mui/material": "^6.0.1", "@mui/x-date-pickers": "^7.15.0", "axios": "^1.7.2", "dayjs": "^1.11.13", "dotenv": "^16.4.5", "react": "^18.3.1", - "react-dom": "^18.3.1" + "react-dom": "^18.3.1", + "react-router-dom": "^7.0.1" }, "devDependencies": { "@types/node": "^20.14.11", diff --git a/task-calendar-frontend/pnpm-lock.yaml b/task-calendar-frontend/pnpm-lock.yaml index 158ccbf..5e8e671 100644 --- a/task-calendar-frontend/pnpm-lock.yaml +++ b/task-calendar-frontend/pnpm-lock.yaml @@ -32,6 +32,9 @@ importers: react-dom: specifier: ^18.3.1 version: 18.3.1(react@18.3.1) + react-router-dom: + specifier: ^7.0.1 + version: 7.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) devDependencies: '@types/node': specifier: ^20.14.11 @@ -703,6 +706,9 @@ packages: '@types/babel__traverse@7.20.6': resolution: {integrity: sha512-r1bzfrm0tomOI8g1SzvCaQHo6Lcv6zu0EA+W2kHrt8dyrHQxGzBBL4kdkzIS+jBMV+EYcMAEAqXqYaLJq5rOZg==} + '@types/cookie@0.6.0': + resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==} + '@types/estree@1.0.5': resolution: {integrity: sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==} @@ -937,6 +943,10 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + cookie@1.0.2: + resolution: {integrity: sha512-9Kr/j4O16ISv8zBBhJoi4bXOYNTkFLOqSL3UDB0njXxCXNezjeyVrJyGOWtgfs/q2km1gwBcfH8q1yEGoMYunA==} + engines: {node: '>=18'} + cosmiconfig@7.1.0: resolution: {integrity: sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==} engines: {node: '>=10'} @@ -1544,6 +1554,23 @@ packages: resolution: {integrity: sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==} engines: {node: '>=0.10.0'} + react-router-dom@7.0.1: + resolution: {integrity: sha512-duBzwAAiIabhFPZfDjcYpJ+f08TMbPMETgq254GWne2NW1ZwRHhZLj7tpSp8KGb7JvZzlLcjGUnqLxpZQVEPng==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + + react-router@7.0.1: + resolution: {integrity: sha512-WVAhv9oWCNsja5AkK6KLpXJDSJCQizOIyOd4vvB/+eHGbYx5vkhcmcmwWjQ9yqkRClogi+xjEg9fNEOd5EX/tw==} + engines: {node: '>=20.0.0'} + peerDependencies: + react: '>=18' + react-dom: '>=18' + peerDependenciesMeta: + react-dom: + optional: true + react-transition-group@4.4.5: resolution: {integrity: sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==} peerDependencies: @@ -1606,6 +1633,9 @@ packages: engines: {node: '>=10'} hasBin: true + set-cookie-parser@2.7.1: + resolution: {integrity: sha512-IOc8uWeOZgnb3ptbCURJWNjWUPcO3ZnTTdzsurqERrP6nPyv+paC55vJM0LpOlT2ne+Ix+9+CRG1MNLlyZ4GjQ==} + shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} @@ -1709,6 +1739,9 @@ packages: tslib@2.6.3: resolution: {integrity: sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==} + turbo-stream@2.4.0: + resolution: {integrity: sha512-FHncC10WpBd2eOmGwpmQsWLDoK4cqsA/UT/GqNoaKOQnT8uzhtCbg3EoUDMvqpOSAI0S26mr0rkjzbOO6S3v1g==} + type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} @@ -2382,6 +2415,8 @@ snapshots: dependencies: '@babel/types': 7.24.9 + '@types/cookie@0.6.0': {} + '@types/estree@1.0.5': {} '@types/node@20.14.11': @@ -2647,6 +2682,8 @@ snapshots: convert-source-map@2.0.0: {} + cookie@1.0.2: {} + cosmiconfig@7.1.0: dependencies: '@types/parse-json': 4.0.2 @@ -3226,6 +3263,22 @@ snapshots: react-refresh@0.14.2: {} + react-router-dom@7.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + react: 18.3.1 + react-dom: 18.3.1(react@18.3.1) + react-router: 7.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1) + + react-router@7.0.1(react-dom@18.3.1(react@18.3.1))(react@18.3.1): + dependencies: + '@types/cookie': 0.6.0 + cookie: 1.0.2 + react: 18.3.1 + set-cookie-parser: 2.7.1 + turbo-stream: 2.4.0 + optionalDependencies: + react-dom: 18.3.1(react@18.3.1) + react-transition-group@4.4.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1): dependencies: '@babel/runtime': 7.25.4 @@ -3303,6 +3356,8 @@ snapshots: semver@7.6.3: {} + set-cookie-parser@2.7.1: {} + shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 @@ -3418,6 +3473,8 @@ snapshots: tslib@2.6.3: {} + turbo-stream@2.4.0: {} + type-check@0.4.0: dependencies: prelude-ls: 1.2.1 diff --git a/task-calendar-frontend/src/App.css b/task-calendar-frontend/src/App.css index 3328fb2..39ecde5 100644 --- a/task-calendar-frontend/src/App.css +++ b/task-calendar-frontend/src/App.css @@ -1,8 +1,7 @@ #root { - margin: 0 auto; - padding: 2rem; + margin: 0; + padding: 0; text-align: center; - background-color: #fff; color: #191919; width: 100%; max-width: 100%; diff --git a/task-calendar-frontend/src/App.tsx b/task-calendar-frontend/src/App.tsx index 8a92eaf..04c68ff 100644 --- a/task-calendar-frontend/src/App.tsx +++ b/task-calendar-frontend/src/App.tsx @@ -1,164 +1,15 @@ -// import { useState } from 'react' -import { useEffect, useState } from "react"; import "./App.css"; -import TaskCalendar from "./components/Calendars/Calendar"; -import axios from "axios"; -import TopAppBar from "./components/TopAppBar"; - -export type EventProps = { - title: string; - description: string; - startDate: Date; - endDate: Date; - color: string; -}; - -const PREFIX = "/api/v1"; -export const API_ENDPOINT = import.meta.env.VITE_TASK_ENDPOINT + PREFIX; +import Index from "./components/Main"; +import { BrowserRouter, Routes, Route } from "react-router-dom"; function App() { const TODAY = new Date(); - - const EVENTS: Array = [ - { - title: "Task 1", - color: "#7B9CD7", - description: "First task description", - startDate: new Date("1997-12-16T08:00:00"), // 8:00 AM - endDate: new Date("1997-12-16T09:15:00"), // 9:15 AM - }, - { - title: "Task 2", - color: "#1da1f2", - description: "Second task description", - startDate: new Date("1997-12-16T10:30:00"), // 10:30 AM - endDate: new Date("1997-12-16T11:45:00"), // 11:45 AM - }, - { - title: "Task 3", - color: "#DD4646", - description: "Third task description", - startDate: new Date("1997-12-16T13:15:00"), // 1:15 PM - endDate: new Date("1997-12-16T14:00:00"), // 2:00 PM - }, - { - title: "Task 4", - color: "#1da1f2", - description: "Write conclusion", - startDate: new Date("1997-12-16T15:30:00"), // 3:30 PM - endDate: new Date("1997-12-17T09:00:00"), // Next day, 9:00 AM - }, - { - title: "Task 5", - color: "#DD4646", - description: "Review draft", - startDate: new Date("1997-12-16T16:30:00"), // 4:30 PM - endDate: new Date("1997-12-17T10:00:00"), // Next day, 10:00 AM - }, - { - title: "Task 6", - color: "#eec900", - description: "Project kickoff meeting", - startDate: new Date("1997-12-16T14:00:00"), // 2:00 PM - endDate: new Date("1997-12-18T11:30:00"), // Third day, 11:30 AM - }, - { - title: "Task 7", - color: "#7BD699", - description: "Data Collection", - startDate: new Date("1997-12-14T09:00:00"), // Dec 14, 1997, 9:00 AM - endDate: new Date("1997-12-16T17:30:00"), // Dec 16, 1997, 5:30 PM - }, - { - title: "Task 8", - color: "#7B9CD7", - description: "Data Analysis", - startDate: new Date("1997-12-18T10:00:00"), // Dec 18, 1997, 10:00 AM - endDate: new Date("1997-12-20T15:45:00"), // Dec 20, 1997, 3:45 PM - }, - { - title: "Task 9", - color: "#7B9CD7", - description: "Data Analysis", - startDate: new Date("1997-12-12T10:00:00"), // Dec 18, 1997, 10:00 AM - endDate: new Date("1997-12-19T15:45:00"), // Dec 20, 1997, 3:45 PM - }, - { - title: "Task 10", - color: "#7B9CD7", - description: "Data Analysis", - startDate: new Date("1997-12-12T10:00:00"), // Dec 18, 1997, 10:00 AM - endDate: new Date("1997-12-21T15:45:00"), // Dec 20, 1997, 3:45 PM - }, - { - title: "Task 11", - color: "#7BD699", - description: "Data Collection", - startDate: new Date("1997-12-16T09:00:00"), // Dec 14, 1997, 9:00 AM - endDate: new Date("1997-12-16T17:30:00"), // Dec 16, 1997, 5:30 PM - }, - { - title: "Task 12", - color: "#eec900", - description: "Data Collection", - startDate: new Date("1997-12-16T14:00:00"), // Dec 14, 1997, 9:00 AM - endDate: new Date("1997-12-16T17:30:00"), // Dec 16, 1997, 5:30 PM - }, - { - title: "Task 13", - color: "#7B9CD7", - description: "Data Collection", - startDate: new Date("1997-12-16T09:00:00"), // Dec 14, 1997, 9:00 AM - endDate: new Date("1997-12-16T09:30:00"), // Dec 16, 1997, 5:30 PM - }, - { - title: "Task 14", - color: "#7B9CD7", - description: "Data Collection", - startDate: new Date("1997-12-16T09:00:00"), // Dec 14, 1997, 9:00 AM - endDate: new Date("1997-12-16T09:30:00"), // Dec 16, 1997, 5:30 PM - }, - { - title: "Task 15", - color: "#7BD699", - description: "Data Collection", - startDate: new Date("1997-12-18T09:00:00"), // Dec 14, 1997, 9:00 AM - endDate: new Date("1997-12-18T17:30:00"), // Dec 16, 1997, 5:30 PM - }, - ]; - - const [_events, setEvents] = useState([]); - useEffect(() => { - axios - .get(API_ENDPOINT + "/events") - .then((response) => { - setEvents( - response.data.map((datum: EventProps) => { - return { - title: datum.title, - color: datum.color, - description: datum.description, - startDate: new Date(datum.startDate), - endDate: new Date(datum.endDate), - }; - }), - ); - }) - .catch((error) => { - console.log(error); - setEvents(EVENTS); - }); - }, []); - return ( -
-
- -
-
- -
-
+ + + }> + + ); } diff --git a/task-calendar-frontend/src/components/Calendars/Calendar.tsx b/task-calendar-frontend/src/components/Calendars/Calendar.tsx index d05317e..970460c 100644 --- a/task-calendar-frontend/src/components/Calendars/Calendar.tsx +++ b/task-calendar-frontend/src/components/Calendars/Calendar.tsx @@ -1,7 +1,7 @@ import { FC } from "react"; import CalendarHeader from "./CalendarHeader"; import CalendarCells from "./CalendarCells"; -import { EventProps } from "../../App"; +import { EventProps } from "../Main"; import CalendarWeekBar from "./CalendarWeekBar"; const TaskCalendar: FC<{ @@ -27,7 +27,7 @@ const TaskCalendar: FC<{ } }); return ( -
+
diff --git a/task-calendar-frontend/src/components/Calendars/CalendarCells.tsx b/task-calendar-frontend/src/components/Calendars/CalendarCells.tsx index b6ab412..d11db1d 100644 --- a/task-calendar-frontend/src/components/Calendars/CalendarCells.tsx +++ b/task-calendar-frontend/src/components/Calendars/CalendarCells.tsx @@ -1,5 +1,6 @@ import { FC } from "react"; -import { EventProps } from "../../App"; +import { EventProps } from "../Main"; +import "../../styles/main.scss"; const CalendarCells: FC<{ date: Date; @@ -20,7 +21,7 @@ const CalendarCells: FC<{ MIN_15_UNITS.push(
@@ -32,7 +33,7 @@ const CalendarCells: FC<{ DAYS_COLUMN.push(
{MIN_15_UNITS}
@@ -66,17 +67,17 @@ const CalendarCells: FC<{ } return ( -
-
+
+
{(() => { const ITEMS = []; for (let hourCount = 0; hourCount < 24; hourCount++) { ITEMS.push(
- + {hourCount != 0 && hourCount + (hourCount > 11 ? " AM" : " PM")} @@ -86,7 +87,7 @@ const CalendarCells: FC<{ return ITEMS; })()}
-
{DAYS_COLUMN}
+
{DAYS_COLUMN}
); }; diff --git a/task-calendar-frontend/src/components/Calendars/CalendarHeader.tsx b/task-calendar-frontend/src/components/Calendars/CalendarHeader.tsx index 3c9a972..4df3591 100644 --- a/task-calendar-frontend/src/components/Calendars/CalendarHeader.tsx +++ b/task-calendar-frontend/src/components/Calendars/CalendarHeader.tsx @@ -1,5 +1,7 @@ import { FC } from "react"; -import formatDate from "../../utils/format-date"; +import { formatMonth, formatDate } from "../../utils/format"; +import { NavigateBefore, NavigateNext } from "@mui/icons-material"; +import "../../styles/main.scss"; const CalendarHeader: FC<{ date: Date; weekdays: Array }> = ({ date, @@ -7,13 +9,15 @@ const CalendarHeader: FC<{ date: Date; weekdays: Array }> = ({ }) => { const dayIndex = date.getDay(); const dateValue = date.getDate(); + const currentMonth = date.getMonth(); + const currentYear = date.getFullYear(); const DAY_HEADERS = weekdays.map((day, index) => (
@@ -24,10 +28,26 @@ const CalendarHeader: FC<{ date: Date; weekdays: Array }> = ({ )); return ( -
-
-
{DAY_HEADERS}
-
+ <> +
+

+ {formatMonth(currentMonth)} {currentYear} +

+
+ + + +
+
+
+
+
{DAY_HEADERS}
+
+ ); }; diff --git a/task-calendar-frontend/src/components/Calendars/CalendarWeekBar.tsx b/task-calendar-frontend/src/components/Calendars/CalendarWeekBar.tsx index 4ecab59..2e4a9c6 100644 --- a/task-calendar-frontend/src/components/Calendars/CalendarWeekBar.tsx +++ b/task-calendar-frontend/src/components/Calendars/CalendarWeekBar.tsx @@ -1,5 +1,5 @@ import { FC } from "react"; -import { EventProps } from "../../App"; +import { EventProps } from "../Main"; import isDateInRange from "../../utils/isDateBefore"; const CalendarWeekBar: FC<{ @@ -9,8 +9,8 @@ const CalendarWeekBar: FC<{ }> = ({ date, events }) => { return (
-
-
+
+
{events.map((event, index) => { const IS_DATE_BEFORE = !isDateInRange( date, diff --git a/task-calendar-frontend/src/components/Main.tsx b/task-calendar-frontend/src/components/Main.tsx new file mode 100644 index 0000000..f642d57 --- /dev/null +++ b/task-calendar-frontend/src/components/Main.tsx @@ -0,0 +1,61 @@ +import "../styles/main.scss"; +import { FC, useEffect, useState } from "react"; +import TaskCalendar from "./Calendars/Calendar"; +import axios from "axios"; +// import TopAppBar from "./TopAppBar"; +import SideBar from "./SideBar"; +import Navbar from "./Navbar"; + +const PREFIX = "/api/v1"; +export const API_ENDPOINT = import.meta.env.VITE_TASK_ENDPOINT + PREFIX; + +export type EventProps = { + title: string; + description: string; + startDate: Date; + endDate: Date; + color: string; +}; + +interface Props { + date: Date; +} +const Main: FC = ({ date }) => { + const EVENTS: Array = []; + + const [_events, setEvents] = useState([]); + useEffect(() => { + axios + .get(API_ENDPOINT + "/events") + .then((response) => { + setEvents( + response.data.map((datum: EventProps) => { + return { + title: datum.title, + color: datum.color, + description: datum.description, + startDate: new Date(datum.startDate), + endDate: new Date(datum.endDate), + }; + }), + ); + }) + .catch((error) => { + console.log(error); + setEvents(EVENTS); + }); + }, []); + return ( +
+
+ +
+ + +
+
+
+ ); +}; + +export default Main; diff --git a/task-calendar-frontend/src/components/Navbar.tsx b/task-calendar-frontend/src/components/Navbar.tsx new file mode 100644 index 0000000..0d14de6 --- /dev/null +++ b/task-calendar-frontend/src/components/Navbar.tsx @@ -0,0 +1,25 @@ +import { NavLink } from "react-router-dom"; +import "../styles/main.scss"; +import { FC } from "react"; +interface Props {} + +const Navbar: FC = () => { + return ( +
+ + Day + + + Week + + + Month + + + Year + +
+ ); +}; + +export default Navbar; diff --git a/task-calendar-frontend/src/components/SideBar.tsx b/task-calendar-frontend/src/components/SideBar.tsx new file mode 100644 index 0000000..de80af7 --- /dev/null +++ b/task-calendar-frontend/src/components/SideBar.tsx @@ -0,0 +1,13 @@ +import "../styles/sidebar.scss"; +import { FC } from "react"; +interface Props {} + +const SideBar: FC = () => { + return ( +
+

Hanna Kazak

+
+ ); +}; + +export default SideBar; diff --git a/task-calendar-frontend/src/components/TopAppBar.tsx b/task-calendar-frontend/src/components/TopAppBar.tsx index ce69bd0..6c29dd3 100644 --- a/task-calendar-frontend/src/components/TopAppBar.tsx +++ b/task-calendar-frontend/src/components/TopAppBar.tsx @@ -7,7 +7,7 @@ import Button from "@mui/material/Button"; import AddIcon from "@mui/icons-material/Add"; import { Drawer, FormControl, Stack, TextField } from "@mui/material"; import { DateTimePicker } from "@mui/x-date-pickers"; -import { API_ENDPOINT } from "../App"; +import { API_ENDPOINT } from "./Main"; import axios from "axios"; import { getRandomHexColor } from "../utils/common"; diff --git a/task-calendar-frontend/src/styles/_variables.scss b/task-calendar-frontend/src/styles/_variables.scss new file mode 100644 index 0000000..13dc571 --- /dev/null +++ b/task-calendar-frontend/src/styles/_variables.scss @@ -0,0 +1,16 @@ +// color codes +$grayBackgroundColor: #e4e4e4; +$lightGrayBackgroundColor: #f3f3f3; +$whiteBackgroundColor: #fff; +$lightWhiteBackgroundColor: #fcfcfc; +$softMintGreenColor: #bde6d8; +$softPurpleColor: #dab7f3; +$softOrangeColor: #f9b6a4; +$lightBlueColor: #d9e4f6; +$primaryTextColor: #333; +$secondaryTextColor: #555; +$primaryBorderColor: rgba(0, 0, 0, 0.1); +$secondaryBorderColor: #e3e3e3; +$activeButtonColor: #a5c7c7; +// font styles +$fontFamily: Arial, sans-serif; \ No newline at end of file diff --git a/task-calendar-frontend/src/styles/main.scss b/task-calendar-frontend/src/styles/main.scss new file mode 100644 index 0000000..6a25e9b --- /dev/null +++ b/task-calendar-frontend/src/styles/main.scss @@ -0,0 +1,118 @@ +@import './_variables.scss'; + +.calendar-container { + font-family: $fontFamily; + display: flex; + padding: 50px; + background-color: $grayBackgroundColor; +} +/* Main Calendar View */ +.calendar-view { + flex: 1; + background-color: $lightWhiteBackgroundColor; + padding: 20px; + border-radius: 0 20px 20px 0; + .calendar-navbar { + display: flex; + margin: auto; + background-color: $lightGrayBackgroundColor; + border-radius: 5px; + width: fit-content; + box-sizing: border-box; + padding: 5px 10px; + .nav-btn { + padding: 5px 15px; + &.active { + border: 1px solid #e3e3e3; + border-radius: 5px; + background-color: $lightWhiteBackgroundColor; + } + } + } +} + +.calendar-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; + h3 { + font-size: 20px; + font-weight: bold; + color: #333333; + } + .view-buttons{ + display: flex; + gap: 10px; + button { + background-color: #fff; + border: 1px solid #e3e3e3; + border-radius: 2px; + height: 35px; + padding: 5px 15px; + display: flex; + flex-direction: column; + justify-content: center; + font-size: 18px; + color: #333333; + cursor: pointer; + &:hover { + color: #66b3ff; + } + &.active { + color: #66b3ff; + } + } + } +} + +.day { + background-color: #fff; + border: 1px solid #e3e3e3; + border-radius: 5px; + height: 120px; + padding: 10px; + display: flex; + flex-direction: column; + justify-content: flex-start; + font-size: 14px; + color: #333333; + &.today { + color: $activeButtonColor; + border-bottom: 4px solid $activeButtonColor; + } +} + +/* Event Cards */ +.event { + margin-top: 5px; + padding: 5px 10px; + border-radius: 5px; + font-size: 12px; + color: #ffffff; + display: flex; + justify-content: space-between; + align-items: center; + &.personal { background-color: #bde6d8; color: #333333; } + &.work { background-color: #dab7f3; color: #333333; } + &.special { background-color: #f9b6a4; color: #333333; } + &.workout { background-color: #d9e4f6; color: #333333; } + span.time { + font-size: 10px; + color: #ffffff; + } +} + +/* Settings and Info */ +.settings { + margin-top: 20px; + font-size: 12px; + color: #555555; + a { + text-decoration: none; + color: #66b3ff; + &:hover { + text-decoration: underline; + } + } +} diff --git a/task-calendar-frontend/src/styles/sidebar.scss b/task-calendar-frontend/src/styles/sidebar.scss new file mode 100644 index 0000000..01c860c --- /dev/null +++ b/task-calendar-frontend/src/styles/sidebar.scss @@ -0,0 +1,59 @@ +@import './_variables.scss'; + +.calendar-sidebar { + width: 20%; + padding: 15px; + background-color: $lightGrayBackgroundColor; + border-radius: 20px 0 0 20px; +} + +.calendar-sidebar h2 { + font-size: 18px; + font-weight: bold; + margin-bottom: 20px; +} + +.calendar-sidebar .month-picker { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.calendar-sidebar .month-picker button { + background-color: #66b3ff; + color: white; + border: none; + padding: 5px 10px; + border-radius: 5px; + cursor: pointer; + font-size: 14px; +} + +.calendar-sidebar .month-picker button:hover { + background-color: #4d9ae6; +} + +.calendar-sidebar .calendar-view { +margin-top: 20px; +} + +.calendar-sidebar .event-category { +display: flex; +align-items: center; +margin-top: 10px; +font-size: 14px; +color: #333333; +} + +.calendar-sidebar .event-category span { +display: inline-block; +width: 15px; +height: 15px; +border-radius: 50%; +margin-right: 10px; +} + +.calendar-sidebar .event-category.personal { background-color: #bde6d8; } +.calendar-sidebar .event-category.work { background-color: #dab7f3; } +.calendar-sidebar .event-category.event { background-color: #f9b6a4; } \ No newline at end of file diff --git a/task-calendar-frontend/src/utils/format-date.ts b/task-calendar-frontend/src/utils/format-date.ts deleted file mode 100644 index 3c846d0..0000000 --- a/task-calendar-frontend/src/utils/format-date.ts +++ /dev/null @@ -1,11 +0,0 @@ -const formatDate = (date: number) => { - const J = date % 10, - K = date % 100; - - if (J === 1 && K !== 11) return `${date}st`; - else if (J === 2 && K !== 12) return `${date}nd`; - else if (J === 3 && K !== 13) return `${date}rd`; - else return `${date}th`; -}; - -export default formatDate; diff --git a/task-calendar-frontend/src/utils/format.ts b/task-calendar-frontend/src/utils/format.ts new file mode 100644 index 0000000..f033ac5 --- /dev/null +++ b/task-calendar-frontend/src/utils/format.ts @@ -0,0 +1,27 @@ +export const formatDate = (date: number) => { + const J = date % 10, + K = date % 100; + + if (J === 1 && K !== 11) return `${date}st`; + else if (J === 2 && K !== 12) return `${date}nd`; + else if (J === 3 && K !== 13) return `${date}rd`; + else return `${date}th`; +}; + +export const formatMonth = (month: number) => { + const months = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec", + ]; + return months[month - 1]; +};