Backend work to support actions consuming their resources on use
This commit is contained in:
@@ -20,6 +20,8 @@ import {
|
||||
import {setDocToLastOrder} from '/imports/api/parenting/order.js';
|
||||
import { storedIconsSchema } from '/imports/api/icons/Icons.js';
|
||||
|
||||
import '/imports/api/creature/actions/doAction.js';
|
||||
|
||||
let CreatureProperties = new Mongo.Collection('creatureProperties');
|
||||
|
||||
let CreaturePropertySchema = new SimpleSchema({
|
||||
@@ -56,7 +58,7 @@ for (let key in propertySchemasIndex){
|
||||
});
|
||||
}
|
||||
|
||||
function getCreature(property){
|
||||
export function getCreature(property){
|
||||
if (!property) throw new Meteor.Error('No property provided');
|
||||
let creature = Creatures.findOne(property.ancestors[0].id);
|
||||
if (!creature) throw new Meteor.Error('Creature does not exist');
|
||||
@@ -231,6 +233,37 @@ const updateProperty = new ValidatedMethod({
|
||||
},
|
||||
});
|
||||
|
||||
export function damagePropertyWork({property, operation, value}){
|
||||
if (operation === 'set'){
|
||||
let currentValue = property.value;
|
||||
// Set represents what we want the value to be after damage
|
||||
// So we need the actual damage to get to that value
|
||||
let damage = currentValue - value;
|
||||
// Damage can't exceed total value
|
||||
if (damage > currentValue) damage = currentValue;
|
||||
// Damage must be positive
|
||||
if (damage < 0) damage = 0;
|
||||
CreatureProperties.update(property._id, {
|
||||
$set: {damage}
|
||||
}, {
|
||||
selector: property
|
||||
});
|
||||
} else if (operation === 'increment'){
|
||||
let currentValue = property.value - (property.damage || 0);
|
||||
let currentDamage = property.damage;
|
||||
let increment = value;
|
||||
// Can't increase damage above the remaining value
|
||||
if (increment > currentValue) increment = currentValue;
|
||||
// Can't decrease damage below zero
|
||||
if (-increment > currentDamage) increment = -currentDamage;
|
||||
CreatureProperties.update(property._id, {
|
||||
$inc: {damage: increment}
|
||||
}, {
|
||||
selector: property
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const damageProperty = new ValidatedMethod({
|
||||
name: 'creatureProperties.damage',
|
||||
validate: new SimpleSchema({
|
||||
@@ -258,38 +291,39 @@ const damageProperty = new ValidatedMethod({
|
||||
`Property of type "${currentProperty.type}" can't be damaged`
|
||||
);
|
||||
}
|
||||
if (operation === 'set'){
|
||||
let currentValue = currentProperty.value;
|
||||
// Set represents what we want the value to be after damage
|
||||
// So we need the actual damage to get to that value
|
||||
let damage = currentValue - value;
|
||||
// Damage can't exceed total value
|
||||
if (damage > currentValue) damage = currentValue;
|
||||
// Damage must be positive
|
||||
if (damage < 0) damage = 0;
|
||||
CreatureProperties.update(_id, {
|
||||
$set: {damage}
|
||||
}, {
|
||||
selector: currentProperty
|
||||
});
|
||||
} else if (operation === 'increment'){
|
||||
let currentValue = currentProperty.value - (currentProperty.damage || 0);
|
||||
let currentDamage = currentProperty.damage;
|
||||
let increment = value;
|
||||
// Can't increase damage above the remaining value
|
||||
if (increment > currentValue) increment = currentValue;
|
||||
// Can't decrease damage below zero
|
||||
if (-increment > currentDamage) increment = -currentDamage;
|
||||
CreatureProperties.update(_id, {
|
||||
$inc: {damage: increment}
|
||||
}, {
|
||||
selector: currentProperty
|
||||
});
|
||||
}
|
||||
damagePropertyWork({property: currentProperty, operation, value})
|
||||
recomputeCreatures(currentProperty);
|
||||
},
|
||||
});
|
||||
|
||||
export function adjustQuantityWork({property, operation, value}){
|
||||
// Check if property has quantity
|
||||
let schema = CreatureProperties.simpleSchema(property);
|
||||
if (!schema.allowsKey('quantity')){
|
||||
throw new Meteor.Error(
|
||||
'Adjust quantity failed',
|
||||
`Property of type "${property.type}" doesn't have a quantity`
|
||||
);
|
||||
}
|
||||
if (operation === 'set'){
|
||||
CreatureProperties.update(property._id, {
|
||||
$set: {quantity: value}
|
||||
}, {
|
||||
selector: property
|
||||
});
|
||||
} else if (operation === 'increment'){
|
||||
// value here is 'damage'
|
||||
value = -value;
|
||||
let currentQuantity = property.quantity;
|
||||
if (currentQuantity + value < 0) value = -currentQuantity;
|
||||
CreatureProperties.update(property._id, {
|
||||
$inc: {quantity: value}
|
||||
}, {
|
||||
selector: property
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const adjustQuantity = new ValidatedMethod({
|
||||
name: 'creatureProperties.adjustQuantity',
|
||||
validate: new SimpleSchema({
|
||||
@@ -309,31 +343,7 @@ const adjustQuantity = new ValidatedMethod({
|
||||
let currentProperty = CreatureProperties.findOne(_id);
|
||||
// Check permissions
|
||||
assertPropertyEditPermission(currentProperty, this.userId);
|
||||
// Check if property can take damage
|
||||
let schema = CreatureProperties.simpleSchema(currentProperty);
|
||||
if (!schema.allowsKey('quantity')){
|
||||
throw new Meteor.Error(
|
||||
'Adjust quantity failed',
|
||||
`Property of type "${currentProperty.type}" doesn't have a quantity`
|
||||
);
|
||||
}
|
||||
if (operation === 'set'){
|
||||
CreatureProperties.update(_id, {
|
||||
$set: {quantity: value}
|
||||
}, {
|
||||
selector: currentProperty
|
||||
});
|
||||
} else if (operation === 'increment'){
|
||||
// value here is 'damage'
|
||||
value = -value;
|
||||
let currentQuantity = currentProperty.quantity;
|
||||
if (currentQuantity + value < 0) value = -currentQuantity;
|
||||
CreatureProperties.update(_id, {
|
||||
$inc: {quantity: value}
|
||||
}, {
|
||||
selector: currentProperty
|
||||
});
|
||||
}
|
||||
adjustQuantityWork({property: currentProperty, operation, value})
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
89
app/imports/api/creature/actions/doAction.js
Normal file
89
app/imports/api/creature/actions/doAction.js
Normal file
@@ -0,0 +1,89 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import { ValidatedMethod } from 'meteor/mdg:validated-method';
|
||||
import { RateLimiterMixin } from 'ddp-rate-limiter-mixin';
|
||||
import CreatureProperties, { getCreature, damagePropertyWork, adjustQuantityWork } from '/imports/api/creature/CreatureProperties.js';
|
||||
import { assertEditPermission } from '/imports/api/creature/creaturePermissions.js';
|
||||
import { recomputeCreatureByDoc } from '/imports/api/creature/computation/recomputeCreature.js';
|
||||
|
||||
const doAction = new ValidatedMethod({
|
||||
name: 'creatureProperties.doAction',
|
||||
validate: new SimpleSchema({
|
||||
actionId: SimpleSchema.RegEx.Id,
|
||||
}).validator(),
|
||||
mixins: [RateLimiterMixin],
|
||||
rateLimit: {
|
||||
numRequests: 10,
|
||||
timeInterval: 5000,
|
||||
},
|
||||
run({actionId}) {
|
||||
let action = CreatureProperties.findOne(actionId);
|
||||
// Check permissions
|
||||
let creature = getCreature(action);
|
||||
assertEditPermission(creature, this.userId);
|
||||
doActionWork(action);
|
||||
// Note this only recomputes the top-level creature, not the nearest one
|
||||
recomputeCreatureByDoc(creature);
|
||||
},
|
||||
});
|
||||
|
||||
function doActionWork(action){
|
||||
spendResources(action);
|
||||
}
|
||||
|
||||
function spendResources(action){
|
||||
// Check Uses
|
||||
if (action.usesUsed >= action.usesResult){
|
||||
throw new Meteor.Error('Insufficient Uses',
|
||||
'This action has no uses left');
|
||||
}
|
||||
// Resources
|
||||
if (action.insufficientResources){
|
||||
throw new Meteor.Error('Insufficient Resources',
|
||||
'This creature doesn\'t have sufficient resources to perform this action');
|
||||
}
|
||||
// Items
|
||||
let itemQuantityAdjustments = [];
|
||||
action.resources.itemsConsumed.forEach(itemConsumed => {
|
||||
if (!itemConsumed.itemId){
|
||||
throw new Meteor.Error('Ammo not selected',
|
||||
'No ammo was selected for this action');
|
||||
}
|
||||
let item = CreatureProperties.findOne(itemConsumed.itemId);
|
||||
if (!item || item.ancestors[0].id !== action.ancestors[0].id){
|
||||
throw new Meteor.Error('Ammo not found',
|
||||
'The action\'s ammo was not found on the creature');
|
||||
}
|
||||
if (!item.equipped){
|
||||
throw new Meteor.Error('Ammo not equipped',
|
||||
'The selected ammo is not equipped');
|
||||
}
|
||||
if (!itemConsumed.quantity) return;
|
||||
itemQuantityAdjustments.push({
|
||||
property: item,
|
||||
operation: 'increment',
|
||||
value: itemConsumed.quantity,
|
||||
});
|
||||
});
|
||||
// No more errors should be thrown after this line
|
||||
// Now that we have confirmed that there are no errors, do actual work
|
||||
//Items
|
||||
itemQuantityAdjustments.forEach(adjustQuantityWork);
|
||||
// Use uses
|
||||
CreatureProperties.update(action._id, {
|
||||
$inc: {usesUsed: 1}
|
||||
}, {
|
||||
selector: action
|
||||
});
|
||||
// Damage stats
|
||||
action.resources.attributesConsumed.forEach(attConsumed => {
|
||||
if (!attConsumed.quantity) return;
|
||||
let stat = CreatureProperties.findOne(attConsumed.statId);
|
||||
damagePropertyWork({
|
||||
property: stat,
|
||||
operation: 'increment',
|
||||
value: attConsumed.quantity,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
export default doAction;
|
||||
@@ -20,6 +20,7 @@ export default function computeEndStepProperty(prop, memo){
|
||||
}
|
||||
|
||||
function computeAction(prop, memo){
|
||||
// Uses
|
||||
let {value, errors} = evaluateCalculation(prop.uses, memo);
|
||||
prop.usesResult = value;
|
||||
if (errors.length){
|
||||
@@ -27,10 +28,28 @@ function computeAction(prop, memo){
|
||||
} else {
|
||||
delete prop.usesErrors;
|
||||
}
|
||||
// TODO compute resources.$.$.available and insufficientResources
|
||||
prop.insufficientResources = undefined;
|
||||
if (prop.usesUsed >= prop.usesResult){
|
||||
prop.insufficientResources = true;
|
||||
}
|
||||
// Attributes consumed
|
||||
prop.resources.attributesConsumed.forEach((attConsumed, i) => {
|
||||
if (attConsumed.variableName){
|
||||
let stat = memo.statsByVariableName[attConsumed.variableName];
|
||||
prop.resources.attributesConsumed[i].statId = stat && stat._id;
|
||||
let available = stat && stat.currentValue || 0;
|
||||
prop.resources.attributesConsumed[i].available = available;
|
||||
if (available < attConsumed.quantity){
|
||||
prop.insufficientResources = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
// Items consumed
|
||||
// TODO
|
||||
}
|
||||
|
||||
function computeAttack(prop, memo){
|
||||
// Roll bonus
|
||||
let {value, errors} = evaluateCalculation(prop.rollBonus, memo);
|
||||
prop.rollBonusResult = value;
|
||||
if (errors.length){
|
||||
|
||||
@@ -91,7 +91,7 @@ export function recomputeCreatureById(creatureId){
|
||||
* - Mark the stat as computed
|
||||
* - Write the computed results back to the database
|
||||
*/
|
||||
function recomputeCreatureByDoc(creature){
|
||||
export function recomputeCreatureByDoc(creature){
|
||||
const creatureId = creature._id;
|
||||
let props = getActiveProperties({
|
||||
ancestorId: creatureId,
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
import SimpleSchema from 'simpl-schema';
|
||||
import ResourcesSchema from '/imports/api/properties/subSchemas/ResourcesSchema.js'
|
||||
import ErrorSchema from '/imports/api/properties/subSchemas/ErrorSchema.js';
|
||||
|
||||
/*
|
||||
@@ -41,13 +40,60 @@ let ActionSchema = new SimpleSchema({
|
||||
'tags.$': {
|
||||
type: String,
|
||||
},
|
||||
// Duplicate the ResourceSchema here so we can extend it elegantly.
|
||||
resources: {
|
||||
type: ResourcesSchema,
|
||||
type: Object,
|
||||
defaultValue: {},
|
||||
},
|
||||
'resources.itemsConsumed': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
},
|
||||
'resources.itemsConsumed.$': {
|
||||
type: Object,
|
||||
},
|
||||
'resources.itemsConsumed.$._id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue(){
|
||||
if (!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
'resources.itemsConsumed.$.tag': {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
'resources.itemsConsumed.$.quantity': {
|
||||
type: Number,
|
||||
defaultValue: 1,
|
||||
},
|
||||
'resources.itemsConsumed.$.itemId': {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
'resources.attributesConsumed': {
|
||||
type: Array,
|
||||
defaultValue: [],
|
||||
},
|
||||
'resources.attributesConsumed.$': {
|
||||
type: Object,
|
||||
},
|
||||
'resources.attributesConsumed.$._id': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
autoValue(){
|
||||
if (!this.isSet) return Random.id();
|
||||
}
|
||||
},
|
||||
'resources.attributesConsumed.$.variableName': {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
'resources.attributesConsumed.$.quantity': {
|
||||
type: Number,
|
||||
defaultValue: 1,
|
||||
},
|
||||
// Calculation of how many times this action can be used
|
||||
// Only set if this action tracks its own uses, rather than adjusting
|
||||
// resources
|
||||
uses: {
|
||||
type: String,
|
||||
optional: true,
|
||||
@@ -84,12 +130,23 @@ const ComputedOnlyActionSchema = new SimpleSchema({
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
// This appears both in the computed and uncomputed schema because it can be
|
||||
// set by both a computation or a form
|
||||
'resources.itemsConsumed.$.itemId': {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
'resources.attributesConsumed': Array,
|
||||
'resources.attributesConsumed.$': Object,
|
||||
'resources.attributesConsumed.$.available': {
|
||||
type: Number,
|
||||
optional: true,
|
||||
},
|
||||
'resources.attributesConsumed.$.statId': {
|
||||
type: String,
|
||||
regEx: SimpleSchema.RegEx.Id,
|
||||
optional: true,
|
||||
},
|
||||
insufficientResources: {
|
||||
type: Boolean,
|
||||
optional: true,
|
||||
|
||||
@@ -17,6 +17,10 @@ const ItemConsumedSchema = new SimpleSchema({
|
||||
type: Number,
|
||||
defaultValue: 1,
|
||||
},
|
||||
itemId: {
|
||||
type: String,
|
||||
optional: true,
|
||||
},
|
||||
});
|
||||
|
||||
export default ItemConsumedSchema;
|
||||
|
||||
@@ -247,24 +247,15 @@
|
||||
</div>
|
||||
|
||||
<div
|
||||
v-if="actions.length"
|
||||
v-for="action in actions"
|
||||
:key="action._id"
|
||||
class="actions"
|
||||
>
|
||||
<v-card>
|
||||
<v-list
|
||||
two-line
|
||||
subheader
|
||||
>
|
||||
<v-subheader>Actions</v-subheader>
|
||||
<action-list-tile
|
||||
v-for="action in actions"
|
||||
:key="action._id"
|
||||
:model="action"
|
||||
:data-id="action._id"
|
||||
@click="clickProperty({_id: action._id})"
|
||||
/>
|
||||
</v-list>
|
||||
</v-card>
|
||||
<action-card
|
||||
:model="action"
|
||||
:data-id="action._id"
|
||||
@click="clickProperty({_id: action._id})"
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
v-if="attacks.length"
|
||||
@@ -304,6 +295,7 @@
|
||||
import ResourceCard from '/imports/ui/properties/components/attributes/ResourceCard.vue';
|
||||
import SpellSlotListTile from '/imports/ui/properties/components/attributes/SpellSlotListTile.vue';
|
||||
import ActionListTile from '/imports/ui/properties/components/actions/ActionListTile.vue';
|
||||
import ActionCard from '/imports/ui/properties/components/actions/ActionCard.vue';
|
||||
import RestButton from '/imports/ui/creature/RestButton.vue';
|
||||
import getActiveProperties from '/imports/api/creature/getActiveProperties.js';
|
||||
|
||||
@@ -346,6 +338,7 @@
|
||||
ResourceCard,
|
||||
SpellSlotListTile,
|
||||
ActionListTile,
|
||||
ActionCard,
|
||||
},
|
||||
props: {
|
||||
creatureId: {
|
||||
|
||||
@@ -160,6 +160,7 @@ export default {
|
||||
});
|
||||
},
|
||||
push({path, value, ack}){
|
||||
console.log({path, value, ack});
|
||||
pushToProperty.call({_id: this._id, path, value}, (error) =>{
|
||||
if (error) console.warn(error);
|
||||
ack && ack(error && error.reason || error);
|
||||
|
||||
110
app/imports/ui/properties/components/actions/ActionCard.vue
Normal file
110
app/imports/ui/properties/components/actions/ActionCard.vue
Normal file
@@ -0,0 +1,110 @@
|
||||
<template lang="html">
|
||||
<v-card
|
||||
class="action"
|
||||
@click="$emit('click')"
|
||||
>
|
||||
<v-card-title
|
||||
primary-title
|
||||
class="layout row pa-2"
|
||||
>
|
||||
<div class="avatar">
|
||||
<v-btn
|
||||
flat
|
||||
icon
|
||||
class="headline"
|
||||
:disabled="model.insufficientResources"
|
||||
@click.stop="doAction"
|
||||
>
|
||||
<template v-if="rollBonus">
|
||||
{{ rollBonus }}
|
||||
</template>
|
||||
<v-icon v-else>
|
||||
$vuetify.icons.action
|
||||
</v-icon>
|
||||
</v-btn>
|
||||
</div>
|
||||
<div class="action-header flex">
|
||||
<div class="action-title">
|
||||
{{ model.name }}
|
||||
</div>
|
||||
<div class="action-type">
|
||||
action type text
|
||||
</div>
|
||||
</div>
|
||||
<div class="action-uses">
|
||||
{{ usesLeft }}/{{ totalUses }}
|
||||
</div>
|
||||
</v-card-title>
|
||||
<v-card-text
|
||||
v-if="childText"
|
||||
class="action-details"
|
||||
v-html="childText"
|
||||
/>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
|
||||
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
|
||||
import doAction from '/imports/api/creature/actions/doAction.js';
|
||||
|
||||
export default {
|
||||
inject: {
|
||||
context: {
|
||||
default: {},
|
||||
},
|
||||
},
|
||||
props: {
|
||||
model: {
|
||||
type: Object,
|
||||
required: true,
|
||||
},
|
||||
attack: {
|
||||
type: Boolean,
|
||||
},
|
||||
},
|
||||
computed: {
|
||||
hasClickListener(){
|
||||
return this.$listeners && this.$listeners.click
|
||||
},
|
||||
rollBonus(){
|
||||
return numberToSignedString(this.model.rollBonusResult);
|
||||
},
|
||||
childText(){
|
||||
let scope = this.context.creature && this.context.creature.variables;
|
||||
if (!this.model.children || !this.model.children.length) return;
|
||||
let textArray = [];
|
||||
this.model.children.forEach(child => {
|
||||
if (child.type === 'damage'){
|
||||
let { result } = evaluateString(child.amount, scope);
|
||||
textArray.push(`${result} ${child.damageType}`);
|
||||
} else if (child.type === 'savingThrow'){
|
||||
textArray.push(`DC ${child.dcResult} ${child.name}`);
|
||||
}
|
||||
});
|
||||
return textArray.join(' ');
|
||||
},
|
||||
totalUses(){
|
||||
return Math.max(this.model.usesResult, 0);
|
||||
},
|
||||
usesLeft(){
|
||||
return Math.max(this.model.usesResult - this.model.usesUsed, 0);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
click(e){
|
||||
this.$emit('click', e);
|
||||
},
|
||||
doAction(){
|
||||
doAction.call({actionId: this.model._id}, error => {
|
||||
if (error){
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="css" scoped>
|
||||
</style>
|
||||
@@ -3,12 +3,19 @@
|
||||
class="ability-list-tile"
|
||||
v-on="hasClickListener ? {click} : {}"
|
||||
>
|
||||
<v-list-tile-avatar
|
||||
<v-list-tile-action
|
||||
v-if="attack"
|
||||
class="headline"
|
||||
>
|
||||
{{ rollBonus }}
|
||||
</v-list-tile-avatar>
|
||||
<v-btn
|
||||
flat
|
||||
icon
|
||||
class="headline"
|
||||
:disabled="model.insufficientResources"
|
||||
@click.stop="doAction"
|
||||
>
|
||||
{{ rollBonus }}
|
||||
</v-btn>
|
||||
</v-list-tile-action>
|
||||
<v-list-tile-content>
|
||||
<v-list-tile-title>
|
||||
{{ model.name }}
|
||||
@@ -20,7 +27,7 @@
|
||||
</v-list-tile-content>
|
||||
<v-list-tile-action v-if="model.usesResult">
|
||||
<v-list-tile-action-text>
|
||||
{{ model.usesResult - (model.usesUsed) }}/{{ model.usesResult }}
|
||||
{{ usesLeft }}/{{ totalUses }}
|
||||
</v-list-tile-action-text>
|
||||
</v-list-tile-action>
|
||||
</v-list-tile>
|
||||
@@ -29,6 +36,7 @@
|
||||
<script>
|
||||
import numberToSignedString from '/imports/ui/utility/numberToSignedString.js';
|
||||
import evaluateString from '/imports/api/creature/computation/afterComputation/evaluateString.js';
|
||||
import doAction from '/imports/api/creature/actions/doAction.js';
|
||||
|
||||
export default {
|
||||
inject: {
|
||||
@@ -65,12 +73,25 @@ export default {
|
||||
}
|
||||
});
|
||||
return textArray.join(' ');
|
||||
},
|
||||
totalUses(){
|
||||
return Math.max(this.model.usesResult, 0);
|
||||
},
|
||||
usesLeft(){
|
||||
return Math.max(this.model.usesResult - this.model.usesUsed, 0);
|
||||
},
|
||||
},
|
||||
methods: {
|
||||
click(e){
|
||||
this.$emit('click', e);
|
||||
},
|
||||
doAction(){
|
||||
doAction.call({actionId: this.model._id}, error => {
|
||||
if (error){
|
||||
console.error(error);
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -83,6 +83,7 @@
|
||||
this.addResourceLoading = false;
|
||||
},
|
||||
addAttributesConsumed(){
|
||||
console.log(AttributeConsumedSchema.clean({}));
|
||||
this.addResourceLoading = true;
|
||||
this.$emit('push', {
|
||||
path: ['attributesConsumed'],
|
||||
|
||||
Reference in New Issue
Block a user