Compare commits

...

4 Commits

35 changed files with 136 additions and 34 deletions

2
.gitignore vendored
View File

@@ -174,4 +174,4 @@ cython_debug/
# PyPI configuration file # PyPI configuration file
.pypirc .pypirc
appunti/settings.py server/appunti/settings.py

View File

@@ -1,23 +0,0 @@
{% extends "notes/base.html" %}
{% block title %}Dashboard{% endblock %}
{% block body %}
<h1>Dashboard</h1>
{% include "notes/messages.html" %}
<h2>Notes for you</h2>
{% if notes %}
<div class="notes">
{% for note in notes %}
<div class="note">
<p>{{ note.note }}</p>
<p class="note-from">From {{ note.from_user.visible_name }}</p>
<p class="note-at">{{ note.created_at|date:"j M, H:i"}}</p>
</div>
{% endfor %}
</div>
{% else %}
<div class="card">No Notes found</div>
{% endif %}
{% endblock %}

View File

@@ -121,6 +121,15 @@ USE_TZ = True
# https://docs.djangoproject.com/en/5.1/howto/static-files/ # https://docs.djangoproject.com/en/5.1/howto/static-files/
STATIC_URL = "static/" STATIC_URL = "static/"
STATIC_ROOT = BASE_DIR / "static"
STORAGES = {
"default": {
"BACKEND": "django.core.files.storage.FileSystemStorage",
},
"staticfiles": {
"BACKEND": "django.contrib.staticfiles.storage.ManifestStaticFilesStorage",
},
}
# Default primary key field type # Default primary key field type
# https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field # https://docs.djangoproject.com/en/5.1/ref/settings/#default-auto-field

View File

@@ -11,10 +11,34 @@ class User(AbstractUser):
) )
expiry_seconds = models.PositiveIntegerField(default=86400) expiry_seconds = models.PositiveIntegerField(default=86400)
@property
def alive_created_notes(self) -> models.QuerySet["Note"]:
return (
self.created_notes.filter(expiry__gt=timezone.now())
.select_related("from_user", "to_user")
.order_by("-created_at")
)
@property
def all_created_notes(self) -> models.QuerySet["Note"]:
return self.created_notes.select_related("from_user", "to_user").order_by(
"-created_at"
)
@property @property
def alive_received_notes(self) -> models.QuerySet["Note"]: def alive_received_notes(self) -> models.QuerySet["Note"]:
return self.received_notes.filter(expiry__gt=timezone.now()).select_related( return (
"from_user", "to_user" self.received_notes.filter(expiry__gt=timezone.now())
.select_related("from_user", "to_user")
.order_by("-created_at")
)
@property
def expired_received_notes(self) -> models.QuerySet["Note"]:
return (
self.received_notes.filter(expiry__lte=timezone.now())
.select_related("from_user", "to_user")
.order_by("-created_at")
) )
@property @property

View File

Before

Width:  |  Height:  |  Size: 2.3 KiB

After

Width:  |  Height:  |  Size: 2.3 KiB

View File

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

Before

Width:  |  Height:  |  Size: 3.0 KiB

After

Width:  |  Height:  |  Size: 3.0 KiB

View File

Before

Width:  |  Height:  |  Size: 2.5 KiB

After

Width:  |  Height:  |  Size: 2.5 KiB

View File

@@ -74,10 +74,6 @@ button {
margin: auto; margin: auto;
} }
.notes.notes-grid {
display: grid;
}
.note { .note {
display: inline-block; display: inline-block;
width: 100%; width: 100%;
@@ -119,8 +115,9 @@ select:nth-child(3n + 2), textarea:nth-child(3n + 2), button:nth-child(3n + 2),
background-color: #a4d6f7; background-color: #a4d6f7;
} }
.note-from, .note-at { .note-from, .note-to, .note-at {
text-align: right; text-align: right;
color: #333;
} }
@@ -137,3 +134,9 @@ select:nth-child(3n + 2), textarea:nth-child(3n + 2), button:nth-child(3n + 2),
border-radius: 25px 155px 15px 25px / 115px 25px 225px 150px; border-radius: 25px 155px 15px 25px / 115px 25px 225px 150px;
background-color: #fac9dc; background-color: #fac9dc;
} }
.title-and-link {
display: flex;
justify-content: space-between;
align-items: center;
}

View File

@@ -0,0 +1,23 @@
{% extends "notes/base.html" %}
{% block title %}Dashboard{% endblock %}
{% block body %}
<h1>Dashboard</h1>
{% include "notes/messages.html" %}
<h2>Notes for you</h2>
{% include "notes/notes.html" with object_list=notes show_from=True show_to=False %}
<div class="title-and-link">
<h2>Active Sent Notes</h2>
<a href="{% url "sent-notes" %}">View All including expired</a>
</div>
{% include "notes/notes.html" with object_list=alive_created show_from=False show_to=True %}
<div class="title-and-link">
<h2>Archived Notes</h2>
<a href="{% url "archive" %}">View All</a>
</div>
{% include "notes/notes.html" with object_list=archive show_from=True show_to=False %}
{% endblock %}

