This commit is contained in:
parent
4c705703b5
commit
4ceb28e7ef
|
|
@ -3,5 +3,5 @@ $MY_TITLE = "Schedule & Sessions";
|
|||
$MY_CRUMB = "My Schedule";
|
||||
$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>';
|
||||
$CONTENT = '<div id="timeslot-config" class="hidden"></div><activitylist :itineraryview="1" :show_all_sessions="true"></activitylist>';
|
||||
include 'layout.php';
|
||||
|
|
|
|||
134
js/dir_app.js
134
js/dir_app.js
|
|
@ -872,11 +872,16 @@ const ActivityList = Vue.component('activitylist', {
|
|||
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:[], timeslotConfig:null, } },
|
||||
hosts_by_sesid: {}, options:{}, conference:{}, ay:{}, conf:-1, survey_on:0, zoom_on:1,
|
||||
cancelingIds:[], timeslotConfig:null, expandedDesc:{}, collapsedDays:{}, } },
|
||||
mounted: function() {
|
||||
this.fetch_myevents()
|
||||
},
|
||||
methods: {
|
||||
normalizeId: function(id) {
|
||||
var parsed = parseInt(id, 10);
|
||||
return Number.isNaN(parsed) ? id : parsed;
|
||||
},
|
||||
special_signup: function(activity_id) {
|
||||
if (activity_id==1462 || activity_id==1455) {
|
||||
return true;
|
||||
|
|
@ -896,12 +901,45 @@ const ActivityList = Vue.component('activitylist', {
|
|||
group.classList.toggle('hidden');
|
||||
btn.textContent = group.classList.contains('hidden') ? 'Expand Day' : 'Collapse Day';
|
||||
},
|
||||
toggleDaySlots: function(dayKey) {
|
||||
var isCollapsed = !!this.collapsedDays[dayKey];
|
||||
this.$set(this.collapsedDays, dayKey, !isCollapsed);
|
||||
},
|
||||
isDayCollapsed: function(dayKey) {
|
||||
return !!this.collapsedDays[dayKey];
|
||||
},
|
||||
toggleDescription: function(id) {
|
||||
const para = document.getElementById(id);
|
||||
const btn = document.getElementById(id + '-btn');
|
||||
para.classList.toggle('line-clamp-2');
|
||||
btn.textContent = para.classList.contains('line-clamp-2') ? 'Show More' : 'Show Less';
|
||||
},
|
||||
descPlain: function(desc) {
|
||||
if (!desc) { return ''; }
|
||||
return String(desc)
|
||||
.replace(/<[^>]*>/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
},
|
||||
descPreview: function(desc) {
|
||||
var text = this.descPlain(desc);
|
||||
var limit = 200;
|
||||
if (text.length <= limit) { return text; }
|
||||
var slice = text.slice(0, limit + 1);
|
||||
var cutoff = slice.lastIndexOf(' ');
|
||||
if (cutoff < 0) { cutoff = limit; }
|
||||
return text.slice(0, cutoff).trim() + '...';
|
||||
},
|
||||
descIsTruncated: function(desc) {
|
||||
return this.descPlain(desc).length > 200;
|
||||
},
|
||||
isDescExpanded: function(id) {
|
||||
return this.expandedDesc && this.expandedDesc[id];
|
||||
},
|
||||
toggleDesc: function(id) {
|
||||
if (!this.expandedDesc) { this.expandedDesc = {}; }
|
||||
this.$set(this.expandedDesc, id, !this.expandedDesc[id]);
|
||||
},
|
||||
slotKeyFromStart: function(starttime) {
|
||||
if (!starttime) { return null; }
|
||||
return this.$root.$dj(starttime).format('YYYY-MM-DDTHH:mm');
|
||||
|
|
@ -952,7 +990,9 @@ const ActivityList = Vue.component('activitylist', {
|
|||
basic_get('api2.php?query=app',
|
||||
function(r2) {
|
||||
self.mysessions = r2.mysessions
|
||||
self.my_ses_ids = _.pluck(r2.mysessions, 'id')
|
||||
self.my_ses_ids = _.map(_.pluck(r2.mysessions, 'id'), function(val) {
|
||||
return self.normalizeId(val);
|
||||
})
|
||||
self.activities = r2.sessions
|
||||
if (r2.host != null) { self.my_host_ids = r2.host }
|
||||
else { self.my_host_ids = [] }
|
||||
|
|
@ -971,20 +1011,35 @@ const ActivityList = Vue.component('activitylist', {
|
|||
},
|
||||
joinme: function(id) {
|
||||
var self = this
|
||||
id = self.normalizeId(id)
|
||||
basic_get('dir_api.php?a=signup/' + id,
|
||||
function(r2) {
|
||||
self.mysessions.push(_.findWhere(self.activities, {'id':id}))
|
||||
self.my_ses_ids.push(id)
|
||||
if (!self.my_ses_ids.includes(id)) { self.my_ses_ids.push(id) }
|
||||
var existing = _.find(self.mysessions, function(s) {
|
||||
return self.normalizeId(s.id) === id;
|
||||
});
|
||||
if (!existing) {
|
||||
var activity = _.find(self.activities, function(a) {
|
||||
return self.normalizeId(a.id) === id;
|
||||
});
|
||||
if (activity) { self.mysessions.push(activity) }
|
||||
}
|
||||
self.$forceUpdate()
|
||||
alert_message("Added activity") })
|
||||
},
|
||||
dumpme: function(id) {
|
||||
var self = this
|
||||
id = self.normalizeId(id)
|
||||
if (!self.cancelingIds.includes(id)) { self.cancelingIds.push(id) }
|
||||
basic_get('dir_api.php?a=signdown/' + id,
|
||||
function(r2) {
|
||||
setTimeout(function() {
|
||||
self.mysessions = _.without( self.mysessions, _.findWhere(self.activities, {'id':id}))
|
||||
self.my_ses_ids = _.without( self.my_ses_ids, id)
|
||||
self.mysessions = _.filter(self.mysessions, function(s) {
|
||||
return self.normalizeId(s.id) !== id;
|
||||
});
|
||||
self.my_ses_ids = _.filter(self.my_ses_ids, function(sid) {
|
||||
return self.normalizeId(sid) !== id;
|
||||
});
|
||||
self.cancelingIds = _.without(self.cancelingIds, id)
|
||||
self.$forceUpdate()
|
||||
alert_message("Removed activity")
|
||||
|
|
@ -1008,6 +1063,18 @@ const ActivityList = Vue.component('activitylist', {
|
|||
},
|
||||
slotPresets: function(slot) { return slot.presetSessions || []; },
|
||||
slotHasPreset: function(slot) { return (slot.presetSessions && slot.presetSessions.length>0); },
|
||||
canJoin: function(activity) {
|
||||
if (this.zoom_on !== 1) { return false; }
|
||||
return activity && (activity.mode === 'online' || activity.mode === 'hybrid');
|
||||
},
|
||||
canSurvey: function(activity) {
|
||||
if (this.survey_on !== 1) { return false; }
|
||||
return !!activity;
|
||||
},
|
||||
hasJoinLink: function(activity) {
|
||||
if (!activity || !activity.location) { return false; }
|
||||
return String(activity.location).trim().length > 0;
|
||||
},
|
||||
loadTimeslots: function() {
|
||||
var self=this;
|
||||
if (window.TIMESLOT_CONFIG) {
|
||||
|
|
@ -1134,8 +1201,14 @@ const ActivityList = Vue.component('activitylist', {
|
|||
</div>
|
||||
|
||||
<div v-for="(slots, mmyy) in timeSlotsByDay" :key="mmyy" class="mb-6">
|
||||
<h4 class="text-lg font-semibold text-gray-700 mb-2">{{ mmyy }} {{ get_day_title(mmyy) }}</h4>
|
||||
<div class="space-y-3">
|
||||
<div class="flex items-center gap-2 mb-2">
|
||||
<button class="text-lg font-semibold text-gray-500 hover:text-gray-700 w-6 text-center"
|
||||
@click.prevent="toggleDaySlots(mmyy)">
|
||||
{{ isDayCollapsed(mmyy) ? '+' : '-' }}
|
||||
</button>
|
||||
<h4 class="text-lg font-semibold text-gray-700">{{ mmyy }} {{ get_day_title(mmyy) }}</h4>
|
||||
</div>
|
||||
<div v-if="!isDayCollapsed(mmyy)" class="space-y-3">
|
||||
<div v-for="slot in slots" :key="slot.key" :class="['border rounded-lg p-4 bg-white shadow-sm flex items-start gap-4', selectedSlotKey === slot.key ? 'ring-2 ring-blue-400' : '']">
|
||||
<div class="w-32 text-right">
|
||||
<div class="text-2xl font-extrabold text-blue-800 leading-tight">{{ slot.startLabel }}</div>
|
||||
|
|
@ -1160,13 +1233,28 @@ const ActivityList = Vue.component('activitylist', {
|
|||
<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">
|
||||
{{ $root.$dj(sel.starttime).format('h:mma') }} · {{ mode_string(sel) }}
|
||||
<div class="flex flex-wrap items-center gap-2">
|
||||
<span>{{ $root.$dj(sel.starttime).format('h:mma') }} - {{ addTime(sel.starttime, sel.length) }} · {{ mode_string(sel) }}</span>
|
||||
<span v-if="sel.mode === 'hybrid'">·
|
||||
<span v-if="sel.location_irl">{{ sel.location_irl }}</span>
|
||||
<span v-else>(location TBD)</span>
|
||||
</span>
|
||||
</div>
|
||||
<div class="mt-1 flex flex-wrap items-center gap-2">
|
||||
<a v-if="canJoin(sel) && hasJoinLink(sel)"
|
||||
:href="sel.location"
|
||||
class="inline-flex items-center px-2 py-1 text-xs font-semibold uppercase tracking-wide text-white bg-blue-600 rounded hover:bg-blue-700">JOIN Online</a>
|
||||
<span v-else-if="canJoin(sel)" class="text-xs text-red-600">[missing zoom link]</span>
|
||||
<a v-if="canSurvey(sel)"
|
||||
:href="'survey.php?s=' + sel.id"
|
||||
class="inline-flex items-center px-2 py-1 text-xs font-semibold uppercase tracking-wide text-white bg-emerald-600 rounded hover:bg-emerald-700">TAKE Survey</a>
|
||||
<button v-if="!my_host_ids.includes(sel.id)"
|
||||
class="mt-2 text-sm text-red-600 hover:underline"
|
||||
class="inline-flex items-center px-2 py-1 text-xs font-semibold uppercase tracking-wide text-white bg-red-600 rounded hover:bg-red-700"
|
||||
@click.prevent="dumpme(sel.id)">Cancel</button>
|
||||
</div>
|
||||
<button class="mt-1 text-sm text-blue-600 hover:underline" @click="selectSlot(slot)">Change selection</button>
|
||||
</div>
|
||||
</div>
|
||||
<button class="mt-1 text-sm text-blue-600 hover:underline" @click="selectSlot(slot)">Change session</button>
|
||||
</div>
|
||||
<div v-else class="mt-1">
|
||||
<emptyslotcard label="Signup" @select="selectSlot(slot)"></emptyslotcard>
|
||||
|
|
@ -1187,15 +1275,28 @@ const ActivityList = Vue.component('activitylist', {
|
|||
<div class="flex items-start justify-between gap-3">
|
||||
<div class="flex-1">
|
||||
<div class="font-semibold text-gray-900">{{ a.title }}</div>
|
||||
<div class="text-xs uppercase text-gray-500">{{ mode_string(a) }}</div>
|
||||
<!--<div class="text-xs uppercase text-gray-500">{{ mode_string(a) }}</div>-->
|
||||
<div class="text-sm text-gray-600 mt-1">
|
||||
<span v-if="a.mode === 'hybrid'">In person at {{ a.location_irl }} or online</span>
|
||||
<span v-if="a.mode === 'inperson'">In person at {{ a.location_irl }}</span>
|
||||
<span v-if="a.mode === 'hybrid'">In person<span v-if="a.location_irl"> at {{ a.location_irl }}</span> or online</span>
|
||||
<span v-if="a.mode === 'inperson'">In person<span v-if="a.location_irl"> at {{ a.location_irl }}</span><span v-else> location TBD</span></span>
|
||||
<span v-if="a.mode === 'online'">Online</span>
|
||||
</div>
|
||||
<div v-if="a.desc" class="text-sm text-gray-700 mt-2">
|
||||
<span v-if="!isDescExpanded(a.id)">
|
||||
{{ descPreview(a.desc) }}
|
||||
<button v-if="descIsTruncated(a.desc)"
|
||||
class="ml-1 text-blue-600 hover:underline"
|
||||
@click.prevent="toggleDesc(a.id)">[+ read more]</button>
|
||||
</span>
|
||||
<span v-else>
|
||||
<span v-html="a.desc"></span>
|
||||
<button class="ml-1 text-blue-600 hover:underline"
|
||||
@click.prevent="toggleDesc(a.id)">[- read less]</button>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="flex items-center gap-2">
|
||||
<button v-if="!my_ses_ids.includes(a.id)"
|
||||
<button v-if="!my_ses_ids.includes(normalizeId(a.id))"
|
||||
class="px-3 py-1 text-sm font-medium text-white bg-blue-600 rounded hover:bg-blue-700"
|
||||
@click.prevent="chooseSession(a)">
|
||||
Sign Up
|
||||
|
|
@ -1216,10 +1317,13 @@ const ActivityList = Vue.component('activitylist', {
|
|||
|
||||
<!-- No sessions signed up -->
|
||||
<div v-if="itineraryview && mysessions_filtered.length === 0" class="mb-6">
|
||||
<p v-if="active < 1" class="font-semibold text-gray-800">Loading your itinerary...</p>
|
||||
<template v-else>
|
||||
<p class="font-semibold text-gray-800">It looks like you haven't signed up for any sessions yet!</p>
|
||||
<p class="mt-2 text-sm text-blue-600">
|
||||
Go to the <a class="underline hover:text-blue-800" href="allsessions.php">Sessions List</a> to sign up.
|
||||
</p>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
<!-- Simple full list for All Sessions -->
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ $MOD_DATE = file_exists(__FILE__) ? date("F d Y H:i.", filemtime(__FILE__)) : "(
|
|||
<title><?= htmlspecialchars($MY_TITLE) ?> | Gavilan Intranet</title>
|
||||
<script src="js/tailwind.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" />
|
||||
</head>
|
||||
|
|
@ -74,7 +74,7 @@ $MOD_DATE = file_exists(__FILE__) ? date("F d Y H:i.", filemtime(__FILE__)) : "(
|
|||
<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?v=<?= $DIR_APP_VER ?>"></script>
|
||||
<script src="<?= $XTRAJS ?>"></script>
|
||||
</body>
|
||||
|
|
|
|||
Loading…
Reference in New Issue