changing react-scripts version / create-react-app ver

This commit is contained in:
morsh 2017-03-19 15:45:09 +02:00
Родитель 4f9d84310f
Коммит e59e02a8d1
154 изменённых файлов: 3250 добавлений и 8162 удалений

Просмотреть файл

@ -1,2 +0,0 @@
[config]
command = bash ./scripts/deployment/azure-deploy.sh

46
.gitignore поставляемый
Просмотреть файл

@ -1,40 +1,18 @@
# Logs
logs
*.log
# See http://help.github.com/ignore-files/ for more about ignoring files.
# Runtime data
pids
*.pid
*.seed
# dependencies
/node_modules
# Directory for instrumented libs generated by jscoverage/JSCover
lib-cov
# testing
/coverage
# Coverage directory used by tools like istanbul
coverage
# production
/build
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
.grunt
# node-waf configuration
.lock-wscript
# Compiled binary addons (http://nodejs.org/api/addons.html)
build/Release
# Dependency directory
# https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git
node_modules
node_modules_old
bower_components
dist
build
tmp
*.pem
*.config.json
# Localhost config file
# misc
.DS_Store
.env
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editors
.vscode/*

6
.vscode/settings.json поставляемый Normal file
Просмотреть файл

@ -0,0 +1,6 @@
// Place your settings in this file to overwrite default and user settings.
{
"files.exclude": {
"**/*.css": { "when": "$(basename).scss"}
}
}

22
LICENSE
Просмотреть файл

@ -1,22 +0,0 @@
The MIT License (MIT)
Copyright (c) 2015 Catalyst Code
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

1382
README.md

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,26 +0,0 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostingPlanName": {
"value": "changeme"
},
"webSiteName": {
"value": "changeme"
},
"siteTitle": {
"value": "Change Me"
},
"logoURl": {
"value": "none"
},
"repoURL": {
"value": "https://github.com/CatalystCode/bot-fmk-dashboard ==> fork & copy new url"
},
"branch": {
"value": "master"
}
}
}

Просмотреть файл

@ -1,26 +0,0 @@
{
"$schema": "https://schema.management.azure.com/schemas/2015-01-01/deploymentParameters.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostingPlanName": {
"value": "mrsh-bots-dash"
},
"webSiteName": {
"value": "mrsh-bots-dash"
},
"siteTitle": {
"value": "mrsh-bots-dash"
},
"logoURl": {
"value": "none"
},
"repoURL": {
"value": "https://github.com/CatalystCode/bot-fmk-dashboard"
},
"branch": {
"value": "master"
}
}
}

Просмотреть файл

@ -1,178 +0,0 @@
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostingPlanName": { "type": "string" },
"webSiteName": { "type": "string" },
"siteTitle": { "type": "string" },
"logoURl": { "type": "string" },
"hostingPlanSKU": {
"type": "string",
"allowedValues": [
"Free",
"Shared",
"Basic",
"Standard"
],
"defaultValue": "Basic",
"metadata": {
"description": "SKU value"
}
},
"hostingPlanWorkerSize": {
"type": "string",
"allowedValues": [
"0",
"1",
"2"
],
"defaultValue": "1",
"metadata": {
"description": "Worker Size( 0=Small, 1=Medium, 2=Large )"
}
},
"repoURL": {
"type": "string",
"defaultValue": "https://github.com/CatalystCode/bot-fmk-dashboard.git",
"metadata": {
"description": "Fork that repo than use the new url in [parameters file:repoUrl:value]."
}
},
"branch": {
"type": "string",
"defaultValue": "master",
"metadata": {
"description": "The branch of the GitHub repository to use."
}
}
},
"variables": {
"applicationInsightName": "[concat(parameters('webSiteName'), '-ai')]"
},
"resources": [
{
"apiVersion": "2015-04-01",
"name": "[parameters('hostingPlanName')]",
"type": "Microsoft.Web/serverfarms",
"location": "[resourceGroup().location]",
"properties": {
"name": "[parameters('hostingPlanName')]",
"sku": "[parameters('hostingPlanSKU')]",
"workerSize": "[parameters('hostingPlanWorkerSize')]",
"numberOfWorkers": 1
}
},
{
"name": "[parameters('webSiteName')]",
"type": "Microsoft.Web/sites",
"location": "[resourceGroup().location]",
"apiVersion": "2015-08-01",
"dependsOn": [
"[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
],
"tags": {
"[concat('hidden-related:', resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName')))]": "Resource",
"displayName": "DashboardWebSite"
},
"properties": {
"name": "[parameters('webSiteName')]",
"serverFarmId": "[resourceId('Microsoft.Web/serverfarms', parameters('hostingPlanName'))]"
},
"resources": [
{
"apiVersion": "2015-08-01",
"name": "web",
"type": "config",
"dependsOn": [
"[concat('Microsoft.Web/sites/', parameters('webSiteName'))]"
],
"properties": {
"alwaysOn": true,
"virtualApplications": [
{
"virtualPath": "/",
"physicalPath": "site\\wwwroot"
},
{
"virtualPath": "/MyApp",
"physicalPath": "site\\wwwroot"
}
],
"defaultDocuments": [
"index.html",
"hostingstart.html"
]
}
},
{
"name": "appsettings",
"type": "config",
"apiVersion": "2015-08-01",
"dependsOn": [
"[resourceId('Microsoft.Web/sites', parameters('webSiteName'))]",
"[resourceId('Microsoft.Insights/components', variables('applicationInsightName'))]"
],
"tags": {
"displayName": "ApplicationSettings"
},
"properties": {
"WEBSITE_NODE_DEFAULT_VERSION": "6.9.1",
"SCM_COMMAND_IDLE_TIMEOUT": "7200",
"REACT_APP_SITE_LOGO": "[parameters('logoURl')]",
"REACT_APP_SITE_TITLE ": "[parameters('siteTitle')]",
"REACT_APP_APP_INSIGHTS_APPID": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightName')), '2014-04-01').AppId]",
"REACT_APP_APP_INSIGHTS_APIKEY": "TODO: AppInsights ==> Settings ==> API Access ==> Create API Key ==> Generate ==> [Copy Key here]"
}
},
{
"apiVersion": "2015-08-01",
"name": "web",
"type": "sourcecontrols",
"dependsOn": [
"[concat('Microsoft.Web/sites/', parameters('webSiteName'))]",
"[resourceId('Microsoft.Web/sites/config', parameters('webSiteName'), 'appsettings')]"
],
"properties": {
"RepoUrl": "[parameters('repoURL')]",
"branch": "[parameters('branch')]",
"IsManualIntegration": false
}
}
]
},
{
"name": "[variables('applicationInsightName')]",
"type": "Microsoft.Insights/components",
"location": "[resourceGroup().location]",
"apiVersion": "2014-04-01",
"dependsOn": [
"[concat('Microsoft.Web/sites/', parameters('webSiteName'))]"
],
"tags": {
"displayName": "Component ApplicationInsight"
},
"properties": {
"applicationId": "[resourceId('Microsoft.Web/sites', parameters('webSiteName'))]"
},
"resources": [
{
"apiVersion": "2015-05-01",
"name": "dashboard-keys",
"type": "apikeys",
"dependsOn": [
"[concat('Microsoft.Insights/components/', variables('applicationInsightName'))]"
],
"properties": {
"linkedReadProperties": "[concat(resourceId('Microsoft.insights/components', variables('applicationInsightName'))), '/api']"
}
}
]
}
],
"outputs": {
"appInsightsInstrumentationKey": {
"type": "string",
"value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightName')), '2014-04-01').InstrumentationKey]"
}
}
}

Просмотреть файл

@ -1 +0,0 @@
azure group create <resource group name> -l <location> -f dashboard.template.json -e dashboard.parameters.private.json

Просмотреть файл

@ -1,79 +0,0 @@
{
"$schema": "http://schema.management.azure.com/schemas/2015-01-01/deploymentTemplate.json#",
"contentVersion": "1.0.0.0",
"parameters": {
"hostingPlanName": { "type": "string" },
"webSiteName": { "type": "string" },
"siteTitle": { "type": "string" },
"logoURl": { "type": "string" },
"hostingPlanSKU": {
"type": "string",
"allowedValues": [
"Free",
"Shared",
"Basic",
"Standard"
],
"defaultValue": "Basic",
"metadata": {
"description": "SKU value"
}
},
"hostingPlanWorkerSize": {
"type": "string",
"allowedValues": [
"0",
"1",
"2"
],
"defaultValue": "1",
"metadata": {
"description": "Worker Size( 0=Small, 1=Medium, 2=Large )"
}
},
"repoURL": {
"type": "string",
"defaultValue": "https://github.com/CatalystCode/bot-fmk-dashboard.git",
"metadata": {
"description": "Fork that repo than use the new url in [parameters file:repoUrl:value]."
}
},
"branch": {
"type": "string",
"defaultValue": "master",
"metadata": {
"description": "The branch of the GitHub repository to use."
}
}
},
"variables": {
"applicationInsightName": "[concat(parameters('webSiteName'), '-ai')]"
},
"resources": [
{
"name": "[variables('applicationInsightName')]",
"type": "Microsoft.Insights/components",
"location": "[resourceGroup().location]",
"apiVersion": "2014-04-01",
"resources": [
{
"apiVersion": "2015-05-01",
"name": "dashboard-keys",
"type": "apikeys",
"dependsOn": [
"[concat('Microsoft.Insights/components/', variables('applicationInsightName'))]"
],
"properties": {
"linkedReadProperties": "[concat(resourceId('Microsoft.insights/components', variables('applicationInsightName')), '/api')]"
}
}
]
}
],
"outputs": {
"appInsightsInstrumentationKey": {
"type": "string",
"value": "[reference(resourceId('Microsoft.Insights/components', variables('applicationInsightName')), '2014-04-01').InstrumentationKey]"
}
}
}

Двоичные данные
docs/bot-framedash-msgs.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 76 KiB

Двоичные данные
docs/bot-framedash.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 91 KiB

Просмотреть файл

@ -1,63 +1,43 @@
{
"name": "bot-fmk-dashboard",
"description": "This repository holds all client / front-end related code for the Microsoft/OCHA Hackfest. .",
"version": "0.0.1",
"license": "MIT",
"main": "./dist/js/index.min.js",
"repository": {
"type": "git",
"url": "https://github.com/CatalystCode/bot-fmk-dashboard.git"
},
"maintainers": [
{
"name": "mor shemesh",
"email": "mor.shemesh@gmail.com"
}
],
"name": "my-app",
"version": "0.1.0",
"private": true,
"devDependencies": {
"@types/alt": "^0.16.31",
"@types/lodash": "^4.14.48",
"@types/alt": "^0.16.32",
"@types/jest": "^19.2.2",
"@types/jquery": "^2.0.41",
"@types/lodash": "^4.14.55",
"@types/node": "^7.0.8",
"@types/react-dom": "^0.14.20",
"mocha": "^3.1.2",
"@types/react": "^15.0.16",
"@types/react-dom": "^0.14.23",
"@types/react-router": "^3.0.8",
"node-sass": "^4.5.0",
"npm-run-all": "^4.0.2",
"react-scripts": "^0.6.0"
"react-scripts-ts": "1.1.6"
},
"dependencies": {
"alt": "^0.18.6",
"alt-utils": "^1.0.0",
"async": "^2.0.1",
"classnames": "^2.2.5",
"jquery": "^3.1.1",
"lodash": "^4.17.2",
"material-ui": "^0.16.5",
"moment": "^2.17.1",
"nprogress": "^0.2.0",
"react": "^15.4.1",
"react-addons-create-fragment": "^15.4.1",
"jquery": "^3.2.0",
"lodash": "^4.17.4",
"material-colors": "^1.2.5",
"moment": "^2.18.0",
"react": "^15.4.2",
"react-addons-css-transition-group": "^15.4.2",
"react-dom": "^15.4.1",
"react-grid-layout": "^0.13.9",
"react-infinite": "^0.10.0",
"react-list-view": "^1.0.0",
"react-md": "^1.0.7",
"react-router": "^3.0.0",
"react-tap-event-plugin": "^2.0.1",
"recharts": "^0.19.1",
"request": "^2.78.0"
"react-addons-transition-group": "^15.4.2",
"react-dom": "^15.4.2",
"react-grid-layout": "^0.14.4",
"react-md": "^1.0.10",
"react-router": "3.0.0",
"recharts": "^0.21.2"
},
"scripts": {
"build-css": "node-sass ts-src/ -o src/",
"watch-css": "npm run build-css && node-sass ts-src/ -o src/ --watch --recursive",
"start-js": "react-scripts start",
"build-css": "node-sass src/ -o src/",
"watch-css": "yarn run build-css && node-sass src/ -o src/ --watch --recursive",
"start-js": "react-scripts-ts start",
"start": "npm-run-all -p watch-css start-js",
"build": "npm run build-css && react-scripts build",
"react-test": "react-scripts test --env=jsdom",
"eject": "react-scripts eject",
"test": "mocha"
},
"eslintConfig": {
"extends": "./node_modules/react-scripts/config/eslint.js"
"build": "yarn run build-css && react-scripts-ts build",
"test": "react-scripts-ts test --env=jsdom",
"eject": "react-scripts-ts eject"
}
}

Двоичные данные
public/images/OCHA_Logo.png

Двоичный файл не отображается.

До

Ширина:  |  Высота:  |  Размер: 6.1 KiB

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

До

Ширина:  |  Высота:  |  Размер: 28 KiB

Просмотреть файл

@ -1,32 +1,33 @@
<!DOCTYPE html>
<html>
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<title>Bot Analytics Dashboard</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico">
<script src="https://secure.aadcdn.microsoftonline-p.com/lib/1.0.0/js/adal.min.js"></script>
<link href="https://fonts.googleapis.com/icon?family=Material+Icons" rel="stylesheet">
<link rel='stylesheet' href='https://fonts.googleapis.com/css?family=Roboto:300,400,500' />
<!--
Notice the use of %PUBLIC_URL% in the tag above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.
<script>
String.prototype.format = function(){
var content = this;
for (var i=0; i < arguments.length; i++)
{
var replacement = '{' + i + '}';
content = content.replace(replacement, arguments[i]);
}
return content;
};
</script>
Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<div id="app">
</div>
<style>
#map { height: 100%; }
</style>
<div id='map'></div>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.
You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.
To begin the development, run `npm start`.
To create a production bundle, use `npm run build`.
-->
</body>
<script src="https://use.fontawesome.com/ae5201ad7c.js"></script>
</html>
</html>

Просмотреть файл

@ -1,24 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
This configuration file is required if iisnode is used to run node processes behind
IIS or IIS Express. For more information, visit:
https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config
-->
<configuration>
<system.webServer>
<handlers>
<clear />
<add
name="StaticFile"
path="*" verb="*"
modules="StaticFileModule,DefaultDocumentModule,DirectoryListingModule"
resourceType="Either"
requireAccess="Read" />
</handlers>
<staticContent>
<mimeMap fileExtension=".*" mimeType="application/octet-stream" />
</staticContent>
</system.webServer>
</configuration>

Просмотреть файл

