1
1
Fork 0

Challenges + manage books

This commit is contained in:
Jordy van Zeeland 2023-11-13 16:09:59 +01:00
parent 5dd2ad780c
commit 9a33be5460
23 changed files with 1192 additions and 326 deletions

View File

@ -0,0 +1,122 @@
import jwt, json
from django.contrib.auth import get_user_model
from rest_framework.decorators import api_view
import ras.settings
from sqlalchemy import create_engine
from sqlalchemy.sql import text
from django.http import JsonResponse
import pandas as pd
from rest_framework.response import Response
def filterData(df, datayear = None):
df['readed'] = pd.to_datetime(df['readed'], format='%Y-%m-%d')
df['readed'] = df['readed'].dt.strftime('%m-%Y')
# Filter data on year
if datayear and datayear is not None:
df = df.where(df['readed'].str.contains(datayear))
return df
def getBooksData():
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'])
df = pd.read_sql('SELECT * FROM api_books ORDER BY readed', engine, parse_dates={'readed': {'format': '%m-%Y'}})
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
@api_view(['GET'])
def getAllChallenges(request):
data = []
df = getBookChallenge()
for index, row in df.iterrows():
books = filterData(getBooksData(), str(row['year']))
books = books.dropna()
totalBooksRead = books['name'].count()
data.append({
"id": row['id'],
"year": row['year'],
"nrofbooks": row['nrofbooks'],
"booksread": totalBooksRead
})
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(['POST'])
def addChallenge(request):
if(request.headers.get('Authorization')):
token = request.headers.get('Authorization').split(' ')[1]
try:
User = get_user_model()
payload = jwt.decode(token, 'secret', algorithms=['HS256'])
user = User.objects.get(id=payload['id'])
if(user):
year = request.POST.get('year')
challenge = request.POST.get('challenge')
if(year and challenge):
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'])
conn = engine.connect()
conn.execute(text("INSERT INTO book_challenge (year, nrofbooks) VALUES ('" + str(year) + "', '" + str(challenge) + "')"))
return JsonResponse("OK", safe=False)
else:
return JsonResponse({'error': 'No year and challenge detected'}, safe=False)
else:
return JsonResponse({'error': 'No user detected'}, safe=False)
except (jwt.DecodeError, User.DoesNotExist):
return JsonResponse({'error': 'Token invalid'}, safe=False)
@api_view(['DELETE'])
def deleteChallenge(request, id = None):
if(request.headers.get('Authorization')):
token = request.headers.get('Authorization').split(' ')[1]
try:
User = get_user_model()
payload = jwt.decode(token, 'secret', algorithms=['HS256'])
user = User.objects.get(id=payload['id'])
if(user):
if(id):
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'])
conn = engine.connect()
conn.execute(text("DELETE FROM book_challenge WHERE id = " + str(id)))
return JsonResponse("OK", safe=False)
else:
return JsonResponse({'error': 'No challengeid detected'}, safe=False)
else:
return JsonResponse({'error': 'No user detected'}, safe=False)
except (jwt.DecodeError, User.DoesNotExist):
return JsonResponse({'error': 'Token invalid'}, safe=False)

View File

