Skip to content

Commit

Permalink
patch: fetch and render announcements (#1074)
Browse files Browse the repository at this point in the history
* patch: fetch and render announcements

* patch: update url path

* feat: fetch announcements

* feat: define announcements path

* feat: add pinax packages

* patch: add pinax to installed apps

* feat: display announcements from pinax

* patch: adjust component

* patch: add filter to fetch announcements

* patch: update fetch announcements view
  • Loading branch information
tinashechiraya authored Sep 2, 2024
1 parent 115d8b1 commit ca000cc
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 16 deletions.
2 changes: 2 additions & 0 deletions deployment/docker/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ lxml==4.9.3
oauthlib==3.2.2
packaging==23.2
Pillow==10.1.0
pinax-announcements==4.0.1
pinax-templates==3.0.0
platformdirs==3.11.0
psycopg2==2.9.9
pycparser==2.21
Expand Down
3 changes: 3 additions & 0 deletions django_project/minisass/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -206,6 +206,9 @@
'minisass_authentication',
'monitor',
'minisass',
'pinax.announcements',
'bootstrapform',
'pinax.templates',
# 'google_analytics'
]

Expand Down
4 changes: 3 additions & 1 deletion django_project/minisass/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@
from minisass.views import (
GroupScoresListView,
VideoListView,
GetMobileApp
GetMobileApp,
get_announcements
)

from minisass_frontend.views import ReactHomeView
Expand All @@ -18,6 +19,7 @@
path('group-scores/', GroupScoresListView.as_view(), name='group-scores'),
path('videos/', VideoListView.as_view(), name='video-list'),
path('mobile-app/', GetMobileApp.as_view(), name='get-mobile-app'),
path('announcements/', get_announcements, name='get_announcements'),

path('jsi18n/<str:packages>/', JavaScriptCatalog.as_view(), name='javascript-catalog'), # Use JavaScriptCatalog directly
path('admin/', admin.site.urls),
Expand Down
28 changes: 28 additions & 0 deletions django_project/minisass/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,9 @@
VideoSerializer,
MobileAppSerializer
)
from django.http import JsonResponse
from pinax.announcements.models import Announcement
from django.utils.timezone import now

class GroupScoresListView(generics.ListAPIView):
queryset = GroupScores.objects.all().order_by('name')
Expand All @@ -28,3 +31,28 @@ class GetMobileApp(APIView):
def get(self, request):
mobile_app = MobileApp.objects.filter(active=True).order_by('-id').first()
return Response(self.serializer_class(mobile_app).data)


def get_announcements(request):
current_time = now()

user_is_authenticated = request.user.is_authenticated if request.user else False

announcements = Announcement.objects.filter(
publish_start__lte=current_time,
publish_end__gte=current_time,
site_wide=True
)

if user_is_authenticated:
announcements = announcements | Announcement.objects.filter(
publish_start__lte=current_time,
publish_end__gte=current_time,
members_only=True
)
else:
announcements = announcements.exclude(members_only=True)

announcements_data = announcements.values('title', 'content', 'dismissal_type')
return JsonResponse(list(announcements_data), safe=False)

55 changes: 40 additions & 15 deletions django_project/minisass_frontend/src/components/Banner/index.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,40 @@
import React, { useState, useEffect } from 'react';
import { AiOutlineClose } from 'react-icons/ai';
import { globalVariables } from '../../utils';

const Banner: React.FC = () => {
const [visible, setVisible] = useState(false);
const [announcements, setAnnouncements] = useState([]);
const [showCloseButton, setShowCloseButton] = useState(false);

const announcements_url = globalVariables.baseUrl + '/announcements/';

// Fetch announcements from the API
useEffect(() => {
if(visible)
fetch(announcements_url)
.then((response) => response.json())
.then((data) => {
if (data.length > 0) {
setAnnouncements(data);
const shouldShowCloseButton = data.some(announcement => announcement.dismissal_type !== 1);
setShowCloseButton(shouldShowCloseButton);
setVisible(true);
} else {
setVisible(false);
}
})
.catch((error) => console.error('Error fetching announcements:', error));
}, []);

useEffect(() => {
if (showCloseButton && visible) {
const timer = setTimeout(() => {
setVisible(false);
}, 15000);

return () => clearTimeout(timer);
}, []);
return () => clearTimeout(timer);
}
}, [visible, showCloseButton]);

const bannerStyle: React.CSSProperties = {
backgroundColor: '#003f81',
Expand All @@ -32,15 +55,13 @@ const Banner: React.FC = () => {

const textStyle: React.CSSProperties = {
flex: 1,
textAlign: 'center',
marginTop: '2%'
textAlign: 'center'
};

const buttonStyle: React.CSSProperties = {
color: 'white',
background: 'transparent',
border: 'none',
marginTop: '2%',
display: 'flex',
alignItems: 'center'
};
Expand All @@ -49,15 +70,19 @@ const Banner: React.FC = () => {
<>
{visible && (
<div style={bannerStyle}>
<span style={textStyle}>
The miniSASS site will undergo routine maintenance on Friday, 30th August 2024. We apologize for any inconvenience caused by the brief downtime.
</span>
<button
onClick={() => setVisible(false)}
style={buttonStyle}
>
<AiOutlineClose size={24} />
</button>
<div style={textStyle}>
{announcements.map((announcement, index) => (
<div key={index}>
<strong>{announcement.title}</strong><br />
{announcement.content}
</div>
))}
</div>
{showCloseButton && (
<button onClick={() => setVisible(false)} style={buttonStyle}>
<AiOutlineClose size={24} />
</button>
)}
</div>
)}
</>
Expand Down

0 comments on commit ca000cc

Please sign in to comment.