FullCalendar doesn't work in BuildYourOwn(LWR) - salesforce

I would like to use FullCalendar.js in BuildYourOwn(LWR) but it doesn't work in there.
I confirmed following facts.
FulCalendar.js(v3) worked in LightningRecordPage.
FulCalendar.js(v3) didn't work in BuildYourOwn(LWR), with occuring error "Cannot read properties of undefined (reading 'indexOf')".
FullCalenar.js(v4,v5) didn't work in both BuildYourOwn(LWR) and LightningRecordPage.
An error "FullCalendar is not defined" occured when using FullCalenar.js(v4,v5) in both BuildYourOwn(LWR) and LightningRecordPage.
my code is here. This code use FullCalendar.js(v3).
Could you tell me why doesn't it work?
import { LightningElement, track } from 'lwc';
import { loadScript, loadStyle } from 'lightning/platformResourceLoader';
import FullCalendarJS from '#salesforce/resourceUrl/FullCalendarJS';
import getTourApplication from '#salesforce/apex/OppoCloseDate.getTourApplication';
export default class FullCalendar extends LightningElement {
#track returnedApps = [] ;
#track reserveList = [] ;
renderedCallback() {
Promise.all([
loadScript(this, FullCalendarJS + '/FullCalendarJS/jquery.min.js'),
loadScript(this, FullCalendarJS + '/FullCalendarJS/moment.min.js'),
loadScript(this, FullCalendarJS + '/FullCalendarJS/fullcalendar.min.js'),
loadStyle(this, FullCalendarJS + '/FullCalendarJS/fullcalendar.min.css'),
])
.then(() => {
this.getTasks();
})
.catch(error => {
console.error({
message: 'Not Loading FullCalendarJS',
error
});
})
}
initialiseFullCalendarJs() {
const ele = this.template.querySelector('div.fullcalendarjs');
$(ele).fullCalendar({
// fullcaledar options
});
}
getTasks(){
// get data from Salesforce
}
}

Related

QR shaded region not coming in Html5QrcodeScanner

I'm trying to build the barcode scanner using the html5-qrcode npm package in react js, everything working fine except QR shaded region. I added the qrbox in the configuration but not getting the QR shaded region. The qrbox width and height are not taken. I tried multiple ways not getting the desired result. Thanks in advance.
Here is my component:
import { Html5QrcodeScanner, Html5Qrcode } from "html5-qrcode";
import React from 'react';
const qrcodeRegionId = "html5qr-code-full-region";
class Html5QrcodePlugin extends React.Component {
render() {
return <div id={qrcodeRegionId} />;
}
componentWillUnmount() {
this.html5QrcodeScanner.clear().catch(error => {
console.error("Failed to clear html5QrcodeScanner. ", error);
});
}
componentDidMount() {
function createConfig(props) {
var config = {};
if (props.fps) {
config.fps = props.fps;
}
config.qrbox = { width: 250, height: 250 };
if (props.aspectRatio) {
config.aspectRatio = props.aspectRatio;
}
if (props.disableFlip !== undefined) {
config.disableFlip = props.disableFlip;
}
return config;
}
var config = createConfig(this.props);
var verbose = this.props.verbose === true;
if (!(this.props.qrCodeSuccessCallback )) {
throw "qrCodeSuccessCallback is required callback.";
}
this.html5QrcodeScanner = new Html5QrcodeScanner(
qrcodeRegionId, { facingMode: { exact: "environment"} }, config, verbose);
this.html5QrcodeScanner.render(
this.props.qrCodeSuccessCallback,
this.props.qrCodeErrorCallback);
}
};
export default Html5QrcodePlugin;

TypeError: Cannot read property 'method' of undefined using react router (express)