@ -8,17 +8,26 @@ from django.http import JsonResponse
import pandas as pd
from rest_framework.response import Response
def getBooksData():
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'])
df = pd.read_sql('SELECT * FROM api_books ORDER BY readed', engine, parse_dates={'readed': {'format': '%m-%Y'}})
conn = engine.connect()
return df
# -------------------------------
# Get all books in the database
# -------------------------------
@api_view(['GET'])
def getAllBooks(request):
if(request.headers.get('Authorization')):
token = request.headers.get('Authorization').split(' ')[1]
try:
User = get_user_model()
payload = jwt.decode(token, 'secret', algorithms=['HS256'])
user = User.objects.get(id=payload['id'])
if(user):
books = pd.read_sql('SELECT * FROM api_books ORDER BY readed', engine, parse_dates={'readed': {'format': '%m-%Y'}})
data = []
books = getBooksData()
for index, row in books.iterrows():
data.append({
@ -35,6 +44,17 @@ def getAllBooks(request):
})
return Response(data)
else:
return JsonResponse({'error': 'No user detected'}, safe=False)
except (jwt.DecodeError, User.DoesNotExist):
return JsonResponse({'error': 'Token invalid'}, safe=False)
else:
return JsonResponse({'error': 'Unauthorized'}, safe=False)
# -------------------------------
# Add a book into the database
# -------------------------------
@api_view(['POST'])
def addBook(request):
@ -49,8 +69,6 @@ def addBook(request):
user = User.objects.get(id=payload['id'])
if(user):
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'])
conn = engine.connect()
conn.execute(text("INSERT INTO api_books (name, author, genre, country, country_code, pages, readed, rating) VALUES ('" + str(book['name']) + "', '" + str(book['author']) + "', '" + str(book['genre']) + "', '" + str(book['country']) + "', '" + str(book['country_code']) + "', " + str(book['pages']) + ", '" + str(book['readed']) + "', " + str(book['rating']) + ")"))
return JsonResponse("OK", safe=False)
else:
@ -59,7 +77,11 @@ def addBook(request):
except (jwt.DecodeError, User.DoesNotExist):
return JsonResponse({'error': 'Token invalid'}, safe=False)
else:
return JsonResponse({'error': 'testing'}, safe=False)
return JsonResponse({'error': 'Unauthorized'}, safe=False)
# -------------------------------
# Update a book in the database
# -------------------------------
@api_view(['PUT'])
def updateBook(request):
@ -75,10 +97,7 @@ def updateBook(request):
user = User.objects.get(id=payload['id'])
if(user):
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'])
conn = engine.connect()
conn.execute(text("UPDATE api_books set name='" + str(book['name']) + "', author='" + str(book['author']) + "', genre='" + str(book['genre']) + "', country='" + str(book['country']) + "', country_code='" + str(book['country_code']) + "', pages='" + str(book['pages']) + "', readed='" + str(book['readed']) + "', rating='" + str(book['rating']) + "' WHERE id=" + str(bookid)))
return JsonResponse("OK", safe=False)
else:
return JsonResponse({'error': 'No user detected'}, safe=False)
@ -86,7 +105,12 @@ def updateBook(request):
except (jwt.DecodeError, User.DoesNotExist):
return JsonResponse({'error': 'Token invalid'}, safe=False)
else:
return JsonResponse({'error': 'No Token'}, safe=False)
return JsonResponse({'error': 'Unauthorized'}, safe=False)
# -------------------------------
# Delete a book in the database
# -------------------------------
@api_view(['DELETE'])
def deleteBook(request):
@ -100,8 +124,6 @@ def deleteBook(request):
user = User.objects.get(id=payload['id'])
if(user):
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'])
conn = engine.connect()
conn.execute(text("DELETE FROM api_books WHERE id = " + str(bookid)))
return JsonResponse("OK", safe=False)
else:
@ -110,4 +132,4 @@ def deleteBook(request):
except (jwt.DecodeError, User.DoesNotExist):
return JsonResponse({'error': 'Token invalid'}, safe=False)
else:
return JsonResponse({'error': 'No Token'}, safe=False)
return JsonResponse({'error': 'Unauthorized'}, safe=False)

View File

@ -0,0 +1,37 @@
from sqlalchemy import create_engine
import ras.settings
import pandas as pd
from django.contrib.auth import get_user_model
import jwt
from django.http import JsonResponse
def isAuthorized(authtoken):
if(authtoken):
token = authtoken.split(' ')[1]
User = get_user_model()
payload = jwt.decode(token, 'secret', algorithms=['HS256'])
user = User.objects.get(id=payload['id'])
if(user):
return True;
else:
return False;
else:
return JsonResponse({'error': 'Unauthorized'}, safe=False)
def getBooksData():
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'])
df = pd.read_sql('SELECT * FROM api_books ORDER BY readed', engine, parse_dates={'readed': {'format': '%m-%Y'}})
return df
def filterData(df, datayear = None):
df['readed'] = pd.to_datetime(df['readed'], format='%Y-%m-%d')
df['readed'] = df['readed'].dt.strftime('%m-%Y')
# Filter data on year
if datayear and datayear is not None:
df = df.where(df['readed'].str.contains(datayear))
return df

View File

