React google chart timeline handle event not working on first load - javascript

I have react google chart timeline with chartEvents.
I want when clicked on define chart, display modal window.
On first page load it doesnt work (and when I reload page).
But if I going to another tab in my app,then return to my timeline and click on chart its work without problems.
Please give me advice, whats a problem?
P.S.
I have some thoughts about this:
Problems with load of timeline(in first load page doesnt load all data in google-chart)
Problems with useState(Many counts of useState in my component)
My code example:
Modal.setAppElement('#modal')
interface IItem {
name: string,
duration: string,
id: string,
startDate: Date,
endDate: Date
}
export const options = {
timeline: {
groupByRowLabel: true,
colorByRowLabel: true,
showBarLabels: false,
barLabelStyle: { fontSize: 18 },
},
tooltip: {
isHtml: true
}
}
export function NewGantt() {
const [modalIsOpen, setIsOpen] = useState(false)
const [selectedVacation, setselectedVacation] = useState({ fullname: '', duration: '', startDate: new Date(), endDate: new Date(), vacationId: '' })
const [rows, setrows] = useState([['', '', '', new Date(), new Date(), null, 0, null]])
const [projects, setprojects] = useState<IProject[]>([])
const [selected, setSelected] = useState(selectedOptions[0])
const [dataArray, setDataArray] = useState([
[
{ type: 'string', id: 'Name' },
{ type: 'string', id: 'Duration' },
{ type: 'string', role: 'tooltip', div: { html: true } },
{ type: 'string', id: 'style', role: 'style' },
{ type: 'date', id: 'Start' },
{ type: 'date', id: 'End' },
],
[
'User', '01.01.2022-02.01.2022', `
<div style='padding: 10px'>
<b>User:</b> user <br><hr>
<div style='margin-top: 10px'><b>Vacation dates:</b> 01.01.2022-02.01.2022</div>
</div>
`, 'black', new Date('Sun Mar 12 2023 00:00:00 GMT+0700'), new Date('Sun Mar 13 2023 00:00:00 GMT+0700')
]
]) as any
const { isLoading: isLoadingProjects, refetch: refetch } = useQuery(
['projects'],
async () => {
const res = await axios.get(`${SERVER_URI}/rest/workspace/pm/projects`, {
headers: {
Authorization: `${localStorage.getItem('JWT_Token')}`,
},
})
if (res.data) {
setprojects(res.data.map((el: IProject) => ({ ...el, isActive: false })))
}
},
{
refetchOnWindowFocus: false,
},
)
function projectsButton(project: IProject) {
const projectIndex = projects.findIndex(
(pr) => pr.projectId === project.projectId,
)
const arr = [...projects]
arr[projectIndex].isActive = !arr[projectIndex].isActive
setprojects(arr)
}
useEffect(() => {
const activeProjectIds = [...projects]
.filter((pr) => pr.isActive)
.map((pr) => pr.projectId)
.join(',')
if (activeProjectIds.length > 0) {
requestOfStatus(selected.statusType, activeProjectIds)
} else {
requestOfStatus(selected.statusType, '')
}
}, [projects])
const arr: any[][] = []
useEffect(() => {
const arrayObjects: any[] = [
{ type: 'string', id: 'Name' },
{ type: 'string', id: 'Duration' },
{ type: 'string', role: 'tooltip', div: { html: true } },
{ type: 'string', id: 'style', role: 'style' },
{ type: 'date', id: 'Start' },
{ type: 'date', id: 'End' },
]
rows.forEach((vacation: any) => {
const name = vacation[1]
const startDate = new Date(vacation[2])
const endDate = new Date(vacation[3])
const duration = `${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()} + ${vacation[0].toString()}`
const tooltip = `
<div style='padding: 10px'>
<b>Пользователь:</b> ${name}<br><hr>
<div style='margin-top: 10px'><b>Dates of vacations:</b> ${startDate.toLocaleDateString()} - ${endDate.toLocaleDateString()}</div>
</div>
`
let color: string
if (vacation[7] === 'decisionRejected') {
color = 'red'
} else if (vacation[7] === 'noDecision') {
color = 'blue'
} else if (vacation[7] === 'decisionApproved') {
color = 'green'
} else {
color = 'black'
}
const object = [name, duration, tooltip, color, startDate, endDate]
arr.push(object)
})
const merged = [arrayObjects, ...arr]
setDataArray(merged)
}, [rows])
const requestOfStatus = async (statusType: string, activeProjectIds: string) => {
const requests = []
const output: any[][] = []
if (statusType === 'all') {
requests.push(await axios.get(`${SERVER_URI}/rest/workspace/pm/vacations?stringProjectIds=${activeProjectIds}&approveType=noDecision`, {
headers: {
Authorization: `${localStorage.getItem('JWT_Token')}`,
},
}))
requests.push(await axios.get(`${SERVER_URI}/rest/workspace/pm/vacations?stringProjectIds=${activeProjectIds}&approveType=decisionApproved`, {
headers: {
Authorization: `${localStorage.getItem('JWT_Token')}`,
},
}))
requests.push(await axios.get(`${SERVER_URI}/rest/workspace/pm/vacations?stringProjectIds=${activeProjectIds}&approveType=decisionRejected`, {
headers: {
Authorization: `${localStorage.getItem('JWT_Token')}`,
},
}))
} else {
requests.push(await axios.get(`${SERVER_URI}/rest/workspace/pm/vacations?stringProjectIds=${activeProjectIds}&approveType=${statusType}`, {
headers: {
Authorization: `${localStorage.getItem('JWT_Token')}`,
},
}))
}
axios.all(requests).then(axios.spread((...res) => {
const rows: any[][] = []
for (let i = 0; i < res.length; i++) {
const merged = [...res[i].data]
const urls = res[i].config.url
merged.forEach((vacation: any) => {
const startDate = new Date(vacation.vacationStartDate)
startDate.setDate(startDate.getDate() - 1)
rows.push([
vacation.vacationId, // index
vacation.user.userDetails.fullName, // user fullname
startDate, // start date
new Date(vacation.vacationEndDate), // end date
++vacation.duration, // duration
100, // progress
null, // dependencies
urls?.split('approveType=')[1].trim()
])
})
}
const flags: never[] = [], l = rows?.length
let i
// eslint-disable-next-line #typescript-eslint/no-explicit-any, #typescript-eslint/ban-ts-comment
// #ts-ignore
for (i = 0; i < l; i++) {
// eslint-disable-next-line #typescript-eslint/no-explicit-any, #typescript-eslint/ban-ts-comment
// #ts-ignore
if (flags[rows[i][3]]) continue;
// eslint-disable-next-line #typescript-eslint/no-explicit-any, #typescript-eslint/ban-ts-comment
// #ts-ignore
flags[rows[i][3]] = true;
// eslint-disable-next-line #typescript-eslint/no-explicit-any, #typescript-eslint/ban-ts-comment
// #ts-ignore
output.push(rows[i]);
}
setrows(output)
}))
}
const openModal = () => {
setIsOpen(()=>{
return true
})
}
return (
<>
<Header />
<div className='flex flex-col justify-center w-10/12 p-6 m-auto'>
<div className='flex flex-row items-center'>
<div className='flex flex-row'>
{!isLoadingProjects &&
projects &&
(projects as [])?.map((project: IProject) => (
<button
key={project.projectId}
onClick={() => {
projectsButton(project)
}}
className={`px-4 py-2 pt-0 mt-5 mr-5 font-bold text-white rounded hover:bg-gray-700 ${!project.isActive
? 'bg-white border-2 text-gray-500 border-gray-500/50'
: 'text-white bg-gray-500'
}`}
>
{project.projectName}
</button>
))}
</div>
<ApproveTypeMenu onSelected={(item: { id: number, name: string, statusType: string }) => { setSelected(item) }} selected={selected} />
<button className='px-4 py-2 pt-0 mt-5 mr-5 font-bold text-white rounded hover:bg-gray-700 bg-white border-2 text-gray-500 border-gray-500/50'>Применить фильтры</button>
</div>
<Chart
chartType="Timeline"
data={dataArray}
width='100%'
height="600px"
options={options}
chartLanguage='ru'
chartEvents={[
{
eventName: 'ready',
callback: (params) => {
const { chartWrapper, google } = params
const chart: any = chartWrapper.getChart()
google.visualization.events.addListener(chart, 'select', () => {
const [fullname, duration, , , startDate, endDate] = dataArray[chart.getSelection()[0].row + 1]
const vacationId = duration.split('+')[1].trim()
setselectedVacation({ fullname: `${fullname}`, duration: `${duration}`, startDate: startDate, endDate: endDate, vacationId: `${vacationId}` })
openModal()
})
},
},
]}
controls={[
{
controlType: 'CategoryFilter',
options: {
filterColumnIndex: 0,
ui: {
allowTyping: true,
allowMultiple: true,
orientation: 'horizontal',
caption: 'Choose colleague',
showRangeValues: false,
label: 'Filter by colleagues',
labelSeparator: ' ',
},
filterColumnLabel: 'Column Label'
},
controlPosition: 'top',
}
]}
/>
</div>
{selectedVacation.vacationId?.length > 0 && (
<PMModal
vacation={selectedVacation}
isModalOpen={modalIsOpen}
setIsModalOpen={setIsOpen}
refetch={refetch}
/>
)}
</>
);
}
My chartEvents code example without 'ready' event
chartEvents={[
{
eventName: 'select',
callback: (params) => {
const { chartWrapper, google } = params
const chart: any = chartWrapper.getChart()
const [fullname, duration, , , startDate, endDate] = dataArray[chart.getSelection()[0].row + 1]
const vacationId = duration.split('+')[1].trim()
setselectedVacation({ fullname: `${fullname}`, duration: `${duration}`, startDate: startDate, endDate: endDate, vacationId: `${vacationId}` })
openModal()
},
},
]}

