UNPKG

json-object-editor

Version:

JOE the Json Object Editor | Platform Edition

463 lines (440 loc) 20.4 kB
var user = function(){return{ title : '${fullname} | ${name}', info:"Manage each user you've given access to supervise, edit, or view elements of your dashboard.", default_schema:true, searchables:['name','info','fullname','_id','email'], // Curated summary for agents summary:{ description:'Account representing a person with roles, access, and identity.', purpose:'Users authenticate into JOE. They hold roles and may belong to groups, tags, and apps. Many schemas reference users (e.g., tasks.members, project.members). Use user to resolve identities, access scopes, and human names for references.', labelField:'fullname', defaultSort:{ field:'joeUpdated', dir:'desc' }, searchableFields:['name','fullname','email','_id'], allowedSorts:['joeUpdated','created','name','fullname','email','role'], relationships:{ outbound:[ { field:'status', targetSchema:'status', cardinality:'one' }, { field:'tags', targetSchema:'tag', cardinality:'many' }, // administrative associations on the user record { field:'apps', targetSchema:'app', cardinality:'many' }, { field:'schemas', targetSchema:'<schemaName>', cardinality:'many' }, { field:'items', targetSchema:'*', 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:'user' }, { name:'name', type:'string', required:true }, { name:'fullname', type:'string' }, { name:'email', type:'string' }, { name:'role', type:'string', enumValues:['','viewer','editor','admin','super'] }, { name:'status', type:'string', isReference:true, targetSchema:'status' }, { name:'tags', type:'string', isArray:true, isReference:true, targetSchema:'tag' }, { name:'styles', type:'code', comment:'css to append to dashboard head' }, { name:'description', type:'string', display:'bio' }, { name:'joeUpdated', type:'string', format:'date-time', required:true }, { name:'created', type:'string', format:'date-time', required:true } ] }, listView:{ title: function(user){ var template = '<joe-full-right>${role}</joe-full-right>'+ '<joe-title>${name}</joe-title><joe-subtitle>${fullname}</joe-subtitle><joe-subtext>${info}</joe-subtext>'; if (user.image_url){ template = '<joe-listitem-icon style="background-image:url(${image_url});"></joe-listitem-icon>'+template; } return template; }, listWindowTitle: 'Users'/*, icon:function(userobj){ if (userobj.image_url){ return {url:userobj.image_url,height:'100px'}; } return false; }*/ }, itemExpander:function(u){ return `<joe-subtext>email</joe-subtext> ${u.email} <br/> ${u.description}`; }, menuicon:'<svg xmlns="http://www.w3.org/2000/svg" viewBox="-20 -20 140 140"><g><path d="M74 79.4c-4-6.8-13.3-14.9-24-14.9S30 72.6 26 79.4a44.1 44.1 0 0 0 24 6.7A44.1 44.1 0 0 0 74 79.4Z"/><circle cx="50" cy="39.3" r="18.8"/><path d="M50 14.9A35.1 35.1 0 1 1 14.9 50 35.1 35.1 0 0 1 50 14.9m0-1A36.1 36.1 0 1 0 86.1 50 36.1 36.1 0 0 0 50 13.9Z"/><path d="M90 50A40 40 0 1 1 50 10 40 40 0 0 1 90 50ZM50 13.9A36.1 36.1 0 1 0 86.1 50 36.1 36.1 0 0 0 50 13.9Z"/></g></svg>', stripeColor:function(item){ return item.color; }, subsets:function(item){ var subs = []; subs.push({group_start:'roles',collapsed:true}), ['viewer','editor','admin','super'].map(function(role){ subs.push({name:role+'s',filter:{role:role}}) }) subs.push({group_end:'roles'}) subs.push({group_start:'groups',collapsed:true}), _joe.Data.group.sortBy('name').map(function(g){ subs.push({name:g.name,filter:{_id:{$in:g.members}}}); }) subs.push({group_end:'groups'}) subs.push({group_start:'tags',collapsed:true}); subs = subs.concat(_joe.Filter.Options.tags()); subs.push({group_end:'tags'}); var roles = _joe.Utils.Settings('CUSTOM_ROLES') || []; ss_roles = roles.map(r=>{ return{ name:r.name, filter:{custom_roles:{$in:[r._id]}} } }) subs.push({group_start:'custom_roles',display:'custom roles',collapsed:true}); subs = subs.concat(ss_roles); subs.push({group_end:'custom_roles'}) return subs; }, fields:function(){ return [ {extend:'name',specs:{display:'username'}}, {name:'role',type:'select',display:'Primary Role',values:['','viewer','editor','admin','super'], locked:function(item){ if(_joe.isNewItem()){ return false; } if(_joe.User.role == "super"){ return false; } return true; }}, {name:'password',condition:function(item){ if(_joe.isNewItem()){ return true; } if(!_joe.User){ return false; } if(_joe.User._id == item._id ||_joe.User.role == "super"){ return true; } return false; }, type:'password', /*type:function(item){ if(_joe.User && _joe.User._id == item._id){ return 'text'; }else{ return 'password'; } },*/ locked:function(item){ if(_joe.isNewItem()){ return false; } if(_joe.User && _joe.User._id == item._id){ return false; } if(_joe.User.role == 'super'){ return false; } return true; } }, // 'password:password', // 'confirm_password:password', {name:'fullname',width:'50%'}, {extend:'info',specs:{width:'50%'}}, {section_start:'contact'}, 'email', 'phone', {section_end:'contact'}, {section_start:'avatar',collapsed:true}, 'color:color', {name:'image_url',display:'image URL', onchange:'_joe.current.object.image = this.value; $(\'.joe-image-field[name=image]\').val(this.value); _joe.Fields.rerender(\'image_upload\')'}, {name:'image_upload',type:'uploader',field:'image', url_field:'image_url', use_legacy:true,height:'300px',comment:'drag an image here to upload',onConfirm:_joe.SERVER.Plugins.awsConnect}, {name:'image',type:'image',hidden:true}, {section_end:'avatar'}, {section_start:'customization',collapsed:true}, {name:'styles',comment:'css to append to dashboard head',type:'code'}, {section_end:'customization'}, {section_start:'about',collapsed:true}, {extend:'description',specs:{display:'bio'}}, 'tags', {section_end:'about'}, {sidebar_start:'right'}, 'status', 'updated', 'reports', {section_start:'Apps',collapsed:true}, { name:'apps',type:'group', display:function(user){ return `user has access to ${user.apps.length} of ${all_apps.length} apps`; }, values:function(){return all_apps;}, default:function(){return all_apps;}, locked:function(item){ if(['super','admin'].indexOf(_joe.User.role) != -1){ return false; } return true; } }, {section_end:'Apps'}, {section_start:'Schemas',collapsed:true}, { name:'schemas',type:'group', display:function(user){ return `user has access to ${user.schemas.length} of ${__collectionNames.length} schemas`; }, values:function(){return __collectionNames;}, default:function(){return __collectionNames;}, locked:function(item){ if(['super'].indexOf(_joe.User.role) != -1){ return false; } return true; } }, {section_end:'Schemas'}, {section_start:'Items',collapsed:true}, { name:'items',type:'objectReference', values:function(u){ var haystack =[]; for(var d in _joe.Data){ haystack = haystack.concat(_joe.Data[d]); } return haystack; } }, {section_end:'Items'}, {section_start:'Roles',collapsed:true}, { name:'custom_roles',type:'group', display:function(user){ return `user holds ${(user.custom_roles || []).length} custom roles`; }, values:function(){ var roles = _joe.Utils.Settings('CUSTOM_ROLES') return roles; }, //default:function(){return __collectionNames;}, locked:function(item){ if(['super','admin'].indexOf(_joe.User.role) != -1){ return false; } return true; } }, {section_end:'Roles'}, {sidebar_end:'right'}, {section_start:'system',collapsed:true}, '_id','created','itemtype','token', {section_end:'system'} ]; }, reports:{ chore_assignee_screen:{ name:'Chore Assignee Screen', template_type:'module', template:function(data){ var CHORES_TAG = JOE.Utils.Settings('CHORES_TAG'); if(!CHORES_TAG){ return 'Error: Please configure a CHORES_TAG in the setting.' } var user = data.ITEM; var icons = { task:JOE.Schemas.raw_schemas.task.menuicon, reward:JOE.Schemas.raw_schemas.reward.menuicon } var points = 0; if(!user || user.itemtype !="user"){ return `Invalid user`; } //logit(data); function toDateString(dt,specs){ var specs = specs || {}; var dt = dt || new Date(); if(typeof dt == 'string'){ dt = new Date(dt); } var dts_str = ''; var monthstr = (dt.getUTCMonth()+1); monthstr = (monthstr < 10 ? '0' + monthstr : monthstr); var dayStr =('0'+dt.getUTCDate()).slice(-2); var yearStr = dt.getUTCFullYear().toString(); if(specs.format == "Y-m-d"){ dts_str = `${yearStr}-${monthstr}-${dayStr}`; return dts_str; } dts_str += monthstr +'/'+dayStr +'/'+((specs.fullyear && yearStr)||yearStr.substr(2,4)); return dts_str; } function getHistory(){ let iPoints=0; var pendingList = []; var pending_instances = []; var approved_instances = []; var completed_instances = JOE.Data.instance.where({ instance_type:{$in:['reward','task']}, members:{$in:[user._id]} })||[]; completed_instances.map(ci=>{ if(ci.instance_type == "task"){ if(ci.approved){ iPoints += parseInt(ci.points) || 0; approved_instances.push(ci); }else{ pending_instances.push(ci); pendingList.push(ci.reference[0]); } }else{ if(ci.approved){ iPoints -= parseInt(ci.points) || 0; approved_instances.push(ci); }else{ pending_instances.push(ci); pendingList.push(ci.reference[0]); } } }) return { pending:pending_instances, history:completed_instances, approved:approved_instances, points:iPoints, pendingList:pendingList } } var instances = getHistory(); function getCurrentPoints(){ var pts = points; return pts; } function getCurrentTaskLists(){ var activeStatus = JOE.Data.status.reduce((res,s)=>{ if(s.active){ res.push(s._id); } return res },[]) //chores list var choresLists = JOE.Data.list.where({ tags:{$in:[CHORES_TAG]}, status:{$in:activeStatus} }) //lists where in active status //console.log('choresLists',choresLists) //finally get the lists var chores = []; choresLists.map(cl=>{ chores = chores.concat(cl.items); }) //console.log(instances.pendingList,'chores '+ chores.length); chores = chores.filter(ch=>{ return (instances.pendingList.indexOf(ch) == -1); }) //console.log(chores.length); logit('available',chores); return chores; } var availableChores = getCurrentTaskLists(); function renderAvailableRow(task_id){ let task = task_id; if(typeof task == "string"){ task = $J.get(task_id); } return `<card-row class="available" onclick="takeOnTask('${task._id}','${task.itemtype}')"> <card-icon>${icons[task.itemtype]}</card-icon> <card-pts ><b>${task.points}</b> pts</card-pts> <joe-title>${task.name}</joe-title> </card-row>`; } function renderCardRow(i){ return `<card-row class="${i.approved && 'approved' ||''} ${i.instance_type}" > <card-icon>${icons[i.instance_type]}</card-icon> <card-pts ><b>${i.points}</b> pts</card-pts> <joe-title>${i.name}</joe-title> <card-date>${toDateString(new Date(i.date))}</card-date> </card-row>`; } function renderCardPanel(name,id,content,count){ return `<card-panel data-panel="${id}"> <card-panel-title>${name} [${count}]</card-panel-title> ${content || 'coming soon'} </card-panel> `; } console.log('rewards',JOE.Data.reward) var active = ''; if(instances.pending.length){ active = "pending"; } var template =` <html> <head> <script src="/JsonObjectEditor/js/libs/jquery-3.5.1.min.js"></script> <script src="/JsonObjectEditor/js/libs/craydent-1.9.2.min.js"></script> <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="stylesheet" href="/JsonObjectEditor/css/scorecard-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 Assignee Screen</title> <style> html,body{ background:${user.color}; } user-points { text-shadow: 0 0 8px ${user.color}; background: ${user.color}; } </style> <script> var user={ _id:'${user._id}' } var today = '${toDateString(null,{fullyear:true,format:'Y-m-d'})}'; </script> </head> <body> <score-card> <user-name>${user.fullname.split(' ')[0].toLowerCase()}'s scorecard </user-name> <user-points>${instances.points} <pts-label> points </pts-label> </user-points> ${renderCardPanel('Available tasks','available', availableChores.map(renderAvailableRow).join(''), availableChores.length )} ${renderCardPanel('Claim Rewards','rewards', JOE.Data.reward.sortBy('points').map(renderAvailableRow).join(''),JOE.Data.reward.length )} ${renderCardPanel('Pending Approval','pending', instances.pending.sortBy('!date').map(renderCardRow).join('')||'no pending tasks' ,instances.pending.length)} ${renderCardPanel('History','history', instances.approved.sortBy('!date').map(renderCardRow).join('') ,instances.approved.length)} <menu-nav> <menu-nav-option data-panel="available">Available</menu-nav-option> <menu-nav-option data-panel="rewards">Rewards</menu-nav-option> <menu-nav-option data-panel="pending">Pending</menu-nav-option> <menu-nav-option data-panel="history">History</menu-nav-option> </menu-nav> </score-card> <script src="/JsonObjectEditor/js/scorecard-ux.js"></script> </body> </html> `; return template; } } }, idprop : "_id" }}; module.exports = user();