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) }}

+
+
+
+
{{ slot.startLabel }}
+
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) }}

- -
-
- - -
- Edit - - Info -
- - -
-

{{ 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) }} - -
- - -
-
-
- -
- Cancel - - Sign Up - - -        - - - Edit - You are Host. -
- - -
-

{{ 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; +}