Related

How do you set filename for Webdatarocks default export

As the title suggests i cant for the life of me find a way to give the exported file a name except "Pivot"
The HTML/Vue part only has the Pivot and a select dropdown that filters by date and that works fine, it's only the export that i'm struggling with
<template>
<v-card>
<v-progress-linear
v-if="loading"
class="position-absolute"
style="z-index: 1"
color="red"
height="10"
indeterminate></v-progress-linear>
<v-card-title style="text-align:center"> Reports </v-card-title>
<v-card-text v-if="!loading">
<v-row>
<v-col cols="3" offset="1">
<v-select
v-model="selectedDate"
:items="reportdates"
label="Period:"
item-title="rd_date_label"
item-value="rd_date"
density="compact"
hide-details>
</v-select>
</v-col>
</v-row>
<br />
<v-row>
<Pivot id="pivotid"
ref="pivotref"
height="650px"
:report="report"
:toolbar="toolbar"
:beforetoolbarcreated="beforetoolbarcreated">
</Pivot>
</v-row>
</v-card-text>
</v-card>
</template>
Javascript part with my methods and data structure
<script>
import { mapGetters } from 'vuex'
import 'webdatarocks/webdatarocks.css'
import Pivot from "../../Common/Pivot.vue";
import '../../Common/webdatarocks.css';
export default {
name: 'Reports',
props: {
curDate: String,
},
components: {
Pivot
},
computed: {
...mapGetters (['reportdates', 'worktask']),
},
mounted() {
},
created() {
this.loadingData();
},
data() {
return {
isAdmin: null,
loading: false,
loaded: {
reportdates: false,
worktask: false,
},
selectedDate: null,
datachanged: null,
toolbar: true,
beforetoolbarcreated: this.customizeToolbar,
report: {
dataSource: {
data: [],
},
formats: [{
name: "hours",
maxDecimalPlaces: 2,
maxSymbols: 20,
textAlign: "right"
}],
slice: {
rows: [{
uniqueName: "Employee"
}
// ,{
// uniqueName: "Date"
// },
],
columns: [
{
uniqueName: "Client"
},
{
uniqueName: "[Measures]"
},],
measures: [{
uniqueName: "Hours",
aggregation: "sum",
format: "hours"
}]
},
options: {
grid: {
type: "compact",
title: "",
showFilter: true,
showHeaders: true,
showTotals: true,
showGrandTotals: "on",
showHierarchies: true,
showHierarchyCaptions: true,
showReportFiltersArea: true
},
configuratorActive: false,
configuratorButton: true,
showAggregations: true,
showCalculatedValuesButton: true,
drillThrough: true,
showDrillThroughConfigurator: true,
sorting: "on",
datePattern: "dd/MM/yyyy",
dateTimePattern: "dd/MM/yyyy HH:mm:ss",
saveAllFormats: false,
showDefaultSlice: true,
defaultHierarchySortName: "asc",
},
}
}
},
watch: {
reportdates() {
this.loaded.reportdates = true;
let fdate = this.reportdates.find(r => r.rd_date_label === this.curDate);
this.selectedDate = fdate.rd_date;
this.checkLoading();
},
worktask() {
this.loaded.worktask = true;
this.checkLoading();
//console.log('worktask: ' + this.worktask.length);
this.report.dataSource.data = this.getJSONData();
},
async selectedDate() {
//console.log('selectedDate: ' + this.selectedDate);
let fdate = this.reportdates.find(r => r.rd_date_label === this.curDate);
let tUserEmail = window.Laravel.user.email;
let tUserId = window.Laravel.user.id;
if(window.Laravel.user.role === "USR"){
if (fdate.rd_date == this.selectedDate) {
this.report.dataSource.data = [];
this.$store.dispatch('fetchWorkTaskReportDataUser', { rdate : this.selectedDate, tUserEmail : tUserEmail, });
} else {
let dstr = new Date(this.selectedDate).toISOString().slice(0, 7);
const params = Object.assign({}, this.$route.params);
params.curDate = dstr;
await this.$router.push({ params });
this.$router.go();
}
}else{
if (fdate.rd_date == this.selectedDate) {
this.report.dataSource.data = [];
this.$store.dispatch('fetchWorkTaskReportData', { rdate : this.selectedDate, tUserEmail : tUserEmail, });
} else {
let dstr = new Date(this.selectedDate).toISOString().slice(0, 7);
const params = Object.assign({}, this.$route.params);
params.curDate = dstr;
await this.$router.push({ params });
this.$router.go();
}
}
// if (fdate.rd_date == this.selectedDate) {
// this.report.dataSource.data = [];
// this.$store.dispatch('fetchWorkTaskReportDataUser', { rdate : this.selectedDate, tUserEmail : tUserEmail, });
// } else {
// let dstr = new Date(this.selectedDate).toISOString().slice(0, 7);
// const params = Object.assign({}, this.$route.params);
// params.curDate = dstr;
// await this.$router.push({ params });
// this.$router.go();
// }
}
},
methods: {
loadingData() {
this.loading = true;
// let tUserId = window.Laravel.user.id;
// if(window.Laravel.user.role === "ADM"){
// console.log("Admin");
// this.$store.dispatch('fetchReportDates',{
// tUserId : tUserId,
// });
// }else{
// console.log("User");
// this.$store.dispatch('fetchReportDatesUser',{
// tUserId : tUserId,
// })}
this.$store.dispatch('fetchReportDates',{
// tUserId : tUserId,
})
},
checkLoading() {
this.loading = !(this.loaded.reportdates && this.loaded.worktask);
},
getText(item) {
return item;
},
customizeToolbar(toolbar) {
var tabs = toolbar.getTabs();
toolbar.getTabs = function() {
delete tabs[0];
delete tabs[1];
delete tabs[2];
delete tabs[7];
return tabs;
}
},
getJSONData() {
return this.worktask
for(let r=0; r < this.worktask.length; r++){
returns.push(this.worktask[r]);
}
return returns;
},
},
}
This can be done by customizing the default tabs from the Toolbar and using the exporTo() API call. For example, here's how you can change the name of the exported PDF file:
function customizeToolbar(toolbar) {
// get all tabs
let tabs = toolbar.getTabs();
toolbar.getTabs = function() {
let exportTab = tabs.find(tab => tab.id == "wdr-tab-export");
if (!exportTab) return tabs;
let exportToPdfTab = exportTab.menu.find(tab => tab.id == "wdr-tab-export-pdf");
exportToPdfTab.handler = () => {
this.pivot.exportTo("pdf", {
filename: "custom-name" // set the necessary name
});
}
return tabs;
}
}
Please check the following CodePen: https://codepen.io/webdatarocks/pen/QWxYLRy?editors=1010

