diff --git a/gulp/babel.js b/gulp/babel.js index 3c33996..3ef77d3 100644 --- a/gulp/babel.js +++ b/gulp/babel.js @@ -12,10 +12,18 @@ const SRC_DIR = path.join(PROJECT_DIR, 'js'); const STATIC_SUFFIX = 'dist/js/'; const CORE_DIR = path.join(PROJECT_DIR, 'news', 'core', 'static', 'core'); +const COLLECTION_DIR = path.join( + PROJECT_DIR, + 'news', + 'collection', + 'static', + 'collection' +); const taskMappings = [ { name: 'homepage', destDir: `${CORE_DIR}/${STATIC_SUFFIX}` }, { name: 'categories', destDir: `${CORE_DIR}/${STATIC_SUFFIX}` }, + { name: 'rules', destDir: `${COLLECTION_DIR}/${STATIC_SUFFIX}` }, ]; const babelTask = done => { diff --git a/gulp/sass.js b/gulp/sass.js index 2a71455..46cd291 100644 --- a/gulp/sass.js +++ b/gulp/sass.js @@ -10,6 +10,13 @@ const STATIC_SUFFIX = 'dist/css/'; export const ACCOUNTS_DIR = path.join(PROJECT_DIR, 'accounts', 'static', 'accounts'); export const CORE_DIR = path.join(PROJECT_DIR, 'news', 'core', 'static', 'core'); +export const COLLECTION_DIR = path.join( + PROJECT_DIR, + 'news', + 'collection', + 'static', + 'collection' +); const taskMappings = [ { name: 'login', destDir: `${ACCOUNTS_DIR}/${STATIC_SUFFIX}` }, @@ -19,6 +26,8 @@ const taskMappings = [ { name: 'homepage', destDir: `${CORE_DIR}/${STATIC_SUFFIX}` }, { name: 'categories', destDir: `${CORE_DIR}/${STATIC_SUFFIX}` }, { name: 'category', destDir: `${CORE_DIR}/${STATIC_SUFFIX}` }, + { name: 'rules', destDir: `${COLLECTION_DIR}/${STATIC_SUFFIX}` }, + { name: 'rule', destDir: `${COLLECTION_DIR}/${STATIC_SUFFIX}` }, ]; export const sassTask = done => { diff --git a/src/newsreader/js/components/Messages.js b/src/newsreader/js/components/Messages.js index fe8cb20..a985381 100644 --- a/src/newsreader/js/components/Messages.js +++ b/src/newsreader/js/components/Messages.js @@ -1,9 +1,11 @@ import React from 'react'; const Messages = props => { - const messages = props.messages.map(message => { + const messages = props.messages.map((index, message) => { return ( -
  • {message.text}
  • +
  • + {message.text} +
  • ); }); diff --git a/src/newsreader/js/pages/categories/components/CategoryCard.js b/src/newsreader/js/pages/categories/components/CategoryCard.js index 04fccef..5c58a51 100644 --- a/src/newsreader/js/pages/categories/components/CategoryCard.js +++ b/src/newsreader/js/pages/categories/components/CategoryCard.js @@ -19,9 +19,7 @@ const CategoryCard = props => { const cardHeader = ( <>

    {category.name}

    - - {category.created} - + {category.created} ); const cardContent = <>{category.rules && }; diff --git a/src/newsreader/js/pages/rules/App.js b/src/newsreader/js/pages/rules/App.js new file mode 100644 index 0000000..fd01229 --- /dev/null +++ b/src/newsreader/js/pages/rules/App.js @@ -0,0 +1,100 @@ +import React from 'react'; + +import Cookies from 'js-cookie'; + +import Card from '../../components/Card.js'; +import RuleCard from './components/RuleCard.js'; +import RuleModal from './components/RuleModal.js'; +import Messages from '../../components/Messages.js'; + +class App extends React.Component { + selectRule = ::this.selectRule; + deselectRule = ::this.deselectRule; + deleteRule = ::this.deleteRule; + + constructor(props) { + super(props); + + this.token = Cookies.get('csrftoken'); + this.state = { + rules: props.rules, + selectedRuleId: null, + message: null, + }; + } + + selectRule(ruleId) { + this.setState({ selectedRuleId: ruleId }); + } + + deselectRule() { + this.setState({ selectedRuleId: null }); + } + + deleteRule(ruleId) { + const url = `/api/rules/${ruleId}/`; + const options = { + method: 'DELETE', + headers: { + 'X-CSRFToken': this.token, + }, + }; + + fetch(url, options).then(response => { + if (response.ok) { + const rules = this.state.rules.filter(rule => { + return rule.pk != ruleId; + }); + + return this.setState({ + rules: rules, + selectedRuleId: null, + message: null, + }); + } + }); + + const message = { + type: 'error', + text: 'Unable to remove rule, try again later', + }; + return this.setState({ selectedRuleId: null, message: message }); + } + + render() { + const { rules } = this.state; + const cards = rules.map(rule => { + return ; + }); + + const selectedRule = rules.find(rule => { + return rule.pk === this.state.selectedRuleId; + }); + + const pageHeader = ( + <> +

    Rules

    + + Create rule + + + ); + + return ( + <> + {this.state.message && } + + {cards} + {selectedRule && ( + + )} + + ); + } +} + +export default App; diff --git a/src/newsreader/js/pages/rules/components/RuleCard.js b/src/newsreader/js/pages/rules/components/RuleCard.js new file mode 100644 index 0000000..c94144e --- /dev/null +++ b/src/newsreader/js/pages/rules/components/RuleCard.js @@ -0,0 +1,62 @@ +import React from 'react'; + +import Card from '../../../components/Card.js'; + +const RuleCard = props => { + const { rule } = props; + + const faviconUrl = rule.favicon ? rule.favicon : '/static/picture.svg'; + const stateIcon = rule.succeeded + ? '/static/checkmark-circle.svg' + : '/static/warning.svg'; + + const cardHeader = ( + <> +
    + +

    {rule.name}

    +
    + + + ); + + const cardContent = ( + <> + + {!rule.succeeded && ( +
      +
    • {rule.error}
    • +
    + )} + + ); + + const cardFooter = ( + <> + + Edit + + + + ); + + return ; +}; + +export default RuleCard; diff --git a/src/newsreader/js/pages/rules/components/RuleModal.js b/src/newsreader/js/pages/rules/components/RuleModal.js new file mode 100644 index 0000000..5d6f09b --- /dev/null +++ b/src/newsreader/js/pages/rules/components/RuleModal.js @@ -0,0 +1,33 @@ +import React from 'react'; + +import Modal from '../../../components/Modal.js'; + +const RuleModal = props => { + const content = ( +
    +
    +

    Delete rule

    +
    + +
    +

    Are you sure you want to delete {props.rule.name}?

    +
    + +
    + + +
    +
    + ); + + return ; +}; + +export default RuleModal; diff --git a/src/newsreader/js/pages/rules/index.js b/src/newsreader/js/pages/rules/index.js new file mode 100644 index 0000000..7fc7a5c --- /dev/null +++ b/src/newsreader/js/pages/rules/index.js @@ -0,0 +1,9 @@ +import React from 'react'; +import ReactDOM from 'react-dom'; + +import App from './App.js'; + +const dataScript = document.getElementById('rules-data'); +const rules = JSON.parse(dataScript.textContent); + +ReactDOM.render(, document.getElementsByClassName('content')[0]); diff --git a/src/newsreader/news/collection/forms.py b/src/newsreader/news/collection/forms.py new file mode 100644 index 0000000..89c72e9 --- /dev/null +++ b/src/newsreader/news/collection/forms.py @@ -0,0 +1,30 @@ +from django import forms + +from newsreader.news.collection.models import CollectionRule +from newsreader.news.core.models import Category + + +class CollectionRuleForm(forms.ModelForm): + category = forms.ModelChoiceField(required=False, queryset=Category.objects.all()) + + def __init__(self, *args, **kwargs) -> None: + self.user = kwargs.pop("user") + + super().__init__(*args, **kwargs) + + if self.user: + self.fields["category"].queryset = Category.objects.filter(user=self.user) + + def save(self, commit=True) -> CollectionRule: + instance = super().save(commit=False) + instance.user = self.user + + if commit: + instance.save() + self.save_m2m() + + return instance + + class Meta: + model = CollectionRule + fields = ("name", "url", "timezone", "favicon", "category") diff --git a/src/newsreader/news/collection/templates/collection/rule-create.html b/src/newsreader/news/collection/templates/collection/rule-create.html new file mode 100644 index 0000000..4bb242a --- /dev/null +++ b/src/newsreader/news/collection/templates/collection/rule-create.html @@ -0,0 +1,31 @@ +{% extends "collection/rule.html" %} + +{% block form-header %} +

    Create a rule

    +{% endblock %} + +{% block name-input %} + +{% endblock %} + +{% block category-input %} + +{% endblock %} + +{% block url-input %} + +{% endblock %} + +{% block favicon-input %} + +{% endblock %} + +{% block timezone-input %} + +{% endblock %} + +{% block confirm-button %} + +{% endblock %} diff --git a/src/newsreader/news/collection/templates/collection/rule-update.html b/src/newsreader/news/collection/templates/collection/rule-update.html new file mode 100644 index 0000000..7c0a4ba --- /dev/null +++ b/src/newsreader/news/collection/templates/collection/rule-update.html @@ -0,0 +1,34 @@ +{% extends "collection/rule.html" %} + +{% block form-header %} +

    Update rule

    +{% endblock %} + +{% block name-input %} + +{% endblock %} + +{% block category-input %} + +{% endblock %} + +{% block url-input %} + +{% endblock %} + +{% block favicon-input %} + +{% endblock %} + +{% block timezone-input %} + +{% endblock %} + +{% block confirm-button %} + +{% endblock %} diff --git a/src/newsreader/news/collection/templates/collection/rule.html b/src/newsreader/news/collection/templates/collection/rule.html new file mode 100644 index 0000000..0d67e08 --- /dev/null +++ b/src/newsreader/news/collection/templates/collection/rule.html @@ -0,0 +1,70 @@ +{% extends "base.html" %} + +{% load static %} + +{% block head %} + +{% endblock %} + +{% block content %} +
    +
    + {% csrf_token %} + {{ form.non_field_errors }} + +
    + {% block form-header %}{% endblock %} +
    +
    +
    + + {% block name-input %}{% endblock %} + {{ form.name.errors }} +
    + +
    + + + + + {{ form.category.errors }} +
    + +
    + + {% block url-input %}{% endblock %} + {{ form.url.errors }} +
    + +
    + + {% block favicon-input %}{% endblock %} + {{ form.favicon.errors }} +
    + +
    + + The timezone which the feed uses + + + {{ form.timezone.errors }} +
    +
    + +
    +
    + Cancel + {% block confirm-button %}{% endblock %} +
    +
    +
    +
    +{% endblock %} diff --git a/src/newsreader/news/collection/templates/collection/rules.html b/src/newsreader/news/collection/templates/collection/rules.html new file mode 100644 index 0000000..844e27b --- /dev/null +++ b/src/newsreader/news/collection/templates/collection/rules.html @@ -0,0 +1,33 @@ +{% extends "base.html" %} + +{% load static %} + +{% block head %} + +{% endblock %} + +{% block content %} +
    +{% endblock %} + +{% block scripts %} + + +{% endblock %} diff --git a/src/newsreader/news/collection/tests/test_views.py b/src/newsreader/news/collection/tests/test_views.py new file mode 100644 index 0000000..d6a478e --- /dev/null +++ b/src/newsreader/news/collection/tests/test_views.py @@ -0,0 +1,148 @@ +from django.test import Client, TestCase +from django.urls import reverse + +import pytz + +from newsreader.accounts.tests.factories import UserFactory +from newsreader.news.collection.models import CollectionRule +from newsreader.news.collection.tests.factories import CollectionRuleFactory +from newsreader.news.core.tests.factories import CategoryFactory + + +class CollectionRuleViewTestCase: + def setUp(self): + self.user = UserFactory(password="test") + self.client.login(email=self.user.email, password="test") + + self.category = CategoryFactory(user=self.user) + self.form_data = {"name": "", "category": "", "url": "", "timezone": ""} + + def test_simple(self): + response = self.client.get(self.url) + + self.assertEquals(response.status_code, 200) + + def test_no_category(self): + self.form_data.update(category="") + + response = self.client.post(self.url, self.form_data) + self.assertEquals(response.status_code, 302) + + rule = CollectionRule.objects.get() + + self.assertEquals(rule.category, None) + + def test_categories_only_from_user(self): + other_user = UserFactory() + other_categories = CategoryFactory.create_batch(size=4, user=other_user) + + response = self.client.get(self.url) + + for category in other_categories: + self.assertNotContains(response, category.name) + + def test_category_of_other_user(self): + other_user = UserFactory() + other_rule = CollectionRuleFactory(name="other rule", user=other_user) + + self.form_data.update( + name="new name", + category=other_rule.category, + url=other_rule.url, + timezone=other_rule.timezone, + ) + + other_url = reverse("rule-update", args=[other_rule.pk]) + response = self.client.post(other_url, self.form_data) + + self.assertEquals(response.status_code, 404) + + other_rule.refresh_from_db() + + self.assertEquals(other_rule.name, "other rule") + + def test_with_other_user_rules(self): + other_user = UserFactory() + other_categories = CategoryFactory.create_batch(size=4, user=other_user) + + self.form_data.update(category=other_categories[2].pk) + + response = self.client.post(self.url, self.form_data) + + self.assertContains(response, "not one of the available choices") + + +class CollectionRuleCreateViewTestCase(CollectionRuleViewTestCase, TestCase): + def setUp(self): + super().setUp() + + self.url = reverse("rule-create") + + self.form_data.update( + name="new rule", + url="https://www.rss.com/rss", + timezone=pytz.utc, + category=str(self.category.pk), + ) + + def test_creation(self): + response = self.client.post(self.url, self.form_data) + + self.assertEquals(response.status_code, 302) + + rule = CollectionRule.objects.get(name="new rule") + + self.assertEquals(rule.url, "https://www.rss.com/rss") + self.assertEquals(rule.timezone, str(pytz.utc)) + self.assertEquals(rule.favicon, None) + self.assertEquals(rule.category.pk, self.category.pk) + self.assertEquals(rule.user.pk, self.user.pk) + + +class CollectionRuleUpdateViewTestCase(CollectionRuleViewTestCase, TestCase): + def setUp(self): + super().setUp() + + self.rule = CollectionRuleFactory( + name="collection rule", user=self.user, category=self.category + ) + self.url = reverse("rule-update", args=[self.rule.pk]) + + self.form_data.update( + name=self.rule.name, + category=self.rule.category.pk, + url=self.rule.url, + timezone=self.rule.timezone, + ) + + def test_name_change(self): + self.form_data.update(name="new name") + + response = self.client.post(self.url, self.form_data) + self.assertEquals(response.status_code, 302) + + self.rule.refresh_from_db() + + self.assertEquals(self.rule.name, "new name") + + def test_category_change(self): + new_category = CategoryFactory(user=self.user) + + self.form_data.update(category=new_category.pk) + + response = self.client.post(self.url, self.form_data) + self.assertEquals(response.status_code, 302) + + self.rule.refresh_from_db() + + self.assertEquals(self.rule.category.pk, new_category.pk) + + def test_category_removal(self): + self.form_data.update(category="") + + response = self.client.post(self.url, self.form_data) + self.assertEquals(response.status_code, 302) + + self.rule.refresh_from_db() + + self.assertEquals(self.rule.category, None) diff --git a/src/newsreader/news/collection/urls.py b/src/newsreader/news/collection/urls.py index 606ec3a..16c80b7 100644 --- a/src/newsreader/news/collection/urls.py +++ b/src/newsreader/news/collection/urls.py @@ -1,3 +1,4 @@ +from django.contrib.auth.decorators import login_required from django.urls import path from newsreader.news.collection.endpoints import ( @@ -6,11 +7,30 @@ from newsreader.news.collection.endpoints import ( NestedRuleView, RuleReadView, ) +from newsreader.news.collection.views import ( + CollectionRuleCreateView, + CollectionRuleListView, + CollectionRuleUpdateView, +) endpoints = [ - path("rules/", DetailRuleView.as_view(), name="rules-detail"), + path("rules//", DetailRuleView.as_view(), name="rules-detail"), path("rules//posts/", NestedRuleView.as_view(), name="rules-nested-posts"), path("rules//read/", RuleReadView.as_view(), name="rules-read"), path("rules/", ListRuleView.as_view(), name="rules-list"), ] + +urlpatterns = [ + path("rules/", login_required(CollectionRuleListView.as_view()), name="rules"), + path( + "rules//", + login_required(CollectionRuleUpdateView.as_view()), + name="rule-update", + ), + path( + "rules/create/", + login_required(CollectionRuleCreateView.as_view()), + name="rule-create", + ), +] diff --git a/src/newsreader/news/collection/views.py b/src/newsreader/news/collection/views.py index e69de29..8919ddf 100644 --- a/src/newsreader/news/collection/views.py +++ b/src/newsreader/news/collection/views.py @@ -0,0 +1,58 @@ +from typing import Dict, Iterable + +from django.urls import reverse_lazy +from django.views.generic.edit import CreateView, UpdateView +from django.views.generic.list import ListView + +import pytz + +from newsreader.news.collection.forms import CollectionRuleForm +from newsreader.news.collection.models import CollectionRule +from newsreader.news.core.models import Category + + +class CollectionRuleViewMixin: + queryset = CollectionRule.objects.order_by("name") + + def get_queryset(self) -> Iterable: + user = self.request.user + return self.queryset.filter(user=user) + + +class CollectionRuleDetailMixin: + success_url = reverse_lazy("rules") + form_class = CollectionRuleForm + + def get_context_data(self, **kwargs) -> Dict: + context_data = super().get_context_data(**kwargs) + + rules = Category.objects.filter(user=self.request.user).order_by("name") + timezones = [timezone for timezone in pytz.all_timezones] + + context_data["categories"] = rules + context_data["timezones"] = timezones + + return context_data + + def get_form_kwargs(self) -> Dict: + kwargs = super().get_form_kwargs() + kwargs["user"] = self.request.user + return kwargs + + +class CollectionRuleListView(CollectionRuleViewMixin, ListView): + template_name = "collection/rules.html" + context_object_name = "rules" + + +class CollectionRuleUpdateView( + CollectionRuleViewMixin, CollectionRuleDetailMixin, UpdateView +): + template_name = "collection/rule-update.html" + context_object_name = "rule" + + +class CollectionRuleCreateView( + CollectionRuleViewMixin, CollectionRuleDetailMixin, CreateView +): + template_name = "collection/rule-create.html" diff --git a/src/newsreader/news/core/forms.py b/src/newsreader/news/core/forms.py index 38e03f4..8fa2221 100644 --- a/src/newsreader/news/core/forms.py +++ b/src/newsreader/news/core/forms.py @@ -6,8 +6,8 @@ from newsreader.news.core.models import Category class CategoryForm(forms.ModelForm): rules = forms.ModelMultipleChoiceField( - queryset=CollectionRule.objects.all(), required=False, + queryset=CollectionRule.objects.all(), widget=forms.widgets.CheckboxSelectMultiple, ) @@ -17,7 +17,9 @@ class CategoryForm(forms.ModelForm): super().__init__(*args, **kwargs) if self.user: - self.fields["rules"].queryset = CollectionRule.objects.filter(user=self.user) + self.fields["rules"].queryset = CollectionRule.objects.filter( + user=self.user + ) def save(self, commit=True) -> Category: instance = super().save(commit=False) diff --git a/src/newsreader/scss/components/card/_card.scss b/src/newsreader/scss/components/card/_card.scss index d979eeb..23a8079 100644 --- a/src/newsreader/scss/components/card/_card.scss +++ b/src/newsreader/scss/components/card/_card.scss @@ -13,6 +13,7 @@ &__header { display: flex; justify-content: space-between; + align-items: center; padding: 15px 0; diff --git a/src/newsreader/scss/components/errorlist/; b/src/newsreader/scss/components/errorlist/; deleted file mode 100644 index efcee40..0000000 --- a/src/newsreader/scss/components/errorlist/; +++ /dev/null @@ -1,3 +0,0 @@ -.errorlist { - -} diff --git a/src/newsreader/scss/pages/rule/components/index.scss b/src/newsreader/scss/pages/rule/components/index.scss new file mode 100644 index 0000000..de2a031 --- /dev/null +++ b/src/newsreader/scss/pages/rule/components/index.scss @@ -0,0 +1 @@ +@import "rule-form/index"; diff --git a/src/newsreader/scss/pages/rule/components/rule-form/_rule-form.scss b/src/newsreader/scss/pages/rule/components/rule-form/_rule-form.scss new file mode 100644 index 0000000..95a2388 --- /dev/null +++ b/src/newsreader/scss/pages/rule/components/rule-form/_rule-form.scss @@ -0,0 +1,27 @@ +.rule-form { + margin: 20px 0; + + &__section:last-child { + + & .rule-form__fieldset { + display: flex; + flex-direction: row; + justify-content: space-between; + } + } + + &__select[name=category] { + width: 50%; + + padding: 0 10px; + } + + &__select[name=timezone] { + max-height: 200px; + width: 50%; + + margin: 0 15px; + padding: 0 10px; + + } +} diff --git a/src/newsreader/scss/pages/rule/components/rule-form/index.scss b/src/newsreader/scss/pages/rule/components/rule-form/index.scss new file mode 100644 index 0000000..4c7fbee --- /dev/null +++ b/src/newsreader/scss/pages/rule/components/rule-form/index.scss @@ -0,0 +1 @@ +@import "rule-form"; diff --git a/src/newsreader/scss/pages/rule/elements/index.scss b/src/newsreader/scss/pages/rule/elements/index.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/scss/pages/rule/index.scss b/src/newsreader/scss/pages/rule/index.scss new file mode 100644 index 0000000..16b6493 --- /dev/null +++ b/src/newsreader/scss/pages/rule/index.scss @@ -0,0 +1,8 @@ +// General imports +@import "../../partials/variables"; +@import "../../components/index"; +@import "../../elements/index"; + +// Page specific +@import "./components/index"; +@import "./elements/index"; diff --git a/src/newsreader/scss/pages/rules/components/card/_card.scss b/src/newsreader/scss/pages/rules/components/card/_card.scss new file mode 100644 index 0000000..ec09189 --- /dev/null +++ b/src/newsreader/scss/pages/rules/components/card/_card.scss @@ -0,0 +1,20 @@ +.card { + &__header { + & div { + display: flex; + flex-direction: row; + + & img { + padding: 0 10px; + } + } + } + + &__content { + flex-direction: column; + } + + &__footer > *:last-child { + margin: 0 0 0 10px; + } +} diff --git a/src/newsreader/scss/pages/rules/components/card/index.scss b/src/newsreader/scss/pages/rules/components/card/index.scss new file mode 100644 index 0000000..484e154 --- /dev/null +++ b/src/newsreader/scss/pages/rules/components/card/index.scss @@ -0,0 +1 @@ +@import "card"; diff --git a/src/newsreader/scss/pages/rules/components/index.scss b/src/newsreader/scss/pages/rules/components/index.scss new file mode 100644 index 0000000..7e96fec --- /dev/null +++ b/src/newsreader/scss/pages/rules/components/index.scss @@ -0,0 +1,3 @@ +@import "card/index"; +@import "rules/index"; +@import "rule-modal/index"; diff --git a/src/newsreader/scss/pages/rules/components/rule-modal/_rule-modal.scss b/src/newsreader/scss/pages/rules/components/rule-modal/_rule-modal.scss new file mode 100644 index 0000000..0dc53bb --- /dev/null +++ b/src/newsreader/scss/pages/rules/components/rule-modal/_rule-modal.scss @@ -0,0 +1,27 @@ +.rule-modal { + display: flex; + flex-direction: column; + align-self: center; + + margin: 20px 0; + width: 50%; + + border-radius: 2px; + background-color: $white; + + &__header { + padding: 5px 20px; + } + + &__content { + padding: 10px 30px; + } + + &__footer { + display: flex; + flex-direction: row; + justify-content: space-between; + + padding: 10px; + } +} diff --git a/src/newsreader/scss/pages/rules/components/rule-modal/index.scss b/src/newsreader/scss/pages/rules/components/rule-modal/index.scss new file mode 100644 index 0000000..c19060f --- /dev/null +++ b/src/newsreader/scss/pages/rules/components/rule-modal/index.scss @@ -0,0 +1 @@ +@import "rule-modal"; diff --git a/src/newsreader/scss/pages/rules/components/rules/_rules.scss b/src/newsreader/scss/pages/rules/components/rules/_rules.scss new file mode 100644 index 0000000..3fd4c22 --- /dev/null +++ b/src/newsreader/scss/pages/rules/components/rules/_rules.scss @@ -0,0 +1,7 @@ +.rules { + &__item { + & > * { + margin: 0; + } + } +} diff --git a/src/newsreader/scss/pages/rules/components/rules/index.scss b/src/newsreader/scss/pages/rules/components/rules/index.scss new file mode 100644 index 0000000..e6a0ebf --- /dev/null +++ b/src/newsreader/scss/pages/rules/components/rules/index.scss @@ -0,0 +1 @@ +@import "rules"; diff --git a/src/newsreader/scss/pages/rules/elements/index.scss b/src/newsreader/scss/pages/rules/elements/index.scss new file mode 100644 index 0000000..e69de29 diff --git a/src/newsreader/scss/pages/rules/index.scss b/src/newsreader/scss/pages/rules/index.scss new file mode 100644 index 0000000..16b6493 --- /dev/null +++ b/src/newsreader/scss/pages/rules/index.scss @@ -0,0 +1,8 @@ +// General imports +@import "../../partials/variables"; +@import "../../components/index"; +@import "../../elements/index"; + +// Page specific +@import "./components/index"; +@import "./elements/index"; diff --git a/src/newsreader/templates/base.html b/src/newsreader/templates/base.html index 445d507..087aca7 100644 --- a/src/newsreader/templates/base.html +++ b/src/newsreader/templates/base.html @@ -11,7 +11,7 @@ {% if request.user.is_authenticated %} - + {% else %} diff --git a/src/newsreader/urls.py b/src/newsreader/urls.py index be59ad1..fb68235 100644 --- a/src/newsreader/urls.py +++ b/src/newsreader/urls.py @@ -6,8 +6,9 @@ from rest_framework_swagger.views import get_swagger_view from newsreader.accounts.urls import urlpatterns as login_urls from newsreader.news.collection.urls import endpoints as collection_endpoints +from newsreader.news.collection.urls import urlpatterns as collection_patterns from newsreader.news.core.urls import endpoints as core_endpoints -from newsreader.news.core.urls import urlpatterns +from newsreader.news.core.urls import urlpatterns as core_patterns schema_view = get_swagger_view(title="Newsreader API") @@ -18,7 +19,8 @@ endpoints = [ ] urlpatterns = [ - path("", include(urlpatterns)), + path("", include(core_patterns)), + path("", include(collection_patterns)), path("accounts/", include((login_urls, "accounts")), name="accounts"), path("admin/", admin.site.urls, name="admin"), path("api/", include((endpoints, "api")), name="api"),