I have the following class using React with express.
I'm trying to call another method from the method getOne which is declared in a route. But when I call the route for method getOne I have this error:
TypeError: Cannot read property 'createFromSystem' of undefined
at /home/joao/tcc/efono-api/src/controllers/ContrastiveAnalysis/phonetic_inventory.controller.ts:193:42
at Generator.next (<anonymous>)
at fulfilled (/home/joao/tcc/efono-api/src/controllers/ContrastiveAnalysis/phonetic_inventory.controller.ts:5:58)
at process._tickCallback (internal/process/next_tick.js:68:7)
How can I solve this problem? Anyone can help? I already try another things without sucess. I'm newby with react.
The class:
import { Router, Response } from 'express';
import { AuthenticatedRequest } from '#interfaces/AuthenticatedRequest';
import authenticationMiddleware from '#middlewares/authentication.middleware';
import authorizationMiddleware from '#middlewares/authorization.middleware';
import validationMiddleware from '#middlewares/validation.middleware';
import asyncWrapper from '#utils/asyncWrapper';
import Controller from '#interfaces/Controller';
import PhoneticInventoryModel, { PhoneticInventory, Plosiva, Fricativa, Africada, Nasal, Glide, Outro, LiquidaLateral, LiquidaNaoLateral, PhoneticInventoryClass } from '#models/ContrastiveAnalysis/phonetic_inventory.model';
import PhonemaRealizatonModel, { PhonemaRealization } from '#models/ContrastiveAnalysis/phonema_realization.model';
import { Role } from '#models/user.model';
import { NotFoundException } from '#exceptions/NotFoundException';
class PhoneticInventoryController implements Controller {
public path = '/phoneticInventory';
public router = Router();
constructor() {
this.initializeRoutes();
// This binding is necessary to make `this` work in the callback
this.createFromSystem = this.createFromSystem.bind(this);
}
private initializeRoutes() {
this.router.get(
`${this.path}/getOne/:evaluationId/:targetWordId`,
asyncWrapper(authenticationMiddleware),
asyncWrapper(this.getOne)
);
}
private async getOne(request: AuthenticatedRequest<any>, response: Response) {
const evaluationId = request.params.evaluationId;
const targetWordId = request.params.targetWordId;
if (!evaluationId || !targetWordId) {
return Promise.reject(new NotFoundException());
}
const evaluation = await PhoneticInventoryModel.findOne({ evaluationId, targetWordId });
if (!evaluation) {
const otherEval = await this.createFromSystem(evaluationId, targetWordId);
if (!otherEval) {
return Promise.reject(new NotFoundException());
}
response.status(200).send(otherEval);
} else {
response.status(200).send(evaluation);
}
}
private async createFromSystem(evaluationId: string, targetWordId: string): Promise<PhoneticInventory> {
console.log("Calculating phonetic inventory for " + evaluationId + " (" + targetWordId + ")...");
if (!evaluationId || !targetWordId) {
return null;
}
const phonemaRealization = await PhonemaRealizatonModel.findOne({ evaluationId, targetWordId });
if (!phonemaRealization) {
return null;
}
const phoneticInventory: PhoneticInventory = {
plosiva, fricativa, africada, nasal, glide, outro, liquida_lateral, liquida_nao_lateral
};
return phoneticInventory;
}
}
export default PhoneticInventoryController;
Please bind getOne function
constructor() {
this.initializeRoutes();
// This binding is necessary to make `this` work in the callback
this.createFromSystem = this.createFromSystem.bind(this);
this.getOne = this.getOne.bind(this);
}
or please use the arrow function.
private getOne = async(request: AuthenticatedRequest<any>, response: Response) => {
...
}
private createFromSystem = async (evaluationId: string, targetWordId: string): Promise<PhoneticInventory> => {
...
}

Google Apps Script & React.js : DELETE https://mygoogleappapi.com/exec 405 (Method Not Allowed)

