Redesign components in frontend (DRY)
This commit is contained in:
parent
cfb3290194
commit
09182ee58d
|
@ -83,7 +83,7 @@ def books_per_genre_per_month(request):
|
||||||
|
|
||||||
# Filter array on genre and date
|
# Filter array on genre and date
|
||||||
booksPerMonth = df.groupby(['genre','readed'])['genre'].count().reset_index(name="count")
|
booksPerMonth = df.groupby(['genre','readed'])['genre'].count().reset_index(name="count")
|
||||||
booksPerMonth = booksPerMonth.sort_values(by=['readed', 'count'], ascending=False)
|
booksPerMonth = booksPerMonth.sort_values(by=['genre', 'readed', 'count'], ascending=False)
|
||||||
|
|
||||||
for index, row in booksPerMonth.iterrows():
|
for index, row in booksPerMonth.iterrows():
|
||||||
data.append({
|
data.append({
|
||||||
|
|
|
@ -1266,6 +1266,11 @@
|
||||||
"@emotion/utils": "^1.2.0"
|
"@emotion/utils": "^1.2.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"@emotion/stylis": {
|
||||||
|
"version": "0.8.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/stylis/-/stylis-0.8.5.tgz",
|
||||||
|
"integrity": "sha512-h6KtPihKFn3T9fuIrwvXXUOwlx3rfUvfZIcP5a6rh8Y7zjE3O06hT5Ss4S/YI1AYhuZ1kjaE/5EaOOI2NqSylQ=="
|
||||||
|
},
|
||||||
"@emotion/unitless": {
|
"@emotion/unitless": {
|
||||||
"version": "0.8.0",
|
"version": "0.8.0",
|
||||||
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz",
|
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.0.tgz",
|
||||||
|
@ -1826,6 +1831,23 @@
|
||||||
"@babel/helper-define-polyfill-provider": "^0.3.2"
|
"@babel/helper-define-polyfill-provider": "^0.3.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"babel-plugin-styled-components": {
|
||||||
|
"version": "2.0.7",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-plugin-styled-components/-/babel-plugin-styled-components-2.0.7.tgz",
|
||||||
|
"integrity": "sha512-i7YhvPgVqRKfoQ66toiZ06jPNA3p6ierpfUuEWxNF+fV27Uv5gxBkf8KZLHUCc1nFA9j6+80pYoIpqCeyW3/bA==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-annotate-as-pure": "^7.16.0",
|
||||||
|
"@babel/helper-module-imports": "^7.16.0",
|
||||||
|
"babel-plugin-syntax-jsx": "^6.18.0",
|
||||||
|
"lodash": "^4.17.11",
|
||||||
|
"picomatch": "^2.3.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"babel-plugin-syntax-jsx": {
|
||||||
|
"version": "6.18.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/babel-plugin-syntax-jsx/-/babel-plugin-syntax-jsx-6.18.0.tgz",
|
||||||
|
"integrity": "sha512-qrPaCSo9c8RHNRHIotaufGbuOBN8rtdC4QrrFFc43vyWCCz7Kl7GL1PGaXtMGQZUXrkCjNEgxDfmAuAabr/rlw=="
|
||||||
|
},
|
||||||
"big.js": {
|
"big.js": {
|
||||||
"version": "5.2.2",
|
"version": "5.2.2",
|
||||||
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
"resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz",
|
||||||
|
@ -1865,6 +1887,11 @@
|
||||||
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
|
||||||
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
|
"integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ=="
|
||||||
},
|
},
|
||||||
|
"camelize": {
|
||||||
|
"version": "1.0.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/camelize/-/camelize-1.0.1.tgz",
|
||||||
|
"integrity": "sha512-dU+Tx2fsypxTgtLoE36npi3UqcjSSMNYfkqgmoEhtZrraP5VWq0K7FkWVTYa8eMPtnU/G2txVsfdCJTn9uzpuQ=="
|
||||||
|
},
|
||||||
"caniuse-lite": {
|
"caniuse-lite": {
|
||||||
"version": "1.0.30001390",
|
"version": "1.0.30001390",
|
||||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001390.tgz",
|
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001390.tgz",
|
||||||
|
@ -2039,6 +2066,21 @@
|
||||||
"which": "^2.0.1"
|
"which": "^2.0.1"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"css-color-keywords": {
|
||||||
|
"version": "1.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-color-keywords/-/css-color-keywords-1.0.0.tgz",
|
||||||
|
"integrity": "sha512-FyyrDHZKEjXDpNJYvVsV960FiqQyXc/LlYmsxl2BcdMb2WPx0OGRVgTg55rPSyLSNMqP52R9r8geSp7apN3Ofg=="
|
||||||
|
},
|
||||||
|
"css-to-react-native": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/css-to-react-native/-/css-to-react-native-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-Ro1yETZA813eoyUp2GDBhG2j+YggidUmzO1/v9eYBKR2EHVEniE2MI/NqpTQ954BMpTPZFsGNPm46qFB9dpaPQ==",
|
||||||
|
"requires": {
|
||||||
|
"camelize": "^1.0.0",
|
||||||
|
"css-color-keywords": "^1.0.0",
|
||||||
|
"postcss-value-parser": "^4.0.2"
|
||||||
|
}
|
||||||
|
},
|
||||||
"csstype": {
|
"csstype": {
|
||||||
"version": "3.1.0",
|
"version": "3.1.0",
|
||||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
||||||
|
@ -2460,6 +2502,11 @@
|
||||||
"p-locate": "^4.1.0"
|
"p-locate": "^4.1.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"lodash": {
|
||||||
|
"version": "4.17.21",
|
||||||
|
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||||
|
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
|
||||||
|
},
|
||||||
"lodash.debounce": {
|
"lodash.debounce": {
|
||||||
"version": "4.0.8",
|
"version": "4.0.8",
|
||||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||||
|
@ -2620,6 +2667,11 @@
|
||||||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||||
"dev": true
|
"dev": true
|
||||||
},
|
},
|
||||||
|
"picomatch": {
|
||||||
|
"version": "2.3.1",
|
||||||
|
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||||
|
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
||||||
|
},
|
||||||
"pkg-dir": {
|
"pkg-dir": {
|
||||||
"version": "4.2.0",
|
"version": "4.2.0",
|
||||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||||
|
@ -2629,6 +2681,11 @@
|
||||||
"find-up": "^4.0.0"
|
"find-up": "^4.0.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"postcss-value-parser": {
|
||||||
|
"version": "4.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/postcss-value-parser/-/postcss-value-parser-4.2.0.tgz",
|
||||||
|
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ=="
|
||||||
|
},
|
||||||
"prop-types": {
|
"prop-types": {
|
||||||
"version": "15.8.1",
|
"version": "15.8.1",
|
||||||
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
|
||||||
|
@ -2678,6 +2735,23 @@
|
||||||
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
|
||||||
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
|
||||||
},
|
},
|
||||||
|
"react-loader-spinner": {
|
||||||
|
"version": "5.3.4",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-loader-spinner/-/react-loader-spinner-5.3.4.tgz",
|
||||||
|
"integrity": "sha512-G2vw4ssX+RDZ/vfaeva06yfNqyFViv/u+tVZ3kFLy5TKNlNx2DbuwreBSpRtPespQA+VxinxUJsigwLwG9erOg==",
|
||||||
|
"requires": {
|
||||||
|
"react-is": "^18.2.0",
|
||||||
|
"styled-components": "^5.3.5",
|
||||||
|
"styled-tools": "^1.7.2"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"react-is": {
|
||||||
|
"version": "18.2.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz",
|
||||||
|
"integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w=="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
"react-router": {
|
"react-router": {
|
||||||
"version": "6.3.0",
|
"version": "6.3.0",
|
||||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz",
|
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz",
|
||||||
|
@ -2856,6 +2930,11 @@
|
||||||
"kind-of": "^6.0.2"
|
"kind-of": "^6.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"shallowequal": {
|
||||||
|
"version": "1.1.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/shallowequal/-/shallowequal-1.1.0.tgz",
|
||||||
|
"integrity": "sha512-y0m1JoUZSlPAjXVtPPW70aZWfIL/dSP7AFkRnniLCrK/8MDKog3TySTBmckD+RObVxH0v4Tox67+F14PdED2oQ=="
|
||||||
|
},
|
||||||
"shebang-command": {
|
"shebang-command": {
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||||
|
@ -2887,6 +2966,48 @@
|
||||||
"source-map": "^0.6.0"
|
"source-map": "^0.6.0"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"styled-components": {
|
||||||
|
"version": "5.3.6",
|
||||||
|
"resolved": "https://registry.npmjs.org/styled-components/-/styled-components-5.3.6.tgz",
|
||||||
|
"integrity": "sha512-hGTZquGAaTqhGWldX7hhfzjnIYBZ0IXQXkCYdvF1Sq3DsUaLx6+NTHC5Jj1ooM2F68sBiVz3lvhfwQs/S3l6qg==",
|
||||||
|
"requires": {
|
||||||
|
"@babel/helper-module-imports": "^7.0.0",
|
||||||
|
"@babel/traverse": "^7.4.5",
|
||||||
|
"@emotion/is-prop-valid": "^1.1.0",
|
||||||
|
"@emotion/stylis": "^0.8.4",
|
||||||
|
"@emotion/unitless": "^0.7.4",
|
||||||
|
"babel-plugin-styled-components": ">= 1.12.0",
|
||||||
|
"css-to-react-native": "^3.0.0",
|
||||||
|
"hoist-non-react-statics": "^3.0.0",
|
||||||
|
"shallowequal": "^1.1.0",
|
||||||
|
"supports-color": "^5.5.0"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@emotion/unitless": {
|
||||||
|
"version": "0.7.5",
|
||||||
|
"resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.7.5.tgz",
|
||||||
|
"integrity": "sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg=="
|
||||||
|
},
|
||||||
|
"has-flag": {
|
||||||
|
"version": "3.0.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
|
||||||
|
"integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw=="
|
||||||
|
},
|
||||||
|
"supports-color": {
|
||||||
|
"version": "5.5.0",
|
||||||
|
"resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
|
||||||
|
"integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
|
||||||
|
"requires": {
|
||||||
|
"has-flag": "^3.0.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"styled-tools": {
|
||||||
|
"version": "1.7.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/styled-tools/-/styled-tools-1.7.2.tgz",
|
||||||
|
"integrity": "sha512-IjLxzM20RMwAsx8M1QoRlCG/Kmq8lKzCGyospjtSXt/BTIIcvgTonaxQAsKnBrsZNwhpHzO9ADx5te0h76ILVg=="
|
||||||
|
},
|
||||||
"stylis": {
|
"stylis": {
|
||||||
"version": "4.0.13",
|
"version": "4.0.13",
|
||||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
|
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
|
||||||
|
|
|
@ -30,6 +30,7 @@
|
||||||
"datatables.net": "^1.12.1",
|
"datatables.net": "^1.12.1",
|
||||||
"datatables.net-dt": "^1.12.1",
|
"datatables.net-dt": "^1.12.1",
|
||||||
"jquery": "^3.6.1",
|
"jquery": "^3.6.1",
|
||||||
|
"react-loader-spinner": "^5.3.4",
|
||||||
"react-router-dom": "^6.3.0"
|
"react-router-dom": "^6.3.0"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,6 +1,12 @@
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
import Challenge from "./components/Challenge";
|
import Challenge from "./components/Challenge";
|
||||||
import BookStats from "./components/Stats";
|
import BookStats from "./components/Stats";
|
||||||
|
import { ColorRing } from 'react-loader-spinner'
|
||||||
|
import Countries from "./components/Countries";
|
||||||
|
import Pages from "./components/Pages";
|
||||||
|
import Genres from "./components/Genres";
|
||||||
|
import Books from "./components/Books";
|
||||||
|
import { getReadingYears } from "./components/Data.js";
|
||||||
|
|
||||||
export default class App extends Component {
|
export default class App extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -8,485 +14,33 @@ export default class App extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
year: new Date().getFullYear(),
|
year: new Date().getFullYear(),
|
||||||
readingYears: [],
|
readingYears: [],
|
||||||
countries: [],
|
|
||||||
pagesStats: [],
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.yearsArray = [];
|
|
||||||
}
|
|
||||||
|
|
||||||
getGenres() {
|
|
||||||
fetch('/api/books/genres', {
|
|
||||||
"method": "GET",
|
|
||||||
"headers": {
|
|
||||||
"year": this.state.year
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(books => {
|
|
||||||
this.initChart(books, this.state.year);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getCountries(init) {
|
|
||||||
fetch('/api/books/countries', {
|
|
||||||
"method": "GET",
|
|
||||||
"headers": {
|
|
||||||
"year": this.state.year
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
this.setState({
|
|
||||||
countries: data
|
|
||||||
})
|
|
||||||
|
|
||||||
if (init == true) {
|
|
||||||
$('#DataTable').DataTable({
|
|
||||||
paging: false,
|
|
||||||
ordering: false,
|
|
||||||
info: false,
|
|
||||||
searching: false
|
|
||||||
})
|
|
||||||
}
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
getShortestLongestBook(currentyear) {
|
|
||||||
fetch('/api/books/pages/stats', {
|
|
||||||
"method": "GET",
|
|
||||||
"headers": {
|
|
||||||
"year": currentyear
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(bookstats => {
|
|
||||||
this.setState({
|
|
||||||
pagesStats: bookstats
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
changeYear(event) {
|
changeYear(event) {
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
year: event.target.value
|
year: event.target.value
|
||||||
})
|
})
|
||||||
|
|
||||||
fetch('/api/books/countries', {
|
|
||||||
"method": "GET",
|
|
||||||
"headers": {
|
|
||||||
"year": event.target.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
this.setState({
|
|
||||||
countries: data
|
|
||||||
})
|
|
||||||
|
|
||||||
this.getCountries(false);
|
|
||||||
})
|
|
||||||
|
|
||||||
var $this = this;
|
|
||||||
|
|
||||||
this.getShortestLongestBook(event.target.value);
|
|
||||||
|
|
||||||
fetch('/api/books/genres/count', {
|
|
||||||
"method": "GET",
|
|
||||||
"headers": {
|
|
||||||
"year": event.target.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
this.initDoughnut(data);
|
|
||||||
})
|
|
||||||
|
|
||||||
fetch('/api/books/genres', {
|
|
||||||
"method": "GET",
|
|
||||||
"headers": {
|
|
||||||
"year": event.target.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(books => {
|
|
||||||
this.initChart(books, event.target.value);
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
initHorBar(data) {
|
|
||||||
|
|
||||||
var countries = [];
|
|
||||||
var counts = [];
|
|
||||||
|
|
||||||
data.forEach((count) => {
|
|
||||||
if (!countries.includes(count.country)) {
|
|
||||||
countries.push(count.country)
|
|
||||||
}
|
|
||||||
|
|
||||||
counts.push(count.count)
|
|
||||||
})
|
|
||||||
|
|
||||||
$("canvas#countryChart").remove();
|
|
||||||
$("div.books-per-country").append('<canvas id="countryChart"></canvas>');
|
|
||||||
|
|
||||||
var ctx = document.getElementById("countryChart");
|
|
||||||
new Chart(ctx, {
|
|
||||||
type: 'bar',
|
|
||||||
options: {
|
|
||||||
indexAxis: 'y',
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
display: false
|
|
||||||
},
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
ticks: {
|
|
||||||
beginAtZero: true,
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
stacked: true,
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
ticks: {
|
|
||||||
beginAtZero: true,
|
|
||||||
stepSize: 1,
|
|
||||||
color: "white",
|
|
||||||
},
|
|
||||||
stacked: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
},
|
|
||||||
data: {
|
|
||||||
labels: countries,
|
|
||||||
datasets: [
|
|
||||||
{
|
|
||||||
label: "Boeken",
|
|
||||||
data: counts,
|
|
||||||
backgroundColor: '#696ffc',
|
|
||||||
}]
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
initDoughnut(data) {
|
|
||||||
|
|
||||||
var labels = [];
|
|
||||||
var counts = [];
|
|
||||||
|
|
||||||
data.forEach((count) => {
|
|
||||||
if (!labels.includes(count.genre)) {
|
|
||||||
labels.push(count.genre)
|
|
||||||
}
|
|
||||||
|
|
||||||
counts.push(count.count)
|
|
||||||
})
|
|
||||||
|
|
||||||
const legendMargin = {
|
|
||||||
id: 'legendMargin',
|
|
||||||
beforeInit(chart, legend, options) {
|
|
||||||
const fitValue = chart.legend.fit;
|
|
||||||
|
|
||||||
chart.legend.fit = function fit() {
|
|
||||||
fitValue.bind(chart.legend)();
|
|
||||||
return this.height += 30;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
$("canvas#chartGenres").remove();
|
|
||||||
$("div.genresPercent").append('<canvas id="chartGenres"></canvas>');
|
|
||||||
|
|
||||||
var ctx = document.getElementById("chartGenres");
|
|
||||||
var myChart = new Chart(ctx, {
|
|
||||||
type: 'pie',
|
|
||||||
data: {
|
|
||||||
labels: labels,
|
|
||||||
datasets: [{
|
|
||||||
label: '# of Tomatoes',
|
|
||||||
data: counts,
|
|
||||||
backgroundColor: [
|
|
||||||
'#8066ee', '#58c8d6', '#fe4c62', '#49b8fd', '#ffbe0e'
|
|
||||||
],
|
|
||||||
borderWidth: 0,
|
|
||||||
borderColor: '#1f2940',
|
|
||||||
tooltip: {
|
|
||||||
callbacks: {
|
|
||||||
label: function (context) {
|
|
||||||
let label = context.label;
|
|
||||||
let value = context.formattedValue;
|
|
||||||
|
|
||||||
if (!label)
|
|
||||||
label = 'Unknown'
|
|
||||||
|
|
||||||
let sum = 0;
|
|
||||||
let dataArr = context.chart.data.datasets[0].data;
|
|
||||||
dataArr.map(data => {
|
|
||||||
sum += Number(data);
|
|
||||||
});
|
|
||||||
|
|
||||||
let percentage = (value * 100 / sum).toFixed(1) + '%';
|
|
||||||
return label + ": " + percentage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}]
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
cutout: '80%',
|
|
||||||
responsive: true,
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
position: 'top',
|
|
||||||
labels: {
|
|
||||||
padding: 20,
|
|
||||||
usePointStyle: true,
|
|
||||||
// This more specific font property overrides the global property
|
|
||||||
color: "##101010",
|
|
||||||
font: {
|
|
||||||
size: 14,
|
|
||||||
family: 'Source Sans Pro'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: [{
|
|
||||||
id: 'legendMargin',
|
|
||||||
beforeInit(chart, legend, options) {
|
|
||||||
const fitValue = chart.legend.fit;
|
|
||||||
|
|
||||||
chart.legend.fit = function fit() {
|
|
||||||
fitValue.bind(chart.legend)();
|
|
||||||
return this.height += 30;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, {
|
|
||||||
afterDraw: chart => {
|
|
||||||
var ctx = chart.ctx;
|
|
||||||
ctx.save();
|
|
||||||
var image = new Image();
|
|
||||||
image.src = 'https://www.iconsdb.com/icons/preview/gray/book-xxl.png';
|
|
||||||
var imageSize = 80;
|
|
||||||
ctx.drawImage(image, chart.width / 2 - imageSize / 2, chart.height / 2 - imageSize / 6, imageSize, imageSize);
|
|
||||||
ctx.restore();
|
|
||||||
}
|
|
||||||
}],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
initChart(data, year) {
|
|
||||||
|
|
||||||
/*
|
|
||||||
----------------------------------
|
|
||||||
Books per month per genre
|
|
||||||
----------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
var genres = [];
|
|
||||||
|
|
||||||
var colors = [
|
|
||||||
// '#696ffc', '#7596fa', '#92adfe', '#abc0ff'
|
|
||||||
'#8066ee', '#58c8d6', '#fe4c62', '#49b8fd', '#ffbe0e'
|
|
||||||
]
|
|
||||||
|
|
||||||
var dataSet = [];
|
|
||||||
|
|
||||||
data.forEach(book => {
|
|
||||||
if (!genres.includes(book.genre)) {
|
|
||||||
genres.push(book.genre)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if (genres && genres.length > 0) {
|
|
||||||
genres.forEach((genre, index) => {
|
|
||||||
var genreData = [];
|
|
||||||
|
|
||||||
for (var i = 0; i < 12; i++) {
|
|
||||||
|
|
||||||
genreData[i] = 0;
|
|
||||||
|
|
||||||
if ((i + 1) < 10) {
|
|
||||||
var month = "0" + (i + 1);
|
|
||||||
} else {
|
|
||||||
month = (i + 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var j = 0; j < data.length; j++) {
|
|
||||||
if (data && data[j] && data[j].readed == month + '-' + year) {
|
|
||||||
if (data[j].genre == genre) {
|
|
||||||
genreData[i] = data[j].count;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
dataSet.push({
|
|
||||||
label: genre,
|
|
||||||
data: genreData,
|
|
||||||
backgroundColor: colors[index],
|
|
||||||
order: 2
|
|
||||||
})
|
|
||||||
})
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
----------------------------------
|
|
||||||
Stacked bar chart
|
|
||||||
----------------------------------
|
|
||||||
*/
|
|
||||||
|
|
||||||
$("canvas#chart").remove();
|
|
||||||
$("div.books-per-month").append('<canvas id="chart"></canvas>');
|
|
||||||
|
|
||||||
const legendMargin = {
|
|
||||||
id: 'legendMargin',
|
|
||||||
beforeInit(chart, legend, options) {
|
|
||||||
const fitValue = chart.legend.fit;
|
|
||||||
|
|
||||||
chart.legend.fit = function fit() {
|
|
||||||
fitValue.bind(chart.legend)();
|
|
||||||
return this.height += 30;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
new Chart(document.getElementById('chart'), {
|
|
||||||
type: 'bar',
|
|
||||||
data: {
|
|
||||||
labels: ["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"],
|
|
||||||
datasets: dataSet
|
|
||||||
},
|
|
||||||
options: {
|
|
||||||
maintainAspectRatio: false,
|
|
||||||
responsive: true,
|
|
||||||
showTooltips: true,
|
|
||||||
legend: {
|
|
||||||
display: true,
|
|
||||||
labels: {
|
|
||||||
usePointStyle: true,
|
|
||||||
}
|
|
||||||
},
|
|
||||||
interaction: {
|
|
||||||
mode: 'index'
|
|
||||||
},
|
|
||||||
scales: {
|
|
||||||
x: {
|
|
||||||
ticks: {
|
|
||||||
beginAtZero: true,
|
|
||||||
color: "#101010",
|
|
||||||
fontFamily: "Source Sans Pro",
|
|
||||||
},
|
|
||||||
stacked: true,
|
|
||||||
},
|
|
||||||
y: {
|
|
||||||
ticks: {
|
|
||||||
beginAtZero: true,
|
|
||||||
stepSize: 1,
|
|
||||||
color: "#101010",
|
|
||||||
fontFamily: "Source Sans Pro",
|
|
||||||
},
|
|
||||||
stacked: true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: {
|
|
||||||
legend: {
|
|
||||||
position: 'top',
|
|
||||||
labels: {
|
|
||||||
usePointStyle: true,
|
|
||||||
color: "#101010",
|
|
||||||
padding: 20,
|
|
||||||
font: {
|
|
||||||
size: 14,
|
|
||||||
family: 'Source Sans Pro',
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
tooltips: {
|
|
||||||
bodyFont: 'Source Sans Pro'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
plugins: [legendMargin],
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
||||||
var $this = this;
|
setTimeout(() =>{
|
||||||
|
document.getElementById("loading-overlay").style.display = "none";
|
||||||
|
}, 1000);
|
||||||
|
|
||||||
var currentyear = this.state.year ? this.state.year : new Date().getFullYear();
|
getReadingYears().then(data => {
|
||||||
|
this.setState({
|
||||||
fetch('/api/books/genres', {
|
readingYears: data
|
||||||
"method": "GET",
|
|
||||||
"headers": {
|
|
||||||
"year": currentyear
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(books => {
|
|
||||||
this.initChart(books, currentyear);
|
|
||||||
})
|
})
|
||||||
|
|
||||||
this.getShortestLongestBook(this.state.year);
|
|
||||||
|
|
||||||
fetch('/api/books/genres/count', {
|
|
||||||
"method": "GET",
|
|
||||||
"headers": {
|
|
||||||
"year": this.state.year
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
this.initDoughnut(data);
|
|
||||||
})
|
|
||||||
|
|
||||||
this.getCountries(true);
|
|
||||||
|
|
||||||
fetch('/api/books/years', {
|
|
||||||
"method": "GET",
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
this.setState({
|
|
||||||
readingYears: data
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
var url = window.location.href.split("/");
|
var url = window.location.href.split("/");
|
||||||
var ratingshort = '';
|
|
||||||
var ratinglong = '';
|
|
||||||
|
|
||||||
|
|
||||||
if (this.state.pagesStats.shortestbook) {
|
|
||||||
for (var i = 0; i < this.state.pagesStats.shortestbook.rating; i++) {
|
|
||||||
ratingshort += "<i class='fas fa-star'></i>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.getElementById("shortest_rating") !== null) {
|
|
||||||
document.getElementById('shortest_rating').innerHTML = ratingshort;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.state.pagesStats.longestbook) {
|
|
||||||
for (var i = 0; i < this.state.pagesStats.longestbook.rating; i++) {
|
|
||||||
ratinglong += "<i class='fas fa-star'></i>";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.getElementById("longest_rating") !== null) {
|
|
||||||
document.getElementById('longest_rating').innerHTML = ratinglong;
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|
||||||
|
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<div className="sidebar">
|
<div className="sidebar">
|
||||||
<div className={`menu-item ${url && url[3] == "" ? 'selected' : ''}`}>
|
<div className={`menu-item ${url && url[3] == "" ? 'selected' : ''}`}>
|
||||||
|
@ -499,6 +53,21 @@ export default class App extends Component {
|
||||||
</div>
|
</div>
|
||||||
<div className="content">
|
<div className="content">
|
||||||
|
|
||||||
|
<div className="loading-screen-overlay" id="loading-overlay">
|
||||||
|
<div className="loading-screen">
|
||||||
|
<ColorRing
|
||||||
|
visible={true}
|
||||||
|
height="80"
|
||||||
|
width="80"
|
||||||
|
ariaLabel="blocks-loading"
|
||||||
|
wrapperStyle={{}}
|
||||||
|
wrapperClass="blocks-wrapper"
|
||||||
|
colors={['#8066ee', '#58c8d6', '#fe4c62', '#49b8fd', '#ffbe0e']}
|
||||||
|
/>
|
||||||
|
<span>Data wordt geladen...</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<h1>Dashboard</h1>
|
<h1>Dashboard</h1>
|
||||||
<h2>Leesanalyse van Jordy van Zeeland</h2>
|
<h2>Leesanalyse van Jordy van Zeeland</h2>
|
||||||
|
|
||||||
|
@ -509,16 +78,9 @@ export default class App extends Component {
|
||||||
<div className="stat-block">
|
<div className="stat-block">
|
||||||
<i className="fa fa-calendar"></i>
|
<i className="fa fa-calendar"></i>
|
||||||
<span className="stats-number">
|
<span className="stats-number">
|
||||||
<select className="yearselector" defaultValue={this.state.year} onChange={(event) => this.changeYear(event)}>
|
<select className="yearselector" value={this.state.year} onChange={(event) => this.changeYear(event)}>
|
||||||
{this.state.readingYears.map((year, i) => {
|
{this.state.readingYears.map((year, i) => {
|
||||||
|
return (<option key={i} value={year}>{year}</option>)
|
||||||
if (year === this.state.year) {
|
|
||||||
var selected = 'selected'
|
|
||||||
} else {
|
|
||||||
selected = ''
|
|
||||||
}
|
|
||||||
|
|
||||||
return (<option key={i} selected={selected} value={year}>{year}</option>)
|
|
||||||
})}
|
})}
|
||||||
</select>
|
</select>
|
||||||
</span>
|
</span>
|
||||||
|
@ -535,66 +97,16 @@ export default class App extends Component {
|
||||||
<div className="container-fluid">
|
<div className="container-fluid">
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-9">
|
<div className="col-md-9">
|
||||||
<div className="books-per-month"><span className="block_name">Boeken per maand per genre</span><canvas id="chart"></canvas></div>
|
<Books year={this.state.year} />
|
||||||
<div className="row">
|
<Pages year={this.state.year} />
|
||||||
<div className="col-md-6">
|
|
||||||
<div className="book shortest">
|
|
||||||
<span className="block_name">Kortste boek</span>
|
|
||||||
<i className="fa fa-book book-icon"></i>
|
|
||||||
<div className="book_pages">{this.state.pagesStats.shortestbook ? this.state.pagesStats.shortestbook.pages : ''} pagina's</div>
|
|
||||||
<div className="book_title_author">{this.state.pagesStats.shortestbook ? this.state.pagesStats.shortestbook.name : ''} - {this.state.pagesStats.shortestbook ? this.state.pagesStats.shortestbook.author : ''}</div>
|
|
||||||
<div id="shortest_rating" className="book_rating"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div className="col-md-6">
|
|
||||||
<div className="book longest">
|
|
||||||
<span className="block_name">Langste boek</span>
|
|
||||||
<i className="fa fa-book book-icon"></i>
|
|
||||||
<div className="book_pages">{this.state.pagesStats.longestbook ? this.state.pagesStats.longestbook.pages : ''} pagina's</div>
|
|
||||||
<div className="book_title_author">{this.state.pagesStats.longestbook ? this.state.pagesStats.longestbook.name : ''} - {this.state.pagesStats.longestbook ? this.state.pagesStats.longestbook.author : ''}</div>
|
|
||||||
<div id="longest_rating" className="book_rating"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-md-3">
|
<div className="col-md-3">
|
||||||
<div className="books-per-country">
|
<Countries year={this.state.year} />
|
||||||
<span className="block_name">Landen</span>
|
<Genres year={this.state.year} />
|
||||||
<table id="DataTable" className="showHead table responsive nowrap" width="100%">
|
|
||||||
<thead>
|
|
||||||
<tr>
|
|
||||||
<th>#</th>
|
|
||||||
<th>Land</th>
|
|
||||||
<th>Boeken</th>
|
|
||||||
</tr>
|
|
||||||
</thead>
|
|
||||||
<tbody>
|
|
||||||
{this.state.countries.map((country, i) => {
|
|
||||||
|
|
||||||
var code = country.code.toLowerCase();
|
|
||||||
return (
|
|
||||||
<React.Fragment>
|
|
||||||
<tr key="{i}">
|
|
||||||
<td>{i + 1}</td>
|
|
||||||
<td><img src={`https://flagcdn.com/32x24/${code}.png`} /> {country.country}</td>
|
|
||||||
<td>{country.count}</td>
|
|
||||||
</tr>
|
|
||||||
</React.Fragment>
|
|
||||||
)
|
|
||||||
|
|
||||||
})}
|
|
||||||
</tbody>
|
|
||||||
</table>
|
|
||||||
</div>
|
|
||||||
<div className="genresPercent"><span className="block_name">Genres</span><canvas id="chartGenres"></canvas></div>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
|
|
|
@ -0,0 +1,43 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { getBooksPerYearPerGenres } from "./Data.js";
|
||||||
|
import { initChart } from "./Charts.js";
|
||||||
|
|
||||||
|
export default class Books extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
books: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentData() {
|
||||||
|
getBooksPerYearPerGenres(this.props.year).then(books => {
|
||||||
|
this.setState({
|
||||||
|
books: books
|
||||||
|
})
|
||||||
|
|
||||||
|
initChart(books, this.props.year);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevProps.year !== this.props.year) {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="books-per-month">
|
||||||
|
<span className="block_name">Boeken per maand per genre</span>
|
||||||
|
<canvas id="chart"></canvas>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,298 @@
|
||||||
|
export const initChart = (data, year) => {
|
||||||
|
|
||||||
|
/*
|
||||||
|
----------------------------------
|
||||||
|
Books per month per genre
|
||||||
|
----------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
var genres = [];
|
||||||
|
|
||||||
|
var colors = [
|
||||||
|
// '#696ffc', '#7596fa', '#92adfe', '#abc0ff'
|
||||||
|
'#8066ee', '#58c8d6', '#fe4c62', '#49b8fd', '#ffbe0e'
|
||||||
|
]
|
||||||
|
|
||||||
|
var dataSet = [];
|
||||||
|
|
||||||
|
data.forEach(book => {
|
||||||
|
if (!genres.includes(book.genre)) {
|
||||||
|
genres.push(book.genre)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (genres && genres.length > 0) {
|
||||||
|
genres.forEach((genre, index) => {
|
||||||
|
var genreData = [];
|
||||||
|
|
||||||
|
for (var i = 0; i < 12; i++) {
|
||||||
|
|
||||||
|
genreData[i] = 0;
|
||||||
|
|
||||||
|
if ((i + 1) < 10) {
|
||||||
|
var month = "0" + (i + 1);
|
||||||
|
} else {
|
||||||
|
month = (i + 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var j = 0; j < data.length; j++) {
|
||||||
|
if (data && data[j] && data[j].readed == month + '-' + year) {
|
||||||
|
if (data[j].genre == genre) {
|
||||||
|
genreData[i] = data[j].count;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dataSet.push({
|
||||||
|
label: genre,
|
||||||
|
data: genreData,
|
||||||
|
backgroundColor: colors[index],
|
||||||
|
order: 2
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
----------------------------------
|
||||||
|
Stacked bar chart
|
||||||
|
----------------------------------
|
||||||
|
*/
|
||||||
|
|
||||||
|
$("canvas#chart").remove();
|
||||||
|
$("div.books-per-month").append('<canvas id="chart"></canvas>');
|
||||||
|
|
||||||
|
const legendMargin = {
|
||||||
|
id: 'legendMargin',
|
||||||
|
beforeInit(chart, legend, options) {
|
||||||
|
const fitValue = chart.legend.fit;
|
||||||
|
|
||||||
|
chart.legend.fit = function fit() {
|
||||||
|
fitValue.bind(chart.legend)();
|
||||||
|
return this.height += 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
new Chart(document.getElementById('chart'), {
|
||||||
|
type: 'bar',
|
||||||
|
data: {
|
||||||
|
labels: ["Januari", "Februari", "Maart", "April", "Mei", "Juni", "Juli", "Augustus", "September", "Oktober", "November", "December"],
|
||||||
|
datasets: dataSet
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
maintainAspectRatio: false,
|
||||||
|
responsive: true,
|
||||||
|
showTooltips: true,
|
||||||
|
legend: {
|
||||||
|
display: true,
|
||||||
|
labels: {
|
||||||
|
usePointStyle: true,
|
||||||
|
}
|
||||||
|
},
|
||||||
|
interaction: {
|
||||||
|
mode: 'index'
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
color: "#101010",
|
||||||
|
fontFamily: "Source Sans Pro",
|
||||||
|
},
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
stepSize: 1,
|
||||||
|
color: "#101010",
|
||||||
|
fontFamily: "Source Sans Pro",
|
||||||
|
},
|
||||||
|
stacked: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
labels: {
|
||||||
|
usePointStyle: true,
|
||||||
|
color: "#101010",
|
||||||
|
padding: 20,
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
family: 'Source Sans Pro',
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
tooltips: {
|
||||||
|
bodyFont: 'Source Sans Pro'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [legendMargin],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initDoughnut = (data) => {
|
||||||
|
|
||||||
|
var labels = [];
|
||||||
|
var counts = [];
|
||||||
|
|
||||||
|
data.forEach((count) => {
|
||||||
|
if (!labels.includes(count.genre)) {
|
||||||
|
labels.push(count.genre)
|
||||||
|
}
|
||||||
|
|
||||||
|
counts.push(count.count)
|
||||||
|
})
|
||||||
|
|
||||||
|
const legendMargin = {
|
||||||
|
id: 'legendMargin',
|
||||||
|
beforeInit(chart, legend, options) {
|
||||||
|
const fitValue = chart.legend.fit;
|
||||||
|
|
||||||
|
chart.legend.fit = function fit() {
|
||||||
|
fitValue.bind(chart.legend)();
|
||||||
|
return this.height += 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$("canvas#chartGenres").remove();
|
||||||
|
$("div.genresPercent").append('<canvas id="chartGenres"></canvas>');
|
||||||
|
|
||||||
|
var ctx = document.getElementById("chartGenres");
|
||||||
|
var myChart = new Chart(ctx, {
|
||||||
|
type: 'pie',
|
||||||
|
data: {
|
||||||
|
labels: labels,
|
||||||
|
datasets: [{
|
||||||
|
label: '# of Tomatoes',
|
||||||
|
data: counts,
|
||||||
|
backgroundColor: [
|
||||||
|
'#8066ee', '#58c8d6', '#fe4c62', '#49b8fd', '#ffbe0e'
|
||||||
|
],
|
||||||
|
borderWidth: 0,
|
||||||
|
borderColor: '#1f2940',
|
||||||
|
tooltip: {
|
||||||
|
callbacks: {
|
||||||
|
label: function (context) {
|
||||||
|
let label = context.label;
|
||||||
|
let value = context.formattedValue;
|
||||||
|
|
||||||
|
if (!label)
|
||||||
|
label = 'Unknown'
|
||||||
|
|
||||||
|
let sum = 0;
|
||||||
|
let dataArr = context.chart.data.datasets[0].data;
|
||||||
|
dataArr.map(data => {
|
||||||
|
sum += Number(data);
|
||||||
|
});
|
||||||
|
|
||||||
|
let percentage = (value * 100 / sum).toFixed(1) + '%';
|
||||||
|
return label + ": " + percentage;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}]
|
||||||
|
},
|
||||||
|
options: {
|
||||||
|
cutout: '80%',
|
||||||
|
responsive: true,
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
position: 'top',
|
||||||
|
labels: {
|
||||||
|
padding: 20,
|
||||||
|
usePointStyle: true,
|
||||||
|
// This more specific font property overrides the global property
|
||||||
|
color: "##101010",
|
||||||
|
font: {
|
||||||
|
size: 14,
|
||||||
|
family: 'Source Sans Pro'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
plugins: [{
|
||||||
|
id: 'legendMargin',
|
||||||
|
beforeInit(chart, legend, options) {
|
||||||
|
const fitValue = chart.legend.fit;
|
||||||
|
|
||||||
|
chart.legend.fit = function fit() {
|
||||||
|
fitValue.bind(chart.legend)();
|
||||||
|
return this.height += 30;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, {
|
||||||
|
afterDraw: chart => {
|
||||||
|
var ctx = chart.ctx;
|
||||||
|
ctx.save();
|
||||||
|
var image = new Image();
|
||||||
|
image.src = 'https://www.iconsdb.com/icons/preview/gray/book-xxl.png';
|
||||||
|
var imageSize = 80;
|
||||||
|
ctx.drawImage(image, chart.width / 2 - imageSize / 2, chart.height / 2 - imageSize / 6, imageSize, imageSize);
|
||||||
|
ctx.restore();
|
||||||
|
}
|
||||||
|
}],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export const initHorBar = (data) => {
|
||||||
|
|
||||||
|
var countries = [];
|
||||||
|
var counts = [];
|
||||||
|
|
||||||
|
data.forEach((count) => {
|
||||||
|
if (!countries.includes(count.country)) {
|
||||||
|
countries.push(count.country)
|
||||||
|
}
|
||||||
|
|
||||||
|
counts.push(count.count)
|
||||||
|
})
|
||||||
|
|
||||||
|
$("canvas#countryChart").remove();
|
||||||
|
$("div.books-per-country").append('<canvas id="countryChart"></canvas>');
|
||||||
|
|
||||||
|
var ctx = document.getElementById("countryChart");
|
||||||
|
new Chart(ctx, {
|
||||||
|
type: 'bar',
|
||||||
|
options: {
|
||||||
|
indexAxis: 'y',
|
||||||
|
plugins: {
|
||||||
|
legend: {
|
||||||
|
display: false
|
||||||
|
},
|
||||||
|
},
|
||||||
|
scales: {
|
||||||
|
x: {
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
stacked: true,
|
||||||
|
},
|
||||||
|
y: {
|
||||||
|
ticks: {
|
||||||
|
beginAtZero: true,
|
||||||
|
stepSize: 1,
|
||||||
|
color: "white",
|
||||||
|
},
|
||||||
|
stacked: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: {
|
||||||
|
labels: countries,
|
||||||
|
datasets: [
|
||||||
|
{
|
||||||
|
label: "Boeken",
|
||||||
|
data: counts,
|
||||||
|
backgroundColor: '#696ffc',
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { getCountries } from "./Data.js";
|
||||||
|
|
||||||
|
export default class Countries extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
countries: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentData() {
|
||||||
|
getCountries(this.props.year).then(countries => {
|
||||||
|
this.setState({
|
||||||
|
countries: countries
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevProps.year !== this.props.year) {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="books-per-country">
|
||||||
|
<span className="block_name">Landen</span>
|
||||||
|
<table id="DataTable" className="showHead table responsive nowrap" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>Land</th>
|
||||||
|
<th>Boeken</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this.state.countries.map((country, i) => {
|
||||||
|
var code = country.code.toLowerCase();
|
||||||
|
return (
|
||||||
|
<tr key={i}>
|
||||||
|
<td>{i + 1}</td>
|
||||||
|
<td><img src={`https://flagcdn.com/32x24/${code}.png`} /> {country.country}</td>
|
||||||
|
<td>{country.count}</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -33,3 +33,55 @@ export const getReadingYears = () => {
|
||||||
return data
|
return data
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const getCountries = (year) => {
|
||||||
|
return fetch('/api/books/countries', {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"year": year
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getShortestLongestBook = (year) => {
|
||||||
|
return fetch('/api/books/pages/stats', {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"year": year
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getBooksPerYearPerGenres = (year) => {
|
||||||
|
return fetch('/api/books/genres', {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"year": year
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getGenresCount = (year) => {
|
||||||
|
return fetch('/api/books/genres/count', {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"year": year
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,43 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { getGenresCount } from "./Data.js";
|
||||||
|
import { initDoughnut } from "./Charts.js";
|
||||||
|
|
||||||
|
export default class Genres extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
genres: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentData() {
|
||||||
|
getGenresCount(this.props.year).then(genres => {
|
||||||
|
this.setState({
|
||||||
|
genres: genres
|
||||||
|
})
|
||||||
|
|
||||||
|
initDoughnut(this.state.genres, this.props.year);
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevProps.year !== this.props.year) {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="genresPercent">
|
||||||
|
<span className="block_name">Genres</span>
|
||||||
|
<canvas id="chartGenres"></canvas>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { getShortestLongestBook } from "./Data.js";
|
||||||
|
|
||||||
|
export default class Pages extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
pagesStats: []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentData() {
|
||||||
|
getShortestLongestBook(this.props.year).then(bookstats => {
|
||||||
|
this.setState({
|
||||||
|
pagesStats: bookstats
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevProps.year !== this.props.year) {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
|
||||||
|
var ratingshort = '';
|
||||||
|
var ratinglong = '';
|
||||||
|
|
||||||
|
|
||||||
|
if (this.state.pagesStats.shortestbook) {
|
||||||
|
for (var i = 0; i < this.state.pagesStats.shortestbook.rating; i++) {
|
||||||
|
ratingshort += "<i class='fas fa-star'></i>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.getElementById("shortest_rating") !== null) {
|
||||||
|
document.getElementById('shortest_rating').innerHTML = ratingshort;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (this.state.pagesStats.longestbook) {
|
||||||
|
for (var i = 0; i < this.state.pagesStats.longestbook.rating; i++) {
|
||||||
|
ratinglong += "<i class='fas fa-star'></i>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (document.getElementById("longest_rating") !== null) {
|
||||||
|
document.getElementById('longest_rating').innerHTML = ratinglong;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-6">
|
||||||
|
<div className="book shortest">
|
||||||
|
<span className="block_name">Kortste boek</span>
|
||||||
|
<i className="fa fa-book book-icon"></i>
|
||||||
|
<div className="book_pages">{this.state.pagesStats.shortestbook ? this.state.pagesStats.shortestbook.pages : ''} pagina's</div>
|
||||||
|
<div className="book_title_author">{this.state.pagesStats.shortestbook ? this.state.pagesStats.shortestbook.name : ''} - {this.state.pagesStats.shortestbook ? this.state.pagesStats.shortestbook.author : ''}</div>
|
||||||
|
<div id="shortest_rating" className="book_rating"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="col-md-6">
|
||||||
|
<div className="book longest">
|
||||||
|
<span className="block_name">Langste boek</span>
|
||||||
|
<i className="fa fa-book book-icon"></i>
|
||||||
|
<div className="book_pages">{this.state.pagesStats.longestbook ? this.state.pagesStats.longestbook.pages : ''} pagina's</div>
|
||||||
|
<div className="book_title_author">{this.state.pagesStats.longestbook ? this.state.pagesStats.longestbook.name : ''} - {this.state.pagesStats.longestbook ? this.state.pagesStats.longestbook.author : ''}</div>
|
||||||
|
<div id="longest_rating" className="book_rating"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,262 @@
|
||||||
|
html, body{
|
||||||
|
background:#f8f8fa;
|
||||||
|
margin:0;
|
||||||
|
padding:0;
|
||||||
|
font-family: 'Source Sans Pro', sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1{
|
||||||
|
font-size: 30px;
|
||||||
|
font-weight: 500;
|
||||||
|
padding: 0 10px 0px 10px;
|
||||||
|
margin-bottom: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
h2{
|
||||||
|
padding-left: 10px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #a7adbd;
|
||||||
|
display: block;
|
||||||
|
margin-bottom: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.content{
|
||||||
|
padding: 50px 50px 50px 110px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.filter{
|
||||||
|
width:100%;
|
||||||
|
background:#1f2940;
|
||||||
|
padding: 20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-per-month{
|
||||||
|
height:700px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-per-month canvas{
|
||||||
|
height:600px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-per-month, .genresPercent, .books-per-country, .book{
|
||||||
|
background: #ffffff;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 2px 0px 1px rgb(0 0 0 / 3%);
|
||||||
|
margin-bottom: 20px;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book .book-icon{
|
||||||
|
font-size: 60px;
|
||||||
|
float: left;
|
||||||
|
margin-right: 20px;
|
||||||
|
margin-bottom: 40px;
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book_rating{
|
||||||
|
margin-top: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book_rating i{
|
||||||
|
font-family: "Font Awesome 5 Free";
|
||||||
|
color: #ffbe0e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book .book_pages{
|
||||||
|
font-size: 18px;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.book .book_title_author{
|
||||||
|
font-size: 16px;
|
||||||
|
color: #808080;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar{
|
||||||
|
background: #363a53;
|
||||||
|
width: 70px;
|
||||||
|
height: 100vh;
|
||||||
|
position: fixed;
|
||||||
|
padding-top: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .menu-item{
|
||||||
|
text-align: center;
|
||||||
|
padding: 15px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .menu-item i{
|
||||||
|
font-size: 25px;
|
||||||
|
color: #727794;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar .menu-item.selected i{
|
||||||
|
color:#fff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar svg{
|
||||||
|
color:#ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-stats{
|
||||||
|
margin:20px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-icon{
|
||||||
|
background:#000;
|
||||||
|
padding: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-stats .stat-block, .stat-block{
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 2px 0px 1px rgb(0 0 0 / 3%);
|
||||||
|
padding: 15px 5px;
|
||||||
|
color: #101010;
|
||||||
|
text-align: center;
|
||||||
|
border-radius: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-stats .col-md-2:nth-child(1) i{
|
||||||
|
background: #f8f5fc;
|
||||||
|
color: #8066ee;
|
||||||
|
}
|
||||||
|
.books-stats .col-md-2:nth-child(2) i{
|
||||||
|
background: #f1fcf8;
|
||||||
|
color: #58c8d6;
|
||||||
|
}
|
||||||
|
.books-stats .col-md-2:nth-child(3) i{
|
||||||
|
background: #fff5f6;
|
||||||
|
color: #fe4c62;
|
||||||
|
}
|
||||||
|
.books-stats .col-md-2:nth-child(4) i{
|
||||||
|
background: #f2f9ff;
|
||||||
|
color: #49b8fd;
|
||||||
|
}
|
||||||
|
.books-stats .col-md-2:nth-child(5) i{
|
||||||
|
background: #fffaee;
|
||||||
|
color: #ffbe0e;
|
||||||
|
}
|
||||||
|
.books-stats .col-md-2:nth-child(6) i{
|
||||||
|
background: #f8f5fc;
|
||||||
|
color: #8066ee;
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-stats .stat-block i{
|
||||||
|
font-weight: 900;
|
||||||
|
font-size: 25px;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 17px;
|
||||||
|
width: 65px;
|
||||||
|
height: 65px;
|
||||||
|
line-height: 30px;
|
||||||
|
text-align: center;
|
||||||
|
background: #696ffc;
|
||||||
|
/* box-shadow: 2px 2px 0px 0px rgba(0, 0, 0, 0.3); */
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-stats .stat-block .stats-number, .stats-number{
|
||||||
|
font-weight: 600;
|
||||||
|
display: inline-block;
|
||||||
|
margin-left: 10px;
|
||||||
|
font-size: 20px;
|
||||||
|
margin-right: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.books-stats .stat-block .stats-label, .stats-label{
|
||||||
|
color: #a7adbd;
|
||||||
|
font-weight: 400;
|
||||||
|
font-size: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.yearselector{
|
||||||
|
display: inline-block;
|
||||||
|
width: auto;
|
||||||
|
background: #ffffff;
|
||||||
|
border: none;
|
||||||
|
color: #101010;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.container-fluid{
|
||||||
|
margin-bottom:20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table{
|
||||||
|
border-bottom: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td{
|
||||||
|
color: #101010;
|
||||||
|
border-bottom:none !important;
|
||||||
|
padding: 10px 10px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table td img{
|
||||||
|
margin-right:5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#DataTable thead{
|
||||||
|
display:none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
span.block_name{
|
||||||
|
color: #101010;
|
||||||
|
font-weight: 600;
|
||||||
|
border-bottom: solid 1px #f5f6fa;
|
||||||
|
width: 100%;
|
||||||
|
display: block;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress{
|
||||||
|
background: #f8f8fa;
|
||||||
|
height: 50px;
|
||||||
|
border: solid 2px #efefef;
|
||||||
|
padding: 5px;
|
||||||
|
border-radius: 0;
|
||||||
|
margin: 0 15px 15px 15px;
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar{
|
||||||
|
background-color: #8066ee;
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
border-right: solid 2px #333;
|
||||||
|
}
|
||||||
|
|
||||||
|
.progress-bar-number{
|
||||||
|
position: absolute;
|
||||||
|
right: 0;
|
||||||
|
background: #333;
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 10px;
|
||||||
|
top: -20px;
|
||||||
|
right: -20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-screen-overlay{
|
||||||
|
background: #f8f8fa;
|
||||||
|
width:100%;
|
||||||
|
height:100vh;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 1;
|
||||||
|
top:0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.loading-screen{
|
||||||
|
width: 100%;
|
||||||
|
position: fixed;
|
||||||
|
background: #f8f8fa;
|
||||||
|
z-index: 1;
|
||||||
|
text-align: center;
|
||||||
|
top: 50%;
|
||||||
|
-webkit-transform: translateY(-50%);
|
||||||
|
-ms-transform: translateY(-50%);
|
||||||
|
transform: translateY(-50%);
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -10,10 +10,26 @@
|
||||||
!*** ./src/components/Data.js ***!
|
!*** ./src/components/Data.js ***!
|
||||||
\********************************/
|
\********************************/
|
||||||
|
|
||||||
|
/*!*********************************!*\
|
||||||
|
!*** ./src/components/Books.js ***!
|
||||||
|
\*********************************/
|
||||||
|
|
||||||
|
/*!*********************************!*\
|
||||||
|
!*** ./src/components/Pages.js ***!
|
||||||
|
\*********************************/
|
||||||
|
|
||||||
/*!*********************************!*\
|
/*!*********************************!*\
|
||||||
!*** ./src/components/Stats.js ***!
|
!*** ./src/components/Stats.js ***!
|
||||||
\*********************************/
|
\*********************************/
|
||||||
|
|
||||||
|
/*!**********************************!*\
|
||||||
|
!*** ./src/components/Charts.js ***!
|
||||||
|
\**********************************/
|
||||||
|
|
||||||
|
/*!**********************************!*\
|
||||||
|
!*** ./src/components/Genres.js ***!
|
||||||
|
\**********************************/
|
||||||
|
|
||||||
/*!*************************************!*\
|
/*!*************************************!*\
|
||||||
!*** ./node_modules/react/index.js ***!
|
!*** ./node_modules/react/index.js ***!
|
||||||
\*************************************/
|
\*************************************/
|
||||||
|
@ -22,6 +38,14 @@
|
||||||
!*** ./src/components/Challenge.js ***!
|
!*** ./src/components/Challenge.js ***!
|
||||||
\*************************************/
|
\*************************************/
|
||||||
|
|
||||||
|
/*!*************************************!*\
|
||||||
|
!*** ./src/components/Countries.js ***!
|
||||||
|
\*************************************/
|
||||||
|
|
||||||
|
/*!****************************************!*\
|
||||||
|
!*** ./node_modules/react-is/index.js ***!
|
||||||
|
\****************************************/
|
||||||
|
|
||||||
/*!*****************************************!*\
|
/*!*****************************************!*\
|
||||||
!*** ./node_modules/react-dom/index.js ***!
|
!*** ./node_modules/react-dom/index.js ***!
|
||||||
\*****************************************/
|
\*****************************************/
|
||||||
|
@ -30,14 +54,222 @@
|
||||||
!*** ./node_modules/scheduler/index.js ***!
|
!*** ./node_modules/scheduler/index.js ***!
|
||||||
\*****************************************/
|
\*****************************************/
|
||||||
|
|
||||||
|
/*!********************************************!*\
|
||||||
|
!*** ./node_modules/shallowequal/index.js ***!
|
||||||
|
\********************************************/
|
||||||
|
|
||||||
|
/*!***************************************************!*\
|
||||||
|
!*** ./node_modules/styled-tools/dist/es/prop.js ***!
|
||||||
|
\***************************************************/
|
||||||
|
|
||||||
|
/*!****************************************************!*\
|
||||||
|
!*** ./node_modules/styled-tools/dist/es/index.js ***!
|
||||||
|
\****************************************************/
|
||||||
|
|
||||||
|
/*!****************************************************!*\
|
||||||
|
!*** ./node_modules/styled-tools/dist/es/theme.js ***!
|
||||||
|
\****************************************************/
|
||||||
|
|
||||||
/*!*****************************************************!*\
|
/*!*****************************************************!*\
|
||||||
!*** ./node_modules/react/cjs/react.development.js ***!
|
!*** ./node_modules/react/cjs/react.development.js ***!
|
||||||
\*****************************************************/
|
\*****************************************************/
|
||||||
|
|
||||||
|
/*!*****************************************************!*\
|
||||||
|
!*** ./node_modules/styled-tools/dist/es/ifProp.js ***!
|
||||||
|
\*****************************************************/
|
||||||
|
|
||||||
|
/*!******************************************************!*\
|
||||||
|
!*** ./node_modules/styled-tools/dist/es/palette.js ***!
|
||||||
|
\******************************************************/
|
||||||
|
|
||||||
|
/*!*******************************************************!*\
|
||||||
|
!*** ./node_modules/styled-tools/dist/es/withProp.js ***!
|
||||||
|
\*******************************************************/
|
||||||
|
|
||||||
|
/*!********************************************************!*\
|
||||||
|
!*** ./node_modules/styled-tools/dist/es/ifNotProp.js ***!
|
||||||
|
\********************************************************/
|
||||||
|
|
||||||
|
/*!*********************************************************!*\
|
||||||
|
!*** ./node_modules/styled-tools/dist/es/switchProp.js ***!
|
||||||
|
\*********************************************************/
|
||||||
|
|
||||||
|
/*!***********************************************************!*\
|
||||||
|
!*** ./node_modules/react-is/cjs/react-is.development.js ***!
|
||||||
|
\***********************************************************/
|
||||||
|
|
||||||
|
/*!************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/type.js ***!
|
||||||
|
\************************************************************/
|
||||||
|
|
||||||
/*!*************************************************************!*\
|
/*!*************************************************************!*\
|
||||||
!*** ./node_modules/react-dom/cjs/react-dom.development.js ***!
|
!*** ./node_modules/react-dom/cjs/react-dom.development.js ***!
|
||||||
\*************************************************************/
|
\*************************************************************/
|
||||||
|
|
||||||
|
/*!*************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/index.js ***!
|
||||||
|
\*************************************************************/
|
||||||
|
|
||||||
/*!*************************************************************!*\
|
/*!*************************************************************!*\
|
||||||
!*** ./node_modules/scheduler/cjs/scheduler.development.js ***!
|
!*** ./node_modules/scheduler/cjs/scheduler.development.js ***!
|
||||||
\*************************************************************/
|
\*************************************************************/
|
||||||
|
|
||||||
|
/*!***************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/helpers.js ***!
|
||||||
|
\***************************************************************/
|
||||||
|
|
||||||
|
/*!*****************************************************************!*\
|
||||||
|
!*** ./node_modules/@emotion/stylis/dist/stylis.browser.esm.js ***!
|
||||||
|
\*****************************************************************/
|
||||||
|
|
||||||
|
/*!******************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Dna.js ***!
|
||||||
|
\******************************************************************/
|
||||||
|
|
||||||
|
/*!*******************************************************************!*\
|
||||||
|
!*** ./node_modules/@emotion/memoize/dist/emotion-memoize.esm.js ***!
|
||||||
|
\*******************************************************************/
|
||||||
|
|
||||||
|
/*!*******************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Bars.js ***!
|
||||||
|
\*******************************************************************/
|
||||||
|
|
||||||
|
/*!*******************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Grid.js ***!
|
||||||
|
\*******************************************************************/
|
||||||
|
|
||||||
|
/*!*******************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Oval.js ***!
|
||||||
|
\*******************************************************************/
|
||||||
|
|
||||||
|
/*!*******************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Puff.js ***!
|
||||||
|
\*******************************************************************/
|
||||||
|
|
||||||
|
/*!********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Audio.js ***!
|
||||||
|
\********************************************************************/
|
||||||
|
|
||||||
|
/*!********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Radio.js ***!
|
||||||
|
\********************************************************************/
|
||||||
|
|
||||||
|
/*!********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Rings.js ***!
|
||||||
|
\********************************************************************/
|
||||||
|
|
||||||
|
/*!********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Watch.js ***!
|
||||||
|
\********************************************************************/
|
||||||
|
|
||||||
|
/*!*********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Blocks.js ***!
|
||||||
|
\*********************************************************************/
|
||||||
|
|
||||||
|
/*!*********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Hearts.js ***!
|
||||||
|
\*********************************************************************/
|
||||||
|
|
||||||
|
/*!*********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Vortex.js ***!
|
||||||
|
\*********************************************************************/
|
||||||
|
|
||||||
|
/*!**********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Circles.js ***!
|
||||||
|
\**********************************************************************/
|
||||||
|
|
||||||
|
/*!**********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Comment.js ***!
|
||||||
|
\**********************************************************************/
|
||||||
|
|
||||||
|
/*!**********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Discuss.js ***!
|
||||||
|
\**********************************************************************/
|
||||||
|
|
||||||
|
/*!***********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/LineWave.js ***!
|
||||||
|
\***********************************************************************/
|
||||||
|
|
||||||
|
/*!***********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/TailSpin.js ***!
|
||||||
|
\***********************************************************************/
|
||||||
|
|
||||||
|
/*!***********************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Triangle.js ***!
|
||||||
|
\***********************************************************************/
|
||||||
|
|
||||||
|
/*!************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/ColorRing.js ***!
|
||||||
|
\************************************************************************/
|
||||||
|
|
||||||
|
/*!************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/ThreeDots.js ***!
|
||||||
|
\************************************************************************/
|
||||||
|
|
||||||
|
/*!**************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/ProgressBar.js ***!
|
||||||
|
\**************************************************************************/
|
||||||
|
|
||||||
|
/*!***************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/BallTriangle.js ***!
|
||||||
|
\***************************************************************************/
|
||||||
|
|
||||||
|
/*!***************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/FallingLines.js ***!
|
||||||
|
\***************************************************************************/
|
||||||
|
|
||||||
|
/*!***************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/InfinitySpin.js ***!
|
||||||
|
\***************************************************************************/
|
||||||
|
|
||||||
|
/*!***************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/MutatingDots.js ***!
|
||||||
|
\***************************************************************************/
|
||||||
|
|
||||||
|
/*!***************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/RevolvingDot.js ***!
|
||||||
|
\***************************************************************************/
|
||||||
|
|
||||||
|
/*!***************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/ThreeCircles.js ***!
|
||||||
|
\***************************************************************************/
|
||||||
|
|
||||||
|
/*!****************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/FidgetSpinner.js ***!
|
||||||
|
\****************************************************************************/
|
||||||
|
|
||||||
|
/*!****************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/RotatingLines.js ***!
|
||||||
|
\****************************************************************************/
|
||||||
|
|
||||||
|
/*!*****************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/CirclesWithBar.js ***!
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
/*!*****************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/RotatingSquare.js ***!
|
||||||
|
\*****************************************************************************/
|
||||||
|
|
||||||
|
/*!******************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/MagnifyingGlass.js ***!
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
/*!******************************************************************************!*\
|
||||||
|
!*** ./node_modules/styled-components/dist/styled-components.browser.esm.js ***!
|
||||||
|
\******************************************************************************/
|
||||||
|
|
||||||
|
/*!*******************************************************************************!*\
|
||||||
|
!*** ./node_modules/@emotion/is-prop-valid/dist/emotion-is-prop-valid.esm.js ***!
|
||||||
|
\*******************************************************************************/
|
||||||
|
|
||||||
|
/*!********************************************************************************!*\
|
||||||
|
!*** ./node_modules/react-loader-spinner/dist/esm/loader/RotatingTriangles.js ***!
|
||||||
|
\********************************************************************************/
|
||||||
|
|
||||||
|
/*!**********************************************************************************!*\
|
||||||
|
!*** ./node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js ***!
|
||||||
|
\**********************************************************************************/
|
||||||
|
|
||||||
|
/*!****************************************************************************************************!*\
|
||||||
|
!*** ./node_modules/styled-components/node_modules/@emotion/unitless/dist/unitless.browser.esm.js ***!
|
||||||
|
\****************************************************************************************************/
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
<link
|
<link
|
||||||
rel="stylesheet"
|
rel="stylesheet"
|
||||||
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/>
|
href="https://fonts.googleapis.com/css?family=Roboto:300,400,500,700&display=swap"/>
|
||||||
<link rel="stylesheet" type="text/css" href="{% static "css/index.css" %}"/>
|
|
||||||
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
|
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.7.1/dist/leaflet.css"
|
||||||
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
|
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
|
||||||
|
@ -25,255 +24,13 @@
|
||||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||||
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@200;300;400;600;700&display=swap" rel="stylesheet">
|
<link href="https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@200;300;400;600;700&display=swap" rel="stylesheet">
|
||||||
|
|
||||||
|
<link rel="stylesheet" type="text/css" href="{% static "css/style.css" %}" />
|
||||||
|
|
||||||
|
|
||||||
<!-- Make sure you put this AFTER Leaflet's CSS -->
|
<!-- Make sure you put this AFTER Leaflet's CSS -->
|
||||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
|
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
|
||||||
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
|
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
|
||||||
crossorigin=""></script>
|
crossorigin=""></script>
|
||||||
<style>
|
|
||||||
html, body{
|
|
||||||
background:#f8f8fa;
|
|
||||||
margin:0;
|
|
||||||
padding:0;
|
|
||||||
font-family: 'Source Sans Pro', sans-serif;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1{
|
|
||||||
font-size: 30px;
|
|
||||||
font-weight: 500;
|
|
||||||
padding: 0 10px 0px 10px;
|
|
||||||
margin-bottom: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
h2{
|
|
||||||
padding-left: 10px;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 18px;
|
|
||||||
color: #a7adbd;
|
|
||||||
display: block;
|
|
||||||
margin-bottom: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.content{
|
|
||||||
padding: 50px 50px 50px 110px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.filter{
|
|
||||||
width:100%;
|
|
||||||
background:#1f2940;
|
|
||||||
padding: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.books-per-month{
|
|
||||||
height:700px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.books-per-month canvas{
|
|
||||||
height:600px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.books-per-month, .genresPercent, .books-per-country, .book{
|
|
||||||
background: #ffffff;
|
|
||||||
padding: 20px;
|
|
||||||
box-shadow: 0 2px 0px 1px rgb(0 0 0 / 3%);
|
|
||||||
margin-bottom: 20px;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.book .book-icon{
|
|
||||||
font-size: 60px;
|
|
||||||
float: left;
|
|
||||||
margin-right: 20px;
|
|
||||||
margin-bottom: 40px;
|
|
||||||
color: #808080;
|
|
||||||
}
|
|
||||||
|
|
||||||
.book_rating{
|
|
||||||
margin-top: 5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.book_rating i{
|
|
||||||
font-family: "Font Awesome 5 Free";
|
|
||||||
color: #ffbe0e;
|
|
||||||
}
|
|
||||||
|
|
||||||
.book .book_pages{
|
|
||||||
font-size: 18px;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.book .book_title_author{
|
|
||||||
font-size: 16px;
|
|
||||||
color: #808080;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar{
|
|
||||||
background: #363a53;
|
|
||||||
width: 70px;
|
|
||||||
height: 100vh;
|
|
||||||
position: fixed;
|
|
||||||
padding-top: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .menu-item{
|
|
||||||
text-align: center;
|
|
||||||
padding: 15px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .menu-item i{
|
|
||||||
font-size: 25px;
|
|
||||||
color: #727794;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar .menu-item.selected i{
|
|
||||||
color:#fff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.sidebar svg{
|
|
||||||
color:#ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.books-stats{
|
|
||||||
margin:20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
.books-icon{
|
|
||||||
background:#000;
|
|
||||||
padding: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.books-stats .stat-block, .stat-block{
|
|
||||||
background: #ffffff;
|
|
||||||
box-shadow: 0 2px 0px 1px rgb(0 0 0 / 3%);
|
|
||||||
padding: 15px 5px;
|
|
||||||
color: #101010;
|
|
||||||
text-align: center;
|
|
||||||
border-radius: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.books-stats .col-md-2:nth-child(1) i{
|
|
||||||
background: #f8f5fc;
|
|
||||||
color: #8066ee;
|
|
||||||
}
|
|
||||||
.books-stats .col-md-2:nth-child(2) i{
|
|
||||||
background: #f1fcf8;
|
|
||||||
color: #58c8d6;
|
|
||||||
}
|
|
||||||
.books-stats .col-md-2:nth-child(3) i{
|
|
||||||
background: #fff5f6;
|
|
||||||
color: #fe4c62;
|
|
||||||
}
|
|
||||||
.books-stats .col-md-2:nth-child(4) i{
|
|
||||||
background: #f2f9ff;
|
|
||||||
color: #49b8fd;
|
|
||||||
}
|
|
||||||
.books-stats .col-md-2:nth-child(5) i{
|
|
||||||
background: #fffaee;
|
|
||||||
color: #ffbe0e;
|
|
||||||
}
|
|
||||||
.books-stats .col-md-2:nth-child(6) i{
|
|
||||||
background: #f8f5fc;
|
|
||||||
color: #8066ee;
|
|
||||||
}
|
|
||||||
|
|
||||||
.books-stats .stat-block i{
|
|
||||||
font-weight: 900;
|
|
||||||
font-size: 25px;
|
|
||||||
border-radius: 50%;
|
|
||||||
padding: 17px;
|
|
||||||
width: 65px;
|
|
||||||
height: 65px;
|
|
||||||
line-height: 30px;
|
|
||||||
text-align: center;
|
|
||||||
background: #696ffc;
|
|
||||||
/* box-shadow: 2px 2px 0px 0px rgba(0, 0, 0, 0.3); */
|
|
||||||
}
|
|
||||||
|
|
||||||
.books-stats .stat-block .stats-number, .stats-number{
|
|
||||||
font-weight: 600;
|
|
||||||
display: inline-block;
|
|
||||||
margin-left: 10px;
|
|
||||||
font-size: 20px;
|
|
||||||
margin-right: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.books-stats .stat-block .stats-label, .stats-label{
|
|
||||||
color: #a7adbd;
|
|
||||||
font-weight: 400;
|
|
||||||
font-size: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.yearselector{
|
|
||||||
display: inline-block;
|
|
||||||
width: auto;
|
|
||||||
background: #ffffff;
|
|
||||||
border: none;
|
|
||||||
color: #101010;
|
|
||||||
font-weight: 600;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-fluid{
|
|
||||||
margin-bottom:20px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table{
|
|
||||||
border-bottom: none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table td{
|
|
||||||
color: #101010;
|
|
||||||
border-bottom:none !important;
|
|
||||||
padding: 10px 10px !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.table td img{
|
|
||||||
margin-right:5px;
|
|
||||||
}
|
|
||||||
|
|
||||||
#DataTable thead{
|
|
||||||
display:none !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
span.block_name{
|
|
||||||
color: #101010;
|
|
||||||
font-weight: 600;
|
|
||||||
border-bottom: solid 1px #f5f6fa;
|
|
||||||
width: 100%;
|
|
||||||
display: block;
|
|
||||||
padding-bottom: 10px;
|
|
||||||
margin-bottom: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress{
|
|
||||||
background: #f8f8fa;
|
|
||||||
height: 50px;
|
|
||||||
border: solid 2px #efefef;
|
|
||||||
padding: 5px;
|
|
||||||
border-radius: 0;
|
|
||||||
margin: 0 15px 15px 15px;
|
|
||||||
position: relative;
|
|
||||||
overflow: visible;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar{
|
|
||||||
background-color: #8066ee;
|
|
||||||
position: relative;
|
|
||||||
overflow: visible;
|
|
||||||
border-right: solid 2px #333;
|
|
||||||
}
|
|
||||||
|
|
||||||
.progress-bar-number{
|
|
||||||
position: absolute;
|
|
||||||
right: 0;
|
|
||||||
background: #333;
|
|
||||||
border-radius: 50%;
|
|
||||||
padding: 10px;
|
|
||||||
top: -20px;
|
|
||||||
right: -20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
|
Loading…
Reference in New Issue