View File

@@ -0,0 +1,9 @@
{% extends "notes/base.html" %}
{% block title %}{{ title }}{% endblock %}
{% block body %}
<h1>{{ title }}</h1>
{% include "notes/messages.html" %}
{% include "notes/notes.html" %}
{% endblock %}

View File

@@ -0,0 +1,15 @@
{% if object_list %}
<div class="notes">
{% for note in object_list %}
<div class="note">
<p>{{ note.note | linebreaksbr }}</p>
{% if show_from %}<p class="note-from">From {{ note.from_user.visible_name }}</p>{% endif %}
{% if show_to %}<p class="note-to">To {{ note.to_user.visible_name }}</p>{% endif %}
<p class="note-at">{{ note.created_at|date:"j M Y, H:i"}}</p>
</div>
{% endfor %}
</div>
{% else %}
<div class="card">No Notes found</div>
{% endif %}

View File

@@ -5,11 +5,13 @@ from notes import views, api_views
urlpatterns = [ urlpatterns = [
path("", views.HomePage.as_view(), name="home"), path("", views.HomePageView.as_view(), name="home"),
path("post-a-note/", views.PostNoteView.as_view(), name="post-a-note"), path("post-a-note/", views.PostNoteView.as_view(), name="post-a-note"),
path("profile/", views.ProfileView.as_view(), name="profile"), path("profile/", views.ProfileView.as_view(), name="profile"),
path("login/", LoginView.as_view(), name="login"), path("login/", LoginView.as_view(), name="login"),
path("logout/", LogoutView.as_view(), name="logout"), path("logout/", LogoutView.as_view(), name="logout"),
path("archive/", views.ArchiveView.as_view(), name="archive"),
path("sent-notes/", views.SentNotesView.as_view(), name="sent-notes"),
path("api/notes/", api_views.NoteListView.as_view(), name="api-notes-list"), path("api/notes/", api_views.NoteListView.as_view(), name="api-notes-list"),
path("api-auth/", include("rest_framework.urls", namespace="rest_framework")), path("api-auth/", include("rest_framework.urls", namespace="rest_framework")),
] ]

View File

@@ -2,23 +2,63 @@ from datetime import timedelta
from typing import Any from typing import Any
from django.contrib import messages from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin from django.contrib.auth.mixins import LoginRequiredMixin
from django.core.exceptions import ImproperlyConfigured
from django.urls import reverse_lazy from django.urls import reverse_lazy
from django.utils import timezone from django.utils import timezone
from django.views.generic import CreateView, TemplateView, UpdateView from django.views.generic import CreateView, ListView, TemplateView, UpdateView
from django.views.generic.edit import FormMixin from django.views.generic.edit import FormMixin
from notes.models import Note, User from notes.models import Note, User
# Create your views here. # Create your views here.
class HomePage(LoginRequiredMixin, TemplateView): class HomePageView(LoginRequiredMixin, TemplateView):
template_name = "notes/home.html" template_name = "notes/home.html"
def get_context_data(self, **kwargs: Any) -> dict[str, Any]: def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
ctx = super().get_context_data(**kwargs) ctx = super().get_context_data(**kwargs)
ctx["notes"] = self.request.user.alive_received_notes ctx["notes"] = self.request.user.alive_received_notes
ctx["archive"] = self.request.user.expired_received_notes[:5]
ctx["alive_created"] = self.request.user.alive_created_notes
return ctx return ctx
class NoteView(LoginRequiredMixin, ListView):
# TODO: Add pagination to the template
template_name = "notes/note_list.html"
show_from = True
show_to = True
def get_title(self):
if not hasattr(self, "title"):
raise ImproperlyConfigured(
"Set a `title` attribute on subclass or override `get_title(self)`"
)
return self.title
def get_context_data(self, **kwargs: Any) -> dict[str, Any]:
ctx = super().get_context_data(**kwargs)
ctx["title"] = self.get_title()
ctx["show_from"] = self.show_from
ctx["show_to"] = self.show_to
return ctx
class ArchiveView(NoteView):
title = "Archive"
show_to = False
def get_queryset(self):
return self.request.user.expired_received_notes
class SentNotesView(NoteView):
title = "Sent Notes"
show_from = False
def get_queryset(self):
return self.request.user.all_created_notes
class PostNoteView(LoginRequiredMixin, CreateView): class PostNoteView(LoginRequiredMixin, CreateView):
model = Note model = Note
fields = ["to_user", "note"] fields = ["to_user", "note"]

View File