Thank you for reading!
I am learning how to use GAS now,
I can't delete the specific row I selected on google spread sheet.
I got the theme error after trying to delete using "axios.delete method" when I used react app and google script api.
I already passed GET method and POST method using axios. Actually , I could get and post my data from my google spread sheet.
but deleting could not access well.
I found this error 405 is not allowed to access my google sheet, but Why can I get this error even though the post method was accessible?
My App script or My react.js code need to have any code else ? I can't solve this problem...
I want to solve this error and delete the specific row I selected. Also, I want to know a workaround for this error.
Do you have any idea ? If you have some good idea,Could you tell me please ?
Thank you for reading.
this is my App script code.
function doDelete(req, sheet) {
var id = req.parameter.id;
var Row = sheet.getLastRow();
for (var i = 1; i <= Row; i++) {
var idTemp = sheet.getRange(i, 1).getValue();
if (idTemp == id) {
sheet.deleteRow(i);
}
}
}
this is my reactjs code.
import React,{ useState , Component } from 'react';
import Paper from '#material-ui/core/Paper';
import Grid from '#material-ui/core/Grid';
import axios from 'axios';
axios.defaults.baseURL = 'http://localhost:3000';
var optionAxios = {
headers: {
'Content-Type': 'application/json;charset=utf-8',
'Access-Control-Allow-Origin':'*' ,
}
}
const api = 'https://mygoogleappscriptapi.com/exec';
class Price extends Component {
constructor(){
super();
this.state = {
info: []
};
this.getInfo();
this.createInfo = this.createInfo.bind(this);
this.deleteInfo = this.deleteInfo.bind(this);
};
// accessed get!
getInfo = () =>{
axios.get(api)
.then((res) =>{
console.log(res.data)
this.setState({
info: res.data
})
})
}
// accessed post!
createInfo = () =>{
axios.post(api,{
product: "hoge",
price: 1000,
miniLot: 1000,
cartonSize: "40*30*50"
},optionAxios)
.then((res) => {
this.getInfo(res);
})
}
// cant't access delete!
deleteInfo = (e) => {
console.log(e);
axios.delete(api,{
id: e,
},optionAxios)
.then((res) =>{
this.getInfo(res);
console.log('success!');
})
}
render(){
return (
<div className={this.root}>
<Grid container>
<Grid item xs={11}>
<button onClick={this.createInfo}>createButon</button>
<Paper>
{this.state.info.map(info => <div key={info.id}>
{info.product}
<button onClick={() => this.deleteInfo(info.id)}>×</button>
</div>)}
</Paper>
</Grid>
</Grid>
</div>
);
}
}
export default Price;
Only the following HTTP methods are supported:
POST
GET
DELETE method is not supported by google-apps-script-web-application.
You can use post:
Server side:
function doPost(e){
if(e.parameter.option === "DELETE") return doDelete(e);
/*rest of doPost here*/
}
React:
// convert to axios.post
deleteInfo = (e) => {
console.log(e);
axios.post(api,{//modified
id: e,
option: "DELETE",//added
},optionAxios)
.then((res) =>{
this.getInfo(res);
console.log('success!');
})
}
Try this:
function doDelete(req, sh) {
var id = req.parameter.id;
const ss=SpreadsheetApp.getActive();
sh=sh||ss.getActiveSheet();
var vs=sh.getRange(1,1,sh.getLastRow(),1).getValues();
var d=0;
for (var i=0;i<vs.length;i++) {
if (vs[i][0]== id) {
sh.deleteRow(i+1-d++);
}
}
}

Observable to Array *ngFor saying undefined