@ -1,148 +0,0 @@
#!/bin/bash
# ----------------------
# KUDU Deployment Script
# Version: 0.2.2
# ----------------------
# Helpers
# -------
exitWithMessageOnError () {
if [ ! $? -eq 0 ]; then
echo "An error has occurred during web site deployment."
echo $1
exit 1
fi
}
# Prerequisites
# -------------
# Verify node.js installed
hash node 2>/dev/null
exitWithMessageOnError "Missing node.js executable, please install node.js, if already installed make sure it can be reached from current environment."
# Setup
# -----
SCRIPT_DIR="${BASH_SOURCE[0]%\\*}"
SCRIPT_DIR="${SCRIPT_DIR%/*}"
ARTIFACTS=$SCRIPT_DIR/../artifacts
KUDU_SYNC_CMD=${KUDU_SYNC_CMD//\"}
if [[ ! -n "$DEPLOYMENT_SOURCE" ]]; then
DEPLOYMENT_SOURCE=$SCRIPT_DIR
fi
if [[ ! -n "$NEXT_MANIFEST_PATH" ]]; then
NEXT_MANIFEST_PATH=$ARTIFACTS/manifest
if [[ ! -n "$PREVIOUS_MANIFEST_PATH" ]]; then
PREVIOUS_MANIFEST_PATH=$NEXT_MANIFEST_PATH
fi
fi
if [[ ! -n "$DEPLOYMENT_TARGET" ]]; then
DEPLOYMENT_TARGET=$ARTIFACTS/wwwroot
else
KUDU_SERVICE=true
fi
WEB_CONFIG=$DEPLOYMENT_SOURCE/scripts/deployment/Web.config
if [[ ! -n "$KUDU_SYNC_CMD" ]]; then
# Install kudu sync
echo Installing Kudu Sync
npm install kudusync -g --silent
exitWithMessageOnError "npm failed"
if [[ ! -n "$KUDU_SERVICE" ]]; then
# In case we are running locally this is the correct location of kuduSync
KUDU_SYNC_CMD=kuduSync
else
# In case we are running on kudu service this is the correct location of kuduSync
KUDU_SYNC_CMD=$APPDATA/npm/node_modules/kuduSync/bin/kuduSync
fi
fi
# Node Helpers
# ------------
selectNodeVersion () {
echo Selecting Node Version
if [[ -n "$KUDU_SELECT_NODE_VERSION_CMD" ]]; then
SELECT_NODE_VERSION="$KUDU_SELECT_NODE_VERSION_CMD \"$DEPLOYMENT_SOURCE\" \"$DEPLOYMENT_TARGET\" \"$DEPLOYMENT_TEMP\""
eval $SELECT_NODE_VERSION
exitWithMessageOnError "select node version failed"
if [[ -e "$DEPLOYMENT_TEMP/__nodeVersion.tmp" ]]; then
NODE_EXE=`cat "$DEPLOYMENT_TEMP/__nodeVersion.tmp"`
exitWithMessageOnError "getting node version failed"
fi
if [[ -e "$DEPLOYMENT_TEMP/.tmp" ]]; then
NPM_JS_PATH=`cat "$DEPLOYMENT_TEMP/__npmVersion.tmp"`
exitWithMessageOnError "getting npm version failed"
fi
if [[ ! -n "$NODE_EXE" ]]; then
NODE_EXE=node
fi
NPM_CMD="\"$NODE_EXE\" \"$NPM_JS_PATH\""
else
NPM_CMD=npm
NODE_EXE=node
fi
}
##################################################################################################################################
# Deployment
# ----------
echo Handling custom node.js deployment.
touch server.js
selectNodeVersion
if [ -e "$DEPLOYMENT_SOURCE/package.json" ]; then
echo Installing Create React App Package
eval $NPM_CMD install -g create-react-app
exitWithMessageOnError "create react app install failed"
echo Installing NPM Packages
eval $NPM_CMD install
exitWithMessageOnError "npm failed"
echo Building React App
eval $NPM_CMD run build
exitWithMessageOnError "react build failed"
cd - > /dev/null
fi
if [ -e "$WEB_CONFIG" ]; then
echo Copying $WEB_CONFIG over to the build folder
cp $WEB_CONFIG build/
exitWithMessageOnError "Unable to copy $WEB_CONFIG over to build"
fi
if [[ "$IN_PLACE_DEPLOYMENT" -ne "1" ]]; then
echo Syncing Files
"$KUDU_SYNC_CMD" -v 50 -f "$DEPLOYMENT_SOURCE/build" -t "$DEPLOYMENT_TARGET" -n "$NEXT_MANIFEST_PATH" -p "$PREVIOUS_MANIFEST_PATH" -i ".git;.hg;.deployment;deploy.sh"
exitWithMessageOnError "Kudu Sync failed"
fi
##################################################################################################################################
# Post deployment stub
if [[ -n "$POST_DEPLOYMENT_ACTION" ]]; then
POST_DEPLOYMENT_ACTION=${POST_DEPLOYMENT_ACTION//\"}
cd "${POST_DEPLOYMENT_ACTION_DIR%\\*}"
"$POST_DEPLOYMENT_ACTION"
exitWithMessageOnError "post deployment action failed"
fi
echo "Finished successfully."

24
src/App.css Normal file
Просмотреть файл

@ -0,0 +1,24 @@
.App {
text-align: center;
}
.App-logo {
animation: App-logo-spin infinite 20s linear;
height: 80px;
}
.App-header {
background-color: #222;
height: 150px;
padding: 20px;
color: white;
}
.App-intro {
font-size: large;
}
@keyframes App-logo-spin {
from { transform: rotate(0deg); }
to { transform: rotate(360deg); }
}

22
src/App.tsx Normal file
Просмотреть файл

@ -0,0 +1,22 @@
import * as React from 'react';
import './App.css';
const logo = require('./logo.svg');
class App extends React.Component<null, null> {
render() {
return (
<div className="App">
<div className="App-header">
<img src={logo} className="App-logo" alt="logo" />
<h2>Welcome to React</h2>
</div>
<p className="App-intro">
To get started, edit <code>src/App.tsx</code> and save to reload.
</p>
</div>
);
}
}
export default App;

Просмотреть файл

@ -1,38 +0,0 @@
import alt from '../alt';
import common from './actions-common';
class ConversionActions {
constructor() {
this.generateActions(
'refreshFail'
);
}
refresh(timespan) {
return (dispatch) => {
var query = ` customEvents` +
` | extend successful=customDimensions.successful` +
` | where name startswith 'message.convert'` +
` | summarize event_count=count() by name, tostring(successful)`;
var mappings = [
{ key: 'name' },
{ key: 'successful', val: (val) => val === 'true' },
{ key: 'event_count', def: 0 }
];
common.fetchQuery({ timespan, query, mappings }, (error, conversions) => {
if (error) {
return this.refreshFail(error)
}
return dispatch({ conversions, timespan });
});
}
}
}
export default alt.createActions(ConversionActions);

Просмотреть файл

@ -1,88 +0,0 @@
import $ from 'jquery';
import _ from 'lodash';
import alt from '../alt';
import common from './actions-common';
var appInsightsAppId = common.appInsightsAppId;
var appInsightsApiKey = common.appInsightsApiKey;
class ErrorsActions {
constructor() {
this.generateActions(
'refreshFail',
'updateSearchTerm',
'selectHandler',
'searchResults',
'searchFail',
'selectError'
);
}
refresh(timespan) {
return (dispatch) => {
var query = ` exceptions` +
` | summarize count_error=count() by handledAt, innermostMessage` +
` | order by count_error desc `
var mappings = [
{ key: 'handledAt', def: 'Unknown' },
{ key: 'message', def: '' },
{ key: 'count', def: '' }
];
common.fetchQuery({ timespan, query, mappings }, (error, results) => {
if (error) {
return this.refreshFail(error)
}
var errors = results;
var handlers = {};
errors.forEach(error => {
if (!handlers[error.handledAt]) handlers[error.handledAt] = {
name: error.handledAt,
count: 0
};
handlers[error.handledAt].count += error.count;
});
return dispatch({
errors,
handlers: _.values(handlers),
timespan
});
});
}
}
queryExceptions(handledAt, timespan, searchQuery, top, skip) {
top = top || 100;
skip = skip || 0;
var queryspan = timespan === '24 hours' ? 'PT24H' : timespan === '1 week' ? 'P7D' : 'P30D';
var search = searchQuery ? `&$search=${encodeURIComponent(searchQuery)}` : '';
var url = `${common.appInsights.uri}/${appInsightsAppId}/events/exceptions?timespan=${queryspan}` +
search +
`&&$orderby=timestamp&$top=${top}&$skip=${skip}`;
$.ajax({
url,
method: "GET",
headers: {
"x-api-key": appInsightsApiKey
}
})
.then(json => {
return this.searchResults(json.value);
})
.fail((err) => {
this.searchFail(err);
});
}
}
export default alt.createActions(ErrorsActions);

Просмотреть файл

@ -1,84 +0,0 @@
import $ from 'jquery';
import _ from 'lodash';
import alt from '../alt';
import common from './actions-common';
var appInsightsAppId = common.appInsightsAppId;
var appInsightsApiKey = common.appInsightsApiKey;
class GeneralActions {
constructor() {
this.generateActions(
'refreshFail',
'initialize'
);
}
refresh(timespan) {
return (dispatch) => {
var query = ` exceptions` +
` | summarize count_error=count() by handledAt, innermostMessage` +
` | order by count_error desc `
var mappings = [
{ key: 'handledAt', def: 'Unknown' },
{ key: 'message', def: '' },
{ key: 'count', def: '' }
];
common.fetchQuery({ timespan, query, mappings }, (error, results) => {
if (error) {
return this.refreshFail(error)
}
var errors = results;
var handlers = {};
errors.forEach(error => {
if (!handlers[error.handledAt]) handlers[error.handledAt] = {
name: error.handledAt,
count: 0
};
handlers[error.handledAt].count += error.count;
});
return dispatch({
errors,
handlers: _.values(handlers),
timespan
});
});
}
}
queryExceptions(handledAt, timespan, searchQuery, top, skip) {
top = top || 100;
skip = skip || 0;
var queryspan = timespan == '24 hours' ? 'PT24H' : timespan == '1 week' ? 'P7D' : 'P30D';
var search = searchQuery ? `&$search=${encodeURIComponent(searchQuery)}` : '';
var url = `${common.appInsights.uri}/${appInsightsAppId}/events/exceptions?timespan=${queryspan}` +
search +
`&&$orderby=timestamp&$top=${top}&$skip=${skip}`;
$.ajax({
url,
method: "GET",
headers: {
"x-api-key": appInsightsApiKey
}
})
.then(json => {
return this.searchResults(json.value);
})
.fail((err) => {
this.searchFail(err);
});
}
}
export default alt.createActions(GeneralActions);

Просмотреть файл

@ -1,126 +0,0 @@
import $ from 'jquery';
import _ from 'lodash';
import alt from '../alt';
import common from './actions-common';
var appInsightsAppId = common.appInsightsAppId;
var appInsightsApiKey = common.appInsightsApiKey;
class IntentActions {
constructor() {
this.generateActions(
'refreshFail',
'selectIntent',
'selectConversation'
);
}
refresh (timespan) {
var queryspan = common.timespanToQueryspan(timespan);
var query = ` customEvents` +
` | extend cslen = customDimensions.callstack_length, intent=customDimensions.intent` +
` | where name startswith "message.intent" and (cslen == 0 or strlen(cslen) == 0) and strlen(intent) > 0` +
` | summarize event_count=count() by tostring(intent)`;
var url = `${common.appInsights.uri}/${appInsightsAppId}/query?timespan=${queryspan}&query=${encodeURIComponent(query)}`;
return (dispatch) => {
$.ajax({
url,
method: "GET",
headers: { "x-api-key": appInsightsApiKey }
})
.then(json => {
var _intents = [];
json.Tables[0].Rows.forEach(row => {
var intent = row[0] || 'Unknown';
var count = row[1] || 0;
_intents.push({ intent, count });
});
return dispatch({ intents: _intents, timespan });
})
.fail((err) => {
this.refreshFail(err);
})
};
}
fetchIntentConversations(intent, timespan) {
var queryspan = 'P30D';
var query = ` customEvents` +
` | extend conversation = customDimensions.conversationId, intent=customDimensions.intent` +
` | where name startswith "message.intent" and intent =~ '${intent}'` +
` | summarize event_count=count() by tostring(conversation)`;
var url = `${common.appInsights.uri}/${appInsightsAppId}/query?timespan=${queryspan}&query=${encodeURIComponent(query)}`;
return (dispatch) => {
$.ajax({
url,
method: "GET",
headers: { "x-api-key": appInsightsApiKey }
})
.then(json => {
var _conversations = [];
json.Tables[0].Rows.forEach(row => {
var conversation = row[0] || 'Unknown';
var count = row[1] || 0;
_conversations.push({ conversation, count });
});
return dispatch(_conversations);
})
.fail((err) => {
this.refreshFail(err);
})
};
}
fetchConversationMessages(conversation, timespan) {
var queryspan = 'P30D';
var query = ` customEvents` +
` | extend conversation = customDimensions.conversationId, intent=customDimensions.intent` +
` | where name in ("message.send", "message.received") and conversation == '${conversation}'` +
` | order by timestamp asc` +
` | project timestamp, name, customDimensions.text, customDimensions.userName, customDimensions.userId`;
var url = `${common.appInsights.uri}/${appInsightsAppId}/query?timespan=${queryspan}&query=${encodeURIComponent(query)}`;
return (dispatch) => {
$.ajax({
url,
method: "GET",
headers: { "x-api-key": appInsightsApiKey }
})
.then(json => {
var messages = json.Tables[0].Rows.map(row => {
var timestamp = row[0];
var eventName = row[1];
var message = row[2];
var userName = row[3];
var userId = row[4];
return { timestamp, eventName, message, userName, userId };
});
return dispatch(messages);
})
.fail((err) => {
this.refreshFail(err);
})
};
}
}
export default alt.createActions(IntentActions);

Просмотреть файл

@ -1,36 +0,0 @@
import alt from '../alt';
import common from './actions-common';
class SentimentActions {
constructor() {
this.generateActions(
'refreshFail'
);
}
refresh(timespan) {
return (dispatch) => {
var query = ` customEvents` +
` | extend score=customDimensions.score, text=customDimensions.text` +
` | where name startswith 'message.sentiment'` +
` | summarize sentiment=avg(todouble(score))`;
var mappings = [
{ key: 'sentiment' }
];
common.fetchQuery({ timespan, query, mappings }, (error, sentiments) => {
if (error) {
return this.refreshFail(error)
}
return dispatch({ sentiments, timespan });
});
}
}
}
export default alt.createActions(SentimentActions)

Просмотреть файл

@ -1,86 +0,0 @@
import $ from 'jquery';
import _ from 'lodash';
import moment from 'moment';
import alt from '../alt';
import common from './actions-common';
import TimelineStore from '../stores/TimelineStore';
var appInsightsAppId = common.appInsightsAppId;
var appInsightsApiKey = common.appInsightsApiKey;
class TimelineActions {
constructor() {
this.generateActions(
'refreshFail',
'dismissError',
'updateMode'
);
}
refresh (timespan) {
var state = TimelineStore.getState();
var queryspan = common.timespanToQueryspan(timespan);
var granularity = common.timespanToGranularity(timespan);
// Query messages per time
var query1 = ` customEvents` +
` | where name == 'Activity'` +
` | summarize event_count=count() by bin(timestamp, ${granularity}), name, tostring(customDimensions.channel)` +
` | order by timestamp asc `;
// Query users per time
var query2 = ` customEvents` +
` | where name == 'Activity'` +
` | summarize event_count=dcount(tostring(customDimensions.from)) by bin(timestamp, ${granularity}), name, tostring(customDimensions.channel)` +
` | order by timestamp asc`;
var query = state.mode == 'users' ? query2 : query1;
var url = `${common.appInsights.uri}/${appInsightsAppId}/query?timespan=${queryspan}&query=${encodeURIComponent(query)}`;
return (dispatch) => {
$.ajax({
url,
method: "GET",
headers: { "x-api-key": appInsightsApiKey }
})
.then(json => {
var now = moment();
var _timeline = {};
var _channels = {};
json.Tables[0].Rows.forEach(row => {
var channel = row[2] || 'unknown';
var time = (new Date(row[0])).getTime();
var count = row[3];
if (!_timeline[time]) _timeline[time] = { time: (new Date(row[0])).toUTCString() };
if (!_channels[channel]) _channels[channel] = { name: channel, value: 0 };
_timeline[time][channel] = count;
_channels[channel].value += count;
});
var channels = Object.keys(_channels);
var channelUsage = _.values(_channels);
var timeline = _.map(_timeline, value => {
channels.forEach(channel => {
if (!value[channel]) value[channel] = 0;
});
return value;
});
return dispatch({ data: timeline, channelUsage, channels, timespan });
})
.fail((err) => {
this.refreshFail(err);
})
};
}
}
export default alt.createActions(TimelineActions);

Просмотреть файл

@ -1,16 +0,0 @@
import alt from '../alt';
class TimespanActions {
constructor() {
this.generateActions(
'update24Hours',
'update1Week',
'update1Month',
'toggleChannel',
'toggleAllChannel');
}
}
export default alt.createActions(TimespanActions);

Просмотреть файл

@ -1,40 +0,0 @@
import moment from 'moment';
import alt from '../alt';
import common from './actions-common';
class UsersActions {
constructor() {
this.generateActions(
'refreshFail'
);
}
refresh(timespan) {
return (dispatch) => {
var query = ` customEvents` +
` | where name == 'Activity' and customDimensions.activitytype == 'conversationUpdate' and timestamp > ago(365d)` +
` | summarize min_timestamp=min(timestamp), max(timestamp), count() by tostring(customDimensions.from)` +
` | order by min_timestamp desc `;
var mappings = [
{ key: 'address', def: 'Unknown' },
{ key: 'minTimestamp', val: moment },
{ key: 'maxTimestamp', val: moment },
{ key: 'count', def: 0 }
];
common.fetchQuery({ timespan, query, mappings }, (error, users) => {
if (error) {
return this.refreshFail(error)
}
return dispatch({ users, timespan });
});
}
}
}
export default alt.createActions(UsersActions);

Просмотреть файл

@ -1,14 +0,0 @@
/**
* @class Constants
*/
class Constants {
/** Default dimentions available for updates */
DefaultDimentions = {
Timespan: 'timespan'
}
}
export default new Constants();

Просмотреть файл

@ -1,146 +0,0 @@
import $ from 'jquery';
import _ from 'lodash';
import Constants from '../constants';
import {DataSourcePlugin, IDataSourceOptions} from './DataSourcePlugin';
var appInsightsUri = 'https://api.applicationinsights.io/beta/apps';
var appId = process.env.REACT_APP_APP_INSIGHTS_APPID;
var apiKey = process.env.REACT_APP_APP_INSIGHTS_APIKEY;
class AIHelper {
/**
* Execute a query with the application insights query API.
* @param {QueryConfig} config - Configuration for the query
* @param {function} callback
*/
fetchQuery(config, callback) {
var mappings = this._props.mappings;
var queryspan = ActionsCommon.timespanToQueryspan(timespan);
var url = `${appInsightsUri}/${appId}/query?timespan=${queryspan}&query=${this._props.params.query}`;
$.ajax({
url,
method: "GET",
headers: {
"x-api-key": apiKey
}
})
.then(json => {
var resultRows = json.Tables[0].Rows;
if (!mappings || mappings.length === 0) {
return callback(null, ActionsCommon.prepareResult(resultRows));
}
var rows = resultRows.map(row => {
var item = {};
mappings.forEach((mapping, index) => {
var key = typeof mapping === 'string' ? mapping : mapping.key;
var idx = mapping.idx ? mapping.idx : index;
var def = mapping.def ? mapping.def : null;
item[key] = mapping.val && row[idx] && mapping.val(row[index]) || row[idx] || def;
});
return item;
});
return callback(null, this._prepareResult(rows));
})
.fail((err) => {
return callback(err);
});
}
/**
* Execute a query with the application insights events API.
* @param {EventsQueryConfig} config - Configuration for the query
* @param {function} callback
*/
fetchEvents(config, callback) {
var {
timespan,
search,
top,
skip
} = config || new EventsQueryConfig();
var queryspan = ActionsCommon.timespanToQueryspan(timespan);
var url = `${appInsights.uri}/${ActionsCommon.appInsightsAppId}/events/exceptions?timespan=${queryspan}` +
search ? `&$search=${encodeURIComponent(search)}` : '' +
`&&$orderby=timestamp` +
top ? `&$top=${top}` : '' +
skip ? `&$skip=${skip}` : '';
$.ajax({
url,
method: "GET",
headers: {
"x-api-key": ActionsCommon.appInsightsApiKey
}
})
.then(json => {
return callback(null, this._helper.prepareResult(json.value));
})
.fail((err) => {
return callback(err);
});
}
}
class QueryConfig {
/** @type {string} */
timespan;
}
interface IApplicationInsightsOptions extends IDataSourceOptions {
/** @type {string} */
query;
/** @type {(string|object)[]} mappings */
mappings;
}
export class ApplicationInsightsData extends DataSourcePlugin {
type = 'application-insights';
_helper = new AIHelper();
/**
* @param {ApplicationInsightsDataOptions} options - Options object
*/
constructor(options) {
super(options);
var props = this._props;
if (!props.params.query || !props.dependencies || !props.dependencies.length) {
throw new Error('AIAnalyticsEvents requires a query to run and dependencies that trigger updates.');
}
// Bind helper method to 'this'
this._helper.timespanToQueryspan = this._helper.timespanToQueryspan.bind(this);
this._helper.prepareResult = this._helper.prepareResult.bind(this);
this._helper.fetchQuery = this._helper.fetchQuery.bind(this);
this._helper.fetchEvents = this._helper.fetchEvents.bind(this);
}
/**
* update - called when dependencies are created
* @param {object} dependencies
* @param {function} callback
*/
updateDependencies(dependencies, callback) {
var { timespan } = dependencies;
// TODO: insert dependencies into query [format]or[function]
if (this._props.type == 'generic') {
this._helper.fetchQuery({timespan}, callback);
} else {
this._helper.fetchEvents(dependencies, callback);
}
}
}

Просмотреть файл

@ -1,208 +0,0 @@
import $ from 'jquery';
import _ from 'lodash';
import Constants from '../constants';
import common from '../actions-common';
var appInsightsUri = 'https://api.applicationinsights.io/beta/apps';
var appId = process.env.REACT_APP_APP_INSIGHTS_APPID;
var apiKey = process.env.REACT_APP_APP_INSIGHTS_APIKEY;
class QueryConfig {
/** @type {string} */
timespan;
}
class EventsQueryConfig {
/** @type {string} */
timespan;
/** @type {string} */
search;
/** @type {number} */
top;
/** @type {number} */
skip;
}
class ApplicationInsightsDataOptions {
/** @type {string} */
query;
/** @type {string} */
type;
/** @type {(string|object)[]} */
dependencies;
/** @type {(string|object)[]} */
mappings;
/** @type {string} outputResultsName - This would be variable storing the results */
outputResultsName;
}
export class ApplicationInsightsData {
_props = {
type: 'generic',
dependencies: [Constants.DefaultDimentions.Timespan],
output: null,
mappings: [],
actions: [],
listeners: [],
params: {
query: ''
}
}
/**
* @param {ApplicationInsightsDataOptions} options - Options object
*/
constructor(options) {
if (!options.params.query || !options.dependencies || !options.dependencies.length) {
throw new Error('AIAnalyticsEvents requires a query to run and dependencies that trigger updates.');
}
var props = this._props;
props.params.query = options.params.query;
props.dependencies = props.dependencies.concat(options.dependencies);
props.mappings = options.mappings;
props.type = options.type === 'table' ? 'table' : 'generic';
props.output = options.outputResultsName;
}
/**
* @returns {string[]} Array of dependencies
*/
getDependencies() {
return this._props.dependencies;
}
getDependables() {
return this._props.outputParameter;
}
getActions() {
return this._props.actions;
}
getParams() {
return Object.keys(this._props);
}
listen(listener) {
if (!this._props.listeners.find(func => func === listener)) {
this._props.listeners.push(listener);
}
}
/**
* update - called when dependencies are created
* @param {object} dependencies
* @param {function} callback
*/
updateDependencies(dependencies, callback) {
var { timespan } = dependencies;
// TODO: insert dependencies into query [format]or[function]
if (this._props.type == 'generic') {
this._fetchQuery({timespan}, callback);
} else {
this._fetchEvents(dependencies, callback);
}
}
_timespanToQueryspan(timespan) {
return timespan === '24 hours' ? 'PT24H' : timespan === '1 week' ? 'P7D' : 'P30D';
}
/**
* Prepare results to ship via callback
* @param {Array} results
* @returns {object} object to be returned
*/
_prepareResult(results) {
var obj = {};
obj[this._props.output] = results;
return obj;
}
/**
* Execute a query with the application insights query API.
* @param {QueryConfig} config - Configuration for the query
* @param {function} callback
*/
_fetchQuery(config, callback) {
var mappings = this._props.mappings;
var queryspan = this._timespanToQueryspan(timespan);
var url = `${appInsightsUri}/${appId}/query?timespan=${queryspan}&query=${this._props.params.query}`;
$.ajax({
url,
method: "GET",
headers: {
"x-api-key": apiKey
}
})
.then(json => {
var resultRows = json.Tables[0].Rows;
if (!mappings || mappings.length === 0) {
return callback(null, this._prepareResult(resultRows));
}
var rows = resultRows.map(row => {
var item = {};
mappings.forEach((mapping, index) => {
var key = typeof mapping === 'string' ? mapping : mapping.key;
var idx = mapping.idx ? mapping.idx : index;
var def = mapping.def ? mapping.def : null;
item[key] = mapping.val && row[idx] && mapping.val(row[index]) || row[idx] || def;
});
return item;
});
return callback(null, this._prepareResult(rows));
})
.fail((err) => {
return callback(err);
});
}
/**
* Execute a query with the application insights events API.
* @param {EventsQueryConfig} config - Configuration for the query
* @param {function} callback
*/
_fetchEvents(config, callback) {
var {
timespan,
search,
top,
skip
} = config || new EventsQueryConfig();
var queryspan = ActionsCommon.timespanToQueryspan(timespan);
var url = `${appInsights.uri}/${ActionsCommon.appInsightsAppId}/events/exceptions?timespan=${queryspan}` +
search ? `&$search=${encodeURIComponent(search)}` : '' +
`&&$orderby=timestamp` +
top ? `&$top=${top}` : '' +
skip ? `&$skip=${skip}` : '';
$.ajax({
url,
method: "GET",
headers: {
"x-api-key": ActionsCommon.appInsightsApiKey
}
})
.then(json => {
return callback(null, this._prepareResult(json.value));
})
.fail((err) => {
return callback(err);
});
}
}

Просмотреть файл

@ -1,45 +0,0 @@
module.exports = {
dataSources: [
{
id: 'timespan',
type: 'constant',
values: ['24 hours', '1 week', '1 month'],
defaultValue: '24 hours'
},
{
id: 'conversionRate',
type: 'app-insights',
dependencies: ['timespan'],
params: {
query: ` customEvents` +
` | extend successful=customDimensions.successful` +
` | where name startswith 'message.convert'` +
` | summarize event_count=count() by name, tostring(successful)`,
mappings: [
{ key: 'name' },
{ key: 'successful', val: (val) => val === 'true' },
{ key: 'event_count', def: 0 }
]
}
},
{
id: 'timelineMessages',
type: 'app-insights',
dependencies: ['timespan'],
query: (state, timespan) => {
var granularity = common.timespanToGranularity(timespan);
return ` customEvents` +
` | where name == 'Activity'` +
` | summarize event_count=count() by bin(timestamp, ${granularity}), name, tostring(customDimensions.channel)` +
` | order by timestamp asc `
},
mappings: [
{ key: 'timestamp' },
{ key: 'name' },
{ key: 'channel' },
{ key: 'event_count', def: 0 }
]
},
]
};

Просмотреть файл

@ -1,16 +0,0 @@
import { ApplicationInsightsData } from './application-insights-data';
// Example of using with conversion rate graph
var a = new ApplicationInsightsData({
query: ` customEvents` +
` | extend successful=customDimensions.successful` +
` | where name startswith 'message.convert'` +
` | summarize event_count=count() by name, tostring(successful)`,
dependencies: [ 'timespan' ],
mappings: [
{ key: 'name' },
{ key: 'successful', val: (val) => val === 'true' },
{ key: 'event_count', def: 0 }
]
})

Просмотреть файл

@ -1,15 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const Alt = require("alt");
exports.default = new Alt();
/**
*
* Declarations for inheritance purposes
*
*/
class AbstractActions {
constructor(alt) { }
}
exports.AbstractActions = AbstractActions;
class AbstractStoreModel {
}
exports.AbstractStoreModel = AbstractStoreModel;

Просмотреть файл

Просмотреть файл

@ -1,14 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const Navbar_1 = require("./Navbar");
class App extends React.Component {
render() {
var { children } = this.props;
return (
// <Navbar history={this.props.history}>
<Navbar_1.default history={this.props.history}>
{children}
</Navbar_1.default>);
}
}
exports.default = App;

Просмотреть файл

@ -9,7 +9,7 @@ class App extends React.Component<any, any> {
return (
// <Navbar history={this.props.history}>
<Navbar history={this.props.history}>
<Navbar>
{ children }
</Navbar>
);

Просмотреть файл

@ -1,10 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const Media_1 = require("react-md/lib/Media");
const Cards_1 = require("react-md/lib/Cards");
exports.default = ({ children = null, title = '', subtitle = '' }) => <Cards_1.Card>
<Cards_1.CardTitle title={title} subtitle={subtitle}/>
<Media_1.Media>
{children}
</Media_1.Media>
</Cards_1.Card>;

Просмотреть файл

Просмотреть файл

@ -1,60 +0,0 @@
import React, { Component } from 'react';
import connectToStores from 'alt-utils/lib/connectToStores';
import {List, ListItem} from 'material-ui/List';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import ErrorsStore from '../../stores/ErrorsStore';
import ErrorsActions from '../../actions/ErrorsActions';
class ErrorDetails extends Component {
// static propTypes = {}
// static defaultProps = {}
static getStores() {
return [ErrorsStore];
}
static getPropsFromStores() {
return ErrorsStore.getState();
}
handleClose() {
ErrorsActions.selectError(null);
}
render() {
const { selectedError } = this.props;
// Display tabs for all messages or grouped by type
var dialogActions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={this.handleClose}
/>,
];
return (
<Dialog
title={`Error Details`}
modal={false}
open={!!selectedError}
actions={dialogActions}
autoScrollBodyContent={true}
onRequestClose={this.handleClose}>
<List>
{Object.keys(selectedError || {}).map( (key, index) => (
<ListItem key={index}>
{key}:{(!selectedError[key] && '') || (typeof selectedError[key] == 'string' && selectedError[key]) || JSON.stringify(selectedError[key])}
</ListItem>
))}
</List>
</Dialog>
);
}
}
export default connectToStores(ErrorDetails);

Просмотреть файл

@ -1,181 +0,0 @@
import React, { Component } from 'react';
import connectToStores from 'alt-utils/lib/connectToStores';
import TextField from 'material-ui/TextField';
import {Tabs, Tab} from 'material-ui'
import {Table, TableBody, TableHeader, TableHeaderColumn, TableRow, TableRowColumn} from 'material-ui/Table';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import ActionOpen from 'material-ui/svg-icons/action/open-in-new';
import ErrorsStore from '../../stores/ErrorsStore';
import ErrorsActions from '../../actions/ErrorsActions';
var styles = {
item: {
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
},
timestamp: {
width: '30%'
},
count: {
width: '10%',
},
message: {
width: '50%',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
},
action: {
width: '10%'
}
}
class ErrorHandlers extends Component {
// static propTypes = {}
// static defaultProps = {}
static getStores() {
return [ErrorsStore];
}
static getPropsFromStores() {
return ErrorsStore.getState();
}
constructor (props) {
super(props);
this.state = {
selectedTab: 0
};
this.updateSearchTerm = this.updateSearchTerm.bind(this);
}
handleClose() {
ErrorsActions.selectHandler(null);
}
componentDidMount() {
const { selectedHandler, timespan, searchTerm } = this.props;
ErrorsActions.queryExceptions(selectedHandler, timespan, searchTerm);
}
updateSearchTerm(event) {
const { selectedHandler, timespan } = this.props;
ErrorsActions.updateSearchTerm(event.target.value);
ErrorsActions.queryExceptions(selectedHandler, timespan, event.target.value);
}
escapeRegExp(str) {
return str.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, "\\$&");
}
selectTab(tab) {
this.setState({ selectedTab: tab });
}
handleClickError(error) {
ErrorsActions.selectError(error);
}
render() {
const { selectedHandler, errors, searchTerm, searchResults, selectedError } = this.props;
// Filter grouped messages by search term - does not affect 'all messages' tab
var errorItems = errors;
if (searchTerm !== null && searchTerm !== '') {
var search = new RegExp(this.escapeRegExp(searchTerm), "i");
errorItems = errorItems.filter(error => {
return error.message.search(search) >= 0;
});
}
// Display tabs for all messages or grouped by type
var dialogActions = [
<Tabs value={this.state.selectedTab}>
<Tab value={0} onClick={this.selectTab.bind(this, 0)} label='Message Count' />
<Tab value={1} onClick={this.selectTab.bind(this, 1)} label='All Messages' />
</Tabs>,
<TextField onChange={this.updateSearchTerm} hintText="Search for an error..." />,
<FlatButton
label="Cancel"
primary={true}
onTouchTap={this.handleClose}
/>,
]
var dialogContent = null;
if (this.state.selectedTab === 0) {
dialogContent = (
<Table selectable={false} multiSelectable={false}>
<TableHeader
displaySelectAll={false}
adjustForCheckbox={false}
enableSelectAll={false}>
<TableRow>
<TableHeaderColumn style={styles.message}>Message</TableHeaderColumn>
<TableHeaderColumn style={styles.count}>Count</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false} >
{errorItems.map( (error, index) => (
<TableRow key={index}>
<TableRowColumn style={styles.message}>{error.message}</TableRowColumn>
<TableRowColumn style={styles.count}>{error.count}</TableRowColumn>
</TableRow>
))}
</TableBody>
</Table>
);
} else {
// All messages queried directly on API
dialogContent = (
<Table selectable={false} multiSelectable={false}>
<TableHeader
displaySelectAll={false}
adjustForCheckbox={false}
enableSelectAll={false}>
<TableRow>
<TableHeaderColumn style={styles.timestamp}>Timestamp</TableHeaderColumn>
<TableHeaderColumn style={styles.count}>Count</TableHeaderColumn>
<TableHeaderColumn style={styles.message}>Message</TableHeaderColumn>
<TableHeaderColumn style={styles.action}></TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false} >
{searchResults.map( (error, index) => (
<TableRow key={index}>
<TableRowColumn style={styles.timestamp}>{error.timestamp}</TableRowColumn>
<TableRowColumn style={styles.count}>{error.count}</TableRowColumn>
<TableRowColumn style={styles.message}>{error.exception && error.exception.innermostMessage}</TableRowColumn>
<TableHeaderColumn style={styles.action}>
<IconButton onClick={this.handleClickError.bind(this, error)}><ActionOpen />></IconButton>
</TableHeaderColumn>
</TableRow>
))}
</TableBody>
</Table>
);
}
return (
<Dialog
title={`Handled At: ${selectedHandler}`}
modal={false}
open={!!selectedHandler && !selectedError}
actions={dialogActions}
autoScrollBodyContent={true}
onRequestClose={this.handleClose}>
{dialogContent}
</Dialog>
);
}
}
export default connectToStores(ErrorHandlers);

