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/,