diff --git a/dir_api.php b/dir_api.php
index 53075a2..3073afa 100644
--- a/dir_api.php
+++ b/dir_api.php
@@ -649,6 +649,9 @@ function get_ses_hosts() { global $c, $AY;
$ID = ok($_REQUEST['id']);
return multi_row_select("select s.id, s.title, s.starttime, u.name, u.email, u.id AS hostid from conf_sessions as s LEFT OUTER JOIN conf_hosts as h ON h.session=s.id LEFT OUTER JOIN conf_users AS u ON h.host=u.id WHERE s.id='{$ID}' ORDER BY u.name;",1);
}
+ if (isset($_REQUEST['all']) && $_REQUEST['all'] == '1') {
+ return multi_row_select("select s.id, s.title, s.starttime, u.name, u.email, u.id AS hostid from conf_sessions as s LEFT OUTER JOIN conf_hosts as h ON h.session=s.id LEFT OUTER JOIN conf_users AS u ON h.host=u.id ORDER BY u.name;",1);
+ }
$date_clause = api_date_clause('s.starttime');
return multi_row_select("select s.id, s.title, s.starttime, u.name, u.email, u.id AS hostid from conf_sessions as s LEFT OUTER JOIN conf_hosts as h ON h.session=s.id LEFT OUTER JOIN conf_users AS u ON h.host=u.id WHERE $date_clause ORDER BY u.name;",1); }
diff --git a/edit.php b/edit.php
index 47231de..5235bcd 100644
--- a/edit.php
+++ b/edit.php
@@ -3,5 +3,4 @@
$MY_TITLE = "Edit Sessions";
$MY_CRUMB = "Edit";
$CONTENT = '';
-$XTRAJS = 'js/editor.js';
include 'layout.php';
diff --git a/js/dir_app.js b/js/dir_app.js
index c675f25..0db360f 100644
--- a/js/dir_app.js
+++ b/js/dir_app.js
@@ -28,10 +28,18 @@ function init_file_dropzone(parameter_name) {
function parsesqltime(mysqldate) { // 2021-01-29 09:00:00
- if (! mysqldate) { return 0 }
- var field = mysqldate.match(/^(\d\d\d\d)\-(\d+)\-(\d+)[T|\s](\d+)\:(\d+)\:(\d+)$/)
- var mydate = new Date(field[1], field[2] - 1 , field[3], field[4], field[5], field[6])
- return mydate }
+ if (!mysqldate) { return 0 }
+ if (mysqldate instanceof Date) { return mysqldate }
+ var s = String(mysqldate).trim()
+ var field = s.match(/^(\d{4})-(\d{1,2})-(\d{1,2})[T\s](\d{1,2}):(\d{2})(?::(\d{2}))?$/)
+ if (field) {
+ var sec = field[6] ? field[6] : 0
+ return new Date(field[1], field[2] - 1, field[3], field[4], field[5], sec)
+ }
+ var parsed = new Date(s)
+ if (!isNaN(parsed.getTime())) { return parsed }
+ return 0
+}
function dj(mysqldate) { return dayjs(parsesqltime(mysqldate)) }
@@ -873,7 +881,8 @@ const ActivityList = Vue.component('activitylist', {
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, expandedDesc:{}, collapsedDays:{}, } },
+ cancelingIds:[], timeslotConfig:null, expandedDesc:{}, collapsedDays:{},
+ surveyAnswersBySession: {}, } },
mounted: function() {
this.fetch_myevents()
},
@@ -983,10 +992,24 @@ const ActivityList = Vue.component('activitylist', {
am_editing: function(id) { return 0 },
fetch_myevents: function() {
var self = this;
- basic_get('dir_api.php?a=get/hosts', function(r2) {
+ var hostUrl = 'dir_api.php?a=get/hosts';
+ if (self.show_all_sessions) { hostUrl += '&all=1'; }
+ basic_get(hostUrl, function(r2) {
self.hosts_by_sesid = _.groupBy(r2,function(x) { return x.id } )
} )
+ basic_get('dir_api.php?a=get/questions', function(r2) {
+ var answered = {};
+ _.each(r2, function(row) {
+ var sesId = self.normalizeId(row.ses_id || row.session || row.ses);
+ if (!sesId) { return; }
+ if (row.answer !== null && String(row.answer).trim() !== '') {
+ answered[sesId] = true;
+ }
+ });
+ self.surveyAnswersBySession = answered;
+ })
+
basic_get('api2.php?query=app',
function(r2) {
self.mysessions = r2.mysessions
@@ -999,7 +1022,9 @@ const ActivityList = Vue.component('activitylist', {
self.options = r2.options
self.conference = r2.conference
self.ay = r2.ay
- self.hosts_by_sesid = _.groupBy(r2.hostbysession,function(x) { return x.id } )
+ if (r2.hostbysession) {
+ self.hosts_by_sesid = _.groupBy(r2.hostbysession,function(x) { return x.id } )
+ }
self.survey_on = parseInt( _.findWhere(self.options, { label:'survey_on' }).value )
self.zoom_on = parseInt( _.findWhere(self.options, { label:'zoom_on' }).value )
@@ -1063,6 +1088,10 @@ const ActivityList = Vue.component('activitylist', {
},
slotPresets: function(slot) { return slot.presetSessions || []; },
slotHasPreset: function(slot) { return (slot.presetSessions && slot.presetSessions.length>0); },
+ nl2br: function(text) {
+ if (!text) { return ''; }
+ return String(text).replace(/\r?\n/g, '
');
+ },
canJoin: function(activity) {
if (this.zoom_on !== 1) { return false; }
return activity && (activity.mode === 'online' || activity.mode === 'hybrid');
@@ -1071,6 +1100,11 @@ const ActivityList = Vue.component('activitylist', {
if (this.survey_on !== 1) { return false; }
return !!activity;
},
+ hasSurveyAnswer: function(activity) {
+ if (!activity) { return false; }
+ var sid = this.normalizeId(activity.id);
+ return !!this.surveyAnswersBySession[sid];
+ },
hasJoinLink: function(activity) {
if (!activity || !activity.location) { return false; }
return String(activity.location).trim().length > 0;
@@ -1221,11 +1255,11 @@ const ActivityList = Vue.component('activitylist', {
{{ sel.title }}
- {{ sel.audience }} meeting
+ {{ sel.audience }} meeting
at {{ sel.location }}
· {{ sel.mode }}
-
{{ sel.notes }}
+
@@ -1247,7 +1281,10 @@ const ActivityList = Vue.component('activitylist', {
[missing zoom link]
TAKE Survey
+ :class="['inline-flex items-center px-2 py-1 text-xs font-semibold uppercase tracking-wide text-white rounded',
+ hasSurveyAnswer(sel) ? 'bg-red-600 hover:bg-red-700' : 'bg-emerald-600 hover:bg-emerald-700']">
+ {{ hasSurveyAnswer(sel) ? 'EDIT Survey' : 'TAKE Survey' }}
+
@@ -1336,6 +1373,10 @@ const ActivityList = Vue.component('activitylist', {
{{ $root.$dj(a.starttime).format('h:mma') }} - {{ addTime(a.starttime, a.length) }}
{{ a.title }}
+
+ {{ hoststr(a.id) }}
+ no host registered
+
{{ mode_string(a) }}
Location: {{ a.location_irl }}
@@ -1554,6 +1595,7 @@ const ActivityEditorList = Vue.component('activityeditorlist', {
reversed: false,
selectedIndex: 0,
editingId: null,
+ dayFilter: "all",
// filters
show_filters: "all",
filters: {
@@ -1577,9 +1619,27 @@ const ActivityEditorList = Vue.component('activityeditorlist', {
this.$root.fetch_menus();
this.fetchAll();
window.addEventListener("keydown", this.onKey);
+ this.changedHandler = (dat) => {
+ const column = dat[0];
+ const table = dat[1];
+ const value = dat[2];
+ const target = dat[3];
+ if (table !== "conf_sessions" || !target) return;
+ const a = this.byId[target];
+ if (!a) return;
+ a[column] = value;
+ if (column === "title" || column === "desc") {
+ a.searchable = ((a.title || "") + " " + (a.desc || "")).toLowerCase();
+ }
+ a.missing = this.computeMissing(a);
+ };
+ this.$root.events.bind("changed", this.changedHandler);
},
beforeDestroy() {
window.removeEventListener("keydown", this.onKey);
+ if (this.changedHandler && this.$root && this.$root.events && this.$root.events.unbind) {
+ this.$root.events.unbind("changed", this.changedHandler);
+ }
},
methods: {
async fetchAll() {
@@ -1678,6 +1738,13 @@ const ActivityEditorList = Vue.component('activityeditorlist', {
ff = ff.filter((x) => x.searchable.includes(q));
}
+ if (this.dayFilter && this.dayFilter !== "all") {
+ ff = ff.filter((item) => {
+ const t = dayjs(item.starttime);
+ return t.isValid() && t.format("YYYY-MM-DD") === this.dayFilter;
+ });
+ }
+
// AY filter example
const ay = this.$root.settings["default_ay"];
if (ay && this.$root.ay_menu && this.$root.ay_menu[ay]) {
@@ -1708,7 +1775,7 @@ const ActivityEditorList = Vue.component('activityeditorlist', {
} else if (e.key === "ArrowUp") {
this.selectedIndex = Math.max(0, this.selectedIndex - 1);
e.preventDefault();
- } else if (e.key === "Enter" || e.key.toLowerCase() === "e") {
+ } else if (e.key === "Enter") {
const a = this.activitiesFiltered[this.selectedIndex];
this.startEdit(a.id);
e.preventDefault();
@@ -1745,6 +1812,20 @@ const ActivityEditorList = Vue.component('activityeditorlist', {
activitiesFiltered() {
return this.filtered(this.activities);
},
+ availableDays() {
+ const seen = {};
+ this.activities.forEach((a) => {
+ const t = dayjs(a.starttime);
+ if (!t.isValid()) return;
+ const key = t.format("YYYY-MM-DD");
+ if (!seen[key]) {
+ seen[key] = t.format("MMM D YYYY");
+ }
+ });
+ return Object.keys(seen)
+ .sort()
+ .map((key) => ({ key, label: seen[key] }));
+ },
groupedByDay() {
return _.groupBy(this.activitiesFiltered, (x) => this.month_year(x.starttime));
},
@@ -1760,6 +1841,10 @@ const ActivityEditorList = Vue.component('activityeditorlist', {
/>
+
↑/↓ select • Enter/E edit • Esc close
@@ -2129,14 +2214,30 @@ const AskSurvey = Vue.component('asksurvey', {
function(r2) {
self.questions = r2 })
},
+ saveAll: function() {
+ var self = this
+ _.each(self.questions, function(q) {
+ self.$root.events.trigger('update_survey', [q.user, q.session, q.qid, q.answer])
+ })
+ if (typeof alert_message === 'function') {
+ alert_message('Saved.', 'lightgreen')
+ }
+ },
},
mounted: function() { this.start() },
- template: `
-
{{ questions[0].title }}
+ template: `
+
+ Session name:
+ [{{ questions[0].title }}]
+
+
+
+
`
});
diff --git a/style.css b/style.css
index 8ed2a36..e7b36df 100644
--- a/style.css
+++ b/style.css
@@ -64,3 +64,23 @@ ol {
.slotpanel-leave {
max-height: 600px;
}
+
+/* Survey page label tone */
+.survey-page .question,
+.survey-page label,
+.survey-page .form-check-label {
+ color: #4b5563;
+}
+
+/* Survey textarea should span full width */
+.survey-page textarea {
+ width: 100%;
+ border: 1px solid #d1d5db;
+ border-radius: 6px;
+ padding: 8px;
+ box-sizing: border-box;
+}
+
+.survey-page .session-survey {
+ margin-bottom: 12px;
+}