Просмотреть файл

@ -1,71 +0,0 @@
import React, { Component } from 'react';
import connectToStores from 'alt-utils/lib/connectToStores';
import {Card, CardHeader, CardMedia} from 'material-ui/Card';
import {List, ListItem} from 'material-ui/List';
import ReportProblem from 'material-ui/svg-icons/action/report-problem';
import Divider from 'material-ui/Divider';
import colors from './colors';
import styles from './styles';
import ErrorHandlers from './ErrorHandlers';
import ErrorDetails from './ErrorDetails';
import ErrorsStore from '../../stores/ErrorsStore';
import ErrorsActions from '../../actions/ErrorsActions';
class Errors extends Component {
// static propTypes = {}
// static defaultProps = {}
static getStores() {
return [ErrorsStore];
}
static getPropsFromStores() {
return ErrorsStore.getState();
}
selectHandler(handledAt) {
ErrorsActions.selectHandler(handledAt);
}
render() {
const { handlers } = this.props;
var errorsCount = 0;
var errorItems = handlers.map((handledAt, idx) => {
errorsCount += handledAt.count;
return <ListItem
key={idx}
primaryText={handledAt.count}
secondaryText={handledAt.name}
onClick={this.selectHandler.bind(this, handledAt.name)}
leftIcon={<ReportProblem color={colors.DangerColor} />} />
});
//if (!errorItems.length) return null;
return (
<Card className='dash-card dash-card-list'>
<CardHeader
style={styles.cardHeaderStyle}
title="Errors"
subtitle="Click errors" />
<CardMedia style={styles.cardMediaStyle}>
<List className='list-compact'>
{errorItems}
</List>
<Divider />
<List className='list-compact'>
<ListItem key={0} primaryText={errorsCount} secondaryText="Total errors" leftIcon={<ReportProblem color={colors.DangerColor} />}/>
</List>
</CardMedia>
<ErrorHandlers />
<ErrorDetails />
</Card>
);
}
}
export default connectToStores(Errors);

