flexday/js/dir_app.js

2746 lines
112 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

var PROD = 0
if (PROD && location.protocol !== 'https:') {
location.replace(`https:${location.href.substring(location.protocol.length)}`);
}
// _ _
// | | | |
// | |__ ___| |_ __ ___ _ __ ___
// | '_ \ / _ \ | '_ \ / _ \ '__/ __|
// | | | | __/ | |_) | __/ | \__ \
// |_| |_|\___|_| .__/ \___|_| |___/
// | |
// |_|
function init_file_dropzone(parameter_name) {
Dropzone.options.myGreatDropzone = { // camelized version of the `id`
paramName: parameter_name, // The name that will be used to transfer the file
maxFilesize: 6 }; // MB
}
// init_file_dropzone("staffpicupload")
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 }
function dj(mysqldate) { return dayjs(parsesqltime(mysqldate)) }
Object.defineProperty(Vue.prototype, '$dj', { value: dj });
Object.defineProperty(Vue.prototype, '$parsesqltime', { value: parsesqltime });
// ________ __
// | ____\ \ / /
// | |__ \ V /
// | __| > <
// | | / . \
// |_| /_/ \_\
//
//
//
// VISUAL EFFECTS
//
function fade_message(theselector='#alert') {
var a_dom = document.querySelector(theselector);
TinyAnimate.animateCSS(a_dom, 'opacity', '', 1.0, 0.0, 750, 'easeInOutQuart', function() { }); }
function alert_message(msg,color='yellow') {
var a = $('#alert')
a.text(msg)
a.removeClass('hidden');
a.css('background-color',color)
var a_dom = document.querySelector('#alert');
TinyAnimate.animateCSS(a_dom, 'opacity', '', 0.0, 1.0, 500, 'easeInOutQuart', function() {
setTimeout( fade_message, 2500 ) }); }
function fadein_message(theclass='success') {
//var a = $('#'+theclass)
//a.css('visibility','visible')
var a_dom = document.querySelector('.'+theclass);
TinyAnimate.animateCSS(a_dom, 'opacity', '', 0.0, 1.0, 500, 'easeInOutQuart', function() {
setTimeout( function() { fade_message('.'+theclass) }, 2500 ) }); }
// _
// | |
// ___ ___ _ __ ___ _ __ ___ _ __ ___ _ __ | |_ ___
// / __/ _ \| '_ ` _ \| '_ \ / _ \| '_ \ / _ \ '_ \| __/ __|
// | (_| (_) | | | | | | |_) | (_) | | | | __/ | | | |_\__ \
// \___\___/|_| |_| |_| .__/ \___/|_| |_|\___|_| |_|\__|___/
// | |
// |_|
//
// FORM COMPONENTS
//
// TODO these should know if they're modifying the current user or someone else.
// A single text style question
const TQuestion = Vue.component('field', {
props: [ 'table', 'qid', 'question', 'answer', 'placeholder', 'targetid', ],
data: function () {
return { "a":this.answer } },
watch: {
"answer": function(val,Oldval) { this.a = val },
"a": function (val, oldVal) {
this.$root.events.trigger('changed',[this.qid,this.table, val, this.targetid]) }, },
template: `<div class="mb-4">
<label v-if="question" :for="qid" class="block text-sm font-medium text-gray-700 mb-1">
{{ question }}
</label>
<input
:id="qid"
type="text"
v-model="a"
:placeholder="placeholder"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50 p-2 text-sm"
/>
</div>`,
})
// A single INLINE text style question
const TIQuestion = Vue.component('ifield', {
props: [ 'table', 'qid', 'question', 'answer', 'placeholder', 'myclass', 'targetid', ],
data: function () {
return { "a":this.answer } },
watch: {
"answer": function(val,Oldval) { this.a = val },
"a": function (val, oldVal) {
this.$root.events.trigger('changed',[this.qid,this.table, val, this.targetid]) }, },
template: `<input :id="this.qid" type="text" v-model="a" :placeholder="placeholder" :class="this.myclass" />`,
})
// A single checkbox
const Checkbox = Vue.component('checkbox', {
props: [ 'table', 'qid', 'question', 'answer', 'targetid', ],
data: function () {
return { "a":this.answer } },
watch: {
"answer": function(val,Oldval) { this.a = val },
"a": function (val, oldVal) {
var newVal = 0
if (val==true) { newVal = 1 }
this.$root.events.trigger('changed',[this.qid,this.table, newVal, this.targetid]) }, },
template: `<div class="pure-controls">
<label :for="this.qid" class="question pure-checkbox">
<input :id="this.qid" type="checkbox" class="form-control" v-model="a" />
{{ question }}</label></div>`
})
// A single long format text question
const TAQuestion = Vue.component('tfield', {
props: [ 'table', 'qid', 'question', 'answer', 'targetid', 'myclass' ],
data: function () {
return { "a": this.answer } },
watch: {
"a": function (val, oldVal) {
this.$root.events.trigger('changed',[this.qid,this.table, val, this.targetid]) },
"answer": function (val, oldVal) { this.a = val },
},
template: `<div class="mb-4">
<label :for="qid" class="block text-sm font-medium text-gray-700 mb-1">
{{ question }}
</label>
<textarea
:id="qid"
v-model="a"
:class="myclass"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50 p-2 text-sm"
></textarea>
</div>` })
// long format text WYSIWYG HTML
const HTAQuestion = Vue.component('htfield', {
props: [ 'table', 'qid', 'question', 'answer', 'targetid', 'myclass' ],
data: function () { return { "a": this.answer, "p":0, } }, // the pell editor object
mounted: function() {
var self = this
var element = document.getElementById(self.qid)
this.p = pell.init( { element: element, onChange: function(h) {
self.$root.events.trigger('changed',[self.qid, self.table, h, self.targetid]) } } )
self.p.content.innerHTML = self.answer },
watch: {
"answer": function (val, oldVal) { this.p.content.innerHTML = val }, },
template: `<div><label :for="this.qid" class="question">{{ question }} </label>
<div :id="this.qid" class="pell"></div></div>` })
// long format text WYSIWYG HTML FORM ALIGNED STYLE
const HTAQuestionFA = Vue.component('htfield_fa', {
props: [ 'table', 'qid', 'question', 'answer', 'targetid', 'myclass' ],
data: function () { return { "a": this.answer, "p":0, } }, // the pell editor object
mounted: function() {
var self = this
var element = document.getElementById(self.qid)
this.p = pell.init( { element: element, onChange: function(h) {
self.$root.events.trigger('changed',[self.qid, self.table, h, self.targetid]) } } )
self.p.content.innerHTML = self.answer },
watch: {
"answer": function (val, oldVal) { this.p.content.innerHTML = val }, },
template: `<div class=""><label :for="this.qid" class="question">{{ question }} </label>
<div :id="this.qid" class="form-control pell"></div></div>` })
// A single numeric question
const NQuestioon = Vue.component('n-question', {
props: [ 'qq' ],
data: function () { return { "answer": this.qq.answer } },
watch: { "answer": function (val, oldVal) {
this.$root.events.trigger('update_survey', [this.qq.user, this.qq.session, this.qq.qid, val]) }, },
template: `<div class=""><span class="question">{{ qq.question }}</span><br />
<div class="makeup_indent "><input class="form-check-input" type="radio" id="one" value="1" v-model="answer">
<label class="form-check-label" for="one">One</label> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
<input class="form-check-input" type="radio" id="two" value="2" v-model="answer">
<label class="form-check-label" for="two">Two</label> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
<input class="form-check-input" type="radio" id="three" value="3" v-model="answer">
<label class="form-check-label" for="three">Three</label> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
<input class="form-check-input" type="radio" id="four" value="4" v-model="answer">
<label class="form-check-label" for="four">Four</label> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
<input class="form-check-input" type="radio" id="five" value="5" v-model="answer">
<label class="form-check-label" for="five">Five</label> &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;
</div></div>`
})
// A single survey text question
const STQuestion = Vue.component('st-question', {
props: [ 'qq' ],
data: function () { return { "answer": this.qq.answer } },
watch: { "answer": function (val, oldVal) {
this.$emit('dirty')
this.$root.events.trigger('update_survey', [this.qq.user, this.qq.session, this.qq.qid, val]); }, },
template: `<div class="pure-form-aligned"><label class="question">{{ qq.question }}</label>
<textarea :id="qq.id" class="form-control pure-form-aligned" v-model="answer"></textarea></div>`,
})
// The "I Certify" question
const ICertify = Vue.component('icertify', {
props: [ 'c' ],
data: function () { return { "checked": this.c } },
watch: { checked() {
this.$emit('dirty')
this.$parent.docert(this.checked); },
},
template: `<div class="makeup_indent"><input class="form-check-input" type="checkbox" id="checkbox" v-model="checked">
<label class="icert form-check-label" for="checkbox">I certify that I have attended this event.</label><br />
</div>` });
// Select menu
/*
table: name of database table
qid: what column of the table to edit
question: label displayed to user
answer: data item used for v-model
menu: key of root vue object with menu items
labelfield: key of 'menu' object that should be displayed as choices
targetid: when editing existing row: the id of the row
*/
const SelMenu = Vue.component('selectmenu', {
props: [ 'table', 'qid', 'question', 'answer', 'menu', 'labelfield', 'targetid', ],
data: function () { return { "a": this.answer } },
watch: {
"a": function (val, oldVal) {
this.$root.events.trigger('changed',[this.qid,this.table, val, this.targetid]) },
"answer": function (val, oldVal) { this.a = val }, },
template: `<div class="mb-4">
<label :for="qid" class="block text-sm font-medium text-gray-700 mb-1">
{{ question }}
</label>
<select
:id="qid"
v-model="a"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50 p-2 text-sm bg-white"
>
<option v-for="o in $root[menu]" :value="o.id">
{{ o[labelfield] }}
</option>
</select>
</div>` })
// Select menu FORM ALIGNED STYLE
const SelMenuFA = Vue.component('selectmenu_fa', {
props: [ 'table', 'qid', 'question', 'answer', 'menu', 'labelfield', 'targetid', ],
data: function () { return { "a": this.answer } },
watch: {
"a": function (val, oldVal) {
this.$root.events.trigger('changed',[this.qid,this.table, val, this.targetid]) },
"answer": function (val, oldVal) { this.a = val }, },
template: `<div class="mb-4">
<label :for="qid" class="block text-sm font-medium text-gray-700 mb-1">
{{ question }}
</label>
<select
:id="qid"
v-model="a"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50 p-2 text-sm bg-white"
>
<option v-for="o in $root[menu]" :value="o.id">
{{ o[labelfield] }}
</option>
</select>
</div>` })
// A date time picker
const DTPicker = Vue.component('dtpicker', {
props: [ 'table', 'qid', 'question', 'answer', 'targetid', ],
methods: { },
mounted: function() { },
data: function () { return { "a":this.answer } },
watch: {
"answer": function(val,Oldval) { this.a = val },
"a": function (val, oldVal) {
this.$root.events.trigger('changed',[this.qid,this.table, val, this.targetid]) }, },
template: `<div class="mb-4">
<label v-if="question" :for="qid" class="block text-sm font-medium text-gray-700 mb-1">
{{ question }}
</label>
<input
type="datetime-local"
:id="qid"
v-model="a"
step="300"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50 p-2 text-sm"
/>
</div>`,
})
// Accordion style box
const ExpandyBox = Vue.component('expandybox', {
props: [ 'header', 'body', ],
methods: {
toggle: function() {
if (this.state=='close') { this.state='open'; this.symbol='-' }
else { this.state='close'; this.symbol='+' }
}
},
mounted: function() { },
data: function () { return { "state":'close', "symbol":"+", } },
watch: {
"answer": function(val,Oldval) { this.a = val },
"a": function (val, oldVal) {
this.$root.events.trigger('changed',[this.qid,this.table, val, this.targetid]) }, },
template: `<div><div class="pure-g">
<div class="pure-u-1-1">
<h4 class="exp_header">
<a href="#" v-on:click.prevent="toggle()">
<span class="decorate">{{symbol}}</span>
{{header}}</a>
</h4>
</div>
</div>
<div v-if="state=='open'" class="pure-g">
<div class="pure-u-1-1" v-html="body"><slot></slot></div>
</div>
</div>`,
})
// mode pill
Vue.component('ModePill', {
props: ['mode'],
computed: {
label() {
switch (this.mode) {
case 'inperson': return 'In Person';
case 'online': return 'Online';
case 'hybrid': return 'Hybrid';
default: return this.mode;
}
},
bgClass() {
switch (this.mode) {
case 'inperson': return 'bg-green-500';
case 'online': return 'bg-blue-500';
case 'hybrid': return 'bg-yellow-500 text-black';
default: return 'bg-gray-400';
}
}
},
template: `
<span class="text-xs font-semibold text-white px-2 py-1 rounded"
:class="bgClass">
{{ label }}
</span>
`
});
// _ __ __ _____ _____ _____
// | | / _|/ _| | __ \_ _| __ \
// ___| |_ __ _| |_| |_ | | | || | | |__) |
// / __| __/ _` | _| _| | | | || | | _ /
// \__ \ || (_| | | | | | |__| || |_| | \ \
// |___/\__\__,_|_| |_| |_____/_____|_| \_\
//
//
//
// ONE LINE OF THE STAFF DIR EDITOR listing
//
const StaffLine = Vue.component('staff_line', {
props: [ 's', 'i', 'dup_class', ],
//data: function() { return { } },
methods: {
nope: function() { return 1; },
odd: function(i) { if (i % 2 ==0) { return "even" } return "odd" },
bio: function() { return "bio.php?p=" + this.s.id },
gtitle: function() { if (this.s.gtitle) {return _.findWhere(this.$root.titles_menu, {id:this.s.gtitle}).name}; return "" },
gdept1: function() { if (this.s.dept1) {return _.findWhere(this.$root.depts_menu, {id:this.s.dept1}).name}; return "" },
gdept2: function() { if (this.s.dept2) {return _.findWhere(this.$root.depts_menu, {id:this.s.dept2}).name}; return "-" },
grole: function() { if (this.s.role) {return _.findWhere(this.$root.roles_menu, {id:this.s.role}).descr}; return "" },
swapedit: function() { this.$emit('swapout', this.s.id) }
},
template: `<div>
<div :class="odd(i) + ' pure-g'">
<div class="pure-u-1-4"><pre>
status: {{ s.status }}
permissions: {{ grole() }}
pers id: {{ s.id }}
ext id: {{ s.ext_id }}
bio page: {{ s.web_on }}
Photo - use: {{s.use_dir_photo}} release: {{s.general_photo_release}}
path: {{s.dir_photo_path}}
# Sections: {{s.num_taught}}
{{ s.sections }} </pre>
</div>
<div class="pure-u-1-4"><pre>
staff: {{ s.staff_type }}
conf_id: {{ s.conf_id }}
G00{{s.conf_goo}}
espanol: {{s.espanol}}
</pre>
</div>
<div class="pure-u-1-4"><pre>
{{s.first_name}} {{s.last_name}}
{{gtitle()}}
{{gdept1()}}
{{gdept2()}}
(old dept: {{s.department}} ) </pre>
</div>
<div class="pure-u-1-4"><pre>
email: {{s.email}}
room: {{s.room}}
phone: {{s.phone_number}}
Zoom: {{s.zoom}}
Preferred contact: {{s.preferred_contact}}
</pre><div class="xxpure-u-1-24 clicky" v-on:click="swapedit"><span class="pure-button button-inlist">edit</span></div>
</div>
</div>
</div>`
});
//
//
// STAFF DIR LISTING
//
// ONE LINE - BUT EDITING!
//
const StaffLineEdit = Vue.component('staff_line_edit', {
props: [ 's', 'i' ],
//data: function() { return { } },
methods: {
nope: function() { return 1; },
odd: function(i) { if (i % 2 ==0) { return "even" } return "odd" },
bio: function() { return "bio.php?p=" + this.s.id },
done: function() { this.$emit('done_edit') },
gtitle: function() { if (this.s.gtitle) {return _.findWhere(this.$root.titles_menu, {id:this.s.gtitle}).name}; return "" },
gdept1: function() { if (this.s.dept1) {return _.findWhere(this.$root.depts_menu, {id:this.s.dept1}).name}; return "" },
gdept2: function() { if (this.s.dept2) {return _.findWhere(this.$root.depts_menu, {id:this.s.dept2}).name}; return "-" },
grole: function() { if (this.s.role) {return _.findWhere(this.$root.roles_menu, {id:this.s.role}).descr}; return "" },
}, //v-lazy-container="{ selector: 'img' }"
template: `<form class="pure-form">
<div :class="odd(i) + ' list-editor pure-g'">
<div class="pure-u-1-4">
<checkbox table="personnel" qid="status" question="Published" :answer="s.status" :targetid="s.id"></checkbox>
permissions: <selectmenu table="personnel_ext" qid="role" :answer="s.role" menu="roles_menu" :targetid="s.ext_id" labelfield="descr"></selectmenu><br />
pers id: {{ s.id }}<br />
ext id: {{ s.ext_id }}<br />
<checkbox table="personnel" qid="web_on" question="Bio Page On" :answer="s.web_on" :targetid="s.id"></checkbox>
<checkbox table="personnel_ext" qid="use_dir_photo" question="Use Photo" :answer="s.use_dir_photo" :targetid="s.ext_id"></checkbox>
<checkbox table="personnel_ext" qid="general_photo_release" question="Release Photo" :answer="s.general_photo_release" :targetid="s.ext_id"></checkbox>
Dir photo <ifield myclass="double" table="personnel_ext" qid="dir_photo_path" :answer="s.dir_photo_path" :targetid="s.ext_id" placeholder="Dir Photo Path"></ifield><br />
# Sections: {{s.num_taught}}<br />
{{ s.sections }}
</div>
<div class="pure-u-1-4">
Staff Type <ifield myclass="double" table="personnel" qid="staff_type" :answer="s.staff_type" :targetid="s.id" placeholder="Staff Type"></ifield><br />
conf_id: {{ s.conf_id }}<br />
<!-- GOO <ifield myclass="double" table="conf_users" qid="goo" :answer="s.conf_goo" :targetid="s.conf_id" placeholder="GOO"></ifield><br />-->
GOO {{ s.conf_goo }} <br />
<checkbox table="personnel_ext" qid="espanol" question="Speak Spanish" :answer="s.espanol" :targetid="s.ext_id"></checkbox>
</div>
<div class="pure-u-1-4">
<ifield myclass="double" table="personnel" qid="first_name" :answer="s.first_name" :targetid="s.id" placeholder="First Name"></ifield><br />
<ifield myclass="double" table="personnel" qid="last_name" :answer="s.last_name" :targetid="s.id" placeholder="Last Name"></ifield><br />
<selectmenu table="personnel_ext" qid="gtitle" :answer="s.gtitle" menu="titles_menu" :targetid="s.ext_id" labelfield="name"></selectmenu><br />
<selectmenu table="personnel_ext" qid="dept1" :answer="s.dept1" menu="depts_menu" :targetid="s.ext_id" labelfield="name"></selectmenu> <br />
<selectmenu table="personnel_ext" qid="dept2" :answer="s.dept2" menu="depts_menu" :targetid="s.ext_id" labelfield="name"></selectmenu><br />
(old dept: {{s.department}} )
</div>
<div class="pure-u-1-4">
<ifield table="personnel" qid="email" :answer="s.email" :targetid="s.id" placeholder="Email"></ifield><br />
<ifield table="personnel" qid="room" :answer="s.room" :targetid="s.id" placeholder="Room"></ifield><br />
<ifield table="personnel" qid="phone_number" :answer="s.phone_number" :targetid="s.id" placeholder="Phone Number"></ifield><br />
<ifield table="personnel_ext" qid="zoom" :answer="s.zoom" :targetid="s.ext_id" placeholder="Zoom Room"></ifield><br />
<ifield table="personnel_ext" qid="preferred_contact" :answer="s.preferred_contact" :targetid="s.ext_id" placeholder="Preferred contact"></ifield>
<div class="xxpure-u-1-24 clicky" v-on:click="done"><span class="pure-button button-inlist-action">done</span>
</div>
</div>
</div>
</form>
`
});
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
//
// STAFF DIR LISTING MAIN CONTAINER
//
const DirList = Vue.component('dirlist', {
data: function () {
return { "personnel":[], sortby:'last_name', search:'', reversed: false, id_list:[], id_dups:[],
components: {n:StaffLine, e: StaffLineEdit }, editing: -1, } },
mounted: function() {
var self = this;
this.$root.fetch_menus();
fetch('dir_api.php?a=list/staffsemester', { method: 'GET' }).then(function (response) {
if (response.ok) {
response.json().then( function(r2) {
self.personnel = r2;
_.each( self.personnel, function(x) {
if (x.sections==null) { x.num_taught=0 }
if (x.dir_photo_path==null) { x.dir_photo_path='images_sm/nobody.jpg' }
if (x.use_dir_photo == "0") { x.use_dir_photo = false } else { x.use_dir_photo = true }
if (x.espanol == "0" || !x.espanol) { x.espanol = false } else { x.espanol = true }
if (x.general_photo_release == "0") { x.general_photo_release = false
} else { x.general_photo_release = true }
if (self.id_list.includes(x.id)) { self.id_dups.push(x.id) }
else { self.id_list.push(x.id) }
x.searchable = ''
if (x.first_name) { x.searchable += ' ' + x.first_name.toLowerCase() }
if (x.last_name) { x.searchable += ' ' + x.last_name.toLowerCase() }
if (x.dept1name) { x.searchable += ' ' + x.dept1name.toLowerCase() }
if (x.titlename) { x.searchable += ' ' + x.titlename.toLowerCase() }
if (x.status == "1" || x.status == null) { x.status = "1"
} else { x.status = false; x.searchable += ' inactive' }
} ) } ) } else { return Promise.reject(response) }
}).then(function (data) { }).catch(function (err) { console.warn('Something went wrong.', err); });
},
methods: {
setsort: function(ss) {
if (this.sortby == ss) { this.reversed = ! this.reversed }
else {
this.reversed = false
this.sortby = ss
}
},
swapme: function(x) {
this.editing = x
},
done_edit: function(id) {
this.editing = -1
},
am_editing: function(id) {
if (id == this.editing) { return StaffLineEdit }
return StaffLine
},
is_dup_id_class: function(id) { return this.id_dups.includes(id) ? " dup_line" : "" },
},
computed: {
filtered: function() {
var ff = this.personnel
var self = this
if (this.search) {
var ss = self.search.toLowerCase()
ff = ff.filter(function(x) { return x.searchable.includes(ss) }) }
ff = _.sortBy(ff, function(x) {
if (x[self.sortby]) {
var s = x[self.sortby];
return s.trim().toLowerCase() }
return 'zzzzzzzzzz' })
if (this.reversed) {
ff.reverse()
}
return ff
}
},
watch: {
},
template: `<div class="">
<div class="pure-g pure-form">
<div class="pure-u-1-24"></div>
<div class="pure-u-3-4"><b>Filter: </b><input type="text" v-model="search" name="dirfilter" class="double" /></div>
</div>
<br />
<div class="pure-g pure-form">
<div class="pure-u-1-24">&nbsp;</div>
</div>
<div class="pure-g pure-form">
<div v-on:click="setsort('num_taught')" class="pure-u-1-24 clicky"><b>C</b></div>
<div class="pure-u-1-4 clicky">
<b v-on:click="setsort('last_name')">Name</b><br />
<b v-on:click="setsort('gtitle')">Title</b>
</div>
<div class="pure-u-1-6 clicky">
<b v-on:click="setsort('department')">Dept</b><br />
<b v-on:click="setsort('dept1')">Dept1</b>
</div>
<div v-on:click="setsort('email')" class="pure-u-1-6 clicky"><b>Email</b></div>
<div class="pure-u-1-6"><b>Phone</b></div>
<div v-on:click="setsort('room')" class="pure-u-1-6 clicky"><b>Room</b></div>
</div>
<!-- <staff_line_edit :s="this.$root.user" :i="0" :key="'staff_000'"></staff_line_edit> -->
<component :is="am_editing(p.id)" v-on:swapout="swapme" v-on:done_edit="done_edit" v-for="p,i in filtered" :s="p" :i="i" :dup_class="is_dup_id_class(p.id)" :key="'staff_'+p.id + '_' + i"></component>
</div>` })
// https://www.daterangepicker.com/
// https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input/datetime-local
// __ _ _ _ _ _ _
// / _| | | | (_) (_) | (_)
// | |_| | _____ __ __ _ ___| |_ ___ ___| |_ _ ___ ___
// | _| |/ _ \ \/ / / _` |/ __| __| \ \ / / | __| |/ _ \/ __|
// | | | | __/> < | (_| | (__| |_| |\ V /| | |_| | __/\__ \
// |_| |_|\___/_/\_\ \__,_|\___|\__|_| \_/ |_|\__|_|\___||___/
//
//
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
//
// EDIT / CREATE activity or event
//
const ActivityEditor = Vue.component('activityedit', {
props: [ 'which', ],
data: function () {
return { my_activity:0, creating:0,
everyname:[], everyone:[], hosts_by_sesid:{}, this_hosts:[], host_search:[], host_search_str:'',
activities:[], this_activity:{}, editing: -1, active:-1, } },
mounted: function() {
this.fetch_mypeople()
this.$root.fetch_menus()
if (this.which==0) {
this.creating = 1
this.$root.creating = 1
this.this_activity = {"title":"","desc":"","length":"1","starttime":"",
"track":"","location":"","gets_survey":"1","category":"1",
"parent":"","recording":"","instructions":"","image_url":"",
"is_flex_approved":"1","typeId":"101"}
} else { this.my_activity = this.which
this.fetch_myevents() } },
methods: {
set_id: function(new_id) { this.this_activity.id = new_id; this.creating=0; this.$root.creating=0 },
month_year: function(d) { var b = this.$root.$dj(d).format('MMM D YYYY'); return b },
setsort: function(ss) {
if (this.sortby == ss) { this.reversed = ! this.reversed }
else { this.reversed = false; this.sortby = ss } },
swapme: function(x) { this.editing = x },
done_edit: function(id) { this.editing = -1 },
am_editing: function(id) { return 0 },
remove: function(hostid) {
var self = this
basic_get('dir_api.php?a=remove/host/' + this.my_activity + '/' + hostid,
function(r2) {
self.this_hosts = _.reject(self.this_hosts, function(x) { return x.hostid == hostid} )
alert_message('Saved') } )
},
add: function(hostid) {
var self = this
basic_get('dir_api.php?a=add/host/' + this.my_activity + '/' + hostid,
function(r2) {
self.this_hosts.push( _.findWhere(self.everyone, {id:hostid} ) )
alert_message('Saved') } )
},
hostlookup: function() {
var self = this
if (this.host_search_str=='') { this.host_search=[] }
else { this.host_search_str = this.host_search_str.toLowerCase()
this.host_search = _.first(_.filter(self.everyone, function(x) { return x.name.toLowerCase().search( self.host_search_str) != -1 }),7) } },
save_new_event: function() {
this.$root.events.trigger('create_new_session',this.this_activity)
},
fetch_mypeople: function() {
var self = this;
basic_get('dir_api.php?a=get/names',
function(r2) {
self.everyone = r2.users
_.each( self.everyone, function(x) {
x.hostid = x.hostid } )
self.everyname = _.pluck(r2.users,'name') } ) },
fetch_myevents: function() {
var self = this;
basic_get('dir_api.php?a=get/sessions',
function(r2) {
self.activities = _.indexBy(r2,function(x) { return x.id } ); self.$forceUpdate()
self.active += 1;
_.each( self.activities, function(x) {
x.searchable = x.title.toLowerCase() + ' ' + x.desc.toLowerCase() } )
self.this_activity = self.activities[ self.my_activity ]
self.this_activity.starttime = self.this_activity.starttime.replace(' ','T')} )
basic_get('dir_api.php?a=get/hosts',
function(r2) {
var filtered = r2.filter(x => x.hostid != null)
self.hosts_by_sesid = _.groupBy(filtered,function(x) { return x.id } )
self.this_hosts = self.hosts_by_sesid[ self.my_activity ]
} )
},
},
template: `<div class="">
<!--<div class="pure-g pure-form pure-form-aligned">
<div class="pure-u-1-24"></div>
<div class="pure-u-23-24">
<div class="pure-control-group">
<label></label>
<a class="button-inlist" :href="'?w='+(this.my_activity-1)">Prev</a>
&nbsp; &nbsp;
<a class="button-inlist" :href="'?w='+(this.my_activity+1)">Next</a>
</div>
</div>
</div>-->
<div class="pure-g pure-form pure-form-aligned">
<div class="pure-u-1-24"></div>
<div class="pure-u-23-24">
<dtpicker table="conf_sessions" qid="starttime" :answer="this_activity.starttime"
:targetid="this_activity.id" question="Starts at"></dtpicker>
<field myclass="double" table="conf_sessions" qid="title" :answer="this_activity.title"
:targetid="this_activity.id" question="Title" placeholder="Title"></field>
<htfield_fa table="conf_sessions" qid="desc" question="Description" myclass="pell"
:answer="this_activity.desc" :targetid="this_activity.id"></htfield_fa>
<field myclass="double" table="conf_sessions" qid="location" :answer="this_activity.location"
:targetid="this_activity.id" question="Location / Zoom" placeholder="Location"></field>
<field myclass="double" table="conf_sessions" qid="location_irl" :answer="this_activity.location_irl"
:targetid="this_activity.id" question="Location / In Person" placeholder="Location"></field>
<selectmenu_fa table="conf_sessions"
qid="mode" :answer="this_activity.mode" menu="modes_menu"
:targetid="this_activity.id" question="What mode?" labelfield="string"></selectmenu_fa>
<field myclass="double" table="conf_sessions" qid="length" :answer="this_activity.length"
:targetid="this_activity.id" question="Length (in hours or minutes)" placeholder="1"></field>
<selectmenu_fa table="conf_sessions" qid="type" :answer="this_activity.type" menu="sessiontypes_menu"
:targetid="this_activity.id" question="Type" labelfield="type"></selectmenu_fa>
<selectmenu_fa v-if="this_activity.type=='101'" table="conf_sessions"
qid="parent" :answer="this_activity.parent" menu="parents_menu"
:targetid="this_activity.id" question="What flex day?" labelfield="title"></selectmenu_fa>
<field myclass="double" table="conf_sessions" qid="recording" :answer="this_activity.recording"
:targetid="this_activity.id" question="Recording Link" placeholder=""></field>
<br />
<!-- Existing Hosts List -->
<div v-if="!creating" class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Hosts</label>
<div class="border rounded-md p-3 bg-gray-50">
<i v-if="this_hosts.length==0">type below to add a host</i>
<div v-for="h in this_hosts" class="flex items-center justify-between text-sm mb-1">
<span>{{ h.name }}</span>
<button
type="button"
@click="remove(h.hostid)"
class="text-red-500 hover:text-red-700 font-semibold"
>
x
</button>
</div>
</div>
</div>
<!-- Add Host Section -->
<div v-if="!creating" class="mb-4">
<label class="block text-sm font-medium text-gray-700 mb-1">Add a host</label>
<input
type="text"
id="addhost"
name="addhost"
v-model="host_search_str"
@keyup="hostlookup"
autocomplete="off"
class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring focus:ring-blue-200 focus:ring-opacity-50 p-2 text-sm mb-2"
/>
<div class="border rounded-md p-3 bg-gray-50">
<div v-for="ho in host_search" class="flex items-center justify-between text-sm mb-1">
<span
@click="add(ho.id)"
class="clickme">
{{ ho.name }}</span>
<button
type="button"
@click="add(ho.id)"
class="text-blue-500 hover:text-blue-700 font-semibold"
>
+
</button>
</div>
</div>
</div>
</div>
</div>
<!-- add: permissions track, x day if pd day
x add/remove host x New activity general list & signup
show signups, surveys instructions survey?
flex approved? image? what other permissions issues? -->
</div>` })
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
//
// DISPLAY / EDIT activity or event
//
const ActivityInlineEditor = Vue.component('activityinlineedit', {
props: [ 'which', ],
data: function () {
return { my_activity:0, creating:0,
everyname:[], everyone:[], hosts_by_sesid:{}, this_hosts:[], host_search:[], host_search_str:'',
activities:[], this_activity:{}, editing: 0, active:-1, } },
mounted: function() {
this.fetch_mypeople()
this.$root.fetch_menus()
if (this.which==0) {
this.creating = 1
this.$root.creating = 1
this.this_activity = {"title":"","desc":"","length":"1","starttime":"",
"track":"","location":"","gets_survey":"1","category":"1",
"parent":"","recording":"","instructions":"","image_url":"",
"is_flex_approved":"1","typeId":"101"}
} else { this.my_activity = this.which
this.fetch_myevents() } },
methods: {
set_id: function(new_id) { this.this_activity.id = new_id; this.creating=0; this.$root.creating=0 },
month_year: function(d) { var b = this.$root.$dj(d).format('MMM D YYYY'); return b },
setsort: function(ss) {
if (this.sortby == ss) { this.reversed = ! this.reversed }
else { this.reversed = false; this.sortby = ss } },
swapme: function(x) { this.editing = x },
done_edit: function(id) { this.editing = -1 },
am_editing: function(id) { return 0 },
remove: function(hostid) {
var self = this
basic_get('dir_api.php?a=remove/host/' + this.my_activity + '/' + hostid,
function(r2) {
self.this_hosts = _.reject(self.this_hosts, function(x) { return x.hostid == hostid} )
alert_message('Saved') } )
},
add: function(hostid) {
var self = this
basic_get('dir_api.php?a=add/host/' + this.my_activity + '/' + hostid,
function(r2) {
self.this_hosts.push( _.findWhere(self.everyone, {id:hostid} ) )
alert_message('Saved') } )
},
hostlookup: function() {
var self = this
if (this.host_search_str=='') { this.host_search=[] }
else { this.host_search_str = this.host_search_str.toLowerCase()
this.host_search = _.first(_.filter(self.everyone, function(x) { return x.name.toLowerCase().search( self.host_search_str) != -1 }),7) } },
save_new_event: function() {
this.$root.events.trigger('create_new_session',this.this_activity)
},
fetch_mypeople: function() {
var self = this;
basic_get('dir_api.php?a=get/names',
function(r2) {
self.everyone = r2.users
_.each( self.everyone, function(x) {
x.hostid = x.id } )
self.everyname = _.pluck(r2.users,'name') } ) },
fetch_myevents: function() {
var self = this;
basic_get('dir_api.php?a=get/sessions',
function(r2) {
self.activities = _.indexBy(r2,function(x) { return x.id } ); self.$forceUpdate()
self.active += 1;
_.each( self.activities, function(x) {
x.searchable = x.title + ' ' + x.desc
x.searchable = x.searchable.toLowerCase()
} )
self.this_activity = self.activities[ self.my_activity ]
self.this_activity.starttime = self.this_activity.starttime.replace(' ','T')} )
basic_get('dir_api.php?a=get/hosts',
function(r2) {
self.hosts_by_sesid = _.groupBy(r2,function(x) { return x.id } )
var unfiltered = self.hosts_by_sesid[ self.my_activity ]
self.this_hosts = unfiltered.filter(x => x.hostid != null)
} )
},
},
template: `<div class="">
<div v-if="editing" class="bg-white rounded-lg shadow p-4 mb-6">
<!-- Top action bar -->
<div class="flex justify-end mb-4">
<button
@click.prevent="editing = 0"
class="text-sm text-blue-600 hover:underline font-medium"
>
Done
</button>
</div>
<!-- Form fields -->
<div class="space-y-4">
<field
myclass="double"
table="conf_sessions"
qid="title"
:answer="this_activity.title"
:targetid="this_activity.id"
question="Title"
placeholder="Title"
/>
<dtpicker
table="conf_sessions"
qid="starttime"
:answer="this_activity.starttime"
:targetid="this_activity.id"
question="Starts at"
/>
<field
myclass="double"
table="conf_sessions"
qid="length"
:answer="this_activity.length"
:targetid="this_activity.id"
question="Length (in hours)"
placeholder="1"
/>
<selectmenu_fa
table="conf_sessions"
qid="mode"
:answer="this_activity.mode"
menu="modes_menu"
:targetid="this_activity.id"
question="What mode?"
labelfield="string"
/>
<field
myclass="double"
table="conf_sessions"
qid="location"
:answer="this_activity.location"
:targetid="this_activity.id"
question="Location / Zoom"
placeholder="Location"
/>
<field
myclass="double"
table="conf_sessions"
qid="location_irl"
:answer="this_activity.location_irl"
:targetid="this_activity.id"
question="Location / In Person"
placeholder="Location"
/>
<!-- Host List -->
<div v-if="!creating">
<label class="block text-sm font-medium text-gray-700 mb-1">Hosts</label>
<div class="border rounded-md p-3 bg-gray-50 space-y-1">
<div
v-for="h in this_hosts"
:key="h.hostid"
class="flex items-center justify-between text-sm"
>
<span>{{ h.name }}</span>
<button
type="button"
@click="remove(h.hostid)"
class="text-red-500 hover:text-red-700 font-semibold"
>
×
</button>
</div>
<div v-if="this_hosts.length === 0" class="text-sm text-gray-500 italic">
Add a host by typing below
</div>
</div>
</div>
<!-- Host Add Input + Suggestions -->
<div v-if="!creating">
<label class="block text-sm font-medium text-gray-700 mb-1">Add a host</label>
<input
type="text"
id="addhost"
name="addhost"
v-model="host_search_str"
@keyup="hostlookup"
autocomplete="off"
class="w-full rounded-md border border-gray-500 bg-white text-gray-900 shadow focus:border-blue-600 focus:ring focus:ring-blue-300 focus:ring-opacity-50 px-4 py-2 text-base mb-2"
/>
<div class="border rounded-md p-3 bg-gray-50 space-y-1">
<div
v-for="ho in host_search"
:key="ho.id"
class="flex items-center justify-between text-sm"
>
<span @click="add(ho.id)" class="cursor-pointer hover:underline">
{{ ho.name }}
</span>
<button
type="button"
@click="add(ho.id)"
class="text-blue-500 hover:text-blue-700 font-semibold"
>
+
</button>
</div>
</div>
</div>
<htfield_fa
table="conf_sessions"
qid="desc"
question="Description"
myclass="pell"
:answer="this_activity.desc"
:targetid="this_activity.id"
/>
<selectmenu_fa
table="conf_sessions"
qid="type"
:answer="this_activity.type"
menu="sessiontypes_menu"
:targetid="this_activity.id"
question="Type"
labelfield="type"
/>
<selectmenu_fa
v-if="this_activity.type === '101'"
table="conf_sessions"
qid="parent"
:answer="this_activity.parent"
menu="parents_menu"
:targetid="this_activity.id"
question="What flex day?"
labelfield="title"
/>
<field
myclass="double"
table="conf_sessions"
qid="recording"
:answer="this_activity.recording"
:targetid="this_activity.id"
question="Recording Link"
placeholder=""
/>
</div>
</div>
<div v-else class="flex flex-col md:flex-row gap-4 bg-white rounded-md shadow p-4 mb-4">
<!-- Left: Edit Button -->
<div class="md:w-40 shrink-0">
<a href="#" @click.prevent="editing = 1" class="text-sm text-blue-600 hover:underline font-medium">
Edit
</a>
</div>
<!-- Right: Session Info -->
<div class="flex-grow text-sm text-gray-800">
<p class="text-base font-semibold text-gray-900 mb-1">{{ this_activity.title }}</p>
<p class="text-gray-600 mb-1">
{{ $root.$dj(this_activity.starttime).format('h:mma') }} /
<span class="capitalize">{{ this_activity.mode }}</span>
</p>
<p class="text-gray-600 mb-1">
Presented by:
<span v-for="(h, i) in this_hosts" :key="h.hostid">
{{ h.name }}<span v-if="i < this_hosts.length - 1">, </span>
</span>
</p>
<div class="mt-2 text-gray-700" v-html="this_activity.desc"></div>
</div>
</div>
</div>` })
//
//
//
//
//
//
// ACTIVITIES LIST MAIN CONTAINER
// ------------------------------
//
//
//
const ActivityList = Vue.component('activitylist', {
props: [ 'itineraryview','static' ],
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, } },
mounted: function() {
this.fetch_myevents()
},
methods: {
get_day_index: function(dateStr) {
const date = new Date(dateStr);
const yyyy = date.getFullYear();
const mm = String(date.getMonth() + 1).padStart(2, '0');
const dd = String(date.getDate()).padStart(2, '0');
return `${yyyy}${mm}${dd}`;
},
toggleDay: function(id) {
const group = document.getElementById(id);
const btn = document.getElementById(id + '-toggle');
group.classList.toggle('hidden');
btn.textContent = group.classList.contains('hidden') ? 'Expand Day' : 'Collapse Day';
},
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';
},
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
x > 8 ? dt.setMinutes(dt.getMinutes() + x) : dt.setHours(dt.getHours() + x); // add minutes or hours
var rd = dayjs(dt);
return rd.format('h:mma');
//return dt.toISOString().slice(0, 19).replace('T', ' '); // format output
},
hoststr: function(id) { var self = this
return _.reduce( self.hosts_by_sesid[id], function(mem,val) { if (val.name) { return mem + val.name + ", " } return mem }, '') },
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
},
month_year: function(d) { var b = this.$root.$dj(d).format('MMM D YYYY'); return b },
setsort: function(ss) {
if (this.sortby == ss) { this.reversed = ! this.reversed }
else { this.reversed = false; this.sortby = ss } },
swapme: function(x) { this.editing = x },
done_edit: function(id) { this.editing = -1 },
am_editing: function(id) { return 0 },
fetch_myevents: function() {
var self = this;
basic_get('dir_api.php?a=get/hosts', function(r2) {
self.hosts_by_sesid = _.groupBy(r2,function(x) { return x.id } )
} )
basic_get('api2.php?query=app',
function(r2) {
self.mysessions = r2.mysessions
self.my_ses_ids = _.pluck(r2.mysessions, 'id')
self.activities = r2.sessions
if (r2.host != null) { self.my_host_ids = r2.host }
else { self.my_host_ids = [] }
self.options = r2.options
self.conference = r2.conference
self.ay = r2.ay
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 )
self.active = 1
self.$forceUpdate();
} )
},
joinme: function(id) {
var self = this
basic_get('dir_api.php?a=signup/' + id,
function(r2) {
self.mysessions.push(_.findWhere(self.activities, {'id':id}))
self.my_ses_ids.push(id)
alert_message("Added activity") })
},
dumpme: function(id) {
var self = this
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") })
},
filtered: function(ff) {
var self = this
if (this.search) {
var ss = self.search.toLowerCase()
ff = ff.filter(function(x) { return ('searchable' in x ? x.searchable.includes(ss) : 0) })
}
if (this.active>0) {
var default_conf_id = _.findWhere(self.options, {label:'default_conference'}).value
self.conf = _.findWhere(self.conference, {id:default_conf_id})
var start = dayjs( self.conf.date1)
var end = dayjs(self.conf.date2)
ff = ff.filter( function(item,index) {
this_time = dayjs(item.starttime)
return this_time.isBefore(end) && start.isBefore(this_time) } )
ff = _.sortBy(ff, function(x) {
if (x[self.sortby]) { var s = x[self.sortby]; return s.trim().toLowerCase() }
return '' })
if (this.reversed) { ff.reverse() }
return ff
}
return []
}, },
computed: {
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_g_filtered: function() { if (this.active<1) { return {} }
var self=this;
return _.groupBy(self.activities_filtered, function(x) { return self.month_year(x.starttime) } ); },
mysessions_g_filtered: function() { if (this.active<1) { return {} }
var self=this; return _.groupBy(self.mysessions_filtered, function(x) { return self.month_year(x.starttime) } ); }, },
watch: { },
template: `<div class="activitylist">
<div>
<!-- Current time + Zoom info -->
<div v-if="itineraryview" class="mb-6">
<p class="text-sm text-gray-500">
<!--Current time: {{ current_time }}. -->
<span v-if="!zoom_on">
Return here on the day of your sessions for Zoom links.
</span>
</p>
</div>
<!-- No sessions signed up -->
<div v-if="itineraryview && mysessions_filtered.length === 0" class="mb-6">
<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="act.php">Sessions List</a> to sign up.
</p>
</div>
<!-- Grouped sessions by date -->
<div v-if="itineraryview && active > 0" v-for="(items, mmyy) in mysessions_g_filtered" :key="mmyy" class="mb-8">
<h3 class="text-xl font-bold text-gray-800 mb-2">{{ mmyy }} {{ get_day_title(mmyy) }}</h3>
<div v-for="a in items" :key="a.id" class="bg-white rounded-lg shadow p-4 mb-6 border-l-4 border-blue-200">
<div class="flex flex-col md:flex-row md:justify-between md:items-start gap-4">
<!-- Left side controls -->
<div class="flex flex-wrap gap-2 md:flex-col md:min-w-[100px]">
<a v-if="my_host_ids.includes(a.id)"
:href="'ed_act.php?w=' + a.id"
class="text-sm text-blue-600 hover:underline">Edit</a>
<a v-if="my_host_ids.includes(a.id)"
:href="'report.php?s=' + a.id"
class="text-sm text-blue-600 hover:underline">Info</a>
</div>
<!-- Session info -->
<div class="flex-grow relative">
<h2 class="text-base font-semibold text-gray-800">{{ a.title }}</h2>
<p class="text-sm text-gray-600">
{{ $root.$dj(a.starttime).format('h:mma') }} - {{ addTime(a.starttime, a.length) }} <!--· {{ mode_string(a) }} -->
<span v-if="a.mode === 'hybrid'"> · In person at {{ a.location_irl }} or
<a v-if="zoom_on && a.location" :href="a.location" class="underline text-blue-600">online</a>
<span v-else>online (zoom link not found)</span>
</span>
<span v-if="a.mode === 'inperson'"> · In person at {{ a.location_irl }}</span>
<span v-if="zoom_on && a.mode === 'online'"> · <a :href="a.location" class="underline text-blue-600">Online</a></span>
<span v-if="!zoom_on && a.mode === 'online'"> · Online</span>
</p>
<!-- Bottom action buttons -->
<div class="mt-4 flex gap-3 flex-wrap">
<a v-if="zoom_on && a.location"
:href="a.location"
class="px-3 py-1 text-sm font-medium text-white bg-blue-600 rounded hover:bg-blue-700">
Join Zoom
</a>
<a v-if="survey_on"
:href="'survey.php?s=' + a.id"
class="px-3 py-1 text-sm font-medium text-white bg-purple-600 rounded hover:bg-purple-700">
Survey
</a>
</div>
</div>
</div>
</div>
</div>
</div>
<div v-if="!itineraryview && active > 0" v-for="(items, mmyy) in activities_g_filtered" :key="mmyy" class="mb-6">
<div
class="flex justify-between items-baseline text-xl font-semibold text-gray-800 border-b pb-1 mb-2 clickme"
@click="toggleDay(get_day_index(mmyy))">
{{ mmyy }} {{ get_day_title(mmyy) }}
<button
:id="get_day_index(mmyy) + '-toggle'"
@click="toggleDay(get_day_index(mmyy))"
class="text-sm text-blue-600 hover:underline font-normal"
>
Collapse Day
</button>
</div>
<!-- all sessions in a day -->
<div :id="get_day_index(mmyy)">
<div v-for="a in items" :key="a.id" class="relative bg-white rounded-lg shadow p-4 mb-4">
<div class="flex flex-col md:flex-row md:justify-between md:items-start gap-4">
<!-- Action buttons (left on desktop) -->
<div v-if="!static" class="flex-shrink-0 flex gap-2">
<a v-if="my_ses_ids.includes(a.id) && !my_host_ids.includes(a.id)"
@click.prevent="dumpme(a.id)"
href="#"
class="px-3 py-1 text-sm font-medium rounded text-sm text-white bg-red-600 hover:underline">Cancel</a>
<a v-if="!my_ses_ids.includes(a.id) && !my_host_ids.includes(a.id)"
@click.prevent="joinme(a.id)"
href="#"
class="px-3 py-1 text-sm font-medium text-white bg-purple-600 rounded hover:bg-purple-700">Sign Up</a>
<a v-if="my_host_ids.includes(a.id)"
:href="'ed_act.php?w=' + a.id"
class="px-3 py-1 text-sm font-medium text-white bg-purple-600 rounded hover:bg-purple-700">Edit</a>
<b v-if="my_host_ids.includes(a.id)">You are Host.</b>
</div>
<!-- Main session info -->
<div class="flex-grow">
<h2 class="text-lg font-semibold text-gray-900">{{ a.title }}</h2>
<ModePill :mode="a.mode" class="absolute top-2 right-2" />
<p class="text-sm text-gray-700">
{{ $root.$dj(a.starttime).format('h:mma') }} - {{ addTime(a.starttime, a.length) }} ·
{{ mode_string(a) }}
<span v-if="a.mode == 'hybrid' || a.mode == 'inperson'"> · {{ a.location_irl }}</span>
</p>
<p v-if="hoststr(a.id)" class="text-sm text-gray-600">Presented by: {{ hoststr(a.id) }}</p>
<p v-if="static && a.location_irl" class="text-sm text-gray-600">Location: {{ a.location_irl }}</p>
<p v-if="static && a.location" class="text-sm text-blue-600">
Zoom Link:
<a :href="a.location" class="underline hover:text-blue-800">{{ a.location }}</a>
</p>
<div v-if="expanded" class="mt-2 text-sm text-gray-700" v-html="a.desc"></div>
</div>
</div>
</div>
</div>
</div>
</div>` })
// A CHART LABEL
//
const ChartLabel = Vue.component('chartlabel', {
props: [ 'title','start','end' ],
data: function () {
return { max_tracks: 6 } },
template: `<div class="chart_timebox" :style="'grid-column: times; grid-row: time-'+start+' / time-'+end">
<span class='chart_time'>{{title}}</span>
</div>` })
//
//
// A CHART SESSION
//
const ChartSesh = Vue.component('chartsesh', {
props: [ 'title','desc','track','start','end','start_h','end_h','mode','presenter','loc' ],
data: function () {
return { max_tracks: 6 } },
methods: {
t_start: function() {
if (this.track==0 || this.track==123) { return "track-1-start" }
return "track-" + this.track + "-start"
},
t_end: function() {
if (this.track==0 || this.track==123) { return "track-" + this.max_tracks + "-end" }
return "track-" + this.track + "-end"
},
},
template: `<div :class="'session session-1 track-'+track" :style="'grid-column: '+t_start()+' / '+t_end()+'; grid-row: time-'+start+' / time-'+end">
<h3 class="session-title">{{title}}</h3>
<span class="session-time">{{start_h}} - {{end_h}}</span>
<span class="session-desc">{{desc}}</span>
<span class="session-presenter">{{presenter}}</span>
<span class="session-mode">{{mode}}</span>
<span class="session-loc">{{loc}}</span>
</div>` })
//
//
//
//
//
//
// ACTIVITIES LIST CHART STYLE VIEW
// --------------------------------
//
//
//
const ChartView = Vue.component('chartview', {
props: [ 'day'],
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:{},
filters: {'sp23':['2023-01-01','2023-02-02'],'fa22':['2022-08-17','2022-08-20'], 'sp22':['2022-01-26','2022-01-30'], 'all':['2022-01-01','2023-11-30'] },
//day_titles: {'Aug 18 2022':" - Optional PD Day", 'Aug 19 2022':' - Convocation Day'},
day_titles: {'Jan 26 2023':" - Optional Day", 'Jan 27 2023':' - Mandatory Day'},
time_labels: [['0900','0930','9am'],
['0930','1000','9:30am'],
['1000','1030','10am'],
['1030','1100','10:30am'],
['1100','1130','11am'],
['1130','1200','11:30am'],
['1200','1230','12pm'],
['1230','1300','12:30pm'],
['1300','1330','1pm'],
['1330','1400','1:30pm'],
['1400','1430','2pm'],
['1430','1500','2:30pm'],
['1500','1530','3pm'],
['1530','1600','3:30pm'],
['1600','1630','4pm'],
['1630','1700','4:30pm'],
['1700','1730','5pm'],
['1730','1800','5:30pm'],
],
hosts_by_sesid: {}, } },
mounted: function() {
this.$root.fetch_menus();
this.fetch_myevents()
},
methods: {
hoststr: function(id) { var self = this
return _.reduce( self.hosts_by_sesid[id], function(mem,val) { if (val.name) { return mem + val.name + ", " } return mem }, '') },
mode_string: function(a) { return _.findWhere(this.$root.modes_menu, { 'id': a.mode })['string'] },
get_day_title: function(day) { if (day in this.day_titles) { return this.day_titles[day] } return '' },
month_year: function(d) { var b = this.$root.$dj(d).format('MMM D YYYY'); return b },
setsort: function(ss) {
if (this.sortby == ss) { this.reversed = ! this.reversed }
else { this.reversed = false; this.sortby = ss } },
swapme: function(x) { this.editing = x },
done_edit: function(id) { this.editing = -1 },
am_editing: function(id) { return 0 },
fetch_myevents: function() {
var self = this;
basic_get('api2.php?query=ses/'+self.day,
function(r2) {
r2 = r2.data
self.activities = _.sortBy(r2,function(x) { return x.starttime } ); self.$forceUpdate()
self.active += 1;
_.each( self.activities, function(x) {
var field = x.starttime.match(/^(\d\d\d\d)\-(\d+)\-(\d+)\s(\d+)\:(\d+)\:(\d+)$/)
var mydate = new Date(field[1], field[2] - 1 , field[3], field[4], field[5], field[6])
x.dj = dayjs(mydate)
x.searchable = x.title.toLowerCase() + ' ' + x.desc.toLowerCase() } )
self.$forceUpdate();
self.active += 1; } )
basic_get('dir_api.php?a=get/hosts', function(r2) {
self.hosts_by_sesid = _.groupBy(r2,function(x) { return x.id } )
} )
},
joinme: function(id) {
var self = this
basic_get('dir_api.php?a=signup/' + id,
function(r2) {
self.mysessions.push(_.findWhere(self.activities, {'id':id}))
self.my_ses_ids.push(id)
alert_message("Added activity") })
},
dumpme: function(id) {
var self = this
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") })
},
filtered: function(ff) { if (this.active<1) { return [] }
var self = this
if (this.search) {
var ss = self.search.toLowerCase()
ff = ff.filter(function(x) { return ('searchable' in x ? x.searchable.includes(ss) : 0) }) }
if (this.focus && 'options' in this.$root.$data.user ) {
var start = dayjs(this.$root.$data.user.options.conf.date1)
var end = dayjs(this.$root.$data.user.options.conf.date2)
ff = ff.filter( function(item,index) {
this_time = dayjs(item.starttime)
return this_time.isBefore(end) && start.isBefore(this_time) } )
}
ff = _.sortBy(ff, function(x) {
if (x[self.sortby]) { var s = x[self.sortby]; return s.trim().toLowerCase() }
return '' })
if (this.reversed) { ff.reverse() }
return ff }, },
computed: {
current_time: function() { return dayjs().format('MMM D, h:ma') },
activities_filtered: function() { var a = this.filtered(this.activities); return a; },
mysessions_filtered: function() { return this.filtered(this.mysessions) },
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) } ); },
mysessions_g_filtered: function() { if (this.active<1) { return {} }
var self=this; return _.groupBy(self.mysessions_filtered, function(x) { return self.month_year(x.starttime) } ); }, },
watch: { },
template: `<div class="activitylist schedule">
<chartsesh v-for="a in activities_filtered" :title="a.title" :start="$root.$dj(a.starttime).format('HHmm')"
:end="$root.$dj(a.starttime).add(a.length,a.length>8?'minute':'hour').format('HHmm')"
:start_h="$root.$dj(a.starttime).format('h:mma')"
:end_h="$root.$dj(a.starttime).add(a.length,a.length>8?'minute':'hour').format('h:mma')"
:mode="mode_string(a)" :presenter="hoststr(a.id)"
:track="a.track"
:loc="a.location_irl">
</chartsesh>
<chartlabel v-for="a in time_labels" :start="a[0]" :end="a[1]" :title="a[2]"></chartlabel>
</div>` })
//
//
//
//
//
//
// Activity EDITOR List
// ------------------------------
//
//
//
const ActivityEditorList = Vue.component('activityeditorlist', {
props: [ ],
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:{},
filters: {'fa23':['2023-08-21','2023-08-26'], 'sp23':['2023-01-01','2023-02-02'],'fa22':['2022-08-17','2022-08-20'], 'sp22':['2022-01-26','2022-01-30'], 'all':['2022-01-01','2023-11-30'] },
//day_titles: {'Aug 18 2022':" - Optional PD Day", 'Aug 19 2022':' - Convocation Day'},
day_titles: {'Jan 26 2023':" - Optional Day", 'Jan 27 2023':' - Mandatory Day',
'Aug 24 2023':" - Optional Day", 'Aug 25 2023':' - Mandatory Day'},
hosts_by_sesid: {}, } },
mounted: function() {
this.$root.fetch_menus();
this.fetch_myevents()
},
methods: {
hoststr: function(id) { var self = this
return _.reduce( self.hosts_by_sesid[id], function(mem,val) { if (val.name) { return mem + val.name + ", " } return mem }, '') },
mode_string: function(a) { return _.findWhere(this.$root.modes_menu, { 'id': a.mode })['string'] },
get_day_title: function(day) { if (day in this.day_titles) { return this.day_titles[day] } return '' },
month_year: function(d) { var b = this.$root.$dj(d).format('MMM D YYYY'); return b },
setsort: function(ss) {
if (this.sortby == ss) { this.reversed = ! this.reversed }
else { this.reversed = false; this.sortby = ss } },
fetch_myevents: function() {
var self = this;
basic_get('dir_api.php?a=get/mysessions',
function(r2) {
self.mysessions = _.sortBy(r2,function(x) { return x.starttime || 0} );
self.my_ses_ids = _.pluck(r2, 'id')
self.$forceUpdate();
self.active += 1;
} )
basic_get('dir_api.php?a=get/sessions',
function(r2) {
self.activities = _.sortBy(r2,function(x) { return x.starttime } ); self.$forceUpdate()
self.active += 1;
_.each( self.activities, function(x) {
var field = x.starttime.match(/^(\d\d\d\d)\-(\d+)\-(\d+)\s(\d+)\:(\d+)\:(\d+)$/)
var mydate = new Date(field[1], field[2] - 1 , field[3], field[4], field[5], field[6])
x.dj = dayjs(mydate)
x.searchable = x.title + ' ' + x.desc
x.searchable = x.searchable.toLowerCase()
} )
self.$forceUpdate();
self.active += 1; } )
basic_get('dir_api.php?a=get/hosts', function(r2) {
self.hosts_by_sesid = _.groupBy(r2,function(x) { return x.id } )
} )
basic_get('dir_api.php?a=get/allhosts',
function(r2) {
self.hosts = r2
setTimeout(function () {
self.my_host_ids = r2[ self.$root.user.conf_id ]
if (! self.my_host_ids) { self.my_host_ids = [] }
}, 750);
self.active += 1;
} )
},
filtered: function(ff) { if (this.active<1) { return [] }
var self = this
var ay = this.$root.settings['default_ay']
if (this.search) {
var ss = self.search.toLowerCase()
ff = ff.filter(function(x) { return ('searchable' in x ? x.searchable.includes(ss) : 0) }) }
if (this.focus) {
var start = dayjs(this.$root.ay_menu[ay].begin ) /* UPDATE this.filters.sp23[0]*/
var end = dayjs(this.$root.ay_menu[ay].end)
ff = ff.filter( function(item,index) {
this_time = dayjs(item.starttime)
return this_time.isBefore(end) && start.isBefore(this_time) } )
}
ff = _.sortBy(ff, function(x) {
if (x[self.sortby]) { var s = x[self.sortby]; return s.trim().toLowerCase() }
return '' })
if (this.reversed) { ff.reverse() }
return ff }, },
computed: {
current_time: function() { return dayjs().format('MMM D, h:ma') },
activities_filtered: function() { var a = this.filtered(this.activities); return a; },
mysessions_filtered: function() { return this.filtered(this.mysessions) },
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) } ); },
mysessions_g_filtered: function() { if (this.active<1) { return {} }
var self=this; return _.groupBy(self.mysessions_filtered, function(x) { return self.month_year(x.starttime) } ); }, },
watch: { },
template: `<div class="activitylist">
<div v-for="items,mmyy in activities_g_filtered">
<div class="pure-g">
<div class="day_title pure-u-1-1">{{mmyy}} {{get_day_title(mmyy)}}</div>
</div>
<div v-for="a in items" class="activitylist">
<activityinlineedit :which="a.id"></activityinlineedit>
</div>
</div>
</div>` })
//
//
//
//
//
//
// Table of all signups and hosts in a conference
// ------------------------------
//
//
//
const Overview = Vue.component('overview', {
name: 'Overview',
data() {
return {
sessions: [
// Define your sessions data here
// Each session should have an id and title property
],
users: [
// Define your users data here
// Each user should have an id and name property
],
signups: [
// Define your signups data here
// Each signup should have a userId and sessionId property
],
hosts: [
// Define your hosts data here
// Each host should have a userId and sessionId property
],
fetched:0,
};
},
computed: {
sortedSessions() {
// Sort sessions by startdate (modify the property name as per your data)
return this.sessions.sort((a, b) => a.starttime.localeCompare(b.starttime));
},
sortedUsers() {
// Sort users by name (modify the property name as per your data)
return this.users.sort((a, b) => a.name.localeCompare(b.name));
},
filteredUsers() {
// Filter users who have signup or host entries
var sorted = this.users.sort((a, b) => a.name.localeCompare(b.name))
return sorted.filter((user) =>
this.signups.some((signup) => signup.user === user.id) ||
this.hosts.some((host) => host.user === user.id)
);
},
},
methods: {
start: function() {
var self = this
basic_get('dir_api.php?a=get/sessions',
function(r2) { self.sessions = r2; self.fetched += 1; })
basic_get('dir_api.php?a=get/hosttable',
function(r2) { self.hosts = r2; self.fetched += 1; })
basic_get('dir_api.php?a=get/signups',
function(r2) { self.signups = r2; self.fetched += 1; })
basic_get('dir_api.php?a=get/users',
function(r2) { self.users = r2; self.fetched += 1; })
},
getSignupHostStatus(userId, sessionId) {
// Check if the user signed up for the session
// Check if the user hosted the session
const host = this.hosts.find((host) => host.host === userId && host.session === sessionId);
if (host) {
return 'H'; // User hosted
}
const signup = this.signups.find((signup) => signup.user === userId && signup.session === sessionId);
if (signup) {
return 'S'; // User signed up
}
return ''; // User didn't sign up or host
}
},
mounted: function() { this.start() },
template: `<div><table v-if="fetched>3" class="overviewtable">
<thead>
<tr>
<th></th> <!-- Empty header for user names column -->
<!-- Iterate over sessions and display as columns -->
<th v-for="session in sortedSessions" :key="session.id" class="rotate"><div>{{ session.title }}</div></th>
</tr>
</thead>
<tbody>
<!-- Iterate over users and display as rows -->
<tr v-for="user in filteredUsers" :key="user.id">
<td>{{ user.name }}</td> <!-- User name column -->
<!-- Iterate over sessions and display signup/host status -->
<td v-for="session in sortedSessions" :key="session.id">
{{ getSignupHostStatus(user.id, session.id) }}
</td>
</tr>
</tbody>
</table>
</div>`
});
/*
- show upcoming, past, dropdown for category
- show signup status, signup/cancel button
- navigation: check permission, show edit / new button
- page: create / edit activity.
*/
const AskSurvey = Vue.component('asksurvey', {
name: 'AskSurvey',
components: { 'essay-question': STQuestion, 'number-question': NQuestioon },
props: ['id', ],
data () { return {
questions: [] } },
methods: {
start: function() {
var self = this
basic_get('dir_api.php?a=get/questions/'+self.id,
function(r2) {
self.questions = r2 })
},
},
mounted: function() { this.start() },
template: `<div>
<h3 v-if="questions.length">{{ questions[0].title }}</h3>
<div class="session-survey" v-for="q in questions">
<number-question v-if="q['type']=='2'" :qq="q"></number-question>
<essay-question v-if="q['type']=='1'" :qq="q"></essay-question>
</div>
</div>`
});
const ShowSurveys = Vue.component('show-survey', {
name: 'ShowSurveys',
//components: { 'essay-question': EssayQuestion, 'number-question': NumberQuestion },
props: ['answer', ],
methods: {
logger: function() { console.log('logger'); console.log(this); console.log( this.answer ); return ''; },
ses_name: function() {
if (this.answer) {
return this.answer[0][0].s_title }
return '' }
},
template: `<div>
{{ logger() }}
<div class="session-survey" v-for="q in answer">
<b>{{ q[0]['question'] }}</b>
<number-question v-if="q[0]['type']=='2'" :aa="q"></number-question>
<essay-question v-if="q[0]['type']=='1'" :aa="q"></essay-question>
</div>
</div>`
});
const Survey = Vue.component('surveydisplay', {
props: [ 'answers' ],
data () {
return {
qa_all: [ ],
sesh: {},
questions: [],
one: {},
qdata: [],
answer: ""
}
},
created: function() {
var self = this
self.questions = _.where(this.$root.$data.myquestions,
{'ses_id':self.$route.params.take_ses_id})
self.$forceUpdate()
/*this.$axios.get(this.$server + this.$api + '?a=get/questions', {withCredentials: true}).then( function(resp2) {
self.qdata = resp2.data
setTimeout(self.continueExecution, 1000) //wait 1 seconds before continuing
} )*/
this.$axios.get(this.$server + this.$api + '?a=get/answers/all', {withCredentials: true}).then( function(resp2) {
} ) },
methods: {
continueExecution: function() {self.questions = _.where( this.qdata, {'ses_id': this.$route.params.take_ses_id } )},
one_session: function() {
if (this.$route.params.take_ses_id) {
var self = this
var my_id = self.$route.params.take_ses_id
var my_ses = _.find(this.$root.$data.activities, function(x) { return x.id==my_id } )
return my_ses }
return {'title':''}
},
answers: function() {
var a = this.sesh[parseInt( this.$route.params.ses_id)]
if (a) { return a }
return 0
},
requested_session: function() {
var self = this
return _.find(this.$root.$data.activities, function(x) { return x.id == self.$route.params.ses_id } )
/*
a = _.find(this.$root.$data.activities, function(x) { return x.id == self.$route.params.ses_id } )
return a */
}
},
template: `<div class="session-survey" v-for="q in answer">
<b>{{ q[0]['question'] }}</b>
<number-question v-if="q[0]['type']=='2'" :aa="q"></number-question>
<essay-question v-if="q[0]['type']=='1'" :aa="q"></essay-question>
</div>
<!-- <div>
<show-survey v-if="answers()" :answer="answers()"></show-survey>
<span v-else><ul><li>No responses yet</li></ul>
</div>
Show admin all classes -->
<br /> &nbsp;
<br /> &nbsp;
<br /> &nbsp;
</div>
`
})
// ACTIVITIY REPORT - SINGLE - SIGNUPS & SURVEYS
// -----------------------------------
//
//
//
const ActivityInfoReport = Vue.component('activityinforeport', {
props: [ 'a', 'host','num','user','emails','survey' ],
methods: {
mode_string: function(a) { return _.findWhere(this.$root.modes_menu, { 'id': a.mode })['string'] },
},
computed: {
},
watch: { },
template: `<div class="report">
<div class="pure-g pure-form">
<div class="pure-u-7-24">
{{ $root.$dj(a.starttime).format('YYYY MMM DD dd h:mma') }} - {{mode_string(a)}}
</div>
<div class="pure-u-17-24">
<b>{{a.title}}</b>
<span v-if="a.location"><br />{{ a.location }}</span>
<div class="rhs_grey">{{a.typeStr}}</div>
</div>
</div>
<div class="pure-g"><div class="pure-u-1-1">
<a :href="'ed_act.php?w='+a.id" class="button-inlist">Edit</a>
</div></div>
<expandybox v-if="a.desc" header="Description" :body="a.desc"></expandybox>
<expandybox v-if="host" header="Hosts" :body="host"></expandybox>
<expandybox v-if="user" :header="'Attendees (' + num + ')'" :body="user"></expandybox>
<expandybox v-if="emails" :header="'Emails'" :body="emails"></expandybox>
<expandybox v-if="survey" header="Surveys" :body="survey"></expandybox>
</div>
</div>` })
// A single page version, suitable for emailing.
const ActivityInfoReport2 = Vue.component('activityinforeport2', {
props: [ 'a', 'host','num','user','emails','survey' ],
methods: {
mode_string: function(a) { return _.findWhere(this.$root.modes_menu, { 'id': a.mode })['string'] },
},
computed: {
},
watch: { },
template: `<div class="report">
<div class="pure-g pure-form">
<div class="pure-u-7-24 marginbottom">
Title:
</div>
<div class="pure-u-17-24 marginbottom">
<b>{{a.title}}</b>
</div>
<div class="pure-u-7-24 marginbottom">
Date:
</div>
<div class="pure-u-17-24 marginbottom">
{{ $root.$dj(a.starttime).format('YYYY MMM DD dd h:mma') }}
</div>
<div class="pure-u-7-24 marginbottom">
Mode / Location:
</div>
<div class="pure-u-17-24 marginbottom">
{{mode_string(a)}} <span v-if="a.location"><br />{{ a.location }}</span> &nbsp; &nbsp; <span v-if="a.location_irl"><br />{{ a.location_irl }}</span>
</div>
<div class="pure-u-7-24 marginbottom">
Description:
</div>
<div class="pure-u-17-24 marginbottom" v-html="a.desc">
</div>
<div class="pure-u-7-24 marginbottom">
Hosts:
</div>
<div class="pure-u-17-24 marginbottom">
{{host}}
</div>
<div class="pure-u-7-24 marginbottom">
Attendees:
</div>
<div class="pure-u-17-24 marginbottom">
{{user}}
</div>
<div class="pure-u-7-24 marginbottom4">
Survey Results:
</div>
<div class="pure-u-17-24 marginbottom" v-html="survey">
</div>
</div>
</div>
</div>` })
//
//
//
//
//
//
// ACTIVITIES LIST - SIGNUPS & SURVEYS
// -----------------------------------
//
//
//
const ActivityReport = Vue.component('activityreport', {
props: [ 'which' ],
data: function () {
return { activities:[],hosts:[], hosts_by_sesid:[], everyone:[], questions:[], answers:{}, answers2:{}, rosters:[] } },
mounted: function() {
var self = this
basic_get('dir_api.php?a=get/sessions', function(r2) {
self.activities = _.sortBy(r2,function(x) { return x.starttime } ); self.$forceUpdate()
if (self.which=='sp22') {
var start = dayjs('2022-01-26')
var end = dayjs('2022-01-29')
self.activities = self.activities.filter( function(item,index) {
this_time = dayjs(item.starttime)
return this_time.isBefore(end) && start.isBefore(this_time) } )
}
if (self.which=='fa22') { /* UPDATE */
var start = dayjs('2022-08-17')
var end = dayjs('2022-08-20')
self.activities = self.activities.filter( function(item,index) {
this_time = dayjs(item.starttime)
return this_time.isBefore(end) && start.isBefore(this_time) } )
}
if (self.which.match( /^\d+$/ )) { self.activities = self.activities.filter( function(item,index) {
return item.id == self.which } )
}
self.active += 1;
_.each( self.activities, function(x) {
var field = x.starttime.match(/^(\d\d\d\d)\-(\d+)\-(\d+)\s(\d+)\:(\d+)\:(\d+)$/)
var mydate = new Date(field[1], field[2] - 1 , field[3], field[4], field[5], field[6])
x.dj = dayjs(mydate)
x.searchable = x.title.toLowerCase() + ' ' + x.desc.toLowerCase() } )
self.$forceUpdate();
self.active += 1; } )
basic_get('dir_api.php?a=get/hosts', function(r2) {
self.hosts_by_sesid = _.groupBy(r2,function(x) { return x.id } )
} )
basic_get('dir_api.php?a=get/rosters', function(r2) {
self.rosters = _.groupBy(r2,function(x) { return x.sesid } )
} )
basic_get('dir_api.php?a=get/allhosts', function(r2) {
self.hosts = r2
setTimeout(function () { self.hosts = r2; self.$forceUpdate() }, 750);
self.active += 1;
} )
basic_get('dir_api.php?a=get/names', function(r2) {
self.everyone = []
_.each( r2.users, function(x) { self.everyone[x.id] = x.name } ) } )
basic_get('dir_api.php?a=get/questions', function(r2) {
self.questions = r2 })
basic_get('dir_api.php?a=get/answers/all', function(r2) {
var organized = _.groupBy(r2, function(x) { return x.ses_id; } )
// TODO are answers and answers2 the same?
_.each( organized, function(val,key,lis) { self.answers[key] = _.groupBy( val, function(y) { return y.q_id; }); } )
_.each( self.answers, function(val,key,lis) { self.answers2[key] = _.sortBy( val, "q_id" ) } )
self.$forceUpdate()
} )
},
methods: {
hoststr: function(id) {
var self = this
return _.reduce( self.hosts_by_sesid[id], function(mem,val) { if (val.name) { return mem + val.name + ", " } return mem }, '')
},
userstr: function(id) { return _.pluck(this.rosters[id], "name").join(', ') },
usernum: function(id) { if (id in this.rosters) { return this.rosters[id].length } return 0 },
useremails: function(id) { return _.pluck(this.rosters[id], "email").join('; ') },
surveystr: function(id) {
var self = this
var result = ""
if (this.answers2[id]) {
_.each( this.answers2[id], function (qlist) {
result += "<b class='survey_q'>" + qlist[0]['question'] + "</b><br />\n<ul>"
_.each( qlist, function(qanswer) {
result += "<li>" + qanswer['answer'] + "</li>\n"
})
result += "</ul>\n"
})
return result
}
return "<i>no survey results?</i>"
var answers = _.find(this.activities, function(x) { return x.id == id } )
return "Surveys"
return _.reduce( self.hosts_by_sesid[id], function(mem,val) { if (val.name) { return mem + val.name + ", " } return mem }, '')
},
},
computed: {
},
watch: { },
template: `<div class="activityreport">
<div v-for="a in activities">
<activityinforeport2 :a="a" :host="hoststr(a.id)" :num="usernum(a.id)" :user="userstr(a.id)" :emails="useremails(a.id)" :survey="surveystr(a.id)"></activityinforeport2>
</div>
</div>` })
//
//
//
//
//
//
// TRAINING HISTORY - GOTT COURSES
// -------------------------------
//
//
//
const TrainingHistory = Vue.component('traininghistory', {
props: [ '' ],
data: function () {
return { training: {}, mycourses: {}, msg:"Loading..." } },
mounted: function() {
var self = this
basic_get('gott_by_goo.json', function(r2) {
self.training = r2;
setTimeout(() => { self.msg = "" }, 1500);
} )
},
computed: {
user_courses() {
if ('user' in this.$root && this.$root.active && this.training) { return this.training[this.$root.user.conf_goo] || null }
return {}
}
},
methods: {
formatDate(dateString) {
const options = { year: 'numeric', month: 'long', day: 'numeric' };
return new Date(dateString).toLocaleDateString(undefined, options);
}
},
watch: { },
template: `<div class="space-y-4">
<div class="bg-blue-50 text-blue-900 p-4 rounded-md shadow-sm">
<p class="font-medium">These are the GOTT courses you have taken.</p>
</div>
<div v-if="Object.keys(training).length > 0">
<p class="text-sm text-gray-700 mb-2">{{ msg }}</p>
<div class="space-y-3">
<div
v-for="(date, course) in user_courses"
:key="course"
class="flex flex-col md:flex-row md:items-center md:justify-between bg-white p-4 border rounded shadow-sm"
>
<div class="text-gray-800 font-medium">Course: {{ course }}</div>
<div class="text-gray-600 mt-1 md:mt-0">Completed: {{ formatDate(date) }}</div>
</div>
</div>
</div>
<div v-else>
<p class="text-gray-500 italic">No training history found.</p>
</div>
</div>` })
//
//
// _ _ _
// | | | | (_)
// ___ ___| |_| |_ _ _ __ __ _ ___
// / __|/ _ \ __| __| | '_ \ / _` / __|
// \__ \ __/ |_| |_| | | | | (_| \__ \
// |___/\___|\__|\__|_|_| |_|\__, |___/
// __/ |
// |___/
//
//
const Settings = Vue.component('settings', {
props: [ ],
data: function () {
return { 'zoom_on':'', 'survey_on':'', 'ay':'', 'default_conference':'' } },
mounted: function() {
var self = this
this.zoom_on = this.$parent.settings.zoom_on
this.survey_on = this.$parent.settings.survey_on
this.ay = this.$parent.settings.default_ay
this.default_conference = this.$parent.settings.default_conference
},
methods: {
},
computed: {
},
watch: { },
template: `<div class="settingspanel">
<ul>
<li>
<input class="form-check-input" type="checkbox" id="zoom_on" v-model="zoom_on">
<label class="icert form-check-label" for="zoom_on">Zoom Links Visible</label><br />
</li>
<li>
<input class="form-check-input" type="checkbox" id="survey_on" v-model="survey_on">
<label class="icert form-check-label" for="survey_on">Surveys Available</label><br />
</li>
<li>
<selectmenu table="conf_uinforecord" qid="value" :answer="this.ay" menu="ay_menu" :targetid="4" question="Academic Year: " labelfield="label"></selectmenu>
</li>
</ul>
</div>` })
// _ _
// | | | |
// ___ __ _| | ___ _ __ __| | __ _ _ __
// / __/ _` | |/ _ \ '_ \ / _` |/ _` | '__|
// | (_| (_| | | __/ | | | (_| | (_| | |
// \___\__,_|_|\___|_| |_|\__,_|\__,_|_|
//
//
// VERY SIMPLE CALENDAR
//
const MyCal = Vue.component('mycal', {
data: function () {
return { today:new Date(), currentMonth:0, currentYear:0, selectYear:'', selectMonth:'',
months:["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
activities:[],
by_date: {},
editing: -1, } },
mounted: function() {
var self = this
this.currentYear = this.today.getFullYear()
this.currentMonth = this.today.getMonth()
this.selectYear = this.currentYear
this.selectMonth = this.currentMonth
basic_get('dir_api.php?a=get/sessions',
function(r2) { self.activities = _.map(r2, function(x) {
var dd = new Date(x.starttime);
var [m,d,y] = [dd.getMonth(), dd.getDate(), dd.getFullYear()] // months start at 0....
var d_string = d + "-" + m + "-" + y
if (self.by_date[d_string]) { self.by_date[d_string].push(x) }
else { self.by_date[d_string] = [x, ] } } )
self.$forceUpdate() } )
},
methods: {
thisDay: function(i,j) {
var dayOfMonth = ((7*(i-1))+j)-this.firstDay()
return dayOfMonth },
eventsThisDay: function(i,j) {
var dayOfMonth = ((7*(i-1))+j)-this.firstDay()
var d_string = dayOfMonth + "-" + this.selectMonth + "-" + this.selectYear
var day_text = ''
if (this.by_date[d_string]) {
evts = _.filter( this.by_date[d_string], function(x) { return x.typeId!="101" } )
return evts } return [] },
cleanTime: function(e) {
var dd = new Date(e.starttime);
var [h,m] = [dd.getHours(), dd.getMinutes()]
var ampm = 'am'
if (h > 12) { h -= 12; ampm = 'pm' }
if (m == 0) { m = '' }
else { m = ':' + m }
return h + m + ampm },
daysInMonth: function() {
return 32 - new Date(this.selectYear, this.selectMonth, 32).getDate() },
firstDay: function() {
return (new Date(this.selectYear, this.selectMonth)).getDay() },
next: function() {
this.selectYear = (this.selectYear === 11) ? this.selectYear + 1 : this.selectYear;
this.selectMonth = (this.selectMonth + 1) % 12; },
previous: function() {
this.selectYear = (this.selectYear === 0) ? this.selectYear - 1 : this.selectYear;
this.selectMonth = (this.selectMonth === 0) ? 11 : this.selectMonth - 1; },
},
computed: {
},
watch: {
},
template:`<div class="calendar">
<h3>{{months[selectMonth] + " " + selectYear}}
<div class="btn_container"><div class="btn_float">
<button class="pure-button" v-on:click="previous">Previous</button>
<button class="pure-button" v-on:click="next">Next</button>
</div></div>
</h3>
<table><thead><tr>
<th>Sun</th><th>Mon</th><th>Tue</th><th>Wed</th><th>Thu</th><th>Fri</th><th>Sat</th>
</tr></thead>
<tbody id="calendar-body">
<tr v-for="i in 6" v-if="(((7*(i-1)))-firstDay() < daysInMonth()+1)">
<td v-for="j in 7">
<span v-if="( ((7*(i-1))+j)-firstDay() > 0) && (((7*(i-1))+j)-firstDay() < daysInMonth()+1)">
<div class="do_month">{{ thisDay(i,j) }}</div>
<div class="cal_evt" v-for="ev in eventsThisDay(i,j)">{{cleanTime(ev)}} {{ ev.title }}</div>
</span>
</td>
</tr>
</tbody></table>
</div>` })
// _ _ _ _ _
// | | | | | | | | | |
// __ _____| | ___ ___ _ __ ___ ___ | | ___| |_| |_ ___ _ __ | |
// \ \ /\ / / _ \ |/ __/ _ \| '_ ` _ \ / _ \ | |/ _ \ __| __/ _ \ '__| | |
// \ V V / __/ | (_| (_) | | | | | | __/ | | __/ |_| || __/ | |_|
// \_/\_/ \___|_|\___\___/|_| |_| |_|\___| |_|\___|\__|\__\___|_| (_)
//
//
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
// // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // // //
//
// WELCOME LETTER main component
//
const WelcomeLetter = Vue.component('welcomeletter', {
props: [ 'mysem','mycrn', 'teacher_ext_id', ],
data: function () {
return { courses_by_semester:[], wl_sem:'', wl_crn:'', section_wl:{}, sortby:'code', reversed:false, active:-1, } },
watch: {
"teacher_ext_id": function (val, oldVal) { this.fetch_sections() },
},
mounted: function() {
var self = this
if (this.mysem && this.mycrn) { this.fetch_wletters(this.mysem, this.mycrn) }
else { this.$root.do_after_load( self.fetch_sections) }
},
methods: {
swap_section: function(section_obj) { this.fetch_wletters(section_obj.sem,section_obj.crn) },
show_list: function() { this.section_wl = {} },
fetch_wletters: function(ss,cc) {
var self = this;
fetch('dir_api.php?a=get/section/' + ss + '/' + cc,
{ method: 'GET' }).then(function (response) {
// The API call was successful!
if (response.ok) {
response.json().then( function(r2) {
self.section_wl = r2;
} )
} else { return Promise.reject(response) }
}).then(function (data) {
}).catch(function (err) { console.warn('Something went wrong.', err); });
},
fetch_sections: function() {
var self = this;
basic_get('dir_api.php?a=get/sections/' + this.$root.user.ext_id,
function(r2) { self.courses_by_semester = _.groupBy(r2,function(x) { return x.sem || 0} ); self.$forceUpdate(); self.active += 1; } )
},
},
template: `<div class="">
<div v-if="section_wl.id" class="pure-g pure-form">
<div class="pure-u-24-24">
<div class="clicky" v-on:click="show_list()">back to list of sections</div>
<div class="">
Course:
</div>
<div class="">
Description:
</div>
<div class="">
Delivery Format:
</div>
<div class="">
Schedule:
</div>
<div class="">
Length:
</div>
<form class="pure-form pure-form-aligned">
<tfield table="welcome_letters" qid="introduction" question="Introduction" :answer="this.section_wl.introduction" :targetid="this.section_wl.wl_id"></tfield>
<tfield table="welcome_letters" qid="what_expect" question="What to Expect" :answer="this.section_wl.what_expect" :targetid="this.section_wl.wl_id"></tfield>
<tfield table="welcome_letters" qid="textbook" question="Textbook Information" :answer="this.section_wl.textbook" :targetid="this.section_wl.wl_id"></tfield>
<tfield table="welcome_letters" qid="assessments" question="Tests and Assessments" :answer="this.section_wl.assessments" :targetid="this.section_wl.wl_id"></tfield>
<tfield table="welcome_letters" qid="other_info" question="Other Useful Information" :answer="this.section_wl.other_info" :targetid="this.section_wl.wl_id"></tfield>
<tfield table="welcome_letters" qid="additional_resources" question="Additional Resources" :answer="this.section_wl.additional_resources" :targetid="this.section_wl.wl_id"></tfield>
</form>
</div>
</div>
<div v-else class="pure-g pure-form">
<div v-for="sect_list,sm in courses_by_semester" class="pure-u-24-24">
<b> {{sm}}</b><br />
<div v-for="crs in sect_list" class="clicky" v-on:click="swap_section(crs)">
&nbsp; &nbsp; &nbsp; {{crs.code}} - {{crs.crn}} - {{crs.name}}
</div>
</div>
</div>
</div>` })
// _ _ _
// | | | | | |
// _____ _____ _ __ | |_ ___ | |__ ___| |_ __ ___ _ __
// / _ \ \ / / _ \ '_ \| __/ __| | '_ \ / _ \ | '_ \ / _ \ '__|
// | __/\ V / __/ | | | |_\__ \ | | | | __/ | |_) | __/ |
// \___| \_/ \___|_| |_|\__|___/ |_| |_|\___|_| .__/ \___|_|
// | |
// |_| //
// AJAX POST UPDATES TO API
//
function post_update(table,cols,vals,id=0) {
action = "nothing"
//if (table=="update_survey") { action = "update" }
if (table=="personnel") { action = "update" }
if (table=="personnel_ext") { action = "update_xt" }
if (table=="conf_users") { action = "update_cf" }
if (table=="conf_sessions") { action = "update/activity" }
if (table=="webpages") { action = "update_web" }
if (table=="welcome_letters") { action = "update/letter" } // or insert?
if (table=="uniforecord") { action = "update/settings" }
var idstr = ""
if (id) { idstr = "&id=" + id }
fetch('dir_api.php', {
method: 'POST',
headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' }),
body: "a="+action+"&cols="+cols+"&vals="+vals+idstr,
}).then(function (response) {
if (response.ok) {
response.json().then( function(r2) {
// display success alert
alert_message('Saved.')
} )
} else { return Promise.reject(response) }
}).then(function (data) {
}).catch(function (err) { alert_message("Couldn't save!",pink); console.warn('Something went wrong.', err); });
}
function generic_fail(err,x="Something went wrong with an ajax fetch") {
console.log(x); console.log(err) }
function basic_get( url, after_fxn, fail_fxn=generic_fail ) {
fetch(url, { method: 'GET' }).then(function (response) {
if (response.ok) {response.json().then( function(r2) { after_fxn(r2) } )
} else { return Promise.reject(response) }
}).then(function (data) { }).catch(function (err) { fail_fxn(err) } ) }
var evt = {
clear_tables: function() {
var self = this
_.each( this.tables, function(x) { self[x] = [] } )
this.data = {}
this.target_ids = {} },
send_update: function() {
var self = this
_.each( this.tables, function(x) {
if (self[x].length) {
var cols = ""
var vals = ""
_.each(self[x], function(y) {
if (cols.length) { cols += "," }
if (vals.length) { vals += "," }
cols += y
if (typeof self.data[y] == "string") {
re = /,/g
vals += encodeURIComponent(self.data[y].replace(re,'[CMA]') ) }
else { vals += self.data[y] }
})
var edit_other = 0
if (self.target_ids[x]) { edit_other = self.target_ids[x] }
post_update(x, cols, vals, edit_other)
}
} ) },
data: {},
target_ids: {},
tables: ['personnel','personnel_ext','webpages','welcome_letters','conf_sessions','conf_hosts',
'pers_departments','pers_committees','pers_titles'],
}
evt.clear_tables()
MicroEvent.mixin(evt)
// _ _____ _____
// (_) /\ | __ \| __ \
// _ __ ___ __ _ _ _ __ / \ | |__) | |__) |
// | '_ ` _ \ / _` | | '_ \ / /\ \ | ___/| ___/
// | | | | | | (_| | | | | | / ____ \| | | |
// |_| |_| |_|\__,_|_|_| |_| /_/ \_\_| |_|
//
//
var app = new Vue({
el: '#dir_editor',
data: { events: evt,
msg: 'hello', active: false, creating:0,
user: {'last_name':'', 'first_name':'', 'department':'', 'extension':'', 'phone_number':'', 'email':'',
'staff_type':'', 'room':'', 'status':'', 'user_id':'', 'password':'', 'time_created':'', 'time_updated':'',
'id':'', ext_id:false, 'web_on':'', use_dir_photo:0, general_photo_release:0, espanol:0, zoom:'', preferred_contact:'',
officehours:'', title:'', picture:'', education:'', bio:'', courses:'', personal_page:'', changed:'' },
settings:{},
filter: [],
roles_menu: [],
depts_menu: [],
titles_menu: [],
sessiontypes_menu: [],
parents_menu: [],
ay_menu: [],
modes_menu: [ {'id': 'Pending', 'string':'Pending'}, {'id':'online','string':'Online'}, {'id':'inperson','string':'In Person'}, {'id':'hybrid','string':'Hybrid'}, {'id':'','string':''}, ],
waiting_fxns: [],
data_loaded: 0,
committees_menu: [],
menus_fetched: false,
},
watch: {
'data_loaded': function(newVal,oldVal) {
if (newVal > 0) { _.each( this.waiting_fxns, function(fx) { fx() }) } }, },
methods: {
do_after_load: function(do_fxn) { this.waiting_fxns.push(do_fxn) /*....*/ },
fetch_menus: function() {
if (! this.menus_fetched) {
var self = this;
fetch('dir_api.php?a=menus', { method: 'GET' }).then(function (response) {
// The API call was successful!
if (response.ok) {
response.json().then( function(r2) {
self.depts_menu = r2.departments;
self.roles_menu = r2.roles;
self.titles_menu = r2.titles;
self.committees_menu = r2.committees;
self.sessiontypes_menu = r2.sessiontypes;
self.parents_menu = r2.parents;
self.menus_fetched = true;
} )
} else { return Promise.reject(response) }
}).then(function (data) {
}).catch(function (err) {
// FAILED TO LOAD. MOST LIKELY THE SSO/SESSION TIMED OUT
// .... reload whole page to get redirect...?
console.warn('Something went wrong.', err);
});
}
},
my_subscribe_calendar: function() { return "webcal://hhh.gavilan.edu/phowell/map/calendar" + this.user.conf_id + ".ics" },
clip_copy: function(x) {
// see the .htaccess file for the mod_rewrite that makes the ics file work. //
var data = [new ClipboardItem({ "text/plain": new Blob([this.my_subscribe_calendar()], { type: "text/plain" }) })];
navigator.clipboard.write(data).then(function() {
fadein_message()
}, function() { console.error("Unable to write to clipboard. :-("); }) }, },
computed: { },
mounted: function() {
var self = this;
fetch('api2.php?query=start', { method: 'GET' }).then(function (response) {
// The API call was successful!
if (response.ok) {
response.json().then( function(r2) {
var x = self.user.mysessions
self.user = r2.user;
self.depts_menu = r2.departments;
self.roles_menu = r2.roles;
self.titles_menu = r2.titles;
self.committees_menu = r2.committees;
self.sessiontypes_menu = r2.sessiontypes;
self.parents_menu = r2.parents;
self.ay_menu = r2.ay;
self.settings = r2.settings;
self.menus_fetched = true;
self.data_loaded += 1
// pause half a second for the children to get populated before registering update events...
setTimeout(function() {
self.active = true;
// fancier text editors...
//pell.init( { element: document.getElementById('bio2'), onChange: function(h) { console.log(h) } } )
}, 1600);
} )
} else { return Promise.reject(response) }
}).then(function (data) {
}).catch(function (err) { console.warn('Something went wrong.', err); }) } })
// _
// | |
// _____ _____ _ __ | |_ ___
// / _ \ \ / / _ \ '_ \| __/ __|
// | __/\ V / __/ | | | |_\__ \
// \___| \_/ \___|_| |_|\__|___/
//
//
//
// SIMPLE EVENTS
//
var update_fxn = _.debounce( function() {
alert_message('saving...','lightgreen')
evt.send_update(); evt.clear_tables(); }, 1300 )
var update_survey_fxn = function() {
}
evt.bind('update_survey',_.debounce( function(dat) {
if (app.active) {
fetch('dir_api.php?a=update/answers', {
method: 'POST',
headers: new Headers({ 'Content-Type': 'application/x-www-form-urlencoded' }),
body: "session=" +dat[1] + "&user=" +dat[0] + "&qid=" +dat[2] + "&answer="+dat[3],
}).then(function (response) {
if (response.ok) {
response.json().then( function(r2) {
// display success alert
alert_message('Saved.')
} )
} else { return Promise.reject(response) }
}).then(function (data) {
}).catch(function (err) { alert_message("Couldn't save!",pink); console.warn('Something went wrong.', err); });
} } , 1300 ) );
evt.bind('changed', function(dat) {
if (app.active) {
var column = dat[0]
var table = dat[1]
var value = dat[2]
var target = dat[3]
this.data[column] = value
if (!this[table].includes(column) ) {this[table].push(column)}
if (target) { this.target_ids[table] = target } }
if (app.active && !app.creating) { update_fxn() }
});
evt.bind('create_new_session', function(dat) {
var default_activity = {"title":"","desc":"","length":"1","starttime":"","track":"","location":"","gets_survey":"1","category":"1",
"parent":"","recording":"","instructions":"","image_url":"","is_flex_approved":"1","typeId":"101"}
var new_activity = _.extend(default_activity, evt.data)
if ('typeId' in new_activity) { new_activity.type = new_activity.typeId; delete new_activity.typeId; }
let formData = new FormData();
_.each( Object.keys(new_activity), function(x) { formData.append(x, new_activity[x]) } )
fetch('dir_api.php?a=set/newsession', {
method: 'POST',
body: formData, }).then(function (response) {
if (response.ok) {
response.json().then( function(r2) {
alert_message('Saved new activity.')
app.$children[0].set_id(r2.new_id) } )
} else { return Promise.reject(response) }
}).then(function (data) {
}).catch(function (err) { alert_message("Couldn't create the activity!",pink); console.warn('Something went wrong.', err); }) })
// bold the current page
function bold_nav() {
var currentFileName = window.location.pathname.split('/').pop();
// Select the <a> tag with the matching href value and apply the class
$('#nav a[href="' + currentFileName + '"]').addClass('highlight');
}
$(document).ready(function() {
bold_nav()
})
//
//
// MISC
//
//
//
// <img :src="'//www.gavilan.edu/staff/' + s.dir_photo_path" width="25" height="auto" />
// v-lazy-container="{ selector: 'img' }"
//