1
1
Fork 0

Redesign components in frontend (DRY)

This commit is contained in:
Jordy van Zeeland 2022-11-02 13:28:02 +01:00
parent cfb3290194
commit 09182ee58d
14 changed files with 1231 additions and 768 deletions

View File

@ -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({

View File

@ -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",

View File

@ -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"
} }
} }

View File

@ -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>
) )

View File

@ -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>
)
}
}

View File

@ -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',
}]
}
});
}

View File

@ -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>
)
}
}

View File

@ -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;
})
}

View File

@ -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>
)
}
}

View File

@ -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>
)
}
}

View File

@ -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

View File

@ -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 ***!
\****************************************************************************************************/

View File

@ -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>