I am new to Angular. I have a Node and Express backend pulling data from an MS SQL database. If I go to the endpoint URL it displays my data as JSON. I am running on localhost so I set a proxy for CORS. I have a class that defines the data, a service that pulls the data from the endpoint and a component that tries to set an array equal to the data pulled from the service. The HTML has an *ngFor that is supposed to loop through the values and display them in a grid.
If I call my data in my component through my service, so this.userService.getUsers(), and do a console.log I can see the recordset in the browser console. I try to set the array equal to the userService.getUsers() and then call the array and I get "undefined". Being that I am new, I have tried to follow the Heroes tutorial and that did not work. I spent a day searching Google and trying different solutions that I have come across but they all come up as undefined. I will attach the code here. If someone can guide me a bit, it would be much appreciated.
User class defining User:
export class User{
id: number;
ccn: string;
firstName: string;
lastName: string;
email: string;
}
User Service doing Http request:
import { Injectable } from '#angular/core';
import { User } from './user';
import { USERS } from './mock-users';
import { MessageService } from './message.service';
import { Observable, of } from 'rxjs';
import { HttpClient, HttpHeaders } from '#angular/common/http';
import { catchError, map, tap } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class UserService {
private userURL = 'api/users'
//private userURL = 'localhost:5000'
httpOptions = {
headers: new HttpHeaders({ 'Content-Type': 'application/json' })
};
constructor(
private http: HttpClient,
private messageService: MessageService) { }
//getUsers(): Observable<User[]> {
// this.messageService.add('UserService: fetched users');
// return of(USERS);
//}
/** GET users from the server */
getUsers(): Observable<User[]> {
//console.log('getting users');
return this.http.get<User[]>("http://localhost:5000/api/user")
.pipe(
tap(_ => this.log('Fetched users')),
catchError(this.handleError<User[]>('getUsers', []))
);
//return this.http.get<User[]>("http://localhost:5000/api/user");
//console.log('got users');
}
/* GET heroes whose name contains search term */
searchUsers(term: string): Observable<User[]> {
if (!term.trim()) {
// if not search term, return empty hero array.
return of([]);
}
return this.http.get<User[]>(`${this.userURL}/?ccn=${term}`).pipe(
tap(_ => this.log(`found users matching "${term}"`)),
catchError(this.handleError<User[]>('searchUsers', []))
);
}
addUser (user: User): Observable<User> {
return this.http.post<User>(this.userURL, user, this.httpOptions).pipe(
tap((newUser: User) => this.log(`added user w/ id=${newUser.id}`)),
catchError(this.handleError<User>('addUser'))
);
}
private handleError<T> (operation = 'operation', result?: T) {
return (error: any): Observable<T> => {
console.error(error);
this.log(`${operation} failed: ${error.message}`);
return of(result as T);
};
}
private log(message: string) {
this.messageService.add(`User service: ${message}`);
}
}
Display Users Component TS file:
import { Component, OnInit } from '#angular/core';
//import { USERS } from '../mock-users';
import { UserService } from '../user.service';
import { User } from '../user';
import { Observable, of } from 'rxjs';
import { catchError, map, tap } from 'rxjs/operators';
import { element } from 'protractor';
#Component({
selector: 'app-display-users',
templateUrl: './display-users.component.html',
styleUrls: ['./display-users.component.css']
})
export class DisplayUsersComponent implements OnInit {
users: User[] = [];
constructor(private userService: UserService) { }
//users$ = this.getUsers();
ngOnInit() {
this.getUsers();
console.log(this.userService.getUsers());
this.userService.getUsers().forEach(element => {
console.log(element);
});
}
getUsers(): void {
/*this.userService.getUsers()
.subscribe(users => this.users = users);*/
const userObservable = this.userService.getUsers();
userObservable.subscribe((userData: User[]) => {
this.users = userData;
});
}
}
Display Users Component HTML:
<div class="clr-row">
<div class="clr-col-lg-11 clr-col-md-11 clr-col-11 main-div">
<div class="card card-style" style="box-shadow: 0 0 0 0;">
<div class="card-header">
<h1><img src="../assets/images/BSOLOGO_gray.png" class="title-img"><span class="title"> Users</span></h1>
</div>
<div class="card-block">
<div class="card-title">
<clr-datagrid>
<clr-dg-column>CCN</clr-dg-column>
<clr-dg-column>Last Name</clr-dg-column>
<clr-dg-column>First Name</clr-dg-column>
<clr-dg-column>Email</clr-dg-column>
<clr-dg-row *ngFor="let user of users">
<clr-dg-cell>{{user.ccn}}</clr-dg-cell>
<clr-dg-cell>{{user.lastName}}</clr-dg-cell>
<clr-dg-cell>{{user.firstName}}</clr-dg-cell>
<clr-dg-cell>{{user.email}}</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>{{users.length}} users</clr-dg-footer>
</clr-datagrid>
</div>
</div>
</div>
</div>
</div>
Any help would be greatly appreciated!
UPDATED
Ypu can replace getUsers on both classes by these. HTML looks fine to me. I converted users to public too.
//userService
getUsers(callback: Function) {
return this.http.get<User[]>("http://localhost:5000/api/user")
.subscribe(
response => callback(response)
);
}
//Component
public users: User[] = [];
getUsers(): void {
this.userService.getUsers((result) => {this.users = result;})
}
If you do not need it to be Observable you can use toPromise() and using async/await makes it waaay easier
Service
async getUsers(): Promise<User[]> {
return await this.http.get<User[]>('http://localhost:5000/api/user').toPromise();
}
Component.ts
users: User[] = [];
async ngOnInit() {
this.users = await this.userService.getUsers();
}
Component.html
<clr-datagrid *ngIf="users">
<clr-dg-column>CCN</clr-dg-column>
<clr-dg-column>Last Name</clr-dg-column>
<clr-dg-column>First Name</clr-dg-column>
<clr-dg-column>Email</clr-dg-column>
<clr-dg-row *ngFor="let user of users">
<clr-dg-cell>{{user.ccn}}</clr-dg-cell>
<clr-dg-cell>{{user.lastName}}</clr-dg-cell>
<clr-dg-cell>{{user.firstName}}</clr-dg-cell>
<clr-dg-cell>{{user.email}}</clr-dg-cell>
</clr-dg-row>
<clr-dg-footer>{{users.length}} users</clr-dg-footer>
</clr-datagrid>
My issue has been resolved. In my SQL statement I was calling SELECT * FROM table FOR JSON PATH which was creating a weird object being pulled from the server. Removing the FOR JSON PATH provided JSON data. Then the second part of my issue was mapping my DB fields with my user class.
This was done like this:
request.query('SELECT * FROM Table ORDER BY myField', function (err, recordset) {
if (err) console.log(err);
const records = recordset.recordset;
const result = records.map(r => { return { id: r.tableID, field1: r.dbField1, field2: r.dbField2, field3: r.dbField3, field4: r.dbField4}});
res.send(result);
});
I hope this helps someone! Thanks to everyone that posted to help me.

