Refactoring according to Puillrequest comments

This commit is contained in:
Lorenz Padberg 2023-08-23 14:29:44 +02:00
parent 139bb545d8
commit bb54aeb145
17 changed files with 127 additions and 201 deletions

View File

@ -5,16 +5,15 @@
v-if="module.id"
>
<div class="module__header">
<div>
<h2
class="module__meta-title"
id="meta-title"
>
{{ module.metaTitle }}
</h2>
</div>
<div>
<div class="module__categoryindicators">
<pill :text="module.level?.name"></pill>
<pill :text="module.category?.name"></pill>
</div>
@ -178,12 +177,12 @@
display: flex;
justify-content: flex-start;
align-items: stretch;
margin-bottom: 10px;
margin-bottom: $small-spacing;
}
&__meta-title {
@include meta-title;
margin-right: 20px;
margin-right: $medium-spacing;
}

View File

@ -36,13 +36,11 @@
<script setup lang="ts">
import {computed, ref} from "vue";
import ModuleTeaser from "@/components/modules/ModuleTeaser.vue";
import gql from "graphql-tag";
import {useQuery, useMutation} from "@vue/apollo-composable";
import Dropdown from "@/components/ui/Dropdown.vue";
import PillRadioButtons from "@/components/ui/PillRadioButtons.vue";
import gql from "graphql-tag";
// TODO: Fix console warnings... onBeforeUnmount is called when there is no active component instance to be associated with.
// TODO: Improve Typing
const props = defineProps<{
modules: Object[];
@ -106,15 +104,12 @@
filterExpression = filterByLevel(module, selectedLevel) &&
filterByCategory(module, selectedCategory) &&
filterByLanguage(module, selectedLanguage);
//updateLastModuleLevel(selectedLevel.value);
return filterExpression;
}
const updateLevel = (level) => {
selectedLevel.value = level;
updateLastModuleLevelUser(level);
console.log("selectedLevel", selectedLevel.value)
}
const filterByLevel = (module, level) => {
@ -126,6 +121,7 @@
}
const filterByLanguage = (module: Object, language: Object) => {
// TODO: implement filter by language here.
console.log("selectedLanguage", selectedLanguage.value, language);
console.log("module.languages", module);
return true
@ -134,17 +130,19 @@
function updateLastModuleLevelUser(moduleLevel: Object) {
const {mutate: updateLastModuleLevel} = useMutation(gql`
mutation ($input: UpdateLastModuleLevelInput!){updateLastModuleLevel(input: $input) {
clientMutationId
user {
username
lastModuleLevel {
name
id
}
}
}}`);
const {mutate: updateLastModuleLevel} = useMutation(gql `
mutation ($input: UpdateLastModuleLevelInput!){
updateLastModuleLevel(input: $input) {
clientMutationId
user {
username
lastModuleLevel {
name
id
}
}
}
}`);
updateLastModuleLevel({
input: {
@ -161,21 +159,19 @@
.module-filter {
// TODO: how do I correcty set the with of the whole thig including the grid for the modules?
// TODO: Farbe des Arrows für Dropdowns muss platfrom habhängig sein
// TODO: Farbe des Arrows für Dropdowns muss platfrom habhängig sein MS-775
width: 75%;
&__filterselection {
@include desktop;
display: flex;
flex-wrap: wrap;
justify-content: space-between;
}
&__dropdown {
margin-right: 20px;
margin-right: $medium-spacing;
width: 300px;
}
&__language-selection {
@ -184,19 +180,7 @@
}
}
.topic {
display: grid;
padding: $large-spacing $small-spacing;
@include desktop {
padding: $large-spacing 0;
}
grid-template-columns: 1fr;
@include desktop {
grid-template-columns: 300px 1fr;
}
&__modules {
margin-top: 40px;
display: flex;
@ -211,14 +195,6 @@
grid-template-columns: repeat(3, minmax(auto, 380px));
}
}
&__teaser {
color: $color-charcoal-dark;
width: 90%;
@include lead-paragraph;
margin-bottom: $large-spacing;
}
}

View File

@ -100,7 +100,7 @@
}
&__pills {
margin-top: 20px;
margin-top: $medium-spacing;
}
}
</style>

View File

@ -30,8 +30,6 @@ import Avatar from '@/components/profile/Avatar.vue';
import { defineAsyncComponent } from 'vue';
const TrashIcon = defineAsyncComponent(() => import('@/components/icons/TrashIcon.vue'));
// TODO: Kann das mit me.ts zusammengeführt werden?
export default {
components: {
AvatarUploadForm,

View File

@ -1,36 +1,33 @@
<template>
<div
class="class-selection"
class="dropdown"
v-if="selectedItem"
>
<div
data-cy="class-selection"
class="class-selection__selected-class selected-class"
data-cy="dropdown"
class="dropdown__selected-item"
@click.stop="toggle()"
>
<span class="selected-class__text"> {{ selectedItem.name }}</span>
<ChevronDown class="selected-class__dropdown-icon" :class="{'rotate-chevron': showPopover}"
<span class="dropdown__selected-item-text"> {{ selectedItem.name }}</span>
<ChevronDown class="dropdown__dropdown-icon" :class="{'rotate-chevron': showPopover}"
/>
</div>
<transition name="scaleY">
<WidgetPopover
:mobile="mobile"
class="class-selection__popover"
class="dropdown__popover"
v-if="showPopover"
@hide-me="showPopover = false"
>
<li
:label="selectedItem.name"
:item="selectedItem"
data-cy="class-selection-entry"
data-cy="dropdown-item"
class="popover-links__link popover-links__link--large"
v-for="item in items"
:key="item.id"
@click="updateSelectedItem(item)"
><span>{{ item.name }}</span>
>{{ item.name }}
</li>
</WidgetPopover>
</transition>
</div>
@ -64,12 +61,12 @@
<style scoped lang="scss">
@import 'styles/helpers';
.class-selection {
.dropdown {
position: relative;
cursor: pointer;
margin-bottom: $medium-spacing;
border-radius: 4px;
width: 200px;
border-radius: $input-border-radius;
&__popover {
white-space: nowrap;
@ -77,61 +74,55 @@
left: 0;
transform-origin: top;
}
}
.selected-class {
width: 100%;
box-sizing: border-box;
padding: $small-spacing 0;
&__selected-item {
width: 100%;
box-sizing: border-box;
display: flex;
align-items: center;
justify-content: space-between;
border: solid $color-silver 1px;
border-radius: $input-border-radius;
padding: 12px;
}
display: flex;
align-items: center;
justify-content: space-between;
border: solid silver 1px;
border-radius: 4px;
padding: 12px;
&__text {
&__selected-item-text {
@include small-text;
margin-right: $small-spacing;
}
&__dropdown-icon {
width: 20px;
height: 20px;
width: $default-icon-dimension;
height: $default-icon-dimension;
transition: transform 0.3s ease;
fill: $color-brand;
fill: #139EE0;
&.rotate-chevron { // This class will be applied when showPopover is true
&.rotate-chevron {
transform: rotate(180deg);
}
}
}
.widget-popover {
width: 100%;
border-radius: 12px;
margin-top: 4px;
&__popover {
width: 100%;
border-radius: $default-border-radius;
margin-top: 4px;
.popover-links__link {
&:hover {
background-color: #efefef;
.popover-links__link {
&:hover {
background-color: $color-silver-light;
}
}
}
}
.scaleY-enter-active,
.scaleY-leave-active {
transition: transform 0.3s;
}
.scaleY-enter,
.scaleY-leave-to
{
.scaleY-leave-to {
transform: scaleY(0);
}
</style>

View File

@ -12,13 +12,13 @@
@import 'styles/helpers';
.pill {
background-color: white; /* Replace with your desired background color */
color: #333333; /* Replace with your desired text color */
padding: 10px 20px;
border-radius: 30px;
border: 1px solid silver;
background-color: white;
color: $color-charcoal-dark; /* Replace with your desired text color */
padding: $small-spacing $medium-spacing;
border-radius: $round-border-radius;
border: 1px solid $color-silver;
display: inline-block; /* Ensures the pill takes only the necessary width */
margin-right: 10px;
margin-right: $small-spacing;
margin-top: 2px;
@include small-text;
}

View File

@ -31,6 +31,8 @@
</script>
<style scoped lang="scss">
@import 'styles/helpers';
.pill-radio {
display: flex;
overflow: hidden;
@ -40,29 +42,28 @@
&__button {
flex: 1;
cursor: pointer;
background-color: #f0f0f0;
background-color: $color-silver-light;
outline: none;
padding-left: 14px;
padding-right: 14px;
padding-left: 1rem;
padding-right: 1rem;
border: none;
&--active {
background-color: white;
border: solid silver 1px;
border: solid $color-silver 1px;
}
}
&__button:first-child {
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
border-top-left-radius: $round-border-radius;
border-bottom-left-radius: $round-border-radius;
}
&__button:last-child {
border-top-right-radius: 50px;
border-bottom-right-radius: 50px;
border-top-right-radius: $round-border-radius;
border-bottom-right-radius: $round-border-radius;
}
}
</style>

View File

@ -14,9 +14,7 @@
<p class="topic__teaser">
{{ topic.teaser }}
</p>
<div class="topic__modulefilter">
<module-filter :modules="modules" :me="me" v-if="modules.length > 0" ></module-filter>
</div>
<div class="topic__links">
<div
class="topic__video-link topic__link"
@ -36,7 +34,9 @@
<span class="topic__link-description">Anweisungen zum {{ $flavor.textTopic }} anzeigen</span>
</a>
</div>
<div class="topic__modulefilter">
<module-filter :modules="modules" :me="me" v-if="modules.length > 0" ></module-filter>
</div>
</div>
</div>
</template>

View File

@ -1,25 +1,20 @@
from django.db import models
from django.utils import timezone
from wagtail.admin.panels import FieldPanel, InlinePanel, TabbedInterface, ObjectList, MultiFieldPanel, MultipleChooserPanel
from wagtail.fields import RichTextField
from django.utils.translation import gettext_lazy as _
from wagtail.admin.panels import FieldPanel, TabbedInterface, ObjectList
from wagtail.fields import RichTextField
from core.constants import DEFAULT_RICH_TEXT_FEATURES
from core.wagtail_utils import StrictHierarchyPage, get_default_settings
from users.models import SchoolClass
from django.utils.text import slugify
FILTER_ATTRIBUTE_TYPE = (
('all', 'All'),
('exact', 'Exact')
)
FILTER_ATTRIBUTE_TYPE = (("all", "All"), ("exact", "Exact"))
class ModuleLevel(models.Model):
name = models.CharField(max_length=255, unique=True)
filter_attribute_type = models.CharField(
max_length=16,
choices=FILTER_ATTRIBUTE_TYPE,
default='exact'
max_length=16, choices=FILTER_ATTRIBUTE_TYPE, default="exact"
)
def __str__(self):
@ -42,9 +37,7 @@ class ModuleCategory(models.Model):
name = models.CharField(max_length=255)
filter_attribute_type = models.CharField(
max_length=16,
choices=FILTER_ATTRIBUTE_TYPE,
default='exact'
max_length=16, choices=FILTER_ATTRIBUTE_TYPE, default="exact"
)
def __str__(self):
@ -57,9 +50,12 @@ class Module(StrictHierarchyPage):
verbose_name_plural = "Module"
meta_title = models.CharField(max_length=255, help_text="e.g. 'Intro' or 'Modul 1'")
level = models.ForeignKey(ModuleLevel, on_delete=models.SET_NULL, blank=True, null=True)
category = models.ForeignKey(ModuleCategory, on_delete=models.SET_NULL, blank=True, null=True)
level = models.ForeignKey(
ModuleLevel, on_delete=models.SET_NULL, blank=True, null=True
)
category = models.ForeignKey(
ModuleCategory, on_delete=models.SET_NULL, blank=True, null=True
)
hero_image = models.ForeignKey(
"wagtailimages.Image",
@ -86,27 +82,6 @@ class Module(StrictHierarchyPage):
FieldPanel("hero_source"),
FieldPanel("teaser"),
FieldPanel("intro"),
# TODO: Why is this commented out?
# InlinePanel(
# "assignments",
# label=_("Assignment"),
# classname="collapsed",
# heading=_("linked assignments"),
# help_text=_(
# "These %s are automatically linked, they are shown here only to provide an overview. Please don't change anything here."
# )
# % _("assignments"),
# ),
# InlinePanel(
# "surveys",
# heading=_("linked surveys"),
# label=_("Survey"),
# classname="collapsed",
# help_text=_(
# "These %s are automatically linked, they are shown here only to provide an overview. Please don't change anything here."
# )
# % _("surveys"),
# ),
]
edit_handler = TabbedInterface(
@ -175,7 +150,6 @@ class Module(StrictHierarchyPage):
return f"{self.meta_title} - {self.title}"
class RecentModule(models.Model):
module = models.ForeignKey(
Module, on_delete=models.CASCADE, related_name="recent_modules"

View File

@ -117,6 +117,5 @@ class UpdateLastModuleLevel(relay.ClientIDMutation):
id = args.get('id')
module_level = get_object(ModuleLevel, id)
User.objects.filter(pk=user.id).update(last_module_level_id=module_level.id)
return cls(user=user)

View File

@ -3,7 +3,7 @@ from graphene import relay
from api.types import FailureNode, Success
from api.utils import get_object
from books.models import Module, ContentBlock, Chapter
from books.models import Module
from books.models.snapshot import Snapshot
from books.schema.nodes import SnapshotNode, ModuleNode
from users.models import SchoolClass

View File

@ -6,11 +6,16 @@ from graphene_django.filter import DjangoFilterConnectionField
from assignments.models import StudentSubmission
from assignments.schema.types import AssignmentNode, StudentSubmissionNode
from books.models import Module, Chapter, ContentBlock, RecentModule, ModuleLevel, ModuleCategory
from books.models import (
Module,
Chapter,
ContentBlock,
RecentModule,
)
from books.schema.interfaces.module import ModuleInterface
from books.schema.nodes.chapter import ChapterNode
from books.schema.nodes.module_level import ModuleLevelNode
from books.schema.nodes.module_category import ModuleCategoryNode
from books.schema.nodes.module_level import ModuleLevelNode
from notes.models import ModuleBookmark, ContentBlockBookmark, ChapterBookmark
from notes.schema import (
ModuleBookmarkNode,
@ -50,8 +55,7 @@ class ModuleNode(DjangoObjectType):
bookmark = graphene.Field(ModuleBookmarkNode)
my_submissions = DjangoFilterConnectionField(StudentSubmissionNode)
my_answers = DjangoFilterConnectionField(AnswerNode)
my_content_bookmarks = DjangoFilterConnectionField(
ContentBlockBookmarkNode)
my_content_bookmarks = DjangoFilterConnectionField(ContentBlockBookmarkNode)
my_chapter_bookmarks = DjangoFilterConnectionField(ChapterBookmarkNode)
snapshots = graphene.List("books.schema.nodes.SnapshotNode")
objective_groups = graphene.List(ObjectiveGroupNode)
@ -110,12 +114,6 @@ class ModuleNode(DjangoObjectType):
def resolve_objective_groups(parent, info, **kwargs):
return parent.objective_groups.all().prefetch_related("hidden_for")
def resolve_level(self, info, **kwargs):
return ModuleLevel.objects.get(pk=self.level_id) if self.level_id else None
def resolve_category(self, info, **kwargs):
return ModuleCategory.objects.get(pk=self.category_id) if self.category_id else None
@staticmethod
def resolve_snapshots(parent, info, **kwargs):
user = info.context.user

View File

@ -1,5 +1,4 @@
from graphene import relay
from graphene import relay
from graphene_django import DjangoObjectType
from books.models import ModuleCategory
@ -9,8 +8,4 @@ class ModuleCategoryNode(DjangoObjectType):
class Meta:
model = ModuleCategory
interfaces = (relay.Node,)
only_fields = [
"id",
"name",
"filter_attribute_type",
]
only_fields = "__all__"

View File

@ -1,5 +1,4 @@
from graphene import relay
from graphene import relay
from graphene_django import DjangoObjectType
from books.models import ModuleLevel
@ -9,9 +8,4 @@ class ModuleLevelNode(DjangoObjectType):
class Meta:
model = ModuleLevel
interfaces = (relay.Node,)
only_fields = [
"id",
"name",
"filter_attribute_type",
]
only_fields = "__all__"

View File

@ -5,10 +5,16 @@ from graphene_django.filter import DjangoFilterConnectionField
from api.utils import get_object
from core.logger import get_logger
from .connections import TopicConnection, ModuleConnection
from .nodes import ContentBlockNode, ChapterNode, ModuleNode, NotFoundFailure, SnapshotNode, \
TopicOr404Node
from .nodes.module_level import ModuleLevelNode
from .nodes import (
ContentBlockNode,
ChapterNode,
ModuleNode,
NotFoundFailure,
SnapshotNode,
TopicOr404Node,
)
from .nodes.module_category import ModuleCategoryNode
from .nodes.module_level import ModuleLevelNode
from ..models import Book, Topic, Module, Chapter, Snapshot, ModuleLevel, ModuleCategory
logger = get_logger(__name__)
@ -17,8 +23,7 @@ logger = get_logger(__name__)
class BookQuery(object):
node = relay.Node.Field()
topic = graphene.Field(TopicOr404Node, slug=graphene.String())
module = graphene.Field(
ModuleNode, slug=graphene.String(), id=graphene.ID())
module = graphene.Field(ModuleNode, slug=graphene.String(), id=graphene.ID())
chapter = relay.Node.Field(ChapterNode)
content_block = relay.Node.Field(ContentBlockNode)
snapshot = relay.Node.Field(SnapshotNode)
@ -46,13 +51,13 @@ class BookQuery(object):
return Chapter.objects.filter(**kwargs).live()
def resolve_snapshot(self, info, **kwargs):
id = kwargs.get('id')
id = kwargs.get("id")
snapshot = get_object(Snapshot, id)
return snapshot
def resolve_module(self, info, **kwargs):
slug = kwargs.get('slug')
id = kwargs.get('id')
slug = kwargs.get("slug")
id = kwargs.get("id")
module = None
try:
if id is not None:
@ -67,8 +72,8 @@ class BookQuery(object):
return None
def resolve_topic(self, info, **kwargs):
slug = kwargs.get('slug')
id = kwargs.get('id')
slug = kwargs.get("slug")
id = kwargs.get("id")
if id is not None:
return get_object(Topic, id)
@ -79,9 +84,8 @@ class BookQuery(object):
return NotFoundFailure
return None
def resolve_module_level(self, info, **kwargs):
module_level_id = kwargs.get('id')
module_level_id = kwargs.get("id")
try:
if module_level_id is not None:
return get_object(Module, module_level_id)
@ -90,10 +94,10 @@ class BookQuery(object):
return None
def resolve_module_levels(self, *args, **kwargs):
return ModuleLevel.objects.filter(**kwargs)
return ModuleLevel.objects.all()
def resolve_module_category(self, info, **kwargs):
id = kwargs.get('id')
id = kwargs.get("id")
try:
if id is not None:
return get_object(Module, id)
@ -102,14 +106,7 @@ class BookQuery(object):
return None
def resolve_module_categories(self, *args, **kwargs):
return ModuleCategory.objects.filter(**kwargs)
return ModuleCategory.objects.all()
class ModuleTypeQuery(graphene.ObjectType):

View File

@ -3,8 +3,6 @@ from wagtail.contrib.modeladmin.options import (
ModelAdminGroup,
modeladmin_register,
)
from wagtail import hooks
from .models.module import ModuleLevel, ModuleCategory, Module
from django.utils.translation import gettext_lazy as _

6
server/graphql-schema-macos.sh Executable file
View File

@ -0,0 +1,6 @@
#!/bin/bash
python manage.py graphql_schema
sed -i '' 's/Node, Node/Node & Node/g' schema.graphql
python manage.py graphql_schema --schema api.schema_public.schema --out schema-public.graphql
sed -i '' 's/Node, Node/Node & Node/g' schema-public.graphql