Просмотреть файл

@ -1,139 +0,0 @@
import connectToStores from 'alt-utils/lib/connectToStores';
import React, { Component } from 'react';
import _ from 'lodash';
import { PieChart, Pie, Sector, Cell, Tooltip, Legend } from 'recharts';
import {Card, CardHeader, CardMedia} from 'material-ui/Card';
import colors from '../colors';
import styles from '../styles';
var {ThemeColors} = colors;
import TimelineStore from '../../../stores/TimelineStore';
class ChannelsPie extends Component {
// static propTypes = {}
// static defaultProps = {}
static getStores() {
return [TimelineStore];
}
static getPropsFromStores() {
return TimelineStore.getState();
}
state = {
activeIndex: 0
};
constructor(props) {
super(props);
this.onPieEnter = this.onPieEnter.bind(this);
}
onPieEnter(data, index) {
this.setState({ activeIndex: index});
}
renderActiveShape = (props) => {
const { mode } = this.props;
var type = mode === 'users' ? 'Users' : 'Messages';
const RADIAN = Math.PI / 180;
const { name, cx, cy, midAngle, innerRadius, outerRadius, startAngle, endAngle,
fill, payload, percent, value } = props;
const sin = Math.sin(-RADIAN * midAngle);
const cos = Math.cos(-RADIAN * midAngle);
const sx = cx + (outerRadius + 10) * cos;
const sy = cy + (outerRadius + 10) * sin;
const mx = cx + (outerRadius + 30) * cos;
const my = cy + (outerRadius + 30) * sin;
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
const ey = my;
const textAnchor = cos >= 0 ? 'start' : 'end';
var c = {};
c.midAngle = 54.11764705882353;
c.sin = Math.sin(-RADIAN * c.midAngle);
c.cos = Math.cos(-RADIAN * c.midAngle);
c.cx = cx;
c.cy = cy;
c.sx = cx + (outerRadius + 10) * c.cos;
c.sy = cy + (outerRadius + 10) * c.sin;
c.mx = cx + (outerRadius + 30) * c.cos;
c.my = cy + (outerRadius + 30) * c.sin;
c.ex = c.mx + (c.cos >= 0 ? 1 : -1) * 22;
c.ey = c.my;
c.textAnchor = 'start'
return (
<g>
<text x={cx} y={cy} dy={8} textAnchor="middle" fill={fill}>{payload.name}</text>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={c.cx}
cy={c.cy}
startAngle={300}
endAngle={60}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
<path d={`M${c.sx},${c.sy}L${c.mx},${c.my}L${c.ex},${c.ey}`} stroke={fill} fill="none"/>
<circle cx={c.ex} cy={c.ey} r={2} fill={fill} stroke="none"/>
<text x={c.ex + (c.cos >= 0 ? 1 : -1) * 12} y={c.ey} textAnchor={c.textAnchor} fill="#333">{`${value} ${type.toLowerCase()}`}</text>
<text x={c.ex + (c.cos >= 0 ? 1 : -1) * 12} y={c.ey} dy={18} textAnchor={c.textAnchor} fill="#999">
{`(Rate ${(percent * 100).toFixed(2)}%)`}
</text>
</g>
);
};
render() {
const { channelUsage, mode } = this.props;
var type = mode === 'users' ? 'Users' : 'Messages';
// Todo: Receive the width of the SVG component from the container
return (
<Card className='dash-card'>
<CardHeader
style={styles.cardHeaderStyle}
title="Channel Usage"
subtitle={`${type} per channel`} />
<CardMedia style={styles.cardMediaStyle}>
<PieChart width={400} height={240}>
<Pie
data={channelUsage}
onMouseEnter={this.onPieEnter}
cx={150}
cy={120}
innerRadius={60}
outerRadius={80}
fill="#8884d8"
paddingAngle={5}
activeIndex={this.state.activeIndex}
activeShape={this.renderActiveShape.bind(this)}
>
{
channelUsage.map((entry, index) => <Cell key={index} fill={ThemeColors[index % ThemeColors.length]}/>)
}
<Tooltip/>
<Legend />
</Pie>
</PieChart>
</CardMedia>
</Card>
);
}
}
export default connectToStores(ChannelsPie);

Просмотреть файл

@ -1,8 +0,0 @@
.message_send {
text-align: right !important;
background-color: aliceblue !important;
}
.message_received {
}

Просмотреть файл

@ -1,99 +0,0 @@
import React, { Component } from 'react';
import connectToStores from 'alt-utils/lib/connectToStores';
import $ from 'jquery';
import moment from 'moment';
import {Table, TableBody, TableFooter, TableHeader, TableHeaderColumn, TableRow, TableRowColumn} from 'material-ui/Table';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import IntenStore from '../../../stores/IntentStore';
import IntentActions from '../../../actions/IntentActions';
import './ConversationMessages.css'
var styles = {
item: {
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
},
timestamp: {
width: '30%'
},
message: {
width: '70%',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}
}
class ConversationMessages extends Component {
// static propTypes = {}
// static defaultProps = {}
static getStores() {
return [IntenStore];
}
static getPropsFromStores() {
return IntenStore.getState();
}
handleClose() {
IntentActions.selectConversation(null);
}
componentDidUpdate() {
var $container = $('.dialog-messages').find('h3').first().next();
var $content = $container.find('>div');
$container.animate({ scrollTop: $content.height() }, 'slow');
}
render() {
const { selectedConversation, conversationMessages } = this.props;
// Display tabs for all messages or grouped by type
var dialogActions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={this.handleClose}
/>,
]
return (
<Dialog
className='dialog-messages'
title={`Messages for ${selectedConversation}`}
modal={false}
open={!!selectedConversation}
actions={dialogActions}
autoScrollBodyContent={true}
onRequestClose={this.handleClose}>
<Table selectable={false} multiSelectable={false}>
<TableHeader
displaySelectAll={false}
adjustForCheckbox={false}
enableSelectAll={false}>
<TableRow>
<TableHeaderColumn style={styles.timestamp}>Timestamp</TableHeaderColumn>
<TableHeaderColumn style={styles.message}>Message</TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false}>
{conversationMessages.map( (msg, index) => (
<TableRow key={index}>
<TableRowColumn style={styles.timestamp}>
{moment(msg.timestamp).format('MMM-DD HH:mm:ss')}
</TableRowColumn>
<TableRowColumn className={msg.eventName.replace(/\./g, '_')} style={styles.message}>{msg.message}</TableRowColumn>
</TableRow>
))}
</TableBody>
</Table>
</Dialog>
);
}
}
export default connectToStores(ConversationMessages);

Просмотреть файл

@ -1,130 +0,0 @@
import connectToStores from 'alt-utils/lib/connectToStores';
import React, { Component } from 'react';
import _ from 'lodash';
import { PieChart, Pie, Sector, Cell, Legend } from 'recharts';
import {Card, CardHeader, CardMedia} from 'material-ui/Card';
import colors from '../colors';
import styles from '../styles';
import ConversionStore from '../../../stores/ConversionStore';
class ConversionsPie extends Component {
static getStores() {
return [ConversionStore];
}
static getPropsFromStores() {
return ConversionStore.getState();
}
renderActiveShape = (props) => {
const { mode } = this.props;
var type = mode === 'users' ? 'Users' : 'Messages';
const RADIAN = Math.PI / 180;
const { name, cx, cy, midAngle, innerRadius, outerRadius, startAngle, endAngle,
fill, payload, percent, value } = props;
const sin = Math.sin(-RADIAN * midAngle);
const cos = Math.cos(-RADIAN * midAngle);
const sx = cx + (outerRadius + 10) * cos;
const sy = cy + (outerRadius + 10) * sin;
const mx = cx + (outerRadius + 30) * cos;
const my = cy + (outerRadius + 30) * sin;
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
const ey = my;
const textAnchor = cos >= 0 ? 'start' : 'end';
var c = {};
c.midAngle = 54.11764705882353;
c.sin = Math.sin(-RADIAN * c.midAngle);
c.cos = Math.cos(-RADIAN * c.midAngle);
c.cx = cx;
c.cy = cy;
c.sx = cx + (outerRadius + 10) * c.cos;
c.sy = cy + (outerRadius + 10) * c.sin;
c.mx = cx + (outerRadius + 30) * c.cos;
c.my = cy + (outerRadius + 30) * c.sin;
c.ex = c.mx + (c.cos >= 0 ? 1 : -1) * 22;
c.ey = c.my;
c.textAnchor = 'start'
return (
<g>
<text x={cx} y={cy} dy={8} textAnchor="middle" fill={fill}>{payload.name}</text>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={c.cx}
cy={c.cy}
startAngle={300}
endAngle={60}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
<path d={`M${c.sx},${c.sy}L${c.mx},${c.my}L${c.ex},${c.ey}`} stroke={fill} fill="none"/>
<circle cx={c.ex} cy={c.ey} r={2} fill={fill} stroke="none"/>
<text x={c.ex + (c.cos >= 0 ? 1 : -1) * 12} y={c.ey} textAnchor={c.textAnchor} fill="#333">{`${value} ${type.toLowerCase()}`}</text>
<text x={c.ex + (c.cos >= 0 ? 1 : -1) * 12} y={c.ey} dy={18} textAnchor={c.textAnchor} fill="#999">
{`(Rate ${(percent * 100).toFixed(2)}%)`}
</text>
</g>
);
};
render() {
const { conversions } = this.props;
var total = _.find(conversions, { name: 'message.convert.start' });
var successful = _.find(conversions, { name: 'message.convert.end', successful: true }) || { event_count: 0 };
if (!total) {
return null;
}
var values = [
{ name: 'Successful', value: successful.event_count },
{ name: 'Failed', value: total.event_count - successful.event_count },
];
// Todo: Receive the width of the SVG component from the container
return (
<Card className='dash-card'>
<CardHeader
className='card-header'
title="Conversion Usage"
subtitle={`Conversion Rate`} />
<CardMedia style={styles.cardMediaStyle}>
<PieChart width={500} height={240}>
<Pie
data={values}
cx={270}
cy={120}
innerRadius={60}
outerRadius={80}
fill="#8884d8"
activeIndex={0}
activeShape={this.renderActiveShape.bind(this)}
paddingAngle={0}>
<Cell key={0} fill={colors.GoodColor}/>
<Cell key={1} fill={colors.BadColor}/>
</Pie>
<Legend wrapperStyle={{ marginLeft: 70 }} />
</PieChart>
</CardMedia>
</Card>
);
}
}
export default connectToStores(ConversionsPie);

