UNPKG

json-object-editor

Version:

JOE the Json Object Editor | Platform Edition

790 lines (738 loc) 34.3 kB
var task = function(){return{ title : '${name}', info:"Create a new task and assign it to a team member in the project panel.", ai_instructions:"", listWindowTitle: 'Tasks', gridView:{ cols:[ {name:'priority 1',filter:{priority:1}}, {name:'priority 2',filter:{priority:2}}, {name:'priority 3',filter:{priority:3}}, {name:'unprioritized',filter:{priority:1000}} ] }, tableView:{ }, columns:3, // shortTemplate:function(currentitem,asset){ // var asset = asset || currentitem || {}; // var imageURL = (asset.tcin && parseInt(asset.tcin) && 'https://target.scene7.com/is/image/Target/'+asset.tcin)|| // asset.thumbnail_url; // var t = '<img class="asset-icon" src='+imageURL+' />' // +'<joe-subtitle>${name}</joe-subtitle>' // +'${if (${tcin})}<joe-subtext>tcin <b>${tcin}</b></joe-subtext>${end if}' // +'<joe-subtext>#${asset_id}</joe-subtext>' // +'<joe-subtext>${asset_type_name}</joe-subtext>' // ; // return t; // }, menuicon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-10 -10 120 120"><path d="M71.51,28.79a30,30,0,1,0,0,42.43A29.9,29.9,0,0,0,71.51,28.79ZM68.19,67.9a25.31,25.31,0,1,1,0-35.8A25.15,25.15,0,0,1,68.19,67.9Z"/><polygon points="47.65 50.21 39.03 40.84 29.5 50.37 29.5 50.37 47.46 68.33 83.8 34.99 82.87 24.94 47.65 50.21"/></svg>', listTitle:function(item){ var project ={},phase = '',phasename=''; if(item.project){ project = _joe.getDataItem(item.project,"project"); if(item.project_phase){ phase = project.phases.where({id:item.project_phase})[0]||{name:''}; phasename = (phase.name)?' > '+phase.name:''; } } return'<joe-full-right>' +'<joe-subtitle>'+((item.points && item.points+' pts' )||'')+'</joe-subtitle>' +'<joe-title>${RUN[_joe.SERVER.User.Render.cubes;${members};]}</joe-title></joe-full-right>' //+'<joe-subtext>${RUN[_joe.getDataItemProp;${project};"project"]} </joe-subtext>' +'<joe-subtext>'+(project.name||'')+phasename+' </joe-subtext>' +`<joe-title>${item.name}</joe-title><joe-subtitle>${item.info}</joe-subtitle> ${item.due_date && `<joe-subtext>due ${item.due_date}</joe-subtext>`||''}` }, searchables:['name','info','description','_id'], sorter:[ 'priority','project','status', {field:'!due_date',display:'due'}, 'name', {field:'!joeUpdated',display:'updated'}, {field:'!points',display:'points'}, {field:'project_phase',display:'phase'}, ], // Curated summary for agents (normalized, stable) summary:{ description:'Work item tracked against projects and users; used for planning and execution.', purpose:'Tasks track units of work for users and projects. A task belongs to a single project (optional), has a single status, may have a single parent task, and can be associated to many users (members) and many tags. Use the task schema to manage actionable items, planning, and execution state; resolve related names via project/status/user/tag when presenting to users.', labelField:'name', defaultSort:{ field:'joeUpdated', dir:'desc' }, searchableFields:['name','info','description','_id'], allowedSorts:['priority','project','status','due_date','name','joeUpdated','points','project_phase','created'], relationships:{ outbound:[ { field:'status', targetSchema:'status', cardinality:'one' }, { field:'project', targetSchema:'project', cardinality:'one' }, { field:'parent_task', targetSchema:'task', cardinality:'one' }, { field:'members', targetSchema:'user', cardinality:'many' }, { field:'tags', targetSchema:'tag', cardinality:'many' } ], inbound:{ graphRef:'server/relationships.graph.json' } }, joeManagedFields:['created','joeUpdated'], fields:[ { name:'_id', type:'string', required:true }, { name:'itemtype', type:'string', required:true, const:'task' }, { name:'name', type:'string', required:true }, { name:'info', type:'string' }, { name:'description', type:'string' }, { name:'task_type', type:'string', enumValues:['task','chore','epic'] }, { name:'status', type:'string', isReference:true, targetSchema:'status', required:true }, { name:'project', type:'string', isReference:true, targetSchema:'project' }, { name:'parent_task', type:'string', isReference:true, targetSchema:'task' }, { name:'project_phase', type:'string' }, { name:'priority', type:'number', enumValues:[1,2,3,1000] }, { name:'due_date', type:'string', format:'date-time' }, { name:'complete', type:'boolean' }, { name:'members', type:'string', isArray:true, isReference:true, targetSchema:'user' }, { name:'tags', type:'string', isArray:true, isReference:true, targetSchema:'tag' }, { name:'points', type:'number' }, { name:'time_estimate', type:'number' }, { name:'joeUpdated', type:'string', format:'date-time', required:true }, { name:'created', type:'string', format:'date-time', required:true } ] }, task_types:['task','chore','epic'], methods:{ addUser:function(userid,goto){ var current = _jco(true); if(current.members.indexOf(userid) == -1){ current.members.push(userid); _joe.Fields.rerender('members',{members:current.members}); } if(goto){ _joe.gotoSection('manage'); } }, doToday:function(){ var current = _jco(true); var todaystr = $c.format(new Date(),'m/d/Y'); var overwrites = {due_date:todaystr}; var active_status = _joe.Data.status.where({ datasets:{$in:[current.itemtype]}, active:true, }).sortBy('index')[0] || false; if(active_status){ overwrites.status = active_status._id; } _joe.Fields.rerender('due_date,status',overwrites); }, assigned:function(task,userid){ var userid = userid ||_joe.User._id; if((task.members||[]).indexOf(userid) != -1){ return true; } var subs = (task.subtasks ||[]).where({assigned:userid}); if(subs.length){ return true; } return false; }, setType:function(type){ if(type == "chore"){ _joe.Sections.unFocusAll(); _joe.gotoSection('tags',{focus:'tags',activate:'true'}) _joe.gotoSection('overview',{focus:'overview',activate:'true'}) _joe.gotoSection('glance',{focus:'glance',activate:'true'}) _joe.gotoSection('estimates',{focus:'estimates',activate:'true'}) _joe.gotoSection('subtasks',{focus:'subtasks',activate:'true'}) _joe.Sections.setTabbedMode(true); _joe.Sections.toggleSome(['overview','glance','estimates'],true); var wkflw = _joe.getData({query:{workflow_id:'chore'}})[0]||false; if(wkflw && wkflw.statuses && wkflw.statuses[0]){ _joe.Fields.set('status',wkflw.statuses[0]); } }else{ _joe.Sections.unFocusAll(); _joe.Sections.setTabbedMode(false); } }, // (Deprecated) task-specific Thought trigger kept for backward compatibility if needed }, onPanelShow:function(state){ }, menu:function(){ if(__jsu && ['super','admin','editor'].indexOf(__jsu.role) != -1 || $c.isEmpty(self.Data.user)){ return [ __exportBtn__, _joe.SERVER.History.button, {name:'assingme',title:'assign task to me', label:_joe.schemas.user.menuicon+'<button-text>Assign Me</button-text>', action:'_joe.schemas.task.methods.addUser(_joe.User._id,true)', css:'joe-left-button joe-orange-button joe-svg-button'}, {name:'dotoday',title:'today', label:_joe.schemas.event.menuicon+'<button-text>Today</button-text>', action:'_joe.schemas.task.methods.doToday()', css:'joe-left-button joe-orange-button joe-svg-button'}, __quicksaveBtn__, __deleteBtn__ ]; } return []; }, fields:function(){ var fields = [ {sidebar_start:'left'}, {section_start:'JAI',display:'JOE Ai'}, "objectChat", "listConversations", 'proposeThought', 'ai_responses', {section_end:'JAI'}, {sidebar_end:'left'}, {section_start:'overview'}, 'name', 'info', {extend:'description',specs:{ ai:{prompt:'Summarize the task in a few sentences. Take into account the project the task is associated with if there is one.'}} }, {section_end:'overview'}, {section_start:'subtasks'}, {name:'subtasks',type:'objectList',label:false, template:function(obj,subobj){ var done = (subobj.sub_complete)?'joe-strike':''; return '<joe-full-right>'+(subobj.assigned && _renderUserCubes(_joe.getDataItem(subobj.assigned,'user'))||'')+'</joe-full-right>' +'<joe-subtext>${sub_duedate}</joe-subtext>' +'<joe-title class="'+done+'">${info}</joe-title>' }, properties:[ {name:'info'}, {name:'sub_duedate', display:'due', type:'date',width:'50px'}, {name:'assigned',display:'asgt',type:'select',width:'50px', onchange:'_joe.schemas.task.methods.addUser(this.value);', values:function(item){ var opts = []; var item = _jco(true); if(item.members && item.members.length){ opts = opts.concat(item.members); } if(item.project){ var proj = _joe.getDataItem(item.project,'project'); if(proj.members && proj.members.length){ opts = opts.concat(proj.members); } } //opts = $.unique(opts); //opts(new Set(opts)); Array.from(new Set(opts)); if(!opts || !opts.length){ return []; } var users = _joe.Data.user.where({_id:{$in:opts}}); return users; },blank:true, idprop:'_id',template:function(t,u){ if(_joe.sizeClass == "large-size"){ return u.name; } var name = (u.first_name && u.first_name+' '+u.last_name) ||u.fullname || u.name; if(!name){ return ''; } var initials = name[0]+ (((name.indexOf(' ') > 0) && name[name.indexOf(' ')+1])||''); return initials; } }, {name:'sub_complete',display:'done',type:'boolean',width:'50px'} ] }, {section_end:'subtasks'}, {section_start:'acceptance',collapsed:function(item){ return !(item.scceptance_criteria && item.scceptance_criteria.length); }}, {name:'scceptance_criteria',display:'acceptance criteria',type:'objectList',label:false, template:function(obj,subobj){ var done = (subobj.sub_complete)?'joe-strike':''; return '<joe-title class="'+done+'">${criteria}</joe-title>' ; }, properties:[ {name:'criteria'}, {name:'sub_complete',display:'done',type:'boolean',width:'50px'} ] }, {section_end:'acceptance'}, {section_start:'related',collapsed:function(item){ return !(item.files && item.files.length); }}, {name:'files',type:'uploader',allowmultiple:true, height:'300px',comment:'drag files here to upload', onConfirm:_joe.SERVER.Plugins.awsFileUpload}, 'references', {section_end:'related'}, {sidebar_start:'right',collapsed:function(item){ if(_joe.sizeClass == "large-size"){ return false; } return (item.priority && item.project && item.status && item.due_date); }}, {section_start:'glance'}, {name:'task_type',type:'select',display:'task type', values:function(){ return (['']).concat(_joe.schemas.task.task_types); }, onchange:'_joe.schemas.task.methods.setType(this.value);', onPanelShow:function(cur){ console.log('CURRENT',cur); if(cur.task_type){ _joe.schemas.task.methods.setType(cur.task_type); } } }, 'status', {name:'due_date',type:'date',display:'due'}, {name:'complete',type:'boolean',display:'complete task',label:'click to hide from list'}, {section_end:'glance'}, {section_start:'organize'}, {name:'project',type:'select',values:'project',goto:'project',idprop:'_id',width:'50%',blank:true,rerender:'project_phase,subtasks,parent_task',icon:_joe.schemas.project.menuicon}, {name:'parent_task', width:'50%', hidden:function(item){ return !item.project; },goto:'task', type:'select',values:function(item){ var query = {project:item.project}; if(item.project_phase){ query.project_phase = item.project_phase; } var tasks = _joe.Data.task.where(query).sortBy('project_phase,priority'); return tasks; },icon:'task', schema:'task',blank:true }, {name:'project_phase', type:'select', blank:true, rerender:'parent_task', values:function(item){ if(!item.project){ return []; } var proj = _joe.getDataItem(item.project,'project'); if(!proj || !proj.phases || !proj.phases.length){ return []; } var vals = []; proj.phases.map(function(phase){ vals.push({name:phase.name+' '+(phase.end_date && ' - '+phase.end_date ||''),value:phase.id||phase.name}) }) return vals; }, display:'phase',width:'50%',hidden:function(item){ if(!item.project){ return true; } var proj = _joe.getDataItem(item.project,'project'); if(!proj || !proj.phases || !proj.phases.length){ return true; } return false; }}, 'priority', {section_end:'organize'}, {section_start:'estimates'}, {name:'points',comment:'fte, scrum, etc',type:'number',display:'story points'}, {name:'time_estimate',comment:'minutes',type:'number',display:'time estimate'}, {section_end:'estimates'}, {section_start:'manage', collapsed:function(item){ return false; return (item.members && item.members.length); }}, {extend:'members',specs:{rerender:'subtasks'}}, {label:'comments'}, 'user_comments', {section_end:'manage'}, {section_start:'reports',collapsed:true}, 'reports', {section_end:'reports'}, {section_start:'tags',collapsed:true}, 'tags', {section_end:'tags'}, {section_start:'access'}, '_protected', {section_end:'access'}, {sidebar_end:'right'}, {section_start:'system',collapsed:true}, '_id','created','itemtype', {section_end:'system'} ]; return fields; }, instance:{ checkbox:function(instance){ return {prop:'approved'} }, fields:function(){ return [ {name:'approved',type:'boolean',display:'APPROVED','label':'click when task is approved',width:'50%'}, {name:'date',type:'date',width:'50%',native:true}, //'members', {name:'members',type:'group',label:'completed by -', values:function(){ return _joe.Data.user.where({custom_roles:{$in:['chore_assignee']}}); } }, {name:'points',type:'number'}, {name:'files',type:'uploader',allowmultiple:true, height:'300px',comment:'drag files here to upload', onConfirm:_joe.SERVER.Plugins.awsFileUpload}, ]; } }, filters:function(){ if(!_joe.Cache.static.weekstart){ var curr = new Date; var startD,endD; startD = new Date(curr.setDate(curr.getDate() - curr.getDay() +1)); startD.setHours(0,0,0); _joe.Cache.static.weekstart = _joe.Utils.toDateString(startD,{fullyear:true}) endD = new Date(curr.setDate(curr.getDate() - curr.getDay() +8)) endD.setHours(0,0,0); _joe.Cache.static.weekend = _joe.Utils.toDateString(endD,{fullyear:true}) } var filters = []; var others =[ {group_start:'priorities',collapsed:true/*,name:'prioritized', filter:{$and:[{priority:{$nin:[null,'']}},{priority:{$exists:true}}]}*/ }, {name:'priority 1',filter:{priority:1}, stripecolor:_joe.Colors.priority[1]}, {name:'priority 2',filter:{priority:2},stripecolor: _joe.Colors.priority[2]}, {name:'priority 3',filter:{priority:3}, stripecolor:_joe.Colors.priority[3]}, {name:'unprioritized',filter:{priority:1000}}, {group_end:'priorities'}, {name:'My Tasks',filter:{members:{$in:[_joe.User._id]}}}, {name:'unassigned',filter:{complete:{$nin:['true',true]},$or:[{members:{$lte:0}},{members:{$exists:false}}]}}, {name:'this week',filter:{due_date:{$gte:_joe.Cache.static.weekstart,$lte:_joe.Cache.static.weekend}}} ]; var stats = _joe.Filter.Options.status({ schema:'task', group:'status', collapsed:true, none:true }); filters = filters.concat(stats, _joe.Filter.Options.tags({ schema:'task', group:'tags', collapsed:true }), _joe.Filter.Options.datasetProperty('user','members',{ group:'members', collapsed:true }), others ); /*_joe.Data.status.sortBy('index') .where({datasets:{$in:['task']}}).map(function(status){ stats.push({name:status.name,filter:{status:status._id},bgcolor:status.color}) })*/ return filters; }, stripeColor:function(item){ if(item.priority && item.priority < 100){ return { title:`P${item.priority}`, color:_joe.Colors.priority[item.priority] }; } }, bgColor:function(item){ if(!item.status){ return ''; } var status = _joe.getDataItem(item.status,'status'); /* var color = _joe.getDataItemProp(item.status,'status','color');*/ return {color:status.color,title:status.name}; }, /* stripeColor:[ {color:'#000',filter:{priority:1}}, {color:'#999',filter:{priority:2}} ],*/ checkbox:{prop:'complete',percentage:function(item){ if(!item.subtasks || !item.subtasks.length){ return false; } var subs = item.subtasks; return (subs.filter(function(sub){return sub.sub_complete;}).length/subs.length); }}, subsets:function(){ if(!_joe.Cache.static.weekstart){ var curr = new Date; var startD,endD; startD = new Date(curr.setDate(curr.getDate() - curr.getDay() +1)); startD.setHours(0,0,0); _joe.Cache.static.weekstart = _joe.Utils.toDateString(startD,{fullyear:true}) endD = new Date(curr.setDate(curr.getDate() - curr.getDay() +8)) endD.setHours(0,0,0); _joe.Cache.static.weekend = _joe.Utils.toDateString(endD,{fullyear:true}) } var sets = [ {name:'ACTIVE',default:true,filter: function(){ if(this.task_type == "chore"){ return false; } if(this.complete){ return false; } if(!this.status){ return false; } else{ //var s = _joe.Cache.get(this.status); var s = $.get(this.status); if(s.inactive || s.terminal || s.default){ return false; } } return true; } }, {name:'Incomplete',filter: function(){ if(this.complete){ return false; } if(this.status){ if(_joe.Cache.get(this.status).inactive){ return false; } } return true; } }, {name:'inactive',filter: function(){ if(this.complete){ return false; } if(this.status){ if(_joe.Cache.get(this.status).inactive){ return true; } } return false; } }, //{name:'My Tasks',default:true,filter:{complete:{$nin:['true',true]},members:{$in:[_joe.User._id]}}}, {name:'My Tasks',filter:function(ind,item){ if(parseBoolean(this.complete)){ return false; } if((this.members||[]).indexOf(_joe.User._id) != -1){ return true; } var subs = (this.subtasks ||[]).where({assigned:_joe.User._id}); if(subs.length){ return true; } }}, // {complete:{$nin:['true',true]},members:{$in:[_joe.User._id]}} {name:'this week',filter:{due_date:{$gte:_joe.Cache.static.weekstart,$lte:_joe.Cache.static.weekend}}}, ]; sets.push({group_start:'projects',collapsed:true, name:'projects',filter:{project:{$in:[null,'']}}}) var projs = []; _joe.Data.project.map(function(p){ if(p.complete != true){ projs.push({name: p.name,filter:{project: p._id,complete:{$nin:['true',true]}}}); } }); sets = sets.concat(projs.sortBy('name')); sets.push({group_end:'projects'}) var tts = []; sets.push({group_start:'task_type',collapsed:false, name:'task type',filter:{project:{$in:[null,'']}}}) _joe.schemas.task.task_types.map(function(tt){ tts.push({name: tt,filter:{task_type: tt}}); }); sets = sets.concat(tts); sets.push({group_end:'task_type'}) sets.push({name:'completed',sorter:['!joeUpdated'],filter:{complete:{$in:['true',true]}}}); return sets; }, idprop : "_id", onDemandExpander:true, itemExpander:function(task){ if(task.subtasks && task.subtasks.length){ var subtask_html = '<joe-card><joe-title>Subtasks</joe-title>'; task.subtasks.map(function(st){ subtask_html +='<div class="joe-subtitle '+(st.assigned && "lh-30"||"")+' clear ' +(st.sub_complete && 'joe-strike' || '')+'">' +((st.assigned && _renderUserCubes(_joe.getDataItem(st.assigned,'user')))||'') +st.info +((st.sub_duedate && ' - '+st.sub_duedate)||'') +'</div>'; }) subtask_html+='</joe-card>'; //+fillTemplate('<div class="joe-subtitle">${info}</div>',task.subtasks); return subtask_html; }else{ return ''; } }, new:function(){ var payload = { itemtype:'task', created:(new Date).toISOString(), _id:cuid() }; if(_joe.current.subset){ payload.project = (_joe.Data.project.where({name:_joe.current.subset.name})[0]||{_id:null})._id; } return payload; }, report:{ name:'standard report', info:'the report that comes with joe', template:function(task,templateData){ var content = ``; if(task.project){ var projectSchema = JOE.Schemas.schema.project; var project = JOE.Cache.findByID("project",task.project); content+=` <report-section class="marginless"> <a class="report-content-item" target="_blank" href="/API/plugin/reportbuilder/standard/?itemid=${task.project}"> <report-icon>${projectSchema.menuicon}</report-icon> <joe-subtitle>Project</joe-subtitle><joe-title>${project.name}</joe-title> </a> <div style="clear:both;"></div> </report-section> `; } if(task.description){ content+=`<report-section> <report-section-label>Description</report-section-label> ${task.description} </report-section>`; } if(task.subtasks && task.subtasks.length){ content+=`<report-section> <report-section-label>Subtasks</report-section-label> ${task.subtasks.map(t=>{ return `<report-subtask class="complete-${t.sub_complete}">${t.info}</report-subtask>`; }).join('')} </report-section>`; } return content; } }, reports:{ chore_cards:{ name:'Chore Cards', template_type:'module', template:function(data){ var schema = JOE.Schemas.raw_schemas.task; var icon = schema.menuicon; //var icon = JOE.SC var start=0,count=10,tasks; if(data.request.query && data.request.query.where){ tasks = JOE.Data.task.where(eval('('+data.request.query.where+')')); }else{ tasks = JOE.Data.task; } tasks = tasks.slice(start,count); logit(tasks); function renderCard(task){ return `<task-card><card-front> <card-icon>${icon}</card-icon> <joe-title>${task.name}</joe-title> <card-points>${(task.points && `<pts-number>${task.points}</pts-number> pts`)||''}</card-points> </card-front> </task-card>`; } var template =` <html> <head> <meta name="viewport" content="initial-scale=1.0, user-scalable=no" /> <link rel="stylesheet" href="/JsonObjectEditor/css/joe.css"> <link rel="stylesheet" href="/JsonObjectEditor/css/report-styles.css"> <link rel="icon" href="/chore_favicon.ico?v=2" type="image/x-icon" /> <link rel="icon" sizes="192x192" href="/chore-192x192.png"> <meta name="mobile-web-app-capable" content="yes"> <title>Chore Cards</title> <style> html,body{ background:#fff; } *{ padding:0; margin:0; position:relative;} task-card{ width:calc(50% - 12px); margin:4px; height:198px; border:2px solid #999; display:block; float:left; } card-front{ display:block; text-align:center; padding-top:20px; } card-icon { position: absolute; top: -10px; left: -10px; width: 80px; opacity:.8; } card-points{ font-weight:bold; color:#999; font-size:24px; } card-front joe-title { font-size: 24px; margin: 30px 20px 10px 20px; } </style> </head> <body> ${tasks.map(renderCard).join('')} </body> </html> `; return template; } } }, events:{ create:function(item,specs){ var members = item.members; var member; var status = JOE.Cache.findByID('status',item.status)||{}; var project = JOE.Cache.findByID('project',item.project)||{}; members.map(function(mem){ member = JOE.Cache.findByID('user',mem); if(member){ logit('sending assignment email to: '+member.email); /*cd75a051-dcec-4306-8894-e1dc64853306*/ JOE.Apps.plugins.notifier.sendNotification('TASK_ASSIGNED',{ ASSIGNED:member, TASK:item, PROJECT:project, STATUS:status }) } }) }, save:function(item,specs){ $c.DEBUG_MODE =false; if(specs.cached && specs.historical_info.changes && specs.historical_info.changes.members){ var cached_members = specs.cached.members; var members = item.members; var member; var status = JOE.Cache.findByID('status',item.status)||{}; var project = JOE.Cache.findByID('project',item.project)||{}; members.map(function(mem){ if(cached_members.indexOf(mem) == -1){ member = JOE.Cache.findByID('user',mem); if(member){ logit('sending assignment email to: '+member.email); /*cd75a051-dcec-4306-8894-e1dc64853306*/ JOE.Apps.plugins.notifier.sendNotification('TASK_ASSIGNED',{ ASSIGNED:member, TASK:item, PROJECT:project, STATUS:status }) } } }) } } } } }; module.exports = task();