date formats, eslint no-explicit-any
This commit is contained in:
parent
750632b6da
commit
29ef721e93
|
@ -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'));
|
||||
|
|
|
@ -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",
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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 => {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
title: {
|
||||
display: false,
|
||||
text: "Page Views"
|
||||
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(() => {
|
||||
if (selTimeSpan && selTimeInterval) {
|
||||
setNoData(false);
|
||||
_loadPageViewsCount();
|
||||
_loadPageViews();
|
||||
const fetchaData=async(selTimeSpan:string, selTimeInterval:string):Promise<void> =>{
|
||||
if (selTimeSpan && selTimeInterval) {
|
||||
setNoData(false);
|
||||
await _loadPageViewsCount(selTimeSpan, selTimeInterval);
|
||||
await _loadPageViews(selTimeSpan, selTimeInterval);
|
||||
}
|
||||
}
|
||||
fetchaData(selTimeSpan, selTimeInterval)
|
||||
.catch(console.error); //KK:todo:logger
|
||||
}, [selTimeSpan, selTimeInterval]);
|
||||
|
||||
React.useEffect(() => {
|
||||
|
|
|
@ -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,91 +73,98 @@ 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
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
title: {
|
||||
display: false,
|
||||
text: ""
|
||||
},
|
||||
setChartData(data);
|
||||
const options :ChartOptions = {
|
||||
responsive: true,
|
||||
animation: {
|
||||
animation:{
|
||||
easing: 'easeInQuad'
|
||||
},
|
||||
plugins:{
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
title: {
|
||||
display: false,
|
||||
text: ""
|
||||
},
|
||||
},
|
||||
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(() => {
|
||||
if (startDate && endDate) {
|
||||
setMenuClick(true);
|
||||
setNoDataChart1(false);
|
||||
setMessage('');
|
||||
_loadOperationsDurations();
|
||||
const fetchData= async(startDate:Date, endDate:Date): Promise<void>=>{
|
||||
if (startDate && endDate) {
|
||||
setMenuClick(true);
|
||||
setnoDataChart(false);
|
||||
setMessage('');
|
||||
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>
|
||||
|
|
|
@ -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,50 +75,50 @@ 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
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
title: {
|
||||
display: false,
|
||||
text: ""
|
||||
},
|
||||
const options :ChartOptions= {
|
||||
responsive: true,
|
||||
animation: {
|
||||
easing: 'easeInQuad'
|
||||
},
|
||||
scales:
|
||||
{
|
||||
yAxes: [
|
||||
{
|
||||
ticks:
|
||||
{
|
||||
beginAtZero: true
|
||||
}
|
||||
}
|
||||
]
|
||||
plugins:{
|
||||
legend: {
|
||||
display: false
|
||||
},
|
||||
title: {
|
||||
display: false,
|
||||
text: ""
|
||||
},
|
||||
},
|
||||
scales:{
|
||||
y: {
|
||||
min:0
|
||||
}
|
||||
}
|
||||
};
|
||||
setChartOptions(options);
|
||||
|
@ -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 => {
|
||||
if (selTimeSpan || (startDate && endDate)) {
|
||||
setNoData(false);
|
||||
setNoListData(false);
|
||||
_loadUserStatistics();
|
||||
_loadUsersPageViewsList();
|
||||
const fetchData= async(selTimeSpan:string, startDate:Date, endDate:Date): Promise<void>=>{
|
||||
if (selTimeSpan || (startDate && endDate)) {
|
||||
setNoData(false);
|
||||
setNoListData(false);
|
||||
await _loadUserStatistics();
|
||||
await _loadUsersPageViewsList();
|
||||
}
|
||||
}
|
||||
fetchData(selTimeSpan, startDate, endDate)
|
||||
.catch(console.error); //KK:todo:logger
|
||||
|
||||
}, [selTimeSpan, startDate, endDate]);
|
||||
|
||||
React.useEffect(():void => {
|
||||
|
|
|
@ -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
|
||||
}
|
||||
);
|
||||
|
||||
|
|
|
@ -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 (
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
"inlineSources": false,
|
||||
"strictNullChecks": false,
|
||||
"noUnusedLocals": false,
|
||||
// "noImplicitAny": true,
|
||||
"noImplicitAny": true,
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"typeRoots": [
|
||||
"./node_modules/@types",
|
||||
|
|
Loading…
Reference in New Issue