Got the pictured error while trying to set up onChange event handler for the FormRow function. Ultimately need to store the user data from Que Reset Time, Service Level threshold and Short Abandoned Threshold. The Queuecontainer is the main class that uses the editor to create the container forms.
QuesContainer.js
import React from 'react';
import Editor from './Editor';
import LoginForm from '../LoginForm';
import axios from 'axios';
export default class QueuesContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
workspaceSID: '',
apiKey: '',
apiSecret: '',
queues: [],
name: '',
}
this.listQueues = this.listQueues.bind(this);
this.setCredentials = this.setCredentials.bind(this);
this.getCredentials = this.getCredentials.bind(this);
this.setQueues = this.setQueues.bind(this);
this.handleChange = this.handleChange.bind(this);
}
/*
handleChange(target) {
this.setState({ [target.id]: target.value });
}
*/
//Added by JR for Save Function
handleChange(event){
console.log("From handle change", event);
this.setState({ name: event.target.value })
}
/*
handleSubmit(event) {
event.preventDefault();
this.props.login(this.state.workspaceSID, this.state.apiKey, this.state.apiSecret);
}
*/
handleSubmit(event) {
event.preventDefault();
var text = this.state.text;
console.log("form testing value output:");
}
setCredentials(workspaceSID, apiKey, apiSecret) {
this.setState({ workspaceSID: workspaceSID });
this.setState({ apiKey: apiKey });
this.setState({ apiSecret: apiSecret });
}
getCredentials() {
return this.state;
}
setQueues(queues) {
this.setState({ queues: queues });
}
getConfig(apiKey, apiSecret) {
return axios.get(`https://flex-api.twilio.com/v1/Configuration`, {
auth: {
username: apiKey,
password: apiSecret
},
});
}
//Added by JR used to post configuration
postConfig(apiKey, apiSecret, params) {
return axios.post(`https://flex-api.twilio.com/v1/Configuration`, {
auth: {
username: apiKey,
password: apiSecret
},
});
}
listQueues(workspaceSID, apiKey, apiSecret) {
this.setCredentials(workspaceSID, apiKey, apiSecret);
const queuesPromise = axios.get(`https://taskrouter.twilio.com/v1/Workspaces/${workspaceSID}/TaskQueues?PageSize=1000&Page=0`, {
auth: {
username: apiKey,
password: apiSecret
},
});
const channelsPromise = axios.get(`https://taskrouter.twilio.com/v1/Workspaces/${workspaceSID}/TaskChannels?PageSize=1000&Page=0`, {
auth: {
username: apiKey,
password: apiSecret
},
});
const configPromise = this.getConfig(apiKey, apiSecret);
Promise.all([queuesPromise, channelsPromise, configPromise])
.then((values) => {
console.log(values);
const twilioQueues = values[0].data.task_queues;
const twilioChannels = values[1].data.channels;
// const config = values[2].data.queue_stats_configuration;
const voice = twilioChannels.find(channel => {
console.log(channel)
return channel.unique_name === 'voice'
});
const chat = twilioChannels.find(channel => {
console.log(channel)
return channel.unique_name === 'chat'
});
const email = twilioChannels.find(channel => {
console.log(channel)
return channel.unique_name === 'email'
});
const channels = [voice, chat, email];
const queues = twilioQueues.map(q => {
const queue = {
queue_sid: q.sid,
friendly_name: q.friendly_name,
channels
}
return queue;
});
this.setQueues(queues);
});
}
//onChange={this.handleInputChange.bind(this)}
render() {
const { name } = this.state
return (
<div>
<p> Test Value is : {name} </p>
<LoginForm login={this.listQueues} buttonText='Load Queues' />
<Editor onSubmit={this.handleSubmit} onChange={this.handleChange} data={this.state.queues} credentials={this.getCredentials} setData={this.setQueues} />
</div>
);
}
}
Editor.js
import React from 'react';
import MaterialTable from 'material-table'
import Button from '@material-ui/core/Button';
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import axios from 'axios';
import { waitForDomChange } from '@testing-library/react';
export default class Editor extends React.Component {
constructor(props) {
super(props);
this.state = {
open: false,
queue: {},
columns: [
{ title: 'Queue Name', field: 'friendly_name', editable: 'never' },
{ title: 'Channel', field: 'channel_friendly_name', editable: 'never' },
{ title: 'Reset Timezone', field: 'channel_reset_timezone' },
{ title: 'Reset Time', field: 'channel_reset_time' },
{ title: 'Service Level Threshold', field: 'channel_service_level_threshold', type: 'numeric' },
{ title: 'Short Abandoned Threshold', field: 'channel_service_level_threshold', type: 'numeric' },
]
}
this.handleClose = this.handleClose.bind(this);
// this.handleSave = this.handleSave.bind(this);
}
handleClose(xxx) {
console.log('this happening');
console.log(xxx);
this.setState({ open: true });
}
/*
handleSave(event) {
console.log('Saving user input data');
console.log(event);
this.setState({ value: event.target.value});
}
*/
render() {
console.log('hey ho');
console.group(this.props.data);
const queues = this.props.data.map(q => {
const channel = q.channels.find(c => c.unique_name === 'voice');
q.channel_friendly_name = channel.friendly_name;
q.channel_reset_time = channel.reset_time;
q.channel_reset_timezone = channel.reset_timezone;
q.channel_service_level_threshold = channel.service_level_threshold;
q.channel_service_level_threshold = channel.service_level_threshold;
return q;
})
return (
<div>
<MaterialTable
title='Queue SLA'
columns={this.state.columns}
data={queues}
options={{
pageSize: 25,
pageSizeOptions: [25, 100, 500, 1000]
}}
detailPanel={rowData => {
//Modified by JR for Save Function
return FormContainer(rowData, this.props.credentials().apiKey,
this.props.credentials().apiSecret, this.handleChange);
}}
/>
</div>
);
}
}
function FormRow(data, handleChange) {
const queue = data.data;
const channels = queue.channels;
return (
channels.map((channel, index) => (
<Grid container item sm={12} spacing={3}>
<Grid item sm={2}>
<TextField
autoFocus
margin="dense"
id="channel_name"
label="Channel"
type="text"
value={channel.friendly_name}
fullWidth
disabled
/>
</Grid>
<Grid item sm={2}>
<TextField
autoFocus
margin="dense"
id="reset_timezone"
label="Reset Timezone"
type="text"
value="GMT"
fullWidth
disabled
/>
</Grid>
<Grid item sm={2}>
<TextField
autoFocus
margin="dense"
id="service_level_threshold"
label="Service Level Threshold"
value={channel.service_level_threshold}
onChange={handleChange}
type="text"
fullWidth
/>
</Grid>
<Grid item sm={2}>
<TextField
autoFocus
margin="dense"
id="short_abandoned_threshold"
label="Short Abandoned Threshold"
value={channel.short_abandoned_threshold}
onChange={handleChange}
type="text"
fullWidth
/>
</Grid>
</Grid>
))
);
}
//Modified by JR for Save Function
function FormContainer(data, apiKey, apiSecret, props, handleChange) {
console.log('what');
console.log(data);
console.log('JSON what');
console.log(JSON.stringify(data));
const queue = data;
// const queue = data.data;
const channels = queue.channels;
const cancel = () => {
console.log('cancel');
}
const save = () => {
//Modified by JR for Save Function
console.log('save');
console.log(data);
console.log(JSON.stringify(data));
console.log('SaveButtonClicked');
waitForDomChange();
console.log(data);
console.log(JSON.stringify(data));
savebuttonclicked(queue, apiKey, apiSecret);
console.log('Props Information');
console.log(props);
console.log('Save Sucessful');
}
return (
<div>
<Grid container>
<Grid item sm={1}></Grid>
<Grid container item sm={11} spacing={3}>
<FormRow data={queue}
formOnChange={handleChange}/>
</Grid>
<Grid
container
direction="row"
justify="flex-end"
alignItems="center"
>
<Grid item sm={1}></Grid>
<Grid item sm={11} spacing={3} justify="flex-end"
alignItems="center">
<Grid item sm={2}>
<TextField
autoFocus
margin="dense"
id="reset_time"
label="Queue Reset Time"
type="text"
value={channels.reset_time}
onChange={handleChange}
fullWidth
/>
</Grid>
<Button variant="outlined" onClick={cancel} color="secondary">Cancel</Button>
<Button variant="outlined" onClick={save} color="primary">Save</Button>
</Grid>
</Grid>
</Grid>
</div>
);
}
//Add by JR for Save Function
function savebuttonclicked(data, apiKey, apiSecret) {
const workspace = workspaces[0];
console.log('Test');
alert(JSON.stringify(data));
console.log(JSON.stringify(data));
var params = [];
for (const [index, value] of data.channels.entries()) {
params.push({
'url': data.channels[index].url,
'unique_name': data.channels[index].unique_name,
'account_sid': data.channels[index].account_sid,
'channel_reset_time': data.channels[index].channel_reset_time,
'channel_reset_timezone': data.channels[index].channel_reset_timezone,
'channel_service_level_threshold': data.channels[index].channel_service_level_threshold
})
}
alert(JSON.stringify(params));
console.log('Parms for API post:');
console.log(JSON.stringify(params));
/*
* Loop for API call for each URL in created JSON
*
for (const [index, value] of params.entries())
{
axios.post(params[index].url, params[index], {
auth: {
username: apiKey,
password: apiSecret,
}
})
}
*/
axios.get(workspace.apiURL, {
auth: {
username: apiKey,
password: apiSecret,
},
})
.then(function (response) {
alert('Save Sucessful.')
alert(JSON.stringify(response))
console.log('success');
console.log(response.headers);
})
.catch(function (error) {
alert('Error Occured.')
alert(error)
console.log('Error');
console.log(error);
});
}
With applied fix this is new error:
TypeError: Cannot read property 'channels' of undefined FormRow C:/Users/drago/Source/Repos/twilio-flex-editor/src/queues/Editor.js:99 96 | 97 | function FormRow({ data, handleChange }) { 98 | const queue = data.data; > 99 | const channels = queue.channels; 100 | 101 | 102 | View compiled ▶ 18 stack frames were collapsed.
You have defined FormRow
to consume a handleChange
callback and also incorrectly defined the props. They should be destructured from a single props object.
function FormRow(data, handleChange) {...
but have passed an formOnChange
prop callback
<FormRow data={queue} formOnChange={handleChange} />
You also pass this.handleChange
to Editor
but never consume it
<Editor
onSubmit={this.handleSubmit}
onChange={this.handleChange}
data={this.state.queues}
credentials={this.getCredentials}
setData={this.setQueues}
/>
and (possibly coincidentally) pass an undefined this.handleChange
function on to FormContainer
which is passed to FormRow
component.
Fix the FormRow
component definition
function FormRow({ data, handleChange }) {...
Pass handleChange
on to FormRow
as handleChange
.
<FormRow data={queue} handleChange={handleChange} />
Either define this.handleChange
in Editor
to be passed, or pass this.props.onChange
on.
return FormContainer(
rowData,
this.props.credentials().apiKey,
this.props.credentials().apiSecret,
this.props.onChange,
);