Просмотреть файл

@ -1,104 +0,0 @@
import React, { Component } from 'react';
import connectToStores from 'alt-utils/lib/connectToStores';
import TextField from 'material-ui/TextField';
import {Tabs, Tab} from 'material-ui'
import {Table, TableBody, TableFooter, TableHeader, TableHeaderColumn, TableRow, TableRowColumn} from 'material-ui/Table';
import ReportProblem from 'material-ui/svg-icons/action/report-problem';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import ActionQuestionAnswer from 'material-ui/svg-icons/action/question-answer';
import IntenStore from '../../../stores/IntentStore';
import IntentActions from '../../../actions/IntentActions';
var styles = {
item: {
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
},
timestamp: {
width: '30%'
},
count: {
width: '10%',
},
message: {
width: '60%',
textOverflow: 'ellipsis',
whiteSpace: 'nowrap'
}
}
class IntentConversations extends Component {
// static propTypes = {}
// static defaultProps = {}
static getStores() {
return [IntenStore];
}
static getPropsFromStores() {
return IntenStore.getState();
}
handleClose() {
IntentActions.selectIntent(null);
}
handleCoversationClick(conversation) {
IntentActions.selectConversation(conversation.conversation);
}
render() {
const { selectedIntent, intentConversations, selectedConversation } = this.props;
// Display tabs for all messages or grouped by type
var dialogActions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={this.handleClose}
/>,
]
return (
<Dialog
title={`Conversations for ${selectedIntent}`}
modal={false}
open={!!selectedIntent && !selectedConversation}
actions={dialogActions}
autoScrollBodyContent={true}
onRequestClose={this.handleClose}>
<Table selectable={false} multiSelectable={false}>
<TableHeader
displaySelectAll={false}
adjustForCheckbox={false}
enableSelectAll={false}>
<TableRow>
<TableHeaderColumn style={styles.message}>Conversations</TableHeaderColumn>
<TableHeaderColumn style={styles.count}>Count</TableHeaderColumn>
<TableHeaderColumn style={styles.timestamp}></TableHeaderColumn>
</TableRow>
</TableHeader>
<TableBody displayRowCheckbox={false}>
{intentConversations.map( (conversation, index) => (
<TableRow key={index}>
<TableRowColumn style={styles.message}>{conversation.conversation}</TableRowColumn>
<TableRowColumn style={styles.count}>{conversation.count}</TableRowColumn>
<TableRowColumn style={styles.timestamp}>
<IconButton onClick={this.handleCoversationClick.bind(this, conversation)}><ActionQuestionAnswer /></IconButton>
</TableRowColumn>
</TableRow>
))}
</TableBody>
</Table>
</Dialog>
);
}
}
export default connectToStores(IntentConversations);

Просмотреть файл

