Skip to content

Commit 5350329

Browse files
committed
fix: conference subscriptions
1 parent 69e667c commit 5350329

File tree

7 files changed

+699
-520
lines changed

7 files changed

+699
-520
lines changed
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<!-- Conference Detail Page Action Buttons -->
2+
{% assign conf_id = page.conference | append: "-" | append: page.year | slugify: "latin" %} {% assign cfp_date =
3+
page.cfp_ext | default: page.cfp %}
4+
5+
<div
6+
class="conference-actions"
7+
data-conf-id="{{ conf_id }}"
8+
data-conf-name="{{ page.conference }}"
9+
data-conf-year="{{ page.year }}"
10+
data-conf-cfp="{{ cfp_date }}"
11+
data-conf-place="{{ page.place }}"
12+
>
13+
<!-- Save to Favorites Button -->
14+
<button
15+
class="btn-action btn-action-secondary btn-save-conference"
16+
data-action="save"
17+
data-conf-id="{{ conf_id }}"
18+
aria-label="Save {{ page.conference }} {{ page.year }} to favorites"
19+
>
20+
<i class="far fa-bookmark"></i>
21+
<span class="btn-text">Save</span>
22+
</button>
23+
24+
<!-- Follow Series Button (Primary CTA) -->
25+
<button
26+
class="btn-action btn-action-primary btn-follow-series"
27+
data-series="{{ page.conference }}"
28+
data-conf-id="{{ conf_id }}"
29+
aria-label="Follow {{ page.conference }} series"
30+
>
31+
<i class="fas fa-bell"></i>
32+
<span class="btn-text">Follow Series</span>
33+
</button>
34+
35+
<!-- Add to Calendar Button -->
36+
<div class="btn-action-calendar-wrapper">
37+
<button
38+
class="btn-action btn-action-secondary btn-add-calendar"
39+
data-action="calendar"
40+
data-conf-id="{{ conf_id }}"
41+
aria-label="Add {{ page.conference }} {{ page.year }} to calendar"
42+
>
43+
<i class="far fa-calendar-plus"></i>
44+
<span class="btn-text">Add to Calendar</span>
45+
<i class="fas fa-chevron-down calendar-dropdown-arrow"></i>
46+
</button>
47+
48+
<!-- Calendar Dropdown -->
49+
<div class="calendar-dropdown-menu" data-conf-id="{{ conf_id }}">
50+
<a href="#" class="calendar-option" data-calendar="google" data-conf-id="{{ conf_id }}">
51+
<i class="fab fa-google"></i>
52+
<span>Google Calendar</span>
53+
</a>
54+
<a href="#" class="calendar-option" data-calendar="outlook" data-conf-id="{{ conf_id }}">
55+
<i class="fab fa-microsoft"></i>
56+
<span>Outlook</span>
57+
</a>
58+
<a href="#" class="calendar-option" data-calendar="apple" data-conf-id="{{ conf_id }}">
59+
<i class="fab fa-apple"></i>
60+
<span>Apple Calendar</span>
61+
</a>
62+
<a href="#" class="calendar-option" data-calendar="ics" data-conf-id="{{ conf_id }}">
63+
<i class="fas fa-download"></i>
64+
<span>Download .ics</span>
65+
</a>
66+
</div>
67+
</div>
68+
</div>

_includes/head.html

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@
3535
<link rel="stylesheet" type="text/css" href="{{ "/static/css/mobile-fixes.css" | prepend:site.baseurl_root }}?t={{site.time | date: '%s'}}" media="screen,projection">
3636
<link rel="stylesheet" type="text/css" href="{{ "/static/css/dashboard.css" | prepend:site.baseurl_root }}?t={{site.time | date: '%s'}}" media="screen,projection">
3737
<link rel="stylesheet" type="text/css" href="{{ "/static/css/action-bar-minimal.css" | prepend:site.baseurl_root }}?t={{site.time | date: '%s'}}" media="screen,projection">
38+
<link rel="stylesheet" type="text/css" href="{{ "/static/css/conference-detail-actions.css" | prepend:site.baseurl_root }}?t={{site.time | date: '%s'}}" media="screen,projection">
3839
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/fontawesome.min.css" media="screen,projection">
3940
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/solid.min.css" media="screen,projection">
4041
<link rel="stylesheet" type="text/css" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.6.0/css/regular.min.css" media="screen,projection">

_layouts/conference.html

