diff --git a/justhink_client/src/API/GroupService.js b/justhink_client/src/API/GroupService.js new file mode 100644 index 0000000..09210d8 --- /dev/null +++ b/justhink_client/src/API/GroupService.js @@ -0,0 +1,9 @@ +import api from "../api"; + +export default class GroupService { + static async getAllUsersGroups() { + const response = await api.get("/api/groups/"); + return response; + } +} + diff --git a/justhink_client/src/components/UI/Modal/Modal.jsx b/justhink_client/src/components/UI/Modal/Modal.jsx new file mode 100644 index 0000000..9608c5c --- /dev/null +++ b/justhink_client/src/components/UI/Modal/Modal.jsx @@ -0,0 +1,20 @@ +import React from "react"; +import cl from "./Modal.module.css"; + +const Modal = ({ children, visible, setVisible }) => { + const rootClasses = [cl.myModal]; + + if (visible) { + rootClasses.push(cl.active); + } + + return ( +
setVisible(false)}> +
e.stopPropagation()}> + {children} +
+
+ ); +}; + +export default Modal; diff --git a/justhink_client/src/components/UI/Modal/Modal.module.css b/justhink_client/src/components/UI/Modal/Modal.module.css new file mode 100644 index 0000000..764c8c9 --- /dev/null +++ b/justhink_client/src/components/UI/Modal/Modal.module.css @@ -0,0 +1,22 @@ +.myModal { + position: fixed; + top: 0; + bottom: 0; + right: 0; + left: 0; + display: none; + background: rgba(0, 0, 0, 0.5); +} + +.myModalContent { + padding: 25px; + background: white; + border-radius: 16px; + min-width: 250px; +} + +.myModal.active { + display: flex; + justify-content: center; + align-items: center; +} \ No newline at end of file diff --git a/justhink_client/src/pages/DeckId.jsx b/justhink_client/src/pages/DeckId.jsx index 5c66c22..f42b4a8 100644 --- a/justhink_client/src/pages/DeckId.jsx +++ b/justhink_client/src/pages/DeckId.jsx @@ -3,24 +3,41 @@ import CardSlider from "../components/UI/CardSlider/CardSlider"; import DeckService from "../API/DeckService"; import { useParams } from "react-router-dom"; import { useFetching } from "../hooks/useFetching"; +import Modal from "../components/UI/Modal/Modal"; +import GroupService from "../API/GroupService"; const DeckId = () => { const params = useParams(); const [deck, setDeck] = useState({}); + const [modal, setModal] = useState(false); + const [groups, setGroups] = useState([]); const [fetchDeckById, isLoading, error] = useFetching(async () => { const response = await DeckService.getById(params.id); setDeck(response.data); }); + const [fetchGroups, grLoading, grError] = useFetching(async () => { + const response = await GroupService.getAllUsersGroups(); + setGroups(response.data); + }); + useEffect(() => { fetchDeckById(); + fetchGroups(); }, []); return (
{/* */} {deck.name} - + + + {groups.map((group, id) => ( +
+
{group.name}
+
+ ))} +
); }; diff --git a/justhink_client/src/router/index.js b/justhink_client/src/router/index.js index 6db00b2..0cb463b 100644 --- a/justhink_client/src/router/index.js +++ b/justhink_client/src/router/index.js @@ -23,5 +23,7 @@ export const routes = [ { path: "/register", element: }, { path: "/decks", element: }, { path: "/decks/:id", element: }, + // { path: "/groups", element: }, + // { path: "/groups/:id", element: }, { path: "/lobby/:uid", element: }, ]; diff --git a/justhink_server/justhink/auth/serializers.py b/justhink_server/justhink/auth/serializers.py index e878e94..7237b70 100644 --- a/justhink_server/justhink/auth/serializers.py +++ b/justhink_server/justhink/auth/serializers.py @@ -14,9 +14,6 @@ class Meta: extra_kwargs = {"password": {"write_only": True}} def create(self, validated_data): - # user = core.models.User.objects.create(**validated_data) - # return user - user = core.models.User(username=validated_data["username"]) user.set_password(validated_data["password"]) user.save() diff --git a/justhink_server/justhink/cards/models.py b/justhink_server/justhink/cards/models.py index 95cbf90..dab24be 100644 --- a/justhink_server/justhink/cards/models.py +++ b/justhink_server/justhink/cards/models.py @@ -17,12 +17,12 @@ class Card(django.db.models.Model): on_delete=django.db.models.CASCADE, ) - def __str__(self): - return self.text - class Meta: verbose_name = "карта" verbose_name_plural = "карты" + def __str__(self): + return self.text + __all__ = [Card] diff --git a/justhink_server/justhink/core/migrations/0003_alter_user_options_user_friends.py b/justhink_server/justhink/core/migrations/0003_alter_user_options_user_friends.py new file mode 100644 index 0000000..f30f7d5 --- /dev/null +++ b/justhink_server/justhink/core/migrations/0003_alter_user_options_user_friends.py @@ -0,0 +1,28 @@ +# Generated by Django 5.1.1 on 2024-11-16 11:42 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("core", "0002_alter_user_username"), + ] + + operations = [ + migrations.AlterModelOptions( + name="user", + options={ + "verbose_name": "пользователь", + "verbose_name_plural": "пользователи", + }, + ), + migrations.AddField( + model_name="user", + name="friends", + field=models.ManyToManyField( + blank=True, to=settings.AUTH_USER_MODEL + ), + ), + ] diff --git a/justhink_server/justhink/core/models.py b/justhink_server/justhink/core/models.py index 1a2802f..e7bbe71 100644 --- a/justhink_server/justhink/core/models.py +++ b/justhink_server/justhink/core/models.py @@ -1,16 +1,8 @@ -import pathlib -import uuid - import django.contrib.auth import django.core.exceptions import django.db.models -def get_path_image(instance, filename): - file_extension = pathlib.Path(filename).suffix - return f"users/{uuid.uuid4()}{file_extension}" - - class User(django.contrib.auth.models.AbstractUser): username = django.db.models.CharField( verbose_name="никнейм", @@ -24,10 +16,14 @@ class User(django.contrib.auth.models.AbstractUser): null=True, blank=True, ) - # image = django.db.models.ImageField( - # "аватарка", - # help_text="Загрузите аватарку", - # upload_to=get_path_image, - # null=True, - # blank=True, - # ) + friends = django.db.models.ManyToManyField( + "self", + blank=True, + ) + + class Meta: + verbose_name = "пользователь" + verbose_name_plural = "пользователи" + + def __str__(self): + return self.username diff --git a/justhink_server/justhink/deck/models.py b/justhink_server/justhink/deck/models.py index 8764fb7..3257d47 100644 --- a/justhink_server/justhink/deck/models.py +++ b/justhink_server/justhink/deck/models.py @@ -40,12 +40,12 @@ class Languages(models.TextChoices): related_name="decks", ) - def __str__(self): - return self.name - class Meta: verbose_name = "колода" verbose_name_plural = "колоды" + def __str__(self): + return self.name + __all__ = [Deck] diff --git a/justhink_server/justhink/groups/__init__.py b/justhink_server/justhink/groups/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/justhink_server/justhink/groups/admin.py b/justhink_server/justhink/groups/admin.py new file mode 100644 index 0000000..780d916 --- /dev/null +++ b/justhink_server/justhink/groups/admin.py @@ -0,0 +1,14 @@ +import django.contrib.admin + +import groups.models + + +@django.contrib.admin.register(groups.models.Group) +class AdminGroup(django.contrib.admin.ModelAdmin): + list_display = ( + groups.models.Group.name.field.name, + groups.models.Group.created_at.field.name, + ) + + +__all__ = [] diff --git a/justhink_server/justhink/groups/apps.py b/justhink_server/justhink/groups/apps.py new file mode 100644 index 0000000..70a187c --- /dev/null +++ b/justhink_server/justhink/groups/apps.py @@ -0,0 +1,7 @@ +from django.apps import AppConfig + + +class GroupsConfig(AppConfig): + default_auto_field = "django.db.models.BigAutoField" + name = "groups" + verbose_name = "Группы" diff --git a/justhink_server/justhink/groups/migrations/0001_initial.py b/justhink_server/justhink/groups/migrations/0001_initial.py new file mode 100644 index 0000000..aa49b51 --- /dev/null +++ b/justhink_server/justhink/groups/migrations/0001_initial.py @@ -0,0 +1,52 @@ +# Generated by Django 5.1.1 on 2024-11-16 12:16 + +from django.conf import settings +from django.db import migrations, models + + +class Migration(migrations.Migration): + + initial = True + + dependencies = [ + migrations.swappable_dependency(settings.AUTH_USER_MODEL), + ] + + operations = [ + migrations.CreateModel( + name="Group", + fields=[ + ( + "id", + models.BigAutoField( + auto_created=True, + primary_key=True, + serialize=False, + verbose_name="ID", + ), + ), + ( + "name", + models.CharField( + blank=True, + help_text="Придумайте название", + max_length=255, + verbose_name="название", + ), + ), + ("created_at", models.DateTimeField(auto_now_add=True)), + ( + "users", + models.ManyToManyField( + blank=True, + related_name="user_groups", + to=settings.AUTH_USER_MODEL, + ), + ), + ], + options={ + "verbose_name": "Группа", + "verbose_name_plural": "Группы", + }, + ), + ] diff --git a/justhink_server/justhink/groups/migrations/__init__.py b/justhink_server/justhink/groups/migrations/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/justhink_server/justhink/groups/models.py b/justhink_server/justhink/groups/models.py new file mode 100644 index 0000000..ec884c5 --- /dev/null +++ b/justhink_server/justhink/groups/models.py @@ -0,0 +1,27 @@ +import django.db.models + +import core.models + + +class Group(django.db.models.Model): + name = django.db.models.CharField( + verbose_name="название", + help_text="Придумайте название", + max_length=255, + blank=True, + ) + users = django.db.models.ManyToManyField( + core.models.User, + blank=True, + related_name="user_groups", + ) + created_at = django.db.models.DateTimeField( + auto_now_add=True, + ) + + class Meta: + verbose_name = "Группа" + verbose_name_plural = "Группы" + + def __str__(self): + return self.name diff --git a/justhink_server/justhink/groups/permissions.py b/justhink_server/justhink/groups/permissions.py new file mode 100644 index 0000000..6a292aa --- /dev/null +++ b/justhink_server/justhink/groups/permissions.py @@ -0,0 +1,6 @@ +from rest_framework import permissions + + +class IsGroupParticipant(permissions.BasePermission): + def has_object_permission(self, request, view, obj): + return obj.users.filter(id=request.user.id).exists() diff --git a/justhink_server/justhink/groups/serializers.py b/justhink_server/justhink/groups/serializers.py new file mode 100644 index 0000000..5cfcb4d --- /dev/null +++ b/justhink_server/justhink/groups/serializers.py @@ -0,0 +1,18 @@ +import rest_framework.serializers + +import groups.models + + +class GroupSerializer(rest_framework.serializers.ModelSerializer): + class Meta: + model = groups.models.Group + fields = (groups.models.Group.name.field.name,) + + def create(self, validated_data): + user = self.context["request"].user + group = groups.models.Group.objects.create(**validated_data) + group.users.add(user) + return group + + +__all__ = [GroupSerializer] diff --git a/justhink_server/justhink/groups/tests.py b/justhink_server/justhink/groups/tests.py new file mode 100644 index 0000000..7ce503c --- /dev/null +++ b/justhink_server/justhink/groups/tests.py @@ -0,0 +1,3 @@ +from django.test import TestCase + +# Create your tests here. diff --git a/justhink_server/justhink/groups/views.py b/justhink_server/justhink/groups/views.py new file mode 100644 index 0000000..9b2b74f --- /dev/null +++ b/justhink_server/justhink/groups/views.py @@ -0,0 +1,23 @@ +import rest_framework.generics +import rest_framework.viewsets +import rest_framework.decorators +import rest_framework.permissions + +import groups.models +import groups.serializers +import groups.permissions + + +class GroupViewSet(rest_framework.viewsets.ModelViewSet): + queryset = groups.models.Group.objects.all() + serializer_class = groups.serializers.GroupSerializer + permission_classes = [ + rest_framework.permissions.IsAuthenticated, + groups.permissions.IsGroupParticipant, + ] + + def get_queryset(self): + return groups.models.Group.objects.filter(users=self.request.user) + + +__all__ = [GroupViewSet] diff --git a/justhink_server/justhink/justhink/routers.py b/justhink_server/justhink/justhink/routers.py index bda7417..f82814d 100644 --- a/justhink_server/justhink/justhink/routers.py +++ b/justhink_server/justhink/justhink/routers.py @@ -2,6 +2,7 @@ import deck.views import cards.views +import groups.views deck_router = routers.SimpleRouter() @@ -10,3 +11,5 @@ card_router = routers.SimpleRouter() card_router.register(r"card", cards.views.CardViewSet) +group_router = routers.SimpleRouter() +group_router.register(r"groups", groups.views.GroupViewSet) diff --git a/justhink_server/justhink/justhink/settings.py b/justhink_server/justhink/justhink/settings.py index f006670..304f1bf 100644 --- a/justhink_server/justhink/justhink/settings.py +++ b/justhink_server/justhink/justhink/settings.py @@ -37,6 +37,7 @@ "deck.apps.DeckConfig", "lobby.apps.LobbyConfig", "users.apps.UsersConfig", + "groups.apps.GroupsConfig", "core.apps.CoreConfig", "rest_framework", "corsheaders", diff --git a/justhink_server/justhink/justhink/urls.py b/justhink_server/justhink/justhink/urls.py index 13b4662..63e896e 100644 --- a/justhink_server/justhink/justhink/urls.py +++ b/justhink_server/justhink/justhink/urls.py @@ -18,6 +18,9 @@ django.urls.path( API_PREFIX, django.urls.include(justhink.routers.card_router.urls) ), + django.urls.path( + API_PREFIX, django.urls.include(justhink.routers.group_router.urls) + ), django.urls.path(API_PREFIX + "user/", django.urls.include(auth.urls)), # django.urls.path(API_PREFIX + "auth/", django.urls.include("rest_framework.urls")), django.urls.path( diff --git a/justhink_server/setup.cfg b/justhink_server/setup.cfg index 9ebf07f..6c41a9b 100644 --- a/justhink_server/setup.cfg +++ b/justhink_server/setup.cfg @@ -1,6 +1,6 @@ [flake8] inline-quotes = double -application_import_names = core, cards, deck, lobby, users +application_import_names = core, cards, deck, lobby, users, groups import-order-style = google exclude = */migrations/,