diff --git a/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js b/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js index 1bba9421..177225b0 100644 --- a/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js +++ b/app/imports/api/creature/creatureProperties/methods/getSlotFillFilter.js @@ -15,9 +15,30 @@ export default function getSlotFillFilter({slot, libraryIds}){ slotFillerType: slot.slotType, }] }); + } else if (slot.type === 'class') { + filter.$and.push({ + $or: [{ + type: 'classLevel', + },{ + type: 'slotFiller', + slotFillerType: 'classLevel', + }] + }); + filter.variableName = slot.variableName; + + // Only search for levels the class needs + const levels = []; + if (slot.missingLevels && slot.missingLevels.length) { + levels.push(...slot.missingLevels); + } else if (slot.level) { + levels.push(slot.level); + } + if (levels.length) { + filter.level = {$or: levels}; + } } let tagsOr = []; - let tagsNor = []; + let tagsNin = []; if (slot.slotTags && slot.slotTags.length){ tagsOr.push({tags: {$all: slot.slotTags}}); } @@ -27,15 +48,15 @@ export default function getSlotFillFilter({slot, libraryIds}){ if (extra.operation === 'OR'){ tagsOr.push({tags: {$all: extra.tags}}); } else if (extra.operation === 'NOT'){ - tagsNor.push({tags: {$all: extra.tags}}); + tagsNin.push(...extra.tags); } }); } if (tagsOr.length){ filter.$and.push({$or: tagsOr}); } - if (tagsNor.length){ - filter.$and.push({$nor: tagsNor}); + if (tagsNin.length){ + filter.$and.push({$nin: tagsNin}); } if (!filter.$and.length){ delete filter.$and; diff --git a/app/imports/server/publications/library.js b/app/imports/server/publications/library.js index f1d04175..00a45a5b 100644 --- a/app/imports/server/publications/library.js +++ b/app/imports/server/publications/library.js @@ -28,6 +28,7 @@ const LIBRARY_NODE_TREE_FIELDS = { amount: 1, // Class level level: 1, + variableName: 1, // Proficiency value: 1, // Reference diff --git a/app/imports/server/publications/slotFillers.js b/app/imports/server/publications/slotFillers.js index 3dd6d141..e97fa20f 100644 --- a/app/imports/server/publications/slotFillers.js +++ b/app/imports/server/publications/slotFillers.js @@ -88,3 +88,62 @@ Meteor.publish('slotFillers', function(slotId, searchTerm){ }); }); }); + +Meteor.publish('classFillers', function(classId){ + let self = this; + if (!classId) return []; + + this.autorun(function (){ + let userId = this.userId; + if (!userId) { + return []; + } + // Get the class + let classProp = CreatureProperties.findOne(classId); + if (!classProp){ + return []; + } + + // Get all the ids of libraries the user can access + const user = Meteor.users.findOne(userId, { + fields: {subscribedLibraries: 1} + }); + const subs = user && user.subscribedLibraries || []; + let libraries = Libraries.find({ + $or: [ + {owner: userId}, + {writers: userId}, + {readers: userId}, + {_id: {$in: subs}}, + ] + }, { + fields: {_id: 1, name: 1}, + }); + let libraryIds = libraries.map(lib => lib._id); + + // Build a filter for nodes in those libraries that match the slot + let filter = getSlotFillFilter({slot: classProp, libraryIds}); + + this.autorun(function(){ + // Get the limit of the documents the user can fetch + var limit = self.data('limit') || 50; + check(limit, Number); + + let options = { + sort: { + name: 1, + order: 1, + }, + fields: FIELDS, + limit, + }; + + self.autorun(function () { + self.setData('countAll', LibraryNodes.find(filter).count()); + }); + self.autorun(function () { + return [LibraryNodes.find(filter, options), libraries]; + }); + }); + }); +}); diff --git a/app/imports/ui/creature/slots/LevelUpDialog.vue b/app/imports/ui/creature/slots/LevelUpDialog.vue new file mode 100644 index 00000000..4eba1043 --- /dev/null +++ b/app/imports/ui/creature/slots/LevelUpDialog.vue @@ -0,0 +1,406 @@ + + + + + {{ model.name }} + + + + + + + {{ slotPropertyTypeName }} with tags: + + + + + + + + + + + + + + + + + {{ libraryNode.slotFillerCondition }} + + + + {{ libraryNames[libraryNode.ancestors[0].id ] }} + + + + {{ libraryNode.slotQuantityFilled }} slots + + + + mdi-window-restore + + + + + + + + + + + + + Load More + + + + + + Requirements of {{ disabledNodeCount }} properties were not met + + + Show All + + + + + + Cancel + + + + + {{ totalQuantitySelected }} / {{ model.spaceLeft }} + + + Insert + + + Close Test + + + + + + + + + diff --git a/app/imports/ui/dialogStack/DialogComponentIndex.js b/app/imports/ui/dialogStack/DialogComponentIndex.js index 4f0f96e0..8fb87bc7 100644 --- a/app/imports/ui/dialogStack/DialogComponentIndex.js +++ b/app/imports/ui/dialogStack/DialogComponentIndex.js @@ -10,6 +10,7 @@ const DeleteUserAccountDialog = () => import('/imports/ui/user/DeleteUserAccount const ExperienceInsertDialog = () => import( '/imports/ui/creature/experiences/ExperienceInsertDialog.vue'); const ExperienceListDialog = () => import( '/imports/ui/creature/experiences/ExperienceListDialog.vue'); const InviteDialog = () => import('/imports/ui/user/InviteDialog.vue'); +const LevelUpDialog = () => import('/imports/ui/creature/slots/LevelUpDialog.vue'); const LibraryCreationDialog = () => import('/imports/ui/library/LibraryCreationDialog.vue'); const LibraryEditDialog = () => import('/imports/ui/library/LibraryEditDialog.vue'); const LibraryNodeCreationDialog = () => import('/imports/ui/library/LibraryNodeCreationDialog.vue'); @@ -36,6 +37,7 @@ export default { ExperienceInsertDialog, ExperienceListDialog, InviteDialog, + LevelUpDialog, LibraryCreationDialog, LibraryEditDialog, LibraryNodeCreationDialog, diff --git a/app/imports/ui/properties/viewers/ClassViewer.vue b/app/imports/ui/properties/viewers/ClassViewer.vue index 44322a52..d474faec 100644 --- a/app/imports/ui/properties/viewers/ClassViewer.vue +++ b/app/imports/ui/properties/viewers/ClassViewer.vue @@ -47,24 +47,20 @@ :cols="{cols: 12}" > mdi-plus - Get Missing Levels - - - - mdi-plus - - Level Up + + Get Missing Levels + + + Level Up +
+ {{ slotPropertyTypeName }} with tags: + + +