date formats, eslint no-explicit-any

This commit is contained in:
Kazala Kinga (ID) 2023-02-06 18:33:33 +01:00
parent 750632b6da
commit 29ef721e93
12 changed files with 221 additions and 152 deletions

View File

@ -4,4 +4,13 @@ const build = require('@microsoft/sp-build-web');
build.addSuppression(`Warning - [sass] The local CSS class 'ms-Grid' is not camelCase and will not be type-safe.`);
var getTasks = build.rig.getTasks;
build.rig.getTasks = function () {
var result = getTasks.call(build.rig);
result.set('serve', result.get('serve-deprecated'));
return result;
};
build.initialize(require('gulp'));

View File

@ -1,10 +1,10 @@
{
"name": "react-appinsights-dashboard",
"version": "0.0.1",
"version": "1.0.1",
"private": true,
"main": "lib/index.js",
"engines": {
"node": ">=0.10.0"
"node": ">=16.13.0 <17.0.0"
},
"scripts": {
"build": "gulp bundle",
@ -22,7 +22,9 @@
"@microsoft/sp-property-pane": "1.16.1",
"@microsoft/sp-webpart-base": "1.16.1",
"@pnp/spfx-controls-react": "^3.12.0",
"chart.js": "^4.2.0",
"history": "~5.3.0",
"lodash": "^4.17.21",
"moment": "^2.29.4",
"react": "17.0.1",
"react-dom": "17.0.1",
@ -41,6 +43,7 @@
"@types/react": "17.0.45",
"@types/react-dom": "17.0.17",
"@types/webpack-env": "~1.15.2",
"@types/lodash": "~4.14.184",
"ajv": "^6.12.5",
"eslint": "8.7.0",
"eslint-plugin-react-hooks": "4.3.0",

View File

@ -20,14 +20,17 @@ specifiers:
'@microsoft/sp-webpart-base': 1.16.1
'@pnp/spfx-controls-react': ^3.12.0
'@rushstack/eslint-config': 2.5.1
'@types/lodash': ~4.14.184
'@types/react': 17.0.45
'@types/react-dom': 17.0.17
'@types/webpack-env': ~1.15.2
ajv: ^6.12.5
chart.js: ^4.2.0
eslint: 8.7.0
eslint-plugin-react-hooks: 4.3.0
gulp: 4.0.2
history: ~5.3.0
lodash: ^4.17.21
moment: ^2.29.4
react: 17.0.1
react-dom: 17.0.1
@ -45,7 +48,9 @@ dependencies:
'@microsoft/sp-property-pane': 1.16.1_lozypg5aq7ao6qbkylypqd4zei
'@microsoft/sp-webpart-base': 1.16.1_lozypg5aq7ao6qbkylypqd4zei
'@pnp/spfx-controls-react': 3.12.0_lozypg5aq7ao6qbkylypqd4zei
chart.js: 4.2.0
history: 5.3.0
lodash: 4.17.21
moment: 2.29.4
react: 17.0.1
react-dom: 17.0.1_react@17.0.1
@ -58,6 +63,7 @@ devDependencies:
'@microsoft/sp-build-web': 1.16.1
'@microsoft/sp-module-interfaces': 1.16.1
'@rushstack/eslint-config': 2.5.1_j2dpqr3duszxwhwhg5puk5zm6e
'@types/lodash': 4.14.191
'@types/react': 17.0.45
'@types/react-dom': 17.0.17
'@types/webpack-env': 1.15.3
@ -1482,6 +1488,10 @@ packages:
'@jridgewell/sourcemap-codec': 1.4.14
dev: true
/@kurkle/color/0.3.2:
resolution: {integrity: sha512-fuscdXJ9G1qb7W8VdHi+IwRqij3lBkosAm4ydQtEmbY58OzHXqQhvlxqEkoz0yssNVn38bcpRWgA9PP+OGoisw==}
dev: false
/@leichtgewicht/ip-codec/2.0.4:
resolution: {integrity: sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A==}
dev: true
@ -3756,12 +3766,15 @@ packages:
/@types/lodash.isequal/4.5.6:
resolution: {integrity: sha512-Ww4UGSe3DmtvLLJm2F16hDwEQSv7U0Rr8SujLUA2wHI2D2dm8kPu6Et+/y303LfjTIwSBKXB/YTUcAKpem/XEg==}
dependencies:
'@types/lodash': 4.14.117
'@types/lodash': 4.14.191
dev: false
/@types/lodash/4.14.117:
resolution: {integrity: sha512-xyf2m6tRbz8qQKcxYZa7PA4SllYcay+eh25DN3jmNYY6gSTL7Htc/bttVdkqj2wfJGbeWlQiX8pIyJpKU+tubw==}
/@types/lodash/4.14.191:
resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
/@types/lru-cache/5.1.1:
resolution: {integrity: sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==}
dev: false
@ -5769,6 +5782,13 @@ packages:
moment: 2.29.4
dev: false
/chart.js/4.2.0:
resolution: {integrity: sha512-wbtcV+QKeH0F7gQZaCJEIpsNriFheacouJQTVIjITi3eQA8bTlIBoknz0+dgV79aeKLNMAX+nDslIVE/nJ3rzA==}
engines: {pnpm: ^7.0.0}
dependencies:
'@kurkle/color': 0.3.2
dev: false
/chartjs-color-string/0.6.0:
resolution: {integrity: sha512-TIB5OKn1hPJvO7JcteW4WY/63v6KwEdt6udfnDE9iCAZgy+V4SrbSxoIbTw/xkUIapjEI4ExGtD0+6D3KyFd7A==}
dependencies:

View File

@ -1,9 +1,10 @@
/* eslint-disable @typescript-eslint/no-explicit-any */
import { HttpClient, IHttpClientOptions, HttpClientResponse } from '@microsoft/sp-http';
import { TimeInterval, TimeSpan, Segments } from './enumHelper';
import { IPageViewCountProps, IPageViewDetailProps, defaultDateFormat, chartDateFormat, Dictionary } from './CommonProps';
import moment from 'moment';
export default class Helper {
private _appid: string = '';
private _appkey: string = '';
@ -11,8 +12,9 @@ export default class Helper {
private requestHeaders: Headers = new Headers();
private httpClientOptions: IHttpClientOptions = {};
private httpClient: HttpClient = null;
private cultureName:string=null;
constructor(appid: string, appkey: string, httpclient: HttpClient) {
constructor(appid: string, appkey: string, httpclient: HttpClient, cultureName:string) {
this._appid = appid;
this._appkey = appkey;
this.httpClient = httpclient;
@ -20,14 +22,17 @@ export default class Helper {
this.requestHeaders.append('Content-type', 'application/json; charset=utf-8');
this.requestHeaders.append('x-api-key', this._appkey);
this.httpClientOptions = { headers: this.requestHeaders };
this.cultureName= cultureName;
}
public getPageViewCount = async (timespan: TimeSpan, timeinterval: TimeInterval): Promise<IPageViewCountProps[]> => {
const finalRes: IPageViewCountProps[] = [];
const finalPostUrl: string = this._postUrl + `/metrics/pageViews/count?timespan=${timespan}&interval=${timeinterval}`;
const response: HttpClientResponse = await this.httpClient.get(finalPostUrl, HttpClient.configurations.v1, this.httpClientOptions);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const responseJson: any = await response.json();
if (responseJson.value && responseJson.value.segments.length > 0) {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const segments: any[] = responseJson.value.segments;
segments.map((seg: any) => {
finalRes.push({
@ -53,9 +58,9 @@ export default class Helper {
finalRes.push({
oriStartDate: mainseg.start,
oriEndDate: mainseg.end,
start: this.getFormattedDate(mainseg.start),
end: this.getFormattedDate(mainseg.end),
date: `${this.getFormattedDate(mainseg.start)} - ${this.getFormattedDate(mainseg.end)}`,
start: this.getFormattedDate(mainseg.start, 'L'),
end: this.getFormattedDate(mainseg.end, 'L'),
date: `${this.getFormattedDate(mainseg.start, 'L')} - ${this.getFormattedDate(mainseg.end, 'L')}`,
Url: seg[segment[0]],
count: seg['pageViews/count'].sum
});
@ -66,8 +71,8 @@ export default class Helper {
return finalRes;
}
public getResponseByQuery = async (query: string, useTimespan: boolean, timespan?: TimeSpan): Promise<any[]> => {
let finalRes: any[] = [];
public getResponseByQuery = async <T>(query: string, useTimespan: boolean, timespan?: TimeSpan): Promise<T[]> => {
let finalRes: T[] = [];
const urlQuery: string = useTimespan ? `timespan=${timespan}&query=${encodeURIComponent(query)}` : `query=${encodeURIComponent(query)}`;
const finalPostUrl: string = this._postUrl + `/query?${urlQuery}`;
const responseJson: any = await this.getAPIResponse(finalPostUrl);
@ -116,7 +121,7 @@ export default class Helper {
}
public getTimeSpanMenu = (): Dictionary<string>[] => {
const items: Dictionary<string>[] = []; //KK: any=> string
const items: Dictionary<string>[] = [];
Object.keys(TimeSpan).map(key => {
items.push({
text: key,
@ -138,11 +143,12 @@ export default class Helper {
}
public getLocalTime = (utcTime: string): string => {
return moment(utcTime).local().format(chartDateFormat);
}
public getFormattedDate = (datetime: string, format?: string): string => {
return moment(datetime).local().format(format ? format : defaultDateFormat);
public getFormattedDate = (datetime: string, format: string=defaultDateFormat): string => {
return moment(datetime).locale(this.cultureName).format(format ? format : defaultDateFormat);
}
public getQueryDateFormat = (datetime: string): string => {

View File

@ -6,7 +6,7 @@ export interface IPivotProps {
ShowLabel: boolean;
LabelText: string;
SelectedKey: string;
Items: any[];
Items: any[]; // eslint-disable-line @typescript-eslint/no-explicit-any
OnMenuClick: (item: PivotItem) => void;
}

View File

@ -2,13 +2,10 @@ import * as React from 'react';
import styles from '../CommonControl.module.scss';
import { IColumn, DetailsListLayoutMode, ConstrainMode, SelectionMode, IGroup, ShimmeredDetailsList } from '@fluentui/react';
import { findIndex, groupBy } from '@microsoft/sp-lodash-subset';
export interface Dictionary<T> {
[index: string]: T;
}
import { Dictionary } from '../CommonProps';
export interface IDataListProps {
Items: any[];
Items: any[]; // eslint-disable-line @typescript-eslint/no-explicit-any
Columns: IColumn[];
GroupBy: boolean;
GroupByCol?: string;
@ -18,14 +15,15 @@ export interface IDataListProps {
const DataList: React.FunctionComponent<IDataListProps> = (props) => {
const [columns, setColumns] = React.useState<IColumn[]>([]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const [items, setItems] = React.useState<any[]>([]);
const [groups, setGroups] = React.useState<IGroup[]>([]);
const _getItemIndex = (key): number => {
return findIndex(props.Items, (o) => { return o.date == key; });
const _getItemIndex = (key: string): number => {
return findIndex(props.Items, (o) => { return o.date === key; });
};
const _buildGroups = () => {
let grouped: Dictionary<string[]> = groupBy(props.Items, props.GroupByCol); //KK: any=> string
const _buildGroups = ():void => {
const grouped: Dictionary<string[]> = groupBy(props.Items, props.GroupByCol);
const groupsTemp: IGroup[] = [];
Object.keys(grouped).map((key, index) => {
groupsTemp.push({
@ -65,7 +63,7 @@ const DataList: React.FunctionComponent<IDataListProps> = (props) => {
constrainMode={ConstrainMode.unconstrained}
isHeaderVisible={true}
selectionMode={SelectionMode.none}
enableShimmer={true} />
enableShimmer={!items} />
) : (
<ShimmeredDetailsList
items={items}
@ -76,9 +74,8 @@ const DataList: React.FunctionComponent<IDataListProps> = (props) => {
constrainMode={ConstrainMode.unconstrained}
isHeaderVisible={true}
selectionMode={SelectionMode.none}
enableShimmer={true} />
enableShimmer={!items} />
)}
</div>
);
};

View File

@ -4,14 +4,15 @@ import styles from '../CommonControl.module.scss';
import { ChartControl, ChartType } from '@pnp/spfx-controls-react/lib/ChartControl';
import { AppInsightsProps } from '../../webparts/appInsightsDashboard/components/AppInsightsDashboard';
import { TimeInterval, TimeSpan, Segments } from '../enumHelper';
import { IPageViewDetailProps, IPageViewCountProps } from '../CommonProps';
import { IPageViewDetailProps, IPageViewCountProps, Dictionary } from '../CommonProps';
import SectionTitle from '../components/SectionTitle';
import CustomPivot from '../components/CustomPivot';
import DataList from '../components/DataList';
import Helper from '../Helper';
import { IColumn, PivotItem, Icon, IconType, css, Spinner, MessageBar, MessageBarType } from '@fluentui/react';
import { IColumn, PivotItem, Icon, css, Spinner, MessageBar, MessageBarType } from '@fluentui/react';
const map: any = require('lodash/map');
import {map} from 'lodash'
import { ChartData, ChartOptions } from 'chart.js';
export interface IPageViewsProps {
helper: Helper;
@ -23,43 +24,47 @@ const PageViews: React.FunctionComponent<IPageViewsProps> = (props) => {
const [loadingChart, setLoadingChart] = React.useState<boolean>(true);
const [loadingList, setLoadingList] = React.useState<boolean>(true);
const [noData, setNoData] = React.useState<boolean>(false);
const [timespanMenus, setTimeSpanMenus] = React.useState<any[]>([]);
const [timeintervalMenus, setTimeIntervalMenus] = React.useState<any[]>([]);
const [timespanMenus, setTimeSpanMenus] = React.useState<Dictionary<string>[]>([]);
const [timeintervalMenus, setTimeIntervalMenus] = React.useState<Dictionary<string>[]>([]);
const [selTimeSpan, setSelTimeSpan] = React.useState<string>('');
const [selTimeInterval, setSelTimeInterval] = React.useState<string>('');
const [menuClick, setMenuClick] = React.useState<boolean>(false);
const [chartData, setChartData] = React.useState<any>(null);
const [chartOptions, setChartOptions] = React.useState<any>(null);
const [chartData, setChartData] = React.useState<ChartData>(null);
const [chartOptions, setChartOptions] = React.useState<ChartOptions>(null);
const [listCols, setListCols] = React.useState<IColumn[]>([]);
const [items, setItems] = React.useState<any[]>([]);
const [items, setItems] = React.useState<IPageViewDetailProps[]>([]);
const _loadMenus = () => {
let tsMenus: any[] = props.helper.getTimeSpanMenu();
const _loadMenus = ():void => {
const tsMenus: Dictionary<string>[] = props.helper.getTimeSpanMenu();
setTimeSpanMenus(tsMenus);
setSelTimeSpan(tsMenus[4].key);
let tiMenus: any[] = props.helper.getTimeIntervalMenu();
const tiMenus: Dictionary<string>[] = props.helper.getTimeIntervalMenu();
setTimeIntervalMenus(tiMenus);
setSelTimeInterval(tiMenus[3].key);
};
const handleTimeSpanMenuClick = (item: PivotItem) => {
const handleTimeSpanMenuClick = (item: PivotItem):void => {
setMenuClick(true);
setSelTimeSpan(item.props.itemKey);
};
const handleTimeIntervalMenuClick = (item: PivotItem) => {
const handleTimeIntervalMenuClick = (item: PivotItem):void => {
setMenuClick(true);
setSelTimeInterval(item.props.itemKey);
};
const _loadPageViewsCount = async () => {
const _loadPageViewsCount = async (selTimeSpan:string, selTimeInterval:string) :Promise<void>=> {
if (menuClick) setLoadingChart(true);
let response: IPageViewCountProps[] = await props.helper.getPageViewCount(TimeSpan[selTimeSpan], TimeInterval[selTimeInterval]);
const response: IPageViewCountProps[] = await props.helper.getPageViewCount(
TimeSpan[selTimeSpan as keyof typeof TimeSpan],
TimeInterval[selTimeInterval as keyof typeof TimeInterval]);
//'as keyof typeof' added above because of the error:
//error TS7053: Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'typeof xxx'
if (response.length > 0) {
const data = { //: Chart.ChartData
const data : ChartData= { //
labels: map(response, 'date'),
datasets: [
{
label: 'Total Page Views',
fill: true,
lineTension: 0,
tension:0,
data: map(response, 'sum'),
backgroundColor: 'rgba(255, 159, 64, 0.2)',
borderColor: 'rgb(255, 159, 64)',
@ -68,13 +73,15 @@ const PageViews: React.FunctionComponent<IPageViewsProps> = (props) => {
]
};
setChartData(data);
const options= {//: Chart.ChartOptions
const options:ChartOptions= {
plugins:{
legend: {
display: false
},
title: {
display: false,
text: "Page Views"
}
},
responsive: true,
animation: {
@ -90,15 +97,15 @@ const PageViews: React.FunctionComponent<IPageViewsProps> = (props) => {
setMenuClick(false);
}
};
const _generateColumns = () => {
let cols: IColumn[] = [];
const _generateColumns = ():void => {
const cols: IColumn[] = [];
cols.push({
key: 'Url', name: 'Url', fieldName: 'Url', minWidth: 100, maxWidth: 350,
onRender: (item: any, index: number, column: IColumn) => {
onRender: (item: IPageViewDetailProps, index: number, column: IColumn) => {
return (
<div className={styles.textWithIcon}>
<div className={styles.fileiconDiv}>
<Icon iconName="FileASPX" ariaLabel={item.Url} iconType={IconType.Default} />
<Icon iconName="FileASPX" aria-label={item.Url}/>
</div>
{item.Url ? (
<a href={item.Url} target="_blank" className={styles.pageLink}>{item.Url}</a>
@ -120,9 +127,12 @@ const PageViews: React.FunctionComponent<IPageViewsProps> = (props) => {
});
setListCols(cols);
};
const _loadPageViews = async () => {
const _loadPageViews = async (selTimeSpan:string, selTimeInterval:string):Promise<void> => {
if (menuClick) setLoadingList(true);
let response: IPageViewDetailProps[] = await props.helper.getPageViews(TimeSpan[selTimeSpan], TimeInterval[selTimeInterval], [Segments.PV_URL]);
const response: IPageViewDetailProps[] = await props.helper.getPageViews(
TimeSpan[selTimeSpan as keyof typeof TimeSpan],
TimeInterval[selTimeInterval as keyof typeof TimeInterval],
[Segments.PV_URL]);
if (response.length > 0) {
_generateColumns();
setItems(response);
@ -136,11 +146,15 @@ const PageViews: React.FunctionComponent<IPageViewsProps> = (props) => {
};
React.useEffect(() => {
const fetchaData=async(selTimeSpan:string, selTimeInterval:string):Promise<void> =>{
if (selTimeSpan && selTimeInterval) {
setNoData(false);
_loadPageViewsCount();
_loadPageViews();
await _loadPageViewsCount(selTimeSpan, selTimeInterval);
await _loadPageViews(selTimeSpan, selTimeInterval);
}
}
fetchaData(selTimeSpan, selTimeInterval)
.catch(console.error); //KK:todo:logger
}, [selTimeSpan, selTimeInterval]);
React.useEffect(() => {

View File

@ -8,7 +8,8 @@ import SectionTitle from '../components/SectionTitle';
import Helper from '../Helper';
import { addDays, DatePicker, css, Spinner, MessageBar, MessageBarType } from '@fluentui/react';
const map: any = require('lodash/map');
import {map} from 'lodash'
import { ChartData, ChartOptions } from 'chart.js';
const today: Date = new Date(Date.now());
const startMaxDate: Date = addDays(today, -1);
@ -24,13 +25,13 @@ const PerformanceStatistics: React.FunctionComponent<IPerformanceProps> = (props
const mainProps = React.useContext(AppInsightsProps);
const [menuClick, setMenuClick] = React.useState<boolean>(false);
const [message, setMessage] = React.useState<string>('');
const [loadingChart1, setLoadingChart1] = React.useState<boolean>(true);
const [noDataChart1, setNoDataChart1] = React.useState<boolean>(false);
const [loadingChart, setloadingChart] = React.useState<boolean>(true);
const [noDataChart, setnoDataChart] = React.useState<boolean>(false);
const [startDate, setStartDate] = React.useState<Date>(null);
const [endDate, setEndDate] = React.useState<Date>(null);
const [chartData1, setChartData1] = React.useState<any>(null);
const [chartOptions1, setChartOptions1] = React.useState<any>(null);
const [chartData, setChartData] = React.useState<ChartData>(null);
const [chartOptions, setChartOptions] = React.useState<ChartOptions>(null);
const handleStartDateChange = (selDate: Date | null | undefined): void => {
setStartDate(selDate);
@ -38,8 +39,8 @@ const PerformanceStatistics: React.FunctionComponent<IPerformanceProps> = (props
const handleEndDateChange = (selDate: Date | null | undefined): void => {
setEndDate(selDate);
};
const _loadOperationsDurations = async () => {
if (menuClick) setLoadingChart1(true);
const _loadOperationsDurations = async ():Promise<void> => {
if (menuClick) setloadingChart(true);
let query: string = ``;
if (startDate && endDate) {
query += `let start=datetime("${props.helper.getQueryStartDateFormat(startDate.toUTCString())}");
@ -54,13 +55,14 @@ const PerformanceStatistics: React.FunctionComponent<IPerformanceProps> = (props
| summarize count_=sum(itemCount), avg(duration), percentiles(duration, 50, 95, 99))
| order by count_ desc
`;
let response: any[] = await props.helper.getResponseByQuery(query, false);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const response: any[] = await props.helper.getResponseByQuery(query, false);
if (response.length > 0) {
if (response[0][1] == 0 && response[0][2] == 'NaN') {
if (response[0][1] === 0 && response[0][2] === 'NaN') {
setMessage(strings.Msg_InvalidDate);
setNoDataChart1(true);
setnoDataChart(true);
} else {
let finalRes: IPerfDurationProps[] = [];
const finalRes: IPerfDurationProps[] = [];
response.map(res => {
finalRes.push({
PageName: res[0] ? res[0].indexOf('ModernDev -') >= 0 ? res[0].replace('ModernDev -', '') : res[0] : 'OverAll',
@ -71,48 +73,53 @@ const PerformanceStatistics: React.FunctionComponent<IPerformanceProps> = (props
PerDur_99: res[5]
});
});
const data= {//: Chart.ChartData
const data: ChartData= {
labels: map(finalRes, 'PageName'),
datasets: [
{
label: 'Count',
fill: true,
lineTension: 0,
tension:0,
data: map(finalRes, 'count'),
backgroundColor: props.helper.getRandomColor()
},
{
label: 'Avg Duration',
label: 'Avg Duration [ms]',
fill: true,
lineTension: 0,
tension:0,
data: map(finalRes, 'AvgDuration'),
backgroundColor: props.helper.getRandomColor()
},
{
label: 'Percentile Duration 50',
fill: true,
lineTension: 0,
tension:0,
data: map(finalRes, 'PerDur_50'),
backgroundColor: props.helper.getRandomColor()
},
{
label: 'Percentile Duration 95',
fill: true,
lineTension: 0,
tension:0,
data: map(finalRes, 'PerDur_95'),
backgroundColor: props.helper.getRandomColor()
},
{
label: 'Percentile Duration 99',
fill: true,
lineTension: 0,
tension:0,
data: map(finalRes, 'PerDur_99'),
backgroundColor: props.helper.getRandomColor()
}
]
};
setChartData1(data);
const options = {//: Chart.ChartOptions
setChartData(data);
const options :ChartOptions = {
responsive: true,
animation:{
easing: 'easeInQuad'
},
plugins:{
legend: {
display: false
},
@ -120,42 +127,44 @@ const PerformanceStatistics: React.FunctionComponent<IPerformanceProps> = (props
display: false,
text: ""
},
responsive: true,
animation: {
easing: 'easeInQuad'
},
scales:
{
xAxes: [{
x: {
stacked: true
}],
yAxes: [{
ticks: { beginAtZero: true },
},
y: {
min: 0 ,
stacked: true
}],
},
}
};
setChartOptions1(options);
setChartOptions(options);
}
} else {
setNoDataChart1(true);
setnoDataChart(true);
}
} else {
setMessage(strings.Msg_NoDate);
setNoDataChart1(true);
setnoDataChart(true);
}
setLoadingChart1(false);
setloadingChart(false);
setMenuClick(false);
};
React.useEffect(() => {
const fetchData= async(startDate:Date, endDate:Date): Promise<void>=>{
if (startDate && endDate) {
setMenuClick(true);
setNoDataChart1(false);
setnoDataChart(false);
setMessage('');
_loadOperationsDurations();
await _loadOperationsDurations();
}
}
fetchData(startDate, endDate)
.catch(console.error); //KK:todo:logger
}, [startDate, endDate]);
React.useEffect(() => {
@ -173,6 +182,7 @@ const PerformanceStatistics: React.FunctionComponent<IPerformanceProps> = (props
<label className={styles.dataLabel} style={{ paddingTop: '5px' }}>{"Date Range: "}</label>
<div style={{ paddingRight: '5px' }}>
<DatePicker
isRequired={false}
placeholder="Start Date..."
ariaLabel="Select start date"
@ -181,7 +191,7 @@ const PerformanceStatistics: React.FunctionComponent<IPerformanceProps> = (props
allowTextInput={false}
highlightSelectedMonth={true}
initialPickerDate={startMaxDate}
formatDate={(date?: Date) => { return props.helper.getFormattedDate(date.toUTCString()); }}
formatDate={(date?: Date) => { return props.helper.getFormattedDate(date.toUTCString(),'L'); }}
onSelectDate={handleStartDateChange}
value={startDate}
/>
@ -195,7 +205,7 @@ const PerformanceStatistics: React.FunctionComponent<IPerformanceProps> = (props
maxDate={maxDate}
allowTextInput={false}
highlightSelectedMonth={true}
formatDate={(date?: Date) => { return props.helper.getFormattedDate(date.toUTCString()); }}
formatDate={(date?: Date) => { return props.helper.getFormattedDate(date.toUTCString(),'L'); }}
onSelectDate={handleEndDateChange}
value={endDate}
/>
@ -205,15 +215,15 @@ const PerformanceStatistics: React.FunctionComponent<IPerformanceProps> = (props
</div>
<div className={css("ms-Grid-row", styles.content)}>
<div style={{ minHeight: '450px', maxHeight: '450px' }}>
{loadingChart1 ? (
{loadingChart ? (
<Spinner label={strings.Msg_LoadChart} labelPosition={"bottom"} />
) : (
<>
{!noDataChart1 ? (
{!noDataChart ? (
<ChartControl
type={ChartType.Bar}
data={chartData1}
options={chartOptions1}
data={chartData}
options={chartOptions}
/>
) : (
<MessageBar messageBarType={MessageBarType.error}>{message ? message : strings.Msg_NoData}</MessageBar>

View File

@ -9,10 +9,11 @@ import CustomPivot from './CustomPivot';
import Helper from '../Helper';
import DataList from './DataList';
import { addDays, IColumn, PivotItem, Icon, IconType, DatePicker, css, Spinner, MessageBar, MessageBarType } from '@fluentui/react';
import { IPageViewDetailProps } from '../CommonProps';
import { addDays, IColumn, PivotItem, Icon, DatePicker, css, Spinner, MessageBar, MessageBarType } from '@fluentui/react';
import { Dictionary, IPageViewDetailProps } from '../CommonProps';
const map: any = require('lodash/map');
import {map} from 'lodash'
import { ChartData, ChartOptions } from 'chart.js';
const today: Date = new Date(Date.now());
const startMaxDate: Date = addDays(today, -1);
@ -28,20 +29,20 @@ const UserStatistics: React.FunctionComponent<IUserStatisticsProps> = (props) =>
const mainProps = React.useContext(AppInsightsProps);
const [loadingChart, setLoadingChart] = React.useState<boolean>(true);
const [loadingList, setLoadingList] = React.useState<boolean>(true);
const [timespanMenus, setTimeSpanMenus] = React.useState<any[]>([]);
const [timespanMenus, setTimeSpanMenus] = React.useState<Dictionary<string>[]>([]);
const [selTimeSpan, setSelTimeSpan] = React.useState<string>('');
const [menuClick, setMenuClick] = React.useState<boolean>(false);
const [noData, setNoData] = React.useState<boolean>(false);
const [noListData, setNoListData] = React.useState<boolean>(false);
const [chartData, setChartData] = React.useState<any>(null);
const [chartOptions, setChartOptions] = React.useState<any>(null);
const [chartData, setChartData] = React.useState<ChartData>(null);
const [chartOptions, setChartOptions] = React.useState<ChartOptions>(null);
const [startDate, setStartDate] = React.useState<Date>(null);
const [endDate, setEndDate] = React.useState<Date>(null);
const [listCols, setListCols] = React.useState<IColumn[]>([]);
const [items, setItems] = React.useState<any[]>([]);
const [items, setItems] = React.useState<IPageViewDetailProps[]>([]);
const _loadMenus = (): void => {
let tsMenus: any[] = props.helper.getTimeSpanMenu();
const tsMenus: Dictionary<string>[] = props.helper.getTimeSpanMenu();
setTimeSpanMenus(tsMenus);
setSelTimeSpan(tsMenus[4].key);
};
@ -74,29 +75,38 @@ const UserStatistics: React.FunctionComponent<IUserStatisticsProps> = (props) =>
| order by timestamp asc
`;
}
let response: any[] = await props.helper.getResponseByQuery(query, (startDate && endDate) ? false : true, TimeSpan[selTimeSpan]);
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const response: any[] = await props.helper.getResponseByQuery(query, (startDate && endDate) ? false : true, TimeSpan[selTimeSpan as keyof typeof TimeSpan]);
if (response.length > 0) {
let results: any[] = [];
response.map((res: any[]) => {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const results: any[] = [];
// eslint-disable-next-line @typescript-eslint/no-explicit-any
response.map((res: any) => {
results.push({
oriDate: res[0],
date: props.helper.getLocalTime(res[0]),
sum: res[1]
});
});
const data = {//: Chart.ChartData
const data: ChartData = {
labels: map(results, 'date'),
datasets: [
{
label: 'Total Users',
fill: true,
lineTension: 0,
tension:0,
data: map(results, 'sum'),
}
]
};
setChartData(data);
const options = {//: Chart.ChartOptions
const options :ChartOptions= {
responsive: true,
animation: {
easing: 'easeInQuad'
},
plugins:{
legend: {
display: false
},
@ -104,21 +114,12 @@ const UserStatistics: React.FunctionComponent<IUserStatisticsProps> = (props) =>
display: false,
text: ""
},
responsive: true,
animation: {
easing: 'easeInQuad'
},
scales:
{
yAxes: [
{
ticks:
{
beginAtZero: true
scales:{
y: {
min:0
}
}
]
}
};
setChartOptions(options);
setLoadingChart(false);
@ -131,9 +132,10 @@ const UserStatistics: React.FunctionComponent<IUserStatisticsProps> = (props) =>
};
const _generateColumns = ():void => {
let cols: IColumn[] = [];
const cols: IColumn[] = [];
cols.push({
key: 'user', name: 'User', fieldName: 'user', minWidth: 100, maxWidth: 150,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onRender: (item: any, index: number, column: IColumn) => {
return (
<div className={styles.textWithIcon}>
@ -148,11 +150,12 @@ const UserStatistics: React.FunctionComponent<IUserStatisticsProps> = (props) =>
});
cols.push({
key: 'Url', name: 'Url', fieldName: 'Url', minWidth: 100, maxWidth: 350,
// eslint-disable-next-line @typescript-eslint/no-explicit-any
onRender: (item: any, index: number, column: IColumn) => {
return (
<div className={styles.textWithIcon}>
<div className={styles.fileiconDiv}>
<Icon iconName="FileASPX" ariaLabel={item.Url} iconType={IconType.Default} />
<Icon iconName="FileASPX" aria-label={item.Url}/>
</div>
{item.Url ? (
<a href={item.Url} target="_blank" className={styles.pageLink}>{item.Url}</a>
@ -176,7 +179,7 @@ const UserStatistics: React.FunctionComponent<IUserStatisticsProps> = (props) =>
response = await props.helper.getUserPageViews(`${props.helper.getFormattedDate(startDate.toUTCString(), 'YYYY-MM-DD')}/${props.helper.getFormattedDate(addDays(endDate, 1).toUTCString(), 'YYYY-MM-DD')}`,
TimeInterval["1 Hour"], [Segments.Cust_UserTitle, Segments.PV_URL]);
} else {
response = await props.helper.getUserPageViews(TimeSpan[selTimeSpan], TimeInterval["1 Hour"], [Segments.Cust_UserTitle, Segments.PV_URL]);
response = await props.helper.getUserPageViews(TimeSpan[selTimeSpan as keyof typeof TimeSpan], TimeInterval["1 Hour"], [Segments.Cust_UserTitle, Segments.PV_URL]);
}
if (response.length > 0) {
_generateColumns();
@ -191,12 +194,17 @@ const UserStatistics: React.FunctionComponent<IUserStatisticsProps> = (props) =>
};
React.useEffect((): void => {
const fetchData= async(selTimeSpan:string, startDate:Date, endDate:Date): Promise<void>=>{
if (selTimeSpan || (startDate && endDate)) {
setNoData(false);
setNoListData(false);
_loadUserStatistics();
_loadUsersPageViewsList();
await _loadUserStatistics();
await _loadUsersPageViewsList();
}
}
fetchData(selTimeSpan, startDate, endDate)
.catch(console.error); //KK:todo:logger
}, [selTimeSpan, startDate, endDate]);
React.useEffect(():void => {

View File

@ -22,11 +22,12 @@ export default class AppInsightsDashboardWebPart extends BaseClientSideWebPart<I
const element: React.ReactElement<IAppInsightsDashboardProps> = React.createElement(
AppInsightsDashboard,
{
AppId: this.properties.AppId,
AppKey: this.properties.AppKey,
AppId: this.properties.AppId ?? '89ae2a2e-a9a1-480a-bcb1-f36116637d86',
AppKey: this.properties.AppKey ?? 'sgpmcdfvbjfd34imv6o2exx8s5x2ncjwahn8oewb',
DisplayMode: this.displayMode,
onConfigure: this._onConfigure,
httpClient: this.context.httpClient
httpClient: this.context.httpClient,
cultureName: this.context.pageContext.legacyPageContext.currentCultureName
}
);

View File

@ -16,6 +16,7 @@ export interface IAppInsightsDashboardProps {
DisplayMode: DisplayMode;
onConfigure: () => void;
httpClient: HttpClient;
cultureName:string;
}
export const AppInsightsProps = React.createContext<IAppInsightsDashboardProps>(null);
@ -25,7 +26,7 @@ const AppInsightsDashboard: React.FunctionComponent<IAppInsightsDashboardProps>
const [helper, setHelper] = React.useState<Helper>(null); //KK
React.useEffect(() => {
setHelper(new Helper(props.AppId, props.AppKey, props.httpClient));
setHelper(new Helper(props.AppId, props.AppKey, props.httpClient, props.cultureName));
}, [props.AppId, props.AppKey]);
return (

View File

@ -14,7 +14,7 @@
"inlineSources": false,
"strictNullChecks": false,
"noUnusedLocals": false,
// "noImplicitAny": true,
"noImplicitAny": true,
"allowSyntheticDefaultImports": true,
"typeRoots": [
"./node_modules/@types",