How to display the chart by series in react hooks

index.tsx
const [ data, setData ] = useState({});
const [ dataChart, setDataChart] = useState({});
const [ isLoading, setIsLoading ] = useState(false);
const fetchChartDetail = useCallback(async (data?) => {
if (data && Object.keys(data).length > 0 && data['data'].length > 0) {
data['data'].map(async (res) => {
try {
await getAll({start: 1, length: 45, sensor: data.sensor}).then((response) => {
let DATA = {
...response,
id: res['id'],
sensor: res['sensor'],
chart: [{
...configChart,
data: response && response['data'] ? response['data']['data'] : []
}]
}
if (res.spinning) {
res['spinning'] = false;
}
const data = {...res, chart: [{
...configChart,
data: response && response['data'] ? response['data']['data'] : []
}]};
if (!res.spinning) setDataChart(DATA);
});
} catch (err) {
setError(err);
}
})
}
},[])
const fetchData = useCallback(async (params?, status?) => {
try {
await getLatest().then((response) => {
let DATA = response['data']['data'];
DATA = Object.values(DATA.map((obj) => {
return {
...obj,
spinning: true,
}
}));
response['data']['data'] = DATA;
if (!status) {
setData(response['data']);
setIsLoading(false);
fetchChartDetail(response['data']);
}
})
}catch(err) {
setError(err)
}
},[])
useEffect(() => {
let ignore = false;
setIsLoading(true)
const timeout = setTimeout(() => {
fetchData(null, ignore);
});
return () => {
ignore = true;
clearTimeout(timeout);
};
}, [fetchData]);
return (
<>
{
isLoading ? (<Loading src={`/images/icons/gif/loader.gif`} imgStyle={imgStyle} />) :
<div className="md:flex content-center flex-wrap -mx-2 p-3">
{
data && Object.keys(data).length > 0 && data['data'].length > 0 ?
(
data && data['data'].map((item, idx) => (
<div key={idx} className="md:flex md:w-1/2 lg:w-1/2 xl:w-1/3 px-2 py-2">
<div className="md:flex-1 p-4 rounded shadow-lg bg-white border-b border-r border-grey-dark" onClick={() => routeDetail(router, item)}>
<div className="flex flex-col">
<Room config={{ item: item, ...configRoom }} index={idx} />
</div>
<div>
<Echart config={dataChart && dataChart['chart'] ? dataChart['chart'] : [configChart]} loading={item.spinning} colors={colors} dataZoom={true} />
</div>
</div>
</div>
))
) : ('NO DATA')
}
</div>
}
</>
);
What I'm trying to do here is to display the line chart by series or if the data already complete then it will display the data. but my problem is for example I have 3 chart then after fetch the first all data from chart 1 it will display then the next fetching data is chart 2 but instead it will display the data chart 2 on chart 2 it will display the data of chart 1 in chat 2, then the data of chart 1 is empty.
on the console it will be like this when it console the dataChart
first load
{ data: [...], name: 'chart1'}
{ data: [], name: 'chart2'}
{ data: [], name: 'chart3'}
second load
{ data: [], name: 'chart1'}
{ data: [...], name: 'chart2'}
{ data: [], name: 'chart3'}
third load
{ data: [], name: 'chart1'}
{ data: [], name: 'chart2'}
{ data: [...], name: 'chart3'}
where it should be like this:
first load
{ data: [...], name: 'chart1'}
second load
{ data: [...], name: 'chart1'}
{ data: [...], name: 'chart2'}
third load
{ data: [...], name: 'chart1'}
{ data: [...], name: 'chart2'}
{ data: [...], name: 'chart3'}

