UI design + Ratings block
This commit is contained in:
parent
e7551b0599
commit
c0e78bee2d
Binary file not shown.
|
@ -19,6 +19,7 @@ urlpatterns = [
|
||||||
path('books/genres', books_per_genre_per_month),
|
path('books/genres', books_per_genre_per_month),
|
||||||
path('books/genres/count', countGenres),
|
path('books/genres/count', countGenres),
|
||||||
path('books/ratings', avg_ratings_per_month),
|
path('books/ratings', avg_ratings_per_month),
|
||||||
|
path('books/ratings/count', countRatings),
|
||||||
path('books/authors', books_per_author),
|
path('books/authors', books_per_author),
|
||||||
path('books/countries', books_per_country),
|
path('books/countries', books_per_country),
|
||||||
|
|
||||||
|
|
|
@ -296,6 +296,30 @@ def avg_ratings_per_month(request):
|
||||||
"rating": int(row['rating'])
|
"rating": int(row['rating'])
|
||||||
})
|
})
|
||||||
|
|
||||||
|
return Response(data)
|
||||||
|
else:
|
||||||
|
return Response("No year header included")
|
||||||
|
|
||||||
|
@api_view(['GET'])
|
||||||
|
def countRatings(request):
|
||||||
|
datayear = request.META.get('HTTP_YEAR')
|
||||||
|
|
||||||
|
if datayear:
|
||||||
|
data = []
|
||||||
|
|
||||||
|
# Get CSV file with book data
|
||||||
|
df = filterData(getBooksData(), request.META.get('HTTP_YEAR'))
|
||||||
|
|
||||||
|
countratings = df.groupby('rating')['rating'].count().reset_index(name="count")
|
||||||
|
countratings = countratings.sort_values(by='rating', ascending=False)
|
||||||
|
|
||||||
|
for index, row in countratings.iterrows():
|
||||||
|
|
||||||
|
data.append({
|
||||||
|
"rating": int(row['rating']),
|
||||||
|
"count": int(row['count'])
|
||||||
|
})
|
||||||
|
|
||||||
return Response(data)
|
return Response(data)
|
||||||
else:
|
else:
|
||||||
return Response("No year header included")
|
return Response("No year header included")
|
Binary file not shown.
Binary file not shown.
|
@ -107,4 +107,17 @@ export const getAvgRatings = (year) => {
|
||||||
.then(data => {
|
.then(data => {
|
||||||
return data;
|
return data;
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getRatingsCount = (year) => {
|
||||||
|
return fetch('/api/books/ratings/count', {
|
||||||
|
"method": "GET",
|
||||||
|
"headers": {
|
||||||
|
"year": year
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.then(response => response.json())
|
||||||
|
.then(data => {
|
||||||
|
return data;
|
||||||
|
})
|
||||||
}
|
}
|
|
@ -0,0 +1,104 @@
|
||||||
|
import React, { Component } from 'react';
|
||||||
|
import { getGenresCount, getRatingsCount } from "./Data.js";
|
||||||
|
import { initDoughnut } from "./Charts.js";
|
||||||
|
|
||||||
|
export default class Ratings extends Component {
|
||||||
|
constructor(props) {
|
||||||
|
super(props);
|
||||||
|
this.state = {
|
||||||
|
ratings: [],
|
||||||
|
totalRatings: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
getComponentData() {
|
||||||
|
getRatingsCount(this.props.year).then(ratings => {
|
||||||
|
|
||||||
|
var total = 0;
|
||||||
|
|
||||||
|
ratings.forEach(rating => {
|
||||||
|
total += rating.count;
|
||||||
|
})
|
||||||
|
|
||||||
|
var ratingsArray = {
|
||||||
|
5: 0,
|
||||||
|
4: 0,
|
||||||
|
3: 0,
|
||||||
|
2: 0,
|
||||||
|
1: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
for(var i = 5; i > 0; i--){
|
||||||
|
ratings.forEach(rating => {
|
||||||
|
if(rating.rating === i){
|
||||||
|
ratingsArray[i] = rating.count
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log(Object.entries(ratingsArray));
|
||||||
|
|
||||||
|
this.setState({
|
||||||
|
ratings: Object.entries(ratingsArray),
|
||||||
|
totalRatings: total
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidMount() {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
|
||||||
|
componentDidUpdate(prevProps, prevState) {
|
||||||
|
if (prevProps.year !== this.props.year) {
|
||||||
|
this.getComponentData();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<div className="ratings">
|
||||||
|
<span className="block_name">Waarderingen ({this.state.totalRatings})</span>
|
||||||
|
<table id="DataTable" className="table responsive nowrap" width="100%">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>#</th>
|
||||||
|
<th>percentage</th>
|
||||||
|
<th>aantal</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
{this.state.ratings.map((rating) => {
|
||||||
|
var ratingstars = '';
|
||||||
|
var rating_percentage = rating[1] / this.state.totalRatings * 100;
|
||||||
|
|
||||||
|
console.log(rating[1], this.state.totalRatings);
|
||||||
|
|
||||||
|
if (rating[0]) {
|
||||||
|
for (var i = 0; i < rating[0]; i++) {
|
||||||
|
ratingstars += "<i class='fas fa-star'></i>";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return(
|
||||||
|
<tr>
|
||||||
|
<td style={{width: '120px'}} className='book_rating' dangerouslySetInnerHTML={{__html: ratingstars}}></td>
|
||||||
|
<td style={{width: '257px'}}>
|
||||||
|
<div className="progress">
|
||||||
|
<div className="progress-bar progress-bar-striped" role="progressbar" style={{ width: rating_percentage + '%' }} aria-valuenow={rating_percentage} aria-valuemin="0" aria-valuemax="100">
|
||||||
|
{/* <div className="progress-bar-number">{rating_percentage}%</div> */}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</td>
|
||||||
|
<td>{rating[1]}</td>
|
||||||
|
</tr>
|
||||||
|
)
|
||||||
|
})}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
</React.Fragment>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
|
@ -9,6 +9,7 @@ function Sidebar(){
|
||||||
<li><NavLink to="/"><i className="fa fa-chart-bar"></i> Dashboard</NavLink></li>
|
<li><NavLink to="/"><i className="fa fa-chart-bar"></i> Dashboard</NavLink></li>
|
||||||
<li><NavLink to="/booklist"><i className="fas fa-book"></i> Boekenlijst</NavLink></li>
|
<li><NavLink to="/booklist"><i className="fas fa-book"></i> Boekenlijst</NavLink></li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
</div>
|
</div>
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
)
|
)
|
||||||
|
|
|
@ -6,8 +6,9 @@ import Countries from "../components/Countries";
|
||||||
import Pages from "../components/Pages";
|
import Pages from "../components/Pages";
|
||||||
import Genres from "../components/Genres";
|
import Genres from "../components/Genres";
|
||||||
import Books from "../components/Books";
|
import Books from "../components/Books";
|
||||||
import { getReadingYears } from "../components/Data.js";
|
import { getRatingsCount, getReadingYears } from "../components/Data.js";
|
||||||
import Sidebar from "../components/Sidebar";
|
import Sidebar from "../components/Sidebar";
|
||||||
|
import Ratings from "../components/Ratings";
|
||||||
|
|
||||||
export default class Dashboard extends Component {
|
export default class Dashboard extends Component {
|
||||||
|
|
||||||
|
@ -31,6 +32,10 @@ export default class Dashboard extends Component {
|
||||||
readingYears: data
|
readingYears: data
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
|
getRatingsCount(this.state.year).then(data => {
|
||||||
|
console.log(data);
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
|
@ -65,14 +70,15 @@ export default class Dashboard 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-8">
|
||||||
<Books year={this.state.year} />
|
<Books year={this.state.year} />
|
||||||
<Pages year={this.state.year} />
|
<Pages year={this.state.year} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className="col-md-3">
|
<div className="col-md-4">
|
||||||
<Countries year={this.state.year} />
|
<Countries year={this.state.year} />
|
||||||
<Genres year={this.state.year} />
|
<Genres year={this.state.year} />
|
||||||
|
<Ratings year={this.state.year} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
Binary file not shown.
|
@ -47,7 +47,7 @@ html, body{
|
||||||
height:600px !important;
|
height:600px !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.books-per-month, .genresPercent, .books-per-country, .book{
|
.books-per-month, .genresPercent, .books-per-country, .book, .ratings{
|
||||||
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%);
|
||||||
|
@ -85,7 +85,7 @@ html, body{
|
||||||
.sidebar{
|
.sidebar{
|
||||||
background: #363a53;
|
background: #363a53;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
height: 54px;
|
padding: 15px 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar .menu-item{
|
.sidebar .menu-item{
|
||||||
|
@ -108,6 +108,7 @@ html, body{
|
||||||
|
|
||||||
.sidebar ul {
|
.sidebar ul {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
|
margin:0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar ul li {
|
.sidebar ul li {
|
||||||
|
@ -120,21 +121,19 @@ html, body{
|
||||||
.sidebar ul li a {
|
.sidebar ul li a {
|
||||||
color: #a9b7d0;
|
color: #a9b7d0;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
padding: 15px 0;
|
padding: 12px 15px;
|
||||||
padding-left: 15px;
|
|
||||||
padding-right: 15px;
|
|
||||||
display:block;
|
display:block;
|
||||||
width:100%;
|
width:100%;
|
||||||
|
border-radius: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar ul li:has(> a.active) {
|
.sidebar ul li:has(> a.active) {
|
||||||
background: #333f54 !important;
|
background: #333f54 !important;
|
||||||
border-bottom: solid 3px #8066ee;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar ul li a.active {
|
.sidebar ul li a.active {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background: rgba(0,0,0,0.2) !important;
|
background: rgba(0,0,0,0.3) !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebar ul li a.active i {
|
.sidebar ul li a.active i {
|
||||||
|
@ -148,7 +147,7 @@ html, body{
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
line-height: 1;
|
line-height: 1;
|
||||||
margin-right: 1.125rem;
|
margin-right: 12px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.books-stats{
|
.books-stats{
|
||||||
|
@ -268,7 +267,7 @@ html, body{
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress{
|
.stat-block .progress{
|
||||||
background: #f8f8fa;
|
background: #f8f8fa;
|
||||||
height: 50px;
|
height: 50px;
|
||||||
border: solid 2px #efefef;
|
border: solid 2px #efefef;
|
||||||
|
@ -279,14 +278,20 @@ html, body{
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar{
|
.stat-block .progress-bar{
|
||||||
background-color: #8066ee;
|
background-color: #8066ee;
|
||||||
position: relative;
|
position: relative;
|
||||||
overflow: visible;
|
overflow: visible;
|
||||||
border-right: solid 2px #333;
|
border-right: solid 2px #333;
|
||||||
}
|
}
|
||||||
|
|
||||||
.progress-bar-number{
|
.progress-bar{
|
||||||
|
background-color: #8066ee;
|
||||||
|
position: relative;
|
||||||
|
overflow: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
.stat-block .progress-bar-number{
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 0;
|
right: 0;
|
||||||
background: #333;
|
background: #333;
|
||||||
|
|
|
@ -1,2 +1,2 @@
|
||||||
/*! For license information please see src_components_Data_js.js.LICENSE.txt */
|
/*! For license information please see src_components_Data_js.js.LICENSE.txt */
|
||||||
"use strict";(self.webpackChunkfrontend=self.webpackChunkfrontend||[]).push([["src_components_Data_js"],{"./src/components/Data.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{eval('__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ "getAllBooks": () => (/* binding */ getAllBooks),\n/* harmony export */ "getAvgRatings": () => (/* binding */ getAvgRatings),\n/* harmony export */ "getBooksPerYearPerGenres": () => (/* binding */ getBooksPerYearPerGenres),\n/* harmony export */ "getChallenge": () => (/* binding */ getChallenge),\n/* harmony export */ "getCountries": () => (/* binding */ getCountries),\n/* harmony export */ "getGenresCount": () => (/* binding */ getGenresCount),\n/* harmony export */ "getReadingYears": () => (/* binding */ getReadingYears),\n/* harmony export */ "getShortestLongestBook": () => (/* binding */ getShortestLongestBook),\n/* harmony export */ "getStats": () => (/* binding */ getStats)\n/* harmony export */ });\nconst getAllBooks = () => {\n return fetch(\'/api/books\', {\n "method": "GET"\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getStats = year => {\n return fetch(\'/api/books/stats\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getChallenge = year => {\n return fetch(\'/api/books/challenge\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getReadingYears = () => {\n return fetch(\'/api/books/years\', {\n "method": "GET"\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getCountries = year => {\n return fetch(\'/api/books/countries\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getShortestLongestBook = year => {\n return fetch(\'/api/books/pages/stats\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getBooksPerYearPerGenres = year => {\n return fetch(\'/api/books/genres\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getGenresCount = year => {\n return fetch(\'/api/books/genres/count\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getAvgRatings = year => {\n return fetch(\'/api/books/ratings\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\n\n//# sourceURL=webpack://frontend/./src/components/Data.js?')}}]);
|
"use strict";(self.webpackChunkfrontend=self.webpackChunkfrontend||[]).push([["src_components_Data_js"],{"./src/components/Data.js":(__unused_webpack_module,__webpack_exports__,__webpack_require__)=>{eval('__webpack_require__.r(__webpack_exports__);\n/* harmony export */ __webpack_require__.d(__webpack_exports__, {\n/* harmony export */ "getAllBooks": () => (/* binding */ getAllBooks),\n/* harmony export */ "getAvgRatings": () => (/* binding */ getAvgRatings),\n/* harmony export */ "getBooksPerYearPerGenres": () => (/* binding */ getBooksPerYearPerGenres),\n/* harmony export */ "getChallenge": () => (/* binding */ getChallenge),\n/* harmony export */ "getCountries": () => (/* binding */ getCountries),\n/* harmony export */ "getGenresCount": () => (/* binding */ getGenresCount),\n/* harmony export */ "getRatingsCount": () => (/* binding */ getRatingsCount),\n/* harmony export */ "getReadingYears": () => (/* binding */ getReadingYears),\n/* harmony export */ "getShortestLongestBook": () => (/* binding */ getShortestLongestBook),\n/* harmony export */ "getStats": () => (/* binding */ getStats)\n/* harmony export */ });\nconst getAllBooks = () => {\n return fetch(\'/api/books\', {\n "method": "GET"\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getStats = year => {\n return fetch(\'/api/books/stats\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getChallenge = year => {\n return fetch(\'/api/books/challenge\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getReadingYears = () => {\n return fetch(\'/api/books/years\', {\n "method": "GET"\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getCountries = year => {\n return fetch(\'/api/books/countries\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getShortestLongestBook = year => {\n return fetch(\'/api/books/pages/stats\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getBooksPerYearPerGenres = year => {\n return fetch(\'/api/books/genres\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getGenresCount = year => {\n return fetch(\'/api/books/genres/count\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getAvgRatings = year => {\n return fetch(\'/api/books/ratings\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\nconst getRatingsCount = year => {\n return fetch(\'/api/books/ratings/count\', {\n "method": "GET",\n "headers": {\n "year": year\n }\n }).then(response => response.json()).then(data => {\n return data;\n });\n};\n\n//# sourceURL=webpack://frontend/./src/components/Data.js?')}}]);
|
File diff suppressed because one or more lines are too long
|
@ -26,6 +26,10 @@
|
||||||
!*** ./src/components/Genres.js ***!
|
!*** ./src/components/Genres.js ***!
|
||||||
\**********************************/
|
\**********************************/
|
||||||
|
|
||||||
|
/*!***********************************!*\
|
||||||
|
!*** ./src/components/Ratings.js ***!
|
||||||
|
\***********************************/
|
||||||
|
|
||||||
/*!***********************************!*\
|
/*!***********************************!*\
|
||||||
!*** ./src/components/Sidebar.js ***!
|
!*** ./src/components/Sidebar.js ***!
|
||||||
\***********************************/
|
\***********************************/
|
||||||
|
|
Loading…
Reference in New Issue