Lines changed: 181 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -90,14 +90,29 @@ <h3 id="conf-subtitle">a.k.a. {{page.alt_name}} {{page.year}}</h3>
9090
</div>
9191
</div>
9292
</div>
93-
<div id="page-content">
93+
<div id="page-content"
94+
data-conf-id="{{page.conference | slugify: "latin"}}-{{page.year}}"
95+
data-conf-name="{{page.conference}}"
96+
data-conf-year="{{page.year}}"
97+
data-location="{{page.place}}"
98+
data-cfp="{{page.cfp}}"
99+
data-cfp-ext="{{page.cfp_ext}}"
100+
data-start="{{page.start}}"
101+
data-end="{{page.end}}"
102+
data-link="{{page.link}}">
94103
<div id="conf-deadline-timer" class="row">
95104
<div id="cfp-timer" class="col-12 conf-timer countdown-display"
96105
data-deadline="{{ page.cfp_ext | default: page.cfp }}"
97106
data-timezone="{{ page.timezone | default: 'UTC-12' }}">
98107
{% if cfp == "TBA" or cfp == "Cancelled" or cfp == "None" %}{{cfp}}{%endif%}
99108
</div>
100109
</div>
110+
<!-- Conference Action Buttons -->
111+
<div class="row">
112+
<div class="col-12">
113+
{% include conference_detail_actions.html %}
114+
</div>
115+
</div>
101116
<div id="conf-key-facts" class="row">
102117
<div class="col-12 col-md-6">
103118
<div>
@@ -161,14 +176,6 @@ <h3 id="conf-subtitle">a.k.a. {{page.alt_name}} {{page.year}}</h3>
161176
</div>
162177
<div id="conf-deadlines" class="row">
163178
<div class="col-12 col-md-6">
164-
<div class="meta deadline">
165-
{% t conference.download_dl %}:
166-
</div>
167-
<div id="conference-deadline" class="calendar meta"></div>
168-
<div class="meta deadline">
169-
{% t conference.download_date %}:
170-
</div>
171-
<div id="conference-calendar" class="calendar meta"></div>
172179
<div class="meta deadline">
173180
{% t conference.deadline_tz_theirs %}:
174181
</div>
@@ -264,36 +271,9 @@ <h3 id="conf-subtitle">a.k.a. {{page.alt_name}} {{page.year}}</h3>
264271
console.log("Invalid timezone. Using system timezone instead.");
265272
}
266273

267-
// add calendar
268-
269-
var conferenceDeadlineCalendar = createCalendarFromObject({
270-
id: '{{page.conference | slugify: "latin"}}-{{page.year}}',
271-
title: '{{page.conference}} {{page.year}} deadline',
272-
start_date: confDeadline.toJSDate(),
273-
duration: 60,
274-
place: '{{page.place}}',
275-
link: '{{page.link}}',
276-
});
277-
var deadlineContainer = document.querySelector('#conference-deadline');
278-
if (deadlineContainer) {
279-
deadlineContainer.appendChild(conferenceDeadlineCalendar);
280-
}
281-
282-
283-
var conferenceCalendar = createCalendarFromObject({
284-
id: '{{page.conference | slugify: "latin"}}-{{page.year}}',
285-
title: '{{page.conference}} {{page.year}}',
286-
start_date: DateTime.fromSQL("{{page.start}}").toJSDate(),
287-
end_date: DateTime.fromSQL("{{page.end}}").toJSDate(),
288-
place: '{{page.place}}',
289-
link: '{{page.link}}',
290-
});
291-
var calendarContainer = document.querySelector('#conference-calendar');
292-
if (calendarContainer) {
293-
calendarContainer.appendChild(conferenceCalendar);
294-
}
274+
// Calendar export is now handled by conference action buttons
295275

296-
// Countdown timer is now handled by countdown-simple.js automatically
276+
// Countdown timer is handled by countdown-simple.js automatically
297277

298278
// render deadline conference time
299279
{% if page.timezone %}
@@ -307,6 +287,169 @@ <h3 id="conf-subtitle">a.k.a. {{page.alt_name}} {{page.year}}</h3>
307287

308288
{% endif %}
309289

