Merge branch 'master' of cloud.ol:oliver/simcompanies-dashboard

This commit is contained in:
2020-04-30 23:13:17 +02:00
16 changed files with 2608 additions and 551 deletions

View File

@@ -2,30 +2,78 @@ const express = require('express')
const app = express();
const status = require('http-status');
const fetch = require('node-fetch');
const cors = require('cors');
const bodyParser = require('body-parser');
var mysql = require('mysql');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var session = require('express-session');
var MySQLStore = require('express-mysql-session')(session);
const bcrypt = require('bcrypt');
const path = require('path');
var connection = mysql.createConnection({
var connection = mysql.createPool({
connectionLimit: 25,
host: 'localhost',
user: 'simcompanies',
password: '',
database: 'simcompanies'
});
var sessionStore = new MySQLStore({
clearExpired: false,
schema: {
tableName: 'sessions',
columnNames: {
session_id: 'session_id',
expires: 'expires',
data: 'data'
}
}
}, connection);
const mockDataDay = require('./mockdata-test/day.json');
var resourceList;
const saltRounds = 13;
var serverStartupComplete = false;
const DEBUG = process.env.NODE_ENV === "debug" ? true : false;
function twoDigits(d) {
if (0 <= d && d < 10) return "0" + d.toString();
if (-10 < d && d < 0) return "-0" + (-1 * d).toString();
return d.toString();
}
Date.prototype.toMysqlFormat = function () {
return this.getFullYear() + "-" + twoDigits(1 + this.getMonth()) + "-" + twoDigits(this.getDate()) + " " + twoDigits(this.getHours()) + ":" + twoDigits(this.getMinutes()) + ":" + twoDigits(this.getSeconds());
};
async function loadData() {
var rL = await fetch("https://www.simcompanies.com/api/v3/en/encyclopedia/resources/");
resourceList = await rL.json();
serverStartupComplete = true;
console.log("SERVER STARTUP COMPLETED");
}
loadData();
app.use(cors({ credentials: true }));
app.use(bodyParser.urlencoded({ extended: true }));
app.use(bodyParser.json());
app.use(session({
secret: "simCoRoxUFocker",
saveUninitialized: true,
resave: true,
store: DEBUG ? undefined : sessionStore,
"cookie": {
"maxAge": 86400 * 1000
}
}));
app.use(passport.initialize());
app.use(passport.session());
app.get("*", function (req, res, next) {
if (!serverStartupComplete)
return res.send("Server is starting...");
return res.send("Server is starting... Please try again soon.");
else return next();
});
@@ -39,29 +87,116 @@ app.all("/*", function (req, res, next) {
if (!/^\/API\/.*/.test(req.url)) {
return res.sendFile(path.join(__dirname, 'frontend', 'index.html'));
} else return next();
})
});
passport.use('local-login', new LocalStrategy({
usernameField: "email",
passwordField: "password",
passReqToCallback: true
}, function (req, email, password, done) {
if (DEBUG) {
if (email === "test" && password === "test") {
return done(null, {
deactivated: false,
email: "test1",
id: 0,
created: new Date(),
password: "test"
});
} else return done(null, false);
}
email = mysql.escape(email);
connection.query(`SELECT * from user WHERE email = ${email} AND deactivated = 0`, function (err, rows) {
if (err) {
return res.status(static.INTERNAL_SERVER_ERROR).send("error querying the database - Please contact sys admin");
}
if (!rows.length) {
return done(null, false);
}
if (!bcrypt.compareSync(password, rows[0].password)) {
return done(null, false);
}
return done(null, rows[0]);
})
}
));
passport.serializeUser(function (user, done) {
done(null, user.id);
});
passport.deserializeUser(function (id, done) {
if (!DEBUG)
connection.query(`select * from user where id = ${id} AND deactivated = 0`, function (err, rows) {
done(err, rows[0]);
})
else {
if (id === 0) {
done(null, {
deactivated: false,
email: "test1",
id: 0,
created: new Date(),
password: "test"
});
} else return done(null, []);
}
});
app.post("/API/user/login", passport.authenticate('local-login'), function (req, res) {
res.status(status.OK).send("login success");
});
app.put("/API/user/create", function (req, res) {
let { email, password } = req.body;
if (email && password) {
email = mysql.escape(email);
password = mysql.escape(bcrypt.hashSync(password, saltRounds));
if (DEBUG) return res.status(status.OK).send();
connection.query(`INSERT INTO user (deactivated, email, password) values (1, ${email}, ${password})`, function (err, rows) {
if (err)
return res.send(status.INTERNAL_SERVER_ERROR).send("the user seems to exist already - if you think this is an error contact the sys admin");
return res.status(status.OK).send("account successfully created");
});
} else {
res.status(status.BAD_REQUEST).send("invalid data supplied");
}
});
app.all("*", function (req, res, next) {
if (req.isAuthenticated()) {
return next();
}
else {
res.status(status.UNAUTHORIZED).send("please log in before using this site");
}
});
app.get("/API/testlogin", function (req, res) {
res.status(status.OK).send(req.user["email"]);
});
app.get('/API/day', function (req, res) {
var date = new Date(req.query.date);
const kind = parseInt(req.query.kind);
if (date instanceof Date && Number.isInteger(kind)) {
if (Number.isInteger(kind)) {
//Mock Data:
if (kind === -1) return res.send(mockDataDay);
if (kind === -1 || DEBUG) return res.send(mockDataDay);
if (!isNaN(date.getTime()) && kind >= 1 && kind <= 113) {
const daybegin = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
date.setDate(date.getDate() + 1);
const dayend = `${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`;
const querystring = `SELECT time, price FROM market WHERE kind = ${kind} AND time > "${daybegin}" AND time < "${dayend}"`
if (kind >= 1 && kind <= 113) {
var dayend = new Date().toMysqlFormat();
var daybegin = new Date();
daybegin.setDate(daybegin.getDate() - 1);
daybegin = daybegin.toISOString().slice(0, 19).replace('T', ' ');
const querystring = `SELECT time, price, quality FROM marketv2 WHERE kind = ${kind} AND time > "${daybegin}" AND time < "${dayend}" ORDER BY time, quality`;
connection.query(querystring, function (error, results, fields) {
if (error) {
throw error;
res.status(status.INTERNAL_SERVER_ERROR).send("database connection failed");
}
res.send(results);
});
}
else
res.status(status.BAD_REQUEST).send("invalid data provided");
res.status(status.BAD_REQUEST).send("invalid data range provided");
}
else

File diff suppressed because it is too large Load Diff

View File

@@ -48,6 +48,20 @@
"picomatch": "^2.0.4"
}
},
"aproba": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/aproba/-/aproba-1.2.0.tgz",
"integrity": "sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw=="
},
"are-we-there-yet": {
"version": "1.1.5",
"resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz",
"integrity": "sha512-5hYdAkZlcG8tOLujVDTgCT+uPX0VnpAH28gWsLfzpXYm7wP6mp5Q/gYyR7YQ0cKVJcXJnl3j2kpBan13PtQf6w==",
"requires": {
"delegates": "^1.0.0",
"readable-stream": "^2.0.6"
}
},
"array-flatten": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz",
@@ -58,6 +72,15 @@
"resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
"integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
},
"bcrypt": {
"version": "4.0.1",
"resolved": "https://registry.npmjs.org/bcrypt/-/bcrypt-4.0.1.tgz",
"integrity": "sha512-hSIZHkUxIDS5zA2o00Kf2O5RfVbQ888n54xQoF/eIaquU4uaLxK8vhhBdktd0B3n2MjkcAWzv4mnhogykBKOUQ==",
"requires": {
"node-addon-api": "^2.0.0",
"node-pre-gyp": "0.14.0"
}
},
"bignumber.js": {
"version": "9.0.0",
"resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.0.0.tgz",
@@ -156,6 +179,11 @@
"readdirp": "~3.3.0"
}
},
"chownr": {
"version": "1.1.4",
"resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz",
"integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg=="
},
"ci-info": {
"version": "1.6.0",
"resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz",
@@ -166,6 +194,11 @@
"resolved": "https://registry.npmjs.org/cli-boxes/-/cli-boxes-1.0.0.tgz",
"integrity": "sha1-T6kXw+WclKAEzWH47lCdplFocUM="
},
"code-point-at": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz",
"integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c="
},
"color-convert": {
"version": "1.9.3",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz",
@@ -197,6 +230,11 @@
"xdg-basedir": "^3.0.0"
}
},
"console-control-strings": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
"integrity": "sha1-PXz0Rk22RG6mRL9LOVB/mFEAjo4="
},
"content-disposition": {
"version": "0.5.3",
"resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz",
@@ -225,6 +263,15 @@
"resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
"integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
},
"cors": {
"version": "2.8.5",
"resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz",
"integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==",
"requires": {
"object-assign": "^4",
"vary": "^1"
}
},
"create-error-class": {
"version": "3.0.2",
"resolved": "https://registry.npmjs.org/create-error-class/-/create-error-class-3.0.2.tgz",
@@ -233,6 +280,58 @@
"capture-stack-trace": "^1.0.0"
}
},
"cross-env": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.2.tgz",
"integrity": "sha512-KZP/bMEOJEDCkDQAyRhu3RL2ZO/SUVrxQVI0G3YEQ+OLbRA3c6zgixe8Mq8a/z7+HKlNEjo8oiLUs8iRijY2Rw==",
"dev": true,
"requires": {
"cross-spawn": "^7.0.1"
},
"dependencies": {
"cross-spawn": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.2.tgz",
"integrity": "sha512-PD6G8QG3S4FK/XCGFbEQrDqO2AnMMsy0meR7lerlIOHAAbkuavGU/pOqprrlvfTNjvowivTeBsjebAL0NSoMxw==",
"dev": true,
"requires": {
"path-key": "^3.1.0",
"shebang-command": "^2.0.0",
"which": "^2.0.1"
}
},
"path-key": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
"integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
"dev": true
},
"shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
"integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
"dev": true,
"requires": {
"shebang-regex": "^3.0.0"
}
},
"shebang-regex": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
"integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
"dev": true
},
"which": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
"integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
"dev": true,
"requires": {
"isexe": "^2.0.0"
}
}
}
},
"cross-spawn": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-5.1.0.tgz",
@@ -261,6 +360,11 @@
"resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz",
"integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA=="
},
"delegates": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz",
"integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o="
},
"depd": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz",
@@ -271,6 +375,11 @@
"resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz",
"integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA="
},
"detect-libc": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz",
"integrity": "sha1-+hN8S9aY7fVc1c0CrFWfkaTEups="
},
"dot-prop": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/dot-prop/-/dot-prop-4.2.0.tgz",
@@ -360,6 +469,99 @@
"vary": "~1.1.2"
}
},
"express-mysql-session": {
"version": "2.1.4",
"resolved": "https://registry.npmjs.org/express-mysql-session/-/express-mysql-session-2.1.4.tgz",
"integrity": "sha512-Fcq168xVI8jtIJLhVHLJvBCvJlHnFWCcPmtt93UrWH38T2YsB919KrMCCh57/YkECkfff/L5zTQ95K1DxfOixg==",
"requires": {
"debug": "4.1.1",
"express-session": "1.17.0",
"mysql": "2.18.1",
"underscore": "1.9.2"
},
"dependencies": {
"debug": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz",
"integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==",
"requires": {
"ms": "^2.1.1"
}
},
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"express-session": {
"version": "1.17.0",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.0.tgz",
"integrity": "sha512-t4oX2z7uoSqATbMfsxWMbNjAL0T5zpvcJCk3Z9wnPPN7ibddhnmDZXHfEcoBMG2ojKXZoCyPMc5FbtK+G7SoDg==",
"requires": {
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.0",
"uid-safe": "~2.1.5"
},
"dependencies": {
"debug": {
"version": "2.6.9",
"resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
"integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
"requires": {
"ms": "2.0.0"
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
"integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g="
}
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
},
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
}
}
},
"express-session": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/express-session/-/express-session-1.17.1.tgz",
"integrity": "sha512-UbHwgqjxQZJiWRTMyhvWGvjBQduGCSBDhhZXYenziMFjxst5rMV+aJZ6hKPHZnPyHGsrqRICxtX8jtEbm/z36Q==",
"requires": {
"cookie": "0.4.0",
"cookie-signature": "1.0.6",
"debug": "2.6.9",
"depd": "~2.0.0",
"on-headers": "~1.0.2",
"parseurl": "~1.3.3",
"safe-buffer": "5.2.0",
"uid-safe": "~2.1.5"
},
"dependencies": {
"depd": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw=="
},
"safe-buffer": {
"version": "5.2.0",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.0.tgz",
"integrity": "sha512-fZEwUGbVl7kouZs1jCdMLdt95hdIv0ZeHg6L7qPeciMZhZ+/gdesW4wgTARkrFWEpspjEATAzUGPG8N2jJiwbg=="
}
}
},
"fill-range": {
"version": "7.0.1",
"resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
@@ -392,17 +594,91 @@
"resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz",
"integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac="
},
"fs-minipass": {
"version": "1.2.7",
"resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz",
"integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==",
"requires": {
"minipass": "^2.6.0"
}
},
"fs.realpath": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
"integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
},
"fsevents": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.2.tgz",
"integrity": "sha512-R4wDiBwZ0KzpgOWetKDug1FZcYhqYnUYKtfZYt4mD5SBz76q0KR4Q9o7GIPamsVPGmW3EYPPJ0dOOjvx32ldZA==",
"optional": true
},
"gauge": {
"version": "2.7.4",
"resolved": "https://registry.npmjs.org/gauge/-/gauge-2.7.4.tgz",
"integrity": "sha1-LANAXHU4w51+s3sxcCLjJfsBi/c=",
"requires": {
"aproba": "^1.0.3",
"console-control-strings": "^1.0.0",
"has-unicode": "^2.0.0",
"object-assign": "^4.1.0",
"signal-exit": "^3.0.0",
"string-width": "^1.0.1",
"strip-ansi": "^3.0.1",
"wide-align": "^1.1.0"
},
"dependencies": {
"ansi-regex": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz",
"integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8="
},
"is-fullwidth-code-point": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz",
"integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs=",
"requires": {
"number-is-nan": "^1.0.0"
}
},
"string-width": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz",
"integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=",
"requires": {
"code-point-at": "^1.0.0",
"is-fullwidth-code-point": "^1.0.0",
"strip-ansi": "^3.0.0"
}
},
"strip-ansi": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz",
"integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=",
"requires": {
"ansi-regex": "^2.0.0"
}
}
}
},
"get-stream": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
"integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ="
},
"glob": {
"version": "7.1.6",
"resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
"integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
"requires": {
"fs.realpath": "^1.0.0",
"inflight": "^1.0.4",
"inherits": "2",
"minimatch": "^3.0.4",
"once": "^1.3.0",
"path-is-absolute": "^1.0.0"
}
},
"glob-parent": {
"version": "5.1.0",
"resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.0.tgz",
@@ -447,6 +723,11 @@
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
"integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0="
},
"has-unicode": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
"integrity": "sha1-4Ob+aijPUROIVeCG0Wkedx3iqLk="
},
"http-errors": {
"version": "1.7.2",
"resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz",
@@ -477,6 +758,14 @@
"resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
"integrity": "sha1-SMptcvbGo68Aqa1K5odr44ieKwk="
},
"ignore-walk": {
"version": "3.0.3",
"resolved": "https://registry.npmjs.org/ignore-walk/-/ignore-walk-3.0.3.tgz",
"integrity": "sha512-m7o6xuOaT1aqheYHKf8W6J5pYH85ZI9w077erOzLje3JsB1gkafkAhHHY19dqjulgIZHFm32Cp5uNZgcQqdJKw==",
"requires": {
"minimatch": "^3.0.4"
}
},
"import-lazy": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/import-lazy/-/import-lazy-2.1.0.tgz",
@@ -487,6 +776,15 @@
"resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
"integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o="
},
"inflight": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
"integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
"requires": {
"once": "^1.3.0",
"wrappy": "1"
}
},
"inherits": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz",
@@ -669,6 +967,45 @@
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
"integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ="
},
"minipass": {
"version": "2.9.0",
"resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz",
"integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==",
"requires": {
"safe-buffer": "^5.1.2",
"yallist": "^3.0.0"
},
"dependencies": {
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
}
}
},
"minizlib": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz",
"integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==",
"requires": {
"minipass": "^2.9.0"
}
},
"mkdirp": {
"version": "0.5.5",
"resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz",
"integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==",
"requires": {
"minimist": "^1.2.5"
},
"dependencies": {
"minimist": {
"version": "1.2.5",
"resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
"integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw=="
}
}
},
"ms": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
@@ -685,16 +1022,74 @@
"sqlstring": "2.3.1"
}
},
"needle": {
"version": "2.4.1",
"resolved": "https://registry.npmjs.org/needle/-/needle-2.4.1.tgz",
"integrity": "sha512-x/gi6ijr4B7fwl6WYL9FwlCvRQKGlUNvnceho8wxkwXqN8jvVmmmATTmZPRRG7b/yC1eode26C2HO9jl78Du9g==",
"requires": {
"debug": "^3.2.6",
"iconv-lite": "^0.4.4",
"sax": "^1.2.4"
},
"dependencies": {
"debug": {
"version": "3.2.6",
"resolved": "https://registry.npmjs.org/debug/-/debug-3.2.6.tgz",
"integrity": "sha512-mel+jf7nrtEl5Pn1Qx46zARXKDpBbvzezse7p7LqINmdoIk8PYP5SySaxEmYv6TZ0JyEKA1hsCId6DIhgITtWQ==",
"requires": {
"ms": "^2.1.1"
}
},
"ms": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
"integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w=="
}
}
},
"negotiator": {
"version": "0.6.2",
"resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz",
"integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw=="
},
"node-addon-api": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.0.tgz",
"integrity": "sha512-ASCL5U13as7HhOExbT6OlWJJUV/lLzL2voOSP1UVehpRD8FbSrSDjfScK/KwAvVTI5AS6r4VwbOMlIqtvRidnA=="
},
"node-fetch": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
"integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
},
"node-pre-gyp": {
"version": "0.14.0",
"resolved": "https://registry.npmjs.org/node-pre-gyp/-/node-pre-gyp-0.14.0.tgz",
"integrity": "sha512-+CvDC7ZttU/sSt9rFjix/P05iS43qHCOOGzcr3Ry99bXG7VX953+vFyEuph/tfqoYu8dttBkE86JSKBO2OzcxA==",
"requires": {
"detect-libc": "^1.0.2",
"mkdirp": "^0.5.1",
"needle": "^2.2.1",
"nopt": "^4.0.1",
"npm-packlist": "^1.1.6",
"npmlog": "^4.0.2",
"rc": "^1.2.7",
"rimraf": "^2.6.1",
"semver": "^5.3.0",
"tar": "^4.4.2"
},
"dependencies": {
"nopt": {
"version": "4.0.3",
"resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.3.tgz",
"integrity": "sha512-CvaGwVMztSMJLOeXPrez7fyfObdZqNUK1cPAEzLHrTybIua9pMdmmPR5YwtfNftIOMv3DPUhFaxsZMNTQO20Kg==",
"requires": {
"abbrev": "1",
"osenv": "^0.1.4"
}
}
}
},
"nodemon": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/nodemon/-/nodemon-2.0.2.tgz",
@@ -740,6 +1135,29 @@
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
"integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA=="
},
"npm-bundled": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/npm-bundled/-/npm-bundled-1.1.1.tgz",
"integrity": "sha512-gqkfgGePhTpAEgUsGEgcq1rqPXA+tv/aVBlgEzfXwA1yiUJF7xtEt3CtVwOjNYQOVknDk0F20w58Fnm3EtG0fA==",
"requires": {
"npm-normalize-package-bin": "^1.0.1"
}
},
"npm-normalize-package-bin": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/npm-normalize-package-bin/-/npm-normalize-package-bin-1.0.1.tgz",
"integrity": "sha512-EPfafl6JL5/rU+ot6P3gRSCpPDW5VmIzX959Ob1+ySFUuuYHWHekXpwdUZcKP5C+DS4GEtdJluwBjnsNDl+fSA=="
},
"npm-packlist": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/npm-packlist/-/npm-packlist-1.4.8.tgz",
"integrity": "sha512-5+AZgwru5IevF5ZdnFglB5wNlHG1AOOuw28WhUq8/8emhBmLv6jX5by4WJCh7lW0uSYZYS6DXqIsyZVIXRZU9A==",
"requires": {
"ignore-walk": "^3.0.1",
"npm-bundled": "^1.0.1",
"npm-normalize-package-bin": "^1.0.1"
}
},
"npm-run-path": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-2.0.2.tgz",
@@ -748,6 +1166,27 @@
"path-key": "^2.0.0"
}
},
"npmlog": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/npmlog/-/npmlog-4.1.2.tgz",
"integrity": "sha512-2uUqazuKlTaSI/dC8AzicUck7+IrEaOnN/e0jd3Xtt1KcGpwx30v50mL7oPyr/h9bL3E4aZccVwpwP+5W9Vjkg==",
"requires": {
"are-we-there-yet": "~1.1.2",
"console-control-strings": "~1.1.0",
"gauge": "~2.7.3",
"set-blocking": "~2.0.0"
}
},
"number-is-nan": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz",
"integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0="
},
"object-assign": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz",
"integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM="
},
"on-finished": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz",
@@ -756,6 +1195,38 @@
"ee-first": "1.1.1"
}
},
"on-headers": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz",
"integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA=="
},
"once": {
"version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
"integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
"requires": {
"wrappy": "1"
}
},
"os-homedir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz",
"integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M="
},
"os-tmpdir": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz",
"integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ="
},
"osenv": {
"version": "0.1.5",
"resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz",
"integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==",
"requires": {
"os-homedir": "^1.0.0",
"os-tmpdir": "^1.0.0"
}
},
"p-finally": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz",
@@ -777,6 +1248,33 @@
"resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz",
"integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ=="
},
"passport": {
"version": "0.4.1",
"resolved": "https://registry.npmjs.org/passport/-/passport-0.4.1.tgz",
"integrity": "sha512-IxXgZZs8d7uFSt3eqNjM9NQ3g3uQCW5avD8mRNoXV99Yig50vjuaez6dQK2qC0kVWPRTujxY0dWgGfT09adjYg==",
"requires": {
"passport-strategy": "1.x.x",
"pause": "0.0.1"
}
},
"passport-local": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-local/-/passport-local-1.0.0.tgz",
"integrity": "sha1-H+YyaMkudWBmJkN+O5BmYsFbpu4=",
"requires": {
"passport-strategy": "1.x.x"
}
},
"passport-strategy": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/passport-strategy/-/passport-strategy-1.0.0.tgz",
"integrity": "sha1-tVOaqPwiWj0a0XlHbd8ja0QPUuQ="
},
"path-is-absolute": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
"integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
},
"path-is-inside": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/path-is-inside/-/path-is-inside-1.0.2.tgz",
@@ -792,6 +1290,11 @@
"resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz",
"integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w="
},
"pause": {
"version": "0.0.1",
"resolved": "https://registry.npmjs.org/pause/-/pause-0.0.1.tgz",
"integrity": "sha1-HUCLP9t2kjuVQ9lvtMnf1TXZy10="
},
"picomatch": {
"version": "2.2.1",
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.2.1.tgz",
@@ -836,6 +1339,11 @@
"resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz",
"integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ=="
},
"random-bytes": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/random-bytes/-/random-bytes-1.0.0.tgz",
"integrity": "sha1-T2ih3Arli9P7lYSMMDJNt11kNgs="
},
"range-parser": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz",
@@ -902,6 +1410,14 @@
"rc": "^1.0.1"
}
},
"rimraf": {
"version": "2.7.1",
"resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz",
"integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==",
"requires": {
"glob": "^7.1.3"
}
},
"safe-buffer": {
"version": "5.1.2",
"resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
@@ -912,6 +1428,11 @@
"resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz",
"integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
},
"sax": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/sax/-/sax-1.2.4.tgz",
"integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw=="
},
"semver": {
"version": "5.7.1",
"resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -963,6 +1484,11 @@
"send": "0.17.1"
}
},
"set-blocking": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz",
"integrity": "sha1-BF+XgtARrppoA93TgrJDkrPYkPc="
},
"setprototypeof": {
"version": "1.1.1",
"resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz",
@@ -1039,6 +1565,27 @@
"has-flag": "^3.0.0"
}
},
"tar": {
"version": "4.4.13",
"resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz",
"integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==",
"requires": {
"chownr": "^1.1.1",
"fs-minipass": "^1.2.5",
"minipass": "^2.8.6",
"minizlib": "^1.2.1",
"mkdirp": "^0.5.0",
"safe-buffer": "^5.1.2",
"yallist": "^3.0.3"
},
"dependencies": {
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g=="
}
}
},
"term-size": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/term-size/-/term-size-1.2.0.tgz",
@@ -1082,6 +1629,14 @@
"mime-types": "~2.1.24"
}
},
"uid-safe": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/uid-safe/-/uid-safe-2.1.5.tgz",
"integrity": "sha512-KPHm4VL5dDXKz01UuEd88Df+KzynaohSL9fBh096KWAxSKZQDI2uBrVqtvRM4rwrIrRRKsdLNML/lnaaVSRioA==",
"requires": {
"random-bytes": "~1.0.0"
}
},
"undefsafe": {
"version": "2.0.3",
"resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.3.tgz",
@@ -1090,6 +1645,11 @@
"debug": "^2.2.0"
}
},
"underscore": {
"version": "1.9.2",
"resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.2.tgz",
"integrity": "sha512-D39qtimx0c1fI3ya1Lnhk3E9nONswSKhnffBI0gME9C99fYOkNi04xs8K6pePLhvl1frbDemkaBQ5ikWllR2HQ=="
},
"unique-string": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/unique-string/-/unique-string-1.0.0.tgz",
@@ -1156,6 +1716,14 @@
"isexe": "^2.0.0"
}
},
"wide-align": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz",
"integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==",
"requires": {
"string-width": "^1.0.2 || 2"
}
},
"widest-line": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/widest-line/-/widest-line-2.0.1.tgz",
@@ -1164,6 +1732,11 @@
"string-width": "^2.1.1"
}
},
"wrappy": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
"integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
},
"write-file-atomic": {
"version": "2.4.3",
"resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-2.4.3.tgz",

View File

@@ -5,16 +5,26 @@
"main": "index.js",
"scripts": {
"start": "node index.js",
"debug": "nodemon index.js",
"debug": "cross-env NODE_ENV=debug nodemon index.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Oliver Boehlk",
"license": "GPL-3.0",
"dependencies": {
"bcrypt": "^4.0.1",
"body-parser": "^1.19.0",
"cors": "^2.8.5",
"express": "^4.17.1",
"express-mysql-session": "^2.1.4",
"express-session": "^1.17.1",
"http-status": "^1.4.2",
"mysql": "^2.18.1",
"node-fetch": "^2.6.0",
"nodemon": "^2.0.2"
"nodemon": "^2.0.2",
"passport": "^0.4.1",
"passport-local": "^1.0.0"
},
"devDependencies": {
"cross-env": "^7.0.2"
}
}

View File

@@ -1292,6 +1292,18 @@
"@babel/runtime": "^7.4.4"
}
},
"@material-ui/lab": {
"version": "4.0.0-alpha.51",
"resolved": "https://registry.npmjs.org/@material-ui/lab/-/lab-4.0.0-alpha.51.tgz",
"integrity": "sha512-X/qv/sZQGhXhKDn83L94gNahGDQj2Rd6r7/9tPpQbSn2A1LAt1+jlTiWD1HUgDXZEPqTsJMajOjWSEmTL7/q7w==",
"requires": {
"@babel/runtime": "^7.4.4",
"@material-ui/utils": "^4.9.6",
"clsx": "^1.0.4",
"prop-types": "^15.7.2",
"react-is": "^16.8.0"
}
},
"@material-ui/styles": {
"version": "4.9.10",
"resolved": "https://registry.npmjs.org/@material-ui/styles/-/styles-4.9.10.tgz",

View File

@@ -1,41 +1,42 @@
{
"name": "simcompanies-dashboard",
"version": "0.1.0",
"homepage": "https://projects.oliver.boehlk.io/simcompanies/",
"private": true,
"dependencies": {
"@material-ui/core": "^4.9.8",
"@material-ui/icons": "^4.9.1",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.0",
"typeface-roboto": "0.0.75",
"recharts": "^1.8.5"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"proxy": "http://127.0.0.1:3001"
"name": "simcompanies-dashboard",
"version": "0.1.0",
"homepage": "https://projects.oliver.boehlk.io/simcompanies/",
"private": true,
"dependencies": {
"@material-ui/core": "^4.9.8",
"@material-ui/icons": "^4.9.1",
"@material-ui/lab": "^4.0.0-alpha.51",
"@testing-library/jest-dom": "^4.2.4",
"@testing-library/react": "^9.4.0",
"@testing-library/user-event": "^7.2.1",
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-router-dom": "^5.1.2",
"react-scripts": "3.4.0",
"recharts": "^1.8.5",
"typeface-roboto": "0.0.75"
},
"scripts": {
"start": "react-scripts start",
"build": "react-scripts build",
"test": "react-scripts test",
"eject": "react-scripts eject"
},
"eslintConfig": {
"extends": "react-app"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
},
"proxy": "http://127.0.0.1:3001"
}

View File

@@ -9,3 +9,12 @@
margin-left: 10px;
height: 64px;
}
.loginBackground {
background-position: center;
background-repeat: no-repeat;
background-size: cover;
margin: auto;
min-height: 100vh;
background-image: url(./img/loginBackground.png);
}

View File

@@ -4,12 +4,53 @@ import {
} from "react-router-dom";
import Navbar from "./components/navigation";
import { createMuiTheme, ThemeProvider } from '@material-ui/core/styles';
import {
Route, Switch
} from "react-router-dom";
import Login from './components/login/login';
import CreateAccount from './components/login/createAccount';
import "./App.css";
import { Redirect } from "react-router-dom";
class ProtectedRoute extends Component {
constructor(props) {
super(props);
this.state = { auth: null };
}
async componentWillMount() {
const url = "/simcompanies/API/testlogin";
const response = await fetch(url);
if (response.status == "200") {
this.setState({
auth: true
});
} else {
this.setState({
auth: false
});
}
}
render() {
const { component: Component, ...props } = this.props;
return (
this.state.auth === null ?
"Loading..." :
<Route
{...props}
render={props => (
this.state.auth ?
<Component {...props} /> :
<Redirect to='/login' />
)}
/>
)
}
}
class App extends Component {
render() {
const theme = createMuiTheme({
palette: {
@@ -24,11 +65,16 @@ class App extends Component {
}
})
//change basename in browserrouter if uri changes
return (
<ThemeProvider theme={theme}>
<BrowserRouter basename="/simcompanies">
<Navbar></Navbar>
<Switch>
<Route exact path="/login" component={Login} />
<Route exact path="/login/createuser" component={CreateAccount} />
<ProtectedRoute path="/*" component={Navbar} />
</Switch>
</BrowserRouter>
</ThemeProvider>

View File

@@ -7,6 +7,7 @@ import {
import SelectResource from './selectResource';
import ResourceChart from './resourcechart/resourcechart';
const useStyles = makeStyles(theme => ({
root: {
flexGrow: 1,
@@ -23,7 +24,7 @@ export default function Content() {
<div className={classes.root}>
<Route exact path="/" component={Overview} />
<Route path="/resources" component={SelectResource} />
<Route path="/resourcechart/:id" render={() => <ResourceChart day={new Date().toUTCString()}></ResourceChart>} />
<Route path="/resourcechart/:id" render={() => <ResourceChart />} />
</div>
);

View File

@@ -0,0 +1,187 @@
import React from 'react';
import { Link } from "react-router-dom";
import { makeStyles, withStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import Button from '@material-ui/core/Button';
import Logo from "../../img/logo.png";
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import { Redirect } from "react-router-dom";
import LoginFeedback from './loginfeedback';
const url = "/simcompanies/API/user/create"
const useStyles = makeStyles(theme => ({
card: {
backgroundColor: "#0B1929",
display: "flex"
},
logo: {
maxWidth: "50%",
height: "auto"
},
input: {
color: "#9daac1"
},
grid: {
display: "flex"
}
}));
const CssTextField = withStyles({
root: {
'& label.Mui-focused': {
color: '#FFC800',
},
'&:hover label': {
color: '#FFC800',
},
'& label': {
color: 'white',
},
'& .MuiOutlinedInput-root': {
'& input': {
color: "white"
},
'& fieldset': {
borderColor: 'white',
color: "white"
},
'&:hover fieldset': {
borderColor: "#FFC800",
},
'&.Mui-focused fieldset': {
borderColor: '#FFC800',
},
},
},
})(TextField);
export default function CreateAccount() {
const classes = useStyles();
const [username, setUsername] = React.useState("");
const [password, setPassword] = React.useState("");
const [login, setLogin] = React.useState(true);
const [status, setStatus] = React.useState(null);
const [statusText, setStatusText] = React.useState(null);
const [disabled, setDisabled] = React.useState(false);
const putCreateAccount = async (url, data) => {
const response = await fetch(url, {
method: 'PUT',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: JSON.stringify(data)
});
const text = await response.text()
await setStatusText(text)
setStatus(response.status)
if (response.status == "200") {
setTimeout(() => { setLogin(false) }, 2000)
}
setDisabled(false)
setTimeout(() => { setStatus(null) }, 2000)
}
const handleClick = (e) => {
if (username !== "" && password !== "") {
setDisabled(true)
putCreateAccount(url, { email: username, password: password })
setUsername("")
setPassword("")
}
}
const handleKeyDown = (e) => {
if (e.key === "Enter") {
if (username !== "" && password !== "") {
setDisabled(true)
putCreateAccount(url, { email: username, password: password })
setUsername("")
setPassword("")
}
}
}
const handleChangeUSR = (e) => {
setUsername(e.target.value)
}
const handleChangePW = (e) => {
setPassword(e.target.value)
}
return (
login === true ?
<Box className="loginBackground" display="flex" justifyContent="center" alignItems="center" m={1} p={1} >
<Card className={classes.card} raised>
<CardContent>
<Box display="flex" justifyContent="center" alignItems="center" m={1} p={1} >
<img src={Logo} alt="SC Dashboard Logo" className={classes.logo} />
</Box >
<form>
<Box display="flex" justifyContent="center" alignItems="center" m={1} p={1} >
<Grid container spacing={2} >
<Grid item xs={12} >
<Box display="flex" justifyContent="center" >
<Typography variant="h4" color="secondary">
Create new Account
</Typography>
</Box >
</Grid>
<Grid item xs={12} >
<Box display="flex" justifyContent="center" >
<CssTextField
error={status != null && status != "200"}
id="usernameInput"
label="Username"
variant="outlined"
onChange={handleChangeUSR}
value={username}
className={classes.input}
onKeyDown={handleKeyDown}
disabled={disabled}
/>
</Box >
</Grid>
<Grid item xs={12}>
<Box display="flex" justifyContent="center" >
<CssTextField
error={status != null && status != "200"}
id="passwordInput"
type="password"
label="Password"
autoComplete="current-password"
variant="outlined"
value={password}
onChange={handleChangePW}
className={classes.input}
onKeyDown={handleKeyDown}
disabled={disabled}
/>
</Box >
</Grid>
<Grid item xs={12}>
<Box display="flex" justifyContent="center" >
<Button disabled={disabled} variant="contained" color="secondary" onClick={handleClick} >
Create Account
</Button>
</Box >
</Grid>
</Grid>
</Box >
</form>
</CardContent>
</Card>
<LoginFeedback login={status} text={statusText}></LoginFeedback>
</Box > : <Redirect to={"/login"} />
)
}

View File

@@ -0,0 +1,206 @@
import React from 'react';
import { Link } from "react-router-dom";
import { makeStyles, withStyles } from '@material-ui/core/styles';
import Card from '@material-ui/core/Card';
import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import Box from '@material-ui/core/Box';
import Button from '@material-ui/core/Button';
import Logo from "../../img/logo.png";
import TextField from '@material-ui/core/TextField';
import Grid from '@material-ui/core/Grid';
import Link1 from '@material-ui/core/Link';
import { Redirect } from "react-router-dom";
import LoginFeedback from './loginfeedback';
const useStyles = makeStyles(theme => ({
card: {
backgroundColor: "#0B1929",
display: "flex"
},
logo: {
maxWidth: "50%",
height: "auto"
},
input: {
color: "#9daac1"
},
grid: {
display: "flex"
}
}));
const CssTextField = withStyles({
root: {
'& label.Mui-focused': {
color: "#FFC800",
},
'&:hover label': {
color: '#FFC800',
},
'& label': {
color: 'white',
},
'& .MuiOutlinedInput-root': {
'& input': {
color: "white"
},
'& fieldset': {
borderColor: 'white',
color: "white"
},
'&:hover fieldset': {
borderColor: "#FFC800",
},
'&.Mui-focused fieldset': {
borderColor: "#FFC800",
},
},
},
})(TextField);
export default function Login() {
const classes = useStyles();
const url = "/simcompanies/API/user/login"
const [username, setUsername] = React.useState("");
const [password, setPassword] = React.useState("");
const [login, setLogin] = React.useState(true);
const [status, setStatus] = React.useState(null);
const [disabled, setDisabled] = React.useState(false);
const [statusText, setStatusText] = React.useState(null);
const postLogin = async (url, data) => {
const response = await fetch(url, {
method: 'POST',
mode: 'cors',
cache: 'no-cache',
headers: {
'Content-Type': 'application/json'
},
redirect: 'follow',
referrerPolicy: 'no-referrer',
body: JSON.stringify(data)
});
const text = await response.text()
await setStatusText(text)
setStatus(response.status)
if (response.status == "200") {
setTimeout(() => { setLogin(false) }, 2000)
}
setDisabled(false)
setTimeout(() => { setStatus(null) }, 2000)
}
const handleClick = (e) => {
if (username !== "" && password !== "") {
setDisabled(true)
postLogin(url, { email: username, password: password })
setUsername("")
setPassword("")
}
}
const handleKeyDown = (e) => {
if (e.key === "Enter") {
if (username !== "" && password !== "") {
setDisabled(true)
postLogin(url, { email: username, password: password })
setUsername("")
setPassword("")
}
}
}
const handleChangeUSR = (e) => {
setUsername(e.target.value)
}
const handleChangePW = (e) => {
setPassword(e.target.value)
}
return (
login === true ?
< Box className="loginBackground" display="flex" justifyContent="center" alignItems="center" m={1} p={1} >
<Card className={classes.card} raised>
<CardContent>
<Box display="flex" justifyContent="center" alignItems="center" m={1} p={1} >
<img src={Logo} alt="SC Dashboard Logo" className={classes.logo} />
</Box >
<form>
<Box display="flex" justifyContent="center" alignItems="center" m={1} p={1} >
<Grid container spacing={2} >
<Grid item xs={12} >
<Box display="flex" justifyContent="center" >
<Typography variant="h4" color="secondary">
Login
</Typography>
</Box >
</Grid>
<Grid item xs={12} >
<Box display="flex" justifyContent="center" >
<CssTextField
error={status != null && status != "200"}
id="usernameInput"
label="Username"
variant="outlined"
value={username}
onChange={handleChangeUSR}
className={classes.input}
onKeyDown={handleKeyDown}
disabled={disabled}
/>
</Box >
</Grid>
<Grid item xs={12}>
<Box display="flex" justifyContent="center" >
<CssTextField
error={status != null && status != "200"}
id="passwordInput"
type="password"
label="Password"
autoComplete="current-password"
variant="outlined"
value={password}
onChange={handleChangePW}
className={classes.input}
onKeyDown={handleKeyDown}
disabled={disabled}
/>
</Box >
</Grid>
<Grid item xs={12}>
<Box display="flex" justifyContent="center" >
<Typography variant="subtitle2" >
<Link1 to="/login/createuser" component={Link} color="secondary">
Create new Account
</Link1>
</Typography>
</Box >
</Grid>
<Grid item xs={12}>
<Box display="flex" justifyContent="center" >
<Button disabled={disabled} variant="contained" color="secondary" onClick={handleClick}>
Login
</Button>
</Box >
</Grid>
</Grid>
</Box >
</form>
</CardContent>
</Card>
<LoginFeedback login={status} text={statusText}></LoginFeedback>
</Box > : <Redirect to='/' />
)
}

View File

@@ -0,0 +1,66 @@
import React, { useEffect } from 'react';
import Snackbar from '@material-ui/core/Snackbar';
import MuiAlert from '@material-ui/lab/Alert';
function Alert(props) {
return <MuiAlert elevation={6} variant="filled" {...props} />;
}
export default function LoginFeedback(props) {
const [login, setLogin] = React.useState(null);
const [open, setOpen] = React.useState(true);
const [open1, setOpen1] = React.useState(true);
const [output, setOutput] = React.useState(null);
const [change, setChange] = React.useState(false);
useEffect(() => {
if (login != props.login) {
setChange(true)
setOpen(true)
setOpen1(true)
setLogin(props.login)
}
}, [login, setLogin, props.login]);
const handleClose = (event, reason) => {
setOpen(false);
};
const handleClose1 = (event, reason) => {
setOpen1(false);
};
const snack = (login) => {
if (login == "200") {
setOutput(
<Snackbar open={open} autoHideDuration={2000} onClose={handleClose}>
<Alert onClose={handleClose} severity="success">
{props.text}
</Alert>
</Snackbar>)
} else if (login != null) {
setOutput(
<Snackbar open={open1} autoHideDuration={3000} onClose={handleClose1}>
<Alert onClose={handleClose1} severity="error">
{props.text}
</Alert>
</Snackbar>
)
} else {
setOutput(null)
}
setChange(false)
}
if (change == true) {
snack(login)
}
return (
<React.Fragment>
{output}
</React.Fragment>
)
}

View File

@@ -11,11 +11,15 @@ import CardContent from '@material-ui/core/CardContent';
import Typography from '@material-ui/core/Typography';
import { makeStyles } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import Button from '@material-ui/core/Button';
const useStyles = makeStyles((theme) => ({
root: {
display: 'flex',
},
card: {
padding: 20
}
}));
const output = (data) => {
@@ -38,7 +42,14 @@ const output = (data) => {
<YAxis domain={['datamin', 'datamax']} />
<Tooltip />
<Legend />
<Line type="monotone" dataKey="price" stroke="#8884d8" dot={false} />
<Line type="monotone" dataKey="q0" stroke="#8884d8" dot={false} />
<Line type="monotone" dataKey="q1" stroke="#000000" dot={false} />
<Line type="monotone" dataKey="q2" stroke="#82ca9d" dot={false} />
<Line type="monotone" dataKey="q3" stroke="#ff0066" dot={false} />
<Line type="monotone" dataKey="q4" stroke="#660066" dot={false} />
<Line type="monotone" dataKey="q5" stroke="#99cc00" dot={false} />
<Line type="monotone" dataKey="q6" stroke="#669999" dot={false} />
<Line type="monotone" dataKey="q7" stroke="#996633" dot={false} />
</LineChart>
)
}
@@ -49,20 +60,60 @@ export default function ResourceChart(props) {
const [data, setData] = React.useState(null);
let { id } = useParams();
const loadData = async () => {
let nextData = await fetch(`/simcompanies/API/day?date=${props["day"]}&kind=${id}`);
nextData = await nextData.json();
for (let i = 0; i < nextData.length; i++) {
nextData[i]["time"] = new Date(nextData[i]["time"]);
nextData[i]["time"] = nextData[i]["time"].toLocaleTimeString();
let dayData = await fetch(`/simcompanies/API/day?kind=${id}`);
let dataWithDate = await dayData.json();
let qualitySortedData = [];
for (let i = 0; i < dataWithDate.length; i++) {
if (i === 0) qualitySortedData.push(dataWithDate[i]);
else {
if (dataWithDate[i]["time"] === qualitySortedData[qualitySortedData.length - 1]["time"]) {
switch (dataWithDate[i].quality) {
case 0: break;
case 1:
qualitySortedData[qualitySortedData.length - 1]["q1"] = dataWithDate[i]["price"];
break;
case 2:
qualitySortedData[qualitySortedData.length - 1]["q2"] = dataWithDate[i]["price"];
break;
case 3:
qualitySortedData[qualitySortedData.length - 1]["q3"] = dataWithDate[i]["price"];
break;
case 4:
qualitySortedData[qualitySortedData.length - 1]["q4"] = dataWithDate[i]["price"];
break;
case 5:
qualitySortedData[qualitySortedData.length - 1]["q5"] = dataWithDate[i]["price"];
break;
case 6:
qualitySortedData[qualitySortedData.length - 1]["q6"] = dataWithDate[i]["price"];
break;
case 7:
qualitySortedData[qualitySortedData.length - 1]["q7"] = dataWithDate[i]["price"];
break;
default:
break;
}
} else {
qualitySortedData.push(dataWithDate[i]);
qualitySortedData[qualitySortedData.length - 1]["q0"] = qualitySortedData[qualitySortedData.length - 1]["price"];
}
}
}
setData(nextData)
for (let i = 0; i < qualitySortedData.length; i++) {
qualitySortedData[i]["time"] = new Date(qualitySortedData[i]["time"]);
qualitySortedData[i]["time"] = qualitySortedData[i]["time"].toLocaleTimeString();
}
console.log(qualitySortedData);
setData(qualitySortedData)
}
if (data === null) {
loadData();
}
const handleClick = (e) => {
loadData()
}
return (
<div className={classes.root}>
@@ -73,16 +124,17 @@ export default function ResourceChart(props) {
</Typography>
</Grid>
<Grid item xs={12}>
<Card>
<Card className={classes.card}>
<CardContent>
{output(data)}
</CardContent>
<Button variant="contained" color="secondary" onClick={handleClick}>
Refresh
</Button>
</Card>
</Grid>
</Grid>
</div>
);
}

View File

@@ -2,37 +2,207 @@ import React from 'react';
import { makeStyles } from '@material-ui/core/styles';
import Grid from '@material-ui/core/Grid';
import ResourceCard from './resourceCard';
import Typography from '@material-ui/core/Typography';
const useStyles = makeStyles(theme => ({
card: {
grid: {
display: "flex"
}
}));
const agri = [66, 3, 4, 5, 6, 7, 8, 9, 40, 72, 106]
const construct = [101, 102, 103, 104, 105, 107, 108, 109, 110, 111]
const fashion = [41, 46, 60, 61, 62, 63, 64, 65, 70, 71]
const energy = [10, 11, 12, 1, 73, 74, 83]
const electronics = [20, 21, 22, 23, 24, 25, 26, 27, 28, 79, 98]
const auto = [47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 112]
const aero = [77, 78, 80, 81, 82, 84, 85, 86, 87, 88, 89]
const resource = [2, 13, 14, 15, 16, 17, 18, 19, 42, 43, 44, 45, 68, 69, 75, 76]
const research = [29, 30, 31, 32, 33, 34, 35, 58, 59, 100, 113]
export default function SelectResource() {
const classes = useStyles();
const [resources, setResources] = React.useState(null);
const [resources, setResources] = React.useState([]);
const loadResources = async () => {
var resourceJSON = await fetch("/simcompanies/API/resourcelist");
resourceJSON = await resourceJSON.json();
let rArr = [];
let rArr = [[], [], [], [], [], [], [], [], [], []];
for (let i = 0; i < resourceJSON.length; i++) {
rArr.push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
if (agri.includes(resourceJSON[i]["db_letter"])) {
rArr[0].push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
} else if (construct.includes(resourceJSON[i]["db_letter"])) {
rArr[1].push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
} else if (fashion.includes(resourceJSON[i]["db_letter"])) {
rArr[2].push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
} else if (energy.includes(resourceJSON[i]["db_letter"])) {
rArr[3].push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
} else if (electronics.includes(resourceJSON[i]["db_letter"])) {
rArr[4].push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
} else if (auto.includes(resourceJSON[i]["db_letter"])) {
rArr[5].push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
} else if (aero.includes(resourceJSON[i]["db_letter"])) {
rArr[6].push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
} else if (resource.includes(resourceJSON[i]["db_letter"])) {
rArr[7].push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
} else if (research.includes(resourceJSON[i]["db_letter"])) {
rArr[8].push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
} else {
rArr[9].push(<ResourceCard resource={resourceJSON[i]}></ResourceCard>);
}
}
setResources(rArr);
}
if (resources === null)
if (resources.length === 0)
loadResources();
return (
<Grid container spacing={2}>
{resources}
<Grid container spacing={2} >
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={2} >
<Grid item xs={12}>
<Typography variant="h4">
Agriculture & Food
</Typography>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={1} >
{resources[0]}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={2} >
<Grid item xs={12}>
<Typography variant="h4">
Construction
</Typography>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={1} >
{resources[1]}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={2} >
<Grid item xs={12}>
<Typography variant="h4">
Fashion
</Typography>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={1} >
{resources[2]}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={2} >
<Grid item xs={12}>
<Typography variant="h4">
Energy
</Typography>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={1} >
{resources[3]}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={2} >
<Grid item xs={12}>
<Typography variant="h4">
Electronics
</Typography>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={1} >
{resources[4]}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={2} >
<Grid item xs={12}>
<Typography variant="h4">
Automotive
</Typography>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={1} >
{resources[5]}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={2} >
<Grid item xs={12}>
<Typography variant="h4">
Aerospace
</Typography>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={1} >
{resources[6]}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={2} >
<Grid item xs={12}>
<Typography variant="h4">
Resources
</Typography>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={1} >
{resources[7]}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={2} >
<Grid item xs={12}>
<Typography variant="h4">
Research
</Typography>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={1} >
{resources[8]}
</Grid>
</Grid>
</Grid>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={2} >
<Grid item xs={12}>
<Typography variant="h4">
Others
</Typography>
</Grid>
<Grid item xs={12} className={classes.grid}>
<Grid container spacing={1} >
{resources[9]}
</Grid>
</Grid>
</Grid>
</Grid>
</Grid>
);

View File

@@ -8,7 +8,7 @@ import { Link } from 'react-router-dom';
import Link1 from '@material-ui/core/Link';
import ListItem from '@material-ui/core/ListItem';
import ListItemText from '@material-ui/core/ListItemText';
import CardMedia from '@material-ui/core/CardMedia';
import ListItemIcon from '@material-ui/core/ListItemIcon';
const useStyles = makeStyles(theme => ({
@@ -46,7 +46,10 @@ export default function ResourceCard(props) {
<Card >
<CardContent style={{ padding: 0 }}>
<ListItemLink to={"/resourcechart/" + props.resource["db_letter"]} component={Link}>
<img src={picture} alt={props.resource["name"]} style={{ width: 50, height: 50 }} />
<ListItemIcon>
<img src={picture} alt={props.resource["name"]} style={{ width: 50, height: 50 }} />
</ListItemIcon>
<ListItemText primary={props.resource["name"]} />
</ListItemLink>
</CardContent>

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB