dec 2025 last update
This commit is contained in:
parent
4d40559205
commit
4c705703b5
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
$MY_TITLE = "Schedule & Sessions";
|
||||
$MY_CRUMB = "Schedule & Sessions";
|
||||
$CONTENT = '<activitylist :itineraryview="1" :show_all_sessions="true"></activitylist>';
|
||||
$MY_TITLE = "All Sessions";
|
||||
$MY_CRUMB = "All Sessions";
|
||||
$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';
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?php
|
||||
$MY_TITLE = "Schedule & Sessions";
|
||||
$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';
|
||||
|
|
|
|||
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', {
|
||||
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:{}, 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() {
|
||||
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 },
|
||||
get_day_title: function(day) {
|
||||
var d = dayjs(day, 'MMM DD YYYY')
|
||||
convertedDateString = d.format('YYYY-MM-DD')
|
||||
return _.findWhere( this.conference, {date1:convertedDateString} ).title
|
||||
var convertedDateString = d.format('YYYY-MM-DD')
|
||||
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 },
|
||||
setsort: function(ss) {
|
||||
|
|
@ -950,6 +964,7 @@ const ActivityList = Vue.component('activitylist', {
|
|||
self.survey_on = parseInt( _.findWhere(self.options, { label:'survey_on' }).value )
|
||||
self.zoom_on = parseInt( _.findWhere(self.options, { label:'zoom_on' }).value )
|
||||
self.active = 1
|
||||
self.loadTimeslots()
|
||||
self.$forceUpdate();
|
||||
|
||||
} )
|
||||
|
|
@ -978,6 +993,7 @@ const ActivityList = Vue.component('activitylist', {
|
|||
},
|
||||
sessionsForSlot: function(slot) {
|
||||
var self=this;
|
||||
if (slot.presetSessions && slot.presetSessions.length) { return []; }
|
||||
return _.chain(this.activities_for_slots)
|
||||
.filter(function(item) { return self.slotKeyFromStart(item.starttime) === slot.key; })
|
||||
.sortBy('starttime')
|
||||
|
|
@ -990,6 +1006,23 @@ const ActivityList = Vue.component('activitylist', {
|
|||
}
|
||||
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) },
|
||||
filtered: function(ff, opts = { applySearch: true, applySlot: false }) {
|
||||
var self = this
|
||||
|
|
@ -1028,6 +1061,28 @@ const ActivityList = Vue.component('activitylist', {
|
|||
timeSlotsByDay: function() {
|
||||
if (this.active < 1) { return {}; }
|
||||
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) });
|
||||
return _.mapObject(grouped, function(list) {
|
||||
return _.chain(list)
|
||||
|
|
@ -1038,7 +1093,10 @@ const ActivityList = Vue.component('activitylist', {
|
|||
key: self.slotKeyFromStart(item.starttime),
|
||||
startLabel: start.format('h:mma'),
|
||||
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; })
|
||||
|
|
@ -1086,7 +1144,19 @@ const ActivityList = Vue.component('activitylist', {
|
|||
</div>
|
||||
|
||||
<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 class="font-semibold text-gray-900">{{ sel.title }}</div>
|
||||
<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>
|
||||
</div>
|
||||
<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)">
|
||||
Pick a session
|
||||
</button>
|
||||
<emptyslotcard label="Signup" @select="selectSlot(slot)"></emptyslotcard>
|
||||
</div>
|
||||
|
||||
<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>
|
||||
<div class="text-sm text-gray-600">Pick for {{ slot.startLabel }} - {{ slot.endLabel }}</div>
|
||||
|
|
@ -1154,6 +1222,27 @@ const ActivityList = Vue.component('activitylist', {
|
|||
</p>
|
||||
</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>
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ require_once('semester.php');
|
|||
if (!isset($MY_TITLE)) $MY_TITLE = "Untitled Page";
|
||||
if (!isset($MY_CRUMB)) $MY_CRUMB = $MY_TITLE;
|
||||
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'];
|
||||
$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/vue27max.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" />
|
||||
</head>
|
||||
<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 />
|
||||
<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>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
Loading…
Reference in New Issue