290+
// Conference Detail Action Buttons Event Handlers
291+
292+
// Notification helper
293+
function showConferenceNotification(text, type) {
294+
const bgColor = type === 'success' ? '#28a745' : type === 'info' ? '#17a2b8' : '#ffc107';
295+
const message = $('<div>')
296+
.text(text)
297+
.css({
298+
position: 'fixed',
299+
top: '20px',
300+
right: '20px',
301+
background: bgColor,
302+
color: 'white',
303+
padding: '12px 24px',
304+
borderRadius: '6px',
305+
zIndex: 9999,
306+
boxShadow: '0 4px 12px rgba(0,0,0,0.2)',
307+
animation: 'slideIn 0.3s ease'
308+
});
309+
310+
$('body').append(message);
311+
setTimeout(() => {
312+
message.fadeOut(300, () => message.remove());
313+
}, 3000);
314+
}
315+
316+
// Follow Series Button
317+
$('.btn-follow-series').on('click', function() {
318+
const btn = $(this);
319+
const seriesName = btn.data('series');
320+
321+
if (window.confManager) {
322+
const isFollowing = window.confManager.isSeriesFollowed(seriesName);
323+
324+
if (isFollowing) {
325+
window.confManager.unfollowSeries(seriesName);
326+
btn.removeClass('active');
327+
btn.find('.btn-text').text('Follow Series');
328+
showConferenceNotification('Unfollowed ' + seriesName, 'info');
329+
} else {
330+
window.confManager.followSeries(seriesName);
331+
btn.addClass('active');
332+
btn.find('.btn-text').text('Following');
333+
showConferenceNotification('Now following ' + seriesName + ' series!', 'success');
334+
}
335+
}
336+
});
337+
338+
// Save Conference Button
339+
$('.btn-save-conference').on('click', function() {
340+
const btn = $(this);
341+
const confId = btn.data('conf-id');
342+
343+
if (window.confManager) {
344+
const isSaved = window.confManager.isEventSaved(confId);
345+
346+
if (isSaved) {
347+
window.confManager.removeSavedEvent(confId);
348+
btn.removeClass('active');
349+
btn.find('i').removeClass('fas').addClass('far');
350+
btn.find('.btn-text').text('Save');
351+
showConferenceNotification('Removed from favorites', 'info');
352+
} else {
353+
window.confManager.saveEvent(confId);
354+
btn.addClass('active');
355+
btn.find('i').removeClass('far').addClass('fas');
356+
btn.find('.btn-text').text('Saved');
357+
showConferenceNotification('Saved to favorites!', 'success');
358+
}
359+
}
360+
});
361+
362+
// Calendar Button - Generate calendar links
363+
$('.calendar-option').on('click', function(e) {
364+
e.preventDefault();
365+
const calendarType = $(this).data('calendar');
366+
367+
// Get conference data from page
368+
const confName = '{{ page.conference }}';
369+
const confYear = '{{ page.year }}';
370+
const cfpDate = '{{ page.cfp_ext | default: page.cfp }}';
371+
const confPlace = '{{ page.place }}';
372+
373+
// Format the date for calendar
374+
const eventDate = new Date(cfpDate);
375+
const startDate = eventDate.toISOString().replace(/-|:|\.\d\d\d/g, '');
376+
377+
// Create event details
378+
const eventTitle = confName + ' ' + confYear + ' - CFP Deadline';
379+
const eventDetails = 'Call for Proposals deadline for ' + confName + ' ' + confYear;
380+
381+
let calendarUrl = '';
382+
383+
switch(calendarType) {
384+
case 'google':
385+
calendarUrl = 'https://calendar.google.com/calendar/render?action=TEMPLATE&text=' +
386+
encodeURIComponent(eventTitle) +
387+
'&details=' + encodeURIComponent(eventDetails) +
388+
'&location=' + encodeURIComponent(confPlace) +
389+
'&dates=' + startDate + '/' + startDate;
390+
window.open(calendarUrl, '_blank');
391+
showConferenceNotification('Opening Google Calendar...', 'success');
392+
break;
393+
394+
case 'outlook':
395+
calendarUrl = 'https://outlook.live.com/calendar/0/deeplink/compose?subject=' +
396+
encodeURIComponent(eventTitle) +
397+
'&body=' + encodeURIComponent(eventDetails) +
398+
'&location=' + encodeURIComponent(confPlace) +
399+
'&startdt=' + startDate +
400+
'&enddt=' + startDate;
401+
window.open(calendarUrl, '_blank');
402+
showConferenceNotification('Opening Outlook Calendar...', 'success');
403+
break;
404+
405+
case 'apple':
406+
case 'ics':
407+
// Generate ICS file
408+
const icsContent = 'BEGIN:VCALENDAR\n' +
409+
'VERSION:2.0\n' +
410+
'PRODID:-//PythonDeadlines//Conference CFP//EN\n' +
411+
'BEGIN:VEVENT\n' +
412+
'UID:' + Date.now() + '@pythondeadlin.es\n' +
413+
'DTSTAMP:' + new Date().toISOString().replace(/-|:|\.\d\d\d/g, '') + '\n' +
414+
'DTSTART:' + startDate + '\n' +
415+
'DTEND:' + startDate + '\n' +
416+
'SUMMARY:' + eventTitle + '\n' +
417+
'DESCRIPTION:' + eventDetails + '\n' +
418+
'LOCATION:' + confPlace + '\n' +
419+
'END:VEVENT\n' +
420+
'END:VCALENDAR';
421+
422+
const blob = new Blob([icsContent], { type: 'text/calendar' });
423+
const url = URL.createObjectURL(blob);
424+
const link = document.createElement('a');
425+
link.href = url;
426+
link.download = confName.replace(/[^a-z0-9]/gi, '_') + '_' + confYear + '_CFP.ics';
427+
document.body.appendChild(link);
428+
link.click();
429+
document.body.removeChild(link);
430+
URL.revokeObjectURL(url);
431+
showConferenceNotification('Calendar file downloaded!', 'success');
432+
break;
433+
}
434+
});
435+
436+
// Initialize button states on page load
437+
const confId = '{{ page.conference | slugify: "latin" }}-{{ page.year }}';
438+
const seriesName = '{{ page.conference }}';
439+
440+
if (window.confManager) {
441+
// Update Follow Series button state
442+
if (window.confManager.isSeriesFollowed(seriesName)) {
443+
$('.btn-follow-series').addClass('active').find('.btn-text').text('Following');
444+
}
445+
446+
// Update Save button state
447+
if (window.confManager.isEventSaved(confId)) {
448+
$('.btn-save-conference').addClass('active').find('i').removeClass('far').addClass('fas');
449+
$('.btn-save-conference .btn-text').text('Saved');
450+
}
451+
}
452+
310453
});
311454
</script>
312455

0 commit comments

Comments
 (0)