@ -1,71 +1,44 @@
from rest_framework.decorators import api_view
from sqlalchemy import create_engine
import ras.settings
import pandas as pd
import math
from rest_framework.response import Response
from django.http import JsonResponse
from .functions import isAuthorized, getBooksData, filterData
def getBooksData():
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'])
df = pd.read_sql('SELECT * FROM api_books ORDER BY readed', engine, parse_dates={'readed': {'format': '%m-%Y'}})
return df
def filterData(df, datayear = None):
df['readed'] = pd.to_datetime(df['readed'], format='%Y-%m-%d')
df['readed'] = df['readed'].dt.strftime('%m-%Y')
# Filter data on year
if datayear and datayear is not None:
df = df.where(df['readed'].str.contains(datayear))
return df
# ----------------------
# Get all reading years
# ----------------------
@api_view(['GET'])
def getYears(request):
df = filterData(getBooksData())
if(request.headers.get('Authorization')):
isLoggedIn = isAuthorized(request.headers.get('Authorization'));
if(isLoggedIn):
df = filterData(getBooksData())
df['readed'] = pd.to_datetime(df['readed'], errors='coerce')
df['year']= df['readed'].dt.year
years = df.groupby('year')['year'].count().reset_index(name="count")
return Response(years['year'])
else:
return JsonResponse({'error': 'Unauthorized'}, safe=False)
else:
return JsonResponse({'error': 'No authorization token'}, safe=False)
@api_view(['GET'])
def getBooksByYear(request):
if request.META.get('HTTP_YEAR'):
data = []
df = getBooksData()
df['readed'] = pd.to_datetime(df['readed'], format='%Y-%m-%d')
df['readed'] = df['readed'].dt.strftime('%Y-%m-%d')
df = df.where(df['readed'].str.contains(request.META.get('HTTP_YEAR')))
df = df.fillna('')
for index, row in df.iterrows():
if row['id'] and row['id'] != '':
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)
# ------------------------------------------------------------------
# Get books of selected year and filter it per month and per genre
# ------------------------------------------------------------------
@api_view(['GET'])
def books_per_genre_per_month(request):
if request.META.get('HTTP_YEAR'):
if(request.headers.get('Authorization')):
isLoggedIn = isAuthorized(request.headers.get('Authorization'));
if(isLoggedIn):
if request.META.get('HTTP_YEAR'):
data = []
df = filterData(getBooksData(), request.META.get('HTTP_YEAR'))
# Filter array on genre and date
booksPerMonth = df.groupby(['genre','readed'])['genre'].count().reset_index(name="count")
booksPerMonth = booksPerMonth.sort_values(by=['genre', 'readed', 'count'], ascending=False)
@ -78,10 +51,22 @@ def books_per_genre_per_month(request):
return Response(data)
else:
return Response("No year header included")
return JsonResponse({'error': 'No year in header'}, safe=False)
else:
return JsonResponse({'error': 'Unauthorized'}, safe=False)
else:
return JsonResponse({'error': 'No authorization token'}, safe=False)
# ---------------------------------------------
# Get genres of selected year with percentages
# ---------------------------------------------
@api_view(['GET'])
def countGenres(request):
if(request.headers.get('Authorization')):
isLoggedIn = isAuthorized(request.headers.get('Authorization'));
if(isLoggedIn):
if request.META.get('HTTP_YEAR'):
data = []
@ -91,7 +76,6 @@ def countGenres(request):
genres = genres.sort_values(by='count', ascending=False)
for index, row in genres.iterrows():
data.append({
"genre": row['genre'],
"count": int(row['count'])
@ -99,64 +83,60 @@ def countGenres(request):
return Response(data)
else:
return Response("No year header included")
return JsonResponse({'error': 'No year in header'}, safe=False)
else:
return JsonResponse({'error': 'Unauthorized'}, safe=False)
else:
return JsonResponse({'error': 'No authorization token'}, safe=False)
# ----------------
# Get year stats
# ----------------
@api_view(['GET'])
def getStats(request):
if(request.headers.get('Authorization')):
isLoggedIn = isAuthorized(request.headers.get('Authorization'));
if(isLoggedIn):
if request.META.get('HTTP_YEAR'):
data = []
df = filterData(getBooksData(), request.META.get('HTTP_YEAR'))
df = df.dropna()
statsTotalBooks = df['name'].count()
statsTotalGenres = df['genre'].nunique()
avgratingsperyear = round((df['rating'].sum() / df['rating'].count()), 0)
data.append({
'totalbooks': statsTotalBooks,
'totalgenres': statsTotalGenres
'totalgenres': statsTotalGenres,
'avgyearrating': avgratingsperyear
})
return Response(data[0])
else:
return Response("No year header included")
@api_view(['GET'])
def books_per_country(request):
if request.META.get('HTTP_YEAR'):
data = []
df = filterData(getBooksData(), request.META.get('HTTP_YEAR'))
countries = df.groupby(['country_code', 'country'])['country'].count().reset_index(name="count")
countries = countries.sort_values(by='count', ascending=False)
for index, row in countries.iterrows():
data.append({
"code": row['country_code'],
"country": row['country'],
"count": int(row['count'])
})
return Response(data)
return JsonResponse({'error': 'No year in header'}, safe=False)
else:
return Response("No year header included")
return JsonResponse({'error': 'Unauthorized'}, safe=False)
else:
return JsonResponse({'error': 'No authorization token'}, safe=False)
# ------------------------------------------------
# Get books of selected year and group by ratings
# ------------------------------------------------
@api_view(['GET'])
def avg_ratings_per_month(request):
datayear = request.META.get('HTTP_YEAR')
if(request.headers.get('Authorization')):
isLoggedIn = isAuthorized(request.headers.get('Authorization'));
if datayear:
if(isLoggedIn):
if request.META.get('HTTP_YEAR'):
data = []
# Get CSV file with book data
df = filterData(getBooksData(), request.META.get('HTTP_YEAR'))
avgratingspermonth = df.groupby('readed')['rating'].mean().reset_index(name="rating")
for index, row in avgratingspermonth.iterrows():
data.append({
"date": row['readed'],
"rating": int(row['rating'])
@ -164,23 +144,30 @@ def avg_ratings_per_month(request):
return Response(data)
else:
return Response("No year header included")
return JsonResponse({'error': 'No year in header'}, safe=False)
else:
return JsonResponse({'error': 'Unauthorized'}, safe=False)
else:
return JsonResponse({'error': 'No authorization token'}, safe=False)
# -----------------------------
# Get ratings of selected year
# -----------------------------
@api_view(['GET'])
def countRatings(request):
datayear = request.META.get('HTTP_YEAR')
if(request.headers.get('Authorization')):
isLoggedIn = isAuthorized(request.headers.get('Authorization'));
if datayear:
if(isLoggedIn):
if request.META.get('HTTP_YEAR'):
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'])
@ -188,4 +175,8 @@ def countRatings(request):
return Response(data)
else:
return Response("No year header included")
return JsonResponse({'error': 'No year in header'}, safe=False)
else:
return JsonResponse({'error': 'Unauthorized'}, safe=False)
else:
return JsonResponse({'error': 'No authorization token'}, safe=False)

View File

@ -3,11 +3,12 @@ from django.views.decorators.csrf import csrf_exempt
from .views import *
from .modules.auth import *
from .modules.crud import *
from .modules.challenges import *
from .modules.pandas import *
urlpatterns = [
path('books/all', getAllBooks),
path('books', getBooksByYear),
# path('books', getBooksByYear),
path('books/years', getYears),
path('books/stats', getStats),
path('books/insert', addBook),
@ -17,6 +18,11 @@ urlpatterns = [
path('books/genres/count', countGenres),
path('books/ratings', avg_ratings_per_month),
path('books/ratings/count', countRatings),
path('books/countries', books_per_country),
# path('books/countries', books_per_country),
path('auth/login', csrf_exempt(login)),
path('books/challenge', getChallengeOfYear),
path('books/challenges', getAllChallenges),
path('books/challenges/insert', addChallenge),
path('books/challenges/<int:id>/delete', deleteChallenge),
]

View File

@ -2,6 +2,8 @@ import React, { Component, lazy, Suspense } from "react";
import { Route, Routes, BrowserRouter as Router } from 'react-router-dom';
import { ColorRing } from 'react-loader-spinner'
import Login from "./views/login";
import Books from "./views/bookslist";
import BooksList from "./views/bookslist";
const Dashboard = lazy(() => import("./views/dashboard"));
@ -25,6 +27,7 @@ function App() {
</div>}>
<Routes>
<Route exact path="/" element={localStorage.getItem('token') ? <Dashboard /> : <Login />} />
<Route exact path="/books" element={<BooksList />} />
{/* <Route exact path="/login" element={<Login />} /> */}
</Routes>
</Suspense>

View File

@ -1,59 +1,35 @@
import React, { Component } from 'react';
import { getChallenge, getStats } from "./Data.js";
import React, { useEffect, useState } from 'react';
export default class Challenge extends Component {
constructor(props) {
super(props);
this.state = {
readingYears: [],
challenge: 0
}
const Challenge = (props) => {
const [challenge, setChallenge] = useState(0);
const [challengePercentage, setChallengePercentage] = useState(0)
const getData = async () => {
const data = await import("./Data.js");
const stats = await data.getStats(props.year);
const yearchallenge = await data.getChallenge(props.year);
setChallenge(yearchallenge ? yearchallenge[0].nrofbooks : 0);
setChallengePercentage(Math.round((stats.totalbooks / yearchallenge[0].nrofbooks) * 100, 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 = Math.round((this.state.totalbooks / this.state.challenge) * 100, 0)
useEffect(() => {
getData();
}, [props.year])
return (
<React.Fragment>
{this.state.challenge && this.state.challenge !== 0 ?
<div className="stat-block" style={{ marginBottom: '20px' }}>
<span className="block_name">Book Challenge</span>
{challenge && challenge !== 0 ?
<div className="stat-block challenge" style={{ marginBottom: '20px' }}>
<span className="block_name">Book Challenge ({challenge} boeken)</span>
<div className="progress">
<div className="progress-bar" 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>
: ''}
</React.Fragment>
)
}
}
export default Challenge;

View File

@ -87,8 +87,6 @@ export const initChart = (data, ratings, year) => {
order: 1
})
console.log(dataSet);
/*
----------------------------------
Stacked bar chart
@ -181,7 +179,6 @@ export const initChart = (data, ratings, year) => {
export const initDoughnut = (data) => {
console.log(data);
var labels = [];
var counts = [];

View File

@ -3,19 +3,9 @@ import { readCookie } from "../Functions";
export const getAllBooks = () => {
return fetch('/api/books/all', {
"method": "GET",
})
.then(response => response.json())
.then(data => {
return data;
})
}
export const getBooksByYear = (year) => {
return fetch('/api/books', {
"method": "GET",
"headers": {
"year": year
}
"Authorization": "Bearer " + localStorage.getItem("token")
},
})
.then(response => response.json())
.then(data => {
@ -23,15 +13,28 @@ export const getBooksByYear = (year) => {
})
}
export const getAllChallenges = () => {
return fetch('/api/books/challenges', {
"method": "GET",
})
.then(response => response.json())
.then(data => {
return data;
})
}
// export const getBooksByYear = (year) => {
// return fetch('/api/books', {
// "method": "GET",
// "headers": {
// "year": year
// }
// })
// .then(response => response.json())
// .then(data => {
// return data;
// })
// }
// export const getAllChallenges = () => {
// return fetch('/api/books/challenges', {
// "method": "GET",
// })
// .then(response => response.json())
// .then(data => {
// return data;
// })
// }
export const insertChallenge = (data) => {
return fetch('/api/books/challenges/insert', {
@ -69,7 +72,8 @@ export const getStats = (year) => {
return fetch('/api/books/stats', {
"method": "GET",
"headers": {
"year": year
"year": year,
"Authorization": "Bearer " + localStorage.getItem("token")
}
})
.then(response => response.json())
@ -94,6 +98,9 @@ export const getChallenge = (year) => {
export const getReadingYears = () => {
return fetch('/api/books/years', {
"method": "GET",
"headers": {
"Authorization": "Bearer " + localStorage.getItem("token")
}
})
.then(response => response.json())
.then(data => {
@ -101,37 +108,39 @@ export const getReadingYears = () => {
})
}
export const getCountries = (year) => {
return fetch('/api/books/countries', {
"method": "GET",
"headers": {
"year": year
}
})
.then(response => response.json())
.then(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 getShortestLongestBook = (year) => {
// return fetch('/api/books/pages/stats', {
// "method": "GET",
// "headers": {
// "year": year,
// "Authorization": "Bearer " + localStorage.getItem("token")
// }
// })
// .then(response => response.json())
// .then(data => {
// return data;
// })
// }
export const getBooksPerYearPerGenres = (year) => {
return fetch('/api/books/genres', {
"method": "GET",
"headers": {
"year": year
"year": year,
"Authorization": "Bearer " + localStorage.getItem("token")
}
})
.then(response => response.json())
@ -144,7 +153,8 @@ export const getGenresCount = (year) => {
return fetch('/api/books/genres/count', {
"method": "GET",
"headers": {
"year": year
"year": year,
"Authorization": "Bearer " + localStorage.getItem("token")
}
})
.then(response => response.json())
@ -157,7 +167,8 @@ export const getAvgRatings = (year) => {
return fetch('/api/books/ratings', {
"method": "GET",
"headers": {
"year": year
"year": year,
"Authorization": "Bearer " + localStorage.getItem("token")
}
})
.then(response => response.json())
@ -170,7 +181,8 @@ export const getRatingsCount = (year) => {
return fetch('/api/books/ratings/count', {
"method": "GET",
"headers": {
"year": year
"year": year,
"Authorization": "Bearer " + localStorage.getItem("token")
}
})
.then(response => response.json())

View File

@ -8,7 +8,7 @@
#DataTable td {
color: #333;
font-size: 13px;
font-size: 12px;
height: 40px;
font-weight: 300;
vertical-align: middle;
@ -22,7 +22,8 @@
#DataTable{
margin-bottom:0 !important;
box-shadow: 0 2px 0 1px rgb(0 0 0/3%);
-webkit-box-shadow: 0px 1px 1px 1px #eee;
box-shadow: 0px 1px 1px 1px #eee;
}
#DataTable a {
@ -30,17 +31,22 @@
text-decoration: none;
}
.content-manage #DataTable .fa-star {
color: #ffbe0e;
}
.content-manage #DataTable i {
color: #ffffff;
}
.content-manage #DataTable button{
text-align: center;
border-radius: 100%;
}
.content-manage #DataTable button i{
margin:auto;
font-size: 14px;
font-size: 12px;
}
#DataTable i {
@ -49,10 +55,10 @@
}
#DataTable thead th{
padding: 20px 20px;
padding: 15px 20px;
border-bottom: 1px solid #efefef;
font-weight: 600;
font-size: 15px;
font-size: 12px;
color:#000000;
}
@ -68,11 +74,6 @@
padding:14px;
}
#DataTable{
margin-bottom:0;
box-shadow: none !important;
}
#DataTable .openAlarm .pictogram {
/* display: inline-block;
padding: 0 0 0 0;
@ -97,7 +98,7 @@
height: 0px;
vertical-align: middle;
border-top: none;
border-bottom: none;
border-bottom: solid 1px #f8f8f8 !important;
}
#DataTable .openAlarm td{

View File

@ -44,7 +44,7 @@ const Ratings = (props) => {
<React.Fragment>
<div className="ratings">
<span className="block_name">Ratings</span>
<table id="DataTable" className="table responsive nowrap" width="100%">
<table className="ratingstable responsive nowrap" width="100%">
<thead>
<tr>
<th>#</th>
@ -53,12 +53,10 @@ const Ratings = (props) => {
</tr>
</thead>
<tbody>
{ratings.map((rating) => {
{ratings.map((rating, i) => {
var ratingstars = '';
var rating_percentage = rating[1] / totalRatings * 100;
console.log(rating[1], totalRatings);
if (rating[0]) {
for (var i = 0; i < rating[0]; i++) {
ratingstars += "<i class='fas fa-star'></i>";
@ -66,7 +64,7 @@ const Ratings = (props) => {
}
return(
<tr>
<tr key={i}>
<td style={{width: '200px'}} className='book_rating' dangerouslySetInnerHTML={{__html: ratingstars}}></td>
<td style={{width: '257px'}}>
<div className="progress">

View File

@ -7,15 +7,21 @@ const Sidebar = () => {
<img className="logo_text" src="/static/images/logo_white.png" style={{ width: '100%', padding: '10px 25px' }}/>
<ul>
<NavLink to={'/'} exact="true">
<li><i class="fas fa-chart-line"></i> Dashboard</li>
<li><i className="fas fa-chart-line"></i> Dashboard</li>
</NavLink>
<NavLink to={'/books'} exact="true">
<li><i class="fas fa-book"></i> Boeken</li>
<li><i className="fas fa-book"></i> Boeken</li>
</NavLink>
<NavLink to={'/challenges'} exact="true">
<li><i className="fas fa-tasks"></i> Challenges</li>
</NavLink>
</ul>
<ul className="bottom-menu">
<NavLink to={'/settings'} exact="true">
<li><i class="fas fa-cog"></i> Instellingen</li>
<li><i className="fas fa-cog"></i> Instellingen</li>
</NavLink>
<li><i class="fas fa-power-off"></i> Uitloggen</li>
<li><i className="fas fa-power-off"></i> Uitloggen</li>
</ul>
</div>
)

View File

@ -3,6 +3,7 @@ import React, { useEffect, useState } from 'react';
const BookStats = (props) =>{
const [totalbooks, setTotalbooks] = useState(0);
const [totalgenres, setTotalgenres] = useState(0);
const [yearrating, setYearrating] = useState(0);
const getData = async () => {
const data = await import("./Data.js");
@ -10,6 +11,7 @@ const BookStats = (props) =>{
setTotalbooks(stats.totalbooks);
setTotalgenres(stats.totalgenres);
setYearrating(stats.avgyearrating);
}
useEffect(() => {
@ -21,7 +23,7 @@ const BookStats = (props) =>{
<div className='row'>
<div className="col-md-4">
<div className="stat-block">
<i class="fas fa-book-open"></i>
<i className="fas fa-book-open"></i>
<span className="stats-label">Gelezen boeken:</span>
<span className="stats-number">{totalbooks}</span>
@ -30,7 +32,7 @@ const BookStats = (props) =>{
<div className="col-md-4">
<div className="stat-block">
<i class="fas fa-book-open"></i>
<i className="fas fa-book-open"></i>
<span className="stats-label">Genres:</span>
<span className="stats-number">{totalgenres}</span>
@ -39,9 +41,9 @@ const BookStats = (props) =>{
<div className="col-md-4">
<div className="stat-block">
<i class="fas fa-star"></i>
<i className="fas fa-star"></i>
<span className="stats-label">Jaarbeoordeling:</span>
<span className="stats-number">7</span>
<span className="stats-number">{yearrating}</span>
</div>
</div>
</div>

View File

@ -0,0 +1,74 @@
import React, { useEffect, useState } from "react";
import Sidebar from "../components/Sidebar.js";
import * as moment from 'moment';
import "../components/DataTables.css";
moment.locale('nl');
const BooksList = (props) => {
var [books, setBooks] = useState([]);
const getData = async() => {
const data = await import("../components/Data.js");
const getbooks = await data.getAllBooks();
console.log(getbooks)
setBooks(getbooks);
setTimeout(() => {
$('#DataTable').DataTable({
language: {
url: 'https://cdn.datatables.net/plug-ins/9dcbecd42ad/i18n/Dutch.json',
search: "",
searchPlaceholder: "Zoeken"
},
dom: 'frt<"bottom"pl><"clear">',
order: []
});
}, 300)
}
useEffect(() => {
getData();
}, [])
return (
<React.Fragment>
<Sidebar />
<div className="content-manage">
{/* <button type="button" class="btn btn-success">Toevoegen</button> */}
<h1>Boeken beheren</h1>
<div className="DataTable_Container">
<table id="DataTable" className="showHead table responsive nowrap" width="100%">
<thead>
<tr>
<th>Boek</th>
<th>Schrijver</th>
<th>Gelezen op</th>
<th>Rating</th>
<th>Acties</th>
</tr>
</thead>
<tbody>
{books.map((book, i) => {
return (
<tr key={book.id}>
<td>{book.name}</td>
<td>{book.author}</td>
<td>{moment(book.readed).format('MMMM YYYY')}</td>
<td><i class='fas fa-star'></i> {book.rating}</td>
<td>
<button type="button" class="btn btn-danger"><i className="fa fa-trash"></i></button>
</td>
</tr>
)
})}
</tbody>
</table>
</div>
</div>
</React.Fragment>
)
}
export default BooksList;

View File

@ -3,6 +3,7 @@ import Genres from "../components/Genres";
import Books from "../components/Books";
import Ratings from "../components/Ratings";
import Stats from "../components/Stats";
import Challenge from "../components/Challenge";
import Sidebar from "../components/Sidebar";
const Dashboard = (props) => {
@ -37,9 +38,15 @@ const Dashboard = (props) => {
<Sidebar />
<div className="content">
<div className="container-fluid">
{/* <div className="row">
<div className="col-md-12">
</div>
</div> */}
<div className="row">
<div className="col-md-8">
<Stats year={year} />
<Challenge year={year} />
<Books year={year} />
</div>

View File

@ -8,20 +8,14 @@ html, body{
font-family: 'Poppins', sans-serif;
letter-spacing: .3px;
font-weight: 400;
font-size: 12px;
color:#333;
}
h1{
/* font-size: 30px;
font-weight: 500;
padding: 0 10px 0px 10px;
margin-bottom: 5px; */
border-bottom: 1px solid #ddd;
display: inline-block;
font-size: 25px;
font-size: 18px;
font-weight: 500;
margin-bottom: 20px;
padding: 0 0 10px;
width: 100%;
}
@ -228,6 +222,13 @@ html, body{
color: #48a5a9;
}
.sidebar .bottom-menu{
position: absolute;
bottom: 0;
width:100%;
border-top: solid 1px rgba(255,255,255,0.2);
}
.books-stats{
margin:20px 0;
}
@ -357,7 +358,7 @@ html, body{
.table td{
color: #101010;
border-bottom:none !important;
padding: 10px 10px !important;
padding: 10px 20px !important;
font-size: 13px;
font-weight: 300;
}
@ -375,7 +376,13 @@ html, body{
display:none;
}
.challenge .stats-number, .challenge .stats-label{
display: inline-block;
}
.challenge .stats-number{
font-size:14px;
}
span.block_name{
color: #333;
@ -593,3 +600,12 @@ html, body{
.stats{
margin-bottom:20px;
}
.ratingstable thead{
display:none;
}
.ratingstable td{
height: 35px;
padding: 0 10px 0 0px;
}

File diff suppressed because one or more lines are too long

View File

@ -10,10 +10,26 @@
!*** ./src/views/login.js ***!
\****************************/
/*!********************************!*\
!*** ./src/views/bookslist.js ***!
\********************************/
/*!***********************************!*\
!*** ./src/components/Sidebar.js ***!
\***********************************/
/*!*************************************!*\
!*** ./node_modules/react/index.js ***!
\*************************************/
/*!***************************************!*\
!*** ./node_modules/moment/moment.js ***!
\***************************************/
/*!***************************************!*\
!*** ./src/components/DataTables.css ***!
\***************************************/
/*!****************************************!*\
!*** ./node_modules/react-is/index.js ***!
\****************************************/
@ -26,18 +42,562 @@
!*** ./node_modules/scheduler/index.js ***!
\*****************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/af.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ar.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/az.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/be.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/bg.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/bm.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/bn.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/bo.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/br.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/bs.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ca.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/cs.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/cv.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/cy.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/da.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/de.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/dv.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/el.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/eo.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/es.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/et.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/eu.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/fa.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/fi.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/fo.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/fr.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/fy.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ga.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/gd.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/gl.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/gu.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/he.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/hi.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/hr.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/hu.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/id.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/is.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/it.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ja.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/jv.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ka.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/kk.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/km.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/kn.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ko.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ku.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ky.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/lb.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/lo.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/lt.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/lv.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/me.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/mi.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/mk.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ml.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/mn.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/mr.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ms.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/mt.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/my.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/nb.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ne.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/nl.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/nn.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/pl.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/pt.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ro.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ru.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/sd.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/se.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/si.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/sk.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/sl.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/sq.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/sr.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ss.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/sv.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/sw.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ta.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/te.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/tg.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/th.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/tk.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/tr.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/uk.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/ur.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/uz.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/vi.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/moment/locale/yo.js ***!
\******************************************/
/*!******************************************!*\
!*** ./node_modules/react-dom/client.js ***!
\******************************************/
/*!*******************************************!*\
!*** ./node_modules/moment/locale/fil.js ***!
\*******************************************/
/*!*******************************************!*\
!*** ./node_modules/moment/locale/tet.js ***!
\*******************************************/
/*!*******************************************!*\
!*** ./node_modules/moment/locale/tlh.js ***!
\*******************************************/
/*!*******************************************!*\
!*** ./node_modules/moment/locale/tzl.js ***!
\*******************************************/
/*!*******************************************!*\
!*** ./node_modules/moment/locale/tzm.js ***!
\*******************************************/
/*!********************************************!*\
!*** ./node_modules/shallowequal/index.js ***!
\********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/ar-dz.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/ar-kw.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/ar-ly.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/ar-ma.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/ar-sa.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/ar-tn.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/bn-bd.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/de-at.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/de-ch.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/en-au.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/en-ca.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/en-gb.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/en-ie.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/en-il.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/en-in.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/en-nz.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/en-sg.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/es-do.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/es-mx.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/es-us.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/fr-ca.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/fr-ch.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/hy-am.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/it-ch.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/ms-my.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/nl-be.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/pa-in.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/pt-br.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/tl-ph.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/ug-cn.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/zh-cn.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/zh-hk.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/zh-mo.js ***!
\*********************************************/
/*!*********************************************!*\
!*** ./node_modules/moment/locale/zh-tw.js ***!
\*********************************************/
/*!**********************************************!*\
!*** ./node_modules/moment/locale/oc-lnc.js ***!
\**********************************************/
/*!***********************************************!*\
!*** ./node_modules/moment/locale/sr-cyrl.js ***!
\***********************************************/
/*!***********************************************!*\
!*** ./node_modules/moment/locale/uz-latn.js ***!
\***********************************************/
/*!************************************************!*\
!*** ./node_modules/moment/locale/gom-deva.js ***!
\************************************************/
/*!************************************************!*\
!*** ./node_modules/moment/locale/gom-latn.js ***!
\************************************************/
/*!************************************************!*\
!*** ./node_modules/moment/locale/tzm-latn.js ***!
\************************************************/
/*!************************************************!*\
!*** ./node_modules/moment/locale/x-pseudo.js ***!
\************************************************/
/*!*************************************************!*\
!*** ./node_modules/react-router/dist/index.js ***!
\*************************************************/
/*!***************************************************!*\
!*** ./node_modules/moment/locale/ sync ^\.\/.*$ ***!
\***************************************************/
/*!***************************************************!*\
!*** ./node_modules/styled-tools/dist/es/prop.js ***!
\***************************************************/
@ -50,6 +610,10 @@
!*** ./node_modules/styled-tools/dist/es/theme.js ***!
\****************************************************/
/*!*****************************************************!*\
!*** ./node_modules/css-loader/dist/runtime/api.js ***!
\*****************************************************/
/*!*****************************************************!*\
!*** ./node_modules/react-router-dom/dist/index.js ***!
\*****************************************************/
@ -102,10 +666,18 @@
!*** ./node_modules/scheduler/cjs/scheduler.development.js ***!
\*************************************************************/
/*!**************************************************************!*\
!*** ./node_modules/css-loader/dist/runtime/noSourceMaps.js ***!
\**************************************************************/
/*!***************************************************************!*\
!*** ./node_modules/react-loader-spinner/dist/esm/helpers.js ***!
\***************************************************************/
/*!***************************************************************!*\
!*** ./node_modules/style-loader/dist/runtime/styleDomAPI.js ***!
\***************************************************************/
/*!*****************************************************************!*\
!*** ./node_modules/@emotion/stylis/dist/stylis.browser.esm.js ***!
\*****************************************************************/
@ -150,6 +722,10 @@
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Watch.js ***!
\********************************************************************/
/*!********************************************************************!*\
!*** ./node_modules/style-loader/dist/runtime/insertBySelector.js ***!
\********************************************************************/
/*!*********************************************************************!*\
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Blocks.js ***!
\*********************************************************************/
@ -162,6 +738,10 @@
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Vortex.js ***!
\*********************************************************************/
/*!*********************************************************************!*\
!*** ./node_modules/style-loader/dist/runtime/styleTagTransform.js ***!
\*********************************************************************/
/*!**********************************************************************!*\
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Circles.js ***!
\**********************************************************************/
@ -174,6 +754,10 @@
!*** ./node_modules/react-loader-spinner/dist/esm/loader/Discuss.js ***!
\**********************************************************************/
/*!**********************************************************************!*\
!*** ./node_modules/style-loader/dist/runtime/insertStyleElement.js ***!
\**********************************************************************/
/*!***********************************************************************!*\
!*** ./node_modules/react-loader-spinner/dist/esm/loader/LineWave.js ***!
\***********************************************************************/
@ -230,6 +814,14 @@
!*** ./node_modules/react-loader-spinner/dist/esm/loader/RotatingLines.js ***!
\****************************************************************************/
/*!****************************************************************************!*\
!*** ./node_modules/style-loader/dist/runtime/injectStylesIntoStyleTag.js ***!
\****************************************************************************/
/*!*****************************************************************************!*\
!*** ./node_modules/css-loader/dist/cjs.js!./src/components/DataTables.css ***!
\*****************************************************************************/
/*!*****************************************************************************!*\
!*** ./node_modules/react-loader-spinner/dist/esm/loader/CirclesWithBar.js ***!
\*****************************************************************************/
@ -258,6 +850,10 @@
!*** ./node_modules/hoist-non-react-statics/dist/hoist-non-react-statics.cjs.js ***!
\**********************************************************************************/
/*!**********************************************************************************!*\
!*** ./node_modules/style-loader/dist/runtime/setAttributesWithoutAttributes.js ***!
\**********************************************************************************/
/*!****************************************************************************************************!*\
!*** ./node_modules/styled-components/node_modules/@emotion/unitless/dist/unitless.browser.esm.js ***!
\****************************************************************************************************/

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -18,6 +18,6 @@
!*** ./src/components/Ratings.js ***!
\***********************************/
/*!***********************************!*\
!*** ./src/components/Sidebar.js ***!
\***********************************/
/*!*************************************!*\
!*** ./src/components/Challenge.js ***!
\*************************************/

View File

@ -20,7 +20,7 @@ urlpatterns = [
path('admin/', admin.site.urls),
path('api/', include('api.urls')),
path('', include('frontend.urls')),
path('booklist/', include('frontend.urls')),
path('books/', include('frontend.urls')),
path('login/', include('frontend.urls')),
path('manage/', include('frontend.urls')),
path('manage/challenges', include('frontend.urls'))