Redesign components in frontend (DRY)
This commit is contained in:
parent
91c530bcd5
commit
cfb3290194
|
@ -2,6 +2,8 @@ from django.urls import path
|
||||||
from .views import *
|
from .views import *
|
||||||
|
|
||||||
urlpatterns = [
|
urlpatterns = [
|
||||||
|
path('books', getAllBooks),
|
||||||
|
path('books/challenge', getChallengeOfYear),
|
||||||
path('books/years', getYears),
|
path('books/years', getYears),
|
||||||
path('books/stats', getStats),
|
path('books/stats', getStats),
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,15 @@ def getBooksData():
|
||||||
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
def getBookChallenge(year = None):
|
||||||
|
engine = create_engine('mysql+mysqldb://' + ras.settings.DATABASES['default']['USER'] + ':' + ras.settings.DATABASES['default']['PASSWORD'] + '@' + ras.settings.DATABASES['default']['HOST'] + ':3306/' + ras.settings.DATABASES['default']['NAME'])
|
||||||
|
if(year):
|
||||||
|
df = pd.read_sql('SELECT * FROM book_challenge where year = ' + year, engine)
|
||||||
|
else:
|
||||||
|
df = pd.read_sql('SELECT * FROM book_challenge', engine)
|
||||||
|
|
||||||
|
return df
|
||||||
|
|
||||||
def filterData(df, datayear = None):
|
def filterData(df, datayear = None):
|
||||||
df['readed'] = pd.to_datetime(df['readed'], format='%Y-%m-%d')
|
df['readed'] = pd.to_datetime(df['readed'], format='%Y-%m-%d')
|
||||||
df['readed'] = df['readed'].dt.strftime('%m-%Y')
|
df['readed'] = df['readed'].dt.strftime('%m-%Y')
|
||||||
|
@ -27,6 +36,43 @@ def filterData(df, datayear = None):
|
||||||
|
|
||||||
return df
|
return df
|
||||||
|
|
||||||
|
@api_view(['GET'])
|
||||||
|
def getAllBooks(request):
|
||||||
|
|
||||||
|
data = []
|
||||||
|
books = getBooksData()
|
||||||
|
|
||||||
|
for index, row in books.iterrows():
|
||||||
|
data.append({
|
||||||
|
"id": row['id'],
|
||||||
|
"name": row['name'],
|
||||||
|
"author": row['author'],
|
||||||
|
"genre": row['genre'],
|
||||||
|
"author": row['author'],
|
||||||
|
"country": row['country'],
|
||||||
|
"country_code": row['country_code'],
|
||||||
|
"pages": row['pages'],
|
||||||
|
"readed": row['readed'],
|
||||||
|
"rating": row['rating'],
|
||||||
|
})
|
||||||
|
|
||||||
|
return Response(data)
|
||||||
|
|
||||||
|
@api_view(['GET'])
|
||||||
|
def getChallengeOfYear(request):
|
||||||
|
if request.META.get('HTTP_YEAR'):
|
||||||
|
data = []
|
||||||
|
df = getBookChallenge(request.META.get('HTTP_YEAR'))
|
||||||
|
|
||||||
|
for index, row in df.iterrows():
|
||||||
|
data.append({
|
||||||
|
"year": row['year'],
|
||||||
|
"nrofbooks": row['nrofbooks']
|
||||||
|
})
|
||||||
|
|
||||||
|
return Response(data)
|
||||||
|
else:
|
||||||
|
return Response("No year header included")
|
||||||
|
|
||||||
@api_view(['GET'])
|
@api_view(['GET'])
|
||||||
def books_per_genre_per_month(request):
|
def books_per_genre_per_month(request):
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
|
import Challenge from "./components/Challenge";
|
||||||
|
import BookStats from "./components/Stats";
|
||||||
|
|
||||||
export default class App extends Component {
|
export default class App extends Component {
|
||||||
constructor(props) {
|
constructor(props) {
|
||||||
|
@ -6,18 +8,14 @@ export default class App extends Component {
|
||||||
this.state = {
|
this.state = {
|
||||||
year: new Date().getFullYear(),
|
year: new Date().getFullYear(),
|
||||||
readingYears: [],
|
readingYears: [],
|
||||||
totalbooks: 0,
|
countries: [],
|
||||||
totalpages: 0,
|
pagesStats: [],
|
||||||
totalauthors: 0,
|
|
||||||
totalcountries: 0,
|
|
||||||
totalgenres: 0,
|
|
||||||
countries: []
|
|
||||||
}
|
}
|
||||||
|
|
||||||
this.yearsArray = [];
|
this.yearsArray = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
getGenres(){
|
getGenres() {
|
||||||
fetch('/api/books/genres', {
|
fetch('/api/books/genres', {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
@ -30,7 +28,7 @@ export default class App extends Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
getCountries(init){
|
getCountries(init) {
|
||||||
fetch('/api/books/countries', {
|
fetch('/api/books/countries', {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
@ -43,7 +41,7 @@ export default class App extends Component {
|
||||||
countries: data
|
countries: data
|
||||||
})
|
})
|
||||||
|
|
||||||
if(init == true){
|
if (init == true) {
|
||||||
$('#DataTable').DataTable({
|
$('#DataTable').DataTable({
|
||||||
paging: false,
|
paging: false,
|
||||||
ordering: false,
|
ordering: false,
|
||||||
|
@ -54,33 +52,31 @@ export default class App extends Component {
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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/stats', {
|
|
||||||
"method": "GET",
|
|
||||||
"headers": {
|
|
||||||
"year": event.target.value
|
|
||||||
}
|
|
||||||
})
|
|
||||||
.then(response => response.json())
|
|
||||||
.then(data => {
|
|
||||||
this.setState({
|
|
||||||
totalbooks: data.totalbooks,
|
|
||||||
totalpages: data.totalpages,
|
|
||||||
totalauthors: data.totalauthors,
|
|
||||||
totalcountries: data.totalcountries,
|
|
||||||
totalgenres: data.totalgenres
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
fetch('/api/books/countries', {
|
fetch('/api/books/countries', {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"headers": {
|
"headers": {
|
||||||
"year": this.state.year
|
"year": event.target.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
|
@ -94,19 +90,32 @@ export default class App extends Component {
|
||||||
|
|
||||||
var $this = this;
|
var $this = this;
|
||||||
|
|
||||||
|
this.getShortestLongestBook(event.target.value);
|
||||||
|
|
||||||
fetch('/api/books/genres/count', {
|
fetch('/api/books/genres/count', {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"headers": {
|
"headers": {
|
||||||
"year": this.state.year
|
"year": event.target.value
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
this.initDoughnut(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){
|
initHorBar(data) {
|
||||||
|
|
||||||
var countries = [];
|
var countries = [];
|
||||||
var counts = [];
|
var counts = [];
|
||||||
|
@ -177,10 +186,10 @@ export default class App extends Component {
|
||||||
|
|
||||||
const legendMargin = {
|
const legendMargin = {
|
||||||
id: 'legendMargin',
|
id: 'legendMargin',
|
||||||
beforeInit(chart, legend, options){
|
beforeInit(chart, legend, options) {
|
||||||
const fitValue = chart.legend.fit;
|
const fitValue = chart.legend.fit;
|
||||||
|
|
||||||
chart.legend.fit = function fit(){
|
chart.legend.fit = function fit() {
|
||||||
fitValue.bind(chart.legend)();
|
fitValue.bind(chart.legend)();
|
||||||
return this.height += 30;
|
return this.height += 30;
|
||||||
}
|
}
|
||||||
|
@ -205,7 +214,7 @@ export default class App extends Component {
|
||||||
borderColor: '#1f2940',
|
borderColor: '#1f2940',
|
||||||
tooltip: {
|
tooltip: {
|
||||||
callbacks: {
|
callbacks: {
|
||||||
label: function(context) {
|
label: function (context) {
|
||||||
let label = context.label;
|
let label = context.label;
|
||||||
let value = context.formattedValue;
|
let value = context.formattedValue;
|
||||||
|
|
||||||
|
@ -246,15 +255,15 @@ export default class App extends Component {
|
||||||
},
|
},
|
||||||
plugins: [{
|
plugins: [{
|
||||||
id: 'legendMargin',
|
id: 'legendMargin',
|
||||||
beforeInit(chart, legend, options){
|
beforeInit(chart, legend, options) {
|
||||||
const fitValue = chart.legend.fit;
|
const fitValue = chart.legend.fit;
|
||||||
|
|
||||||
chart.legend.fit = function fit(){
|
chart.legend.fit = function fit() {
|
||||||
fitValue.bind(chart.legend)();
|
fitValue.bind(chart.legend)();
|
||||||
return this.height += 30;
|
return this.height += 30;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},{
|
}, {
|
||||||
afterDraw: chart => {
|
afterDraw: chart => {
|
||||||
var ctx = chart.ctx;
|
var ctx = chart.ctx;
|
||||||
ctx.save();
|
ctx.save();
|
||||||
|
@ -263,7 +272,7 @@ export default class App extends Component {
|
||||||
var imageSize = 80;
|
var imageSize = 80;
|
||||||
ctx.drawImage(image, chart.width / 2 - imageSize / 2, chart.height / 2 - imageSize / 6, imageSize, imageSize);
|
ctx.drawImage(image, chart.width / 2 - imageSize / 2, chart.height / 2 - imageSize / 6, imageSize, imageSize);
|
||||||
ctx.restore();
|
ctx.restore();
|
||||||
}
|
}
|
||||||
}],
|
}],
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -335,10 +344,10 @@ export default class App extends Component {
|
||||||
|
|
||||||
const legendMargin = {
|
const legendMargin = {
|
||||||
id: 'legendMargin',
|
id: 'legendMargin',
|
||||||
beforeInit(chart, legend, options){
|
beforeInit(chart, legend, options) {
|
||||||
const fitValue = chart.legend.fit;
|
const fitValue = chart.legend.fit;
|
||||||
|
|
||||||
chart.legend.fit = function fit(){
|
chart.legend.fit = function fit() {
|
||||||
fitValue.bind(chart.legend)();
|
fitValue.bind(chart.legend)();
|
||||||
return this.height += 30;
|
return this.height += 30;
|
||||||
}
|
}
|
||||||
|
@ -358,7 +367,7 @@ export default class App extends Component {
|
||||||
legend: {
|
legend: {
|
||||||
display: true,
|
display: true,
|
||||||
labels: {
|
labels: {
|
||||||
usePointStyle: true,
|
usePointStyle: true,
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
interaction: {
|
interaction: {
|
||||||
|
@ -405,15 +414,11 @@ export default class App extends Component {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate() {
|
|
||||||
this.getGenres();
|
|
||||||
}
|
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
|
|
||||||
var $this = this;
|
var $this = this;
|
||||||
|
|
||||||
var currentyear = this.state.year ? this.state.year : new Date().getFullYear()
|
var currentyear = this.state.year ? this.state.year : new Date().getFullYear();
|
||||||
|
|
||||||
fetch('/api/books/genres', {
|
fetch('/api/books/genres', {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
|
@ -426,6 +431,8 @@ export default class App extends Component {
|
||||||
this.initChart(books, currentyear);
|
this.initChart(books, currentyear);
|
||||||
})
|
})
|
||||||
|
|
||||||
|
this.getShortestLongestBook(this.state.year);
|
||||||
|
|
||||||
fetch('/api/books/genres/count', {
|
fetch('/api/books/genres/count', {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"headers": {
|
"headers": {
|
||||||
|
@ -439,46 +446,54 @@ export default class App extends Component {
|
||||||
|
|
||||||
this.getCountries(true);
|
this.getCountries(true);
|
||||||
|
|
||||||
fetch('/api/books/stats', {
|
fetch('/api/books/years', {
|
||||||
"method": "GET",
|
"method": "GET",
|
||||||
"headers": {
|
|
||||||
"year": this.state.year
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
.then(response => response.json())
|
.then(response => response.json())
|
||||||
.then(data => {
|
.then(data => {
|
||||||
$this.setState({
|
this.setState({
|
||||||
totalbooks: data.totalbooks,
|
readingYears: data
|
||||||
totalpages: data.totalpages,
|
|
||||||
totalauthors: data.totalauthors,
|
|
||||||
totalcountries: data.totalcountries,
|
|
||||||
totalgenres: data.totalgenres
|
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
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' : ''}`}>
|
||||||
<i class="fa fa-chart-bar"></i>
|
<i className="fa fa-chart-bar"></i>
|
||||||
</div>
|
</div>
|
||||||
<div className={`menu-item ${ url && url[3] == "books" ? 'selected' : ''}`}>
|
<div className={`menu-item ${url && url[3] == "books" ? 'selected' : ''}`}>
|
||||||
<i class="fa fa-book"></i>
|
<i className="fa fa-book"></i>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
|
@ -492,96 +507,85 @@ export default class App extends Component {
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div className="col-md-2">
|
<div className="col-md-2">
|
||||||
<div className="stat-block">
|
<div className="stat-block">
|
||||||
<i class="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" defaultValue={this.state.year} onChange={(event) => this.changeYear(event)}>
|
||||||
{this.state.readingYears.map((year) => {
|
{this.state.readingYears.map((year, i) => {
|
||||||
|
|
||||||
if(year === this.state.year){
|
if (year === this.state.year) {
|
||||||
var selected = 'selected'
|
var selected = 'selected'
|
||||||
}else{
|
} else {
|
||||||
selected = ''
|
selected = ''
|
||||||
}
|
}
|
||||||
|
|
||||||
return(<option selected={selected} value={year}>{year}</option>)
|
return (<option key={i} selected={selected} value={year}>{year}</option>)
|
||||||
})}
|
})}
|
||||||
</select>
|
</select>
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-md-2">
|
<BookStats year={this.state.year} />
|
||||||
<div className="stat-block">
|
|
||||||
<i class="fa fa-book"></i>
|
|
||||||
<span className="stats-number">{this.state.totalbooks}</span>
|
|
||||||
<span className="stats-label">Boeken</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-md-2">
|
|
||||||
<div className="stat-block">
|
|
||||||
<i class="fa fa-book-open"></i>
|
|
||||||
<span className="stats-number">{this.state.totalpages}</span>
|
|
||||||
<span className="stats-label">Bladzijdes</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-md-2">
|
|
||||||
<div className="stat-block">
|
|
||||||
<i class="fa fa-pen"></i>
|
|
||||||
<span className="stats-number">{this.state.totalauthors}</span>
|
|
||||||
<span className="stats-label">Schrijvers</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-md-2">
|
|
||||||
<div className="stat-block">
|
|
||||||
<i class="fa fa-book"></i>
|
|
||||||
<span className="stats-number">{this.state.totalgenres}</span>
|
|
||||||
<span className="stats-label">Genres</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div className="col-md-2">
|
|
||||||
<div className="stat-block">
|
|
||||||
<i class="fa fa-globe"></i>
|
|
||||||
<span className="stats-number">{this.state.totalcountries}</span>
|
|
||||||
<span className="stats-label">Landen</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<Challenge year={this.state.year} />
|
||||||
|
|
||||||
<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>
|
<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>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-md-3">
|
<div className="col-md-3">
|
||||||
<div className="books-per-country">
|
<div className="books-per-country">
|
||||||
<span className="block_name">Landen</span>
|
<span className="block_name">Landen</span>
|
||||||
<table id="DataTable" class="showHead table responsive nowrap" width="100%">
|
<table id="DataTable" className="showHead table responsive nowrap" width="100%">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>#</th>
|
<th>#</th>
|
||||||
<th>Land</th>
|
<th>Land</th>
|
||||||
<th>Boeken</th>
|
<th>Boeken</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{this.state.countries.map((country, i) => {
|
{this.state.countries.map((country, i) => {
|
||||||
|
|
||||||
var code = country.code.toLowerCase();
|
var code = country.code.toLowerCase();
|
||||||
return(
|
return (
|
||||||
<React.Fragment>
|
<React.Fragment>
|
||||||
<tr>
|
<tr key="{i}">
|
||||||
<td>{i+1}</td>
|
<td>{i + 1}</td>
|
||||||
<td><img src={`https://flagcdn.com/32x24/${code}.png`} /> {country.country}</td>
|
<td><img src={`https://flagcdn.com/32x24/${code}.png`} /> {country.country}</td>
|
||||||
<td>{country.count}</td>
|
<td>{country.count}</td>
|
||||||
</tr>
|
</tr>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
|
|
||||||
})}
|
})}
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
</div>
|
</div>
|
||||||
<div className="genresPercent"><span className="block_name">Genres</span><canvas id="chartGenres"></canvas></div>
|
<div className="genresPercent"><span className="block_name">Genres</span><canvas id="chartGenres"></canvas></div>
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { getChallenge, getStats } from "./Data.js";
|
||||||
|
|
||||||
|
export default class Challenge extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
readingYears: [],
|
||||||
|
challenge: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentData(){
|
||||||
|
var $this = this;
|
||||||
|
|
||||||
|
getStats(this.props.year).then(data => {
|
||||||
|
$this.setState({
|
||||||
|
totalbooks: data.totalbooks
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
getChallenge(this.props.year).then(data => {
|
||||||
|
this.setState({
|
||||||
|
challenge: data && data.length > 0 ? data[0].nrofbooks : 0
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevProps.year !== this.props.year) {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
var challengePercentage = (this.state.totalbooks / this.state.challenge) * 100
|
||||||
|
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
{this.state.challenge && this.state.challenge !== 0 ?
|
||||||
|
<div className="container-fluid">
|
||||||
|
<div className="row">
|
||||||
|
<div className="col-md-12">
|
||||||
|
<div className="stat-block">
|
||||||
|
<span className="block_name">Book Challenge</span>
|
||||||
|
<div className="progress">
|
||||||
|
<div className="progress-bar progress-bar-striped" role="progressbar" style={{ width: challengePercentage + '%' }} aria-valuenow={challengePercentage} aria-valuemin="0" aria-valuemax="100">
|
||||||
|
<div className="progress-bar-number">{challengePercentage}%</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<span className="stats-number">{this.state.totalbooks}</span><span className="stats-label">van de</span><span className="stats-number">{this.state.challenge}</span><span className="stats-label">boeken gelezen</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
: ''}
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,35 @@
|
||||||
|
export const getStats = (year) => {
|
||||||
|
return fetch('/api/books/stats', {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"year": year
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
return data;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getChallenge = (year) => {
|
||||||
|
return fetch('/api/books/challenge', {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"year": year
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getReadingYears = () => {
|
||||||
|
return fetch('/api/books/years', {
|
||||||
|
"method": "GET",
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
return data
|
||||||
|
})
|
||||||
|
}
|
|
@ -0,0 +1,88 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { getStats, getReadingYears } from "./Data.js";
|
||||||
|
|
||||||
|
export default class BookStats extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
readingYears: [],
|
||||||
|
totalbooks: 0,
|
||||||
|
totalpages: 0,
|
||||||
|
totalauthors: 0,
|
||||||
|
totalcountries: 0,
|
||||||
|
totalgenres: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentData(){
|
||||||
|
var $this = this;
|
||||||
|
|
||||||
|
getStats(this.props.year).then(data => {
|
||||||
|
$this.setState({
|
||||||
|
totalbooks: data.totalbooks,
|
||||||
|
totalpages: data.totalpages,
|
||||||
|
totalauthors: data.totalauthors,
|
||||||
|
totalcountries: data.totalcountries,
|
||||||
|
totalgenres: data.totalgenres
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
getReadingYears().then(data => {
|
||||||
|
this.setState({
|
||||||
|
readingYears: data
|
||||||
|
})
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevProps.year !== this.props.year) {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<div className="stat-block">
|
||||||
|
<i className="fa fa-book"></i>
|
||||||
|
<span className="stats-number">{this.state.totalbooks}</span>
|
||||||
|
<span className="stats-label">Boeken</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<div className="stat-block">
|
||||||
|
<i className="fa fa-book-open"></i>
|
||||||
|
<span className="stats-number">{this.state.totalpages}</span>
|
||||||
|
<span className="stats-label">Bladzijdes</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<div className="stat-block">
|
||||||
|
<i className="fa fa-pen"></i>
|
||||||
|
<span className="stats-number">{this.state.totalauthors}</span>
|
||||||
|
<span className="stats-label">Schrijvers</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<div className="stat-block">
|
||||||
|
<i className="fa fa-book"></i>
|
||||||
|
<span className="stats-number">{this.state.totalgenres}</span>
|
||||||
|
<span className="stats-label">Genres</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div className="col-md-2">
|
||||||
|
<div className="stat-block">
|
||||||
|
<i className="fa fa-globe"></i>
|
||||||
|
<span className="stats-number">{this.state.totalcountries}</span>
|
||||||
|
<span className="stats-label">Landen</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because one or more lines are too long
|
@ -6,10 +6,22 @@
|
||||||
!*** ./src/index.js ***!
|
!*** ./src/index.js ***!
|
||||||
\**********************/
|
\**********************/
|
||||||
|
|
||||||
|
/*!********************************!*\
|
||||||
|
!*** ./src/components/Data.js ***!
|
||||||
|
\********************************/
|
||||||
|
|
||||||
|
/*!*********************************!*\
|
||||||
|
!*** ./src/components/Stats.js ***!
|
||||||
|
\*********************************/
|
||||||
|
|
||||||
/*!*************************************!*\
|
/*!*************************************!*\
|
||||||
!*** ./node_modules/react/index.js ***!
|
!*** ./node_modules/react/index.js ***!
|
||||||
\*************************************/
|
\*************************************/
|
||||||
|
|
||||||
|
/*!*************************************!*\
|
||||||
|
!*** ./src/components/Challenge.js ***!
|
||||||
|
\*************************************/
|
||||||
|
|
||||||
/*!*****************************************!*\
|
/*!*****************************************!*\
|
||||||
!*** ./node_modules/react-dom/index.js ***!
|
!*** ./node_modules/react-dom/index.js ***!
|
||||||
\*****************************************/
|
\*****************************************/
|
||||||
|
|
|
@ -73,7 +73,7 @@
|
||||||
height:600px !important;
|
height:600px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.books-per-month, .genresPercent, .books-per-country{
|
.books-per-month, .genresPercent, .books-per-country, .book{
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
padding: 20px;
|
padding: 20px;
|
||||||
box-shadow: 0 2px 0px 1px rgb(0 0 0 / 3%);
|
box-shadow: 0 2px 0px 1px rgb(0 0 0 / 3%);
|
||||||
|
@ -81,6 +81,33 @@
|
||||||
border-radius: 10px;
|
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{
|
.sidebar{
|
||||||
background: #363a53;
|
background: #363a53;
|
||||||
width: 70px;
|
width: 70px;
|
||||||
|
@ -116,7 +143,7 @@
|
||||||
padding: 50px;
|
padding: 50px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.books-stats .stat-block{
|
.books-stats .stat-block, .stat-block{
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
box-shadow: 0 2px 0px 1px rgb(0 0 0 / 3%);
|
box-shadow: 0 2px 0px 1px rgb(0 0 0 / 3%);
|
||||||
padding: 15px 5px;
|
padding: 15px 5px;
|
||||||
|
@ -163,7 +190,7 @@
|
||||||
/* box-shadow: 2px 2px 0px 0px rgba(0, 0, 0, 0.3); */
|
/* box-shadow: 2px 2px 0px 0px rgba(0, 0, 0, 0.3); */
|
||||||
}
|
}
|
||||||
|
|
||||||
.books-stats .stat-block .stats-number{
|
.books-stats .stat-block .stats-number, .stats-number{
|
||||||
font-weight: 600;
|
font-weight: 600;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
|
@ -171,7 +198,7 @@
|
||||||
margin-right: 10px;
|
margin-right: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.books-stats .stat-block .stats-label{
|
.books-stats .stat-block .stats-label, .stats-label{
|
||||||
color: #a7adbd;
|
color: #a7adbd;
|
||||||
font-weight: 400;
|
font-weight: 400;
|
||||||
font-size: 20px;
|
font-size: 20px;
|
||||||
|
@ -218,6 +245,34 @@
|
||||||
margin-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>
|
</style>
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue