Skip to content

Commit 355429e

Browse files
Merge pull request #291 from kenanwright1988/calendar_for_registrants
Calendar for registrants
2 parents 692c13a + 9ada358 commit 355429e

17 files changed

Lines changed: 1161 additions & 16 deletions

File tree

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,3 +15,5 @@ venv/
1515
MYNOTES.md
1616
staticfiles
1717
data/
18+
todo.txt
19+
node_modules/

hackathon/admin.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
HackTeam,
66
HackProject,
77
HackProjectScore,
8-
HackProjectScoreCategory)
8+
HackProjectScoreCategory,
9+
Event)
910

1011

1112
# Register your models here.
@@ -16,3 +17,4 @@
1617
admin.site.register(HackProject)
1718
admin.site.register(HackProjectScore)
1819
admin.site.register(HackProjectScoreCategory)
20+
admin.site.register(Event)

hackathon/forms.py

Lines changed: 69 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
from accounts.models import Organisation
44
from .models import Hackathon, HackProject, HackAward, HackTeam, \
5-
HackProjectScoreCategory, HackAwardCategory
5+
HackProjectScoreCategory, HackAwardCategory, Event
66
from .lists import STATUS_TYPES_CHOICES
77

88

@@ -194,3 +194,71 @@ def __init__(self, *args, **kwargs):
194194
self.fields['winning_project'] = forms.ModelChoiceField(
195195
queryset=hack_projects)
196196
self.fields['winning_project'].required = False
197+
198+
199+
class EventForm(forms.ModelForm):
200+
"""
201+
Form to create or update an Event
202+
"""
203+
title = forms.CharField(
204+
label="Webinar Title",
205+
widget=forms.TextInput(attrs={'class': 'form-control'})
206+
)
207+
start = forms.DateTimeField(
208+
label="Start Time",
209+
input_formats=['%d/%m/%Y %H:%M'],
210+
widget=forms.DateTimeInput(
211+
format='%d/%m/%Y %H:%M',
212+
attrs={
213+
'placeholder': 'DD/MM/YYYY HH:MM',
214+
'autocomplete': 'off',
215+
'class': 'form-control'
216+
}
217+
)
218+
)
219+
end = forms.DateTimeField(
220+
label="End Time",
221+
input_formats=['%d/%m/%Y %H:%M'],
222+
widget=forms.DateTimeInput(
223+
format='%d/%m/%Y %H:%M',
224+
attrs={
225+
'placeholder': 'DD/MM/YYYY HH:MM',
226+
'autocomplete': 'off',
227+
'class': 'form-control'
228+
}
229+
)
230+
)
231+
body = forms.CharField(
232+
label="Description",
233+
widget=forms.Textarea(attrs={'rows': 4, 'class': 'form-control'})
234+
)
235+
webinar_link = forms.URLField(
236+
label="Webinar Link",
237+
required=False,
238+
widget=forms.URLInput(attrs={'class': 'form-control'})
239+
)
240+
webinar_code = forms.CharField(
241+
label="Webinar Join Code",
242+
required=False,
243+
widget=forms.Textarea(attrs={'rows': 1, 'class': 'form-control'})
244+
)
245+
class Meta:
246+
model = Event
247+
fields = [
248+
'title', 'start', 'end', 'body',
249+
'webinar_link',
250+
'webinar_code',
251+
]
252+
253+
def __init__(self, *args, **kwargs):
254+
super(EventForm, self).__init__(*args, **kwargs)
255+
256+
def save(self, commit=True):
257+
event = super(EventForm, self).save(commit=False)
258+
# Append f-string to the body field
259+
webinar_link = self.cleaned_data.get('webinar_link', '')
260+
webinar_code = self.cleaned_data.get('webinar_code', '')
261+
event.body += f'<br><br><b>Meeting Join Link:</b> <a href="{webinar_link}" target="_blank">Click here to join</a><br><b>Meeting Join Code:</b> {webinar_code}'
262+
if commit:
263+
event.save()
264+
return event

