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
|
||||
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():
|
||||
data.append({
|
||||
|
|
|
@ -1266,6 +1266,11 @@
|
|||
"@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": {
|
||||
"version": "0.8.0",
|
||||
"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-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": {
|
||||
"version": "5.2.2",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "1.0.30001390",
|
||||
"resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001390.tgz",
|
||||
|
@ -2039,6 +2066,21 @@
|
|||
"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": {
|
||||
"version": "3.1.0",
|
||||
"resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
|
||||
|
@ -2460,6 +2502,11 @@
|
|||
"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": {
|
||||
"version": "4.0.8",
|
||||
"resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz",
|
||||
|
@ -2620,6 +2667,11 @@
|
|||
"integrity": "sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==",
|
||||
"dev": true
|
||||
},
|
||||
"picomatch": {
|
||||
"version": "2.3.1",
|
||||
"resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
|
||||
"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
|
||||
},
|
||||
"pkg-dir": {
|
||||
"version": "4.2.0",
|
||||
"resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
|
||||
|
@ -2629,6 +2681,11 @@
|
|||
"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": {
|
||||
"version": "15.8.1",
|
||||
"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",
|
||||
"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": {
|
||||
"version": "6.3.0",
|
||||
"resolved": "https://registry.npmjs.org/react-router/-/react-router-6.3.0.tgz",
|
||||
|
@ -2856,6 +2930,11 @@
|
|||
"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": {
|
||||
"version": "2.0.0",
|
||||
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
|
||||
|
@ -2887,6 +2966,48 @@
|
|||
"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": {
|
||||
"version": "4.0.13",
|
||||
"resolved": "https://registry.npmjs.org/stylis/-/stylis-4.0.13.tgz",
|
||||
|
|
|
@ -30,6 +30,7 @@
|
|||
"datatables.net": "^1.12.1",
|
||||
"datatables.net-dt": "^1.12.1",
|
||||
"jquery": "^3.6.1",
|
||||
"react-loader-spinner": "^5.3.4",
|
||||
"react-router-dom": "^6.3.0"
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,6 +1,12 @@
|
|||
import React, { Component } from "react";
|
||||
import Challenge from "./components/Challenge";
|
||||
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 {
|
||||
constructor(props) {
|
||||
|
@ -8,449 +14,22 @@ export default class App extends Component {
|
|||
this.state = {
|
||||
year: new Date().getFullYear(),
|
||||
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) {
|
||||
|
||||
this.setState({
|
||||
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() {
|
||||
|
||||
var $this = this;
|
||||
setTimeout(() =>{
|
||||
document.getElementById("loading-overlay").style.display = "none";
|
||||
}, 1000);
|
||||
|
||||
var currentyear = this.state.year ? this.state.year : new Date().getFullYear();
|
||||
|
||||
fetch('/api/books/genres', {
|
||||
"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 => {
|
||||
getReadingYears().then(data => {
|
||||
this.setState({
|
||||
readingYears: data
|
||||
})
|
||||
|
@ -460,33 +39,8 @@ export default class App extends Component {
|
|||
|
||||
render() {
|
||||
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 (
|
||||
|
||||
|
||||
<React.Fragment>
|
||||
<div className="sidebar">
|
||||
<div className={`menu-item ${url && url[3] == "" ? 'selected' : ''}`}>
|
||||
|
@ -499,6 +53,21 @@ export default class App extends Component {
|
|||
</div>
|
||||
<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>
|
||||
<h2>Leesanalyse van Jordy van Zeeland</h2>
|
||||
|
||||
|
@ -509,16 +78,9 @@ export default class App extends Component {
|
|||
<div className="stat-block">
|
||||
<i className="fa fa-calendar"></i>
|
||||
<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) => {
|
||||
|
||||
if (year === this.state.year) {
|
||||
var selected = 'selected'
|
||||
} else {
|
||||
selected = ''
|
||||
}
|
||||
|
||||
return (<option key={i} selected={selected} value={year}>{year}</option>)
|
||||
return (<option key={i} value={year}>{year}</option>)
|
||||
})}
|
||||
</select>
|
||||
</span>
|
||||
|
@ -535,66 +97,16 @@ export default class App extends Component {
|
|||
<div className="container-fluid">
|
||||
<div className="row">
|
||||
<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>
|
||||
<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>
|
||||
<Books year={this.state.year} />
|
||||
<Pages year={this.state.year} />
|
||||
</div>
|
||||
|
||||
<div className="col-md-3">
|
||||
<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 (
|
||||
<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>
|
||||
|
||||
<Countries year={this.state.year} />
|
||||
<Genres year={this.state.year} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
</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
|
||||
})
|
||||
}
|
||||
|
||||
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/Books.js ***!
|
||||
\*********************************/
|
||||
|
||||
/*!*********************************!*\
|
||||
!*** ./src/components/Pages.js ***!
|
||||
\*********************************/
|
||||
|
||||
/*!*********************************!*\
|
||||
!*** ./src/components/Stats.js ***!
|
||||
\*********************************/
|
||||
|
||||
/*!**********************************!*\
|
||||
!*** ./src/components/Charts.js ***!
|
||||
\**********************************/
|
||||
|
||||
/*!**********************************!*\
|
||||
!*** ./src/components/Genres.js ***!
|
||||
\**********************************/
|
||||
|
||||
/*!*************************************!*\
|
||||
!*** ./node_modules/react/index.js ***!
|
||||
\*************************************/
|
||||
|
@ -22,6 +38,14 @@
|
|||
!*** ./src/components/Challenge.js ***!
|
||||
\*************************************/
|
||||
|
||||
/*!*************************************!*\
|
||||
!*** ./src/components/Countries.js ***!
|
||||
\*************************************/
|
||||
|
||||
/*!****************************************!*\
|
||||
!*** ./node_modules/react-is/index.js ***!
|
||||
\****************************************/
|
||||
|
||||
/*!*****************************************!*\
|
||||
!*** ./node_modules/react-dom/index.js ***!
|
||||
\*****************************************/
|
||||
|
@ -30,14 +54,222 @@
|
|||
!*** ./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/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-loader-spinner/dist/esm/index.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
|
||||
rel="stylesheet"
|
||||
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"
|
||||
integrity="sha512-xodZBNTC5n17Xt2atTPuE1HxjVMSvLVW9ocqUKLsCC5CXdbqCmblAshOMAS6/keqq/sMZMZ19scR4PsZChSR7A=="
|
||||
|
@ -25,255 +24,13 @@
|
|||
<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 rel="stylesheet" type="text/css" href="{% static "css/style.css" %}" />
|
||||
|
||||
|
||||
<!-- Make sure you put this AFTER Leaflet's CSS -->
|
||||
<script src="https://unpkg.com/leaflet@1.7.1/dist/leaflet.js"
|
||||
integrity="sha512-XQoYMqMTK8LvdxXYG3nZ448hOEQiglfqkJs1NOQV44cWnUrBc8PkAOcXy20w0vlaXaVUearIOBhiXZ5V3ynxwA=="
|
||||
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>
|
||||
|
||||
<body>
|
||||
|
|
Loading…
Reference in New Issue