--wip-- [skip ci]
This commit is contained in:
24
course_calendar/templates/admin_add_event.html
Normal file
24
course_calendar/templates/admin_add_event.html
Normal file
@@ -0,0 +1,24 @@
|
||||
{% extends "wagtailadmin/base.html" %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block titletag %}
|
||||
{% trans "Add Event" %}
|
||||
{% endblock titletag %}
|
||||
|
||||
{% block content %}
|
||||
{{ form.media }}
|
||||
{% include "wagtailadmin/shared/header.html" with title="Events" icon="calendar" %}
|
||||
|
||||
<div style="margin: auto 72px;">
|
||||
<h2>Add event</h2>
|
||||
<form method="post">
|
||||
{% csrf_token %}
|
||||
{{ form.as_p }}
|
||||
<button type="submit" class="button button-primary">Save</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
|
||||
|
||||
27
course_calendar/templates/admin_events_dashboard.html
Normal file
27
course_calendar/templates/admin_events_dashboard.html
Normal file
@@ -0,0 +1,27 @@
|
||||
{% extends "wagtailadmin/base.html" %}
|
||||
{% load static i18n %}
|
||||
|
||||
{% block titletag %}
|
||||
{% trans "Events" %}
|
||||
{% endblock titletag %}
|
||||
|
||||
{% block content %}
|
||||
{% include "wagtailadmin/shared/header.html" with title="Events" icon="calendar" %}
|
||||
|
||||
<div>
|
||||
<ul>
|
||||
{% for event in events %}
|
||||
|
||||
<li>
|
||||
{{ event | pprint }}
|
||||
</li>
|
||||
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<a href="{% url 'admin_add_event' %}" class="button">{% trans "Add event" %}</a>
|
||||
|
||||
{% endblock content %}
|
||||
|
||||
|
||||
144
course_calendar/templates/calendar.html
Normal file
144
course_calendar/templates/calendar.html
Normal file
@@ -0,0 +1,144 @@
|
||||
{% extends "base.html" %}
|
||||
{% load i18n %}
|
||||
|
||||
{% block title %}{% trans "Course Calendar" %}{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
<script src="https://cdn.jsdelivr.net/npm/fullcalendar@6.1.20/index.global.min.js" integrity="sha256-sQEgS6I+FEeOlX4oTVi7qW/HMRAh0O6vifpeZXIMRsg=" crossorigin="anonymous"></script>
|
||||
<script>
|
||||
function showModal(occurrenceId) {
|
||||
// get event's url
|
||||
const eventApiUrl = `/calendar/api/calendar/occurrences/${occurrenceId}`;
|
||||
|
||||
const eventApi = fetch(eventApiUrl)
|
||||
.then(response => response.json())
|
||||
.then(data => {
|
||||
eventUrl = `/calendar/occurrence/${occurrenceId}/`;
|
||||
|
||||
const modal = document.createElement('div');
|
||||
modal.classList.add('fixed', 'inset-0', 'flex', 'items-center', 'justify-center', 'z-50', 'shadow-lg', 'overflow-auto');
|
||||
modal.style.backgroundColor = 'rgba(0, 0, 0, 0.5)'; // semi-transparent background
|
||||
|
||||
const modalContent = document.createElement('div');
|
||||
modalContent.classList.add('bg-white', 'rounded-lg', 'p-6', 'max-w-3xl', 'relative', 'overflow-auto', 'max-h-[80vh]');
|
||||
modalContent.innerHTML = '<p class="text-gray-500">{% trans "Loading..." %}</p>';
|
||||
|
||||
if (eventUrl) {
|
||||
fetch(eventUrl)
|
||||
.then(response => response.text())
|
||||
.then(html => {
|
||||
modalContent.innerHTML = html;
|
||||
})
|
||||
.catch(error => {
|
||||
modalContent.innerHTML = '<p class="text-red-500">Failed to load event details.</p>';
|
||||
});
|
||||
|
||||
modal.appendChild(modalContent);
|
||||
document.body.appendChild(modal);
|
||||
|
||||
// Close modal when clicking outside modalContent
|
||||
modal.addEventListener('click', function(e) {
|
||||
if (e.target === modal) {
|
||||
document.body.removeChild(modal);
|
||||
|
||||
// remove ?modal=occurrenceId from the url
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.delete('modal');
|
||||
window.history.pushState({}, '', url);
|
||||
}
|
||||
});
|
||||
|
||||
// Close modal on ESC key
|
||||
document.addEventListener('keydown', function escHandler(e) {
|
||||
if (e.key === 'Escape') {
|
||||
if (document.body.contains(modal)) {
|
||||
document.body.removeChild(modal);
|
||||
}
|
||||
document.removeEventListener('keydown', escHandler);
|
||||
// remove ?modal=occurrenceId from the url
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.delete('modal');
|
||||
window.history.pushState({}, '', url);
|
||||
}
|
||||
});
|
||||
|
||||
const closeButton = document.createElement('button');
|
||||
closeButton.classList.add('absolute', 'top-2', 'right-2', 'text-red-500', 'cursor-pointer', 'hover:text-gray-700', 'text-5xl', 'translate-x-[-50%]');
|
||||
closeButton.innerHTML = '×';
|
||||
closeButton.addEventListener('click', function() {
|
||||
if (document.body.contains(modal)) {
|
||||
document.body.removeChild(modal);
|
||||
}
|
||||
// remove ?modal=occurrenceId from the url
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.delete('modal');
|
||||
window.history.pushState({}, '', url);
|
||||
});
|
||||
modal.appendChild(closeButton);
|
||||
|
||||
// add ?modal=occurrenceId to the url
|
||||
const url = new URL(window.location);
|
||||
url.searchParams.set('modal', occurrenceId);
|
||||
window.history.pushState({}, '', url);
|
||||
}
|
||||
return data;
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error fetching event details:', error);
|
||||
return null;
|
||||
});
|
||||
}
|
||||
|
||||
// if ?modal=event_id is in the url, open the modal for that event
|
||||
const urlParams = new URLSearchParams(window.location.search);
|
||||
const modalEventId = urlParams.get('modal');
|
||||
if (modalEventId) {
|
||||
showModal(modalEventId);
|
||||
}
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
var calendarEl = document.getElementById('calendar');
|
||||
var calendar = new FullCalendar.Calendar(calendarEl, {
|
||||
height: "auto",
|
||||
initialView: 'timeGridWeek',
|
||||
firstDay: 1,
|
||||
nowIndicator: true,
|
||||
views: {
|
||||
timeGridWeek: {
|
||||
titleFormat: { month: 'long', year: 'numeric' },
|
||||
slotLabelFormat: { hour: '2-digit', minute: '2-digit' },
|
||||
slotMinTime: '08:00:00',
|
||||
slotMaxTime: '22:00:00',
|
||||
},
|
||||
listWeek: {
|
||||
titleFormat: { month: 'long', year: 'numeric' },
|
||||
listDaySideFormat: false,
|
||||
},
|
||||
},
|
||||
headerToolbar: {
|
||||
left: 'prev,next today',
|
||||
center: 'title',
|
||||
right: 'timeGridWeek,listMonth',
|
||||
},
|
||||
locale: "{% get_current_language as LANGUAGE_CODE %}{{ LANGUAGE_CODE }}",
|
||||
events: "/calendar/api/calendar/occurrences/",
|
||||
eventClick: function(info) {
|
||||
// prevent default navigation
|
||||
info.jsEvent.preventDefault();
|
||||
const eventUrl = info.event.url;
|
||||
if (eventUrl) {
|
||||
showModal(info.event.id);
|
||||
}
|
||||
},
|
||||
});
|
||||
calendar.render();
|
||||
|
||||
});
|
||||
</script>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="overflow-auto">
|
||||
<div id="calendar" class="min-w-xl"></div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
62
course_calendar/templates/event_page.html
Normal file
62
course_calendar/templates/event_page.html
Normal file
@@ -0,0 +1,62 @@
|
||||
{% load static i18n wagtailcore_tags wagtailimages_tags %}
|
||||
|
||||
<h2 class="text-3xl font-bold mb-4">{{ page.title }}</h2>
|
||||
|
||||
{% if page.image %}
|
||||
{% image page.image original alt=page.title class="w-full max-h-64 rounded-lg mb-3 object-cover" %}
|
||||
{% endif %}
|
||||
|
||||
<div class="flex items-center gap-2 text-gray-600 mb-2 text-sm">
|
||||
<i class="fi fi-br-clock-three leading-0"></i>
|
||||
<span>{{ page.start|date }}, {{ page.start|time }} – {{ page.end|date }}, {{ page.end|time }}</span>
|
||||
</div>
|
||||
|
||||
<div class="flex items-center gap-2 text-gray-600 mb-6 text-sm {% if page.attendees_count >= page.max_attendees and page.max_attendees is not None and page.max_attendees != 0 %}text-red-600{% endif %}">
|
||||
<i class="fi fi-br-user leading-0"></i>
|
||||
<span>
|
||||
{{ page.attendees_count }}{% if page.max_attendees is not None and page.max_attendees != 0 %} / {{ page.max_attendees }}{% endif %}
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- sign up button -->
|
||||
<div class="not-prose my-4">
|
||||
{% if page.is_past %}
|
||||
<div class="p-4 bg-gray-100 border-l-4 border-gray-500 text-gray-700">
|
||||
<p>{% trans "This event has already ended. Please check our calendar for upcoming events." %}</p>
|
||||
</div>
|
||||
{% elif user_signed_up %}
|
||||
<div class="p-4 bg-green-100 border-l-4 border-green-500 text-green-700">
|
||||
<p>{% trans "You are signed up for this event. We look forward to seeing you there!" %}</p>
|
||||
<!-- cancel button -->
|
||||
<a href="{% url 'occurrence_signout' page.id %}" class="mt-4 inline-block bg-red-600 text-white px-4 py-2 rounded hover:bg-red-700 transition">
|
||||
{% trans "Cancel Sign Up" %}
|
||||
</a>
|
||||
</div>
|
||||
{% elif not user.is_authenticated %}
|
||||
{# If the user is not authenticated, we can prompt them to log in or sign up. #}
|
||||
<div class="p-4 bg-blue-100 border-l-4 border-blue-500 text-blue-700">
|
||||
<p>{% trans "You need to be logged in to sign up for this event. Please log in or sign up to reserve your spot." %}</p>
|
||||
<a href="{% url 'account_login' %}?next={% url 'calendar' %}?modal={{ page.id }}" class="mt-4 inline-block bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition">
|
||||
{% trans "Login" %}
|
||||
</a>
|
||||
<a href="{% url 'account_signup' %}?next={% url 'calendar' %}?modal={{ page.id }}" class="mt-4 inline-block bg-green-600 text-white px-4 py-2 rounded hover:bg-green-700 transition ml-2">
|
||||
{% trans "Sign Up" %}
|
||||
</a>
|
||||
</div>
|
||||
{% elif page.attendees_count >= page.max_attendees and page.max_attendees is not None and page.max_attendees != 0 %}
|
||||
<div class="p-4 bg-red-100 border-l-4 border-red-500 text-red-700">
|
||||
<p>{% trans "This event is fully booked. Please check back later for any cancellations." %}</p>
|
||||
</div>
|
||||
{% else %}
|
||||
<div class="p-4 bg-yellow-100 border-l-4 border-yellow-500 text-yellow-700">
|
||||
<p>{% trans "You are not signed up for this event. Please sign up to reserve your spot." %}</p>
|
||||
<a href="{% url 'occurrence_signup' page.id %}" class="mt-4 inline-block bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition">
|
||||
{% trans "Sign Up for Event" %}
|
||||
</a>
|
||||
</div>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="prose">
|
||||
{{ page.description | richtext }}
|
||||
</div>
|
||||
Reference in New Issue
Block a user