hackathon/migrations/0054_event.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
# Generated by Django 3.1.13 on 2024-10-04 15:52
2+
3+
from django.db import migrations, models
4+
import django.db.models.deletion
5+
6+
7+
class Migration(migrations.Migration):
8+
9+
dependencies = [
10+
('hackathon', '0053_auto_20240912_1527'),
11+
]
12+
13+
operations = [
14+
migrations.CreateModel(
15+
name='Event',
16+
fields=[
17+
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
18+
('title', models.CharField(max_length=200)),
19+
('start', models.DateTimeField()),
20+
('end', models.DateTimeField()),
21+
('body', models.TextField(default='', max_length=500)),
22+
('isReadOnly', models.BooleanField(default=True)),
23+
('webinar_link', models.URLField(blank=True, null=True)),
24+
('webinar_code', models.CharField(blank=True, max_length=50, null=True)),
25+
('hackathon', models.ForeignKey(on_delete=django.db.models.deletion.CASCADE, related_name='events', to='hackathon.hackathon')),
26+
],
27+
),
28+
]
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Generated by Django 3.1.13 on 2024-10-08 13:58
2+
3+
from django.db import migrations
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
('hackathon', '0054_event'),
10+
]
11+
12+
operations = [
13+
migrations.RemoveField(
14+
model_name='event',
15+
name='isReadOnly',
16+
),
17+
]

hackathon/models.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
from accounts.models import CustomUser as User
44
from accounts.models import Organisation
55
from .lists import STATUS_TYPES_CHOICES
6+
from datetime import timedelta
67

78
# Optional fields are ony set to deal with object deletion issues.
89
# If this isn't a problem, they can all be changed to required fields.
@@ -296,3 +297,28 @@ def __str__(self):
296297

297298
class Meta:
298299
verbose_name_plural = "Hack project score categories"
300+
301+
302+
class Event(models.Model):
303+
"""
304+
Model representing an event in the calendar.
305+
"""
306+
hackathon = models.ForeignKey('Hackathon', on_delete=models.CASCADE, related_name='events')
307+
title = models.CharField(max_length=200)
308+
start = models.DateTimeField()
309+
end = models.DateTimeField()
310+
body = models.TextField(max_length=500, default="")
311+
webinar_link = models.URLField(blank=True, null=True)
312+
webinar_code = models.CharField(max_length=50, blank=True, null=True)
313+
314+
def save(self, *args, **kwargs):
315+
"""
316+
If the end time is not set, it will be set to start time + 1 hour.
317+
318+
"""
319+
if not self.end:
320+
self.end = self.start + timedelta(hours=1)
321+
super().save(*args, **kwargs)
322+
323+
def __str__(self):
324+
return self.title

hackathon/static/hackathon/css/hackathon.css

Lines changed: 49 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,4 +21,52 @@
2121
font-size: 1rem;
2222
} */
2323