Read and save WP post meta field data from Gutenberg block

I used the code from this article as an example.
It is possible to read the data, but not to save it.
Code:
const { Component, Fragment } = wp.element;
const {
RichText,
InspectorControls,
PanelColorSettings,
AlignmentToolbar,
BlockControls,
} = wp.editor;
const { Button, PanelBody, SelectControl, TextControl } = wp.components;
const { __ } = wp.i18n;
const { registerBlockType } = wp.blocks;
const { withSelect, withDispatch } = wp.data;
class Inspector extends Component {
constructor(props) {
super(...arguments);
}
render() {
const backgroundColors = [
{ color: "#525252", name: "Черный" },
{ color: "#872d2d", name: "Акцентный красный" },
{ color: "#e49312", name: "Акцентный желтый" },
{ color: "#bab3a6", name: "Акцентный кремовый" },
];
const fontSizeOptions = [
{ value: "14px", label: __("14px") },
{ value: "16px", label: __("16px") },
{ value: "18px", label: __("18px") },
{ value: "22px", label: __("22px") },
{ value: "28px", label: __("28px") },
];
const paddingTopOptions = [
{ value: "0px", label: __("0px") },
{ value: "10px", label: __("10px") },
{ value: "25px", label: __("25px") },
{ value: "50px", label: __("50px") },
];
const paddingBottomOptions = [
{ value: "0px", label: __("0px") },
{ value: "10px", label: __("10px") },
{ value: "25px", label: __("25px") },
{ value: "50px", label: __("50px") },
];
const {
setAttributes,
attributes: { text_color, font_size, padding_top, padding_bottom },
} = this.props;
let PluginMetaFields = (props) => {
return (
<>
<TextControl
value={props.text_metafield}
label={__("Text Meta", "textdomain")}
onChange={(value) => props.onMetaFieldChange(value)}
/>
</>
);
};
PluginMetaFields = withSelect((select) => {
return {
text_metafield: select("core/editor").getEditedPostAttribute("meta")[
"_myprefix_text_metafield"
],
};
})(PluginMetaFields);
PluginMetaFields = withDispatch((dispatch) => {
return {
onMetaFieldChange: (value) => {
dispatch("core/editor").editPost({
meta: { _myprefix_text_metafield: value },
});
},
};
})(PluginMetaFields);
return (
<InspectorControls key="inspector">
<PanelBody title={__("Настройки абзаца")}>
<PanelColorSettings
title={__("Цвет шрифта")}
initialOpen={true}
colorSettings={[
{
value: text_color,
colors: backgroundColors,
onChange: (value) => setAttributes({ text_color: value }),
label: __("Цвет шрифта"),
},
]}
/>
<SelectControl
label={__("Размер шрифта")}
options={fontSizeOptions}
value={font_size}
onChange={(value) => this.props.setAttributes({ font_size: value })}
/>
<SelectControl
label={__("Отступ сверху")}
options={paddingTopOptions}
value={padding_top}
onChange={(value) =>
this.props.setAttributes({ padding_top: value })
}
/>
<SelectControl
label={__("Отступ снизу")}
options={paddingBottomOptions}
value={padding_bottom}
onChange={(value) =>
this.props.setAttributes({ padding_bottom: value })
}
/>
<PluginMetaFields />
</PanelBody>
</InspectorControls>
);
}
}
class HeadlineBlock extends Component {
render() {
const {
attributes: {
headline,
text_color,
font_size,
padding_top,
padding_bottom,
alignment,
},
setAttributes,
} = this.props;
const onChangeAlignment = (newAlignment) => {
this.props.setAttributes({
alignment: newAlignment === undefined ? "none" : newAlignment,
});
};
return [
<Inspector {...{ setAttributes, ...this.props }} />,
<div>
{
<BlockControls>
<AlignmentToolbar value={alignment} onChange={onChangeAlignment} />
</BlockControls>
}
<RichText
tagName="p"
placeholder={__("Текст...")}
keepPlaceholderOnFocus
value={headline}
formattingControls={["bold", "italic", "strikethrough", "link"]}
className={"font-" + font_size + " post-desc__p-text"}
style={{
color: text_color,
textAlign: alignment,
}}
onChange={(value) => setAttributes({ headline: value })}
/>
</div>,
];
}
}
registerBlockType("amm-custom-block/test-block", {
title: __("Тест блок"),
icon: "shield",
category: "AMM",
attributes: {
headline: {
type: "string",
},
alignment: {
type: "string",
default: "none",
},
text_color: {
type: "string",
default: "#525252",
},
font_size: {
type: "string",
default: "14px",
},
padding_top: {
type: "string",
default: "50px",
},
padding_bottom: {
type: "string",
default: "0px",
},
},
edit: HeadlineBlock,
save: function (props) {
const {
attributes: {
headline,
text_color,
font_size,
padding_top,
padding_bottom,
alignment,
},
} = props;
return (
<Fragment>
{headline && !!headline.length && (
<RichText.Content
tagName="p"
className={"font-" + font_size + " post-desc__p-text"}
style={{
color: text_color,
paddingTop: padding_top,
paddingBottom: padding_bottom,
textAlign: alignment,
}}
value={headline}
/>
)}
</Fragment>
);
},
});
So far just added a text field to the block and am trying to read and save the data.
With reading everything is OK, but saving the data does not work and there are no errors.
Any idea why this is happening?
sorry english is not a native language
The meta field _myprefix_text_metafield is a protected field as it starts with a "_" (underscore). This is why you can read the value in withSelect() but not save over it withDispatch() without passing auth_callback.
To save to a protected field, the auth_callback is required, eg:
<?php
register_post_meta( 'post', '_myprefix_text_metafield', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string',
'auth_callback' => function() {
return current_user_can( 'edit_posts' );
})
);
?>
There is an example in the tutorial you are following:
https://css-tricks.com/managing-wordpress-metadata-in-gutenberg-using-a-sidebar-plugin/#post-291605
Alternatively, if your meta field is not required to be protected/private: re-register your meta field as myprefix_text_metafield (no underscore in the name and no auth_callback) and your current code will work, eg:
<?php
register_post_meta( 'post', 'myprefix_text_metafield', array(
'show_in_rest' => true,
'single' => true,
'type' => 'string'
);
?>

How can I re-render MUI data table upon any user action in react

I have one Lock-Unlock button and a delete button, So the problem is when I render data from axios using useEffect hook its working, but if I lock or unlock a user the table is not changing automatically. That means axios is not getting called.
In that case if I put the useState hook in the useEffect, API is getting called multiple times, that is not as expected. In that case can anyone suggest me how can I re render data table as soon as user clicks on lock-unlock button.
FYI, lock unlock functionality is working 100% correctly. What is needed I have to go to other page and again come back to my datatable page in order to see the change.
code snippet:
const useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1)
},
input: {
display: "none"
}
}));
/*
Customization of mui
*/
const getMuiTheme = () => createMuiTheme({
overrides: {
MUIDataTableBodyCell: {
root: {
backgroundColor: "#FFFFFE"
}
}
}
});
/* eslint-disable */
const UserDetailsDatatable = () => {
// console.log('POS: ' + localStorageService.getItem("auth_user"));
if(localStorageService.getItem("auth_user") == null){
history.push({
pathname: "/session/signin"
});
}
const [responsive, setResponsive] = useState("vertical");
const [dataRenderHook, setDataRenderHook] = useState([]);
const [tableBodyHeight, setTableBodyHeight] = useState("650px");
const [tableBodyMaxHeight, setTableBodyMaxHeight] = useState("");
const classes = useStyles();
// Redirect to edit
const newMountpage = (rowData) => {
var pickedUpRowData = rowData.rowData;
let path = `/Admin/users/editUser`;
history.push({
pathname: path,
state: {detail: pickedUpRowData}
});
};
/* Deletion */
const deleteRow = (rowDatam) => {
deleteUser(rowDatam);
};
/* User Locker/Unlocker */
const LockerUnlocker = (Iopticore_ID, state) => {
console.log('State: '+ state);
lockerUnlocker(Iopticore_ID, Boolean(state));
}
const ChangeHandler = (event) => {
console.log(event);
};
const columns = [
{
name: "userID",
label: "Iopticore_ID",
options: {
filter: true,
sort: true,
}
},
{
name: "userName",
label: "Corporate ID",
options: {
filter: true,
sort: true,
}
},
{
name: "name",
label: "User Name",
options: {
filter: true,
sort: true,
}
},
{
name: "email",
label: "Email",
options: {
filter: true,
sort: true,
}
},
{
name: "role",
label: "Role",
options: {
filter: true,
sort: true,
}
},
{
name: "external",
label: "IsExternal",
options: {
filter: true,
sort: true,
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val) => {
return val === true ? "Yes" : "No";
}
}
},
{
name: "locked",
label: "Access",
options: {
filter: true,
sort: true,
empty: true,
filter: true,
sort: true,
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val, tableMeta) => {
//console.log('v: ' + val + ' ' + JSON.stringify(tableMeta)) ;
return val === true ? (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0], tableMeta.rowData[6])}
>
<img src="https://img.icons8.com/plasticine/25/000000/unlock.png"/>
<b>UnLock </b>
</Fab>
) : (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0])}
>
<img src="https://img.icons8.com/dusk/25/000000/unlock.png"/>
<b> Lock </b>
</Fab>
);
}
}
},
{
name: "Edit",
options: {
filter: true,
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab
size="small"
color="primary"
aria-label="Edit"
className={classes.button}
onClick={() => newMountpage(tableMeta)}
>
<Icon>edit_icon</Icon>
</Fab>
);
}
}
},
{
name: "Delete",
options: {
filter: true,
textAlign: 'center',
sort: false,
empty: true,
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab size="small"
color="secondary"
aria-label="Edit"
className={classes.button}
onClick={() => deleteRow(tableMeta.rowData[0])}
>
<Icon>delete_icon</Icon>
</Fab>
);
}
}
}];
const options = {
selectableRows: 'none',
filter: true,
textAlign: 'center',
filterType: "dropdown",
rowsPerPage: 5,
pagination: true,
responsive,
enableNestedDataAccess: '.',
tableBodyHeight,
tableBodyMaxHeight
};
var recentReceivedToken = localStorage.getItem('jwtAuthtokenManager');
var res= [];
useEffect(() => {
(async () => {
res = await axios.get('<URL>', {
headers: {"Authorization" : `Bearer ${recentReceivedToken}`}
},
// console.log('Hola : ' + JSON.stringify(res))
)
.catch((error)=> {
if(error.response.status != 200){
swal({
title: "Opps! Access Denied",
imageUrl: 'https://notionpress.com/new-rewamp/images/404-error.gif',
text: "You Might Not Have Access To This Page.",
icon: "error",
});
}
})
//console.log('Res : ' + JSON.stringify(res));
setDataRenderHook(res.data);
})();
}, []);
//console.log('Data Promise : ' + renderDatatable());
if(dataRenderHook.length === 0){
//console.log('Length: 0');
return(
<React.Fragment>
<div style={{padding:40}}>
<MuiThemeProvider theme={getMuiTheme()}>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<LinearBuffer />
</div>
</MuiThemeProvider>
</div>
</React.Fragment>
)
}else{
//console.log('Length: 1');
return (
<React.Fragment>
<div style={{padding:40}}>
<MuiThemeProvider theme={getMuiTheme()}>
<MUIDataTable
title={"IoptiCore User List"}
data={dataRenderHook}
columns={columns}
options={options}
/>
</MuiThemeProvider>
</div>
</React.Fragment>
);
}
}
export default UserDetailsDatatable;
import React, { useState, useEffect } from "react";
import MUIDataTable from "mui-datatables";
import { makeStyles,createMuiTheme, MuiThemeProvider } from "#material-ui/core/styles";
import { Icon, Fab } from "#material-ui/core";
import history from "history.js";
import axios from "axios";
import swal from 'sweetalert';
import LinearBuffer from './EditUserHelper/progresscircle';
import lockerUnlocker from './Lock_Unlock/lockerUnlocker';
import deleteUser from './DeleteUser/DeleteUser'
import localStorageService from "../../../services/localStorageService";
import apiDataReturner from "./userAxios";
const useStyles = makeStyles(theme => ({
button: {
margin: theme.spacing(1)
},
input: {
display: "none"
}
}));
/*
Customization of mui
*/
const getMuiTheme = () => createMuiTheme({
overrides: {
MUIDataTableBodyCell: {
root: {
backgroundColor: "#FFFFFE"
}
}
}
});
/* eslint-disable */
const UserDetailsDatatable = () => {
// console.log('POS: ' + localStorageService.getItem("auth_user"));
if(localStorageService.getItem("auth_user") == null){
history.push({
pathname: "/session/signin"
});
}
const [responsive, setResponsive] = useState("vertical");
const [dataRenderHook, setDataRenderHook] = useState([]);
const [tableBodyHeight, setTableBodyHeight] = useState("650px");
const [tableBodyMaxHeight, setTableBodyMaxHeight] = useState("");
const [tracker, setTracker] = useState();
const classes = useStyles();
// Redirect to edit
const newMountpage = (rowData) => {
var pickedUpRowData = rowData.rowData;
let path = `/Admin/users/editUser`;
history.push({
pathname: path,
state: {detail: pickedUpRowData}
});
};
/* Deletion */
const deleteRow = (rowDatam) => {
deleteUser(rowDatam);
var x = apiDataReturner();
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
setTracker(x);
}, 3000);
});
promise.then(values => {
setDataRenderHook(values);
});
};
/* User Locker/Unlocker */
const LockerUnlocker = (Iopticore_ID, state) => {
lockerUnlocker(Iopticore_ID, Boolean(state));
var x = apiDataReturner();
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
setTracker(x);
}, 3000);
});
promise.then(values => {
setDataRenderHook(values);
});
}
const ChangeHandler = (event) => {
console.log(event);
};
const columns = [
{
name: "userID",
label: "Iopticore_ID",
options: {
filter: true,
sort: true,
responsive: 'scrollFullHeightFullWidth',
display: false
}
},
{
name: "userName",
label: "Corporate ID",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "name",
label: "User Name",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "email",
label: "Email",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "role",
label: "Role",
options: {
filter: true,
responsive: 'scrollFullHeightFullWidth',
sort: true,
}
},
{
name: "external",
label: "IsExternal",
options: {
filter: true,
sort: true,
responsive: 'scrollFullHeightFullWidth',
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val) => {
return val === true ? "Yes" : "No";
}
}
},
{
name: "locked",
label: "Access",
options: {
filter: true,
sort: true,
empty: true,
filter: true,
sort: true,
responsive: 'scrollFullHeightFullWidth',
filterOptions: {
names: ["Yes", "No"],
logic(v, filterVal) {
const show =
(filterVal.indexOf("Yes") >= 0 && ( v === true || v === 'true') ) ||
(filterVal.indexOf("No") >= 0 && ( v === false || v === 'false') );
return !show;
}
},
customBodyRender: (val, tableMeta) => {
//console.log('v: ' + val + ' ' + JSON.stringify(tableMeta)) ;
return val === true ? (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0], tableMeta.rowData[6])}
>
<img src="https://img.icons8.com/plasticine/25/000000/unlock.png"/>
<b>UnLock </b>
</Fab>
) : (
<Fab
size="small"
variant="extended"
aria-label="Delete"
className={classes.button}
color="default"
onClick={() => LockerUnlocker(tableMeta.rowData[0])}
>
<img src="https://img.icons8.com/dusk/25/000000/unlock.png"/>
<b> Lock </b>
</Fab>
);
}
}
},
{
name: "Edit",
options: {
filter: true,
sort: false,
empty: true,
responsive: 'scrollFullHeightFullWidth',
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab
size="small"
color="primary"
aria-label="Edit"
className={classes.button}
onClick={() => newMountpage(tableMeta)}
>
<Icon>edit_icon</Icon>
</Fab>
);
}
}
},
{
name: "Delete",
options: {
filter: true,
textAlign: 'center',
sort: false,
empty: true,
responsive: 'scrollMaxWidth',
customBodyRender: (value, tableMeta, updateValue) => {
return (
<Fab size="small"
color="secondary"
aria-label="Edit"
className={classes.button}
onClick={() => deleteRow(tableMeta.rowData[0])}
>
<Icon>delete_icon</Icon>
</Fab>
);
}
}
}];
const options = {
selectableRows: 'none',
filter: true,
textAlign: 'center',
filterType: "dropdown",
fixedHeaderOptions:true,
rowsPerPage: 5,
pagination: true,
responsive: 'stacked',
enableNestedDataAccess: '.',
tableBodyHeight,
tableBodyMaxHeight
};
//var recentReceivedToken = localStorage.getItem('jwtAuthtokenManager');
var res= [];
useEffect(() => {
var x = apiDataReturner();
const promise = new Promise((resolve, reject) => {
setTimeout(() => {
resolve(x);
}, 2000);
});
promise.then(values => {
setDataRenderHook(values);
});
}, [tracker]);
if(dataRenderHook.length === 0){
return(
<React.Fragment>
<div style={{padding:0}}>
<MuiThemeProvider theme={getMuiTheme()}>
<div
style={{
display: "flex",
justifyContent: "center",
alignItems: "center"
}}
>
<LinearBuffer />
</div>
</MuiThemeProvider>
</div>
</React.Fragment>
)
}else{
return (
<div>
<React.Fragment>
<div style={{padding:0}}>
<MuiThemeProvider theme={getMuiTheme()}>
<MUIDataTable
title={"IoptiCore User List"}
data={dataRenderHook}
columns={columns}
options={options}
/>
</MuiThemeProvider>
</div>
</React.Fragment>
</div>
);
}
}
export default UserDetailsDatatable;
Sharing the code, it might help someone.

