diff --git a/allsessions.php b/allsessions.php
index 01e64e7..5b87698 100644
--- a/allsessions.php
+++ b/allsessions.php
@@ -1,5 +1,5 @@
';
+$MY_TITLE = "Schedule & Sessions";
+$MY_CRUMB = "Schedule & Sessions";
+$CONTENT = '';
include 'layout.php';
diff --git a/index.php b/index.php
index 3bf94bc..14c1549 100644
--- a/index.php
+++ b/index.php
@@ -1,5 +1,5 @@
";
+$CONTENT = '';
include 'layout.php';
diff --git a/js/dir_app.js b/js/dir_app.js
index f439bf1..b93c9e9 100644
--- a/js/dir_app.js
+++ b/js/dir_app.js
@@ -856,11 +856,11 @@ const ActivityEditor = Vue.component('activityedit', {
//
//
const ActivityList = Vue.component('activitylist', {
- props: [ 'itineraryview','static' ],
+ props: [ 'itineraryview','static','show_all_sessions' ],
data: function () {
return { activities:[], mysessions:[], search:'', sortby:'starttime', reversed:false, my_ses_ids:[], my_host_ids:[],
- show_filters: 'all', expanded: 1, editing: -1, active:-1, hosts:{},
- hosts_by_sesid: {}, options:{}, conference:{}, ay:{}, conf:-1, survey_on:0, zoom_on:1, } },
+ show_filters: 'all', expanded: 1, editing: -1, active:-1, hosts:{}, selectedSlotKey: null,
+ hosts_by_sesid: {}, options:{}, conference:{}, ay:{}, conf:-1, survey_on:0, zoom_on:1, cancelingIds:[], } },
mounted: function() {
this.fetch_myevents()
},
@@ -890,7 +890,22 @@ const ActivityList = Vue.component('activitylist', {
para.classList.toggle('line-clamp-2');
btn.textContent = para.classList.contains('line-clamp-2') ? 'Show More' : 'Show Less';
},
- addTime: function(time, x) {
+ slotKeyFromStart: function(starttime) {
+ if (!starttime) { return null; }
+ return this.$root.$dj(starttime).format('YYYY-MM-DDTHH:mm');
+ },
+ slotSelection: function(slot) {
+ var self = this;
+ var list = this.filtered(this.mysessions, { applySearch: false, applySlot: false });
+ return _.filter(list, function(s) { return self.slotKeyFromStart(s.starttime) === slot.key; });
+ },
+ selectSlot: function(slot) {
+ if (this.selectedSlotKey === slot.key) { this.selectedSlotKey = null; return; }
+ this.selectedSlotKey = slot.key;
+ this.search = '';
+ },
+ clearSlot: function() { this.selectedSlotKey = null; },
+ addTime: function(time, x) {
x = parseInt(x)
let [y, m, d, h, i, s] = time.split(/[- :]/); // split time into parts
let dt = new Date(y, m-1, d, h, i, s); // create Date object
@@ -949,16 +964,38 @@ const ActivityList = Vue.component('activitylist', {
},
dumpme: function(id) {
var self = this
+ if (!self.cancelingIds.includes(id)) { self.cancelingIds.push(id) }
basic_get('dir_api.php?a=signdown/' + id,
function(r2) {
- self.mysessions = _.without( self.mysessions, _.findWhere(self.activities, {'id':id}))
- self.my_ses_ids = _.without( self.my_ses_ids, id)
- self.$forceUpdate()
- alert_message("Removed activity") })
+ setTimeout(function() {
+ self.mysessions = _.without( self.mysessions, _.findWhere(self.activities, {'id':id}))
+ self.my_ses_ids = _.without( self.my_ses_ids, id)
+ self.cancelingIds = _.without(self.cancelingIds, id)
+ self.$forceUpdate()
+ alert_message("Removed activity")
+ }, 250)
+ })
},
- filtered: function(ff) {
+ sessionsForSlot: function(slot) {
+ var self=this;
+ return _.chain(this.activities_for_slots)
+ .filter(function(item) { return self.slotKeyFromStart(item.starttime) === slot.key; })
+ .sortBy('starttime')
+ .value();
+ },
+ chooseSession: function(activity) {
+ var self=this;
+ if (!self.my_ses_ids.includes(activity.id)) {
+ self.joinme(activity.id);
+ }
+ self.clearSlot();
+ },
+ isCanceling: function(id) { return this.cancelingIds.includes(id) },
+ filtered: function(ff, opts = { applySearch: true, applySlot: false }) {
var self = this
- if (this.search) {
+ var applySearch = (opts.applySearch !== false)
+ var applySlot = (opts.applySlot !== false)
+ if (applySearch && this.search) {
var ss = self.search.toLowerCase()
ff = ff.filter(function(x) { return ('searchable' in x ? x.searchable.includes(ss) : 0) })
}
@@ -971,6 +1008,9 @@ const ActivityList = Vue.component('activitylist', {
ff = ff.filter( function(item,index) {
this_time = dayjs(item.starttime)
return this_time.isBefore(end) && start.isBefore(this_time) } )
+ if (this.selectedSlotKey && applySlot) {
+ ff = ff.filter(function(item) { return self.slotKeyFromStart(item.starttime) === self.selectedSlotKey })
+ }
ff = _.sortBy(ff, function(x) {
if (x[self.sortby]) { var s = x[self.sortby]; return s.trim().toLowerCase() }
return '' })
@@ -984,7 +1024,33 @@ const ActivityList = Vue.component('activitylist', {
current_time: function() { return dayjs().format('MMM D, h:mma') },
activities_filtered: function() { var a = this.filtered(this.activities); return a; },
mysessions_filtered: function() { return this.filtered(this.mysessions) },
-
+ activities_for_slots: function() { return this.filtered(this.activities, { applySearch: false, applySlot: false }) },
+ timeSlotsByDay: function() {
+ if (this.active < 1) { return {}; }
+ var self=this;
+ var grouped = _.groupBy(this.activities_for_slots, function(x) { return self.month_year(x.starttime) });
+ return _.mapObject(grouped, function(list) {
+ return _.chain(list)
+ .sortBy('starttime')
+ .map(function(item) {
+ var start = self.$root.$dj(item.starttime);
+ return {
+ key: self.slotKeyFromStart(item.starttime),
+ startLabel: start.format('h:mma'),
+ endLabel: self.addTime(item.starttime, item.length),
+ dayLabel: self.month_year(item.starttime)
+ };
+ })
+ .uniq(false, function(slot) { return slot.key; })
+ .value();
+ });
+ },
+ selectedSlotLabel: function() {
+ if (!this.selectedSlotKey) { return '' }
+ var m = dayjs(this.selectedSlotKey);
+ if (!m.isValid()) { return '' }
+ return m.format('MMM D, h:mma');
+ },
activities_g_filtered: function() { if (this.active<1) { return {} }
var self=this;
return _.groupBy(self.activities_filtered, function(x) { return self.month_year(x.starttime) } ); },
@@ -1003,6 +1069,83 @@ const ActivityList = Vue.component('activitylist', {
+
+
+
+ No session times are available yet.
+
+
+
+
{{ mmyy }} {{ get_day_title(mmyy) }}
+
+
+
+
+
to {{ slot.endLabel }}
+
Selected
+
+
+
+
+
+
{{ sel.title }}
+
+ {{ $root.$dj(sel.starttime).format('h:mma') }} · {{ mode_string(sel) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
Pick for {{ slot.startLabel }} - {{ slot.endLabel }}
+
Available sessions
+
+
+
+
+
+
+
+
+
{{ a.title }}
+
{{ mode_string(a) }}
+
+ In person at {{ a.location_irl }} or online
+ In person at {{ a.location_irl }}
+ Online
+
+
+
+
+ In itinerary
+
+
+
+
+
No sessions are available in this time slot.
+
+
+
+
+
+
+
+
It looks like you haven't signed up for any sessions yet!
@@ -1011,128 +1154,10 @@ const ActivityList = Vue.component('activitylist', {
-
-
-
{{ mmyy }} {{ get_day_title(mmyy) }}
-
-
-
-
-
-
-
-
-
-
{{ a.title }}
-
- {{ $root.$dj(a.starttime).format('h:mma') }} - {{ addTime(a.starttime, a.length) }}
- · In person at {{ a.location_irl }} or
- online
- online
-
- · In person at {{ a.location_irl }}
- · Online
- · Online
-
-
-
-
-
-
-
-
-
-
-
- {{ mmyy }} {{ get_day_title(mmyy) }}
-
-
-
-
-
-
-
-
-
-
-
-
-
{{ a.title }}
-
-
- {{ $root.$dj(a.starttime).format('h:mma') }} - {{ addTime(a.starttime, a.length) }} ·
- {{ mode_string(a) }}
- · {{ a.location_irl }}
-
-
-
Presented by: {{ hoststr(a.id) }}
-
Location: {{ a.location_irl }}
-
- Zoom Link:
- {{ a.location }}
-
-
-
-
-
-
-
-
` })
diff --git a/style.css b/style.css
index 5ab2c11..8ed2a36 100644
--- a/style.css
+++ b/style.css
@@ -48,3 +48,19 @@ ol {
overflow-wrap: anywhere;
word-break: break-all; /* only affects long unbroken tokens like URLs */
}
+
+/* Slot picker slide/fade */
+.slotpanel-enter-active,
+.slotpanel-leave-active {
+ transition: all 0.25s ease;
+}
+.slotpanel-enter,
+.slotpanel-leave-to {
+ opacity: 0;
+ transform: translateY(-6px);
+ max-height: 0;
+}
+.slotpanel-enter-to,
+.slotpanel-leave {
+ max-height: 600px;
+}