24-
}
24+
}
25+
26+
/* Custom popup styles */
27+
.toastui-calendar-detail-container {
28+
background-color: #fff;
29+
border: 1px solid #ccc;
30+
border-radius: 5px;
31+
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
32+
padding: 20px;
33+
position: fixed;
34+
z-index: 1000;
35+
width: 300px;
36+
/* Adjust as needed */
37+
overflow: hidden;
38+
word-wrap: break-word;
39+
/* Ensure long words break within the container */
40+
white-space: normal;
41+
/* Allow normal wrapping */
42+
top: 50%;
43+
left: 50%;
44+
transform: translate(-50%, -50%);
45+
}
46+
.toastui-calendar-detail-container .toastui-calendar-icon {
47+
display: none;
48+
}
49+
.toastui-calendar-floating-layer > div.toastui-calendar-event-detail-popup-slot > div > div.toastui-calendar-detail-container > div.toastui-calendar-popup-section.toastui-calendar-section-detail > div:nth-child(2) {
50+
display: none;
51+
}
52+
.toastui-calendar-detail-container .toastui-calendar-section-button {
53+
display: none;
54+
}
55+
56+
/* Calendar next buttons */
57+
#next-button,
58+
#prev-button {
59+
background-color: #fff;
60+
color: #4A4A4F;
61+
border: 1px solid #4A4A4F;
62+
padding: 5px 5px;
63+
text-align: center;
64+
text-decoration: none;
65+
font-weight: bold;
66+
display: inline-block;
67+
font-size: 16px;
68+
margin: 4px 2px;
69+
cursor: pointer;
70+
border-radius: 5px;
71+
}
72+
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{% extends "base.html" %}
2+
{% load static %}
3+
{% load crispy_forms_tags %}
4+
5+
{% block css %}
6+
<!-- XDSoft DateTimePicker -->
7+
<link rel="stylesheet"
8+
href="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.20/jquery.datetimepicker.min.css"
9+
integrity="sha256-DOS9W6NR+NFe1fUhEE0PGKY/fubbUCnOfTje2JMDw3Y=" crossorigin="anonymous"/>
10+
{% endblock %}
11+
12+
{% block content %}
13+
14+
<section class="h-100">
15+
<div class="row mb-3">
16+
<div class="col-12">
17+
{% if event %}
18+
<h1>Edit Event</h1>
19+
{% else %}
20+
<h1>Create Event</h1>
21+
{% endif %}
22+
</div>
23+
</div>
24+
25+
<form method="POST">
26+
{% csrf_token %}
27+
<div class="row">
28+
<div class="col-12">
29+
{{ form.title|as_crispy_field }}
30+
</div>
31+
<div class="col-12" >
32+
{{ form.start|as_crispy_field }}
33+
</div>
34+
<div class="col-12" id="webinar_end_date">
35+
{{ form.end|as_crispy_field }}
36+
</div>
37+
<div class="col-12">
38+
{{ form.body|as_crispy_field }}
39+
</div>
40+
<div class="col-12">
41+
{{ form.webinar_link|as_crispy_field }}
42+
</div>
43+
<div class="col-12">
44+
{{ form.webinar_code|as_crispy_field }}
45+
</div>
46+
<div class="col-12">
47+
<button type="submit" class="btn btn-primary">Save</button>
48+
</div>
49+
</div>
50+
</form>
51+
</section>
52+
53+
{% endblock %}
54+
55+
{% block js %}
56+
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-datetimepicker/2.5.20/jquery.datetimepicker.full.min.js"
57+
integrity="sha256-FEqEelWI3WouFOo2VWP/uJfs1y8KJ++FLh2Lbqc8SJk=" crossorigin="anonymous">
58+
</script>
59+
<script src="{% static 'js/datetimepicker.js' %}"></script>
60+
{% endblock %}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
{% extends "base.html" %}
2+
3+
{% block content %}
4+
<h2>{{ hackathon.display_name }} - Webinars</h2>
5+
{% if events %}
6+
<div class="card">
7+
<div class="card-body">
8+
<ul class="list-group">
9+
{% for event in events %}
10+
<li class="list-group-item d-flex justify-content-between align-items-center">
11+
<div>
12+
<h5> {{ hackathon.display_name }} {{ event.title }}</h5>
13+
<p>{{ event.start }}</p>
14+
<p><a href="{{ event.webinar_link }}" target="_blank">Webinar Link </a></p>
15+
<p>{{ event.body | safe }}</p>
16+
</div>
17+
<div>
18+
<a href="{% url 'hackathon:change_event' hackathon.id event.id %}" class="btn btn-primary btn-sm">Edit</a>
19+
<a href="{% url 'hackathon:delete_event' hackathon.id event.id %}" class="btn btn-danger btn-sm" onclick="return confirm('Are you sure you want to delete this event?');">Delete</a>
20+
</div>
21+
</li>
22+
{% endfor %}
23+
</ul>
24+
</div>
25+
</div>
26+
{% else %}
27+
<p>No events found. <a href="{% url 'hackathon:change_event' hackathon.id %}">Add an event</a></p>
28+
{% endif %}
29+
<a href="{% url 'hackathon:change_event' hackathon.id %}" class="btn btn-primary mt-3">Add Event</a>
30+
{% endblock %}

0 commit comments

Comments
 (0)