Unable to call a function on click of button in Vue js using vuetify

I am using Vue 2
I am displaying a table using datatable using Vuetify.
For each row I need to show a button on click of which a function will get called.
I tried #click and v-on:click both of which did not work. It complains about syntax error on this line.
Can someone please guide me how to do it ?
The error is in map function in detail key:
this.calls = calls.data.result.calls
.map((obj, i) => ({
id: obj.id,
createAt: moment.parseZone(obj.created_at).format('YYYY-MM-DD hh:mm a'),
startTime: (obj.start_time) ? moment.parseZone(obj.start_time).format('YYYY-MM-DD hh:mm a') : '',
username: obj.User.username.toUpperCase(),
expertName: obj.Expert.expertName.toUpperCase(),
status: CALL_STATUS[obj.status],
detail: (obj.status !== 'complete' && obj.status !== 'cancel') ? <v-btn v-on:click="callSomeFunction(id)">Change Status</v-btn>:'', // Error is on this line
}));
My code is :
<template>
<div>
<v-data-table
:headers="headers"
:items="calls"
:hide-default-footer="true"
class="elevation-1"
></v-data-table>
<v-pagination v-if="this.pagination.total > 0"
v-model="pagination.current"
:length="pagination.total"
:total-visible=10
#input="onPageChange"
></v-pagination>
</div>
</template>
<script>
import moment from 'moment';
import callAPI from '#/api/CallAPI';
import { CALL_STATUS } from '../../constants/enum';
export default {
name: 'listing',
props: ['dates', 'status'],
mounted() {
this.loadCalls();
},
watch: {
dates() {
this.pagination.current = 1;
this.pagination.total = 0;
this.pagination.offset = 0;
this.loadCalls();
},
status() {
this.pagination.current = 1;
this.pagination.total = 0;
this.pagination.offset = 0;
this.loadCalls();
},
},
data() {
return {
headers: [
{
text: 'Call Id',
align: 'left',
sortable: false,
value: 'id',
},
{ text: 'Booking Time', sortable: false, value: 'createAt' },
{ text: 'Confirmed Time', sortable: false, value: 'startTime' },
{ text: 'User name', sortable: false, value: 'username' },
{ text: 'Expert name', sortable: false, value: 'expertName' },
{ text: 'Status', sortable: false, value: 'status' },
{ text: 'Detail', sortable: false, value: 'detail' },
],
search: '',
pagination: {
current: 1,
total: 0,
perPage: 20,
offset: 0,
},
resizable: true,
adaptive: true,
draggable: true,
calls: [],
};
},
methods: {
show () {
this.$modal.show('example-modal')
},
hide () {
this.$modal.hide('example-modal')
},
async loadCalls() {
const date1 = moment(`${this.dates[0]} 00:00:00`).format('x');
const date2 = moment(`${this.dates[1]} 23:59:59`).format('x');
console.log(`${this.dates[0]} 00:00:00`, date1, `${this.dates[1]} 23:59:59`, date2);
const calls = await callAPI.getListing(
{
startTime: date1,
endTime: date2,
status: this.status,
limit: this.pagination.perPage,
offset: this.pagination.offset,
},
);
this.calls = calls.data.result.calls
.map((obj, i) => ({
id: obj.id,
createAt: moment.parseZone(obj.created_at).format('YYYY-MM-DD hh:mm a'),
startTime: (obj.start_time) ? moment.parseZone(obj.start_time).format('YYYY-MM-DD hh:mm a') : '',
username: obj.User.username.toUpperCase(),
expertName: obj.Expert.expertName.toUpperCase(),
status: CALL_STATUS[obj.status],
detail: (obj.status !== 'complete' && obj.status !== 'cancel') ? <v-btn v-on:click="callSomeFunction(id)">Change Status</v-btn>:'', // Error is on this line
}));
if (this.calls.length > 0) {
this.pagination.current = calls.data.result.currentPage;
}
this.pagination.total = calls.data.result.totalPage;
console.log(this.calls);
},
onPageChange(number) {
this.pagination.offset = (number > 1) ? ((number * this.pagination.perPage) - this.pagination.perPage) : 0;
this.loadCalls();
},
},
};
</script>
this.calls = calls.data.result.calls
.map((obj, i) => ({
id: obj.id,
createAt: moment.parseZone(obj.created_at).format('YYYY-MM-DD hh:mm a'),
startTime: (obj.start_time) ? moment.parseZone(obj.start_time).format('YYYY-MM-DD hh:mm a') : '',
username: obj.User.username.toUpperCase(),
expertName: obj.Expert.expertName.toUpperCase(),
status: CALL_STATUS[obj.status],
detail: (obj.status !== 'complete' && obj.status !== 'cancel') ? '<v-btn v-on:click="callSomeFunction(id)">Change Status</v-btn>' :'', // Error is on this line
}));
try to add '' for your html

Categories