dec 2025 last update
This commit is contained in:
parent
4d40559205
commit
4c705703b5
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
$MY_TITLE = "Schedule & Sessions";
|
$MY_TITLE = "All Sessions";
|
||||||
$MY_CRUMB = "Schedule & Sessions";
|
$MY_CRUMB = "All Sessions";
|
||||||
$CONTENT = '<activitylist :itineraryview="1" :show_all_sessions="true"></activitylist>';
|
$timeslot_config = file_exists('schedule_timeslots.json') ? json_decode(file_get_contents('schedule_timeslots.json'), true) : [];
|
||||||
|
$config_js = '<script>window.TIMESLOT_CONFIG = ' . json_encode($timeslot_config) . ';</script>';
|
||||||
|
$CONTENT = '<div id="timeslot-config" class="hidden"></div><activitylist :itineraryview="0" :static="1" :show_all_sessions="true"></activitylist>';
|
||||||
include 'layout.php';
|
include 'layout.php';
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
<?php
|
<?php
|
||||||
$MY_TITLE = "Schedule & Sessions";
|
$MY_TITLE = "Schedule & Sessions";
|
||||||
$MY_CRUMB = "My Schedule";
|
$MY_CRUMB = "My Schedule";
|
||||||
$CONTENT = '<activitylist :itineraryview="1" :show_all_sessions="true"></activitylist>';
|
$timeslot_config = file_exists('schedule_timeslots.json') ? json_decode(file_get_contents('schedule_timeslots.json'), true) : [];
|
||||||
|
$config_js = '<script>window.TIMESLOT_CONFIG = ' . json_encode($timeslot_config) . ';</script>';
|
||||||
|
$CONTENT = $config_js . '<div id="timeslot-config" class="hidden"></div><activitylist :itineraryview="1" :show_all_sessions="true"></activitylist>';
|
||||||
include 'layout.php';
|
include 'layout.php';
|
||||||
|
|
|
||||||
107
js/dir_app.js
107
js/dir_app.js
|
|
@ -855,12 +855,24 @@ const ActivityEditor = Vue.component('activityedit', {
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
//
|
//
|
||||||
|
// Empty time slot card
|
||||||
|
const EmptySlotCard = Vue.component('emptyslotcard', {
|
||||||
|
props: ['label'],
|
||||||
|
template: `<button
|
||||||
|
class="w-full border-4 border-dashed border-blue-300 bg-blue-50 text-blue-800 rounded-xl py-6 px-4 flex flex-col items-center justify-center gap-2 hover:bg-blue-100 hover:border-blue-400 transition"
|
||||||
|
@click="$emit('select')">
|
||||||
|
<span class="text-4xl font-black leading-none">+</span>
|
||||||
|
<span class="text-lg font-semibold uppercase tracking-wide">{{ label || 'Signup' }}</span>
|
||||||
|
</button>`
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
const ActivityList = Vue.component('activitylist', {
|
const ActivityList = Vue.component('activitylist', {
|
||||||
props: [ 'itineraryview','static','show_all_sessions' ],
|
props: [ 'itineraryview','static','show_all_sessions' ],
|
||||||
data: function () {
|
data: function () {
|
||||||
return { activities:[], mysessions:[], search:'', sortby:'starttime', reversed:false, my_ses_ids:[], my_host_ids:[],
|
return { activities:[], mysessions:[], search:'', sortby:'starttime', reversed:false, my_ses_ids:[], my_host_ids:[],
|
||||||
show_filters: 'all', expanded: 1, editing: -1, active:-1, hosts:{}, selectedSlotKey: null,
|
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:[], } },
|
hosts_by_sesid: {}, options:{}, conference:{}, ay:{}, conf:-1, survey_on:0, zoom_on:1, cancelingIds:[], timeslotConfig:null, } },
|
||||||
mounted: function() {
|
mounted: function() {
|
||||||
this.fetch_myevents()
|
this.fetch_myevents()
|
||||||
},
|
},
|
||||||
|
|
@ -919,8 +931,10 @@ const ActivityList = Vue.component('activitylist', {
|
||||||
mode_string: function(a) { if (this.$root.active) { return _.findWhere(this.$root.modes_menu, { 'id': a.mode })['string'] } return a.mode },
|
mode_string: function(a) { if (this.$root.active) { return _.findWhere(this.$root.modes_menu, { 'id': a.mode })['string'] } return a.mode },
|
||||||
get_day_title: function(day) {
|
get_day_title: function(day) {
|
||||||
var d = dayjs(day, 'MMM DD YYYY')
|
var d = dayjs(day, 'MMM DD YYYY')
|
||||||
convertedDateString = d.format('YYYY-MM-DD')
|
var convertedDateString = d.format('YYYY-MM-DD')
|
||||||
return _.findWhere( this.conference, {date1:convertedDateString} ).title
|
var found = _.findWhere( this.conference, {date1:convertedDateString} )
|
||||||
|
if (found && found.title) { return found.title }
|
||||||
|
return ''
|
||||||
},
|
},
|
||||||
month_year: function(d) { var b = this.$root.$dj(d).format('MMM D YYYY'); return b },
|
month_year: function(d) { var b = this.$root.$dj(d).format('MMM D YYYY'); return b },
|
||||||
setsort: function(ss) {
|
setsort: function(ss) {
|
||||||
|
|
@ -950,6 +964,7 @@ const ActivityList = Vue.component('activitylist', {
|
||||||
self.survey_on = parseInt( _.findWhere(self.options, { label:'survey_on' }).value )
|
self.survey_on = parseInt( _.findWhere(self.options, { label:'survey_on' }).value )
|
||||||
self.zoom_on = parseInt( _.findWhere(self.options, { label:'zoom_on' }).value )
|
self.zoom_on = parseInt( _.findWhere(self.options, { label:'zoom_on' }).value )
|
||||||
self.active = 1
|
self.active = 1
|
||||||
|
self.loadTimeslots()
|
||||||
self.$forceUpdate();
|
self.$forceUpdate();
|
||||||
|
|
||||||
} )
|
} )
|
||||||
|
|
@ -978,6 +993,7 @@ const ActivityList = Vue.component('activitylist', {
|
||||||
},
|
},
|
||||||
sessionsForSlot: function(slot) {
|
sessionsForSlot: function(slot) {
|
||||||
var self=this;
|
var self=this;
|
||||||
|
if (slot.presetSessions && slot.presetSessions.length) { return []; }
|
||||||
return _.chain(this.activities_for_slots)
|
return _.chain(this.activities_for_slots)
|
||||||
.filter(function(item) { return self.slotKeyFromStart(item.starttime) === slot.key; })
|
.filter(function(item) { return self.slotKeyFromStart(item.starttime) === slot.key; })
|
||||||
.sortBy('starttime')
|
.sortBy('starttime')
|
||||||
|
|
@ -990,6 +1006,23 @@ const ActivityList = Vue.component('activitylist', {
|
||||||
}
|
}
|
||||||
self.clearSlot();
|
self.clearSlot();
|
||||||
},
|
},
|
||||||
|
slotPresets: function(slot) { return slot.presetSessions || []; },
|
||||||
|
slotHasPreset: function(slot) { return (slot.presetSessions && slot.presetSessions.length>0); },
|
||||||
|
loadTimeslots: function() {
|
||||||
|
var self=this;
|
||||||
|
if (window.TIMESLOT_CONFIG) {
|
||||||
|
self.timeslotConfig = window.TIMESLOT_CONFIG;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
fetch('schedule_timeslots.json').then(function(resp) {
|
||||||
|
if (!resp.ok) { throw new Error('timeslot fetch'); }
|
||||||
|
return resp.json();
|
||||||
|
}).then(function(json) {
|
||||||
|
self.timeslotConfig = json;
|
||||||
|
}).catch(function(e) {
|
||||||
|
console.warn('No schedule_timeslots.json found or unreadable', e);
|
||||||
|
});
|
||||||
|
},
|
||||||
isCanceling: function(id) { return this.cancelingIds.includes(id) },
|
isCanceling: function(id) { return this.cancelingIds.includes(id) },
|
||||||
filtered: function(ff, opts = { applySearch: true, applySlot: false }) {
|
filtered: function(ff, opts = { applySearch: true, applySlot: false }) {
|
||||||
var self = this
|
var self = this
|
||||||
|
|
@ -1028,6 +1061,28 @@ const ActivityList = Vue.component('activitylist', {
|
||||||
timeSlotsByDay: function() {
|
timeSlotsByDay: function() {
|
||||||
if (this.active < 1) { return {}; }
|
if (this.active < 1) { return {}; }
|
||||||
var self=this;
|
var self=this;
|
||||||
|
if (this.timeslotConfig && this.timeslotConfig.versions && this.timeslotConfig.days) {
|
||||||
|
var res = {};
|
||||||
|
_.each(this.timeslotConfig.days, function(versionKey, dayStr) {
|
||||||
|
var version = self.timeslotConfig.versions[versionKey];
|
||||||
|
if (!version || !version.slots) { return; }
|
||||||
|
var dayLabel = dayjs(dayStr).format('MMM D YYYY');
|
||||||
|
res[dayLabel] = _.map(version.slots, function(slot) {
|
||||||
|
var start = dayjs(dayStr + ' ' + slot.start);
|
||||||
|
var end = dayjs(dayStr + ' ' + slot.end);
|
||||||
|
return {
|
||||||
|
key: dayStr + 'T' + slot.start,
|
||||||
|
startLabel: start.isValid() ? start.format('h:mma') : slot.start,
|
||||||
|
endLabel: end.isValid() ? end.format('h:mma') : slot.end,
|
||||||
|
dayLabel: dayLabel,
|
||||||
|
presetSessions: slot.sessions || [],
|
||||||
|
version: versionKey,
|
||||||
|
versionLabel: version.label || versionKey
|
||||||
|
};
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return res;
|
||||||
|
}
|
||||||
var grouped = _.groupBy(this.activities_for_slots, function(x) { return self.month_year(x.starttime) });
|
var grouped = _.groupBy(this.activities_for_slots, function(x) { return self.month_year(x.starttime) });
|
||||||
return _.mapObject(grouped, function(list) {
|
return _.mapObject(grouped, function(list) {
|
||||||
return _.chain(list)
|
return _.chain(list)
|
||||||
|
|
@ -1038,7 +1093,10 @@ const ActivityList = Vue.component('activitylist', {
|
||||||
key: self.slotKeyFromStart(item.starttime),
|
key: self.slotKeyFromStart(item.starttime),
|
||||||
startLabel: start.format('h:mma'),
|
startLabel: start.format('h:mma'),
|
||||||
endLabel: self.addTime(item.starttime, item.length),
|
endLabel: self.addTime(item.starttime, item.length),
|
||||||
dayLabel: self.month_year(item.starttime)
|
dayLabel: self.month_year(item.starttime),
|
||||||
|
presetSessions: [],
|
||||||
|
version: null,
|
||||||
|
versionLabel: null,
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
.uniq(false, function(slot) { return slot.key; })
|
.uniq(false, function(slot) { return slot.key; })
|
||||||
|
|
@ -1086,7 +1144,19 @@ const ActivityList = Vue.component('activitylist', {
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div class="flex-1">
|
<div class="flex-1">
|
||||||
<div v-if="slotSelection(slot).length" class="space-y-2">
|
<div v-if="slotHasPreset(slot)" class="space-y-2">
|
||||||
|
<div v-for="sel in slotPresets(slot)" :key="sel.title" class="border-b last:border-none pb-2 last:pb-0">
|
||||||
|
<div class="font-semibold text-gray-900">{{ sel.title }}</div>
|
||||||
|
<div class="text-sm text-gray-600">
|
||||||
|
<span v-if="sel.audience" class="mr-2 capitalize">{{ sel.audience }} meeting</span>
|
||||||
|
<span v-if="sel.location">at {{ sel.location }}</span>
|
||||||
|
<span v-if="sel.mode">· {{ sel.mode }}</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="sel.notes" class="text-xs text-gray-500 mt-1">{{ sel.notes }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else-if="slotSelection(slot).length" class="space-y-2">
|
||||||
<div v-for="sel in slotSelection(slot)" :key="sel.id" :class="['border-b last:border-none pb-2 last:pb-0 transition-opacity duration-300', isCanceling(sel.id) ? 'opacity-40' : '']">
|
<div v-for="sel in slotSelection(slot)" :key="sel.id" :class="['border-b last:border-none pb-2 last:pb-0 transition-opacity duration-300', isCanceling(sel.id) ? 'opacity-40' : '']">
|
||||||
<div class="font-semibold text-gray-900">{{ sel.title }}</div>
|
<div class="font-semibold text-gray-900">{{ sel.title }}</div>
|
||||||
<div class="text-sm text-gray-600">
|
<div class="text-sm text-gray-600">
|
||||||
|
|
@ -1099,13 +1169,11 @@ const ActivityList = Vue.component('activitylist', {
|
||||||
<button class="mt-1 text-sm text-blue-600 hover:underline" @click="selectSlot(slot)">Change selection</button>
|
<button class="mt-1 text-sm text-blue-600 hover:underline" @click="selectSlot(slot)">Change selection</button>
|
||||||
</div>
|
</div>
|
||||||
<div v-else class="mt-1">
|
<div v-else class="mt-1">
|
||||||
<button class="w-full border-2 border-dashed border-blue-300 text-blue-700 font-semibold rounded-md py-3 hover:bg-blue-50 text-left px-4" @click="selectSlot(slot)">
|
<emptyslotcard label="Signup" @select="selectSlot(slot)"></emptyslotcard>
|
||||||
Pick a session
|
|
||||||
</button>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<transition name="slotpanel">
|
<transition name="slotpanel">
|
||||||
<div v-if="selectedSlotKey === slot.key" class="mt-4 border rounded-lg bg-blue-50 border-blue-200 p-4 shadow-inner">
|
<div v-if="selectedSlotKey === slot.key && !slotHasPreset(slot)" class="mt-4 border rounded-lg bg-blue-50 border-blue-200 p-4 shadow-inner">
|
||||||
<div class="flex items-start justify-between gap-3 mb-3">
|
<div class="flex items-start justify-between gap-3 mb-3">
|
||||||
<div>
|
<div>
|
||||||
<div class="text-sm text-gray-600">Pick for {{ slot.startLabel }} - {{ slot.endLabel }}</div>
|
<div class="text-sm text-gray-600">Pick for {{ slot.startLabel }} - {{ slot.endLabel }}</div>
|
||||||
|
|
@ -1154,6 +1222,27 @@ const ActivityList = Vue.component('activitylist', {
|
||||||
</p>
|
</p>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<!-- Simple full list for All Sessions -->
|
||||||
|
<div v-if="!itineraryview && active > 0" class="space-y-6">
|
||||||
|
<div v-for="(items, mmyy) in activities_g_filtered" :key="mmyy">
|
||||||
|
<h3 class="text-xl font-semibold text-gray-800 mb-2">{{ mmyy }} {{ get_day_title(mmyy) }}</h3>
|
||||||
|
<div class="space-y-3">
|
||||||
|
<div v-for="a in items" :key="a.id" class="bg-white border rounded-lg p-4 shadow-sm">
|
||||||
|
<div class="flex justify-between items-start gap-3">
|
||||||
|
<div>
|
||||||
|
<div class="text-sm text-gray-500">{{ $root.$dj(a.starttime).format('h:mma') }} - {{ addTime(a.starttime, a.length) }}</div>
|
||||||
|
<h4 class="text-lg font-semibold text-gray-900">{{ a.title }}</h4>
|
||||||
|
<p class="text-sm text-gray-700 mt-1" v-html="a.desc"></p>
|
||||||
|
<p class="text-xs uppercase text-gray-500 mt-2">{{ mode_string(a) }}</p>
|
||||||
|
<p class="text-sm text-gray-600" v-if="a.location_irl">Location: {{ a.location_irl }}</p>
|
||||||
|
<p class="text-sm text-blue-600" v-if="a.location">Link: <a :href="a.location" class="underline">{{ a.location }}</a></p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -13,6 +13,7 @@ require_once('semester.php');
|
||||||
if (!isset($MY_TITLE)) $MY_TITLE = "Untitled Page";
|
if (!isset($MY_TITLE)) $MY_TITLE = "Untitled Page";
|
||||||
if (!isset($MY_CRUMB)) $MY_CRUMB = $MY_TITLE;
|
if (!isset($MY_CRUMB)) $MY_CRUMB = $MY_TITLE;
|
||||||
if (!isset($CONTENT)) $CONTENT = "<p>No content provided.</p>";
|
if (!isset($CONTENT)) $CONTENT = "<p>No content provided.</p>";
|
||||||
|
$DIR_APP_VER = file_exists('js/dir_app.js') ? filemtime('js/dir_app.js') : time();
|
||||||
|
|
||||||
$MY_PATH = $_SERVER['PHP_SELF'];
|
$MY_PATH = $_SERVER['PHP_SELF'];
|
||||||
$MOD_DATE = file_exists(__FILE__) ? date("F d Y H:i.", filemtime(__FILE__)) : "(unknown)";
|
$MOD_DATE = file_exists(__FILE__) ? date("F d Y H:i.", filemtime(__FILE__)) : "(unknown)";
|
||||||
|
|
@ -25,6 +26,7 @@ $MOD_DATE = file_exists(__FILE__) ? date("F d Y H:i.", filemtime(__FILE__)) : "(
|
||||||
<script src="js/tailwind.js"></script>
|
<script src="js/tailwind.js"></script>
|
||||||
<script src="js/vue27max.js"></script>
|
<script src="js/vue27max.js"></script>
|
||||||
<script src="js/intranet_libs.js"/></script>
|
<script src="js/intranet_libs.js"/></script>
|
||||||
|
<?php if (isset($config_js)) { echo $config_js; } ?>
|
||||||
<link rel="stylesheet" type="text/css" href="style.css" />
|
<link rel="stylesheet" type="text/css" href="style.css" />
|
||||||
</head>
|
</head>
|
||||||
<body class="bg-gray-100 min-h-screen">
|
<body class="bg-gray-100 min-h-screen">
|
||||||
|
|
@ -73,7 +75,7 @@ $MOD_DATE = file_exists(__FILE__) ? date("F d Y H:i.", filemtime(__FILE__)) : "(
|
||||||
<br />
|
<br />
|
||||||
<br />
|
<br />
|
||||||
<script src="js/intranet_libs_bottom.js"/></script>
|
<script src="js/intranet_libs_bottom.js"/></script>
|
||||||
<script src="js/dir_app.js"></script>
|
<script src="js/dir_app.js?v=<?= $DIR_APP_VER ?>"></script>
|
||||||
<script src="<?= $XTRAJS ?>"></script>
|
<script src="<?= $XTRAJS ?>"></script>
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue