feat: replace email flows with in-app notifications

Add recipient notification detail and filtering, a queued admin composer, and contextual automatic titles. Retire email delivery, obsolete auth routes, newsletter data, and related templates and settings.
This commit is contained in:
2026-09-26 12:23:10 +03:30
parent b706a2d10a
commit 3ec28638d1
54 changed files with 787 additions and 4843 deletions

View File

@@ -11,7 +11,6 @@ class AnnouncementType(models.TextChoices):
EVENT = 'event', 'Event'
ACADEMIC = 'academic', 'Academic'
URGENT = 'urgent', 'Urgent'
NEWSLETTER = 'newsletter', 'Newsletter'
class AnnouncementPriority(models.TextChoices):
@@ -44,21 +43,6 @@ class Announcement(BaseModel):
)
is_published = models.BooleanField(default=False, verbose_name='Published')
publish_date = models.DateTimeField(null=True, blank=True, verbose_name='Publish Date')
send_email = models.BooleanField(default=False, verbose_name='Send Email Notification')
send_push = models.BooleanField(default=False, verbose_name='Send Push Notification')
email_sent = models.BooleanField(default=False, verbose_name='Email Sent')
push_sent = models.BooleanField(default=False, verbose_name='Push Sent')
target_audience = models.CharField(
max_length=20,
choices=[
('all', 'All Users'),
('members', 'Members Only'),
('committee', 'Committee Only'),
('subscribers', 'Newsletter Subscribers Only'),
],
default='all',
verbose_name='Target Audience'
)
class Meta:
verbose_name = 'Announcement'
@@ -75,45 +59,6 @@ class Announcement(BaseModel):
return markdown.markdown(self.content)
class NewsletterSubscription(BaseModel):
email = models.EmailField(unique=True, verbose_name='Email')
user = models.OneToOneField(
User,
on_delete=models.CASCADE,
null=True,
blank=True,
related_name='newsletter_subscription',
verbose_name='User'
)
is_active = models.BooleanField(default=True, verbose_name='Active')
subscribed_categories = models.JSONField(
default=list,
blank=True,
verbose_name='Subscribed Categories',
help_text='List of announcement types to receive'
)
confirmation_token = models.CharField(max_length=100, blank=True, verbose_name='Confirmation Token')
confirmed_at = models.DateTimeField(null=True, blank=True, verbose_name='Confirmed At')
unsubscribe_token = models.CharField(max_length=100, blank=True, verbose_name='Unsubscribe Token')
class Meta:
verbose_name = 'Newsletter Subscription'
verbose_name_plural = 'Newsletter Subscriptions'
ordering = ['-created_at']
def __str__(self):
return self.email
def save(self, *args, **kwargs):
if not self.confirmation_token:
import uuid
self.confirmation_token = str(uuid.uuid4())
if not self.unsubscribe_token:
import uuid
self.unsubscribe_token = str(uuid.uuid4())
super().save(*args, **kwargs)
class PushNotificationDevice(BaseModel):
user = models.ForeignKey(
User,