Redux mutation detected between dispatches

I'm having some trouble with a react redux I'm currently working on. I'm relatively new to Redux so maybe I'm missing a simple concept here but what I'm trying to do is build a deck building app for a card game and I want to be able to save the deck anytime a user adds or removes a card from their deck.
However, anytime I click add or remove I'm receiving the following error message while trying to dispatch an update action.
The error message reads as follows:
Uncaught Error: A state mutation was detected between dispatches, in the path `decks.0.cards.mainboard.0.quantity`. This may cause incorrect behavior.
My container component
import React, {PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import DeckMobileDisplay from './DeckMobileDisplay';
import * as deckActions from '../../actions/deckActions';
export class DeckEditorContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
deck: Object.assign({}, this.props.deck)
}
this.addCard = this.addCard.bind(this);
this.removeCard = this.removeCard.bind(this);
}
addCard(board, cardName) {
let deck = this.state.deck;
let cards = this.state.deck.cards;
cards[board].forEach(i => {
if(i.name === cardName)
i.quantity += 1;
});
const update = Object.assign(deck, cards);
this.props.deckActions.updateDeck(update).then(deck => {
console.log(deck);
})
.catch(err => {
console.log(err);
});
}
removeCard(board, cardName) {
let deck = this.state.deck;
let cards = this.state.deck.cards;
cards[board].forEach(i => {
if(i.name === cardName) {
if (i.quantity === 1) {
cards[board].splice(cards[board].indexOf(i), 1);
}
else {
i.quantity -= 1;
}
}
});
const update = Object.assign(deck, cards);
this.props.deckActions.updateDeck(update).then(deck => {
console.log(deck);
})
.catch(err => {
console.log(err);
});
}
render() {
const deck = Object.assign({}, this.props.deck);
return (
<div className="editor-container">
<DeckMobileDisplay
deck={deck}
addCard={this.addCard}
removeCard={this.removeCard}
/>
</div>
);
}
}
DeckEditorContainer.PropTypes = {
deck: PropTypes.object
};
function getDeckById(decks, id) {
const deck = decks.filter(deck => deck.id == id);
if (deck.length) return deck[0];
return null;
}
function mapStateToProps(state, ownProps) {
const deckId = ownProps.params.id;
let deck = {
id: '',
userId: '',
cards: []
}
if (state.decks.length > 0) {
deck = getDeckById(state.decks, deckId);
}
return {
deck: deck
};
}
function mapDispatchToProps(dispatch) {
return {
deckActions: bindActionCreators(deckActions, dispatch)
};
}
export default connect(mapStateToProps, mapDispatchToProps)(DeckEditorContainer);
Component for DeckMobileDisplay
import React, {PropTypes} from 'react';
import TabContainer from '../common/Tabs/TabContainer';
import Tab from '../common/Tabs/Tab';
import CardSearchContainer from '../CardSearch/CardSearchContainer';
import DeckList from './DeckList.js';
class DeckMobileDisplay extends React.Component {
render() {
return (
<TabContainer>
<Tab title="DeckList">
<DeckList
deck={this.props.deck}
addCard={this.props.addCard}
removeCard={this.props.removeCard}
/>
</Tab>
<Tab title="Search">
<CardSearchContainer
addCard={this.props.addCard}
removeCard={this.props.removeCard}
/>
</Tab>
<Tab title="Stats">
<p>stats coming soon...</p>
</Tab>
</TabContainer>
);
}
}
DeckMobileDisplay.propTypes = {
deck: PropTypes.object.isRequired,
addCard: PropTypes.func.isRequired,
removeCard: PropTypes.func.isRequired
}
export default DeckMobileDisplay;
Related Actions
export function createDeck(deck) {
return dispatch => {
dispatch(beginAjaxCall());
const config = {
method: 'POST',
headers: { 'Content-Type' : 'application/json' },
body : JSON.stringify({deck: deck})
};
return fetch(`http://localhost:3000/users/${deck.userId}/decks`, config)
.then(res => res.json().then(deck => ({deck, res})))
.then(({res, deck}) => {
if (res.status >= 200 && res.status < 300) {
dispatch(createDeckSuccess(deck.deck));
}
else
dispatch(createDeckFailure(deck));
})
.catch(err => {
console.log(err);
dispatch(ajaxCallError(err));
});
};
}
export function updateDeck(deck) {
return dispatch => {
dispatch(beginAjaxCall());
const body = JSON.stringify({deck: deck});
const config = {
method: 'PUT',
headers : { 'Content-Type' : 'application/json' },
body: body
};
return fetch(`http://localhost:3000/decks/${deck.id}`, config)
.then(res => res.json().then(deck => ({deck, res})))
.then(({res, deck}) => {
if (res.status >= 200 && res.status < 300) {
dispatch(updateDeckSuccess(deck.deck));
}
dispatch(ajaxCallError(err));
})
.catch(err => {
console.log(err);
dispatch(ajaxCallError(err));
});
};
}
export function updateDeckSuccess(deck) {
return {
type: types.UPDATE_DECK_SUCCESS,
deck
};
}
And my deck Reducer
import * as types from '../actions/actionTypes';
import initialState from './initialState';
export default function deckReducer(state = initialState.decks, action) {
switch (action.type) {
case types.LOAD_USERS_DECKS_SUCCESS:
return action.decks;
case types.CREATE_DECK_SUCCESS:
return [
...state,
Object.assign({}, action.deck)
]
case types.UPDATE_DECK_SUCCESS:
return [
...state.filter(deck => deck.id !== action.deck.id),
Object.assign({}, action.deck)
]
default:
return state;
}
}
If you need to see more of the app the repo is here:
https://github.com/dgravelle/magic-redux
Any kind of help would be appreciated, thanks!
Your problem is caused because you are modifying component's state manually.
One Redux's principle is:
State is read-only
The only way to change the state is to emit an action, an object
describing what happened.
This ensures that neither the views nor the network callbacks will
ever write directly to the state. Instead, they express an intent to
transform the state. Because all changes are centralized and happen
one by one in a strict order, there are no subtle race conditions to
watch out for. As actions are just plain objects, they can be logged,
serialized, stored, and later replayed for debugging or testing
purposes.
In the method removeCard you are modifying the state:
removeCard(board, cardName) {
let deck = this.state.deck;
//This is just a reference, not a clone
let cards = this.state.deck.cards;
cards[board].forEach(i => {
if(i.name === cardName) {
if (i.quantity === 1) {
//Here you are modifying cards, which is a pointer to this.state.deck.cards
cards[board].splice(cards[board].indexOf(i), 1);
}
else {
//Here you are modifying cards, which is a pointer to this.state.deck.cards
i.quantity -= 1;
}
}
});
//... more stuff
}
One concept you might be missing is that this.state.deck.cards is a reference/pointer to the Array's memory position. You need to clone it if you want to mutate it.
One solution could be to clone the original array instead:
removeCard(board, cardName) {
let deck = this.state.deck;
//Here you are cloning the original array, so cards references to a totally different memory position
let cards = Object.assign({}, this.state.deck.cards);
cards[board].forEach(i => {
if(i.name === cardName) {
if (i.quantity === 1) {
cards[board].splice(cards[board].indexOf(i), 1);
}
else {
i.quantity -= 1;
}
}
});
//... more stuff
}
Hope it helps you.

Resources