@ -1,67 +0,0 @@
import connectToStores from 'alt-utils/lib/connectToStores';
import React, { Component } from 'react';
import _ from 'lodash';
import {BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts';
import {Card, CardHeader, CardMedia} from 'material-ui/Card';
import colors from '../colors';
import styles from '../styles';
import ConversationMessages from './ConversationMessages';
import IntentConversations from './IntentConversations';
import IntentStore from '../../../stores/IntentStore';
import IntentActions from '../../../actions/IntentActions';
class IntentsGraph extends Component {
// static propTypes = {}
// static defaultProps = {}
static getStores() {
return [IntentStore];
}
static getPropsFromStores() {
return IntentStore.getState();
}
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(data, index) {
const { intents } = this.props;
IntentActions.selectIntent(intents[index].intent);
}
render() {
const { intents } = this.props;
if (intents.length === 0) return null;
return (
<Card className='dash-card'>
<CardHeader
style={styles.cardHeaderStyle}
title="Intents"
subtitle="Which intents are used" />
<CardMedia style={styles.cardMediaStyle}>
<ResponsiveContainer>
<BarChart width={520} height={240} data={intents}
margin={{top: 5, right: 30, left: 20, bottom: 5}}>
<XAxis dataKey="intent"/>
<YAxis/>
<CartesianGrid strokeDasharray="3 3"/>
<Tooltip/>
<Bar dataKey="count" fill={colors.IntentsColor} onClick={this.handleClick}/>
</BarChart>
</ResponsiveContainer>
</CardMedia>
<IntentConversations/>
<ConversationMessages />
</Card>
);
}
}
export default connectToStores(IntentsGraph);

Просмотреть файл

@ -1,122 +0,0 @@
import connectToStores from 'alt-utils/lib/connectToStores';
import React, { Component } from 'react';
import _ from 'lodash';
import { PieChart, Pie, Sector, Cell, Legend } from 'recharts';
import {Card, CardHeader, CardMedia} from 'material-ui/Card';
import colors from '../colors';
import styles from '../styles';
import SentimentStore from '../../../stores/SentimentStore';
class SentimentPie extends Component {
static getStores() {
return [SentimentStore];
}
static getPropsFromStores() {
return SentimentStore.getState();
}
renderActiveShape = (props) => {
const { mode } = this.props;
const RADIAN = Math.PI / 180;
const { name, cx, cy, midAngle, innerRadius, outerRadius, startAngle, endAngle,
fill, payload, percent, value } = props;
const sin = Math.sin(-RADIAN * midAngle);
const cos = Math.cos(-RADIAN * midAngle);
const sx = cx + (outerRadius + 10) * cos;
const sy = cy + (outerRadius + 10) * sin;
const mx = cx + (outerRadius + 30) * cos;
const my = cy + (outerRadius + 30) * sin;
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
const ey = my;
const textAnchor = cos >= 0 ? 'start' : 'end';
var c = {};
c.midAngle = 54.11764705882353;
c.sin = Math.sin(-RADIAN * c.midAngle);
c.cos = Math.cos(-RADIAN * c.midAngle);
c.cx = cx;
c.cy = cy;
c.sx = cx + (outerRadius + 10) * c.cos;
c.sy = cy + (outerRadius + 10) * c.sin;
c.mx = cx + (outerRadius + 30) * c.cos;
c.my = cy + (outerRadius + 30) * c.sin;
c.ex = c.mx + (c.cos >= 0 ? 1 : -1) * 22;
c.ey = c.my;
c.textAnchor = 'start'
return (
<g>
<text x={cx} y={cy} dy={8} textAnchor="middle" fill={fill}>{payload.name}</text>
<Sector
cx={cx}
cy={cy}
innerRadius={innerRadius}
outerRadius={outerRadius}
startAngle={startAngle}
endAngle={endAngle}
fill={fill}
/>
<Sector
cx={c.cx}
cy={c.cy}
startAngle={300}
endAngle={60}
innerRadius={outerRadius + 6}
outerRadius={outerRadius + 10}
fill={fill}
/>
<path d={`M${c.sx},${c.sy}L${c.mx},${c.my}L${c.ex},${c.ey}`} stroke={fill} fill="none"/>
<circle cx={c.ex} cy={c.ey} r={2} fill={fill} stroke="none"/>
<text x={c.ex + (c.cos >= 0 ? 1 : -1) * 12} y={c.ey} textAnchor={c.textAnchor} fill="#333">{`${value}%`}</text>
</g>
);
};
render() {
const { sentiments } = this.props;
if (!sentiments || !sentiments.length || isNaN(sentiments[0].sentiment)) {
return null;
}
var values = [
{ name: 'Positive', value: Math.round(sentiments[0].sentiment * 100) },
{ name: 'Negative', value: Math.round((1 - sentiments[0].sentiment) * 100) },
];
// Todo: Receive the width of the SVG component from the container
return (
<Card className='dash-card'>
<CardHeader
className='card-header'
title="Sentiment"
subtitle={`Sentiment analysis`} />
<CardMedia style={styles.cardMediaStyle}>
<PieChart width={500} height={240}>
<Pie
data={values}
cx={270}
cy={120}
innerRadius={60}
outerRadius={80}
fill="#8884d8"
activeIndex={0}
activeShape={this.renderActiveShape.bind(this)}
paddingAngle={0}>
<Cell key={0} fill={colors.PositiveColor}/>
<Cell key={1} fill={colors.NeutralColor}/>
</Pie>
<Legend wrapperStyle={{ marginLeft: 70 }} />
</PieChart>
</CardMedia>
</Card>
);
}
}
export default connectToStores(SentimentPie);

Просмотреть файл

@ -1,129 +0,0 @@
import connectToStores from 'alt-utils/lib/connectToStores';
import React, { Component } from 'react';
import moment from 'moment';
import classnames from 'classnames';
import _ from 'lodash';
import {LineChart, Line, XAxis, YAxis, CartesianGrid, Tooltip, Legend, ResponsiveContainer} from 'recharts';
import {Card, CardHeader, CardMedia} from 'material-ui/Card';
import Dialog from 'material-ui/Dialog';
import FlatButton from 'material-ui/FlatButton';
import IconButton from 'material-ui/IconButton';
import PersonIcon from 'material-ui/svg-icons/social/person';
import ActionQuestionAnswer from 'material-ui/svg-icons/action/question-answer';
import colors from '../colors';
import styles from '../styles';
var { ThemeColors } = colors;
import TimelineStore from '../../../stores/TimelineStore';
import TimelineActions from '../../../actions/TimelineActions';
class Timeline extends Component {
// static propTypes = {}
// static defaultProps = {}
static getStores() {
return [TimelineStore];
}
static getPropsFromStores() {
return TimelineStore.getState();
}
constructor(props) {
super(props);
this.handleClick = this.handleClick.bind(this);
}
handleClick(data, index) {
}
handleClose = () => {
TimelineActions.dismissError();
}
dateFormat (time) {
return moment(time).format('MMM-DD');
}
hourFormat (time) {
return moment(time).format('HH:mm');
}
channelChecked(channel) {
TimelineActions.toggleChannel(channel);
}
changeMode(mode) {
TimelineActions.updateMode(mode);
}
render() {
const { data, timespan, channels, excluded, mode } = this.props;
const actions = [
<FlatButton
label="Cancel"
primary={true}
onTouchTap={this.handleClose}
/>
];
var format = timespan === "24 hours" ? this.hourFormat : this.dateFormat;
var lines = [];
if (data && data.length) {
lines = _.without(channels, ...excluded).map((channel, idx) => {
return <Line key={idx} type="monotone" dataKey={channel} stroke={ThemeColors[idx]} dot={false} ticksCount={5}/>
})
}
var titleControls = (
<div>
<span key={0}>Channel Usage</span>,
<IconButton
key={1}
tooltip='Show data by messages'
className={classnames(mode == 'messages' && 'active')}
onClick={this.changeMode.bind(this, 'messages')}><ActionQuestionAnswer /></IconButton>,
<IconButton
key={2}
tooltip='Show data by users'
className={classnames(mode == 'users' && 'active')}
onClick={this.changeMode.bind(this, 'users')}><PersonIcon /></IconButton>
</div>
);
return (
<Card className='dash-card'>
<CardHeader
className='card-header'
title={titleControls}
subtitle="How many messages were send in each channel" />
<CardMedia style={styles.cardMediaStyle}>
<ResponsiveContainer>
<LineChart data={data} margin={{top: 5, right: 30, left: 20, bottom: 5}}
onClick={this.handleClick}>
<XAxis dataKey="time" tickFormatter={format} minTickGap={20}/>
<YAxis type="number" domain={['dataMin', 'dataMax']}/>
<CartesianGrid strokeDasharray="3 3"/>
<Tooltip />
<Legend/>
{lines}
</LineChart>
</ResponsiveContainer>
<Dialog
actions={actions}
modal={false}
open={!!this.props.error}
onRequestClose={this.handleClose}>
{this.props.error}
</Dialog>
</CardMedia>
</Card>
);
}
}
export default connectToStores(Timeline);

Просмотреть файл

@ -1,97 +0,0 @@
import React, { Component } from 'react';
import connectToStores from 'alt-utils/lib/connectToStores';
import FlatButton from 'material-ui/FlatButton';
import Checkbox from 'material-ui/Checkbox';
import IconButton from 'material-ui/IconButton';
import SelectAllIcon from 'material-ui/svg-icons/toggle/check-box';
import SelectNoneIcon from 'material-ui/svg-icons/toggle/check-box-outline-blank';
import { Toolbar, ToolbarGroup } from 'material-ui/Toolbar';
import TimespanStore from '../../stores/TimespanStore';
import TimespanActions from '../../actions/TimespanActions';
import './style.css';
const styles = {
channels: {
width: 'auto',
display: 'inline-block',
marginLeft: '20px'
}
}
class Timespan extends Component {
// static propTypes = {}
// static defaultProps = {}
static getStores() {
return [TimespanStore];
}
static getPropsFromStores() {
return TimespanStore.getState();
}
componentDidMount() {
TimespanActions.update24Hours();
}
onTimespanChange(timespan) {
switch(timespan) {
case "1 week":
return TimespanActions.update1Week();
case "1 month":
return TimespanActions.update1Month();
default:
return TimespanActions.update24Hours();
}
}
channelChecked(channel) {
TimespanActions.toggleChannel(channel);
}
toggleAllChannel(on) {
TimespanActions.toggleAllChannel(on);
}
render() {
const { timespan, channels, excluded } = this.props;
var buttons = ["24 hours", "1 week", "1 month"].map((time, idx) => {
return <FlatButton key={idx} label={time} primary={time === timespan} onClick={this.onTimespanChange.bind(null, time)} />
})
var channelBoxes = channels.map(channel => {
return <Checkbox
key={channel}
style={styles.channels}
label={channel}
checked={!excluded.includes(channel)}
onCheck={this.channelChecked.bind(null, channel)} />
});
var channelActions = channels.length === 0 ? [] : [
<IconButton key='sel-all' tooltip="Select all channels" onClick={this.toggleAllChannel.bind(null, true)}>
`<SelectAllIcon color='#555' />
</IconButton>,
<IconButton key='sel-non' tooltip="Select no channels" onClick={this.toggleAllChannel.bind(null, false)}>
<SelectNoneIcon color='#555' />
</IconButton>
];
return (
<Toolbar>
<ToolbarGroup firstChild={true}>
{buttons}
</ToolbarGroup>
<ToolbarGroup>
{channelActions}
{channelBoxes}
</ToolbarGroup>
</Toolbar>
);
}
}
export default connectToStores(Timespan);

Просмотреть файл

@ -1,67 +0,0 @@
import React, { Component } from 'react';
import connectToStores from 'alt-utils/lib/connectToStores';
import moment from 'moment';
import {Card, CardHeader, CardMedia} from 'material-ui/Card';
import {List, ListItem} from 'material-ui/List';
import PersonIcon from 'material-ui/svg-icons/social/person';
import Divider from 'material-ui/Divider';
import colors from './colors';
import styles from './styles';
import UsersStore from '../../stores/UsersStore';
import commonActions from '../../actions/actions-common';
class Users extends Component {
static getStores() {
return [UsersStore];
}
static getPropsFromStores() {
return UsersStore.getState();
}
median(array, key) {
if (!array.length) {return 0};
var numbers = array.slice(0).sort((a,b) =>a[key] - b[key]);
var middle = Math.floor(numbers.length / 2);
var isEven = numbers.length % 2 === 0;
return isEven ? (numbers[middle][key] + numbers[middle - 1][key]) / 2 : numbers[middle][key];
}
render() {
const { users, timespan } = this.props;
var startDate = moment(commonActions.timespanStartDate(timespan));
var active = users.filter(user => user.maxTimestamp.isAfter(startDate));
var newUsers = active.filter(user => user.count === 1);
var totalUsers = active.length;
var countMedian = this.median(active, 'count');
//if (!errorItems.length) return null;
return (
<Card className='dash-card dash-card-list'>
<CardHeader
className='card-header'
title='Users'
subtitle='Active and new' />
<CardMedia style={styles.cardMediaStyle}>
<List className='list-compact'>
<ListItem primaryText={`${newUsers.length} Users`} secondaryText="New users" leftIcon={<PersonIcon color={colors.PersonColor} />}/>
<ListItem primaryText={`${totalUsers} Users`} secondaryText="Total users" leftIcon={<PersonIcon color={colors.PersonColor} />}/>
</List>
<Divider />
<List className='list-compact'>
<ListItem primaryText={`${countMedian} Users`} secondaryText="MSG/USR median" leftIcon={<PersonIcon color={colors.PersonColor} />}/>
</List>
</CardMedia>
</Card>
);
}
}
export default connectToStores(Users);

Просмотреть файл

@ -1,31 +0,0 @@
import * as colors from 'material-ui/styles/colors';
var ThemeColors = [
colors.pink800,
colors.purple800,
colors.cyan800,
colors.red800,
colors.blue800,
colors.lightBlue800,
colors.deepPurple800,
colors.lime800,
colors.teal800
];
export default {
ThemeColors,
DangerColor: colors.red500,
PersonColor: colors.teal700,
IntentsColor: colors.tealA700,
GoodColor: colors.lightBlue700,
BadColor: colors.red700,
PositiveColor: colors.lightBlue700,
NeutralColor: colors.grey500,
getColor: (idx) => {
return ThemeColors[idx];
}
}

Просмотреть файл

@ -1,154 +0,0 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import _ from 'lodash';
//import {Container, Grid, Breakpoint, Span} from 'react-responsive-grid'
import ReactGridLayout from 'react-grid-layout';
var ResponsiveReactGridLayout = ReactGridLayout.Responsive;
var WidthProvider = ReactGridLayout.WidthProvider;
ResponsiveReactGridLayout = WidthProvider(ResponsiveReactGridLayout);
import Timeline from './Graphs/Timeline';
import ChannelsPie from './Graphs/ChannelsPie';
import IntentsGraph from './Graphs/IntentsGraph';
import ConversionPie from './Graphs/ConversionPie';
import SentimentPie from './Graphs/SentimentPie';
import Timespan from './Timespan';
import Errors from './Errors';
import Users from './Users';
import 'react-grid-layout/css/styles.css';
import 'react-resizable/css/styles.css'
import './style.css';
function generateLayouts() {
return {
lg: [
{ "i": "timeline", "x": 0, "y": 8, "w": 5, "h": 8 },
{ "i": "channels", "x": 5, "y": 8, "w": 3, "h": 8 },
{ "i": "errors", "x": 8, "y": 8, "w": 2, "h": 8 },
{ "i": "users", "x": 10, "y": 8, "w": 2, "h": 8 },
{ "i": "intents", "x": 0, "y": 16, "w": 4, "h": 8 },
{ "i": "conversions", "x": 4, "y": 16, "w": 4, "h": 8 },
{ "i": "sentiments", "x": 8, "y": 16, "w": 4, "h": 8 }
],
md: [
{ "x": 0, "y": 8, "w": 5, "h": 8, "i": "0" },
{ "x": 5, "y": 8, "w": 5, "h": 8, "i": "1" },
{ "x": 10, "y": 8, "w": 2, "h": 8, "i": "2" },
{ "x": 0, "y": 16, "w": 5, "h": 8, "i": "3" }
],
sm: [
{ "x": 0, "y": 8, "w": 5, "h": 8, "i": "0" },
{ "x": 5, "y": 8, "w": 5, "h": 8, "i": "1" },
{ "x": 10, "y": 8, "w": 2, "h": 8, "i": "2" },
{ "x": 0, "y": 16, "w": 5, "h": 8, "i": "3" }
],
xs: [
{ "x": 0, "y": 8, "w": 5, "h": 8, "i": "0" },
{ "x": 5, "y": 8, "w": 5, "h": 8, "i": "1" },
{ "x": 10, "y": 8, "w": 2, "h": 8, "i": "2" },
{ "x": 0, "y": 16, "w": 5, "h": 8, "i": "3" }
],
xxs: [
{ "x": 0, "y": 8, "w": 5, "h": 8, "i": "0" },
{ "x": 5, "y": 8, "w": 5, "h": 8, "i": "1" },
{ "x": 10, "y": 8, "w": 2, "h": 8, "i": "2" },
{ "x": 0, "y": 16, "w": 5, "h": 8, "i": "3" }
]
}
}
export default class Dashboard extends Component {
// static propTypes = {}
// static defaultProps = {}
static defaultProps = {
grid: {
className: "layout",
rowHeight: 30,
cols: {lg: 12, md: 10, sm: 6, xs: 4, xxs: 2},
breakpoints: {lg: 1200, md: 996, sm: 768, xs: 480, xxs: 0},
layouts: generateLayouts()
}
};
state = {
currentBreakpoint: 'lg',
mounted: false,
layouts: {lg: this.props.initialLayout},
};
componentDidMount() {
this.setState({mounted: true});
}
onBreakpointChange = (breakpoint) => {
this.setState({
currentBreakpoint: breakpoint
});
};
onLayoutChange = (layout, layouts) => {
//this.props.onLayoutChange(layout, layouts);
};
render() {
const { className } = this.props;
return (
<div className={classnames('Dashboard', className)}>
<Timespan />
<ResponsiveReactGridLayout
{...this.props.grid}
onBreakpointChange={this.onBreakpointChange}
onLayoutChange={this.onLayoutChange}
// WidthProvider option
measureBeforeMount={false}
// I like to have it animate on mount. If you don't, delete `useCSSTransforms` (it's default `true`)
// and set `measureBeforeMount={true}`.
useCSSTransforms={this.state.mounted}>
<div key='timeline'><Timeline /></div>
<div key='channels'><ChannelsPie /></div>
<div key='errors'><Errors /></div>
<div key='users'><Users /></div>
<div key='intents'><IntentsGraph /></div>
<div key='conversions'><ConversionPie /></div>
<div key='sentiments'><SentimentPie /></div>
</ResponsiveReactGridLayout>
</div>
)
// return (
// <Timespan />
// <Grid columns={12} className='dashboard-grid'>
// <Breakpoint minWidth={1600}>
// <Span columns={5}><Timeline /></Span>
// <Span columns={5}><ChannelsPie /></Span>
// <Span columns={2} last><Errors /></Span>
// </Breakpoint>
// <Breakpoint minWidth={1200} maxWidth={1600}>
// <Span columns={6}><Timeline /></Span>
// <Span columns={6} last><ChannelsPie /></Span>
// <Span columns={3}><Errors /></Span>
// </Breakpoint>
// <Breakpoint maxWidth={1200} widthMethod="componentWidth">
// <Span columns={12}><Timeline /></Span>
// <Span columns={12}><ChannelsPie /></Span>
// <Span columns={6}><Errors /></Span>
// </Breakpoint>
// </Grid>
// </div>
// );
}
}

Просмотреть файл

@ -1,30 +0,0 @@
[
{
"x": 0,
"y": 8,
"w": 4,
"h": 8,
"i": "0"
},
{
"x": 4,
"y": 8,
"w": 4,
"h": 8,
"i": "1"
},
{
"x": 8,
"y": 8,
"w": 2,
"h": 8,
"i": "2"
},
{
"x": 10,
"y": 8,
"w": 2,
"h": 8,
"i": "3"
}
]

Просмотреть файл

@ -1,63 +0,0 @@
.purpleStyle {
border: '2px solid purple';
color: 'purple';
height: '10rem';
font-size: '2rem';
line-height: '10rem';
margin-bottom: '1rem';
text-align: 'center';
}
.dashboard-grid > div > div > div {
width: 100%;
}
.dash-card {
padding-bottom: 5px;
height: 100%;
}
.dash-card > div {
height: 100%;
}
.dash-card > div > div > div {
height: 100%;
position: relative;
}
.dash-card .card-header {
height: auto;
width: 100%;
}
.dash-card .card-header div {
padding-right: 0 !important;
width: 100%;
}
.dash-card .card-header button {
top: 0;
right: 0;
float: right;
margin-top: -10px !important;
margin-bottom: -10px !important;
}
.dash-card .card-header button svg {
color: rgb(85, 85, 85) !important;
}
.dash-card .card-header button.active svg {
color: rgb(106, 27, 154) !important;
}
.dash-card .card-header button:hover svg {
color: blueviolet !important;
}
.dash-card-list {
padding-top: 0px;
padding-bottom: 5px;
}
.list-compact {
padding: 7px 0 !important;
}

Просмотреть файл

@ -1,8 +0,0 @@
export default {
cardMediaStyle: {
height: 240
},
cardHeaderStyle: {
height: 'auto'
}
}

Просмотреть файл

@ -1,55 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const _ = require("lodash");
const plugins_1 = require("./generic/plugins");
class ElementConnector {
static loadLayoutFromDashboard(elementsContainer, dashboard) {
var layouts = {};
_.each(dashboard.config.layout.cols, (totalColumns, key) => {
var curCol = 0;
var curRowOffset = 0;
var maxRowHeight = 0;
// Go over all elements in the dashboard and check their size
elementsContainer.elements.forEach(element => {
var { id, size } = element;
if (curCol > 0 && (curCol + size.w) >= totalColumns) {
curCol = 0;
curRowOffset = maxRowHeight;
}
layouts[key] = layouts[key] || [];
layouts[key].push({
"i": id,
"x": curCol,
"y": curRowOffset + size.h,
"w": size.w,
"h": size.h
});
curCol += size.w;
maxRowHeight = Math.max(curRowOffset + size.h, maxRowHeight);
});
});
return layouts;
}
static loadElementsFromDashboard(dashboard, layout) {
var elements = [];
dashboard.elements.forEach((element, idx) => {
var ReactElement = plugins_1.default[element.type];
var { id, dependencies, actions, props, title, subtitle, size, theme } = element;
var layoutProps = _.find(layout, { "i": id });
elements.push(<div key={id}>
<ReactElement key={idx} dependencies={dependencies} actions={actions || {}} props={props || {}} title={title} subtitle={subtitle} layout={layoutProps} theme={theme}/>
</div>);
});
return elements;
}
static loadFiltersFromDashboard(dashboard) {
var filters = [];
var additionalFilters = [];
dashboard.filters.forEach((element, idx) => {
var ReactElement = plugins_1.default[element.type];
(element.first ? filters : additionalFilters).push(<ReactElement key={idx} dependencies={element.dependencies} actions={element.actions}/>);
});
return { filters, additionalFilters };
}
}
exports.default = ElementConnector;

Просмотреть файл

@ -1,29 +0,0 @@
import React, { Component } from 'react';
import classnames from 'classnames';
import './style.css';
class Home extends Component {
constructor(props) {
super(props);
this.state = {
selectedFoods: [],
}
}
render() {
const { className } = this.props;
return (
<div className={classnames('Home', className)} >
<div className='Home-header ui text container'>
<h1>Bots Dashboard</h1>
<p>Move to the <b>Dashboard</b> tab to see bot analytics</p>
</div>
</div>
);
}
}
export default Home;

Просмотреть файл

@ -1,3 +0,0 @@
.Home {
margin-top: 7em;
}

Просмотреть файл

@ -1,29 +0,0 @@
var __rest = (this && this.__rest) || function (s, e) {
var t = {};
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
t[p] = s[p];
if (s != null && typeof Object.getOwnPropertySymbols === "function")
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) if (e.indexOf(p[i]) < 0)
t[p[i]] = s[p[i]];
return t;
};
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const react_router_1 = require("react-router");
class NavigationLink extends React.PureComponent {
// NOTE: Don't try using Stateless (function) component here. `ref` is
// required by React-MD/AccessibleFakeButton, but Stateless components
// don't have one by design:
// https://github.com/facebook/react/issues/4936
render() {
const _a = this.props, { href, as, children } = _a, _props = __rest(_a, ["href", "as", "children"]);
return (<div {..._props} style={{ padding: 0 }}>
<react_router_1.Link href={href} as={as}>
<a className='md-list-tile md-list-tile--mini' style={{ width: '100%', overflow: 'hidden' }}>
{children}
</a>
</react_router_1.Link>
</div>);
}
}
exports.default = NavigationLink;

Просмотреть файл

@ -10,7 +10,7 @@ export default class NavigationLink extends React.PureComponent<any, any> {
const { href, as, children, ..._props } = this.props
return (
<div {..._props} style={{padding: 0}}>
<Link href={href} as={as}>
<Link href={href} to={null}>
<a className='md-list-tile md-list-tile--mini' style={{width: '100%', overflow: 'hidden'}}>
{children}
</a>

Просмотреть файл

@ -1,31 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const NavigationDrawers_1 = require("react-md/lib/NavigationDrawers");
const FontIcons_1 = require("react-md/lib/FontIcons");
const ListItem_1 = require("react-md/lib/Lists/ListItem");
const Avatars_1 = require("react-md/lib/Avatars");
const SelectFields_1 = require("react-md/lib/SelectFields");
const react_router_1 = require("react-router");
require("./style.css");
const avatarSrc = 'https://cloud.githubusercontent.com/assets/13041/19686250/971bf7f8-9ac0-11e6-975c-188defd82df1.png';
const drawerHeaderChildren = [
<Avatars_1.default key={avatarSrc} src={avatarSrc} role='presentation' iconSized style={{ alignSelf: 'center', marginLeft: 16, marginRight: 16, flexShrink: 0 }}/>,
<SelectFields_1.default id='account-switcher' defaultValue='Jonathan' menuItems={['Jonathan', 'Fred']} key='account-switcher' position={SelectFields_1.default.Positions.BELOW} className='md-select-field--toolbar'/>
];
exports.default = ({ children = null, title = 'Bot Framework Dashboard' }) => {
var pathname = '/';
try {
pathname = window.location.pathname;
}
catch (e) { }
var navigationItems = [
<ListItem_1.default key='0' component={react_router_1.Link} href='/' active={pathname == '/'} leftIcon={<FontIcons_1.default>home</FontIcons_1.default>} tileClassName='md-list-tile--mini' primaryText={'Home'}/>,
<ListItem_1.default key='1' component={react_router_1.Link} href='/about' active={pathname == '/about'} leftIcon={<FontIcons_1.default>info</FontIcons_1.default>} tileClassName='md-list-tile--mini' primaryText={'About'}/>,
<ListItem_1.default key='2' component={react_router_1.Link} href='/dashboard' active={pathname == '/dashboard'} leftIcon={<FontIcons_1.default>dashboard</FontIcons_1.default>} tileClassName='md-list-tile--mini' primaryText={'Dashboard'}/>
];
return (<div>
<NavigationDrawers_1.default navItems={navigationItems} contentClassName='md-grid' drawerHeaderChildren={drawerHeaderChildren} mobileDrawerType={NavigationDrawers_1.default.DrawerTypes.TEMPORARY_MINI} tabletDrawerType={NavigationDrawers_1.default.DrawerTypes.TEMPORARY_MINI} desktopDrawerType={NavigationDrawers_1.default.DrawerTypes.TEMPORARY_MINI} toolbarTitle={title} toolbarActions={null}>
{children}
</NavigationDrawers_1.default>
</div>);
};

Просмотреть файл

Просмотреть файл

Просмотреть файл

@ -1,36 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
//import * as colors from 'material-ui/styles/colors';
const colors = require("material-colors");
var ThemeColors = [
colors.pink[800],
colors.purple[800],
colors.cyan[800],
colors.red[800],
colors.blue[800],
colors.lightBlue[800],
colors.deepPurple[800],
colors.lime[800],
colors.teal[800]
];
var ThemeColors2 = ThemeColors.slice().reverse();
const DangerColor = colors.red[500];
const PersonColor = colors.teal[700];
const IntentsColor = colors.teal['a700'];
const GoodColor = colors.lightBlue[700];
const BadColor = colors.red[700];
const PositiveColor = colors.lightBlue[700];
const NeutralColor = colors.grey[500];
exports.default = {
ThemeColors,
ThemeColors2,
DangerColor,
PersonColor,
IntentsColor,
GoodColor,
BadColor,
PositiveColor,
NeutralColor,
getColor: (idx) => {
return ThemeColors[idx];
}
};

Просмотреть файл

Просмотреть файл

@ -1,49 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const GenericComponent_1 = require("./GenericComponent");
const Card_1 = require("../Card");
const recharts_1 = require("recharts");
const colors_1 = require("../colors");
var { ThemeColors } = colors_1.default;
;
class BarData extends GenericComponent_1.GenericComponent {
constructor(props) {
super(props);
this.state = {
values: [],
bars: []
};
this.handleClick = this.handleClick.bind(this);
}
handleClick(data, index) {
this.trigger('onBarClick', data.payload);
}
render() {
var { values, bars } = this.state;
var { title, subtitle, props } = this.props;
var { barProps, showLegend, nameKey } = props;
if (!values) {
return null;
}
var barElements = [];
if (values && values.length && bars) {
barElements = bars.map((bar, idx) => {
return <recharts_1.Bar key={idx} dataKey={bar} fill={ThemeColors[idx]} onClick={this.handleClick}/>;
});
}
// Todo: Receive the width of the SVG component from the container
return (<Card_1.default title={title} subtitle={subtitle}>
<recharts_1.ResponsiveContainer>
<recharts_1.BarChart data={values} margin={{ top: 5, right: 30, left: 0, bottom: 5 }} {...barProps}>
<recharts_1.XAxis dataKey={nameKey || ''}/>
<recharts_1.YAxis />
<recharts_1.CartesianGrid strokeDasharray="3 3"/>
<recharts_1.Tooltip />
{barElements}
{showLegend !== false && <recharts_1.Legend layout="vertical" align="right" verticalAlign="top" wrapperStyle={{ right: 5 }}/>}
</recharts_1.BarChart>
</recharts_1.ResponsiveContainer>
</Card_1.default>);
}
}
exports.default = BarData;

Просмотреть файл

@ -1,92 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const data_sources_1 = require("../../../data-sources");
const ElementConnector_1 = require("../../ElementConnector");
const DialogsActions_1 = require("./DialogsActions");
const DialogsStore_1 = require("./DialogsStore");
const Dialogs_1 = require("react-md/lib/Dialogs");
const ReactGridLayout = require("react-grid-layout");
var ResponsiveReactGridLayout = ReactGridLayout.Responsive;
var WidthProvider = ReactGridLayout.WidthProvider;
ResponsiveReactGridLayout = WidthProvider(ResponsiveReactGridLayout);
class Dialog extends React.PureComponent {
constructor(props) {
super(props);
this.layouts = {};
this.dataSources = {};
this.onBreakpointChange = (breakpoint) => {
var layouts = this.state.layouts;
layouts[breakpoint] = layouts[breakpoint] || this.layouts[breakpoint];
this.setState({
currentBreakpoint: breakpoint,
layouts: layouts
});
};
this.closeDialog = () => {
DialogsActions_1.default.closeDialog();
};
this.openDialog = () => {
DialogsActions_1.default.openDialog('conversations', { title: this.state.dialogArgs['title'] + ':Blue', intent: 'bla', queryTimespan: 'jjj' });
};
this.state = DialogsStore_1.default.getState();
this.onChange = this.onChange.bind(this);
this.openDialog = this.openDialog.bind(this);
// Create dialog data source
var dialogDS = {
id: 'dialog_' + this.props.dialogData.id,
type: 'Constant',
params: {
selectedValue: null
}
};
data_sources_1.DataSourceConnector.createDataSources({ dataSources: [dialogDS] }, this.dataSources);
// Adding other data sources
data_sources_1.DataSourceConnector.createDataSources(this.props.dialogData, this.dataSources);
var layouts = ElementConnector_1.default.loadLayoutFromDashboard(this.props.dialogData, this.props.dashboard);
this.layouts = layouts;
this.state.mounted = false;
this.state.currentBreakpoint = 'lg';
this.state.layouts = { lg: layouts['lg'] };
}
componentDidMount() {
this.setState({ mounted: true });
DialogsStore_1.default.listen(this.onChange);
data_sources_1.DataSourceConnector.connectDataSources(this.dataSources);
}
componentDidUpdate() {
const { dialogData } = this.props;
var { dialogId, dialogArgs } = this.state;
var datasourceId = 'dialog_' + dialogData.id;
this.dataSources[datasourceId].action.updateDependencies(dialogArgs);
}
onChange(state) {
var { dialogId, dialogArgs } = state;
this.setState({ dialogId, dialogArgs });
}
render() {
const { dialogData, dashboard } = this.props;
const { id } = dialogData;
const { dialogId, dialogArgs } = this.state;
var { title } = dialogArgs || { title: '' };
var visible = id === dialogId;
if (!visible) {
return null;
}
var { currentBreakpoint } = this.state;
var layout = this.state.layouts[currentBreakpoint];
// Creating visual elements
var elements = ElementConnector_1.default.loadElementsFromDashboard(dialogData, layout);
let grid = {
className: "layout",
rowHeight: dashboard.config.layout.rowHeight || 30,
cols: dashboard.config.layout.cols,
breakpoints: dashboard.config.layout.breakpoints
};
return (<Dialogs_1.default id={id} visible={visible} title={title} focusOnMount={false} onHide={this.closeDialog} dialogStyle={{ width: dialogData.width || '80%' }} contentStyle={{ padding: '0', maxHeight: 'calc(100vh - 148px)' }}>
{elements}
</Dialogs_1.default>);
}
}
exports.default = Dialog;

Просмотреть файл

@ -57,9 +57,7 @@ export default class Dialog extends React.PureComponent<IDialogProps, IDialogSta
var layouts = ElementConnector.loadLayoutFromDashboard(this.props.dialogData, this.props.dashboard);
this.layouts = layouts;
this.state.mounted = false;
this.state.currentBreakpoint = 'lg';
this.state.layouts = { lg: layouts['lg'] };
(this.state as any).layouts = { lg: layouts['lg'] };
}
componentDidMount() {

Просмотреть файл

@ -1,15 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const alt_1 = require("../../../alt");
class DialogsActions extends alt_1.AbstractActions {
constructor(alt) {
super(alt);
}
openDialog(dialogName, args) {
return { dialogName, args };
}
closeDialog() {
return {};
}
}
const dialogsActions = alt_1.default.createActions(DialogsActions);
exports.default = dialogsActions;

Просмотреть файл

@ -1,30 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const alt_1 = require("../../../alt");
const DialogsActions_1 = require("./DialogsActions");
class DialogsStore extends alt_1.AbstractStoreModel {
constructor() {
super();
this.dialogsStack = [];
this.dialogId = null;
this.dialogArgs = null;
this.bindListeners({
openDialog: DialogsActions_1.default.openDialog,
closeDialog: DialogsActions_1.default.closeDialog
});
}
openDialog(params) {
this.dialogsStack.push(params);
this.dialogId = params.dialogName;
this.dialogArgs = params.args;
}
closeDialog() {
this.dialogsStack.pop();
var dialog = this.dialogsStack.length > 0 ?
this.dialogsStack[this.dialogsStack.length - 1] :
{ dialogName: null, args: null };
this.dialogId = dialog.dialogName;
this.dialogArgs = dialog.args;
}
}
const dialogsStore = alt_1.default.createStore(DialogsStore, "DialogsStore");
exports.default = dialogsStore;

Просмотреть файл

@ -1,18 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const Dialog_1 = require("./Dialog");
const DialogsActions_1 = require("./DialogsActions");
const DialogsStore_1 = require("./DialogsStore");
function loadDialogsFromDashboard(dashboard) {
if (!dashboard.dialogs) {
return null;
}
var dialogs = dashboard.dialogs.map((dialog, idx) => <Dialog_1.default key={idx} dialogData={dialog} dashboard={dashboard}/>);
return dialogs;
}
exports.default = {
loadDialogsFromDashboard,
Dialog: Dialog_1.default,
DialogsActions: DialogsActions_1.default,
DialogsStore: DialogsStore_1.default
};

Просмотреть файл

@ -1,50 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const data_sources_1 = require("../../data-sources");
class GenericComponent extends React.Component {
// static propTypes = {}
// static defaultProps = {}
constructor(props) {
super(props);
this.onStateChange = this.onStateChange.bind(this);
this.trigger = this.trigger.bind(this);
var result = data_sources_1.DataSourceConnector.extrapolateDependencies(this.props.dependencies);
var initialState = {};
Object.keys(result.dependencies).forEach(key => {
initialState[key] = result.dependencies[key];
});
this.state = initialState;
}
componentDidMount() {
var result = data_sources_1.DataSourceConnector.extrapolateDependencies(this.props.dependencies);
Object.keys(result.dataSources).forEach(key => {
result.dataSources[key].store.listen(this.onStateChange);
});
}
componentWillUnmount() {
var result = data_sources_1.DataSourceConnector.extrapolateDependencies(this.props.dependencies);
Object.keys(result.dataSources).forEach(key => {
result.dataSources[key].store.unlisten(this.onStateChange);
});
}
onStateChange(state) {
var result = data_sources_1.DataSourceConnector.extrapolateDependencies(this.props.dependencies);
var updatedState = {};
Object.keys(result.dependencies).forEach(key => {
updatedState[key] = result.dependencies[key];
});
this.setState(updatedState);
}
trigger(actionName, args) {
var action = this.props.actions[actionName];
// if action was not defined, not action needed
if (!action) {
console.warn(`no action was found with name ${name}`);
return;
}
var actionId = typeof action === 'string' ? action : action.action;
var params = typeof action === 'string' ? {} : action.params;
data_sources_1.DataSourceConnector.triggerAction(actionId, params, args);
}
}
exports.GenericComponent = GenericComponent;

Просмотреть файл

@ -1,72 +0,0 @@
var __extends = (this && this.__extends) || (function () {
var extendStatics = Object.setPrototypeOf ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; };
return function (d, b) {
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
var React = require("react");
var Card_1 = require("material-ui/Card");
var recharts_1 = require("recharts");
var moment = require("moment");
var styles_1 = require("../styles");
var colors = styles_1.default.colors;
var ThemeColors = colors.ThemeColors;
;
;
var Graph = (function (_super) {
__extends(Graph, _super);
function Graph(props) {
var _this = _super.call(this, props) || this;
_this.onChange = _this.onChange.bind(_this);
_this.state = props.store.getState();
return _this;
}
Graph.prototype.componentDidMount = function () {
this.props.store.listen(this.onChange);
};
Graph.prototype.componentWillUnmount = function () {
this.props.store.unlisten(this.onChange);
};
Graph.prototype.onChange = function (state) {
this.setState(state);
};
Graph.prototype.dateFormat = function (time) {
return moment(time).format('MMM-DD');
};
Graph.prototype.hourFormat = function (time) {
return moment(time).format('HH:mm');
};
Graph.prototype.render = function () {
var data = this.state[this.props.data || 'data'] || [];
var lines = this.state[this.props.lines || 'lines'] || [];
var format = this.state.timespan === "24 hours" ? this.hourFormat : this.dateFormat;
var glines = [];
if (data && data.length) {
glines = lines.map(function (line, idx) {
return <recharts_1.Line key={idx} type="monotone" dataKey={line} stroke={ThemeColors[idx]} dot={false} ticksCount={5}/>;
});
}
return (<Card_1.Card className='dash-card'>
<Card_1.CardHeader className='card-header' title='Users' subtitle="How many messages were send in each channel"/>
<Card_1.CardMedia style={styles_1.default.cards.cardMediaStyle}>
<recharts_1.ResponsiveContainer>
<recharts_1.LineChart data={data} margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>
<recharts_1.XAxis dataKey="time" tickFormatter={format} minTickGap={20}/>
<recharts_1.YAxis />
<recharts_1.CartesianGrid strokeDasharray="3 3"/>
<recharts_1.Tooltip />
<recharts_1.Legend />
{glines}
</recharts_1.LineChart>
</recharts_1.ResponsiveContainer>
</Card_1.CardMedia>
</Card_1.Card>);
};
return Graph;
}(React.Component));
exports.default = Graph;

Просмотреть файл

@ -1,83 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const GenericComponent_1 = require("./GenericComponent");
const Card_1 = require("../Card");
const recharts_1 = require("recharts");
const colors_1 = require("../colors");
var { ThemeColors } = colors_1.default;
;
class PieData extends GenericComponent_1.GenericComponent {
constructor(props) {
super(props);
this.state = {
activeIndex: 0,
values: null
};
this.renderActiveShape = (props) => {
const { mode } = this.props;
var type = mode === 'users' ? 'Users' : 'Messages';
const RADIAN = Math.PI / 180;
const { name, cx, cy, midAngle, innerRadius, outerRadius, startAngle, endAngle, fill, payload, percent, value } = props;
const sin = Math.sin(-RADIAN * midAngle);
const cos = Math.cos(-RADIAN * midAngle);
const sx = cx + (outerRadius + 10) * cos;
const sy = cy + (outerRadius + 10) * sin;
const mx = cx + (outerRadius + 30) * cos;
const my = cy + (outerRadius + 30) * sin;
const ex = mx + (cos >= 0 ? 1 : -1) * 22;
const ey = my;
const textAnchor = cos >= 0 ? 'start' : 'end';
var c = {};
c.midAngle = 54.11764705882353;
c.sin = Math.sin(-RADIAN * c.midAngle);
c.cos = Math.cos(-RADIAN * c.midAngle);
c.cx = cx;
c.cy = cy;
c.sx = cx + (outerRadius + 10) * c.cos;
c.sy = cy + (outerRadius + 10) * c.sin;
c.mx = cx + (outerRadius + 30) * c.cos;
c.my = cy + (outerRadius + 30) * c.sin;
c.ex = c.mx + (c.cos >= 0 ? 1 : -1) * 22;
c.ey = c.my;
c.textAnchor = 'start';
return (<g>
<text x={cx} y={cy} dy={8} textAnchor="middle" fill={fill}>{name}</text>
<recharts_1.Sector cx={cx} cy={cy} innerRadius={innerRadius} outerRadius={outerRadius} startAngle={startAngle} endAngle={endAngle} fill={fill}/>
<recharts_1.Sector cx={c.cx} cy={c.cy} startAngle={300} endAngle={60} innerRadius={outerRadius + 6} outerRadius={outerRadius + 10} fill={fill}/>
<path d={`M${c.sx},${c.sy}L${c.mx},${c.my}L${c.ex},${c.ey}`} stroke={fill} fill="none"/>
<circle cx={c.ex} cy={c.ey} r={2} fill={fill} stroke="none"/>
<text x={c.ex + (c.cos >= 0 ? 1 : -1) * 12} y={c.ey} textAnchor={c.textAnchor} fill="#333">{`${value} ${type.toLowerCase()}`}</text>
<text x={c.ex + (c.cos >= 0 ? 1 : -1) * 12} y={c.ey} dy={18} textAnchor={c.textAnchor} fill="#999">
{`(Rate ${(percent * 100).toFixed(2)}%)`}
</text>
</g>);
};
this.onPieEnter = this.onPieEnter.bind(this);
}
onPieEnter(data, index) {
this.setState({ activeIndex: index });
}
render() {
var { values } = this.state;
var { props, title, subtitle, layout, theme } = this.props;
var { pieProps, showLegend } = props;
if (!values) {
return null;
}
var themeColors = theme || ThemeColors;
// Todo: Receive the width of the SVG component from the container
return (<Card_1.default title={title} subtitle={subtitle}>
<recharts_1.ResponsiveContainer>
<recharts_1.PieChart>
<recharts_1.Pie data={values} cx={Math.min(layout.h / 4, layout.w) * 60} innerRadius={60} fill="#8884d8" onMouseEnter={this.onPieEnter} activeIndex={this.state.activeIndex} activeShape={this.renderActiveShape.bind(this)} paddingAngle={0} {...pieProps}>
{values.map((entry, index) => <recharts_1.Cell key={index} fill={themeColors[index % themeColors.length]}/>)}
<recharts_1.Cell key={0} fill={colors_1.default.GoodColor}/>
<recharts_1.Cell key={1} fill={colors_1.default.BadColor}/>
</recharts_1.Pie>
{showLegend !== false && <recharts_1.Legend layout="vertical" align="right" verticalAlign="top"/>}
</recharts_1.PieChart>
</recharts_1.ResponsiveContainer>
</Card_1.default>);
}
}
exports.default = PieData;

Просмотреть файл

@ -1,49 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const GenericComponent_1 = require("./GenericComponent");
const Card_1 = require("../Card");
const recharts_1 = require("recharts");
const moment = require("moment");
const colors_1 = require("../colors");
var { ThemeColors } = colors_1.default;
class Scatter extends GenericComponent_1.GenericComponent {
dateFormat(time) {
return moment(time).format('MMM-DD');
}
hourFormat(time) {
return moment(time).format('HH:mm');
}
render() {
var { title, subtitle, theme } = this.props;
var { timeFormat, values, lines } = this.state;
var format = timeFormat === "hour" ? this.hourFormat : this.dateFormat;
var themeColors = theme || ThemeColors;
const data01 = [{ x: 100, y: 200, z: 200 }, { x: 120, y: 100, z: 260 },
{ x: 170, y: 300, z: 400 }, { x: 140, y: 250, z: 280 },
{ x: 150, y: 400, z: 500 }, { x: 110, y: 280, z: 200 }];
const data02 = [{ x: 200, y: 260, z: 240 }, { x: 240, y: 290, z: 220 },
{ x: 190, y: 290, z: 250 }, { x: 198, y: 250, z: 210 },
{ x: 180, y: 280, z: 260 }, { x: 210, y: 220, z: 230 }];
var scatters = [];
// if (data && data.length) {
// scatters = lines.map((line, idx) => {
// return <Scatter key={idx} name={line.name} dataKey={line} stroke={ThemeColors[idx]} dot={false} ticksCount={5}/>
// })
// }
return (<Card_1.default title={title} subtitle={subtitle}>
<recharts_1.ResponsiveContainer>
<recharts_1.ScatterChart margin={{ top: 5, right: 30, left: 20, bottom: 5 }}>
<recharts_1.XAxis dataKey={'x'} tickFormatter={format} minTickGap={20} name='stature'/>
<recharts_1.YAxis dataKey={'y'} name='weight' unit='kg'/>
<recharts_1.ZAxis dataKey={'z'} range={[60, 600]} name='score' unit='km'/>
<recharts_1.CartesianGrid strokeDasharray="3 3"/>
<recharts_1.Tooltip cursor={{ strokeDasharray: '3 3' }}/>
<recharts_1.Legend />
<recharts_1.Scatter name='A school' data={data01} fill={colors_1.default.ThemeColors[0]}/>
<recharts_1.Scatter name='B school' data={data02} fill={colors_1.default.ThemeColors[1]}/>
</recharts_1.ScatterChart>
</recharts_1.ResponsiveContainer>
</Card_1.default>);
}
}
exports.default = Scatter;

Просмотреть файл

@ -1,25 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const GenericComponent_1 = require("./GenericComponent");
const Media_1 = require("react-md/lib/Media");
const Cards_1 = require("react-md/lib/Cards");
const FontIcons_1 = require("react-md/lib/FontIcons");
class Scorecard extends GenericComponent_1.GenericComponent {
render() {
var { value, icon, className } = this.state;
var { title } = this.props;
return (<Cards_1.Card>
<Media_1.Media className='md-card-scorecard'>
<div className='md-grid md-headline'>
{icon &&
<div className="ms-cell md-cell--middle md-cell--2 dash-icon">
<FontIcons_1.default className={className}>{icon}</FontIcons_1.default>
</div>}
<div className='md-cell'>{title}</div>
<div className='md-cell--right dash-value'>{value}</div>
</div>
</Media_1.Media>
</Cards_1.Card>);
}
}
exports.default = Scorecard;

Просмотреть файл

@ -1,10 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const alt_1 = require("../../../alt");
class SpinnerActions extends alt_1.AbstractActions /*implements ISpinnerActions*/ {
constructor(alt) {
super(alt);
this.generateActions('startPageLoading', 'endPageLoading', 'startRequestLoading', 'endRequestLoading');
}
}
const spinnerActions = alt_1.default.createActions(SpinnerActions);
exports.default = spinnerActions;

Просмотреть файл

@ -1,30 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const alt_1 = require("../../../alt");
const SpinnerActions_1 = require("./SpinnerActions");
class SpinnerStore extends alt_1.AbstractStoreModel {
constructor() {
super();
this.pageLoading = 0;
this.requestLoading = 0;
this.bindListeners({
startPageLoading: SpinnerActions_1.default.startPageLoading,
endPageLoading: SpinnerActions_1.default.endPageLoading,
startRequestLoading: SpinnerActions_1.default.startRequestLoading,
endRequestLoading: SpinnerActions_1.default.endRequestLoading,
});
}
startPageLoading() {
this.pageLoading++;
}
endPageLoading() {
this.pageLoading--;
}
startRequestLoading() {
this.requestLoading++;
}
endRequestLoading() {
this.requestLoading--;
}
}
const spinnerStore = alt_1.default.createStore(SpinnerStore, "SpinnerStore");
exports.default = spinnerStore;

Просмотреть файл

@ -3,20 +3,29 @@ import alt, { AbstractStoreModel } from '../../../alt';
import spinnerActions from './SpinnerActions';
export interface ISpinnerStoreState {
pageLoading?: number,
pageLoading?: number
requestLoading?: number
mounted: boolean
currentBreakpoint: string
layouts: object
}
class SpinnerStore extends AbstractStoreModel<ISpinnerStoreState> implements ISpinnerStoreState {
pageLoading: number;
requestLoading: number;
mounted: boolean;
currentBreakpoint: string;
layouts: any;
constructor() {
super();
this.pageLoading = 0;
this.requestLoading = 0;
this.mounted = false;
this.currentBreakpoint = 'lg';
this.layouts = { };
this.bindListeners({
startPageLoading: spinnerActions.startPageLoading,

Просмотреть файл

@ -1,77 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const $ = require("jquery");
const _ = require("lodash");
const CircularProgress_1 = require("react-md/lib/Progress/CircularProgress");
const Snackbars_1 = require("react-md/lib/Snackbars");
const SpinnerStore_1 = require("./SpinnerStore");
const SpinnerActions_1 = require("./SpinnerActions");
class Spinner extends React.Component {
constructor(props) {
super(props);
this.state = SpinnerStore_1.default.getState();
this.state.snacks = {
toasts: [],
autohide: true
};
this.onChange = this.onChange.bind(this);
this._addToast = this._addToast.bind(this);
this._removeToast = this._removeToast.bind(this);
this._429ApplicationInsights = this._429ApplicationInsights.bind(this);
var self = this;
$.ajaxSetup({
beforeSend: function () {
SpinnerActions_1.default.startRequestLoading();
},
complete: function (response) {
SpinnerActions_1.default.endRequestLoading();
if (response.status === 429) {
self._429ApplicationInsights();
}
}
});
// Todo: Add timeout to requests - if no reply received, turn spinner off
}
componentDidMount() {
SpinnerStore_1.default.listen(this.onChange);
}
componentWillUpdate(nextProps, nextState) {
const { snacks } = nextState;
const [toast] = snacks.toasts;
if (this.state.snacks.toasts === snacks.toasts || !toast) {
return;
}
snacks.autohide = toast.action !== 'Retry';
this.setState({ snacks });
}
_removeToast() {
const { snacks } = this.state;
const [, ...toasts] = snacks.toasts;
snacks.toasts = toasts;
this.setState({ snacks });
}
_429ApplicationInsights() {
this._addToast('You have reached the maximum number of Application Insights requests.');
}
_addToast(text, action = null) {
const { snacks } = this.state;
const toasts = snacks.toasts.slice();
if (_.find(toasts, { text })) {
return;
}
toasts.push({ text, action });
snacks.toasts = toasts;
this.setState({ snacks });
}
onChange(state) {
this.setState(state);
}
render() {
let refreshing = this.state.pageLoading || this.state.requestLoading || false;
return (<div>
{refreshing && <CircularProgress_1.default key="progress" id="contentLoadingProgress"/>}
<Snackbars_1.default {...this.state.snacks} onDismiss={this._removeToast}/>
</div>);
}
}
exports.default = Spinner;

Просмотреть файл

@ -22,7 +22,7 @@ export default class Spinner extends React.Component<any, ISpinnerState> {
super(props);
this.state = SpinnerStore.getState();
this.state.snacks = {
(this.state as any).snacks = {
toasts: [],
autohide: true
};

Просмотреть файл

@ -1,54 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const GenericComponent_1 = require("../GenericComponent");
const moment = require("moment");
const DataTables_1 = require("react-md/lib/DataTables");
const Cards_1 = require("react-md/lib/Cards");
const FontIcons_1 = require("react-md/lib/FontIcons");
const Button_1 = require("react-md/lib/Buttons/Button");
require("./Table.css");
class Table extends GenericComponent_1.GenericComponent {
constructor(props) {
super(props);
this.state = {
values: []
};
this.onButtonClick = (col, value) => {
this.trigger(col.onClick, value);
};
this.onButtonClick = this.onButtonClick.bind(this);
}
render() {
var { props } = this.props;
var { checkboxes, cols } = props;
var { values } = this.state;
var arr = values.slice(0);
arr = arr.concat(values);
arr = arr.concat(values);
arr = arr.concat(values);
arr = arr.concat(values);
arr = arr.concat(values);
const rows = arr.map((value, i) => (<DataTables_1.TableRow key={i}>
{cols.map((col, i) => <DataTables_1.TableColumn key={i}>{col.type === 'icon' ?
<FontIcons_1.default>{col.value || value[col.field]}</FontIcons_1.default> :
col.type === 'button' ?
<Button_1.default icon onClick={this.onButtonClick.bind(this, col, value)}>{col.value || value[col.field]}</Button_1.default> :
col.type === 'time' ?
moment(value[col.field]).format('MMM-DD HH:mm:ss') :
value[col.field]}</DataTables_1.TableColumn>)}
</DataTables_1.TableRow>));
return (<Cards_1.Card>
<DataTables_1.DataTable plain={!checkboxes} data={checkboxes}>
<DataTables_1.TableHeader>
<DataTables_1.TableRow>
{cols.map((col, i) => <DataTables_1.TableColumn key={i} width={col.width}>{col.header}</DataTables_1.TableColumn>)}
</DataTables_1.TableRow>
</DataTables_1.TableHeader>
<DataTables_1.TableBody>
{rows}
</DataTables_1.TableBody>
</DataTables_1.DataTable>
</Cards_1.Card>);
}
}
exports.default = Table;

Просмотреть файл

@ -21,18 +21,18 @@ export interface ITableProps extends IGenericProps {
type?: 'text' | 'time' | 'icon' | 'button',
click?: string
}[]
}
};
}
export interface ITableState extends IGenericState {
values: Object[]
values: Object[];
}
export default class Table extends GenericComponent<ITableProps, ITableState> {
state = {
values: []
}
};
constructor(props) {
super(props);
@ -60,16 +60,18 @@ export default class Table extends GenericComponent<ITableProps, ITableState> {
const rows = arr.map((value, i) => (
<TableRow key={i}>
{
cols.map((col, i) =>
<TableColumn key={i}>{
col.type === 'icon' ?
<FontIcon>{col.value || value[col.field]}</FontIcon> :
col.type === 'button' ?
<Button icon onClick={this.onButtonClick.bind(this, col, value)}>{col.value || value[col.field]}</Button> :
col.type === 'time' ?
moment(value[col.field]).format('MMM-DD HH:mm:ss') :
value[col.field]
}</TableColumn>)
cols.map((col, i) => (
<TableColumn key={i}>{
col.type === 'icon' ?
<FontIcon>{col.value || value[col.field]}</FontIcon> :
col.type === 'button' ?
<Button icon={true}
onClick={this.onButtonClick.bind(this, col, value)}>{col.value || value[col.field]}</Button> :
col.type === 'time' ?
moment(value[col.field]).format('MMM-DD HH:mm:ss') :
value[col.field]
}</TableColumn>
))
}
</TableRow>
));
@ -79,9 +81,7 @@ export default class Table extends GenericComponent<ITableProps, ITableState> {
<DataTable plain={!checkboxes} data={checkboxes}>
<TableHeader>
<TableRow>
{
cols.map((col, i) => <TableColumn key={i} width={col.width}>{col.header}</TableColumn>)
}
{cols.map((col, i) => <TableColumn key={i} width={col.width}>{col.header}</TableColumn>)}
</TableRow>
</TableHeader>
<TableBody>

Просмотреть файл

@ -1,3 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const Table_1 = require("./Table");
exports.default = Table_1.default;

Просмотреть файл

@ -1,25 +0,0 @@
Object.defineProperty(exports, "__esModule", { value: true });
const React = require("react");
const GenericComponent_1 = require("./GenericComponent");
const SelectFields_1 = require("react-md/lib/SelectFields");
const numbers = [1, 2, 3, 4, 5, 6, 7, 8, 9];
class TextFilter extends GenericComponent_1.GenericComponent {
// static propTypes = {}
// static defaultProps = {}
constructor(props) {
super(props);
this.onChange = this.onChange.bind(this);
}
onChange(newValue, index, event) {
this.trigger('onChange', [newValue]);
}
render() {
var { selectedValue, values } = this.state;
values = values || [];
// var buttons = values.map((value, idx) => {
// return <Button flat key={idx} label={value} primary={value === selectedValue} onClick={this.onChange.bind(null, value)} />
// })
return (<SelectFields_1.default id="timespan" label="Timespan" value={selectedValue} menuItems={values} position={SelectFields_1.default.Positions.BELOW} onChange={this.onChange} toolbar={false} className='md-select-field--toolbar'/>);
}
}
exports.default = TextFilter;

Просмотреть файл

@ -16,7 +16,7 @@ export default class TextFilter extends GenericComponent<any, any> {
}
onChange(newValue, index, event) {
this.trigger('onChange', [newValue]);
this.trigger('onChange', { 0: newValue });
}
render() {

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше